Skip to main content

sc_neurocore_engine/bindings/
rulkov_map.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 — Rulkov map PyO3 binding
8
9//! Python binding for the Rulkov spiking map.
10
11use numpy::{IntoPyArray, PyArray1};
12use pyo3::exceptions::PyFloatingPointError;
13use pyo3::prelude::*;
14use pyo3::types::PyDict;
15
16use crate::neurons::RulkovMapNeuron;
17
18py_neuron_default!("RulkovMapNeuron", PyRulkovMapNeuron, RulkovMapNeuron, state x, state y);
19
20/// Register the Rulkov map class and simulator with the extension module.
21pub(super) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
22    module.add_class::<PyRulkovMapNeuron>()?;
23    module.add_function(wrap_pyfunction!(py_rulkov_map_simulate, module)?)?;
24    Ok(())
25}
26
27/// Parity contract with `sc_neurocore.neurons.models.rulkov_map.RulkovMapNeuron.simulate`:
28/// for the same parameters and constant input the returned `x` trace, upward-
29/// reset-branch event count, and final `(x, y)` state are bit-identical to the
30/// Python reference (the map is exact floating-point arithmetic — one division,
31/// additions and multiplications, no transcendental functions).
32#[pyfunction]
33#[pyo3(signature = (x0, y0, alpha, sigma, mu, n_steps, current))]
34#[allow(clippy::too_many_arguments)]
35fn py_rulkov_map_simulate<'py>(
36    py: Python<'py>,
37    x0: f64,
38    y0: f64,
39    alpha: f64,
40    sigma: f64,
41    mu: f64,
42    n_steps: usize,
43    current: f64,
44) -> PyResult<(Bound<'py, PyArray1<f64>>, i64, f64, f64)> {
45    let mut neuron = RulkovMapNeuron {
46        x: x0,
47        y: y0,
48        alpha,
49        sigma,
50        mu,
51    };
52    let Some((trace, events)) = neuron.try_simulate(n_steps, current) else {
53        return Err(PyFloatingPointError::new_err(
54            "Rulkov Rust batch rejected an invalid candidate",
55        ));
56    };
57    Ok((trace.into_pyarray(py), events, neuron.x, neuron.y))
58}