Skip to content

Commit

Permalink
update to the new pycommons version and added optional H-table loggin…
Browse files Browse the repository at this point in the history
…g to FFA
  • Loading branch information
thomasWeise committed Apr 17, 2024
1 parent c2b80dd commit 225cdfa
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 34 deletions.
26 changes: 21 additions & 5 deletions moptipy/algorithms/so/fea1plus1.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
import numpy as np
from numpy.random import Generator
from pycommons.strings.string_conv import num_to_str
from pycommons.types import type_error

from moptipy.api.algorithm import Algorithm1
from moptipy.api.operators import Op0, Op1
Expand All @@ -109,7 +110,7 @@


def _fea_flat(process: Process, op0: Callable, op1: Callable,
lb: int, ub: int) -> None:
lb: int, ub: int, log_h_tbl: bool) -> None:
"""
Apply the (1+1)-FEA to an optimization problem.
Expand All @@ -118,6 +119,7 @@ def _fea_flat(process: Process, op0: Callable, op1: Callable,
:param op1: the unary search operator
:param lb: the lower bound
:param ub: the upper bound
:param log_h_tbl: should we log the H table?
"""
# Create records for old and new point in the search space.
best_x = process.create() # record for best-so-far solution
Expand Down Expand Up @@ -146,6 +148,9 @@ def _fea_flat(process: Process, op0: Callable, op1: Callable,
best_f = new_f # Store its objective value.
best_x, new_x = new_x, best_x # Swap best and new.

if not log_h_tbl:
return # we are done here

# After we are done, we want to print the H table.
if h[best_f] == 0: # Fix the H table for the case that only one
h[best_f] = 1 # single FE was performed.
Expand All @@ -155,13 +160,15 @@ def _fea_flat(process: Process, op0: Callable, op1: Callable,
lambda i, _lb=lb: str(i + _lb)))


def _fea_map(process: Process, op0: Callable, op1: Callable) -> None:
def _fea_map(process: Process, op0: Callable, op1: Callable,
log_h_tbl: bool) -> None:
"""
Apply the (1+1)-FEA to an optimization problem.
:param process: the black-box process object
:param op0: the nullary search operator
:param op1: the unary search operator
:param log_h_tbl: should we log the H table?
"""
# Create records for old and new point in the search space.
best_x = process.create() # record for best-so-far solution
Expand Down Expand Up @@ -190,6 +197,9 @@ def _fea_map(process: Process, op0: Callable, op1: Callable) -> None:
best_f = new_f # Store its objective value.
best_x, new_x = new_x, best_x # Swap best and new.

if not log_h_tbl:
return

# After we are done, we want to print the H table.
if h[best_f] == 0: # Fix the H table for the case that only one
h[best_f] = 1 # single FE was performed.
Expand Down Expand Up @@ -224,14 +234,19 @@ class FEA1plus1(Algorithm1):
in module :mod:`~moptipy.algorithms.so.fitnesses.ffa`.
"""

def __init__(self, op0: Op0, op1: Op1) -> None:
def __init__(self, op0: Op0, op1: Op1, log_h_tbl: bool = True) -> None:
"""
Create the (1+1)-FEA.
:param op0: the nullary search operator
:param op1: the unary search operator
:param log_h_tbl: should we log the H table?
"""
super().__init__("fea1p1", op0, op1)
if not isinstance(log_h_tbl, bool):
raise type_error(log_h_tbl, "log_h_tbl", bool)
#: True if we should log the H table, False otherwise
self.log_h_tbl: Final[bool] = log_h_tbl

def solve(self, process: Process) -> None:
"""
Expand All @@ -244,9 +259,10 @@ def solve(self, process: Process) -> None:
ub: Final[int | float] = process.upper_bound()
if isinstance(ub, int) and isinstance(lb, int) \
and ((ub - lb) <= SWITCH_TO_MAP_RANGE):
_fea_flat(process, self.op0.op0, self.op1.op1, lb, ub)
_fea_flat(process, self.op0.op0, self.op1.op1, lb, ub,
self.log_h_tbl)
return
_fea_map(process, self.op0.op0, self.op1.op1)
_fea_map(process, self.op0.op0, self.op1.op1, self.log_h_tbl)


