Skip to content

Pygad optimizer #616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open

Conversation

spline2hg
Copy link
Collaborator

@spline2hg spline2hg commented Jul 15, 2025

This pull request introduces a genetic algorithm optimizer based on the PyGAD library.

Copy link

codecov bot commented Jul 15, 2025

Codecov Report

❌ Patch coverage is 98.15668% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/optimagic/optimizers/pygad_optimizer.py 97.79% 4 Missing ⚠️
Files with missing lines Coverage Δ
src/optimagic/algorithms.py 87.82% <100.00%> (+0.12%) ⬆️
src/optimagic/config.py 100.00% <100.00%> (ø)
src/optimagic/typing.py 88.76% <100.00%> (+0.25%) ⬆️
src/optimagic/optimizers/pygad_optimizer.py 97.79% <97.79%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@spline2hg
Copy link
Collaborator Author

spline2hg commented Jul 18, 2025

PyGAD Parameter Optimagic Mapping
User-facing
num_generations num_generations
sol_per_pop population_size
num_parents_mating num_parents_mating
initial_population initial_population
parent_selection_type parent_selection_type
keep_parents keep_parents
keep_elitism keep_elitism
K_tournament K_tournament
crossover_type crossover_type
crossover_probability crossover_probability
mutation_type mutation_type
mutation_probability mutation_probability
mutation_percent_genes mutation_percent_genes
mutation_num_genes mutation_num_genes
mutation_by_replacement mutation_by_replacement
random_mutation_min_val random_mutation_min_val
random_mutation_max_val random_mutation_max_val
allow_duplicate_genes allow_duplicate_genes
fitness_batch_size fitness_batch_size
stop_criteria stop_criteria
random_seed random_seed
n_cores (Optimagic-only) n_cores
Internal
fitness_func Auto-generated from problem.fun / problem.batch_fun
num_genes Auto-detected from len(x0)
gene_type Fixed to float64
gene_space Auto-built from problem.bounds.lower and problem.bounds.upper
parallel_processing Fixed to None (parallelism handled via Optimagic)
Ignored
init_range_low
init_range_high
gene_constraint
sample_size
save_best_solutions
save_solutions
suppress_warnings
on_start
on_fitness
on_parents
on_crossover
on_mutation
on_generation
on_stop

Comment on lines 76 to 78
| list[NonNegativeFloat]
| tuple[NonNegativeFloat, NonNegativeFloat]
| NDArray[np.float64]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the current documentation I don't understand the cases other than a scalar probability. How many entries would have to be in the list. What happens if I specify a tuple with two values? How long would the array have to be?

At the moment I am not sure if just the documentation is missing or we might need to simplify which cases are allowed.

The same comment applies to the next two options.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three parameters mutation_probability, mutation_num_genes, and mutation_percent_genes support the following modes:

  1. Non-adaptive mutation (mutation_type other than "adaptive"): Only scalar values are allowed.
  2. Adaptive mutation (mutation_type="adaptive"): Only arrays/lists/tuples of length 2 are allowed.

All three parameters (mutation_probability, mutation_num_genes, mutation_percent_genes) support only length-2 arrays/lists for adaptive mutation.

In the adaptive case:

  • First element: Used for low-fitness solutions (fitness < average)
  • Second element: Used for high-fitness solutions (fitness > average)

Copy link
Member

@janosg janosg Jul 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I think we should only support ProbabilityFloat, None and tuple[...]; Not the array and list ones.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read your explanation a bit more carefully now: We want to avoid a situation like this where the value of one argument determines which type is valid for another one. We also want to avoid situations where the value of one argument determines whether another argument is used or ignored.

This is the same situation we solve here by introducing configured algorithms.

Since mutation_functions can be callables, the same solution will work.

Concretely this means:

  • For every mutation function supported by pygad we introduce a callable dataclass. The attributes of the class are the options supported by that mutation function. The __call__ method just calls the corresponding function from pygad.
  • If a user wants to just select a mutation function but otherwise use default values they can pass a string (as now) or a class.
  • If a user wants to configure the mutation further, they need to pass an instance of the mutation functions.
  • If a user wants a completely customized mutation function they can either create a subclass or follow the protocol.
  • The argument mutation_type should then be renamed to mutation.

Usage example for a configured mutation function:

algo = om.algos.pygad(
    mutation=om.pygad.AdaptiveMutation(
        probability_bad=0.3, # for low fitness values
        probability_good=0.1, # for high fitness values
    )
)

Name wise, we need to avoid "high" and "low" because the algorithms can be used in maximize and minimize as well as "fitness" because optimagic users know this as "fun".

Let me know if this explanation was clear, otherwise we can have a short call again. A similar solution might also be necessary for parent selection functions etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants