Coverage for moptipy / api / _process_ss_log.py: 91%
86 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 process with logging and different search and solution space."""
2from typing import Final
4from pycommons.io.path import Path
5from pycommons.types import type_error
7from moptipy.api._process_base import _TIME_IN_NS, _check_log_time
8from moptipy.api._process_no_ss import _write_log
9from moptipy.api._process_ss import _ProcessSS
10from moptipy.api.algorithm import Algorithm
11from moptipy.api.encoding import Encoding
12from moptipy.api.objective import Objective
13from moptipy.api.space import Space
14from moptipy.utils.logger import Logger
17class _ProcessSSLog(_ProcessSS):
18 """A process with logging and different search and solution space."""
20 def __init__(self,
21 solution_space: Space,
22 objective: Objective,
23 algorithm: Algorithm,
24 log_file: Path,
25 search_space: Space | None = None,
26 encoding: Encoding | None = None,
27 rand_seed: int | None = None,
28 max_fes: int | None = None,
29 max_time_millis: int | None = None,
30 goal_f: int | float | None = None,
31 log_all_fes: bool = False) -> None:
32 """
33 Perform the internal initialization. Do not call directly.
35 :param solution_space: the solution space.
36 :param objective: the objective function
37 :param algorithm: the optimization algorithm
38 :param search_space: the search space.
39 :param encoding: the encoding
40 :param log_file: the optional log file
41 :param rand_seed: the optional random seed
42 :param max_fes: the maximum permitted function evaluations
43 :param max_time_millis: the maximum runtime in milliseconds
44 :param goal_f: the goal objective value. if it is reached, the
45 process is terminated
46 :param log_all_fes: should every single FE be logged?
47 """
48 super().__init__(solution_space=solution_space,
49 objective=objective,
50 algorithm=algorithm,
51 log_file=log_file,
52 search_space=search_space,
53 encoding=encoding,
54 rand_seed=rand_seed,
55 max_fes=max_fes,
56 max_time_millis=max_time_millis,
57 goal_f=goal_f)
58 if not isinstance(log_file, str):
59 raise type_error(log_file, "log_file", str)
60 if not isinstance(log_all_fes, bool):
61 raise type_error(log_all_fes, "log_all_fes", bool)
62 #: `True` if all FEs are logged, `False` to only log improvements.
63 self.__log_all: Final[bool] = log_all_fes
64 #: The in-memory log
65 self.__log: list[list[int | float]] = []
66 #: the quick access to the log appending method
67 self.__log_append = self.__log.append
69 def evaluate(self, x) -> float | int:
70 if self._terminated:
71 if self._knows_that_terminated:
72 raise ValueError("The process has been terminated and the "
73 "algorithm knows it.")
74 return self._current_best_f
76 current_y: Final = self._current_y
77 self._g(x, current_y)
78 result: Final[int | float] = self._f(current_y)
79 self._current_fes = current_fes = self._current_fes + 1
80 do_term: bool = current_fes >= self._end_fes
81 do_log: bool = self.__log_all
82 ctn: int = 0
84 if result < self._current_best_f:
85 self._last_improvement_fe = current_fes
86 self._current_best_f = result
87 self.copy(self._current_best_x, x)
88 self._current_y = self._current_best_y
89 self._current_best_y = current_y
90 self._current_time_nanos = ctn = _TIME_IN_NS()
91 self._last_improvement_time_nanos = ctn
92 do_term = do_term or (result <= self._end_f)
93 do_log = True
95 if do_log:
96 if ctn <= 0:
97 self._current_time_nanos = ctn = _TIME_IN_NS()
98 self.__log_append([current_fes, ctn, result])
100 if do_term:
101 self.terminate()
103 return result
105 def register(self, x, f: int | float) -> None:
106 if self._terminated:
107 if self._knows_that_terminated:
108 raise ValueError("The process has been terminated and the "
109 "algorithm knows it.")
110 return
112 self._current_fes = current_fes = self._current_fes + 1
113 do_term: bool = current_fes >= self._end_fes
114 do_log: bool = self.__log_all
115 ctn: int = 0
117 if f < self._current_best_f:
118 self._last_improvement_fe = current_fes
119 self._current_best_f = f
120 self.copy(self._current_best_x, x)
121 current_y: Final = self._current_y
122 self._g(x, current_y)
123 self._current_y = self._current_best_y
124 self._current_best_y = current_y
125 self._current_time_nanos = ctn = _TIME_IN_NS()
126 self._last_improvement_time_nanos = ctn
127 do_term = do_term or (f <= self._end_f)
128 do_log = True
130 if do_log:
131 if ctn <= 0:
132 self._current_time_nanos = ctn = _TIME_IN_NS()
133 self.__log_append([current_fes, ctn, f])
135 if do_term:
136 self.terminate()
138 def _check_timing(self) -> None:
139 super()._check_timing()
140 _check_log_time(self._start_time_nanos, self._current_time_nanos,
141 self.__log)
143 def _write_log(self, logger: Logger) -> None:
144 _write_log(self.__log, self._start_time_nanos, logger)
145 del self.__log
146 super()._write_log(logger)
148 def __str__(self) -> str:
149 return "LoggingProcessWithSearchSpace"