Source code for QHyper.optimizers.base

# This work was supported by the EuroHPC PL infrastructure funded at the
# Smart Growth Operational Programme (2014-2020), Measure 4.2
# under the grant agreement no. POIR.04.02.00-00-D014/20-00


import abc
import dataclasses

from abc import abstractmethod

from typing import Callable, Self


[docs] @dataclasses.dataclass class OptimizationParameter: """ Dataclass for storing bounds, steps and init values for parameters that might be optimized. Most of the time some of this values are not required, but it depends on the chosen optimization algorithm. Check the documentation of the chosen algorithm to see which values are required. Attributes ---------- min : list[float] List of minimum values for each parameter. max : list[float] List of maximum values for each parameter. step : list[float] List of step values for each parameter. Used for example in the grid search algorithm. For 0-th parameter the following values will be generated: min[0], min[0] + step[0], min[0] + 2*step[0], ... init : list[float] List of initial values for each parameter. Some algorithms require starting point to be set. """ min: list[float] = dataclasses.field(default_factory=list) max: list[float] = dataclasses.field(default_factory=list) step: list[float] = dataclasses.field(default_factory=list) init: list[float] = dataclasses.field(default_factory=list) def __post_init__(self) -> None: self.min = list(self.min) self.max = list(self.max) self.step = list(self.step) self.init = list(self.init)
[docs] def assert_bounds(self) -> None: """Check if bounds are correctly set. """ if not self.min: raise ValueError("Min bounds are required") if not self.max: raise ValueError("Max bounds are required") if len(self.min) != len(self.max): raise ValueError("Min and Max bounds must have the same length")
[docs] def assert_step(self) -> None: """Check if steps are correctly set. """ self.assert_bounds() if len(self.step) == 0: raise ValueError("Steps are required") if len(self.min) != len(self.step): raise ValueError("Steps must have the same length as bounds")
[docs] def assert_init(self) -> None: """Check if init values are correctly set. """ if len(self.init) == 0: raise ValueError("Init are required")
[docs] def assert_bounds_init(self) -> None: """Check if bounds and init values are correctly set. """ self.assert_bounds() self.assert_init() if len(self.min) != len(self.init): raise ValueError("Init must have the same length as bounds")
def __add__(self, other: Self) -> 'OptimizationParameter': min_ = self.min + other.min max_ = self.max + other.max step_ = self.step + other.step init_ = self.init + other.init return OptimizationParameter(min_, max_, step_, init_) def __len__(self) -> int: if self.min: return len(self.min) if self.max: return len(self.max) if self.init: return len(self.init) return 0 def update(self, min: list[float] | None = None, max: list[float] | None = None, step: list[float] | None = None, init: list[float] | None = None) -> 'OptimizationParameter': if min is None: min = self.min.copy() if max is None: max = self.max.copy() if step is None: step = self.step.copy() if init is None: init = self.init.copy() return OptimizationParameter(min, max, step, init) @property def bounds(self) -> list[tuple[float, float]]: return list(zip(self.min, self.max))
class OptimizerError(Exception): """ Base class for exceptions in this module. """ ...
[docs] @dataclasses.dataclass class OptimizationResult: """ Dataclass for storing the results of an optimization run. Attributes ---------- value : float The minimum function value found by the optimization algorithm. params : list[float] The optimal point (function arguments) found by the optimization algorithm. history : list[list[OptimizationResult]] The history of the optimization algorithm. Each element of the list represents the results of the objective function at each iteration - there can be multiple results per each iteration (epoch). """ value: float params: list[float] history: list[list['OptimizationResult']] = dataclasses.field( default_factory=list)
[docs] class Optimizer(abc.ABC): """ Base class for Optimizer. """
[docs] @abstractmethod def minimize( self, func: Callable[[list[float]], OptimizationResult], init: OptimizationParameter ) -> OptimizationResult: """ Method that minimizes the given function using the implemented optimization algorithm. This method has to be implemented by the subclass. Parameters ---------- func : Callable[[list[float]], OptimizationResult] The objective function to be minimized. init : OptimizationParameter The initial parameter for the optimization algorithm. The required fields are defined by subclass. Returns ------- OptimizationResult Result contains the minimum function value, the corresponding optimal point, and the history of the optimization. """