def __h_to_str(indices: Iterable[int | float],
Expand Down
53 changes: 34 additions & 19 deletions moptipy/algorithms/so/fitnesses/ffa.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import numpy as np
from numpy.random import Generator
from pycommons.strings.string_conv import num_to_str
from pycommons.types import type_error

from moptipy.algorithms.so.fea1plus1 import SWITCH_TO_MAP_RANGE, log_h
from moptipy.algorithms.so.fitness import Fitness, FRecord
Expand All @@ -75,23 +76,25 @@
class FFA(Fitness):
"""The frequency fitness assignment (FFA) process."""

def __new__(cls, f: Objective) -> "FFA":
def __new__(cls, f: Objective, log_h_tbl: bool = True) -> "FFA":
"""
Create the frequency fitness assignment mapping.
:param f: the objective function
"""
if not isinstance(log_h_tbl, bool):
raise type_error(log_h_tbl, "log_h_tbl", bool)
check_objective(f)
if f.is_always_integer():
lb: Final[int | float] = f.lower_bound()
ub: Final[int | float] = f.upper_bound()
if isinstance(ub, int) and isinstance(lb, int) \
and ((ub - lb) <= SWITCH_TO_MAP_RANGE):
if 0 <= lb <= SWITCH_TO_OFFSET_LB:
return _IntFFA1.__new__(_IntFFA1, cast(int, ub))
return _IntFFA1.__new__(_IntFFA1, cast(int, ub), log_h_tbl)
return _IntFFA2.__new__(_IntFFA2, cast(int, lb),
cast(int, ub))
return _DictFFA.__new__(_DictFFA)
cast(int, ub), log_h_tbl)
return _DictFFA.__new__(_DictFFA, log_h_tbl)

def __str__(self):
"""
Expand All @@ -110,19 +113,23 @@ class _IntFFA1(FFA):
__h: np.ndarray
#: is this the first iteration?
__first: bool
#: log the h table?
__log_h_tbl: bool

def __new__(cls, ub: int) -> "_IntFFA1":
def __new__(cls, ub: int, log_h_tbl: bool = True) -> "_IntFFA1":
"""Initialize the pure integer FFA."""
instance = object.__new__(_IntFFA1)
instance.__h = np.zeros(ub + 1, dtype=DEFAULT_INT)
instance.__first = True
instance.__log_h_tbl = log_h_tbl
return instance

def log_information_after_run(self, process: Process) -> None:
"""Write the H table."""
log_h(process, range(len(self.__h)),
cast(Callable[[int | float], int], self.__h.__getitem__),
str)
if self.__log_h_tbl:
log_h(process, range(len(self.__h)),
cast(Callable[[int | float], int], self.__h.__getitem__),
str)

def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
"""
Expand All @@ -131,7 +138,7 @@ def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
:param p: the list of records
:param random: ignored
>>> fit = _IntFFA1(100)
>>> fit = _IntFFA1(100, False)
>>> a = FRecord(None, 1)
>>> b = FRecord(None, 2)
>>> c = FRecord(None, 2)
Expand Down Expand Up @@ -201,20 +208,24 @@ class _IntFFA2(FFA):
__lb: int
#: is this the first iteration?
__first: bool
#: log the h table?
__log_h_tbl: bool

def __new__(cls, lb: int, ub: int) -> "_IntFFA2":
def __new__(cls, lb: int, ub: int, log_h_tbl: bool = True) -> "_IntFFA2":
"""Initialize the pure integer FFA."""
instance = object.__new__(_IntFFA2)
instance.__h = np.zeros(ub - lb + 1, dtype=DEFAULT_INT)
instance.__lb = lb
instance.__first = True
instance.__log_h_tbl = log_h_tbl
return instance

def log_information_after_run(self, process: Process) -> None:
"""Write the H table."""
log_h(process, range(len(self.__h)),
cast(Callable[[int | float], int], self.__h.__getitem__),
lambda i: str(i + self.__lb))
if self.__log_h_tbl:
log_h(process, range(len(self.__h)),
cast(Callable[[int | float], int], self.__h.__getitem__),
lambda i: str(i + self.__lb))

