neat 1.0.2

Crate for working with NEAT in rust
Documentation
# neat
[<img alt="github" src="https://img.shields.io/github/last-commit/hypercodec/neat" height="20">](https://github.com/hypercodec/neat)
[<img alt="crates.io" src="https://img.shields.io/crates/d/neat" height="20">](https://crates.io/crates/neat)
[<img alt="docs.rs" src="https://img.shields.io/docsrs/neat" height="20">](https://docs.rs/neat)

Implementation of the NEAT algorithm using `genetic-rs`.

### Features
- serde - Implements `Serialize` and `Deserialize` on most of the types in this crate.

*Do you like this crate and want to support it? If so, leave a ⭐*

# How To Use
The `NeuralNetwork<I, O>` struct is the main type exported by this crate. The `I` is the number of input neurons, and `O` is the number of output neurons. It implements `GenerateRandom`, `RandomlyMutable`, `Mitosis`, and `Crossover`, with a lot of customizability. This means that you can use it standalone as your organism's entire genome:
```rust
use neat::*;

fn fitness(net: &NeuralNetwork<5, 6>) -> f32 {
    // ideally you'd test multiple times for consistency,
    // but this is just a simple example.
    // it's also generally good practice to normalize your inputs between -1..1,
    // but NEAT is usually flexible enough to still work anyways
    let inputs = [1.0, 2.0, 3.0, 4.0, 5.0];
    let outputs = net.predict(inputs);

    // simple fitness: sum of outputs
    // you should replace this with a real fitness test
    outputs.iter().sum()
}

fn main() {
    let mut rng = rand::rng();
    let mut sim = GeneticSim::new(
        Vec::gen_random(&mut rng, 100),
        FitnessEliminator::new_without_observer(fitness),
        CrossoverRepopulator::new(0.25, ReproductionSettings::default()),
    );

    sim.perform_generations(100);
}
```

Or just a part of a more complex genome:
```rust,ignore
use neat::*;

#[derive(Clone, Debug)]
struct PhysicalStats {
    strength: f32,
    speed: f32,
    // ...
}

// ... implement `RandomlyMutable`, `GenerateRandom`, `Crossover`, `Default`, etc.

#[derive(Clone, Debug, GenerateRandom, RandomlyMutable, Mitosis, Crossover)]
#[randmut(create_context(name = MyGenomeMutate, derive(Default, Clone, Debug)))]
#[mitosis(create_context(name = MyGenomeReproduce, derive(Default, Clone, Debug)))]
#[crossover(with_context = MyGenomeReproduce)]
struct MyGenome {
    brain: NeuralNetwork<4, 2>,
    stats: PhysicalStats,
}

fn fitness(genome: &MyGenome) -> f32 {
    let inputs = [1.0, 2.0, 3.0, 4.0];
    let outputs = genome.brain.predict(inputs);
    // fitness uses both brain output and stats
    outputs.iter().sum::<f32>() + genome.stats.strength + genome.stats.speed
}

// main is the exact same as before
fn main() {
    let mut rng = rand::rng();
    let mut sim = GeneticSim::new(
        Vec::gen_random(&mut rng, 100),
        FitnessEliminator::new_without_observer(fitness),
        CrossoverRepopulator::new(0.25, MyGenomeReproduce::default()),
    );

    sim.perform_generations(100);
}
```

If you want more in-depth examples, look at the [examples](https://github.com/HyperCodec/neat/tree/main/examples). You can also check out the [genetic-rs docs](https://docs.rs/genetic_rs) to see what other options you have to customize your genetic simulation.

### License
This crate falls under the `MIT` license