A framework for genetic algorithms in Go.
A Genetic Algorithm (GA) is a metaheuristic for solving optimization problems, inspired by natural selection.
The idea is to have multiple tentative solutions to the problem. At each iteration, these solutions are evaluated and their chance of surival depends on how "good" they are. Solutions are picked randomly (depending on their chance of survival) and bred to create children. These children compose the next generation, upon which the same operations are executed at the next iteration of the algorithm.
Doing so allows the tentative solutions to improve with each iteration of the algorithm.
Individual or Member: A single tentative solution to the problem.
Population: Group of solutions, representing a generation.
Generation: An iteration of the algorithm.
Genes: Components of a tentative solution or decision variables of the problem (usually binary encoded in some ways).
Fitness: Metric or score that represents how "good" a tentative solution is.
Selection: Process by which the best performing (highest fitness) solutions are assigned a higher probability of being used for breeding the next generation.
Crossover: Process by which the genes of two tentative solutions are mixed to create a new tentative solution, that becomes part of the next generation.
Mutation: Process by which one or more genes of a tentative solution randomly change.
-
Randomly generate the population of the first generation
-
For N generations:
- Compute the fitness score of each individual
- Select individuals, depending on their fitness, to be the parents
- Crossover each couple of parents to create a child
- Possibly, mutate the child
- Group all children to create the population of the next generation
This framework requires Go version 1.22 or above. Tested on Go version 1.23.4.
First, import the ga package of this module, which provides the genetic
algorithm generic implementation.
import "github.com/BolleA7X/GenetiGo/ga"
Then:
- Define a struct to represent an individual/member. You can embed the provided
ga.MemberDatatype to your struct to make sure it has the correct attributes, or define them by yourself - Make your struct implement the
ga.Memberinterface so that the solver knows how crossover and mutation work for your specific problem - Randomly create a list of individuals to use as the population of the first generation
- Create an instance of
ga.Solverby calling thega.NewSolverfunction, passing the first generation and some options as arguments - Call the
Solvemethod of yourga.Solverinstance. This method returns the individual with the highest fitness at the last generation
The ga.NewSolver function expects an object of type ga.SolverOptions
as its second argument, allowing you to customize the parameters and behaviour
of the Genetic Algorithm. These options are:
- PopulationSize: Number of members at each generation
- MaxGenerations: Maximum number of generations to simulate
- MutationChance: Chance that a member of the population randomly mutates (0 <= chance <= 1)
- NBatches: Number of batches. Population is divided into batches, where each batch is managed by a separate goroutine. If <= 1, the solver works in single-threaded mode. The number of goroutines is limited to the population size.
- Verbose: Enable verbose output on stdout
Some examples are provided in the examples folder. Each example has its
own folder and main function.
To execute an example, run the following command:
go run examples/<example_name>/main.go
You can also check for race conditions while running the program:
go run -race examples/<example_name>/main.go