Skip to content

Commit

Permalink
first version based on moptipy using pycommons
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasWeise committed Mar 13, 2024
1 parent ef29906 commit 7aaeb92
Show file tree
Hide file tree
Showing 69 changed files with 273 additions and 813 deletions.
6 changes: 1 addition & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ clean: status
rm -rf build && \
rm -rf dist && \
rm -rf docs/build && \
mv docs/source/index.rst docs/source/index.x && \
rm -rf docs/source/*.rst && \
mv docs/source/index.x docs/source/index.rst && \
rm -rf moptipyapps.egg-info && \
echo "$(NOW): Done cleaning up, moptipyapps is uninstalled and auto-generated stuff is deleted."

Expand All @@ -64,7 +62,7 @@ test: init
echo "$(NOW): Running pytest with doctests." && \
timeout --kill-after=15s 90m coverage run -a --include="moptipyapps/*" -m pytest --strict-config --doctest-modules --ignore=tests --ignore=examples && \
echo "$(NOW): Running pytest tests." && \
timeout --kill-after=15s 90m coverage run --include="moptipyapps/*" -m pytest --strict-config tests --ignore=examples && \
timeout --kill-after=15s 90m coverage run -a --include="moptipyapps/*" -m pytest --strict-config tests --ignore=examples && \
echo "$(NOW): Finished running pytest tests."

# Perform static code analysis.
Expand Down Expand Up @@ -127,10 +125,8 @@ create_documentation: static_analysis test
echo "$(NOW): Now creating the documentation build folder and building the documentation." && \
sphinx-build -W -a -E -b html docs/source docs/build && \
echo "$(NOW): Done creating HTML documentation, cleaning up documentation temp files." && \
mv docs/source/index.rst docs/source/index.tmp && \
rm -rf docs/source/*.rst && \
rm -rf docs/source/*.md && \
mv docs/source/index.tmp docs/source/index.rst && \
echo "$(NOW): Now we pygmentize all the examples in 'examples' to 'build/examples'." &&\
mkdir -p docs/build/examples &&\
for f in examples/*.py; do \
Expand Down
145 changes: 12 additions & 133 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -1,136 +1,15 @@
"""The configuration for sphinx to generate the documentation."""
import datetime
import os
import re
import sys
from typing import Final

# the path of the documentation configuration
doc_path = os.path.abspath(os.path.dirname(__file__))

# get the path to the root directory of this project
root_path = os.path.abspath(os.path.join(doc_path, "..", ".."))
sys.path.insert(0, root_path)

# set the base url
html_baseurl = "https://thomasweise.github.io/moptipyapps/"

# We want to include the contents of our GitHub README.md file.
# So first, we need to load the README.md file.
old_lines: list[str]
with open(os.path.join(root_path, "README.md"),
encoding="utf-8-sig") as reader:
old_lines = reader.readlines()

# Now, we need to fix the file contents.
# We discard the top-level heading as well as the badge for the build status.
# We need to move all sub-headings one step up.
# Furthermore, we can turn all absolute URLs starting with
# http://thomasweise.github.io/moptipyapps/xxx to local references, i.e.,
# ./xxx. Finally, it seems that the myst parser now again drops the numerical
# prefixes of links, i.e., it tags `## 1.2. Hello` with id `hello` instead of
# `12-hello`. This means that we need to fix all references following the
# pattern `[xxx](#12-hello)` to `[xxx](#hello)`. We do this with a regular
# expression `regex_search`.
new_lines = []
in_code: bool = False # we only process non-code lines
skip: bool = True
# detects strings of the form [xyz](#123-bla) and gives \1=xyz and \2=bla
regex_search = re.compile("(\\[.+?])\\(#\\d+-(.+?)\\)")
license_link: str = \
"https://github.com/thomasWeise/moptipyapps/blob/main/LICENSE"
needs_newline: bool = False
can_add_anyway: bool = True
for line in old_lines:
if skip: # we skip everything until the introduction section
if line.lstrip().startswith("## 1. Introduction"):
skip = False
new_lines.append(line[1:])
elif line.startswith("[![") and can_add_anyway:
needs_newline = True
new_lines.append(line)
continue
else:
can_add_anyway = False
continue
if needs_newline:
new_lines.append("")
needs_newline = False
continue
if in_code:
if line.startswith("```"):
in_code = False # toggle to non-code
else:
if line.startswith("```"):
in_code = True # toggle to code
elif line.startswith("#"):
line = line[1:] # move all sub-headings one step up
else: # fix all internal urls
# replace links of the form "#12-bla" to "#bla"
line = re.sub(regex_search, "\\1(#\\2)", line)

line = line.replace(license_link, "./LICENSE.html")
for k in [html_baseurl, f"http{html_baseurl[5:]}"]:
line = line.replace(f"]({k}", "](./")\
.replace(f' src="{k}', ' src="./')\
.replace(f' href="{k}', ' href="./')

new_lines.append(line)

# write the post-processed README.md file
with open(os.path.join(doc_path, "README.md"), "w",
encoding="utf-8") as outf:
outf.writelines(new_lines)

# enable myst header anchors
myst_heading_anchors = 6

# project information
project = "moptipyapps"
author = "Thomas Weise"
# noinspection PyShadowingBuiltins
copyright = f"2023-{datetime.datetime.now ().year}, {author}"
from pycommons.dev.doc.setup_doc import setup_doc
from pycommons.io.path import Path, file_path

# tell sphinx to go kaboom on errors
nitpicky = True
myst_all_links_external = True

# The full version, including alpha/beta/rc tags.
release = {}
with open(os.path.abspath(os.path.sep.join([
root_path, "moptipyapps", "version.py"]))) as fp:
exec(fp.read(), release) # nosec # nosemgrep # noqa: DUO105
release = release["__version__"]

# The Sphinx extension modules that we use.
extensions = ["myst_parser", # for processing README.md
"sphinx.ext.autodoc", # to convert docstrings to documentation
"sphinx.ext.doctest", # do the doc tests again
"sphinx.ext.intersphinx", # to link to numpy et al.
"sphinx_autodoc_typehints", # to infer types from hints
"sphinx.ext.viewcode", # add rendered source code
]

# Location of dependency documentation for cross-referencing.
intersphinx_mapping = {
"matplotlib": ("https://matplotlib.org/stable/", None),
"moptipy": ("https://thomasweise.github.io/moptipy/", None),
"numpy": ("https://numpy.org/doc/stable/", None),
"python": ("https://docs.python.org/3/", None),
'scipy': ('https://docs.scipy.org/doc/scipy/', None),
"urllib3": ("https://urllib3.readthedocs.io/en/stable/", None),
}

# inherit docstrings in autodoc
autodoc_inherit_docstrings = True

# add default values after comma
typehints_defaults = "comma"

# the sources to be processed
source_suffix = [".rst", ".md"]

# The theme to use for HTML and HTML Help pages.
html_theme = "bizstyle"

# Code syntax highlighting style:
pygments_style = "default"
# the path of the documentation configuration
doc_path: Final[Path] = file_path(__file__).up(1)
root_path: Final[Path] = doc_path.up(2)
setup_doc(doc_path, root_path, 2023, dependencies=(
"matplotlib", "moptipy", "numpy", "pycommons", "scipy", "sklearn",
"urllib3"),
full_urls={
"https://github.com/thomasWeise/moptipyapps/blob/main/LICENSE":
"./LICENSE.html"})
17 changes: 0 additions & 17 deletions docs/source/index.rst

This file was deleted.

13 changes: 7 additions & 6 deletions examples/binpacking2d_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from moptipy.utils.nputils import rand_generator
from moptipy.utils.plot_utils import save_figure
from moptipy.utils.sys_info import is_make_build
from moptipy.utils.temp import TempDir
from pycommons.io.temp import temp_dir

from moptipyapps.binpacking2d.encodings.ibl_encoding_1 import (
ImprovedBottomLeftEncoding1,
Expand All @@ -50,7 +50,8 @@
# can occur multiple times, i.e., may occur repeatedly.
# So we include each of n_different_items item IDs exactly as often as
# it should be repeated. We sometimes include it directly and sometimes
# in the negated form, which is interpreted as a 90° rotation by the encoding.
# in the negated form, which is interpreted as a 90deg rotation by the
# encoding.
# Then, we convert the list of item IDs to a numpy array and, finally, shuffle
# the array. The encoding then inserts the items in the same sequence they
# appear in the array into bins.
Expand Down Expand Up @@ -78,10 +79,10 @@


# We can now plot the packing. We create the figures in a temp directory.
# To keep the figures, you would put an existing directory path into `td`
# by doing `from moptipy.utils.path import Path; td = Path.directory("mydir")`
# and not use the `with` block.
with TempDir.create() as td: # create temporary directory `td`
# To keep the figures, you would put an existing directory path into `td` by
# doing `from pycommons.io.path import Path; td = Path("mydir")` and not use
# the `with` block.
with temp_dir() as td: # create temporary directory `td`
files = [] # the collection of files

# Plot the packings. The default plotting includes the item ID into each
Expand Down
10 changes: 5 additions & 5 deletions examples/binpacking2d_rls.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
)
from moptipy.spaces.signed_permutations import SignedPermutations
from moptipy.utils.plot_utils import save_figure
from moptipy.utils.temp import TempDir
from pycommons.io.temp import temp_dir

from moptipyapps.binpacking2d.encodings.ibl_encoding_2 import (
ImprovedBottomLeftEncoding2,
Expand Down Expand Up @@ -72,10 +72,10 @@
process.get_copy_of_best_y(y)

# We can now plot the best packing. We create the figures in a temp directory.
# To keep the figures, you would put an existing directory path into `td`
# by doing `from moptipy.utils.path import Path; td = Path.directory("mydir")`
# and not use the `with` block.
with TempDir.create() as td: # create temporary directory `td`
# To keep the figures, you would put an existing directory path into `td` by
# doing `from pycommons.io.path import Path; td = Path("mydir")` and not use
# the `with` block.
with temp_dir() as td: # create temporary directory `td`
files = [] # the collection of files

# Plot the packing. The default plotting includes the item ID into each
Expand Down
2 changes: 1 addition & 1 deletion examples/binpacking2d_timing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import numpy as np
import psutil
from moptipy.utils.nputils import rand_generator
from moptipy.utils.types import type_name
from pycommons.types import type_name

from moptipyapps.binpacking2d.encodings.ibl_encoding_1 import (
ImprovedBottomLeftEncoding1,
Expand Down
20 changes: 10 additions & 10 deletions examples/order1_from_dat.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,18 @@
from moptipy.api.execution import Execution
from moptipy.operators.permutations.op0_shuffle import Op0Shuffle
from moptipy.operators.permutations.op1_swap2 import Op1Swap2
from moptipy.utils.console import logger
from moptipy.utils.help import argparser
from moptipy.utils.nputils import rand_seeds_from_str
from moptipy.utils.path import Path
from moptipy.utils.plot_defaults import distinct_colors, distinct_markers
from moptipy.utils.plot_utils import create_figure, get_axes, save_figure
from moptipy.utils.types import check_to_int_range
from pycommons.io.console import logger
from pycommons.io.path import Path, directory_path
from pycommons.types import check_to_int_range

from moptipyapps.order1d.distances import swap_distance
from moptipyapps.order1d.instance import Instance
from moptipyapps.order1d.space import OrderingSpace
from moptipyapps.qap.objective import QAPObjective
from moptipyapps.shared import moptipyapps_argparser

#: the impact of rank differences
POWER: Final[float] = 2.0
Expand All @@ -100,7 +100,7 @@ def parse_data(path: str, collector: Callable[[
:param fitness_limit: the minimum acceptable fitness
:param pattern: the file name pattern
"""
the_path: Final[Path] = Path.path(path)
the_path: Final[Path] = Path(path)
if isdir(the_path): # recursively parse directories
logger(f"recursing into directory '{the_path}'.")
for subpath in listdir(the_path):
Expand Down Expand Up @@ -191,7 +191,7 @@ def run(source: str, dest: str, max_fes: int, fitness_limit: int,
del data # free the now useless data

# run the algorithm
dest_file: Final[Path] = Path.path(f"{dest}.txt")
dest_file: Final[Path] = Path(f"{dest}.txt")
logger(f"finished constructing matrix with {instance.n} rows, "
"now doing optimization for "
f"{max_fes} FEs and, afterwards, writing result to '{dest_file}'.")
Expand All @@ -211,7 +211,7 @@ def run(source: str, dest: str, max_fes: int, fitness_limit: int,

files: Final[list[str]] = sorted({f[0][0] for f in instance.tags})
logger(f"now we begin plotting the data from {len(files)} files.")
dest_dir: Final[Path] = Path.directory(dirname(dest_file))
dest_dir: Final[Path] = directory_path(dirname(dest_file))
figure: Final[Figure] = create_figure()
colors: Final[tuple[tuple[float, float, float], ...]] \
= distinct_colors(len(files))
Expand All @@ -231,15 +231,15 @@ def run(source: str, dest: str, max_fes: int, fitness_limit: int,

# Perform the optimization
if __name__ == "__main__":
parser: Final[argparse.ArgumentParser] = argparser(
parser: Final[argparse.ArgumentParser] = moptipyapps_argparser(
__file__, "One-Dimensional Ordering of Permutations",
"Run the one-dimensional order of permutations experiment.")
parser.add_argument(
"source", help="the directory or file with the input data",
type=Path.path, nargs="?", default="./")
type=Path, nargs="?", default="./")
parser.add_argument(
"dest", help="the file to write the output to",
type=Path.path, nargs="?", default="./result.txt")
type=Path, nargs="?", default="./result.txt")
parser.add_argument("fitnessLimit", help="the minimum acceptable fitness",
type=int, nargs="?", default=1_000_000_000)
parser.add_argument("maxFEs", help="the maximum FEs to perform",
Expand Down
10 changes: 5 additions & 5 deletions examples/qap_example_experiment_rls_rs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
from moptipy.operators.permutations.op0_shuffle import Op0Shuffle
from moptipy.operators.permutations.op1_swap2 import Op1Swap2
from moptipy.spaces.permutations import Permutations
from moptipy.utils.help import argparser
from moptipy.utils.path import Path
from pycommons.io.path import Path

from moptipyapps.qap.instance import Instance
from moptipyapps.qap.objective import QAPObjective
from moptipyapps.shared import moptipyapps_argparser


def make_instances() -> Iterable[Callable[[], Instance]]:
Expand Down Expand Up @@ -68,7 +68,7 @@ def run(base_dir: str, n_runs: int = 3) -> None:
:param base_dir: the base directory
:param n_runs: the number of runs
"""
use_dir: Final[Path] = Path.path(base_dir)
use_dir: Final[Path] = Path(base_dir)
use_dir.ensure_dir_exists()

run_experiment(
Expand All @@ -81,11 +81,11 @@ def run(base_dir: str, n_runs: int = 3) -> None:

# Run the experiment from the command line
if __name__ == "__main__":
parser: Final[argparse.ArgumentParser] = argparser(
parser: Final[argparse.ArgumentParser] = moptipyapps_argparser(
__file__, "Quadratic Assignment Problem (QAP)",
"Run the QAP experiment.")
parser.add_argument(
"dest", help="the directory to store the experimental results under",
type=Path.path, nargs="?", default="./results/")
type=Path, nargs="?", default="./results/")
args: Final[argparse.Namespace] = parser.parse_args()
run(args.dest)
Loading

0 comments on commit 7aaeb92

Please sign in to comment.