A modular, GPU-accelerated framework for Physical Reservoir Computing simulation and evaluation
Project description
OpenPRC: Physical Reservoir Computing Framework
OpenPRC is a modular, GPU-accelerated Python framework for simulating and evaluating physical reservoir computers — mechanical systems that process information through their intrinsic dynamics.
If you use OpenPRC in your research, please cite:
@article{phalak2026openprc,
title={OpenPRC: A Unified Open-Source Framework for Physics-to-Task Evaluation in Physical Reservoir Computing},
author={Phalak, Yogesh and Lor, Wen Sin and Khairnar, Apoorva and Jantzen, Benjamin and Naughton, Noel and Li, Suyi},
journal={arXiv preprint arXiv:2604.07423},
year={2026}
}
Simulation Capabilities
OpenPRC supports diverse mechanical substrates ranging from compliant mass-spring networks to rigid-foldable origami. Below are examples of validated simulation outputs:
|
Soft Reservoir Network Mass-spring lattice under dynamic actuation |
Miura-Ori Tessellation Rigid-foldable origami pattern |
|
Kirigami Structure Compliant network with geometric cuts |
K-Cone Origami Non-periodic origami configuration |
|
Bistable Slab Multistable mechanical metamaterial |
Tapered Spring Nonlinear elastic element dynamics |
Installation
pip install openprc
# With GPU support
pip install openprc[cuda]
# With all optional dependencies
pip install openprc[full]
Dependencies
| Package | Purpose | Extra |
|---|---|---|
numpy, h5py, scipy |
Core numerics and I/O | (always) |
numba |
JIT-compiled CPU physics | (always) |
scikit-learn |
Ridge readout | (always) |
pycuda |
CUDA backend | [cuda] |
jax / jaxlib |
Differentiable JAX backend | [jax] |
piviz-3d, imgui |
3-D animator | [viz] |
opencv-python |
Vision utilities | [vision] |
trimesh, rosbags, yourdfpy |
Robot bundle tooling | [automod] |
Pipeline
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ demlat │────▶│ reservoir │────▶│ analysis │────▶│ optimize │
│ (Physics) │ │ (Readout) │ │(Diagnostics)│ │(Calibration)│
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
Quick Start
import numpy as np
from openprc.demlat import SimulationSetup, Simulation, Engine, ShowSimulation
EXP = "experiments/my_prc"
# ── 1. Build geometry ──────────────────────────────────────────────────────
setup = SimulationSetup(EXP, overwrite=True)
setup.set_simulation_params(duration=10.0, dt=0.001, save_interval=0.01)
setup.set_physics(gravity=-9.81, damping=0.1)
anchor = setup.add_node([0.0, 0.0, 0.0], fixed=True)
mass = setup.add_node([1.0, 0.0, 0.0], mass=1.0)
setup.add_bar(anchor, mass, stiffness=1e4, rest_length=1.0, damping=5.0)
# ── 2. Drive with a force signal ──────────────────────────────────────────
t = np.arange(0, 10.0, 0.001)
sig = np.stack([0.5 * np.sin(2 * np.pi * t),
np.zeros_like(t),
np.zeros_like(t)], axis=1).astype("float32")
setup.add_signal("drive", sig, dt=0.001)
setup.add_actuator(anchor, "drive", type="force")
setup.save()
# ── 3. Run (CUDA, or "cpu" / "jax") ──────────────────────────────────────
eng = Engine(backend="cuda") # BarHingeModel is the default
eng.run(Simulation(EXP))
# ── 4. Animate ────────────────────────────────────────────────────────────
ShowSimulation(EXP)
API Reference
demlat — Physics Simulation
from openprc.demlat import SimulationSetup, Simulation, Engine, ShowSimulation
# ── Geometry ──────────────────────────────────────────────────────────────
setup = SimulationSetup("./experiments/my_exp", overwrite=True)
setup.set_simulation_params(duration=5.0, dt=0.001, save_interval=0.01)
setup.set_physics(
gravity=-9.81, damping=0.1,
enable_collision=True, # node-level sphere collision
collision_radius=0.02,
collision_restitution=0.6,
collision_iterations=3,
)
n0 = setup.add_node([0.0, 0.0, 0.0], fixed=True, collidable=True)
n1 = setup.add_node([0.5, 0.0, 0.0], mass=1.0, collidable=True)
n2 = setup.add_node([0.5, 0.5, 0.0], mass=1.0)
setup.add_bar(n0, n1, stiffness=1e4, rest_length=0.5, damping=5.0)
setup.add_hinge([n0, n1, n2, n2], stiffness=50.0, rest_angle=np.pi / 2)
# ── Signals & Actuators ───────────────────────────────────────────────────
t = np.arange(0, 5.0, 0.001)
pos = np.stack([0.1 * np.sin(2 * np.pi * t),
np.zeros_like(t),
np.zeros_like(t)], axis=1).astype("float32")
setup.add_signal("wave", pos, dt=0.001)
setup.add_actuator(n0, "wave", type="position") # full 3-DOF
setup.add_actuator(n1, "wave", type="force", dof=[0, 0, 1]) # z-axis only
setup.save()
# ── Run ───────────────────────────────────────────────────────────────────
eng = Engine(backend="cuda") # or "cpu" / "jax"; BarHingeModel is default
eng.run(Simulation("./experiments/my_exp"))
ShowSimulation("./experiments/my_exp")
reservoir — Readout Training
from openprc.reservoir import StateLoader, Ridge, Trainer, features
loader = StateLoader("./experiments/my_exp/output/simulation.h5")
# Node position features for selected nodes
feat = features.NodePositions(node_ids=[0, 1, 2], dims="all")
X = feat.transform(loader) # (T, n_features)
# Or use bar strains as the observable (stored as ε = ΔL/L₀)
feat = features.BarStrains()
# Train ridge readout
readout = Ridge(regularization=1e-4)
trainer = Trainer(
features=feat,
readout=readout,
experiment_dir="./experiments/my_exp",
loader=loader,
washout=2.0, # seconds to discard at start
train_duration=6.0,
test_duration=2.0,
)
y_target = ... # (T,) or (T, n_outputs) numpy array
result = trainer.train(y_target, task_name="NARMA10")
result.save()
analysis — Correlation Diagnostics & Multistability
from openprc.analysis import correlation as corr
from openprc.analysis import EquilibriumFinder
# ── Correlation diagnostics (x: features, y: targets) ────────────────────
lin = corr.Linear(x, y, lag_sweep=True)
print(lin.pearson) # zero-lag Pearson r per channel
lin.ccf.plot() # cross-correlation lag profiles
nr = corr.Nonparametric(x, y)
print(nr.dcor) # distance correlation (detects nonlinear deps)
# ── Find all mechanical equilibria ───────────────────────────────────────
finder = EquilibriumFinder.from_experiment("./experiments/my_exp")
results = finder.find_all(num_random=50)
results.summary()
finder.save_results(results, "./experiments/my_exp/equilibria.h5")
optimize — JAX-Based Parameter Calibration
from openprc.optimize import Calibration
from openprc.demlat import BarHingeModel
cal = Calibration(BarHingeModel, backend="jax")
cal.load_geometry("./experiments/my_exp")
cal.load_reference("./experiments/my_exp/output/simulation.h5")
cal.optimize_params(bar_stiffness=True, hinge_stiffness=True)
cal.set_bounds(bar_stiffness=(10.0, 1e5))
result = cal.run(max_iterations=500, lr=0.01, cost="mse")
cal.save("./experiments/my_exp/optimized_geometry.h5")
automod — Robot PRC Pipeline
import openprc.automod as automod
# Stage 1: convert URDF links to spring-mass reservoirs
automod.batch_preprocess(
bundle_dir="./robot_bundle",
robot_name="go1",
params=automod.PRESETS["small"], # "small", "medium", "large"
)
# Stage 2: run DEMLAT simulations for all trajectories
automod.batch_simulate(
bundle_dir="./robot_bundle",
robot_name="go1",
splits=("train", "test"),
gravity=-9.81, damping_scale=2.0,
)
# Stage 3: train ridge readout with k-fold CV
run_dir = automod.run_training(
bundle_dir="./robot_bundle",
robot_name="go1",
features="node_vel", # see automod.FEATURE_LEVELS
targets=["body_vel", "qvel"],
n_folds=5,
)
# Stage 4: generate plots
automod.plot_run(run_dir)
License
Apache 2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openprc-0.1.0.tar.gz.
File metadata
- Download URL: openprc-0.1.0.tar.gz
- Upload date:
- Size: 328.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e32d364b7dc386c217f0bb4a27d0177e8019626e2ca03a41dbdc6971454ccc7
|
|
| MD5 |
2b3c1dab019063a9f030a76f39b915af
|
|
| BLAKE2b-256 |
620d13e72238f577e256bf2934768b46a07cf55a54084c3e893a82121f6a86b4
|
Provenance
The following attestation bundles were made for openprc-0.1.0.tar.gz:
Publisher:
publish.yml on DARE-Lab-VT/OpenPRC-dev
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openprc-0.1.0.tar.gz -
Subject digest:
1e32d364b7dc386c217f0bb4a27d0177e8019626e2ca03a41dbdc6971454ccc7 - Sigstore transparency entry: 1538097577
- Sigstore integration time:
-
Permalink:
DARE-Lab-VT/OpenPRC-dev@edaf6af30d3037d2ab1467ae28baeb39849674c5 -
Branch / Tag:
refs/tags/v0.1.0-alpha - Owner: https://github.com/DARE-Lab-VT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@edaf6af30d3037d2ab1467ae28baeb39849674c5 -
Trigger Event:
release
-
Statement type:
File details
Details for the file openprc-0.1.0-py3-none-any.whl.
File metadata
- Download URL: openprc-0.1.0-py3-none-any.whl
- Upload date:
- Size: 386.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ffffbd6b2fb77698214c36d9f1ed85242ca3427b86cbca00a179e70bce78a34
|
|
| MD5 |
b6eefd65b938ca206a124a292e28e26c
|
|
| BLAKE2b-256 |
8801bf5af137847f1fccd6bba8561e7170238484d9ccf2ca64a4222ce42d2325
|
Provenance
The following attestation bundles were made for openprc-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on DARE-Lab-VT/OpenPRC-dev
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openprc-0.1.0-py3-none-any.whl -
Subject digest:
8ffffbd6b2fb77698214c36d9f1ed85242ca3427b86cbca00a179e70bce78a34 - Sigstore transparency entry: 1538097703
- Sigstore integration time:
-
Permalink:
DARE-Lab-VT/OpenPRC-dev@edaf6af30d3037d2ab1467ae28baeb39849674c5 -
Branch / Tag:
refs/tags/v0.1.0-alpha - Owner: https://github.com/DARE-Lab-VT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@edaf6af30d3037d2ab1467ae28baeb39849674c5 -
Trigger Event:
release
-
Statement type: