Source code of PyGAD, a Python 3 library for building the genetic algorithm and training machine learning algorithms (Keras & PyTorch).
BSD-3-CLAUSE License
Bot releases are hidden (Show)
run()
method completes, update the 2 instance attributes: 1) last_generation_parents
2) last_generation_parents_indices
. This is to keep the list of parents up-to-date with the latest population fitness last_generation_fitness
. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/275
run_
. Their purpose is to keep the main loop inside the run()
method clean. Check the [Other Methods](https://pygad.readthedocs.io/en/latest/pygad.html#other-methods) section for more information.Published by ahmedfgad 9 months ago
Release Date 29 January 2024
stop_ciiteria
parameter is used with the reach
keyword, then multiple numeric values can be passed when solving a multi-objective problem. For example, if a problem has 3 objective functions, then stop_criteria="reach_10_20_30"
means the GA stops if the fitness of the 3 objectives are at least 10, 20, and 30, respectively. The number values must match the number of objective functions. If a single value found (e.g. stop_criteria=reach_5
) when solving a multi-objective problem, then it is used across all the objectives. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/238
delay_after_gen
parameter is now deprecated and will be removed in a future release. If it is necessary to have a time delay after each generation, then assign a callback function/method to the on_generation
parameter to pause the evolution.gene_space
parameter without a step, then mutation occurs by adding a random value to the gene value. The random vaue is generated based on the 2 parameters random_mutation_min_val
and random_mutation_max_val
. For more information, check the [How Mutation Works with the gene_space Parameter?](https://pygad.readthedocs.io/en/latest/pygad_more.html#how-mutation-works-with-the-gene-space-parameter) section. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/229
object
as a supported data type for int (GA.supported_int_types) and float (GA.supported_float_types). https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/174
raise
clause instead of the sys.exit(-1)
to terminate the execution. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/213
fitness_batch_size
set to a non-zero number).pygad.py
script when finding the index of the best solution. It does not work properly with multi-objective optimization where self.best_solutions_fitness
have multiple columns. self.best_solution_generation = numpy.where(numpy.array(
self.best_solutions_fitness) == numpy.max(numpy.array(self.best_solutions_fitness)))[0][0]
Published by ahmedfgad about 1 year ago
pygad.utils.nsga2
is created that has the NSGA2
class that includes the functionalities of NSGA-II. The class has these methods: 1) get_non_dominated_set()
2) non_dominated_sorting()
3) crowding_distance()
4) sort_solutions_nsga2()
. Check [this section](https://pygad.readthedocs.io/en/latest/pygad_more.html#multi-objective-optimization) for an example.NSGA2
class in the pygad.utils.nsga2
module. Just return a list
, tuple
, or numpy.ndarray
from the fitness function and the library will consider the problem as multi-objective optimization. All the objectives are expected to be maximization. Check [this section](https://pygad.readthedocs.io/en/latest/pygad_more.html#multi-objective-optimization) for an example.pygad.utils.parent_selection
module: 1) Tournament selection for NSGA-II 2) NSGA-II selection.plot_fitness()
method in the pygad.plot
module has a new optional parameter named label
to accept the label of the plots. This is only used for multi-objective problems. Otherwise, it is ignored. It defaults to None
and accepts a list
, tuple
, or numpy.ndarray
. The labels are used in a legend inside the plot.pygad.plot
module is changed to the greenish #64f20c
color.pareto_fronts
added to the pygad.GA
instances that holds the pareto fronts when solving a multi-objective problem.gene_type
accepts a list
, tuple
, or numpy.ndarray
for integer data types given that the precision is set to None
(e.g. gene_type=[float, [int, None]]
).cal_pop_fitness()
method, the fitness value is re-used if save_best_solutions=True
and the solution is found in the best_solutions
attribute. These parameters also can help re-using the fitness of a solution instead of calling the fitness function: keep_elitism
, keep_parents
, and save_solutions
.99999999999
is replaced by float('inf')
in the 2 methods wheel_cumulative_probs()
and stochastic_universal_selection()
inside the pygad.utils.parent_selection.ParentSelection
class.plot_result()
method in the pygad.visualize.plot.Plot
class is removed. Instead, please use the plot_fitness()
if you did not upgrade yet.Published by ahmedfgad over 1 year ago
Release Date 20 June 2023
gene_space
parameter can no longer be assigned a tuple.gene_space
parameter has a member of type tuple
.gene_space_unpacked
which has the unpacked gene_space
. It is used to solve duplicates. For infinite ranges in the gene_space
, they are unpacked to a limited number of values (e.g. 100).gene_space
attribute.dict
is used with the gene_space
attribute, the new gene value was calculated by summing 2 values: 1) the value sampled from the dict
2) a random value returned from the random mutation range defined by the 2 parameters random_mutation_min_val
and random_mutation_max_val
. This might cause the gene value to exceed the range limit defined in the gene_space
. To respect the gene_space
range, this release only returns the value from the dict
without summing it to a random value.format()
method. https://github.com/ahmedfgad/GeneticAlgorithmPython/pull/189
__init__()
of the pygad.GA
class, the logged error messages are handled using a try-except
block instead of repeating the logger.error()
command. https://github.com/ahmedfgad/GeneticAlgorithmPython/pull/189
CustomLogger
is created in the pygad.cnn
module to create a default logger using the logging
module assigned to the logger
attribute. This class is extended in all other classes in the module. The constructors of these classes have a new parameter named logger
which defaults to None
. If no logger is passed, then the default logger in the CustomLogger
class is used.pygad.nn
module, the print()
function in all other modules are replaced by the logging
module to log messages.on_fitness()
, on_parents()
, on_crossover()
, and on_mutation()
can return values. These returned values override the corresponding properties. The output of on_fitness()
overrides the population fitness. The on_parents()
function/method must return 2 values representing the parents and their indices. The output of on_crossover()
overrides the crossover offspring. The output of on_mutation()
overrides the mutation offspring.fitness_batch_size
>1. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/195
allow_duplicate_genes=False
and a user-defined gene_space
is used, it sometimes happen that there is no room to solve the duplicates between the 2 genes by simply replacing the value of one gene by another gene. This release tries to solve such duplicates by looking for a third gene that will help in solving the duplicates. These examples explain how it works. Check [this section](https://pygad.readthedocs.io/en/latest/pygad.html#prevent-duplicates-in-gene-values) for more information.random_mutation_min_val
and random_mutation_max_val
can accept iterables (list/tuple/numpy.ndarray) with length equal to the number of genes. This enables customizing the mutation range for each individual gene. https://github.com/ahmedfgad/GeneticAlgorithmPython/discussions/198
init_range_low
and init_range_high
can accept iterables (list/tuple/numpy.ndarray) with length equal to the number of genes. This enables customizing the initial range for each individual gene when creating the initial population.data
parameter in the predict()
function of the pygad.kerasga
module can be assigned a data generator. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/115 https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/207
predict()
function of the pygad.kerasga
module accepts 3 optional parameters: 1) batch_size=None
, verbose=0
, and steps=None
. Check documentation of the [Keras Model.predict()](https://keras.io/api/models/model_training_apis) method for more information. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/207
gene_space
is used with int
or float
data types. Check [this section](https://pygad.readthedocs.io/en/latest/pygad.html#limit-the-gene-value-range-using-the-gene-space-parameter). https://github.com/ahmedfgad/GeneticAlgorithmPython/discussions/198
Published by ahmedfgad over 1 year ago
Fix an issue with passing user-defined function/method for parent selection. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/179
Published by ahmedfgad over 1 year ago
This release has a major change where the fitness function accepts a mandatory parameter referring to the instance of the pygad.GA
class.
This is the release notes:
pygad.py
module are moved to the pygad.utils
, pygad.helper
, and pygad.visualize
submodules.pygad.utils.parent_selection
module has a class named ParentSelection
where all the parent selection operators exist. The pygad.GA
class extends this class.pygad.utils.crossover
module has a class named Crossover
where all the crossover operators exist. The pygad.GA
class extends this class.pygad.utils.mutation
module has a class named Mutation
where all the mutation operators exist. The pygad.GA
class extends this class.pygad.helper.unique
module has a class named Unique
some helper methods exist to solve duplicate genes and make sure every gene is unique. The pygad.GA
class extends this class.pygad.visualize.plot
module has a class named Plot
where all the methods that create plots exist. The pygad.GA
class extends this class....
class GA(utils.parent_selection.ParentSelection,
utils.crossover.Crossover,
utils.mutation.Mutation,
helper.unique.Unique,
visualize.plot.Plot):
...
logging
module to log the outputs to both the console and text file instead of using the print()
function. This is by assigning the logging.Logger
to the new logger
parameter. Check the [Logging Outputs](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#logging-outputs) for more information.logger
to save the logger.fitness_func
parameter accepts a new parameter that refers to the instance of the pygad.GA
class. Check this for an example: [Use Functions and Methods to Build Fitness Function and Callbacks](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#use-functions-and-methods-to-build-fitness-and-callbacks). https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/163
initial_population
parameter.pop_fitness
parameter of the best_solution()
method.random_mutation_min_val
and random_mutation_max_val
) instead of using the parameters init_range_low
and init_range_high
.summary()
method returns the summary as a single-line string. Just log/print the returned string it to see it properly.callback_generation
parameter is removed. Use the on_generation
parameter instead.parallel_processing
parameter with Keras and PyTorch. As Keras/PyTorch are not thread-safe, the predict()
method gives incorrect and weird results when more than 1 thread is used. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/145 https://github.com/ahmedfgad/TorchGA/issues/5 https://github.com/ahmedfgad/KerasGA/issues/6. Thanks to this [StackOverflow answer](https://stackoverflow.com/a/75606666/5426539).numpy.float
by float
in the 2 parent selection operators roulette wheel and stochastic universal. https://github.com/ahmedfgad/GeneticAlgorithmPython/pull/168
Published by ahmedfgad over 1 year ago
Published by ahmedfgad over 1 year ago
summary()
method is supported to return a Keras-like summary of the PyGAD lifecycle.fitness_batch_size
is supported to calculate the fitness function in batches. If it is assigned the value 1
or None
(default), then the normal flow is used where the fitness function is called for each individual solution. If the fitness_batch_size
parameter is assigned a value satisfying this condition 1 < fitness_batch_size <= sol_per_pop
, then the solutions are grouped into batches of size fitness_batch_size
and the fitness function is called once for each batch. In this case, the fitness function must return a list/tuple/numpy.ndarray with a length equal to the number of solutions passed. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/136.cloudpickle
library (https://github.com/cloudpipe/cloudpickle) is used instead of the pickle
library to pickle the pygad.GA
objects. This solves the issue of having to redefine the functions (e.g. fitness function). The cloudpickle
library is added as a dependancy in the requirements.txt
file. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/159
fitness_func
, crossover_type
, mutation_type
, parent_selection_type
, on_start
, on_fitness
, on_parents
, on_crossover
, on_mutation
, on_generation
, and on_stop
. https://github.com/ahmedfgad/GeneticAlgorithmPython/pull/92 https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/138
allow_duplicate_genes=True
. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/39
set()
is no longer supported in Python 3.11. Instead, sampling happens from a list()
. Thanks Marco Brenna
for pointing to this issue.save_solutions=True
that causes the fitness function to be called for solutions already explored and have their fitness pre-calculated. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/160
last_generation_elitism_indices
added to hold the indices of the selected elitism. This attribute helps to re-use the fitness of the elitism instead of calling the fitness function.best_solution()
method which in turns saves some calls to the fitness function.cal_pop_fitness()
method. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/79#issuecomment-1439605442
Published by ahmedfgad over 1 year ago
Published by ahmedfgad over 1 year ago
PyGAD 2.18.2 release notes
numpy.int
and numpy.float
from the list of supported data types. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/151 https://github.com/ahmedfgad/GeneticAlgorithmPython/pull/152
on_crossover()
callback function even if crossover_type
is None
. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/138
on_mutation()
callback function even if mutation_type
is None
. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/138
Published by ahmedfgad about 2 years ago
PyGAD 2.18.1
keep_elitism
is used. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/132
Published by ahmedfgad about 2 years ago
run_completed
property to False
. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/122
run()
method self.best_solutions, self.best_solutions_fitness, self.solutions, self.solutions_fitness
: https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/123. Now, the user can have the flexibility of calling the run()
method more than once while extending the data collected after each generation. Another advantage happens when the instance is loaded and the run()
method is called, as the old fitness value are shown on the graph alongside with the new fitness values. Read more in this section: [Continue without Loosing Progress](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#continue-without-loosing-progress)crossover_type=None
.keep_elitism
. It defaults to 1 which means for each generation keep only the best solution in the next generation. If assigned 0, then it has no effect. Read more in this section: [Elitism Selection](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#elitism-selection). https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/74
last_generation_elitism
added to hold the elitism in the last generation.random_seed
added to accept a seed for the random function generators. Credit to this issue https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/70 and [Prof. Fernando Jiménez Barrionuevo](http://webs.um.es/fernan). Read more in this section: [Random Seed](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#random-seed).pygad.TorchGA
module to make sure the tensor data is moved from GPU to CPU. Thanks to Rasmus Johansson for opening this pull request: https://github.com/ahmedfgad/TorchGA/pull/2
Published by ahmedfgad over 2 years ago
Release Date: 8 July 2022
gene_space
parameter is given a fixed value. e.g. gene_space=[range(5), 4]. The second gene's value is static (4) which causes an exception.allow_duplicate_genes
parameter did not work when mutation is disabled (i.e. mutation_type=None
). This is by checking for duplicates after crossover directly. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/39
tournament_selection()
method as the indices of the selected parents were incorrect. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/89
save_solutions=True
.parallel_processing
in the constructor of the pygad.GA
class. Thanks to [@windowshopr](https://github.com/windowshopr) for opening the issue [#78](https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/78) at GitHub. Check the [Parallel Processing in PyGAD](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#parallel-processing-in-pygad) section for more information and examples.Published by ahmedfgad over 2 years ago
Changes in PyGAD 2.16.3
previous_generation_fitness
added in the pygad.GA
class. It holds the fitness values of one generation before the fitness values saved in the last_generation_fitness
.cal_pop_fitness()
method in getting the correct indices of the previous parents. This is solved by using the previous generation's fitness saved in the new attribute previous_generation_fitness
to return the parents' fitness values. Thanks to Tobias Tischhauser (M.Sc. - [Mitarbeiter Institut EMS, Departement Technik, OST – Ostschweizer Fachhochschule, Switzerland](https://www.ost.ch/de/forschung-und-dienstleistungen/technik/systemtechnik/ems/team)) for detecting this bug.Published by ahmedfgad about 3 years ago
save_solutions=True
.tqdm
library to show a progress bar. https://github.com/ahmedfgad/GeneticAlgorithmPython/discussions/50
import pygad
import numpy
import tqdm
equation_inputs = [4,-2,3.5]
desired_output = 44
def fitness_func(solution, solution_idx):
output = numpy.sum(solution * equation_inputs)
fitness = 1.0 / (numpy.abs(output - desired_output) + 0.000001)
return fitness
num_generations = 10000
with tqdm.tqdm(total=num_generations) as pbar:
ga_instance = pygad.GA(num_generations=num_generations,
sol_per_pop=5,
num_parents_mating=2,
num_genes=len(equation_inputs),
fitness_func=fitness_func,
on_generation=lambda _: pbar.update(1))
ga_instance.run()
ga_instance.plot_result()
solutions
and solutions_fitness
when the save_solutions
parameter is set to True
. Now, the fitness of the last population is appended to the solutions_fitness
array. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/64
solutions
, solutions_fitness
, best_solutions
, and best_solutions_fitness
) doubled after each call of the run()
method. This is solved by resetting these variables at the beginning of the run()
method. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/62
mutation_type="adaptive"
). https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/65
Published by ahmedfgad over 3 years ago
A user-defined function can be passed to the mutation_type, crossover_type, and parent_selection_type parameters in the pygad.GA class to create a custom mutation, crossover, and parent selection operators. Check the User-Defined Crossover, Mutation, and Parent Selection Operators section in the documentation: https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#user-defined-crossover-mutation-and-parent-selection-operators
The example_custom_operators.py script gives an example of building and using custom functions for the 3 operators.
https://github.com/ahmedfgad/GeneticAlgorithmPython/discussions/50
Published by ahmedfgad over 3 years ago
Fix a bug when keep_parents
is set to a positive integer. https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/49
Published by ahmedfgad over 3 years ago
last_generation_parents_indices
holds the indices of the selected parents in the last generation.last_generation_fitness
and last_generation_parents_indices
attributes. This speeds-up the adaptive mutation.None
in the gene_space
parameter (e.g. gene_space=[[1, 2, 3], [5, 6, None]]
), then its value will be randomly generated for each solution rather than being generated once for all solutions. Previously, a value of None
in a sublist of the gene_space
parameter was identical across all solutions.gene_space
parameter itself or one of its elements has a new key called "step"
to specify the step of moving from the start to the end of the range specified by the 2 existing keys "low"
and "high"
. An example is {"low": 0, "high": 30, "step": 2}
to have only even values for the gene(s) starting from 0 to 30. For more information, check the More about the gene_space
Parameter section. https://github.com/ahmedfgad/GeneticAlgorithmPython/discussions/48
predict()
is added in both the pygad.kerasga
and pygad.torchga
modules to make predictions. This makes it easier than using custom code each time a prediction is to be made.stop_criteria
allows the user to specify one or more stop criteria to stop the evolution based on some conditions. Each criterion is passed as str
which has a stop word. The current 2 supported words are reach
and saturate
. reach
stops the run()
method if the fitness value is equal to or greater than a given fitness value. An example for reach
is "reach_40"
which stops the evolution if the fitness is >= 40. saturate
means stop the evolution if the fitness saturates for a given number of consecutive generations. An example for saturate
is "saturate_7"
which means stop the run()
method if the fitness does not change for 7 consecutive generations. Thanks to Rainer for asking about this feature: https://github.com/ahmedfgad/GeneticAlgorithmPython/discussions/44
False
, named save_solutions
is added to the constructor of the pygad.GA
class. If True
, then all solutions in each generation are appended into an attribute called solutions
which is NumPy array.plot_result()
method is renamed to plot_fitness()
. The users should migrate to the new name as the old name will be removed in the future.plot_fitness()
function in the pygad.GA
class which are font_size=14
, save_dir=None
, color="#3870FF"
, and plot_type="plot"
. Use font_size
to change the font of the plot title and labels. save_dir
accepts the directory to which the figure is saved. It defaults to None
which means do not save the figure. color
changes the color of the plot. plot_type
changes the plot type which can be either "plot"
(default), "scatter"
, or "bar"
. https://github.com/ahmedfgad/GeneticAlgorithmPython/pull/47
title
parameter in the plot_fitness()
method is "PyGAD - Generation vs. Fitness"
rather than "PyGAD - Iteration vs. Fitness"
.plot_new_solution_rate()
creates, shows, and returns a figure showing the rate of new/unique solutions explored in each generation. It accepts the same parameters as in the plot_fitness()
method. This method only works when save_solutions=True
in the pygad.GA
class's constructor.plot_genes()
creates, shows, and returns a figure to show how each gene changes per each generation. It accepts similar parameters like the plot_fitness()
method in addition to the graph_type
, fill_color
, and solutions
parameters. The graph_type
parameter can be either "plot"
(default), "boxplot"
, or "histogram"
. fill_color
accepts the fill color which works when graph_type
is either "boxplot"
or "histogram"
. solutions
can be either "all"
or "best"
to decide whether all solutions or only best solutions are used.gene_type
parameter now supports controlling the precision of float
data types. For a gene, rather than assigning just the data type like float
, assign a list
/tuple
/numpy.ndarray
with 2 elements where the first one is the type and the second one is the precision. For example, [float, 2]
forces a gene with a value like 0.1234
to be 0.12
. For more information, check the More about the gene_type
Parameter section.Published by ahmedfgad over 3 years ago
Some bug fixes when setting the save_best_solutions
parameter to True
. Previously, the best solution for generation i
was added into the best_solutions
attribute at generation i+1
. Now, the best_solutions
attribute is updated by each solution at its exact generation.
Published by ahmedfgad over 3 years ago
Some bug fixes when the gene_type
parameter is nested. Thanks to Rainer Engel for opening a discussion to report this bug: https://github.com/ahmedfgad/GeneticAlgorithmPython/discussions/43#discussioncomment-763342
Rainer Engel helped a lot in suggesting new features and suggesting enhancements in 2.14.0 to 2.14.2 releases.