Stoppers
********


Abstract Base Stopper
=====================

class BaseStopper

   Base stopper from which all stoppers must inherit to be compatible
   with GloMPO.

   Attributes:
   logger : logging.Logger
      "logging.Logger" instance into which status messages may be
      added.

   abstract __call__(log, best_opt_id, tested_opt_id)

      Compares two optimizers and returns if one should be terminated
      according to some condition. When called, this method may check
      any values within the logs of both the best and the tested
      optimizer.

      Parameters:
      log
         Instance of "BaseLogger" that contains the iteration history
         of every optimizer.

      best_opt_id
         ID number of the optimizer which has found the lowest
         function value.

      tested_opt_id
         ID number of the optimizer which the Stopper is testing to
         stop or not.

      Returns:
      bool
         "True" if the condition in the method is met, "False"
         otherwise.

      Notes:
      For proper functionality, the result of this method must be
      saved to "last_result" before returning.

   __iter__()

      Provides a (flattened) iteration through all the bases which
      comprise the combined stopper/exitcondition

   __str__()

      Produces a string of the stopper/exit condition's name and
      configuration.

   property last_result

      The result of the last "__call__()".

   reset()

      Clears previous evaluation result to avoid misleading printing.
      Resets "last_result" to "None". Given that stoppers and
      exitconditions are evaluated lazily, it is possible for
      misleading results to be returned by "str_with_result()"
      indicating a stopper/exit condition has been evaluated when it
      has not. Bases are thus reset before "__call__()" to prevent
      this.

   str_with_result()

      String representation of the object with its result.


Combining Base Stoppers
=======================

"BaseStopper" is based on the same structure as "BaseExitCondition".
Thus, simple conditions can also be combined into more sophisticated
termination conditions. See Combining Base ExitConditions.


Included Stoppers
=================

For convenience, GloMPO comes bundled with several simple stoppers
already included.

class BestFunctionValueUnmoving(calls, tol=0)

   Bases: "BaseStopper"

   Considers the lowest function value seen by the optimizer thus far.
   Returns "True" if the tested optimizer's best value has not changed
   significantly in a given amount of time.

   Parameters:
   calls
      Number of function evaluations between comparison points.

   tol
      Tolerance fraction between 0 and 1.

   Returns:
   bool
      "True" if:

         abs(latest_f_value - f_value_calls_ago) <= abs(f_value_calls_ago * self.tol)

class CurrentFunctionValueUnmoving(calls, tol=0)

   Bases: "BaseStopper"

   Considers function values the optimizers are currently exploring.
   Used to terminate an optimizer when its function evaluations are
   unchanging, usually indicating that it is approaching some
   convergence. Best used with a stopper which monitors step size to
   ensure a widely exploring optimizer is not stopped.

   Parameters:
   calls
      Number of function evaluations between comparison points.

   tol
      Tolerance fraction between 0 and 1.

   Returns:
   bool
      Returns "True" if the standard deviation of the last "calls"
      function evaluations is below "tol * abs(latest_f_eval)".

class MaxFunctionCalls(min_pts)

   Bases: "BaseStopper"

   Keeps an optimizer alive for a minimum number of function
   evaluations.

   Parameters:
   min_pts
      Minimum number of points for which an optimizer should be kept
      alive.

   Returns:
   bool
      Returns "True" after the function has been evaluated at least
      "min_pts" times.

class MaxInteroptimizerDistance(bounds, relative_distance, test_all=False)

   Bases: "BaseStopper"

   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
      tested optimizer will be stopped.

   test_all
      If "True" the distance between the tested optimizer and all
      other optimizers is tested, else only the best and tested
      optimizers are compared.

   Returns:
   bool
      Returns "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
   ("max_distance"). Calculates the Euclidean distance between the
   final iterations of the best and tested optimizers
   ("inter_optimizer_distance"). Returns "True" if:

      inter_optimizer_distance <= max_distance * relative_distance

   Caution:

     Use this stopper 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.

class MaxSequentialInvalidPoints(n_iters=1)

   Bases: "BaseStopper"

   Checks for non-numerical solutions. Some pathological functions may
   have undefined regions within them or combinations of parameters
   which return non-finite results.

   Parameters:
   n_iters
      Number of allowed invalid function evaluations.

   Returns:
   bool
      Returns "True" if the optimizer fails to find a valid function
      evaluation in the last "n_iters" function evaluations.

