PyGAD: a Python genetic algorithm library with a Keras and PyTorch training path
Source code of PyGAD, a Python 3 library for building the genetic algorithm and training machine learning algorithms (Keras & PyTorch).
At a glance
- What is it?
- PyGAD is a BSD-3-Clause Python 3 library that separates the genetic algorithm loop from the problem you are solving, and also exposes that loop as a way to train Keras and PyTorch models. The useful part is the callback surface; the cost is that you supply the fitness function and the search quality is yours to own.
- Who is it for?
- Adopt PyGAD if you have a fitness function you can already evaluate in Python and you want a generation loop with hooks rather than a framework to assemble. Skip it if you need constraint handling, integer or mixed-integer encodings, or a distributed evaluator, because none of those appear in the material here.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 69 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem PyGAD solves is the loop, not the search
Most Python optimization code starts as a for loop over candidate solutions with a scoring call inside it. That is fine until you want generation-level control: which parents mate, how offspring are recombined, when to stop early, what happens between generations. PyGAD takes that loop and exposes it as a class, pygad.GA, with named parameters for each stage. The README states that the library supports different types of crossover, mutation, and parent selection, and that it lets you optimize many types of problems by writing your own fitness function. That sentence is the whole contract. PyGAD does not know anything about your problem domain. It knows how to hold a population, score it, select, recombine, mutate, and repeat. The audience is therefore narrow and specific: Python developers who already have a function that maps a candidate solution to a number, and who want the surrounding machinery without writing it. If you do not have that function, PyGAD gives you nothing. If you do have it, the library is mostly a matter of wiring parameters and callbacks.
The callback lifecycle is the actual interface
The README describes the life cycle of a pygad.GA instance as a sequence of stages, and it says PyGAD stops when all generations are completed or when the function passed to the on_generation parameter returns the string stop. The example code implements every callback and has each one print its own name: on_start, on_fitness, on_parents, on_crossover, on_mutation, on_generation, on_stop. Each receives the ga_instance, and most receive the data produced at that stage, such as population_fitness, selected_parents, offspring_crossover, or offspring_mutation. This is the part worth reading the documentation for. The callbacks give you observation points inside a run without subclassing anything. on_generation is the only one with a documented control effect, returning the string stop to end the run early. The others are described as tracing points in the example. Whether they can mutate state or influence the next generation is not something the README establishes, so treat them as instrumentation until the Read the Docs pages say otherwise. That distinction matters: a callback that only observes is a logging hook, not a steering mechanism.
Fitness functions and the shape of a solution
The README example is a linear combination problem. function_inputs is a list of six floats, desired_output is 44, and the fitness function computes numpy.sum(solution*function_inputs), then returns 1.0 / (numpy.abs(output - desired_output) + 0.000001). Two things are visible in that code. First, a solution is a NumPy array whose length matches num_genes, which is set to len(function_inputs). Second, the fitness function signature is fitness_func(ga_instance, solution, solution_idx), so the index of the solution within the population is available if you need it. The reciprocal-with-epsilon pattern is a common way to turn a minimization objective into a maximization score, and the epsilon avoids division by zero when the output exactly matches the target. That pattern is your responsibility, not PyGAD's. The library does not normalize, rank, or scale fitness values on your behalf in anything shown here. If your objective has a wide dynamic range, or if most of the population scores near zero, the selection pressure you get is a direct consequence of the arithmetic you wrote. This is the single largest source of disappointing results with any genetic algorithm library, and PyGAD does not shield you from it.
Installing PyGAD and the extras that are not installed by default
The install command in the README is pip install pygad. The README also states that the core install is intentionally lightweight, depending only on numpy and cloudpickle, and that some features need extra libraries available as optional extras. Two extras are named. Plotting features such as plot_fitness() and plot_genes() need matplotlib, installed with pip install pygad[visualize]. Training Keras or PyTorch models through pygad.kerasga and pygad.torchga needs pip install pygad[deep_learning]. The split is sensible: if you are optimizing a numerical function you never pull in a deep learning stack. The cloudpickle dependency is worth pausing on. It suggests population or fitness state can be serialized, which is what you would want for checkpointing a long run or moving work between processes. The README does not document a save or load workflow in the material here, so the practical use of that dependency is not confirmed. If checkpointing matters to you, check the Read the Docs pages for the relevant methods before assuming it exists. The package is also distributed on conda-forge according to the badges in the README.
Keras and PyTorch training via genetic search, and what that actually means
PyGAD's description says it can train machine learning algorithms, and the README names two modules for it: pygad.kerasga and pygad.torchga. The mechanism is the one you would expect from a genetic algorithm library. Model parameters are the genes, and the fitness function evaluates a candidate parameter set, typically by measuring model performance on data. Nothing in the material indicates gradient computation is involved. That is the point and also the constraint. Genetic parameter search does not need a differentiable loss, so it can be applied to objectives that backpropagation cannot touch, such as a reward from a simulation or a discrete metric. The cost is sample efficiency. Gradient descent uses the derivative of the loss to pick a direction; a genetic algorithm samples and selects. For a model with a large parameter count, the number of fitness evaluations needed to make progress grows with the dimension of the search space, and each evaluation is a full forward pass over your data. PyGAD does not change that arithmetic. Treat the Keras and PyTorch modules as a way to search over small parameter sets or non-differentiable objectives, not as a replacement for an optimizer on a large network.
Where PyGAD is the wrong tool
The material describes a library for optimizing problems by writing your own fitness function, with support for single-objective and multi-objective problems and configurable crossover, mutation, and parent selection. It does not describe constraint handling, integer or mixed-integer gene encodings, a distributed or parallel evaluator, or a surrogate model. Those absences define the boundary. If your problem has hard constraints, you either fold them into the fitness function as penalties, which changes the search surface and can trap the population in infeasible regions, or you handle feasibility outside PyGAD. If your variables are integers or a mix of continuous and categorical, the README does not show how that is expressed. If a single fitness evaluation takes minutes, a generational loop that scores sol_per_pop candidates per generation becomes a scheduling problem, and nothing here indicates PyGAD manages that for you. There is also a subtler failure mode. Because the library accepts any callable as the fitness function, a bug in that callable is invisible to PyGAD. A fitness function that returns a constant, or that accidentally ignores the solution argument, produces a run that completes normally and reports meaningless numbers. The callbacks are the defense: on_fitness receives population_fitness, and printing its range in the first few generations is the cheapest way to confirm the search is actually being driven by your objective.
DEAP and the difference in approach
DEAP is the obvious alternative for evolutionary computation in Python, and the difference is architectural. DEAP gives you a toolbox of primitives: you build individuals, define a creator, register operators, and assemble the evolutionary loop yourself. That is more code for a standard problem and more control when the problem is not standard. PyGAD inverts this. It ships a pygad.GA class with the loop already assembled and named parameters for each stage, so the default path is a constructor call plus a fitness function. The trade is extensibility. In DEAP, a custom selection scheme is a function you register. In PyGAD, based on the README, you choose among the supported crossover, mutation, and parent selection types, and your customization point is the callback set. If your algorithm needs a structure PyGAD does not model, you are working against the library rather than with it. If your algorithm is a fairly standard generational GA with a custom objective, PyGAD is less code. That is the whole comparison, and it is a real one: the two libraries are optimized for different amounts of novelty in the algorithm itself.
Maintenance, licensing, and what to verify before adopting
PyGAD is BSD-3-Clause, a permissive licence that allows use in closed-source products provided the copyright notice and licence text are retained. That is a summary of the licence family, not legal advice; read the LICENSE file and the REUSE badge link in the README for the authoritative terms. On maintenance, the release history shows 3.7.0 in June 2026, 3.6.0 in April 2026, and 3.5.0 in July 2025, with the last push to master in July 2026 and the repository not archived. The README states the library is under active development and that more features are added regularly. The project also points to Vilvik, a hosted service for running PyGAD problems in the cloud, and to a tutorial for pushing a problem there. That is a commercial surface attached to an open source library, which is worth noting when you assess where future effort goes. The upgrade cost is the usual one for a library whose main interface is a constructor with many keyword arguments: parameter names are the API, and a rename breaks callers silently at import time rather than at call time. Pin the version in your requirements file, and when you upgrade, re-run your fitness function against the new version and confirm the callback signatures still match what you wrote.
Editorial conclusion
Adopt PyGAD if you have a fitness function you can already evaluate in Python and you want a generation loop with hooks rather than a framework to assemble. Skip it if you need constraint handling, integer or mixed-integer encodings, or a distributed evaluator, because none of those appear in the material here. Before committing, run the README example with num_generations=3 and your own fitness_func, confirm that on_generation returning the string stop actually halts the run, and check whether your chosen crossover and mutation types are documented for the gene encoding you plan to use.
Community notes