Coverage for moptipy / api / _mo_process_ss_log.py: 92%
65 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
1"""A multi-objective process with solution space and logging."""
2from typing import Final
4import numpy as np
5from numpy import copyto
6from pycommons.io.path import Path
7from pycommons.types import type_error
9from moptipy.api._mo_process_ss import _MOProcessSS
10from moptipy.api._process_base import _TIME_IN_NS, _check_log_time
11from moptipy.api.algorithm import Algorithm
12from moptipy.api.encoding import Encoding
13from moptipy.api.mo_archive import MOArchivePruner
14from moptipy.api.mo_problem import MOProblem
15from moptipy.api.space import Space
16from moptipy.utils.logger import Logger
19class _MOProcessSSLog(_MOProcessSS):
20 """A multi-objective process with solution space and logging."""
22 def __init__(self,
23 solution_space: Space,
24 objective: MOProblem,
25 algorithm: Algorithm,
26 pruner: MOArchivePruner,
27 archive_max_size: int,
28 archive_prune_limit: int,
29 log_file: Path | None = None,
30 search_space: Space | None = None,
31 encoding: Encoding | None = None,
32 rand_seed: int | None = None,
33 max_fes: int | None = None,
34 max_time_millis: int | None = None,
35 goal_f: int | float | None = None,
36 log_all_fes: bool = False) -> None:
37 """
38 Perform the internal initialization. Do not call directly.
40 :param solution_space: the search- and solution space.
41 :param objective: the objective function
42 :param algorithm: the optimization algorithm
43 :param pruner: the archive pruner
44 :param archive_max_size: the maximum archive size after pruning
45 :param archive_prune_limit: the archive size above which pruning will
46 be performed
47 :param log_file: the optional log file
48 :param search_space: the search space.
49 :param encoding: the encoding
50 :param rand_seed: the optional random seed
51 :param max_fes: the maximum permitted function evaluations
52 :param max_time_millis: the maximum runtime in milliseconds
53 :param goal_f: the goal objective value. if it is reached, the process
54 is terminated
55 :param log_all_fes: should we log all FEs?
56 """
57 super().__init__(solution_space=solution_space,
58 objective=objective,
59 algorithm=algorithm,
60 pruner=pruner,
61 archive_max_size=archive_max_size,
62 archive_prune_limit=archive_prune_limit,
63 log_file=log_file,
64 search_space=search_space,
65 encoding=encoding,
66 rand_seed=rand_seed,
67 max_fes=max_fes,
68 max_time_millis=max_time_millis,
69 goal_f=goal_f)
70 if not isinstance(log_file, str):
71 raise type_error(log_file, "log_file", str)
72 if not isinstance(log_all_fes, bool):
73 raise type_error(log_all_fes, "log_all_fes", bool)
75 #: `True` if all FEs are logged, `False` to only log improvements.
76 self.__log_all: Final[bool] = log_all_fes
77 #: The in-memory log
78 self.__log: list[list[int | float | np.ndarray]] = []
79 #: the quick access to the log appending method
80 self.__log_append = self.__log.append
82 def f_evaluate(self, x, fs: np.ndarray) -> float | int:
83 if self._terminated:
84 if self._knows_that_terminated:
85 raise ValueError("The process has been terminated and "
86 "the algorithm knows it.")
87 return self._current_best_f
89 current_y: Final = self._current_y
90 self._g(x, current_y)
91 result: Final[int | float] = self._f_evaluate(current_y, fs)
92 self._current_fes = current_fes = self._current_fes + 1
93 do_term: bool = current_fes >= self._end_fes
94 do_log: bool = self.__log_all
95 ctn: int = 0
97 improved: bool = False
98 if result < self._current_best_f:
99 improved = True
100 self._current_best_f = result
101 copyto(self._current_best_fs, fs)
102 self.copy(self._current_best_x, x)
103 self._current_y = self._current_best_y
104 self._current_best_y = current_y
105 do_term = do_term or (result <= self._end_f)
107 if self.check_in(x, fs, True) or improved:
108 self._last_improvement_fe = current_fes
109 self._current_time_nanos = ctn = _TIME_IN_NS()
110 self._last_improvement_time_nanos = ctn
111 do_log = True
113 if do_log:
114 if ctn <= 0:
115 self._current_time_nanos = ctn = _TIME_IN_NS()
116 self.__log_append([current_fes, ctn, result, fs.copy()])
118 if do_term:
119 self.terminate()
121 return result
123 def _check_timing(self) -> None:
124 super()._check_timing()
125 _check_log_time(self._start_time_nanos, self._current_time_nanos,
126 self.__log)
128 def _write_log(self, logger: Logger) -> None:
129 self._write_mo_log(self.__log, self._start_time_nanos,
130 self.__log_all, logger)
131 del self.__log
132 super()._write_log(logger)
134 def __str__(self) -> str:
135 return "MOLoggingProcessWithSearchSpace"