Class NEAT<T>
- Namespace
- AiDotNet.NeuralNetworks
- Assembly
- AiDotNet.dll
Represents a NeuroEvolution of Augmenting Topologies (NEAT) algorithm implementation, which evolves neural networks through genetic algorithms.
public class NEAT<T> : NeuralNetworkBase<T>, INeuralNetworkModel<T>, INeuralNetwork<T>, IFullModel<T, Tensor<T>, Tensor<T>>, IModel<Tensor<T>, Tensor<T>, ModelMetadata<T>>, IModelSerializer, ICheckpointableModel, IParameterizable<T, Tensor<T>, Tensor<T>>, IFeatureAware, IFeatureImportance<T>, ICloneable<IFullModel<T, Tensor<T>, Tensor<T>>>, IGradientComputable<T, Tensor<T>, Tensor<T>>, IJitCompilable<T>, IInterpretableModel<T>, IInputGradientComputable<T>, IDisposable
Type Parameters
TThe numeric type used for calculations, typically float or double.
- Inheritance
-
NEAT<T>
- Implements
- Inherited Members
- Extension Methods
Remarks
NEAT is an evolutionary algorithm that creates and evolves neural network topologies along with connection weights. Unlike traditional neural networks with fixed structures, NEAT starts with simple networks and gradually adds complexity through evolution. It uses genetic operators like mutation and crossover, along with speciation to protect innovation, to evolve networks that solve specific problems without requiring manual design of the network architecture.
For Beginners: NEAT is a way to grow neural networks through evolution rather than training them with fixed structures.
Think of NEAT like breeding plants to get better features:
- Instead of designing a neural network by hand, you start with simple networks
- These networks "reproduce" and "mutate" over generations
- Networks that perform better on your task are more likely to pass on their "genes"
- Over time, the networks evolve complex structures that solve your problem well
The key differences from traditional neural networks:
- The structure (connections between neurons) evolves along with the weights
- Networks can grow more complex over time by adding new neurons and connections
- You work with a population of many networks, not just one
- Instead of training with gradient descent, you use evolution to improve performance
NEAT is particularly good for:
- Problems where you don't know the ideal network structure
- Reinforcement learning tasks (like game playing)
- Finding novel solutions that a human designer might not think of
Constructors
NEAT(NeuralNetworkArchitecture<T>, int, double, double, ILossFunction<T>?)
Initializes a new instance of the NEAT<T> class with the specified architecture and evolution parameters.
public NEAT(NeuralNetworkArchitecture<T> architecture, int populationSize, double mutationRate = 0.1, double crossoverRate = 0.75, ILossFunction<T>? lossFunction = null)
Parameters
architectureNeuralNetworkArchitecture<T>The neural network architecture defining input and output sizes.
populationSizeintThe number of individual genomes in the population.
mutationRatedoubleThe probability of mutation occurring during reproduction. Default is 0.1.
crossoverRatedoubleThe probability of crossover occurring during reproduction. Default is 0.75.
lossFunctionILossFunction<T>
Remarks
This constructor initializes a NEAT algorithm with the specified parameters. Unlike traditional neural networks, NEAT doesn't use a fixed architecture with predefined layers. Instead, it uses the architecture primarily to determine input and output sizes. The constructor initializes a population of minimal network structures (genomes) that will evolve over time through the evolutionary process.
For Beginners: This creates a new NEAT system with your chosen settings.
When creating a NEAT system, you specify:
Architecture: Mainly used to define how many inputs and outputs your networks need
- For example, if solving a game, inputs might be game state variables
- Outputs might be action choices the AI can make
Population Size: How many different neural networks to evolve at once
- Larger populations explore more possibilities but need more computing power
Mutation Rate: How often random changes occur (default 0.1 or 10%)
- Controls the amount of exploration vs. stability
Crossover Rate: How often networks combine vs. just being copied (default 0.75 or 75%)
- Controls how much genetic material is mixed between solutions
After creation, NEAT initializes a starting population of very simple networks that will grow more complex through evolution.
Properties
ParameterCount
Gets the total number of trainable parameters (connections) in the best genome.
public override int ParameterCount { get; }
Property Value
Remarks
In NEAT, parameters are the connection weights in a genome. This property returns the number of connections in the best-performing genome in the population.
Methods
CreateNewInstance()
Creates a new instance of the NEAT model with the same architecture and evolutionary parameters.
protected override IFullModel<T, Tensor<T>, Tensor<T>> CreateNewInstance()
Returns
- IFullModel<T, Tensor<T>, Tensor<T>>
A new instance of the NEAT model.
Remarks
This method creates a new NEAT model with the same architecture, population size, mutation rate, and crossover rate as the current instance. The new instance starts with a fresh population, making it useful for restarting evolution with the same parameters or for creating parallel evolutionary runs.
For Beginners: This creates a brand new NEAT system with the same settings.
This is useful when you want to:
- Start over with a fresh population but keep the same settings
- Run multiple separate evolutions with identical parameters
- Create a "clean slate" version of a successful setup
The new NEAT system will have:
- The same number of inputs and outputs
- The same population size and mutation/crossover rates
- A brand new initial population (not copying any evolved networks)
This effectively creates a "twin" of your NEAT system, but at the starting point rather than with any of the evolved progress.
DeserializeNetworkSpecificData(BinaryReader)
Deserializes NEAT-specific data from a binary reader.
protected override void DeserializeNetworkSpecificData(BinaryReader reader)
Parameters
readerBinaryReaderThe binary reader to read from.
Remarks
This method loads the state of a previously saved NEAT model from a binary stream. It restores the evolutionary parameters, innovation number, and all genomes in the population, allowing evolution to continue from exactly where it left off.
For Beginners: This loads a complete NEAT system from a saved file.
When loading the NEAT model:
- Population size, mutation rate, and crossover rate are restored
- The innovation number is restored
- Every genome in the population is recreated with all its connections
This lets you:
- Continue evolution from where you left off
- Use previously evolved populations
- Compare or combine results from different runs
EvolvePopulation(Func<Genome<T>, T>, int)
Evolves the population over a specified number of generations using the provided fitness function.
public void EvolvePopulation(Func<Genome<T>, T> fitnessFunction, int generations)
Parameters
fitnessFunctionFunc<Genome<T>, T>A function that evaluates the fitness of each genome.
generationsintThe number of generations to evolve.
Remarks
This method drives the evolutionary process over multiple generations. For each generation, it evaluates the fitness of each genome using the provided fitness function, sorts the population by fitness, and creates a new population through selection, crossover, and mutation. Through this process, the networks evolve to better solve the specified problem.
For Beginners: This method runs the evolutionary process for a specified number of generations.
The evolution process works like this:
Evaluate each network:
- The provided fitness function tests how well each network performs
- Higher fitness scores mean better performance
Sort networks by fitness:
- The best-performing networks are prioritized for reproduction
Create a new population:
- Keep the very best network unchanged (called "elitism")
- Create new networks through either: a) Crossover: Combining two parent networks b) Mutation: Copying and modifying a single parent
Repeat for the specified number of generations
Each generation should produce slightly better networks as successful traits are selected for and new beneficial mutations occur.
The fitness function you provide is crucial - it's what defines "good performance" and guides the entire evolutionary process.
GetModelMetadata()
Gets metadata about the NEAT model.
public override ModelMetadata<T> GetModelMetadata()
Returns
- ModelMetadata<T>
A ModelMetaData object containing information about the NEAT model.
Remarks
This method returns comprehensive metadata about the NEAT model, including its architecture, evolutionary parameters, and population statistics. This information is useful for model management, tracking experiments, and reporting results.
For Beginners: This provides detailed information about your NEAT system.
The metadata includes:
- What this model is and what it does
- Population size and evolutionary parameters
- Statistics about the current population
- Information about the best-performing network
This information is useful for:
- Tracking your experiments
- Comparing different NEAT runs
- Documenting your work
- Understanding the evolved solution
InitializeLayers()
Initializes the layers of the neural network.
protected override void InitializeLayers()
Remarks
This method is intentionally left empty because NEAT does not use fixed layers like traditional neural networks. Instead, NEAT evolves the network structure dynamically through the evolutionary process, adding nodes and connections as needed.
For Beginners: This method is intentionally empty because NEAT works differently from traditional neural networks.
In traditional neural networks:
- You define specific layers (input, hidden, output)
- Each layer has a fixed number of neurons
- The connections between layers are predetermined
In NEAT:
- Networks don't have fixed layers
- The structure evolves dynamically
- Neurons and connections are added gradually through evolution
This fundamental difference is why this method doesn't need to do anything in NEAT. The network structure is defined by the genome, not by predefined layers.
IsReadyToPredict()
Checks if the NEAT model is ready to make predictions.
public bool IsReadyToPredict()
Returns
- bool
True if the model is ready; otherwise, false.
Remarks
This method checks if the NEAT model has a population with at least one genome that can be used for making predictions. It's useful for determining if the model has been properly initialized and evolved.
For Beginners: This checks if your NEAT system is ready to use.
It verifies that:
- The population exists
- There is at least one genome in the population
- At least one genome has connections that can process inputs
This is helpful for error checking before trying to use the model for predictions or continuing evolution.
Predict(Tensor<T>)
Predicts output values for input data using the best genome in the population.
public override Tensor<T> Predict(Tensor<T> input)
Parameters
inputTensor<T>The input tensor to process.
Returns
- Tensor<T>
The output tensor after processing.
Remarks
This method uses the highest-fitness genome in the population to make predictions. It activates the genome's neural network with the provided input data and returns the resulting output activations. For batch inputs, it processes each sample independently.
For Beginners: This method uses the best evolved network to make predictions.
When making a prediction:
- NEAT uses the highest-performing network from the population
- The input data is fed into this network
- The network processes the data through its evolved structure
- The resulting output values are returned
Unlike traditional neural networks with fixed structures, the network used here has evolved its structure through the evolutionary process, potentially developing complex and unique connection patterns that solve the problem effectively.
SerializeNetworkSpecificData(BinaryWriter)
Serializes NEAT-specific data to a binary writer.
protected override void SerializeNetworkSpecificData(BinaryWriter writer)
Parameters
writerBinaryWriterThe binary writer to write to.
Remarks
This method saves the state of the NEAT model to a binary stream. It serializes the evolutionary parameters, innovation number, and all genomes in the population, allowing the complete state to be restored later.
For Beginners: This saves the complete state of your NEAT system to a file.
When saving the NEAT model:
- Population size, mutation rate, and crossover rate are saved
- The current innovation number is saved
- Every genome in the population is saved with all its connections
This allows you to:
- Save your progress and continue evolution later
- Share evolved populations with others
- Keep records of particularly successful runs
- Deploy evolved networks in applications
Train(Tensor<T>, Tensor<T>)
Trains the NEAT system using supervised learning data.
public override void Train(Tensor<T> input, Tensor<T> expectedOutput)
Parameters
inputTensor<T>The input training data tensor.
expectedOutputTensor<T>The expected output tensor.
Remarks
This method adapts NEAT to work with traditional supervised learning data. It creates a fitness function based on the mean squared error between network predictions and expected outputs, then evolves the population to minimize this error. This allows NEAT to be used in scenarios where traditional supervised learning would be applied.
For Beginners: This teaches the NEAT system using example input-output pairs.
Unlike traditional neural networks that use gradient descent, NEAT learns through evolution:
It creates a fitness function based on prediction error
- Networks that make more accurate predictions get higher fitness scores
- Networks with lower error perform better
It evolves the population for several generations
- Better networks reproduce more often
- Genetic operators (crossover and mutation) create diversity
- The population gradually improves at the task
This allows NEAT to work with supervised learning data while using its evolutionary approach to discover effective network structures.
UpdateParameters(Vector<T>)
Updates the connection weights of the best genome using the provided parameter vector.
public override void UpdateParameters(Vector<T> parameters)
Parameters
parametersVector<T>A vector containing parameters to update.
Remarks
This method allows direct parameter updates to the best genome's connection weights, enabling integration with external optimization or parameter management systems. Note that this bypasses NEAT's evolutionary mechanisms and should be used carefully.
For Beginners: This method allows direct weight updates when needed.
In traditional NEAT:
- Parameters evolve through natural selection
- Better-performing networks reproduce more often
- Parameters change through crossover and mutation
However, this method allows you to:
- Directly set connection weights on the best genome
- Integrate with external optimization algorithms
- Transfer parameters from other sources
Important: Changes may be lost if the modified genome doesn't survive selection in subsequent evolution cycles. For typical NEAT training, use the EvolvePopulation method instead.
Exceptions
- InvalidOperationException
Thrown when the best genome has no connections.
- ArgumentException
Thrown when parameter vector length doesn't match connection count.