Metadata-Version: 2.4
Name: resdag
Version: 0.7.0
Summary: PyTorch-native reservoir computing library with GPU acceleration
Project-URL: Homepage, https://github.com/El3ssar/ResDAG
Project-URL: Documentation, https://el3ssar.github.io/ResDAG/
Project-URL: Repository, https://github.com/El3ssar/ResDAG
Project-URL: Issues, https://github.com/El3ssar/ResDAG/issues
Author-email: Daniel Estevez-Moya <kemossabee@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: echo state networks,machine learning,pytorch,reservoir computing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: <3.15,>=3.11
Requires-Dist: networkx>=3.0
Requires-Dist: numpy>=2.0.0
Requires-Dist: pytorch-symbolic>=1.2.1
Requires-Dist: scipy>=1.17.0
Requires-Dist: torch>=2.10.0
Provides-Extra: dev
Requires-Dist: basedpyright>=1.37.1; extra == 'dev'
Requires-Dist: black>=25.12.0; extra == 'dev'
Requires-Dist: graphviz>=0.21; extra == 'dev'
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: optuna>=4.0.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=9.0.2; extra == 'dev'
Requires-Dist: ruff>=0.14.10; extra == 'dev'
Provides-Extra: docs
Requires-Dist: graphviz>=0.21; extra == 'docs'
Requires-Dist: griffe>=1.0; extra == 'docs'
Requires-Dist: matplotlib>=3.9; extra == 'docs'
Requires-Dist: mkdocs-glightbox>=0.4; extra == 'docs'
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocs>=1.6; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.27; extra == 'docs'
Requires-Dist: pymdown-extensions>=10.7; extra == 'docs'
Provides-Extra: dynamics
Requires-Dist: tsdynamics>=2.5.0; (python_version >= '3.12') and extra == 'dynamics'
Provides-Extra: hpo
Requires-Dist: optuna>=3.0.0; extra == 'hpo'
Provides-Extra: ode
Requires-Dist: torchdiffeq>=0.2.0; extra == 'ode'
Provides-Extra: viz
Requires-Dist: graphviz>=0.21; extra == 'viz'
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://el3ssar.github.io/ResDAG/assets/logo.svg" width="88" alt="ResDAG">
</p>

<h1 align="center">ResDAG</h1>

<p align="center"><strong>Reservoir computing for PyTorch.</strong><br>
Compose reservoir models as DAGs, train readouts with a single algebraic solve, run it all on the GPU.</p>

<p align="center">
  <a href="https://pypi.org/project/resdag/"><img src="https://img.shields.io/pypi/v/resdag" alt="PyPI"></a>
  <a href="https://pypi.org/project/resdag/"><img src="https://img.shields.io/pypi/pyversions/resdag" alt="Python"></a>
  <a href="https://github.com/El3ssar/ResDAG/actions/workflows/ci.yml"><img src="https://github.com/El3ssar/ResDAG/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://codecov.io/gh/El3ssar/ResDAG"><img src="https://codecov.io/gh/El3ssar/ResDAG/branch/main/graph/badge.svg" alt="Coverage"></a>
  <a href="https://el3ssar.github.io/ResDAG/"><img src="https://img.shields.io/badge/docs-el3ssar.github.io%2FResDAG-2a63d4" alt="Documentation"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT"></a>
</p>

---

ResDAG treats reservoir models — echo state networks and beyond — as ordinary PyTorch layers. Reservoirs, readouts, and transforms are `nn.Module`s wired together with a functional API; training a readout is one teacher-forced pass and one algebraic ridge solve, with no epochs. Models move with `.to(device)`, serialize with `state_dict()`, and embed in larger PyTorch pipelines, optimizers included.

