Source code for glompo.hunters.parameterdistance

from typing import Sequence, Tuple

import numpy as np

from .basehunter import BaseHunter
from ..common.helpers import distance, is_bounds_valid
from ..core.optimizerlogger import BaseLogger

__all__ = ("ParameterDistance",)


[docs]class ParameterDistance(BaseHunter): """ Terminates optimizers which are too close in parameter space. Parameters ---------- bounds Bounds of each parameter. relative_distance Fraction (between 0 and 1) of the maximum distance in the space (from the point at all lower bounds to the point at all upper bounds) below which the optimizers are deemed too close and the victim will be killed. test_all If :obj:`True` the distance between victim and all other optimizers is tested, else only the hunter and victim are compared. Returns ------- bool Returns :obj:`True` if optimizers are calculated to be too close together. Notes ----- Calculates the maximum Euclidean distance in parameter space between the point of lower bounds and the point of upper bounds (:code:`max_distance`). Calculates the Euclidean distance between the final iterations of the hunter and victim (:code:`inter_optimizer_distance`). Returns :obj:`True` if:: inter_optimizer_distance <= max_distance * relative_distance .. caution:: Use this hunter with care and only in problems where the parameters have been standardised so that every parameter is dimensionless and on the same order of magnitude. """ def __init__(self, bounds: Sequence[Tuple[float, float]], relative_distance: float, test_all: bool = False): super().__init__() if isinstance(relative_distance, (float, int)) and relative_distance > 0: self.relative_distance = relative_distance else: raise ValueError("relative_distance should be a positive float.") if is_bounds_valid(bounds): lower_pt, upper_pt = tuple(np.transpose(bounds)) self.trans_space_dist = distance(lower_pt, upper_pt) self.test_all = test_all def __call__(self, log: BaseLogger, hunter_opt_id: int, victim_opt_id: int) -> bool: if self.test_all: compare_to = range(1, log.n_optimizers + 1) else: compare_to = [hunter_opt_id] for opt_id in compare_to: if opt_id != victim_opt_id: try: h1 = np.array(log.get_history(opt_id, 'x')[-1]) except IndexError: self.logger.debug("Unable to compare to Opt%d, no points in log", opt_id) continue v1 = np.array(log.get_history(victim_opt_id, 'x')[-1]) opt_dist = distance(h1, v1) ratio = opt_dist / self.trans_space_dist self.last_result = ratio <= self.relative_distance if self.last_result: self.logger.debug("ParameterDistance: Hunter=%d, Victim=%d, " "Result=%.2f / %.2f <= %.2f} = " "%s.", opt_id, victim_opt_id, opt_dist, self.trans_space_dist, self.relative_distance, self.last_result) return self.last_result self.last_result = False return self.last_result