Skip to content

Commit 225cdfa

Browse files
committed
update to the new pycommons version and added optional H-table logging to FFA
1 parent c2b80dd commit 225cdfa

File tree

7 files changed

+67
-34
lines changed

7 files changed

+67
-34
lines changed

moptipy/algorithms/so/fea1plus1.py

+21-5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
import numpy as np
9494
from numpy.random import Generator
9595
from pycommons.strings.string_conv import num_to_str
96+
from pycommons.types import type_error
9697

9798
from moptipy.api.algorithm import Algorithm1
9899
from moptipy.api.operators import Op0, Op1
@@ -109,7 +110,7 @@
109110

110111

111112
def _fea_flat(process: Process, op0: Callable, op1: Callable,
112-
lb: int, ub: int) -> None:
113+
lb: int, ub: int, log_h_tbl: bool) -> None:
113114
"""
114115
Apply the (1+1)-FEA to an optimization problem.
115116
@@ -118,6 +119,7 @@ def _fea_flat(process: Process, op0: Callable, op1: Callable,
118119
:param op1: the unary search operator
119120
:param lb: the lower bound
120121
:param ub: the upper bound
122+
:param log_h_tbl: should we log the H table?
121123
"""
122124
# Create records for old and new point in the search space.
123125
best_x = process.create() # record for best-so-far solution
@@ -146,6 +148,9 @@ def _fea_flat(process: Process, op0: Callable, op1: Callable,
146148
best_f = new_f # Store its objective value.
147149
best_x, new_x = new_x, best_x # Swap best and new.
148150

151+
if not log_h_tbl:
152+
return # we are done here
153+
149154
# After we are done, we want to print the H table.
150155
if h[best_f] == 0: # Fix the H table for the case that only one
151156
h[best_f] = 1 # single FE was performed.
@@ -155,13 +160,15 @@ def _fea_flat(process: Process, op0: Callable, op1: Callable,
155160
lambda i, _lb=lb: str(i + _lb)))
156161

157162

158-
def _fea_map(process: Process, op0: Callable, op1: Callable) -> None:
163+
def _fea_map(process: Process, op0: Callable, op1: Callable,
164+
log_h_tbl: bool) -> None:
159165
"""
160166
Apply the (1+1)-FEA to an optimization problem.
161167
162168
:param process: the black-box process object
163169
:param op0: the nullary search operator
164170
:param op1: the unary search operator
171+
:param log_h_tbl: should we log the H table?
165172
"""
166173
# Create records for old and new point in the search space.
167174
best_x = process.create() # record for best-so-far solution
@@ -190,6 +197,9 @@ def _fea_map(process: Process, op0: Callable, op1: Callable) -> None:
190197
best_f = new_f # Store its objective value.
191198
best_x, new_x = new_x, best_x # Swap best and new.
192199

200+
if not log_h_tbl:
201+
return
202+
193203
# After we are done, we want to print the H table.
194204
if h[best_f] == 0: # Fix the H table for the case that only one
195205
h[best_f] = 1 # single FE was performed.
@@ -224,14 +234,19 @@ class FEA1plus1(Algorithm1):
224234
in module :mod:`~moptipy.algorithms.so.fitnesses.ffa`.
225235
"""
226236

227-
def __init__(self, op0: Op0, op1: Op1) -> None:
237+
def __init__(self, op0: Op0, op1: Op1, log_h_tbl: bool = True) -> None:
228238
"""
229239
Create the (1+1)-FEA.
230240
231241
:param op0: the nullary search operator
232242
:param op1: the unary search operator
243+
:param log_h_tbl: should we log the H table?
233244
"""
234245
super().__init__("fea1p1", op0, op1)
246+
if not isinstance(log_h_tbl, bool):
247+
raise type_error(log_h_tbl, "log_h_tbl", bool)
248+
#: True if we should log the H table, False otherwise
249+
self.log_h_tbl: Final[bool] = log_h_tbl
235250

236251
def solve(self, process: Process) -> None:
237252
"""
@@ -244,9 +259,10 @@ def solve(self, process: Process) -> None:
244259
ub: Final[int | float] = process.upper_bound()
245260
if isinstance(ub, int) and isinstance(lb, int) \
246261
and ((ub - lb) <= SWITCH_TO_MAP_RANGE):
247-
_fea_flat(process, self.op0.op0, self.op1.op1, lb, ub)
262+
_fea_flat(process, self.op0.op0, self.op1.op1, lb, ub,
263+
self.log_h_tbl)
248264
return
249-
_fea_map(process, self.op0.op0, self.op1.op1)
265+
_fea_map(process, self.op0.op0, self.op1.op1, self.log_h_tbl)
250266

