Skip to main content

Crate ncps

Crate ncps 

Source
Expand description

§NCPS - Neural Circuit Policies for Rust

Crates.io Documentation

A Rust implementation of Neural Circuit Policies (NCPs) using the Burn deep learning framework. NCPs are biologically-inspired recurrent neural networks with sparse, structured connectivity patterns.

§Why NCPs?

FeatureTraditional RNNNCP
ConnectivityDense (O(N²))Sparse (O(N))
ParametersManyFew
InterpretabilityBlack boxStructured layers
Time handlingDiscreteContinuous

NCPs are particularly well-suited for:

  • Time series with irregular sampling
  • Robotics and control systems
  • Edge deployment where parameters matter
  • Interpretable AI applications

§Quick Start

Add to your Cargo.toml:

[dependencies]
ncps = "0.1"
burn = { version = "0.16", features = ["ndarray"] }

§Basic Usage

use ncps::prelude::*;

// 1. Create wiring (defines network structure)
let mut wiring = AutoNCP::new(
    32,    // total neurons
    8,     // output size
    0.5,   // sparsity (50% connections removed)
    42,    // random seed
);

// 2. Build with your input dimension
wiring.build(16);  // 16 input features

// 3. Verify configuration
assert_eq!(wiring.units(), 32);
assert_eq!(wiring.output_dim(), Some(8));
assert!(wiring.is_built());

§Full RNN Example

use ncps::prelude::*;
use burn::tensor::Tensor;
use burn::backend::NdArray;

type Backend = NdArray<f32>;

let device = Default::default();

// Create wiring
let mut wiring = AutoNCP::new(64, 10, 0.5, 42);
wiring.build(20);  // 20 input features

// Create CfC RNN layer
let cfc = CfC::<Backend>::with_wiring(20, wiring, &device);

// Process sequence: [batch=4, seq_len=100, features=20]
let input: Tensor<Backend, 3> = Tensor::zeros([4, 100, 20], &device);
let (output, final_state) = cfc.forward(input, None, None);

// output: [4, 100, 10] - output at each timestep
// final_state: [4, 64] - hidden state for continuation

§Architecture Overview

                         ┌─────────────────────────┐
                         │    Your Application     │
                         └───────────┬─────────────┘
                                     │
                         ┌───────────▼─────────────┐
                         │   RNN Layers (cfc, ltc) │  ◄── Use these!
                         │   Sequence processing   │
                         └───────────┬─────────────┘
                                     │
                         ┌───────────▼─────────────┐
                         │   Cells (cfc_cell, etc) │  ◄── Single timestep
                         │   Low-level operations  │
                         └───────────┬─────────────┘
                                     │
                         ┌───────────▼─────────────┐
                         │   Wirings (ncp, etc)    │  ◄── Network structure
                         │   Connectivity patterns │
                         └─────────────────────────┘

§Module Guide

ModulePurposeStart Here?
rnnFull RNN layers for sequences✅ Yes
wiringsNetwork connectivity patterns✅ Yes
cellsSingle-timestep processingFor advanced use
activationActivation functionsRarely needed

§Choosing Components

§RNN Layer: CfC vs LTC

Choose CfC when…Choose LTC when…
Speed mattersBiological accuracy matters
Training large modelsResearch applications
Production deploymentVariable time constants needed

§Wiring: AutoNCP vs NCP vs FullyConnected

Choose AutoNCP when…Choose NCP when…Choose FullyConnected when…
Starting out (recommended)Need exact layer sizesNeed baseline comparison
Want automatic configurationFine-tuning connectivityMaximum expressiveness
Most use casesResearch/ablation studiesDon’t care about sparsity

§Common Patterns

§Sequence Classification

// Only return the final output
let cfc = CfC::<Backend>::with_wiring(input_size, wiring, &device)
    .with_return_sequences(false);

let (output, _) = cfc.forward(input, None, None);
// output: [batch, 1, output_size]

§Stateful Processing (streaming)

// Preserve state across batches
let (out1, state) = cfc.forward(batch1, None, None);
let (out2, state) = cfc.forward(batch2, Some(state), None);
let (out3, state) = cfc.forward(batch3, Some(state), None);

§Different Input Formats

// Sequence-first format: [seq_len, batch, features]
let cfc = CfC::<Backend>::new(input_size, hidden_size, &device)
    .with_batch_first(false);

§Important: The .build() Step

All wirings must be built before use:

use ncps::wirings::{AutoNCP, Wiring};

let mut wiring = AutoNCP::new(32, 8, 0.5, 42);

// ❌ Not ready yet - sensory connections don't exist
assert!(!wiring.is_built());

// ✅ Build with input dimension
wiring.build(16);
assert!(wiring.is_built());

// Now you can use it with RNN layers

§References

§Feature Flags

This crate uses Burn’s backend system. Configure via Burn’s features:

# CPU (default)
burn = { version = "0.16", features = ["ndarray"] }

# GPU (WGPU)
burn = { version = "0.16", features = ["wgpu"] }

# GPU (Candle)
burn = { version = "0.16", features = ["candle"] }

Modules§

activation
Custom activation functions for NCPS
cells
RNN Cell Implementations
prelude
rnn
RNN Layers for Sequence Processing
wirings
Wiring Configurations for Neural Circuit Policies