Source code for alogos._optimization.shared.evaluation

import functools as _functools
from numbers import Number as _Number


[docs]def default_evaluator(function, args): """Evaluate a given function on each item of a list.""" return [function(x) for x in args]
[docs]@_functools.lru_cache(2) def get_robust_fitness_function(objective_function, objective): """Wrap a given function to make it robust.""" default_fitness = float("+Inf") if objective == "min" else float("-Inf") default_details = None return RobustCallable(objective_function, default_fitness, default_details)
[docs]class RobustCallable: """Wrapper to make a function robust against failure and invalid return values. It provides following guarantees: - The return value is a tuple with two entries: ``(fitness, details)`` - ``fitness`` is ensured to be of type float, allowing +Inf and -Inf, but excluding NaN. - ``details`` can be any user-chosen type. Notes ----- - Simply wrapping the function into another function would cause pickling errors. - If the robust fitness function cannot be pickled, most parallel and distributed evaluation solutions cannot be applied since they rely on some pickling function. References ---------- - http://gael-varoquaux.info/programming/decoration-in-python-done-right-decorating-and-pickling.html """
[docs] def __init__(self, func, def_fit, def_det): """Create a robust callable object from a given function.""" self.func = func self.def_fit = def_fit self.def_det = def_det
def __call__(self, phe): """Make the object callable.""" # 1) Invalid phenotype if phe is None: fit = self.def_fit det = self.def_det # 2) Valid phenotype else: try: # Objective function evaluation result = self.func(phe) # Result may be a fitness value or a tuple (fitness, details) try: fit, det = result except Exception: fit = result det = self.def_det # Ensure fitness is a comparable number (includes +Inf and -Inf, excludes NaN) if not isinstance(fit, _Number): message = "Returned fitness value is not a number: {}".format(fit) raise ValueError(message) if fit != fit: message = ( "Returned fitness value is NaN. It is replaced by the " "default fitness value {}".format(self.def_fit) ) raise ValueError(message) # Ensure fitness is a float (e.g. not Numpy type), can be stored in a database fit = float(fit) except Exception as excp: fit = self.def_fit exception_text = "{}: {}".format(type(excp).__name__, str(excp)) det = exception_text return fit, det