251267

252268
def __h_to_str(indices: Iterable[int | float],

moptipy/algorithms/so/fitnesses/ffa.py

+34-19
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import numpy as np
6060
from numpy.random import Generator
6161
from pycommons.strings.string_conv import num_to_str
62+
from pycommons.types import type_error
6263

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

78-
def __new__(cls, f: Objective) -> "FFA":
79+
def __new__(cls, f: Objective, log_h_tbl: bool = True) -> "FFA":
7980
"""
8081
Create the frequency fitness assignment mapping.
8182
8283
:param f: the objective function
8384
"""
85+
if not isinstance(log_h_tbl, bool):
86+
raise type_error(log_h_tbl, "log_h_tbl", bool)
8487
check_objective(f)
8588
if f.is_always_integer():
8689
lb: Final[int | float] = f.lower_bound()
8790
ub: Final[int | float] = f.upper_bound()
8891
if isinstance(ub, int) and isinstance(lb, int) \
8992
and ((ub - lb) <= SWITCH_TO_MAP_RANGE):
9093
if 0 <= lb <= SWITCH_TO_OFFSET_LB:
91-
return _IntFFA1.__new__(_IntFFA1, cast(int, ub))
94+
return _IntFFA1.__new__(_IntFFA1, cast(int, ub), log_h_tbl)
9295
return _IntFFA2.__new__(_IntFFA2, cast(int, lb),
93-
cast(int, ub))
94-
return _DictFFA.__new__(_DictFFA)
96+
cast(int, ub), log_h_tbl)
97+
return _DictFFA.__new__(_DictFFA, log_h_tbl)
9598

9699
def __str__(self):
97100
"""
@@ -110,19 +113,23 @@ class _IntFFA1(FFA):
110113
__h: np.ndarray
111114
#: is this the first iteration?
112115
__first: bool
116+
#: log the h table?
117+
__log_h_tbl: bool
113118

114-
def __new__(cls, ub: int) -> "_IntFFA1":
119+
def __new__(cls, ub: int, log_h_tbl: bool = True) -> "_IntFFA1":
115120
"""Initialize the pure integer FFA."""
116121
instance = object.__new__(_IntFFA1)
117122
instance.__h = np.zeros(ub + 1, dtype=DEFAULT_INT)
118123
instance.__first = True
124+
instance.__log_h_tbl = log_h_tbl
119125
return instance
120126

121127
def log_information_after_run(self, process: Process) -> None:
122128
"""Write the H table."""
123-
log_h(process, range(len(self.__h)),
124-
cast(Callable[[int | float], int], self.__h.__getitem__),
125-
str)
129+
if self.__log_h_tbl:
130+
log_h(process, range(len(self.__h)),
131+
cast(Callable[[int | float], int], self.__h.__getitem__),
132+
str)
126133

127134
def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
128135
"""
@@ -131,7 +138,7 @@ def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
131138
:param p: the list of records
132139
:param random: ignored
133140
134-
>>> fit = _IntFFA1(100)
141+
>>> fit = _IntFFA1(100, False)
135142
>>> a = FRecord(None, 1)
136143
>>> b = FRecord(None, 2)
137144
>>> c = FRecord(None, 2)
@@ -201,20 +208,24 @@ class _IntFFA2(FFA):
201208
__lb: int
202209
#: is this the first iteration?
203210
__first: bool
211+
#: log the h table?
212+
__log_h_tbl: bool
204213

205-
def __new__(cls, lb: int, ub: int) -> "_IntFFA2":
214+
def __new__(cls, lb: int, ub: int, log_h_tbl: bool = True) -> "_IntFFA2":
206215
"""Initialize the pure integer FFA."""
207216
instance = object.__new__(_IntFFA2)
208217
instance.__h = np.zeros(ub - lb + 1, dtype=DEFAULT_INT)
209218
instance.__lb = lb
210219
instance.__first = True
220+
instance.__log_h_tbl = log_h_tbl
211221
return instance
212222

