Skip to main content

plasticity_lab/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Reward-modulated SNN learning/training orchestration layer for the
4//! Limen-Neural stack.
5//!
6//! This crate sits above [`neuromod::SpikingNetwork`], which owns neuron/network
7//! dynamics, neuromodulator state, and the foundational classical and
8//! reward-modulated STDP primitives. `plasticity-lab` does not reimplement those
9//! primitives — it drives them through `neuromod`'s public API: single-step
10//! reward modulation via [`PlasticityTrainer::train_step`] and batch sessions via
11//! [`PlasticityTrainer::run_session`] (optional per-step telemetry via
12//! [`PlasticityTrainer::run_session_with_observer`]). Seeded replay uses
13//! [`PlasticityTrainer::train_step_with_rng`] / [`PlasticityTrainer::run_session_with_rng`]
14//! to inject a caller RNG into neuromod's stochastic input encoding.
15//!
16//! # Features
17//!
18//! - **default** — core loop only (`neuromod` + serde/thiserror/rand).
19//! - **`critic`** — optional dep on `limbic-critic`, plus the `bridge`
20//!   adapter that converts critic `limbic_critic::ModulatorVector` into
21//!   [`neuromod::NeuroModulators`].
22//!
23//! `bridge` and `limbic_critic::ModulatorVector` above are plain code spans,
24//! not doc links: both only exist with the `critic` feature enabled.
25//!
26//! # Quick example
27//!
28//! ```rust
29//! use neuromod::SpikingNetwork;
30//! use plasticity_lab::{PlasticityTrainer, TrainingConfig, TrainingExample};
31//! use rand::{rngs::StdRng, SeedableRng};
32//!
33//! let mut trainer = PlasticityTrainer::new(TrainingConfig::default());
34//! let mut network = SpikingNetwork::with_dimensions(4, 2, 8);
35//! for neuron in &mut network.neurons {
36//!     // `with_dimensions` intentionally creates blank weights. Seed the
37//!     // documented L1 budget equally across input channels before training.
38//!     neuron.weights.fill(2.0 / network.num_channels as f32);
39//! }
40//! let batch = vec![TrainingExample {
41//!     stimuli: vec![1.0, 0.8, 0.6, 0.4, 0.2, 0.1, 0.05, 0.02],
42//!     reward: 1.0,
43//! }; 8];
44//! let mut rng = StdRng::seed_from_u64(0x5EED);
45//! let summary = trainer
46//!     .run_session_with_rng(&mut network, &batch, &mut rng)
47//!     .unwrap();
48//! assert!(summary.total_spikes > 0);
49//! assert!(summary.weight_drifts.iter().flatten().any(|delta| delta.abs() > 1e-5));
50//! ```
51//!
52//! # Limbic bridge (`critic`)
53//!
54//! ```rust,ignore
55//! use limbic_critic::SimpleCritic;
56//! use plasticity_lab::bridge::{apply_modulator_vector, to_neuromodulators};
57//!
58//! let vector = SimpleCritic::assess(&env);
59//! let _ = apply_modulator_vector(&mut network, &stimuli, &vector);
60//! // or: network.step(&stimuli, &to_neuromodulators(&vector));
61//! ```
62//!
63//! See the crate README for the ecosystem map, [scope/ownership
64//! boundaries](https://github.com/Limen-Neural/plasticity-lab#scope-and-ownership-boundaries)
65//! (including the boundary with `neuromod`'s network dynamics and plasticity
66//! primitives), and common usage patterns.
67
68pub mod config;
69pub mod observer;
70pub mod trainer;
71
72#[cfg(test)]
73mod replay;
74
75#[cfg(feature = "critic")]
76pub mod bridge;
77
78pub use config::TrainingConfig;
79pub use observer::{TrainingObserver, TrainingStepEvent};
80pub use trainer::{
81    PlasticityTrainer, SampleInvariant, TrainerError, TrainingExample, TrainingSummary,
82};
83
84#[cfg(feature = "critic")]
85pub use bridge::{apply_modulator_vector, from_neuromodulators, to_neuromodulators};
86
87/// Deprecated alias for [`PlasticityTrainer`].
88///
89/// This crate is pre-1.0. The alias is a short-lived migration aid for existing
90/// consumers and is **not** part of the documented public API: new code must use
91/// [`PlasticityTrainer`] directly. It will be removed in a future release.
92#[deprecated(
93    note = "renamed to `PlasticityTrainer`; this alias will be removed in a future release"
94)]
95#[doc(hidden)]
96pub use trainer::PlasticityTrainer as SpikenautTrainer;
97
98#[cfg(test)]
99mod deprecated_alias_tests {
100    #![allow(deprecated)]
101
102    use super::{SpikenautTrainer, TrainingConfig};
103
104    #[test]
105    fn spikenaut_trainer_alias_still_constructs() {
106        let _trainer = SpikenautTrainer::new(TrainingConfig::default());
107    }
108}