The Problem
Researchers and engineers need a lightweight way to experiment with sparse, biologically‑inspired recurrent networks (NCP, LTC, CfC) in both PyTorch and TensorFlow. Existing libraries either expose only one framework or hide the wiring diagram that defines the sparse connectivity, making reproducible experiments cumbersome.
What This Does
The ncps repository provides framework‑agnostic implementations of the three core models and a set of wiring helpers.
- The source tree is split by framework:
ncps/torch/,ncps/tf/, andncps/keras/each contain a__init__.pythat re‑exportsCfC,LTC, and the corresponding cell classes (cfc_cell.py,ltc_cell.py). - Wiring utilities live in
ncps/wirings/wirings.pyand exposeAutoNCP,FixedWiring, etc. - Example scripts in
examples/(e.g.,atari_ppo.py,atari_torch.py) show end‑to‑end training loops that instantiate a model with a wiring and run a closed‑loop RL or behavior‑cloning pipeline.
The package is installable via PyPI (pip install ncps) or from source with the standard setup.py entry point.
How It Is Wired
Execution starts when a user imports a model, e.g.:
from ncps.torch import CfC, AutoNCP
ncps/torch/__init__.pyimportsCfCfromncps/torch/cfc.py.ncps/torch/cfc.pydefines classCfCwhoseforwardmethod builds the recurrent cell, allocates parameters (_allocate_parameters→add_weight– 53 calls across the repo) and delegates the ODE step to_ode_solver.- When a wiring is supplied,
CfC.__init__forwards it toncps/wirings/wirings.py(AutoNCP), which constructs the sparse adjacency matrix and neuron type lists. This file is the most connected module (40 functions, 5 classes, called from 7 other files).
The internal call graph shows the following high‑impact paths:
| Hub function | Callers | Callees |
|---|---|---|
CfC / LTC constructors | 32 places | AutoNCP, _allocate_parameters |
prepare_test_data | 30 places | dataset loaders, get_config |
AutoNCP | 22 places | wirings.py helpers |
get_config | 13 places | prune_details, test utilities |
These hubs have the widest “blast radius”: a change to wirings.py or the model constructors ripples through most examples and tests.
Import cycles – ncps/torch/__init__.py, ncps/tf/__init__.py, and ncps/keras/__init__.py each participate in a circular import (10 modules total). This adds cognitive load and can cause import‑time failures if the order changes.
Deep nesting – files such as ncps/keras/cfc.py and ncps/keras/ltc.py contain indentation depths of up to 9 levels, making the control flow hard to follow.
File I/O – ncps/datasets/icra2020_lidar_collision_avoidance.py opens files without a context manager, risking resource leaks.
Test size – ncps/tests/test_keras.py holds 742 lines, mixing many responsibilities (model construction, data prep, database writes), which hampers maintainability.
How To Use It
# Clone the repo (required for development or custom wiring)
git clone https://github.com/moses-y/ncps
cd ncps
# Install runtime dependencies
pip install -r requirements.txt # or simply: pip install ncps
# Quick sanity check (runs the test suite)
pytest ncps/tests
No additional configuration files are required; the examples assume default hyper‑parameters. To run a concrete script:
python examples/atari_torch.py # trains a Torch CfC on Atari BC
For TensorFlow:
python examples/atari_tf.py
Both scripts instantiate the model as shown in the README and execute a training loop that writes checkpoints to the local filesystem.
Real‑World Use
A robotics team can replace a standard LSTM controller with an NCP‑wired CfC:
from ncps.torch import CfC, AutoNCP
import torch
wiring = AutoNCP(num_neurons=64, num_outputs=4)
rnn = CfC(input_size=12, wiring=wiring) # sparse recurrent core
The wiring enforces a biologically plausible sparsity pattern, reducing parameter count and improving interpretability while preserving performance on time‑series control tasks.
Code Health & Issues
- MEDIUM – No Dependabot –
requirements.txtis the only manifest; add.github/dependabot.yml. - MEDIUM – No dependency scan in CI –
.github/workflows/python-test.ymllacks a vulnerability‑review step. - MEDIUM – Large binary in repo –
misc/sine.webp(4.9 MiB) should be moved to Git LFS or external storage. - MEDIUM – Checkout keeps token –
persist-credentials: falsemissing in CI checkout step. - LOW – No workflow timeout – add
timeout-minutesto jobs inpython-test.yml.
Additional static findings:
- HIGH – Import cycles in
ncps/*/__init__.py(10 modules). Break cycles by extracting shared symbols or using lazy imports. - MEDIUM – Broad exception handling (
except:) in several init files andcfc_cell.py; replace with specific catches. - HIGH – Deep nesting (max depth 9) in several Keras modules; refactor into smaller helpers.
- HIGH – Duplicated code across the three Atari example scripts and dataset init files; factor common logic into a utility module.
- MEDIUM – File opened without context manager in the lidar dataset loader.
- MEDIUM – Oversized test file
test_keras.py; split into focused test modules.
No critical security issues or missing licenses were detected.
The Bottom Line
ncps delivers functional, dual‑framework implementations of NCP, LTC, and CfC models with ready‑to‑run examples, making it a practical research tool. However, the codebase suffers from import cycles, deep nesting, and duplicated boilerplate, which will increase maintenance effort for production use. It is best suited for teams comfortable navigating Python internals and willing to invest in modest refactoring.