Repository navigation
Swap genes when the random mutation finds no free value - #373
Conversation
With allow_duplicate_genes=False and a gene space of num_genes values, as for permutations, every value of the space is used, so a mutated gene kept its value and the mutation never changed a solution. In the 4 mutations by space, swap the gene with another gene whose value is in its space and whose space holds its value instead. Fixes ahmedfgad#372
ahmedfgad
left a comment
There was a problem hiding this comment.
Thanks for reporting this and adding the permutation tests. The fallback addresses a real issue, but please fix these two cases before we merge it.
- Preserve each gene's declared type when swapping.
The assignments in swap_gene_by_space() move the values directly, without converting them for the destination gene. With gene_space=[0, 1], gene_type=[int, float], allow_duplicate_genes=False, and mutation_num_genes=1, mutating an object array containing [0, 1.0] produces [1.0, 0]. The first gene is now a float and the second is an int, contrary to their declared types.
Please convert and round both replacement values for their destination genes before committing the swap. Check that the converted values still satisfy both gene spaces and preserve uniqueness; if they do not, skip that candidate. Add a regression test for mixed gene types.
- Avoid always undoing the mutation on a two-gene permutation.
With gene_space=[0, 1], gene_type=int, and allow_duplicate_genes=False, mutating [0, 1] with either mutation_num_genes=2 or mutation_probability=1.0 swaps the two genes once for each selected gene. The second swap reverses the first, so the solution always ends unchanged. This leaves the original problem unresolved for this configuration.
Please prevent the fallback from processing the same swapped pair twice in one mutation pass, or use another approach that allows this configuration to change. Add tests for both mutation settings. Since the fallback is also added to adaptive mutation, please cover its count-based and probability-based paths as well.
The existing permutation tests pass, but they do not cover these cases. Please include the pygad.utils version bump required by the submodule-version check when updating the PR.
Track swapped positions per mutation pass to avoid cancelling two-gene swaps. Validate destination conversions, spaces, and dependent constraints before changing the solution. Add regression coverage and document the recent operator fixes.
Run the metadata-only chart job with the trusted base workflow. Add a manual trigger to verify and refresh summaries without checking out contributor code.
📊 Change summary11 files changed · +478 / −8 lines pie showData
title Lines changed per file (486 total)
"tests/test_permutation_mutation.py" : 228
"pygad/utils/mutation.py" : 145
"tests/test_crossover_mutation.py" : 45
".github/workflows/pr-change-chart.yml" : 22
"docs/source/utils.md" : 19
"docs/source/releases.md" : 12
"docs/source/pygad.md" : 5
"docs/source/gene_values.md" : 4
"docs/source/adaptive_mutation.md" : 2
"docs/source/benchmarks.md" : 2
"pygad/utils/__init__.py" : 2
Per-file breakdown
|
ahmedfgad
left a comment
There was a problem hiding this comment.
The requested fixes are complete. Fallback swaps preserve destination gene types, numeric values after casting and rounding, both gene spaces, uniqueness, and dependent constraints. Tracking swapped positions within each mutation pass prevents a two-gene permutation from immediately swapping back.
The regression coverage includes all four random/adaptive mutation paths, mixed types, constrained and nested spaces, single-gene permutations, zero mutation probability, and seeded GA runs. Validation passed: 995 local tests, the Python 3.8–3.14 test matrix, all SDK compatibility jobs, package build and installed-wheel checks, and a clean documentation build with warnings treated as errors.
The documentation now explains the fallback and the recent crossover and swap corrections. The chart workflow's fork-token permission failure is also fixed. Its metadata-only job was verified successfully from github-actions: https://github.com/ahmedfgad/GeneticAlgorithmPython/actions/runs/37733700942
Python test matrix: https://github.com/ahmedfgad/GeneticAlgorithmPython/actions/runs/37733466235
SDK compatibility: https://github.com/ahmedfgad/GeneticAlgorithmPython/actions/runs/37733699797
Approved.
Fixes #372.
With
allow_duplicate_genes=Falseand a gene space ofnum_genesvalues (the permutation setup ofpygad.benchmarks.tsp), every value of the space is used, soselect_unique_value()kept the gene's value and the random mutation never changed a solution.This adds
swap_gene_by_space()to theMutationclass. In the 4 mutations by space (mutation_by_space,mutation_probs_by_space,adaptive_mutation_by_space,adaptive_mutation_probs_by_space), when the picked value equals the gene's value and duplicates aren't allowed, the gene swaps values with another gene, picked at random among those whose value is in its space and whose space holds its value. The swap keeps the genes unique, so the duplicate resolution isn't needed. Genes with agene_constraintaren't swapped, and nothing changes when duplicates are allowed or a free value exists.On
examples/benchmarks/example_tsp.py's settings with 12 cities on a circle (200 generations, seeds 0 to 2), the best tours go from lengths 11.7, 12.5 and 9.6 to the optimum, 6.2.Tests: 3 tests in
tests/test_crossover_mutation.pymutate 100 permutations of 8 (withmutation_num_genes, withmutation_probability=1.0, and with a nested gene space) and check that each one changes and stays a permutation. They fail on master and pass with this change.pytest tests(withouttest_kerasga.pyandtest_torchga.py, which need TensorFlow and PyTorch): 874 passed, 1 skipped. The only failure istest_submodule_versions.py::test_changed_submodule_is_version_bumped[utils], sincepygad/utilschanged; I left the version bump to you for the release.