Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
67 changes: 57 additions & 10 deletions Cargo.lock

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

12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ struct MyGenome {

// 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) {
type Context = (); // empty context for a simple mutation

fn mutate(&mut self, _ctx: &(), rate: f32, rng: &mut impl Rng) {
self.field1 += rng.random::<f32>() * rate;
}
}
Expand All @@ -45,14 +47,14 @@ fn my_fitness_fn(ent: &MyGenome) -> f32 {
}

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`.
// this is the `rayon` feature signature.
Vec::gen_random(100),
Vec::gen_random(&mut rng, 100),
FitnessEliminator::new_with_default(my_fitness_fn),
MitosisRepopulator::new(0.25), // 25% mutation rate
MitosisRepopulator::new(0.25, ()), // 25% mutation rate, empty context
);

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

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.
That is the minimal code for a working genetic algorithm on default features. 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.
2 changes: 1 addition & 1 deletion genetic-rs-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
itertools = { version = "0.14.0", optional = true }
rand = { version = "0.9.2", optional = true }
rayon = { version = "1.10.0", optional = true }
rayon = { version = "1.11.0", optional = true }
82 changes: 53 additions & 29 deletions genetic-rs-common/src/builtin/eliminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ where

#[cfg(feature = "knockout")]
mod knockout {
use std::cmp::Ordering;

use super::*;

/// A distinct type to help clarify the result of a knockout function.
Expand All @@ -133,14 +135,14 @@ mod knockout {
First,

/// The second genome parameter won.
Second
Second,
}

impl Into<usize> for KnockoutWinner {
fn into(self) -> usize {
match self {
Self::First => 0,
Self::Second => 1,
impl From<KnockoutWinner> for usize {
fn from(winner: KnockoutWinner) -> Self {
match winner {
KnockoutWinner::First => 0,
KnockoutWinner::Second => 1,
}
}
}
Expand All @@ -151,7 +153,16 @@ mod knockout {
fn not(self) -> Self::Output {
match self {
Self::First => Self::Second,
Self::Second => Self::First
Self::Second => Self::First,
}
}
}

impl From<Ordering> for KnockoutWinner {
fn from(ordering: Ordering) -> Self {
match ordering {
Ordering::Less | Ordering::Equal => Self::First,
Ordering::Greater => Self::Second,
}
}
}
Expand All @@ -164,8 +175,8 @@ mod knockout {
}

impl<G, F> KnockoutFn<G> for F
where
F: Fn(&G, &G) -> KnockoutWinner
where
F: Fn(&G, &G) -> KnockoutWinner,
{
fn knockout(&self, a: &G, b: &G) -> KnockoutWinner {
(self)(a, b)
Expand Down Expand Up @@ -201,10 +212,17 @@ mod knockout {
}

impl ActionIfOdd {
pub(crate) fn exec<G>(&self, rng: &mut impl rand::Rng, genomes: &mut Vec<G>, output: &mut Vec<G>) {
pub(crate) fn exec<G>(
&self,
rng: &mut impl rand::Rng,
genomes: &mut Vec<G>,
output: &mut Vec<G>,
) {
match self {
Self::Panic => panic!("Knockout eliminator received an odd number of genomes"),
Self::DeleteSingle => { genomes.remove(rng.random_range(0..genomes.len())); },
Self::DeleteSingle => {
genomes.remove(rng.random_range(0..genomes.len()));
}
Self::KeepSingle => output.push(genomes.remove(rng.random_range(0..genomes.len()))),
};
}
Expand All @@ -223,7 +241,7 @@ mod knockout {
}

impl<G, K> KnockoutEliminator<G, K>
where
where
G: FeatureBoundedGenome,
K: FeatureBoundedKnockoutFn<G>,
{
Expand All @@ -238,7 +256,7 @@ mod knockout {
}

impl<G, K> Eliminator<G> for KnockoutEliminator<G, K>
where
where
G: FeatureBoundedGenome,
K: FeatureBoundedKnockoutFn<G>,
{
Expand All @@ -252,34 +270,40 @@ mod knockout {
let mut rng = rand::rng();
let mut output = Vec::with_capacity(genomes.len() / 2);

if len % 2 != 0 {
if !len.is_multiple_of(2) {
self.action_if_odd.exec(&mut rng, &mut genomes, &mut output);
}

debug_assert!(genomes.len() % 2 == 0);
debug_assert!(genomes.len().is_multiple_of(2));

#[cfg(not(feature = "rayon"))]
{
use itertools::Itertools;

output.extend(genomes
.drain(..)
.tuples()
.map(|(a, b)| match self.knockout_fn.knockout(&a, &b) {
KnockoutWinner::First => a,
KnockoutWinner::Second => b,
})
.collect::<Vec<_>>()
output.extend(
genomes
.drain(..)
.tuples()
.map(|(a, b)| match self.knockout_fn.knockout(&a, &b) {
KnockoutWinner::First => a,
KnockoutWinner::Second => b,
})
.collect::<Vec<_>>(),
);
}

#[cfg(feature = "rayon")]
{
output.extend(genomes
.par_drain(..)
.chunks(2)
.map(|mut c| c.remove(<KnockoutWinner as Into<usize>>::into(self.knockout_fn.knockout(&c[0], &c[1]))))
.collect::<Vec<_>>()
output.extend(
genomes
.par_drain(..)
.chunks(2)
.map(|mut c| {
c.remove(<KnockoutWinner as Into<usize>>::into(
self.knockout_fn.knockout(&c[0], &c[1]),
))
})
.collect::<Vec<_>>(),
);
}

Expand All @@ -289,4 +313,4 @@ mod knockout {
}

#[cfg(feature = "knockout")]
pub use knockout::*;
pub use knockout::*;
Loading