# moors
> _Evolution is a mystery_

[](https://codecov.io/gh/andresliszt/moo-rs?flag=moors)
[](https://crates.io/crates/moors)
[](https://crates.io/crates/moors)
## Overview
`moors` is the core crate of the [moo-rs](https://github.com/andresliszt/moo-rs/) project for solving multi-objective optimization problems with evolutionary algorithms. It's a pure-Rust crate for high-performance implementations of genetic algorithms
## Features
- Single Objective Genetic Algorithms (SO-GA)
- NSGA-II, NSGA-III, R-NSGA-II, Age-MOEA, REVEA, SPEA-II, IBEA (many more coming soon!)
- Pluggable operators: sampling, crossover, mutation, duplicates removal
- Flexible fitness & constraints via user-provided closures
- Built on [ndarray](https://github.com/rust-ndarray/ndarray) and [faer](https://github.com/sarah-quinones/faer-rs)
## Installation
```toml
[dependencies]
moors = "0.2.9"
```
## Quickstart
```rust
use ndarray::{Array1, Array2, Axis, stack};
use moors::{
algorithms::{AlgorithmError, Nsga2Builder},
duplicates::ExactDuplicatesCleaner,
operators::{SinglePointBinaryCrossover, BitFlipMutation, RandomSamplingBinary},
};
// problem data
const WEIGHTS: [f64; 5] = [12.0, 2.0, 1.0, 4.0, 10.0];
const VALUES: [f64; 5] = [4.0, 2.0, 1.0, 5.0, 3.0];
const CAPACITY: f64 = 15.0;
/// Compute multi-objective fitness [–total_value, total_weight]
/// Returns an Array2<f64> of shape (population_size, 2)
fn fitness_knapsack(population_genes: &Array2<f64>) -> Array2<f64> {
let weights_arr = Array1::from_vec(WEIGHTS.to_vec());
let values_arr = Array1::from_vec(VALUES.to_vec());
let total_values = population_genes.dot(&values_arr);
let total_weights = population_genes.dot(&weights_arr);
// stack two columns: [-total_values, total_weights]
stack(Axis(1), &[(-&total_values).view(), total_weights.view()]).expect("stack failed")
}
fn constraints_knapsack(population_genes: &Array2<f64>) -> Array1<f64> {
let weights_arr = Array1::from_vec(WEIGHTS.to_vec());
population_genes.dot(&weights_arr) - CAPACITY
}
fn main() -> Result<(), AlgorithmError> {
// build the NSGA-II algorithm
let mut algorithm = Nsga2Builder::default()
.fitness_fn(fitness_knapsack)
.constraints_fn(constraints_knapsack)
.sampler(RandomSamplingBinary::new())
.crossover(SinglePointBinaryCrossover::new())
.mutation(BitFlipMutation::new(0.5))
.duplicates_cleaner(ExactDuplicatesCleaner::new())
.num_vars(5)
.population_size(100)
.crossover_rate(0.9)
.mutation_rate(0.1)
.num_offsprings(32)
.num_iterations(2)
.build()?;
algorithm.run()?;
let population = algorithm.population?;
println!("Done! Population size: {}", population.len());
Ok(())
}
```
## Duplicates Cleaner
In the example above, we use `ExactDuplicatesCleaner` to remove duplicated individuals that are exactly the same (hash-based deduplication). The `CloseDuplicatesCleaner` is also available—it accepts an `epsilon` parameter to drop any individuals within an ε-ball. Note that eliminating duplicates can be computationally expensive; if you do not want to use a duplicate cleaner, pass `moors::NoDuplicatesCleaner` to the builder.
## Constraints
In **moors**, constraints (if provided) are used to enforce feasibility:
- **Feasibility dominates everything**: any individual with all constraint values ≤ 0 is preferred over any infeasible one.
- If both tournament participants are infeasible, the one with the smaller sum of positive violations wins.
- All constraints must evaluate to ≤ 0. For “>” constraints, invert the inequality in your function (multiply by –1).
- Equality constraints use an ε-tolerance:
$$g_{\text{ineq}}(x) = \bigl|g_{\text{eq}}(x)\bigr| - \varepsilon \;\le\; 0.$$
```rust
use ndarray::{Array2, Array1, Axis};
const EPSILON: f64 = 1e-6;
/// Returns an Array2 of shape (n, 2) containing two constraints for each row [x, y]:
fn constraints(genes: &Array2<f64>) -> Array2<f64> {
// Constraint 1: |x + y - 1| - EPSILON
let eq = genes.map_axis(Axis(1), |row| (row[0] + row[1] - 1.0).abs() - EPSILON);
// Constraint 2: x^2 + y^2 - 1
let ineq = genes.map_axis(Axis(1), |row| row[0].powi(2) + row[1].powi(2) - 1.0);
// Stack into two columns
stack(Axis(1), &[eq.view(), ineq.view()]).unwrap()
}
```
We know! we don't want to be stacking and using the epsilon technique everywhere. We have a helper macro to build the constraints for us
```rust
use ndarray::{Array2, Array1, Axis};
use moors::impl_constraints_fn;
const EPSILON: f64 = 1e-6;
/// Equality constraint x + y = 1
fn constraints_eq(genes: &Array2<f64>) -> Array1<f64> {
genes.map_axis(Axis(1), |row| row[0] + row[1] - 1.0)
}
/// Inequality constraint: x² + y² - 1 ≤ 0
fn constraints_ineq(genes: &Array2<f64>) -> Array1<f64> {
genes.map_axis(Axis(1), |row| row[0].powi(2) + row[1].powi(2) - 1.0)
}
constraints_fn!(
MyConstraints,
ineq = [constraints_ineq],
eq = [constraints_eq],
);
```
The macro above will generate a struct `MyConstraints` that can be passed to any `moors` algorithm. This macro accepts optional arguments `lower_bound` and `upper_bound` both for now `f64` that are used to control the lower and upper bound of each gene.
If the optimization problem does not have any constraint, then use in the builder the struct `moors::NoConstraints`
## Contributing
Contributions welcome! Please read the [contribution guide](https://andresliszt.github.io/moo-rs/development) and open issues or PRs in the relevant crate’s repository
## License
This project is licensed under the MIT License.