Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f86fad4
use full path qualification in macros
HyperCodec May 3, 2025
caa4719
implement basic eliminator/repopulator types
HyperCodec May 3, 2025
c7ab90b
implement speciation (needs improvement), fix some bugs, and test the…
HyperCodec May 3, 2025
f7b2eda
mess with some of the lib.rs docs
HyperCodec May 3, 2025
01b0066
remove Prunable macro and simplify division macro
HyperCodec May 3, 2025
65f4571
fix examples
HyperCodec May 3, 2025
51615f0
satisfy clippy
HyperCodec May 3, 2025
b29dac7
fix rayon feature
HyperCodec May 3, 2025
6ecfcec
cargo fmt
HyperCodec May 3, 2025
6739309
fix tracing feature
HyperCodec May 3, 2025
2aebd6e
add workflow dispatch to CI/CD
HyperCodec Oct 6, 2025
97c6510
rewrite genrand macro
HyperCodec Oct 6, 2025
b54d820
rename some traits and rewrite crossover macro
HyperCodec Oct 6, 2025
b09f9e7
fix macro compile errors
HyperCodec Oct 6, 2025
6f3b73b
rewrite the rest of hte macros
HyperCodec Oct 6, 2025
8b1acd2
cargo fmt
HyperCodec Oct 6, 2025
f0e9e75
fix compile error from removing dependency
HyperCodec Oct 6, 2025
f203d76
fix examples
HyperCodec Oct 6, 2025
c688b6c
Merge pull request #96 from HyperCodec/split-nextgens
HyperCodec Oct 6, 2025
e9b9454
replace spaghetti code in crossover
HyperCodec Oct 6, 2025
b0dad73
define feature bounded traits
HyperCodec Oct 6, 2025
c97cc6a
implement feature bounding on repopulators and eliminators
HyperCodec Oct 6, 2025
a750201
cargo fmt
HyperCodec Oct 6, 2025
c686812
add missing doc comments to fix clippy
HyperCodec Oct 6, 2025
99f92c7
fix some doc comments
HyperCodec Oct 6, 2025
c4c862f
update README
HyperCodec Oct 6, 2025
8375c45
fix doctest in README
HyperCodec Oct 6, 2025
ac74e81
remove incorrectly qualified path
HyperCodec Oct 6, 2025
efe193a
update version number and toml files
HyperCodec Oct 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches: [main]
pull_request:
workflow_dispatch:

jobs:
test:
Expand Down
99 changes: 12 additions & 87 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ members = ["genetic-rs", "genetic-rs-common", "genetic-rs-macros"]
resolver = "2"

[workspace.package]
version = "0.6.0"
version = "1.0.0"
authors = ["HyperCodec"]
homepage = "https://github.com/hypercodec/genetic-rs"
repository = "https://github.com/hypercodec/genetic-rs"
license = "MIT"
edition = "2021"
edition = "2021"
54 changes: 17 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,74 +6,54 @@

A small crate for quickstarting genetic algorithm projects.

### How to Use
*note: if you are interested in implementing NEAT with this, try out the [neat](https://crates.io/crates/neat) crate*

### Features
First off, this crate comes with the `builtin`, `crossover`, and `genrand` features by default. If you want it to be parallelized, you can add the `rayon` feature. If you want your crossover to be speciated, you can add the `speciation` feature.

Once you have eveything imported as you wish, you can define your genome and impl the required traits:
### How to Use
> [!NOTE]
> If you are interested in implementing NEAT with this, try out the [neat](https://crates.io/crates/neat) crate

Here's a simple genetic algorithm.

```rust
#[derive(Clone, Debug)] // clone is currently a required derive for pruning nextgens.
use genetic_rs::prelude::*;

// `Mitosis` can be derived if both `Clone` and `RandomlyMutable` are present.
#[derive(Clone, Debug, Mitosis)]
struct MyGenome {
field1: f32,
}

// required in all of the builtin functions as requirements of `DivsionReproduction` and `CrossoverReproduction`
// required in all of the builtin Repopulators as requirements of `Mitosis` and `Crossover`
impl RandomlyMutable for MyGenome {
fn mutate(&mut self, rate: f32, rng: &mut impl Rng) {
self.field1 += rng.gen::<f32>() * rate;
}
}

// required for `division_pruning_nextgen`.
impl DivsionReproduction for MyGenome {
fn divide(&self, rng: &mut impl ng) -> Self {
let mut child = self.clone();
child.mutate(0.25, rng); // use a constant mutation rate when spawning children in pruning algorithms.
child
}
}

// required for the builtin pruning algorithms.
impl Prunable for MyGenome {
fn despawn(self) {
// unneccessary to implement this function, but it can be useful for debugging and cleaning up genomes.
println!("{:?} died", self);
}
}

// allows us to use `Vec::gen_random` for the initial population.
// allows us to use `Vec::gen_random` for the initial population. note that `Vec::gen_random` has a slightly different function signature depending on whether the `rayon` feature is enabled.
impl GenerateRandom for MyGenome {
fn gen_random(rng: &mut impl rand::Rng) -> Self {
fn gen_random(rng: &mut impl Rng) -> Self {
Self { field1: rng.gen() }
}
}
```

Once you have a struct, you must create your fitness function:
```rust
fn my_fitness_fn(ent: &MyGenome) -> f32 {
// this just means that the algorithm will try to create as big a number as possible due to fitness being directly taken from the field.
// in a more complex genetic algorithm, you will want to utilize `ent` to test them and generate a reward.
ent.field1
}
```


Once you have your reward function, you can create a `GeneticSim` object to manage and control the evolutionary steps:

```rust
fn main() {
let mut rng = rand::rng();
let mut sim = GeneticSim::new(
// you must provide a random starting population.
// size will be preserved in builtin nextgen fns, but it is not required to keep a constant size if you were to build your own nextgen function.
// in this case, the compiler can infer the type of `Vec::gen_random` because of the input of `my_fitness_fn`.
Vec::gen_random(&mut rng, 100),
my_fitness_fn,
division_pruning_nextgen,
// this is the `rayon` feature signature.
Vec::gen_random(100),
FitnessEliminator::new_with_default(my_fitness_fn),
MitosisRepopulator::new(0.25), // 25% mutation rate
);

// perform evolution (100 gens)
Expand All @@ -83,7 +63,7 @@ fn main() {
}
```

That is the minimal code for a working pruning-based genetic algorithm. You can [read the docs](https://docs.rs/genetic-rs) or [check the examples](/genetic-rs/examples/) for more complicated systems.
That is the minimal code for a working genetic algorithm on default features (+ rayon). You can [read the docs](https://docs.rs/genetic-rs) or [check the examples](/genetic-rs/examples/) for more complicated systems. I highly recommend looking into crossover reproduction, as it tends to produce better results than mitosis.

### License
This project falls under the `MIT` license.
9 changes: 4 additions & 5 deletions genetic-rs-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ categories = ["algorithms", "science", "simulation"]

[features]
default = ["builtin", "genrand", "crossover"]
builtin = []
builtin = ["dep:rand"]
crossover = ["builtin"]
speciation = ["crossover"]
genrand = []
genrand = ["dep:rand"]
rayon = ["dep:rayon"]
tracing = ["dep:tracing"]

Expand All @@ -27,7 +27,6 @@ features = ["crossover", "speciation"]
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
replace_with = "0.1.7"
rand = "0.9.0"
rand = { version = "0.9.2", optional = true }
rayon = { version = "1.10.0", optional = true }
tracing = { version = "0.1.41", optional = true }
tracing = { version = "0.1.41", optional = true }
Loading