Skip to main content

sc_neurocore_engine/bindings/
evolution.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Commercial license available
3// © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
4// © Code 2020–2026 Miroslav Šotek. All rights reserved.
5// ORCID: 0009-0009-3560-0851
6// Contact: www.anulum.li | protoscience@anulum.li
7// SC-NeuroCore — Evolutionary substrate PyO3 bindings
8
9//! Python bindings for population-level evolutionary operators.
10
11use pyo3::prelude::*;
12
13/// Register the evolutionary-substrate functions with the extension module.
14pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
15    module.add_function(wrap_pyfunction!(batch_mutate_weights, module)?)?;
16    module.add_function(wrap_pyfunction!(batch_evaluate_fitness, module)?)?;
17    module.add_function(wrap_pyfunction!(batch_crossover, module)?)?;
18    module.add_function(wrap_pyfunction!(population_diversity, module)?)?;
19    module.add_function(wrap_pyfunction!(novelty_scores, module)?)?;
20    module.add_function(wrap_pyfunction!(tournament_select, module)?)?;
21    Ok(())
22}
23
24/// Batch-mutate population weights.
25#[pyfunction(name = "py_evo_batch_mutate")]
26#[pyo3(signature = (genomes, mutation_rate=0.1, mutation_scale=0.1, seed=42))]
27fn batch_mutate_weights(
28    _py: Python<'_>,
29    mut genomes: Vec<Vec<f64>>,
30    mutation_rate: f64,
31    mutation_scale: f64,
32    seed: u64,
33) -> Vec<Vec<f64>> {
34    crate::evo::batch_mutate_weights(&mut genomes, mutation_rate, mutation_scale, seed);
35    genomes
36}
37
38/// Evaluate population fitness in one Rust call.
39#[pyfunction(name = "py_evo_batch_fitness")]
40fn batch_evaluate_fitness(
41    _py: Python<'_>,
42    genomes: Vec<Vec<f64>>,
43    inputs: Vec<f64>,
44    target: f64,
45) -> Vec<f64> {
46    crate::evo::batch_evaluate_fitness(&genomes, &inputs, target)
47}
48
49/// Apply uniform crossover to paired parent populations.
50#[pyfunction(name = "py_evo_batch_crossover")]
51#[pyo3(signature = (parents_a, parents_b, seed=42))]
52fn batch_crossover(
53    _py: Python<'_>,
54    parents_a: Vec<Vec<f64>>,
55    parents_b: Vec<Vec<f64>>,
56    seed: u64,
57) -> Vec<Vec<f64>> {
58    crate::evo::batch_crossover(&parents_a, &parents_b, seed)
59}
60
61/// Return mean pairwise L2 distance for a population.
62#[pyfunction(name = "py_evo_diversity")]
63fn population_diversity(_py: Python<'_>, genomes: Vec<Vec<f64>>) -> f64 {
64    crate::evo::population_diversity(&genomes)
65}
66
67/// Score population novelty against an archive.
68#[pyfunction(name = "py_evo_novelty")]
69#[pyo3(signature = (genomes, archive, k_nearest=5))]
70fn novelty_scores(
71    _py: Python<'_>,
72    genomes: Vec<Vec<f64>>,
73    archive: Vec<Vec<f64>>,
74    k_nearest: usize,
75) -> Vec<f64> {
76    crate::evo::novelty_scores(&genomes, &archive, k_nearest)
77}
78
79/// Select population indices by seeded tournaments.
80#[pyfunction(name = "py_evo_tournament")]
81#[pyo3(signature = (fitness, n_select, tournament_size=3, seed=42))]
82fn tournament_select(
83    _py: Python<'_>,
84    fitness: Vec<f64>,
85    n_select: usize,
86    tournament_size: usize,
87    seed: u64,
88) -> Vec<usize> {
89    crate::evo::tournament_select(&fitness, n_select, tournament_size, seed)
90}