Skip to content

Commit

Permalink
refcator GA for using FitnessEstimator
Browse files Browse the repository at this point in the history
  • Loading branch information
fonhorst committed Jun 3, 2024
1 parent 147c24e commit 8419511
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 28 deletions.
21 changes: 13 additions & 8 deletions autotm/algorithms_for_tuning/genetic_algorithm/ga.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ def __init__(
crossover_type="blend_crossover",
selection_type="fitness_prop",
elem_cross_prob=0.2,
num_fitness_evaluations: Optional[int] = 500,
early_stopping_iterations: Optional[int] = 500,
best_proc=0.3,
alpha=None,
Expand Down Expand Up @@ -123,8 +122,6 @@ def __init__(
self.selection = selection(selection_type)
self.elem_cross_prob = elem_cross_prob
self.alpha = alpha
self.evaluations_counter = 0
self.num_fitness_evaluations = num_fitness_evaluations
self.early_stopping_iterations = early_stopping_iterations
self.fitness_obj_type = fitness_obj_type
self.best_proc = best_proc
Expand Down Expand Up @@ -278,7 +275,9 @@ def apply_nelder_mead(self, starting_points_set, num_gen, num_iterations=2):
return new_population

def run(self, verbose=False, visualize_results=False) -> Individual:
self.evaluations_counter = 0
assert self.fitness_estimator.evaluations_counter == 0, \
"Fitness estimator has non-zero evaluations count and cannot be reused"

ftime = str(int(time.time()))

# os.makedirs(LOG_FILE_PATH, exist_ok=True)
Expand All @@ -288,7 +287,7 @@ def run(self, verbose=False, visualize_results=False) -> Individual:
logger.info(
f"ALGORITHM PARAMS number of individuals {self.num_individuals}; "
f"number of fitness evals "
f"{self.num_fitness_evaluations if self.num_fitness_evaluations else 'unlimited'}; "
f"{self.fitness_estimator.num_fitness_evaluations if self.fitness_estimator.num_fitness_evaluations else 'unlimited'}; "
f"number of early stopping iterations "
f"{self.early_stopping_iterations if self.early_stopping_iterations else 'unlimited'}; "
f"crossover prob {self.elem_cross_prob}"
Expand All @@ -310,7 +309,10 @@ def run(self, verbose=False, visualize_results=False) -> Individual:

self._sort_population(population)
if self.statistics_collector is not None:
self.statistics_collector.log_iteration(self.evaluations_counter, population[0].fitness_value)
self.statistics_collector.log_iteration(
self.fitness_estimator.evaluations_counter,
population[0].fitness_value
)
pairs_generator = self.selection(
population=population,
best_proc=self.best_proc,
Expand Down Expand Up @@ -401,7 +403,7 @@ def run(self, verbose=False, visualize_results=False) -> Individual:
)
population[i] = elem

if self.num_fitness_evaluations and self.evaluations_counter >= self.num_fitness_evaluations:
if self.fitness_estimator.num_fitness_evaluations and self.fitness_estimator.evaluations_counter >= self.fitness_estimator.num_fitness_evaluations:
self.metric_collector.save_fitness(
generation=ii,
params=[i.params for i in population],
Expand Down Expand Up @@ -483,7 +485,10 @@ def run(self, verbose=False, visualize_results=False) -> Individual:
self.metric_collector.save_trace()

if self.statistics_collector is not None:
self.statistics_collector.log_iteration(self.evaluations_counter, population[0].fitness_value)
self.statistics_collector.log_iteration(
self.fitness_estimator.evaluations_counter,
population[0].fitness_value
)
logger.info(f"Y: {y}")
best_individual = population[0]
ind = log_best_solution(self.ibuilder, best_individual, alg_args=" ".join(sys.argv))
Expand Down
39 changes: 19 additions & 20 deletions autotm/fitness/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,37 @@
logger = logging.getLogger(__name__)


class FitnessEstimator(ABC):
@abstractmethod
def fit(self, iter_num: int) -> None:
...

@abstractmethod
def estimate(self, iter_num: int, population: List[Individual]) -> List[Individual]:
...
class FitnessEstimator:
def __init__(self, num_fitness_evaluations: Optional[int] = None, statistics_collector: Optional[StatisticsCollector] = None):
self._num_fitness_evaluations = num_fitness_evaluations
self._evaluations_counter = 0
self._statistics_collector = statistics_collector
super().__init__()

@property
def num_fitness_evaluations(self) -> Optional[int]:
return self._num_fitness_evaluations

class EstimationLimitedFitnessEstimator(FitnessEstimator):
def __init__(self, num_fitness_evaluations: int, statistics_collector: Optional[StatisticsCollector] = None):
self.num_fitness_evaluations = num_fitness_evaluations
self.evaluations_counter = 0
self.statistics_collector = statistics_collector
super().__init__()
@property
def evaluations_counter(self) -> int:
return self._evaluations_counter

@abstractmethod
def fit(self, iter_num: int) -> None:
pass
...

def estimate(self, iter_num: int, population: List[Individual]) -> List[Individual]:
evaluated = [individual for individual in population if individual.dto.fitness_value is not None]
not_evaluated = [individual for individual in population if individual.dto.fitness_value is None]
evaluations_limit = max(0, self.num_fitness_evaluations - self.evaluations_counter) \
if self.num_fitness_evaluations else len(not_evaluated)
evaluations_limit = max(0, self._num_fitness_evaluations - self._evaluations_counter) \
if self._num_fitness_evaluations else len(not_evaluated)
if len(not_evaluated) > evaluations_limit:
not_evaluated = not_evaluated[:evaluations_limit]
self.evaluations_counter += len(not_evaluated)
self._evaluations_counter += len(not_evaluated)
new_evaluated = self._estimate(iter_num, not_evaluated)
if self.statistics_collector:
if self._statistics_collector:
for individual in new_evaluated:
self.statistics_collector.log_individual(individual)
self._statistics_collector.log_individual(individual)
return evaluated + new_evaluated

@abstractmethod
Expand Down

0 comments on commit 8419511

Please sign in to comment.