**[Documentation →](https://el3ssar.github.io/ResDAG/)**

## Installation

```bash
pip install resdag            # core
pip install "resdag[hpo]"     # + Optuna hyperparameter optimization
```

Python ≥ 3.11, PyTorch ≥ 2.10.

> **Docs track the dev branch.** Releases are frozen until 1.0, so PyPI serves
> **0.6.2** — which predates parts of the [documented](https://el3ssar.github.io/ResDAG/)
> API (the `ESN` facade, `resdag.data` streaming, the newer readout solvers). For
> the documented API today: `pip install "git+https://github.com/El3ssar/ResDAG"`.

## Try it

A reservoir forecaster on a toy signal, end to end:

```python
import torch
import resdag as rd

t = torch.linspace(0, 60, 3000)
data = torch.sin(t).reshape(1, -1, 1)            # (batch, time, features)

warmup, train, target, f_warmup, val = rd.utils.prepare_esn_data(
    data, warmup_steps=100, train_steps=2000, val_steps=300)

model = rd.models.classic_esn(reservoir_size=300, feedback_size=1, output_size=1)
rd.ESNTrainer(model).fit((warmup,), (train,), targets={"output": target})

prediction = model.forecast(f_warmup, horizon=300)   # autoregressive, (1, 300, 1)
```

The [first forecast](https://el3ssar.github.io/ResDAG/start/first-forecast/) walkthrough does the same on the Lorenz attractor, with the math explained.

## Compose, don't configure

Architectures are DAGs you wire, not options you toggle. Two reservoirs on different timescales, read out together:

```python
from resdag import CGReadoutLayer, Concatenate, ESNModel, reservoir_input
from resdag.layers import ESNLayer

inp    = reservoir_input(3)
fast   = ESNLayer(64, feedback_size=3, leak_rate=1.0, spectral_radius=0.9)(inp)
slow   = ESNLayer(64, feedback_size=3, leak_rate=0.2, spectral_radius=0.9)(inp)
merged = Concatenate()(fast, slow)
model  = ESNModel(inp, CGReadoutLayer(128, 3, name="output")(merged))
```

<p align="center">
  <img src="https://el3ssar.github.io/ResDAG/assets/figures/readme/parallel_timescales.svg" width="640" alt="Two parallel reservoirs feeding one readout">
</p>

Branches, feature augmentations, and multiple readout heads compose the same way — one reservoir, squared-state augmentation, two heads:

<p align="center">
  <img src="https://el3ssar.github.io/ResDAG/assets/figures/readme/augmented_two_heads.svg" width="640" alt="Augmented states feeding two readout heads">
</p>

All heads fit in a single pass, in dependency order. The [composition handbook](https://el3ssar.github.io/ResDAG/build/) covers the patterns.

## Coming from scikit-learn?

The whole `fit` → `predict` loop fits in one object. `ESN.fit(series)` slices the warmup window, builds the one-step-ahead target, and runs the algebraic solve; `forecast(horizon=...)` re-synchronizes and rolls out — numpy in, numpy out:

```python
import numpy as np
from resdag import ESN

series = np.cumsum(np.random.randn(2000, 3), axis=0)   # (time, features)

esn = ESN(reservoir_size=300, spectral_radius=0.9).fit(series)
prediction = esn.forecast(horizon=200)                 # (200, 3)
```

`esn.model` drops you back into the full composable graph whenever you outgrow the facade. The [mental model](https://el3ssar.github.io/ResDAG/start/concepts/) maps `fit`/`predict` onto ResDAG's `warmup` + `ESNTrainer.fit` / `forecast` flow.

## Train through it with SGD

A reservoir is an ordinary PyTorch layer, so it drops into a pipeline as a frozen feature extractor and you train any head with a normal optimizer loop. The reservoir has zero trainable parameters, so the optimizer only ever touches the head:

```python
import torch
import torch.nn as nn
from resdag import ReservoirFeatureExtractor

net = nn.Sequential(
    ReservoirFeatureExtractor(500, feedback_size=3, spectral_radius=0.9),
    nn.Linear(500, 64), nn.Tanh(), nn.Linear(64, n_classes),
)
extractor, head = net[0], net[1:]
opt = torch.optim.Adam(head.parameters(), lr=1e-3)     # head only — reservoir is frozen

with torch.no_grad():                                  # frozen features: compute once
    extractor.on_epoch_start()
    feats = extractor(sequences)[:, -1]                # (batch, 500) last-step summary

for _ in range(300):
    loss = nn.functional.cross_entropy(head(feats), labels)
    opt.zero_grad(); loss.backward(); opt.step()
```

This is the pure-PyTorch path: gradient heads, full BPTT through the recurrence (`trainable=True`), and embedding frozen reservoirs in larger networks. [Work · Train](https://el3ssar.github.io/ResDAG/workflows/train/) covers all three training paths; [Work · Scale & deploy](https://el3ssar.github.io/ResDAG/workflows/deploy/) shows the frozen-backbone classifier inside a `nn.Module` pipeline.

## Documentation

| | |
| --- | --- |
| [Start](https://el3ssar.github.io/ResDAG/start/) | Install, a first trained forecaster, the mental model |
| [Build](https://el3ssar.github.io/ResDAG/build/) | Layers, readouts, architectures, topologies, initializers |
| [Work](https://el3ssar.github.io/ResDAG/workflows/) | Training paths, forecasting with drivers, tuning, GPU |
| [Theory](https://el3ssar.github.io/ResDAG/theory/) | Every equation, stated against the code |
| [Reference](https://el3ssar.github.io/ResDAG/reference/) | The full public API |

## Ecosystem

Built on [pytorch_symbolic](https://github.com/gahaalt/pytorch-symbolic) for graph composition. Pairs with [TSDynamics](https://github.com/El3ssar/TSDynamics), a companion library of dynamical systems — it generates the systems, ResDAG forecasts them.

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) — releases are automated from conventional commits, and most component types are one registry decorator away.

## License

MIT — © Daniel Estevez-Moya
