Source code for deephyper.stopper._median_stopper

from numbers import Number

import numpy as np

from deephyper.stopper._stopper import Stopper


[docs]class MedianStopper(Stopper): """Stopper based on the median of observed objectives at similar budgets. .. list-table:: :widths: 25 25 25 :header-rows: 1 * - Single-Objective - Multi-Objectives - Failures * - ✅ - ❌ - ❌ """ def __init__( self, max_steps: int, min_steps: int = 1, min_competing: int = 0, min_fully_completed: int = 0, interval_steps: int = 1, epsilon: float = 1e-10, ) -> None: super().__init__(max_steps=max_steps) self.min_steps = min_steps self._min_competing = min_competing self._min_fully_completed = min_fully_completed self._interval_steps = interval_steps self.epsilon = epsilon self._rung = 0 def _is_halting_budget(self): if self.step < self.min_steps: return False else: return (self.step - self.min_steps) % self._interval_steps == 0 def _get_competiting_objectives(self) -> list: search_id, _ = self.job.id.split(".") values = self.job.storage.load_metadata_from_all_jobs( search_id, f"_completed_rung_{self._rung}" ) # Filter out non numerical values (e.g., "F" for failed jobs) values = [v for v in values if isinstance(v, Number)] return values def _num_fully_completed(self) -> int: search_id, _ = self.job.id.split(".") stopped = self.job.storage.load_metadata_from_all_jobs(search_id, "_completed") num = sum(int(s) for s in stopped) return num
[docs] def observe(self, budget: float, objective: float): super().observe(budget, objective) self._budget = self.observed_budgets[-1] self._objective = self.observed_objectives[-1] if self._is_halting_budget(): # casting float to str to avoid numerical rounding of database # e.g. for Redis: The precision of the output is fixed at 17 digits # after the decimal point regardless of the actual internal precision # of the computation. self.job.storage.store_job_metadata( self.job.id, f"_completed_rung_{self._rung}", self._objective )
[docs] def stop(self) -> bool: # Enforce Pre-conditions if super().stop(): return True if not (self._is_halting_budget()): return False if ( self._min_fully_completed > 0 and self._num_fully_completed() < self._min_fully_completed ): return False # Apply Median Pruning competing_objectives = np.sort(self._get_competiting_objectives()) num_competing = len(competing_objectives) if num_competing < self._min_competing: return False median_objective = np.median(competing_objectives) promotable = self._objective + self.epsilon >= median_objective if promotable: self._rung += 1 return not (promotable)