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/, and ncps/keras/ each contain a __init__.py that re‑exports CfC, LTC, and the corresponding cell classes (cfc_cell.py, ltc_cell.py).
  • Wiring utilities live in ncps/wirings/wirings.py and expose AutoNCP, 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__.py imports CfC from ncps/torch/cfc.py.
  • ncps/torch/cfc.py defines class CfC whose forward method 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 to ncps/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 functionCallersCallees
CfC / LTC constructors32 placesAutoNCP, _allocate_parameters
prepare_test_data30 placesdataset loaders, get_config
AutoNCP22 placeswirings.py helpers
get_config13 placesprune_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.txt is the only manifest; add .github/dependabot.yml.
  • MEDIUM – No dependency scan in CI – .github/workflows/python-test.yml lacks 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: false missing in CI checkout step.
  • LOW – No workflow timeout – add timeout-minutes to jobs in python-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 and cfc_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.