def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
"""
Expand All @@ -223,7 +234,7 @@ def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
:param p: the list of records
:param random: ignored
>>> fit = _IntFFA2(-10, 100)
>>> fit = _IntFFA2(-10, 100, False)
>>> a = FRecord(None, -1)
>>> b = FRecord(None, 2)
>>> c = FRecord(None, 2)
Expand Down Expand Up @@ -296,19 +307,23 @@ class _DictFFA(FFA):
__h: Counter[int | float]
#: is this the first iteration?
__first: bool
#: log the h table?
__log_h_tbl: bool

def __new__(cls) -> "_DictFFA":
def __new__(cls, log_h_tbl: bool = True) -> "_DictFFA":
"""Initialize the pure integer FFA."""
instance = object.__new__(_DictFFA)
instance.__h = Counter()
instance.__first = True
instance.__log_h_tbl = log_h_tbl
return instance

def log_information_after_run(self, process: Process) -> None:
"""Write the H table."""
log_h(process, cast(Iterable[int | float], self.__h.keys()),
cast(Callable[[int | float], int], self.__h.__getitem__),
num_to_str)
if self.__log_h_tbl:
log_h(process, cast(Iterable[int | float], self.__h.keys()),
cast(Callable[[int | float], int], self.__h.__getitem__),
num_to_str)

def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
"""
Expand All @@ -317,7 +332,7 @@ def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
:param p: the list of records
:param random: ignored
>>> fit = _DictFFA()
>>> fit = _DictFFA(False)
>>> a = FRecord(None, -1)
>>> b = FRecord(None, 2.9)
>>> c = FRecord(None, 2.9)
Expand Down
4 changes: 1 addition & 3 deletions moptipy/evaluation/log_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"""

from math import inf, isfinite, isinf
from os import listdir
from typing import Final

from pycommons.io.console import logger
Expand Down Expand Up @@ -421,8 +420,7 @@ def parse_dir(self, path: str) -> bool:

do_files = True
do_dirs = True
for subpath in listdir(folder):
sub: Path = folder.resolve_inside(subpath)
for sub in folder.list_dir():
if sub.is_file():
if do_files and (not self.parse_file(sub)):
logger(f"will parse no more files in {folder!r}.")
Expand Down
12 changes: 8 additions & 4 deletions moptipy/utils/sys_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ def __cpu_affinity(proc: psutil.Process | None = None) -> str | None:

#: the dependencies
__DEPENDENCIES: set[str] | None = {
"contourpy", "cycler", "fonttools", "intel-cmplr-lib-rt", "joblib",
"kiwisolver", "llvmlite", "matplotlib", "moptipy", "numba", "numpy",
"packaging", "pdfo", "Pillow", "psutil", "pyparsing", "python-dateutil",
"scikit-learn", "scipy", "six", "threadpoolctl"}
"cmaes", "contourpy", "cycler", "fonttools", "intel-cmplr-lib-rt",
"joblib", "kiwisolver", "llvmlite", "matplotlib", "moptipy", "numba",
"numpy", "packaging", "pdfo", "Pillow", "psutil", "pycommons",
"pyparsing", "python-dateutil", "scikit-learn", "scipy", "setuptools",
"six", "threadpoolctl"}


def is_make_build() -> bool:
Expand Down Expand Up @@ -351,6 +352,7 @@ def get_sys_info() -> str:
session.workingDirectory
session.ipAddress
version.Pillow
version.cmaes
version.contourpy
version.cycler
version.fonttools
Expand All @@ -365,10 +367,12 @@ def get_sys_info() -> str:
version.packaging
version.pdfo
version.psutil
version.pycommons
version.pyparsing
version.pythondateutil
version.scikitlearn
version.scipy
version.setuptools
version.six
version.threadpoolctl
hardware.machine
Expand Down
2 changes: 1 addition & 1 deletion moptipy/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
from typing import Final

#: the version string of `moptipy`
__version__: Final[str] = "0.9.105"
__version__: Final[str] = "0.9.106"
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ psutil == 5.9.5

# pycommons offers many of the tools and utilities used in moptipy that are
# not related to optimization.
pycommons == 0.8.14
pycommons == 0.8.21

# scikit-learn is used to obtain some clusters of JSSP instances for our
# experiments.
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ install_requires =
matplotlib >= 3.8.0
pdfo >= 1.3.1
psutil >= 5.9.5
pycommons >= 0.8.14
pycommons >= 0.8.21
scikit-learn >= 1.3.1
scipy >= 1.11.3
packages = find:
Expand Down

0 comments on commit 225cdfa

Please sign in to comment.