# Neural

**Neural** is a library for Genetic Algorithms in Rust.

## Concepts

- [**Genetic Algorithm**](https://en.wikipedia.org/wiki/Genetic_algorithm) - A algorithm for solving optimization problems by simulating the process of natural selection, evolution, mutation and crossover (reproduction).
- **Gene** - A single value that represents a possible solution to a problem.
- **Chromosome** - A collection of genes that represent a possible solution to a problem.
- **Population** - A collection of chromosomes that represent potential solutions to a problem.
- **Selection** - A trait for selecting a subset of the population.
- **Crossover** - A trait for crossing over two chromosomes.

## Features

- **cli** - Allows using the `with_print` method on a `PopulationBuilder` to print the population's best chromosome after each generation and enables colored output.

## Getting Started

```sh
cargo add neural

# or add it with the cli feature
cargo add neural --features cli
```

Or add it as a dependency in your `Cargo.toml`:

```toml
[dependencies]
neural = "^0.2"

# or add it with the cli feature
[dependencies]
neural = { version = "^0.2", features = ["cli"] }
```

## Usage

```rust
use neural::{Gene, Population, PopulationBuilder, Result, TournamentSelection, UniformCrossover};
use rand::{rngs::ThreadRng, Rng};
use std::fmt::Display;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct F64(f64);

impl Eq for F64 {}

impl Display for F64 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl From<f64> for F64 {
    fn from(value: f64) -> Self {
        Self(value)
    }
}

impl From<F64> for f64 {
    fn from(val: F64) -> Self {
        val.0
    }
}

impl Gene for F64 {
    fn generate_gene<R>(rng: &mut R) -> Self
    where
        R: Rng + ?Sized,
    {
        rng.random_range(-1.0..=1.0).into()
    }
}

fn main() -> Result<()> {
    let mut pop: Population<F64, TournamentSelection, UniformCrossover, _, ThreadRng> =
        PopulationBuilder::new(None, |c| c.value.iter().map(|g: &F64| g.0).sum::<f64>())
            .with_chromo_size(50)
            .with_population_size(100)
            .with_mutation_rate(0.02)
            .with_elitism(true)
            // .with_print(true) // uncomment to print the best chromosome after each generation. requires the cli feature
            .build()?;

    let num_generations = 200;
    match pop.evolve(num_generations) {
        Some(best) => {
            println!("Evolution complete after {num_generations} generations.");
            println!(
                "Best fitness found: {}, chromosome: {:?}",
                best.fitness, best.value
            );
        }
        None => println!("Evolution ended with an empty population."),
    }

    Ok(())
}
```

## License

This library is distributed under the terms of the [MIT License](LICENSE).