213223
def log_information_after_run(self, process: Process) -> None:
214224
"""Write the H table."""
215-
log_h(process, range(len(self.__h)),
216-
cast(Callable[[int | float], int], self.__h.__getitem__),
217-
lambda i: str(i + self.__lb))
225+
if self.__log_h_tbl:
226+
log_h(process, range(len(self.__h)),
227+
cast(Callable[[int | float], int], self.__h.__getitem__),
228+
lambda i: str(i + self.__lb))
218229

219230
def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
220231
"""
@@ -223,7 +234,7 @@ def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
223234
:param p: the list of records
224235
:param random: ignored
225236
226-
>>> fit = _IntFFA2(-10, 100)
237+
>>> fit = _IntFFA2(-10, 100, False)
227238
>>> a = FRecord(None, -1)
228239
>>> b = FRecord(None, 2)
229240
>>> c = FRecord(None, 2)
@@ -296,19 +307,23 @@ class _DictFFA(FFA):
296307
__h: Counter[int | float]
297308
#: is this the first iteration?
298309
__first: bool
310+
#: log the h table?
311+
__log_h_tbl: bool
299312

300-
def __new__(cls) -> "_DictFFA":
313+
def __new__(cls, log_h_tbl: bool = True) -> "_DictFFA":
301314
"""Initialize the pure integer FFA."""
302315
instance = object.__new__(_DictFFA)
303316
instance.__h = Counter()
304317
instance.__first = True
318+
instance.__log_h_tbl = log_h_tbl
305319
return instance
306320

307321
def log_information_after_run(self, process: Process) -> None:
308322
"""Write the H table."""
309-
log_h(process, cast(Iterable[int | float], self.__h.keys()),
310-
cast(Callable[[int | float], int], self.__h.__getitem__),
311-
num_to_str)
323+
if self.__log_h_tbl:
324+
log_h(process, cast(Iterable[int | float], self.__h.keys()),
325+
cast(Callable[[int | float], int], self.__h.__getitem__),
326+
num_to_str)
312327

313328
def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
314329
"""
@@ -317,7 +332,7 @@ def assign_fitness(self, p: list[FRecord], random: Generator) -> None:
317332
:param p: the list of records
318333
:param random: ignored
319334
320-
>>> fit = _DictFFA()
335+
>>> fit = _DictFFA(False)
321336
>>> a = FRecord(None, -1)
322337
>>> b = FRecord(None, 2.9)
323338
>>> c = FRecord(None, 2.9)

moptipy/evaluation/log_parser.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
"""
2323

2424
from math import inf, isfinite, isinf
25-
from os import listdir
2625
from typing import Final
2726

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

422421
do_files = True
423422
do_dirs = True
424-
for subpath in listdir(folder):
425-
sub: Path = folder.resolve_inside(subpath)
423+
for sub in folder.list_dir():
426424
if sub.is_file():
427425
if do_files and (not self.parse_file(sub)):
428426
logger(f"will parse no more files in {folder!r}.")

moptipy/utils/sys_info.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ def __cpu_affinity(proc: psutil.Process | None = None) -> str | None:
4646

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

5455

5556
def is_make_build() -> bool:
@@ -351,6 +352,7 @@ def get_sys_info() -> str:
351352
session.workingDirectory
352353
session.ipAddress
353354
version.Pillow
355+
version.cmaes
354356
version.contourpy
355357
version.cycler
356358
version.fonttools
@@ -365,10 +367,12 @@ def get_sys_info() -> str:
365367
version.packaging
366368
version.pdfo
367369
version.psutil
370+
version.pycommons
368371
version.pyparsing
369372
version.pythondateutil
370373
version.scikitlearn
371374
version.scipy
375+
version.setuptools
372376
version.six
373377
version.threadpoolctl
374378
hardware.machine

moptipy/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
from typing import Final
33

44
#: the version string of `moptipy`
5-
__version__: Final[str] = "0.9.105"
5+
__version__: Final[str] = "0.9.106"

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ psutil == 5.9.5
3939

4040
# pycommons offers many of the tools and utilities used in moptipy that are
4141
# not related to optimization.
42-
pycommons == 0.8.14
42+
pycommons == 0.8.21
4343

4444
# scikit-learn is used to obtain some clusters of JSSP instances for our
4545
# experiments.

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ install_requires =
8383
matplotlib >= 3.8.0
8484
pdfo >= 1.3.1
8585
psutil >= 5.9.5
86-
pycommons >= 0.8.14
86+
pycommons >= 0.8.21
8787
scikit-learn >= 1.3.1
8888
scipy >= 1.11.3
8989
packages = find:

0 commit comments

Comments
 (0)