This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
NeatEvolution is a Julia package implementing NeuroEvolution of Augmenting Topologies (NEAT). This library is designed as:
- A platform for running ad-hoc evolution experiments to determine feasibility
- A starting point for building custom neuroevolution solutions
Not intended as: A quick-start platform for game development.
The package is based on the neat-python implementation and provides equivalent functionality in pure Julia.
# Run all tests
julia --project -e 'using Pkg; Pkg.test()'
# Run tests interactively (for development)
julia --project -e 'using Pkg; Pkg.test("NeatEvolution"; test_args=["--verbose"])'# Run the XOR example
julia --project examples/xor/evolve.jl
# Other examples: cartpole, checkpoint_demo, ctrnn_oscillator,
# inverted_pendulum, inverted_double_pendulum, iznn_pattern,
# lorenz_ctrnn, sequence
julia --project examples/cartpole/evolve.jl# Activate the package environment
julia --project
# Install dependencies
julia --project -e 'using Pkg; Pkg.instantiate()'
# Load package in REPL for interactive development
julia --project -e 'using NeatEvolution'# Build documentation locally
julia --project=docs docs/make.jlThe project uses GitHub Actions for CI, testing on:
- Julia versions: 1.10, 1.11, 1.12, nightly
- Operating systems: Ubuntu, Windows, macOS
The NEAT implementation is organized into the following modules:
Handles gene attributes (weights, biases, etc.) with:
FloatAttribute: Floating-point parameters (weights, biases)BoolAttribute: Boolean parameters (enabled connections)StringAttribute: Categorical parameters (activation functions)
Each attribute type handles initialization, mutation, and validation.
Defines gene representations:
NodeGene: Represents neurons with bias, response, activation, and aggregationConnectionGene: Represents connections with weight and enabled status
Supports mutation, crossover, and distance calculations for speciation.
Core genome representation and operations:
Genome: Container for nodes and connections- Initialization strategies (full, partial connectivity)
- Structural mutations (add/delete nodes and connections)
- Crossover between parent genomes
- Genetic distance computation for speciation
Configuration system using TOML format:
Config: Main configuration containerGenomeConfig: Genome structure and mutation parametersSpeciesConfig: Speciation parametersStagnationConfig: Stagnation detection parametersReproductionConfig: Reproduction and elitism parameters
Activation functions for nodes:
- sigmoid, tanh, relu, elu, lelu, selu
- sin, gauss, softplus, identity, clamped
- inv, log, exp, abs, hat, square, cube
Extensible: add custom functions with add_activation_function!
Aggregation functions for combining inputs:
- sum, product, max, min, maxabs, median, mean
Extensible: add custom functions with add_aggregation_function!
Graph algorithms for network structure:
creates_cycle: Detects cycles for feed-forward networksrequired_for_output: Finds nodes needed for output computationfeed_forward_layers: Computes evaluation order
Speciation system:
Species: Groups genomes by genetic similaritySpeciesSet: Manages all speciesGenomeDistanceCache: Caches distance computations for efficiency
Tracks species improvement:
- Marks species as stagnant if no fitness improvement
- Protects top species (elitism)
- Uses configurable fitness functions (max, mean, etc.)
Genome reproduction and population management:
- Creates offspring through crossover and mutation
- Implements elitism (preserves best genomes)
- Computes spawn amounts based on adjusted fitness
- Manages survival threshold for breeding selection
Main evolution loop:
- Coordinates evaluation, reproduction, and speciation
- Handles extinction and reset
- Checks termination criteria
- Integrates with reporters for progress tracking
- Supports initialization with imported genomes (auto-adjusts ID counters)
Neural network evaluation:
FeedForwardNetwork: Evaluates genomes as neural networks- Computes feed-forward layers for efficient evaluation
activate!: Runs network with given inputs
Recurrent neural network evaluation:
RecurrentNetwork: Handles cyclic connections with internal state- Double-buffered values (current/previous timestep)
activate!: Runs one timestep;reset!: Clears state
Continuous-Time Recurrent Neural Network:
CTRNNNetwork: Forward Euler integration with per-node time constantsadvance!: Integrates forward by specified time with configurable step sizeset_node_value!: Injects state into network buffers
Izhikevich spiking neural network:
IZNNNetwork: Biologically realistic spiking dynamics (a, b, c, d parameters)- Spike-based communication (binary 0/1)
- Named presets for common neuron types (regular spiking, fast spiking, etc.)
Progress reporting:
StdOutReporter: Prints generation statisticsStatisticsReporter: Collects per-generation fitness and species data for analysis- Tracks fitness, species, and population size
- Reports solutions and extinction events
Statistics collection and export:
StatisticsReporter: Records fitness statistics and species data each generationsave_statistics: Exports collected data to CSV filesbest_genome,best_genomes,best_unique_genomes: Query best performers
Evolution checkpointing:
Checkpointer: Reporter that saves population state at configurable intervalssave_checkpoint/restore_checkpoint: Manual save/restore via Serialization- Supports generation-based and time-based intervals
JSON export/import for model sharing:
export_network_json/import_network_json: Single genome serializationexport_population_json: Batch export of top genomes- Compatible with neat-python JSON format
Configuration validation:
- Validates TOML config files against known parameter names
- Detects typos and unknown parameters
- Warns about missing recommended parameters
Utility functions:
- Statistical functions (mean, median, stdev, variance)
tmean: Trimmed mean;softmax: Softmax normalization- Stat function registry for configuration
src/
├── NeatEvolution.jl # Main module with exports
├── attributes.jl # Attribute system
├── genes.jl # Node and Connection genes
├── genome.jl # Genome with mutation/crossover
├── config.jl # Configuration system (TOML)
├── validation.jl # Config validation and typo detection
├── species.jl # Speciation logic
├── reproduction.jl # Reproduction and elitism
├── stagnation.jl # Species stagnation tracking
├── population.jl # Main evolution loop
├── activations.jl # Activation functions
├── aggregations.jl # Aggregation functions
├── graphs.jl # Graph algorithms
├── feedforward.jl # Feed-forward network evaluation
├── recurrent.jl # Recurrent network evaluation
├── ctrnn.jl # Continuous-time recurrent network
├── iznn.jl # Izhikevich spiking network
├── reporting.jl # Progress reporting
├── statistics.jl # Statistics collection and CSV export
├── checkpointer.jl # Evolution checkpointing
├── export.jl # JSON export/import
└── utils.jl # Math utilities
ext/
├── NeatEvolutionVisualizationExt.jl # Plots.jl visualization extension
└── NeatEvolutionGraphMakieExt.jl # GraphMakie interactive visualization
examples/
├── xor/ # Classic XOR benchmark
├── cartpole/ # Cart-pole balancing
├── sequence/ # Sequence prediction (recurrent)
├── ctrnn_oscillator/ # CTRNN oscillator
├── iznn_pattern/ # Izhikevich spiking patterns
├── checkpoint_demo/ # Checkpointing demonstration
├── inverted_pendulum/ # Inverted pendulum control
├── inverted_double_pendulum/ # Double pendulum control
└── lorenz_ctrnn/ # Lorenz attractor with CTRNN
test/
├── runtests.jl # Test runner
├── test_attributes.jl # Attribute system tests
├── test_genes.jl # Gene tests
├── test_genome_operations.jl # Genome operation tests
├── test_config.toml # Test configuration
├── test_speciation.jl # Speciation tests
├── test_stagnation.jl # Stagnation tests
├── test_reproduction.jl # Reproduction tests
├── test_recurrent.jl # Recurrent network tests
├── test_ctrnn.jl # CTRNN tests
├── test_iznn.jl # Izhikevich network tests
├── test_ctrnn_iznn_genes.jl # CTRNN/IZNN gene tests
├── test_checkpointer.jl # Checkpointing tests
├── test_validation.jl # Validation tests
├── test_population_seeding.jl # Population seeding tests
└── test_misc_coverage.jl # Additional coverage tests
- UUID:
fcc92617-70eb-4c43-847c-323c44b5224c - Stdlib dependencies: Dates, Random, Serialization, Statistics, TOML
- External dependencies: FunctionWrappers, JSON
Configuration files use TOML format with the following sections:
pop_size: Population sizefitness_criterion: "max", "min", or "mean"fitness_threshold: Target fitness for solutionreset_on_extinction: Whether to reset population on extinctionno_fitness_termination: Run for fixed generations
Network structure and mutation parameters:
num_inputs,num_outputs,num_hiddenfeed_forward: true for feed-forward networksinitial_connection: "full", "partial", etc.- Mutation probabilities:
conn_add_prob,node_add_prob, etc. - Attribute parameters for bias, response, weight, activation, etc.
compatibility_threshold: Distance threshold for speciation
species_fitness_func: "max", "mean", etc.max_stagnation: Generations before marking stagnantspecies_elitism: Number of top species to protect
elitism: Number of best genomes to preservesurvival_threshold: Fraction of species for breedingmin_species_size: Minimum species size
using NeatEvolution
NeatEvolution.add_activation_function!(:custom, x -> tanh(x^2))using NeatEvolution
NeatEvolution.add_aggregation_function!(:custom, xs -> sum(xs.^2))Fitness functions receive a list of (genome_id, genome) pairs and config:
function eval_genomes(genomes, config)
for (gid, genome) in genomes
# Evaluate genome
net = FeedForwardNetwork(genome, config.genome_config)
# ... compute fitness ...
genome.fitness = fitness_value
end
endImport evolved networks from JSON (neat-python or NeatEvolution.jl format) and use them to seed new populations:
using NeatEvolution
config = load_config("config.toml")
# Import genomes from JSON files
imported_genomes = [
import_network_json("winner1.json", config.genome_config),
import_network_json("winner2.json", config.genome_config)
]
# Create population seeded with imported genomes
# The constructor automatically adjusts genome IDs, node IDs, and innovation numbers
# to prevent conflicts with newly created genomes
pop = Population(config, imported_genomes)
# The population now contains:
# - All imported genomes (with their original IDs preserved)
# - Newly created random genomes to fill remaining slots
# - Properly adjusted counters ensuring no ID conflicts
# Continue evolution
winner = run!(pop, eval_genomes, n_generations)Options:
fill_remaining=true(default): Fill population to configured size with random genomesfill_remaining=false: Population contains only the imported genomes
This is useful for:
- Transfer learning: Start evolution with pre-trained networks from a related task
- Cross-library experiments: Import neat-python networks and continue evolution in Julia
- Population seeding: Bootstrap evolution with known good solutions
- Checkpoint/resume: Save and restore evolution state across sessions
- Uses Julia structs instead of Python classes
- Leverages multiple dispatch for polymorphic behavior
- TOML configuration (Julia-native format)
- Follows neat-python's core algorithms for compatibility
- Pure Julia implementation (external deps: JSON, FunctionWrappers only)
- Optional visualization via package extensions (Plots.jl, GraphMakie)
When implementing new functionality, follow Julia package conventions and ensure compatibility with Julia 1.10+ (LTS) through nightly builds.