11# NSGA‑II Rust‑Core
22
3- A Rust implementation of the NSGA‑II multi‑objective evolutionary algorithm. The crate provides
4- non‑dominated sorting, crowding distance, simulated binary crossover, polynomial mutation, and
5- tournament selection. Objective evaluation and dominance comparisons run in parallel through Rayon.
6- Constraint handling is supported via the ` Problem ` trait, where infeasible solutions are
7- automatically ranked below feasible ones based on total violation. Per‑generation Pareto front
8- snapshots, hypervolume indicator tracking, and convergence‑based early stopping are available
9- through ` RunResult ` . Algorithm parameters are configured through a builder‑style API.
3+ A high‑performance Rust implementation of the NSGA‑II multi‑objective evolutionary algorithm.
4+ The crate provides:
5+
6+ - fast non‑dominated sorting
7+ - crowding distance
8+ - simulated binary crossover (SBX)
9+ - polynomial mutation
10+ - tournament selection
11+ - constraint handling
12+ - strict and auto‑adjusting hypervolume
13+ - IGD (Inverted Generational Distance)
14+ - per‑generation Pareto snapshots
15+ - early stopping
16+ - reproducible runs via optional RNG seeding
17+
18+ Objective evaluation and dominance checks run in parallel through Rayon.
19+ Algorithm parameters are configured through a builder‑style API.
1020
1121---
1222
1323## Modules
1424
15- - ` problem ` — the ` Problem ` trait and built‑in problems
16- - ` evolve ` — the NSGA‑II engine and ` RunResult `
17- - ` sort ` — non‑dominated sorting and crowding distance
18- - ` data ` — core data structures
19- - ` metrics ` — hypervolume indicator
25+ - ` problem ` — the ` Problem ` trait and built‑in problems
26+ - ` evolve ` — NSGA‑II engine and ` RunResult `
27+ - ` sort ` — non‑dominated sorting and crowding distance
28+ - ` data ` — core data structures
29+ - ` metrics ` — strict HV, auto HV, IGD
2030
2131---
2232
2333## Usage
2434
2535### Built‑in Schaffer problem
36+
2637``` rust
2738use rs_nsga2 :: evolve :: Evolution ;
2839use rs_nsga2 :: problem :: Schaffer ;
@@ -37,6 +48,7 @@ fn main() {
3748```
3849
3950### Custom problem
51+
4052``` rust
4153use rs_nsga2 :: evolve :: Evolution ;
4254use rs_nsga2 :: problem :: Problem ;
@@ -64,6 +76,7 @@ fn main() {
6476```
6577
6678### Constrained problem
79+
6780``` rust
6881use rs_nsga2 :: evolve :: Evolution ;
6982use rs_nsga2 :: problem :: Problem ;
@@ -80,8 +93,7 @@ impl Problem for ConstrainedProblem {
8093 vec! [x [0 ], x [1 ]]
8194 }
8295 fn constraint_violations (& self , x : & [f64 ]) -> Vec <f64 > {
83- // x[0] + x[1] >= 2.0, encoded as 2.0 - x[0] - x[1] <= 0
84- vec! [2.0 - x [0 ] - x [1 ]]
96+ vec! [2.0 - x [0 ] - x [1 ]] // x0 + x1 >= 2
8597 }
8698}
8799
@@ -95,6 +107,7 @@ fn main() {
95107```
96108
97109### Hypervolume tracking and early stopping
110+
98111``` rust
99112use rs_nsga2 :: evolve :: Evolution ;
100113use rs_nsga2 :: problem :: Schaffer ;
@@ -106,87 +119,96 @@ fn main() {
106119 . evolve ();
107120
108121 println! (" Generations completed: {}" , result . generations_completed);
109-
110- for (gen , hv ) in result . hypervolume_history. iter (). enumerate () {
111- println! (" Generation {}: hypervolume = {:.4}" , gen + 1 , hv );
112- }
113-
114- for ind in & result . pareto_front {
115- println! (" {:?} -> {:?}" , ind . features, ind . objectives);
116- }
117122}
118123```
119124
120- ### Hypervolume of an arbitrary front
125+ ---
126+
127+ ## Metrics
128+
129+ ### Strict hypervolume
130+
121131``` rust
122- use rs_nsga2 :: metrics :: hypervolume_2d;
132+ use rs_nsga2 :: metrics :: hypervolume_2d_strict;
133+ ```
123134
124- fn main () {
125- let front = vec! [
126- vec! [0.1 , 3.9 ],
127- vec! [1.0 , 1.0 ],
128- vec! [2.5 , 0.5 ],
129- vec! [3.8 , 0.1 ],
130- ];
131- let reference = vec! [5.0 , 5.0 ];
132- let hv = hypervolume_2d (& front , & reference );
133- println! (" Hypervolume: {:.4}" , hv );
134- }
135+ Strict HV requires the reference point to dominate the front.
136+
137+ ### Auto‑adjusting hypervolume
138+
139+ ``` rust
140+ use rs_nsga2 :: metrics :: hypervolume_2d_auto;
135141```
136142
137- ### Custom algorithm parameters
143+ Auto HV expands the reference point minimally to avoid panics.
144+
145+ ### IGD
146+
138147``` rust
139- use rs_nsga2 :: evolve :: Evolution ;
140- use rs_nsga2 :: problem :: Schaffer ;
148+ use rs_nsga2 :: metrics :: igd;
141149
142150fn main () {
143- let result = Evolution :: new (Schaffer , 100 , 300 )
144- . with_crossover_param (15.0 )
145- . with_mutation_param (10.0 )
146- . evolve ();
147-
148- for ind in & result . pareto_front {
149- println! (" {:?} -> {:?}" , ind . features, ind . objectives);
150- }
151+ let true_front = vec! [vec! [0.0 , 1.0 ], vec! [1.0 , 0.0 ]];
152+ let obtained = vec! [vec! [0.1 , 0.9 ], vec! [0.8 , 0.2 ]];
153+ let d = igd (& true_front , & obtained );
154+ println! (" IGD: {}" , d );
151155}
152156```
153157
154158---
155159
156160## Algorithm
157161
158- Each generation applies binary tournament selection, SBX crossover, and polynomial mutation to
159- produce offspring. Parent and offspring populations are merged, then reduced to the next generation
160- through non‑dominated sorting and crowding distance ranking. Feasibility is tracked per individual
161- and incorporated into dominance comparisons — feasible solutions always dominate infeasible ones,
162- and among infeasible solutions the one with lower total constraint violation is preferred.
162+ Each generation:
163+
164+ 1 . binary tournament selection
165+ 2 . SBX crossover
166+ 3 . polynomial mutation
167+ 4 . parallel objective evaluation
168+ 5 . merge parents + offspring
169+ 6 . fast non‑dominated sort
170+ 7 . crowding‑distance truncation
171+
172+ Feasible solutions dominate infeasible ones.
173+ Among infeasible solutions, lower total violation is preferred.
163174
164175---
165176
166177## RunResult
167178
168- ` evolve() ` returns a ` RunResult ` containing :
179+ ` evolve() ` returns:
169180
170181| Field | Description |
171182| ---| ---|
172183| ` pareto_front ` | Final Pareto‑optimal solutions |
173184| ` history ` | Per‑generation Pareto front snapshots |
174- | ` hypervolume_history ` | Hypervolume indicator per generation (` NaN ` if no reference point set) |
175- | ` generations_completed ` | Number of generations actually run (may be less than ` num_generations ` if early stopping fired) |
185+ | ` hypervolume_history ` | Strict HV per generation (` NaN ` if no reference point) |
186+ | ` igd_history ` | IGD per generation (` NaN ` if no true front) |
187+ | ` generations_completed ` | Actual number of generations run |
176188
177189---
178190
179191## Benchmarks
180192
181- To measure sorting and evolution performance across population sizes:
193+ The crate includes:
194+
195+ - ** evolution** (full NSGA‑II loop)
196+ - ** sorting‑only** (fast non‑dominated sort)
197+ - ** strict vs auto hypervolume**
198+ - ** IGD‑only microbench**
199+
200+ Run all:
201+
182202```
183203cargo bench
184204```
185205
186- To isolate the impact of parallelisation in ` fast_nondominated_sort ` :
206+ Run a specific benchmark:
207+
187208```
188- RAYON_NUM_THREADS=1 cargo bench --bench sorting
209+ cargo bench --bench igd
189210cargo bench --bench sorting
211+ cargo bench --bench evolution
190212```
191213
192214HTML reports are written to ` target/criterion/ ` .
@@ -195,13 +217,11 @@ HTML reports are written to `target/criterion/`.
195217
196218## Original authors (Python version)
197219
198- - Pham Ngo Gia Bao
199- - Tram Loi Quan
200- - Quan Thanh Tho
201- - Akhil Garg
202-
203- ---
220+ - Pham Ngo Gia Bao
221+ - Tram Loi Quan
222+ - Quan Thanh Tho
223+ - Akhil Garg
204224
205225## Rust port
206226
207- - Giorgio
227+ - Giorgio
0 commit comments