class MinStepSize(bounds, calls, relative_tol=0.05)

   Bases: "BaseStopper"

   Monitors distance in parameter space between function evaluations.
   This stopper will stop an optimizer that is excessively focused on
   one area of parameter space.

   Parameters:
   bounds
      Bounds of each parameter.

   calls
      Number of function evaluations over which to perform the
      averaging.

   relative_tol
      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
      tested optimizer will be stopped.

   Returns:
   bool
      "True" if the tested optimizer's average step size over the last
      "calls" function evaluations is less than:

         relative_tol * maximum_parameter_space_distance

class OptimizerType(opt_to_stop)

   Bases: "BaseStopper"

   Stops an optimizer based on its class. Intended for use with other
   stoppers to allow for specific stopping conditions based on the
   type of optimizer.

   Parameters:
   opt_to_stop
      "BaseOptimizer" class which is targeted.

   Returns:
   bool
      "True" if the tested optimizer is an instance of "opt_to_stop".

   Examples:
   >>> OptimizerType(CMAOptimizer) & StopperA() | OptimizerType(Nevergrad) & StopperB()

   In this case "StopperA" will only stop "CMAOptimizer"s and
   "StopperB" will only stop
   "scm.glompo.optimizers.nevergrad.Nevergrad" optimizers. This is
   useful in cases where exploratory optimizers should be stopped
   quickly but late stage optimizers encouraged to converge and
   iterate for longer periods.

class TimeAnnealing(crit_ratio=1)

   Bases: "BaseStopper"

   Keeps optimizers alive based on how long they have been alive.
   Randomly keeps optimizers alive based on how long (in function
   evaluations) they have been active. The newer an optimizer is, the
   more likely it will pass the test and be kept alive. Used to temper
   very strict termination conditions.

   Parameters:
   crit_ratio
      Critical ratio of function calls between best and tested
      optimizers above which the tested optimizer is guaranteed to
      survive. Values lower than one are looser and allow the tested
      optimizer to survive even if it has been in operation longer
      than the best optimizer. Values higher than one are stricter and
      may stop the tested optimizer even if it has iterated fewer
      times than the best optimizer.

   Returns:
   bool
      "True" if an optimizer has been alive long enough and fails a
      comparison test with a uniformly randomly generated number.

   Notes:
   This condition calculates the quotient ("num_bet_fcalls /
   num_tested_fcalls"). A random number is then generated between zero
   and "crit_ratio". Only if the quotient is larger than this number
   does the tested optimizer remains alive.

class ValidationWorsening(calls=1, tol=0)

   Bases: "BaseStopper"

   Considers loss function values of the validation set. Triggers if
   the value of the last evaluation of the validation set cost
   function is worse than the average of the last several calls.

   Parameters:
   calls
      Number of function evaluations between comparison points.
      Increase this number to capture long-term increases in the loss
      value change rather than sudden spurious fluctuations.

   tol
      Tolerance fraction applied to the loss values made "calls"
      prior. The loss value (f) is modified by: f * (1 + "tol"). This
      can also be used to ignore the effects of mild fluctuations.

   Returns:
   bool
      Returns "True" if the loss value of the last validation set
      evaluation is worse than the average of the "calls" evaluations
      before.

class ValueAnnealing(crit_stop_chance=0.5)

   Bases: "BaseStopper"

   Keeps optimizers alive based on the function values they are
   exploring. This condition is unlikely to stop a tested optimizer
   which is very near the best (in terms of function value) but the
   probability of stopping increases with the difference between them.
   This condition can be applied in combination with others to prevent
   'competitive' optimizers for being stopped while still terminating
   poorly performing ones.

   The decision criteria follows an exponential distribution which
   corresponds to the probability of survival. Control of the
   probability can be achieved through the "crit_stop_chance"
   initialisation criteria.

   Parameters:
   crit_stop_chance: float
      The probability of stopping a tested optimizer which is twice as
      large as the best optimizer in absolute value. The default is
      50%.

   Returns:
   bool
      "True" if the difference between the explored function values of
      the best and tested optimizer fails a comparison test with a
      uniformly randomly generated number.
