Algorithms¶
Correctness fixes in the algorithm drivers and variation operators.
ea_generate_update never evaluates MO-CMA parents¶
StrategyMultiObjective.generate used to sample only from already-valid
parents. On generation 0 every parent is unevaluated, so the first
offspring batch was empty or stale and update never saw a scored
child.
Fix. generate skips sort_non_dominated and samples all parents
when any parent fitness is invalid. update ranks only
ind.fitness.is_valid() candidates, so the first generation promotes
evaluated offspring. The shared ea_generate_update driver is
unchanged.
Validator.
tests/test_strategies/test_cma_multi_objective.py::test_invalid_parent_fitness_promotes_evaluated_offspring
ea_generate_update ignores evaluate_batch¶
ea_simple, ea_mu_*, and ea_map_elites score invalids through
evaluate_invalid, which calls toolbox.evaluate_batch when that
operator is registered. The generate-and-update drivers still went
through toolbox.map(toolbox.evaluate, …), so a CMA run with a
vectorized or GPU batch evaluator never used it.
Fix. Both ea_generate_update and ea_generate_update_restarts
evaluate through evaluate_invalid. nevals is the number of
individuals that were actually scored.
Validator.
tests/test_algorithms/test_ea_drivers.py::test_generate_update_uses_evaluate_batch_when_registered
var_or crashed when the parent pool had one individual¶
rng.sample(population, 2) needs two distinct parents. After
ea_mu_comma_lambda keeps a single survivor — a valid
\((1,\lambda)\) setting — the next generation raised
ValueError as soon as a crossover draw fired. The same crash
hits ea_mu_plus_lambda when \(\mu = 1\) and cx_prob > 0.
Fix. When the pool has fewer than two individuals, clone the
only parent twice and mate those clones. Empty pools still fail
on choice.
Validator.
tests/test_algorithms/test_variation.py::test_var_or_single_parent_with_crossover_does_not_crash
var_or accepted negative and NaN probabilities¶
var_and rejects any cx_prob or mut_prob outside [0, 1].
var_or only checked cx_prob + mut_prob > 1. A negative
component whose sum still sat in [0, 1], or a NaN (comparisons
against NaN are false), produced offspring instead of raising.
Fix. Apply the same per-probability [0, 1] checks as
var_and before the existing sum check. NaN and infinities fail
the interval test the same way.
Validator.
tests/test_algorithms/test_variation.py::test_var_or_rejects_negative_probabilities
tests/test_algorithms/test_variation.py::test_var_or_rejects_nan_probabilities
ea_generate_update clobbered the last population on empty generate¶
A custom generate may return [] as a stop signal (no more
samples). The driver assigned that empty list to population and
then called toolbox.update([]). The returned population was []
instead of the last scored generation, and CMA Strategy.update
crashed (ValueError from numpy.dot of weights against an empty
batch). Hall of fame and the logbook still had earlier work; the
function result did not.
Fix. Bind the new batch only when it is non-empty. An empty
generate still stops the loop. update is not called with [].
The returned population is the last evaluated one (or [] if no
generation ran).
Validators.
tests/test_algorithms/test_ea_generate_update.py::test_empty_generate_keeps_last_evaluated_populationtests/test_algorithms/test_ea_generate_update.py::test_empty_generate_from_the_start_returns_emptytests/test_algorithms/test_ea_generate_update.py::test_empty_generate_does_not_call_cma_update
ea_generate_update_restarts discarded the last population on empty generate¶
RestartStrategy.generate returns [] when the eval budget is spent,
and a custom generate may use the same empty batch as a stop signal.
The driver assigned that empty list to population before breaking,
so a run that had already evaluated one or more batches returned []
instead of the last scored generation. Hall of fame and the logbook
still had the work; the function result did not.
Fix. Bind the new batch only when it is non-empty. An empty
generate still stops the loop, but the returned population is the
last evaluated one (or [] if no generation ran).
Validator.
tests/test_algorithms/test_ea_generate_update_restarts.py::test_empty_generate_keeps_last_evaluated_population
ea_map_elites crashed when compiling stats on an empty seed list¶
Generation 0 always compiled stats from initial. An empty seed is
a supported resume path: _parent_pool varies from a pre-filled
archive when initial is []. The documented tutorial stats use
max / numpy.max, which raise ValueError on an empty reduction.
The same crash hit record_generation for an empty ea_simple
population.
Fix. Skip stats.compile when the individual list is empty.
Archive coverage / num_elites / qd_score still record. Later
generations with offspring still compile as before.
Validator.
tests/test_algorithms/test_ea_map_elites.py::test_ea_map_elites_prefilled_archive_empty_initial_with_stats
PolicyActionGuard undercharged step_islands¶
_applied_eval_cost re-estimated after step_islands.
That step already scores invalids, so the post-action
estimate dropped (for example 4 planned vs 2 charged).
With n_evals=4, a second island step could run while
the guard thought the budget was already spent correctly.
Fix. Estimate before dispatch. Charge that planned
count after step_islands. evaluate_invalid still
charges the actual result.value.
Validator.
tests/test_algorithms/test_policy_island_eval_guard.py::test_step_islands_guard_charges_pre_action_estimate