-
Notifications
You must be signed in to change notification settings - Fork 0
Generics
Relevant namespace: Genetic_Algorithm.GA.Generics
Most GA Generics revolve around the same set of constraints: IIndividual and IGene interfaces, where IIndividual's genes must be of the type that you specify in the constraint. This ensures that all of the components will work together properly when you implement any domain based on the defined interfaces.
Genes are an essential part of any genetic algorithm. The only thing a gene must know how to do is mutate. If you also plan to reconstruct individuals from their genes, it is recommended you implement the derived IGene<out EncodedType> (where you specify how the information stored in the gene gets converted back to the property it was encoding) and make your individuals use those genes. The more general interface is sufficient for purposes of the genetic algorithm itself, however.
All genes of the individual must be of the type you specify to ensure internal compatibility. Individuals mainly provide access to their genome (for fitness calculators), to their fitness score (assigned by fitness calculators, used by GA and GA Adapters), and can "mutate" (meaning iterating over every gene and deciding if it should mutate based on the current mutation probability, then telling the gene to mutate if necessary).
Populations are collections (ILists) of individuals with a specified gene type. On top of a generic List, a population is capable of initialising itself with random individuals, finding the fittest individual inside itself, and can have a strictly set DesiredSize which indicates exactly how many individuals the population should hold (for purposes of GA, where we want to have generations of constant size).
There is also a derived NumberedPopulation<TIndividual,TGene>`, which additionally holds an integer intended to indicate the generation number of the specific population. This is used primarily for GA Events.
In a fitness calculator, you have to decide and implement how you evaluate the fitness of an IIndividual with specific IGenes. It's the only required function, but should be given a lot of consideration, as many things about the GA stand and fall on how the fitness function behaves.
It is advisable to calculate (or convert the results) fitness in positive values, better fitness having higher ones, as it makes errors in selection, etc. less likely.
Adapter takes care of the logic of selecting, breeding, and populating further generations for the GA.
Most methods are independent of the concrete implementation of your domain, so an abstract class is already implemented, the only method left to implement based on your specific domain is the CrossOver, where you define how an offspring should be created from the genomes of two parents.
The GA basically only manages the current and next generations, keeps asking the Adapter for new individuals to populate with, and raises events at certain points (after being initialised, i.e. at the end of its constructor, and after every generation being finished).
You can control how many generations should be run before pausing the GA by supplying the desired number to the Run(int n) method.
Note: all of the currently implemented events make use of the custom GaEventArgs<TIndividual,TGene>, where information about the status of the population at the time of the event is saved (list of individuals + generation number)
- GaInitialised [static] - raised when the GA constructor is finishing, before anything is run. Subscribe to this event before constructing an instance of a GA if you want to have the initial population passed back through the
GaEventArgs. - GenerationComplete - raised anytime a generation in GA is selected, breeded and mutated. Subscribe to view the current status of populations.