OpenReservoirComputing 0.3.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/.gitignore +2 -4
- {openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info → openreservoircomputing-0.4.0}/PKG-INFO +32 -55
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/README.md +20 -9
- openreservoircomputing-0.4.0/pyproject.toml +132 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/__init__.py +1 -1
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/classifier/train.py +2 -3
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/control/base.py +1 -3
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/control/models.py +1 -1
- openreservoircomputing-0.4.0/src/orc/control/train.py +213 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/drivers.py +1 -3
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/forecaster/base.py +1 -1
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/forecaster/train.py +116 -38
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/classifier/test_classifier_models.py +4 -6
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/control/test_control_models.py +151 -3
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/forecaster/test_forecast_models.py +296 -8
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/test_drivers.py +1 -1
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/test_readouts.py +10 -4
- openreservoircomputing-0.3.2/.github/CODEOWNERS +0 -1
- openreservoircomputing-0.3.2/.github/workflows/docs.yml +0 -26
- openreservoircomputing-0.3.2/.github/workflows/draft-pdf.yml +0 -28
- openreservoircomputing-0.3.2/.github/workflows/publish.yml +0 -46
- openreservoircomputing-0.3.2/.github/workflows/tests.yml +0 -63
- openreservoircomputing-0.3.2/JOSS/CITATION.cff +0 -39
- openreservoircomputing-0.3.2/JOSS/CODE_OF_CONDUCT.md +0 -85
- openreservoircomputing-0.3.2/JOSS/joss-paper.pdf +0 -0
- openreservoircomputing-0.3.2/JOSS/paper.bib +0 -149
- openreservoircomputing-0.3.2/JOSS/paper.md +0 -115
- openreservoircomputing-0.3.2/PKG-INFO +0 -165
- openreservoircomputing-0.3.2/docs/api/classifier.md +0 -31
- openreservoircomputing-0.3.2/docs/api/control.md +0 -31
- openreservoircomputing-0.3.2/docs/api/data.md +0 -39
- openreservoircomputing-0.3.2/docs/api/drivers.md +0 -19
- openreservoircomputing-0.3.2/docs/api/embeddings.md +0 -19
- openreservoircomputing-0.3.2/docs/api/forecaster.md +0 -59
- openreservoircomputing-0.3.2/docs/api/readouts.md +0 -35
- openreservoircomputing-0.3.2/docs/api/utils.md +0 -36
- openreservoircomputing-0.3.2/docs/contributing.md +0 -68
- openreservoircomputing-0.3.2/docs/getting-started/installation.md +0 -83
- openreservoircomputing-0.3.2/docs/getting-started/quickstart.md +0 -162
- openreservoircomputing-0.3.2/docs/imgs/ORC_logo_cropped.png +0 -0
- openreservoircomputing-0.3.2/docs/index.md +0 -137
- openreservoircomputing-0.3.2/docs/javascripts/mathjax.js +0 -26
- openreservoircomputing-0.3.2/docs/stylesheets/extra.css +0 -78
- openreservoircomputing-0.3.2/docs/user-guide/data.md +0 -93
- openreservoircomputing-0.3.2/docs/user-guide/drivers.md +0 -186
- openreservoircomputing-0.3.2/docs/user-guide/embeddings.md +0 -151
- openreservoircomputing-0.3.2/docs/user-guide/models.md +0 -454
- openreservoircomputing-0.3.2/docs/user-guide/overview.md +0 -108
- openreservoircomputing-0.3.2/docs/user-guide/readouts.md +0 -185
- openreservoircomputing-0.3.2/imgs/ORC_logo_cropped.png +0 -0
- openreservoircomputing-0.3.2/imgs/architecture_paper.pdf +0 -0
- openreservoircomputing-0.3.2/imgs/readme_example_forecast.png +0 -0
- openreservoircomputing-0.3.2/mkdocs.yml +0 -129
- openreservoircomputing-0.3.2/pyproject.toml +0 -113
- openreservoircomputing-0.3.2/setup.cfg +0 -4
- openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info/SOURCES.txt +0 -86
- openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info/dependency_links.txt +0 -1
- openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info/requires.txt +0 -47
- openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info/top_level.txt +0 -1
- openreservoircomputing-0.3.2/src/orc/control/train.py +0 -164
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/LICENSE +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/benchmark_combined.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/classification.ipynb +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/continuous_rc.ipynb +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/control.ipynb +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/data_library.ipynb +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/jit_compatibility.ipynb +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/ks.ipynb +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/lorenz.ipynb +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/rc_background.ipynb +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/classifier/__init__.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/classifier/base.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/classifier/models.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/control/__init__.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/data/__init__.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/data/integrators.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/embeddings.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/forecaster/__init__.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/forecaster/models.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/readouts.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/utils/__init__.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/utils/numerics.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/utils/regressions.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/utils/visualization.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/data/test_integrators.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/test_embeddings.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/utils/test_numerics.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/utils/test_regressions.py +0 -0
- {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/utils/test_visualization.py +0 -0
|
@@ -97,10 +97,8 @@ ipython_config.py
|
|
|
97
97
|
#Pipfile.lock
|
|
98
98
|
|
|
99
99
|
# UV
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
# commonly ignored for libraries.
|
|
103
|
-
#uv.lock
|
|
100
|
+
# uv.lock IS COMMITTED to this repository on purpose: it gives contributors reproducible
|
|
101
|
+
# environments and CI runs `uv sync --locked` against it. Do not add it here.
|
|
104
102
|
|
|
105
103
|
# poetry
|
|
106
104
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
@@ -1,63 +1,29 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: OpenReservoirComputing
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: GPU accelerated implementations of common RC architectures
|
|
5
|
+
Project-URL: Repository, https://github.com/Jan-Williams/OpenReservoirComputing
|
|
5
6
|
Author-email: "Jan P. Williams" <jmpw1@uw.edu>, Dima Tretiak <dtretiak@uw.edu>
|
|
6
7
|
Maintainer-email: "Jan P. Williams" <jmpw1@uw.edu>
|
|
7
|
-
|
|
8
|
+
License-File: LICENSE
|
|
8
9
|
Keywords: reservoir computing
|
|
9
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
10
|
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
16
|
Requires-Python: <3.14,>=3.10
|
|
16
|
-
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
Requires-Dist: jax
|
|
17
|
+
Requires-Dist: diffrax
|
|
19
18
|
Requires-Dist: equinox
|
|
19
|
+
Requires-Dist: jax
|
|
20
20
|
Requires-Dist: matplotlib
|
|
21
|
-
Requires-Dist: setuptools_scm>=8.1
|
|
22
|
-
Requires-Dist: diffrax
|
|
23
|
-
Provides-Extra: all
|
|
24
|
-
Requires-Dist: jax[cuda12]; extra == "all"
|
|
25
|
-
Requires-Dist: ruff; extra == "all"
|
|
26
|
-
Requires-Dist: ty; extra == "all"
|
|
27
|
-
Requires-Dist: pytest; extra == "all"
|
|
28
|
-
Requires-Dist: coverage; extra == "all"
|
|
29
|
-
Requires-Dist: pytest-cov; extra == "all"
|
|
30
|
-
Requires-Dist: notebook; extra == "all"
|
|
31
|
-
Requires-Dist: ipykernel; extra == "all"
|
|
32
|
-
Requires-Dist: pytest-env; extra == "all"
|
|
33
|
-
Requires-Dist: mkdocs; extra == "all"
|
|
34
|
-
Requires-Dist: mkdocs-material; extra == "all"
|
|
35
|
-
Requires-Dist: mkdocs-autorefs; extra == "all"
|
|
36
|
-
Requires-Dist: mkdocstrings[python]; extra == "all"
|
|
37
|
-
Requires-Dist: mkdocs-jupyter; extra == "all"
|
|
38
|
-
Provides-Extra: dev
|
|
39
|
-
Requires-Dist: ruff; extra == "dev"
|
|
40
|
-
Requires-Dist: ty; extra == "dev"
|
|
41
|
-
Requires-Dist: pytest; extra == "dev"
|
|
42
|
-
Requires-Dist: coverage; extra == "dev"
|
|
43
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
44
|
-
Requires-Dist: pytest-env; extra == "dev"
|
|
45
|
-
Requires-Dist: mkdocs; extra == "dev"
|
|
46
|
-
Requires-Dist: mkdocs-material; extra == "dev"
|
|
47
|
-
Requires-Dist: mkdocs-autorefs; extra == "dev"
|
|
48
|
-
Requires-Dist: mkdocstrings[python]; extra == "dev"
|
|
49
|
-
Provides-Extra: notebooks
|
|
50
|
-
Requires-Dist: notebook; extra == "notebooks"
|
|
51
|
-
Requires-Dist: ipykernel; extra == "notebooks"
|
|
52
21
|
Provides-Extra: gpu
|
|
53
|
-
Requires-Dist: jax[cuda12]; extra ==
|
|
54
|
-
Provides-Extra:
|
|
55
|
-
Requires-Dist:
|
|
56
|
-
Requires-Dist:
|
|
57
|
-
|
|
58
|
-
Requires-Dist: mkdocstrings[python]; extra == "docs"
|
|
59
|
-
Requires-Dist: mkdocs-jupyter; extra == "docs"
|
|
60
|
-
Dynamic: license-file
|
|
22
|
+
Requires-Dist: jax[cuda12]; (sys_platform == 'linux') and extra == 'gpu'
|
|
23
|
+
Provides-Extra: notebooks
|
|
24
|
+
Requires-Dist: ipykernel; extra == 'notebooks'
|
|
25
|
+
Requires-Dist: notebook; extra == 'notebooks'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
61
27
|
|
|
62
28
|
<div align="center">
|
|
63
29
|
<img src="imgs/ORC_logo_cropped.png" alt="ORC Logo" width="200px" />
|
|
@@ -79,6 +45,11 @@ The easiest way to get started with ORC is to install from PyPI:
|
|
|
79
45
|
pip install OpenReservoirComputing
|
|
80
46
|
```
|
|
81
47
|
|
|
48
|
+
or, if you use [uv](https://docs.astral.sh/uv/):
|
|
49
|
+
```bash
|
|
50
|
+
uv add OpenReservoirComputing
|
|
51
|
+
```
|
|
52
|
+
|
|
82
53
|
If you're interested in the latest, unreleased version or in contributing, you can install from source. Please see the Contribution guidelines below for more details.
|
|
83
54
|
|
|
84
55
|
## Quick start example
|
|
@@ -131,35 +102,41 @@ plt.show()
|
|
|
131
102
|
ORC models are built on top of [Equinox](https://docs.kidger.site/equinox/), and as a result we strongly recommend the use of Equinox transforms `eqx.filter_{jit, vmap, grad}` over `jax.{jit, vmap, grad}`. For more details, please check out the [JAX JIT Compatibility](https://Jan-Williams.github.io/OpenReservoirComputing/examples/jit_compatibility/) example notebook.
|
|
132
103
|
|
|
133
104
|
## Contribution guidelines
|
|
134
|
-
First off, thanks for helping out! We appreciate your willingness to contribute!
|
|
105
|
+
First off, thanks for helping out! We appreciate your willingness to contribute! ORC uses [uv](https://docs.astral.sh/uv/) to manage development environments, which keeps setup to a single command.
|
|
106
|
+
|
|
107
|
+
First, [install uv](https://docs.astral.sh/uv/getting-started/installation/) if you don't have it. Then clone the repo and sync:
|
|
135
108
|
|
|
136
109
|
```bash
|
|
137
110
|
git clone https://github.com/Jan-Williams/OpenReservoirComputing.git
|
|
111
|
+
cd OpenReservoirComputing
|
|
112
|
+
uv sync
|
|
138
113
|
```
|
|
139
114
|
|
|
140
|
-
|
|
115
|
+
`uv sync` creates a `.venv`, installs ORC in editable mode, and installs the development dependencies at the exact versions recorded in `uv.lock`. There is no need to activate the environment — just prefix commands with `uv run`.
|
|
141
116
|
|
|
142
|
-
|
|
117
|
+
For NVIDIA GPU support (Linux only — see the [installation docs](https://Jan-Williams.github.io/OpenReservoirComputing/getting-started/installation/) for why):
|
|
143
118
|
```bash
|
|
144
|
-
|
|
119
|
+
uv sync --extra gpu
|
|
145
120
|
```
|
|
146
121
|
|
|
147
|
-
|
|
122
|
+
To also install the documentation toolchain:
|
|
148
123
|
```bash
|
|
149
|
-
|
|
124
|
+
uv sync --all-groups
|
|
150
125
|
```
|
|
151
126
|
|
|
152
127
|
The main branch is protected from direct changes. If you would like to make a change please create a new branch and work on your new feature. After you are satisfied with your changes, please run our testing suite to ensure all is working well. We also expect new tests to be written for all changes if additions are made. The tests can be simply run from the root directory of the repository with
|
|
153
128
|
```bash
|
|
154
|
-
pytest
|
|
129
|
+
uv run pytest
|
|
155
130
|
```
|
|
156
131
|
Followed by a formatting check
|
|
157
132
|
```bash
|
|
158
|
-
ruff check
|
|
133
|
+
uv run ruff check
|
|
159
134
|
```
|
|
160
135
|
and a type annotation check
|
|
161
136
|
```bash
|
|
162
|
-
ty check
|
|
137
|
+
uv run ty check src/
|
|
163
138
|
```
|
|
164
139
|
|
|
165
|
-
|
|
140
|
+
If you add or change a dependency, use `uv add <package>` (or `uv add --group dev <package>` for a development tool) and **commit the updated `uv.lock`** — CI runs `uv sync --locked` and will fail if the lockfile is out of date.
|
|
141
|
+
|
|
142
|
+
Finally, submit your changes as a pull request! When you submit the PR, please request reviews from both @dtretiak and @Jan-Williams, we will try to get back to you as soon as possible. When you submit the PR, the above tests will automatically be run on your proposed changes through Github Actions, so it is best to get everything tested first before submitting!
|
|
@@ -18,6 +18,11 @@ The easiest way to get started with ORC is to install from PyPI:
|
|
|
18
18
|
pip install OpenReservoirComputing
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
or, if you use [uv](https://docs.astral.sh/uv/):
|
|
22
|
+
```bash
|
|
23
|
+
uv add OpenReservoirComputing
|
|
24
|
+
```
|
|
25
|
+
|
|
21
26
|
If you're interested in the latest, unreleased version or in contributing, you can install from source. Please see the Contribution guidelines below for more details.
|
|
22
27
|
|
|
23
28
|
## Quick start example
|
|
@@ -70,35 +75,41 @@ plt.show()
|
|
|
70
75
|
ORC models are built on top of [Equinox](https://docs.kidger.site/equinox/), and as a result we strongly recommend the use of Equinox transforms `eqx.filter_{jit, vmap, grad}` over `jax.{jit, vmap, grad}`. For more details, please check out the [JAX JIT Compatibility](https://Jan-Williams.github.io/OpenReservoirComputing/examples/jit_compatibility/) example notebook.
|
|
71
76
|
|
|
72
77
|
## Contribution guidelines
|
|
73
|
-
First off, thanks for helping out! We appreciate your willingness to contribute!
|
|
78
|
+
First off, thanks for helping out! We appreciate your willingness to contribute! ORC uses [uv](https://docs.astral.sh/uv/) to manage development environments, which keeps setup to a single command.
|
|
79
|
+
|
|
80
|
+
First, [install uv](https://docs.astral.sh/uv/getting-started/installation/) if you don't have it. Then clone the repo and sync:
|
|
74
81
|
|
|
75
82
|
```bash
|
|
76
83
|
git clone https://github.com/Jan-Williams/OpenReservoirComputing.git
|
|
84
|
+
cd OpenReservoirComputing
|
|
85
|
+
uv sync
|
|
77
86
|
```
|
|
78
87
|
|
|
79
|
-
|
|
88
|
+
`uv sync` creates a `.venv`, installs ORC in editable mode, and installs the development dependencies at the exact versions recorded in `uv.lock`. There is no need to activate the environment — just prefix commands with `uv run`.
|
|
80
89
|
|
|
81
|
-
|
|
90
|
+
For NVIDIA GPU support (Linux only — see the [installation docs](https://Jan-Williams.github.io/OpenReservoirComputing/getting-started/installation/) for why):
|
|
82
91
|
```bash
|
|
83
|
-
|
|
92
|
+
uv sync --extra gpu
|
|
84
93
|
```
|
|
85
94
|
|
|
86
|
-
|
|
95
|
+
To also install the documentation toolchain:
|
|
87
96
|
```bash
|
|
88
|
-
|
|
97
|
+
uv sync --all-groups
|
|
89
98
|
```
|
|
90
99
|
|
|
91
100
|
The main branch is protected from direct changes. If you would like to make a change please create a new branch and work on your new feature. After you are satisfied with your changes, please run our testing suite to ensure all is working well. We also expect new tests to be written for all changes if additions are made. The tests can be simply run from the root directory of the repository with
|
|
92
101
|
```bash
|
|
93
|
-
pytest
|
|
102
|
+
uv run pytest
|
|
94
103
|
```
|
|
95
104
|
Followed by a formatting check
|
|
96
105
|
```bash
|
|
97
|
-
ruff check
|
|
106
|
+
uv run ruff check
|
|
98
107
|
```
|
|
99
108
|
and a type annotation check
|
|
100
109
|
```bash
|
|
101
|
-
ty check
|
|
110
|
+
uv run ty check src/
|
|
102
111
|
```
|
|
103
112
|
|
|
113
|
+
If you add or change a dependency, use `uv add <package>` (or `uv add --group dev <package>` for a development tool) and **commit the updated `uv.lock`** — CI runs `uv sync --locked` and will fail if the lockfile is out of date.
|
|
114
|
+
|
|
104
115
|
Finally, submit your changes as a pull request! When you submit the PR, please request reviews from both @dtretiak and @Jan-Williams, we will try to get back to you as soon as possible. When you submit the PR, the above tests will automatically be run on your proposed changes through Github Actions, so it is best to get everything tested first before submitting!
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27", "hatch-vcs>=0.4"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "OpenReservoirComputing"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Jan P. Williams", email = "jmpw1@uw.edu" },
|
|
9
|
+
{ name = "Dima Tretiak", email = "dtretiak@uw.edu" },
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
maintainers = [
|
|
13
|
+
{ name = "Jan P. Williams", email = "jmpw1@uw.edu" },
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
description = "GPU accelerated implementations of common RC architectures"
|
|
17
|
+
readme = "README.md"
|
|
18
|
+
dynamic = ["version"]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"jax",
|
|
21
|
+
"equinox",
|
|
22
|
+
"matplotlib",
|
|
23
|
+
"diffrax",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
requires-python = ">=3.10, <3.14"
|
|
27
|
+
keywords = ["reservoir computing"]
|
|
28
|
+
classifiers = [
|
|
29
|
+
"License :: OSI Approved :: Apache Software License",
|
|
30
|
+
"Intended Audience :: Science/Research",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
33
|
+
"Programming Language :: Python :: 3.12",
|
|
34
|
+
"Programming Language :: Python :: 3.13",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Repository = "https://github.com/Jan-Williams/OpenReservoirComputing"
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
# JAX publishes CUDA wheels for Linux only (see JAX's installation docs). The marker
|
|
42
|
+
# keeps this extra a no-op on macOS and native Windows instead of an install error.
|
|
43
|
+
# Windows users with an NVIDIA GPU should install under WSL2, which reports
|
|
44
|
+
# sys_platform == "linux" and therefore does get the CUDA build.
|
|
45
|
+
gpu = ["jax[cuda12]; sys_platform == 'linux'"]
|
|
46
|
+
notebooks = ["notebook", "ipykernel"]
|
|
47
|
+
|
|
48
|
+
# Development and documentation dependencies live in PEP 735 dependency groups rather
|
|
49
|
+
# than extras: they are never published in the package metadata, and `uv sync` installs
|
|
50
|
+
# them automatically. They are therefore NOT reachable via `pip install -e ".[dev]"`.
|
|
51
|
+
[dependency-groups]
|
|
52
|
+
dev = [
|
|
53
|
+
"ruff",
|
|
54
|
+
"ty",
|
|
55
|
+
"pytest",
|
|
56
|
+
"pytest-cov",
|
|
57
|
+
"pytest-env",
|
|
58
|
+
"coverage",
|
|
59
|
+
]
|
|
60
|
+
docs = [
|
|
61
|
+
"mkdocs",
|
|
62
|
+
"mkdocs-material",
|
|
63
|
+
"mkdocs-autorefs",
|
|
64
|
+
"mkdocstrings[python]",
|
|
65
|
+
"mkdocs-jupyter",
|
|
66
|
+
# mkdocs.yml runs mkdocs-jupyter with execute: true / allow_errors: false, so the
|
|
67
|
+
# docs build executes every example notebook and needs a Jupyter kernel.
|
|
68
|
+
"notebook",
|
|
69
|
+
"ipykernel",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
[tool.uv]
|
|
73
|
+
default-groups = ["dev"]
|
|
74
|
+
required-version = ">=0.9.0"
|
|
75
|
+
|
|
76
|
+
[tool.hatch.version]
|
|
77
|
+
source = "vcs"
|
|
78
|
+
|
|
79
|
+
[tool.hatch.build.targets.wheel]
|
|
80
|
+
# Required explicitly: hatchling's autodetection looks for the normalized project name
|
|
81
|
+
# ("openreservoircomputing"), which does not match the import package ("orc").
|
|
82
|
+
packages = ["src/orc"]
|
|
83
|
+
|
|
84
|
+
[tool.hatch.build.targets.sdist]
|
|
85
|
+
# only-include (not include): docs/examples is a symlink to ../examples, and hatchling's
|
|
86
|
+
# tree walk follows symlinks while de-duplicating on (st_dev, st_ino) -- so whichever of
|
|
87
|
+
# examples/ or docs/examples/ is visited first wins and the other is dropped, making the
|
|
88
|
+
# sdist non-deterministic across machines. only-include bypasses the tree walk entirely.
|
|
89
|
+
only-include = [
|
|
90
|
+
"src/orc",
|
|
91
|
+
"tests",
|
|
92
|
+
"examples",
|
|
93
|
+
"pyproject.toml",
|
|
94
|
+
"README.md",
|
|
95
|
+
"LICENSE",
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
[tool.pytest.ini_options]
|
|
99
|
+
testpaths = "tests"
|
|
100
|
+
addopts = "--ignore=scripts --ignore=docs"
|
|
101
|
+
env = ["MPLBACKEND=Agg"]
|
|
102
|
+
|
|
103
|
+
[tool.coverage.run]
|
|
104
|
+
source_pkgs = ["orc"]
|
|
105
|
+
branch = true
|
|
106
|
+
|
|
107
|
+
[tool.coverage.paths]
|
|
108
|
+
# Canonicalize editable (src/orc) and non-editable (site-packages/orc) layouts so
|
|
109
|
+
# reports are comparable regardless of how the project was installed.
|
|
110
|
+
source = ["src/orc", "*/site-packages/orc"]
|
|
111
|
+
|
|
112
|
+
[tool.ruff]
|
|
113
|
+
exclude = ["examples"]
|
|
114
|
+
|
|
115
|
+
[tool.ruff.lint]
|
|
116
|
+
select = ["E", "W", "F", "D", "UP", "B", "A", "C4", "N", "SIM", "I"]
|
|
117
|
+
|
|
118
|
+
ignore = ["N806", "N802", "N803", "F722"]
|
|
119
|
+
|
|
120
|
+
[tool.ruff.lint.per-file-ignores]
|
|
121
|
+
"tests/*" = ["D", "E402"]
|
|
122
|
+
"docs/*.py" = ["D", "A"]
|
|
123
|
+
"*.ipynb" = ["E", "W", "F", "D", "UP", "B", "A", "C4", "N", "SIM", "I"]
|
|
124
|
+
|
|
125
|
+
[tool.ruff.lint.pycodestyle]
|
|
126
|
+
max-line-length = 88
|
|
127
|
+
|
|
128
|
+
[tool.ruff.lint.pydocstyle]
|
|
129
|
+
convention = "numpy"
|
|
130
|
+
|
|
131
|
+
[tool.ty.src]
|
|
132
|
+
exclude = ["tests", "examples"]
|
|
@@ -17,7 +17,7 @@ from orc import (
|
|
|
17
17
|
|
|
18
18
|
if not getattr(jax.config, "jax_enable_x64", False):
|
|
19
19
|
warnings.warn(
|
|
20
|
-
"For good performance, orc often requires float64 precision. Enable it "
|
|
20
|
+
"For good performance, orc often requires float64 precision. Enable it "
|
|
21
21
|
"before importing orc with: "
|
|
22
22
|
"jax.config.update('jax_enable_x64', True)",
|
|
23
23
|
UserWarning,
|
|
@@ -17,6 +17,7 @@ from orc.utils.regressions import (
|
|
|
17
17
|
|
|
18
18
|
VariableRCClassifier = TypeVar(name="VariableRCClassifier", bound=RCClassifierBase)
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
def train_RCClassifier(
|
|
21
22
|
model: VariableRCClassifier,
|
|
22
23
|
train_seqs: Array,
|
|
@@ -66,9 +67,7 @@ def train_RCClassifier(
|
|
|
66
67
|
raise ValueError("Number of training sequences must match number of labels.")
|
|
67
68
|
|
|
68
69
|
n_samples = train_seqs.shape[0]
|
|
69
|
-
initial_res_states = jnp.tile(
|
|
70
|
-
model.driver.default_state(), (n_samples, 1)
|
|
71
|
-
)
|
|
70
|
+
initial_res_states = jnp.tile(model.driver.default_state(), (n_samples, 1))
|
|
72
71
|
|
|
73
72
|
# Force all sequences through the reservoir in parallel via vmap
|
|
74
73
|
all_res_seqs = jax.vmap(model.force)(train_seqs, initial_res_states)
|
|
@@ -179,9 +179,7 @@ class RCControllerBase(eqx.Module, ABC):
|
|
|
179
179
|
return self.force(in_seq, control_seq, res_state)
|
|
180
180
|
|
|
181
181
|
@eqx.filter_jit
|
|
182
|
-
def apply_control(
|
|
183
|
-
self, control_seq: Array, res_state: Array
|
|
184
|
-
) -> tuple[Array, Array]:
|
|
182
|
+
def apply_control(self, control_seq: Array, res_state: Array) -> Array:
|
|
185
183
|
"""Apply a predefined control sequence in closed-loop.
|
|
186
184
|
|
|
187
185
|
The readout feeds back as the next input: u(t+1) = readout(x(t)).
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Training functions for reservoir computer controllers."""
|
|
2
|
+
|
|
3
|
+
from typing import TypeVar
|
|
4
|
+
|
|
5
|
+
import equinox as eqx
|
|
6
|
+
import jax.numpy as jnp
|
|
7
|
+
from jaxtyping import Array
|
|
8
|
+
|
|
9
|
+
from orc.control.base import RCControllerBase
|
|
10
|
+
from orc.control.models import ESNController
|
|
11
|
+
from orc.utils.regressions import (
|
|
12
|
+
_solve_all_ridge_reg,
|
|
13
|
+
_solve_all_ridge_reg_batched,
|
|
14
|
+
ridge_regression,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
VariableRCController = TypeVar(name="VariableRCController", bound=RCControllerBase)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def train_RCController(
|
|
21
|
+
model: VariableRCController,
|
|
22
|
+
train_seq: Array,
|
|
23
|
+
control_seq: Array,
|
|
24
|
+
target_seq: Array | None = None,
|
|
25
|
+
spinup: int = 0,
|
|
26
|
+
initial_res_state: Array | None = None,
|
|
27
|
+
beta: float = 8e-8,
|
|
28
|
+
batch_size: int | None = None,
|
|
29
|
+
multi_sequence: bool = False,
|
|
30
|
+
**force_kwargs,
|
|
31
|
+
) -> tuple[VariableRCController, Array]:
|
|
32
|
+
"""Unified training function for reservoir computer controllers.
|
|
33
|
+
|
|
34
|
+
Works with any model inheriting from RCControllerBase, including
|
|
35
|
+
ESNController and custom models with user-defined readout layers.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
model : RCControllerBase
|
|
40
|
+
Reservoir computer controller model to train.
|
|
41
|
+
train_seq : Array
|
|
42
|
+
Training input sequence for reservoir. Shape ``(seq_len, data_dim)``
|
|
43
|
+
for a single trajectory, or ``(n_traj, seq_len, data_dim)`` when
|
|
44
|
+
``multi_sequence=True``. All trajectories must have the same length.
|
|
45
|
+
control_seq : Array
|
|
46
|
+
Control input sequence for reservoir. Shape ``(seq_len, control_dim)``
|
|
47
|
+
for a single trajectory, or ``(n_traj, seq_len, control_dim)`` when
|
|
48
|
+
``multi_sequence=True``.
|
|
49
|
+
target_seq : Array, optional
|
|
50
|
+
Target sequence for training reservoir. Shape ``(seq_len, data_dim)``
|
|
51
|
+
for a single trajectory, or ``(n_traj, seq_len, data_dim)`` when
|
|
52
|
+
``multi_sequence=True``. If None, defaults to train_seq[1:] (or
|
|
53
|
+
train_seq[:, 1:] for multi-sequence).
|
|
54
|
+
spinup : int
|
|
55
|
+
Initial transient of reservoir states to discard per trajectory.
|
|
56
|
+
initial_res_state : Array, optional
|
|
57
|
+
Initial reservoir state. If None, uses model.driver.default_state()
|
|
58
|
+
(tiled across trajectories when ``multi_sequence=True``). When
|
|
59
|
+
``multi_sequence=True`` and not None, must have a leading trajectory
|
|
60
|
+
axis, i.e. shape ``(n_traj, ...)``.
|
|
61
|
+
beta : float
|
|
62
|
+
Tikhonov regularization parameter.
|
|
63
|
+
batch_size : int, optional
|
|
64
|
+
Number of parallel reservoirs to process in each batch for ridge
|
|
65
|
+
regression. If None (default), processes all reservoirs at once.
|
|
66
|
+
Only used when readout.chunks > 0.
|
|
67
|
+
multi_sequence : bool
|
|
68
|
+
If True, treat train_seq, control_seq, and target_seq as batches of
|
|
69
|
+
trajectories with a leading trajectory axis. Reservoir states from all
|
|
70
|
+
trajectories are concatenated (after spinup) before solving the
|
|
71
|
+
regression. This flag is a static boolean suitable for use as
|
|
72
|
+
``static_argnums`` when wrapping with ``jax.jit``.
|
|
73
|
+
**force_kwargs
|
|
74
|
+
Additional keyword arguments passed to model.force().
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
model : RCControllerBase
|
|
79
|
+
Trained model.
|
|
80
|
+
tot_res_seq : Array
|
|
81
|
+
Full reservoir state sequence from teacher forcing. Shape
|
|
82
|
+
``(seq_len, ...)`` for single-sequence or ``(n_traj, seq_len, ...)``
|
|
83
|
+
for multi-sequence.
|
|
84
|
+
"""
|
|
85
|
+
if multi_sequence:
|
|
86
|
+
if train_seq.shape[:2] != control_seq.shape[:2]:
|
|
87
|
+
raise ValueError(
|
|
88
|
+
"train_seq and control_seq must have the same (n_traj, seq_len)."
|
|
89
|
+
)
|
|
90
|
+
else:
|
|
91
|
+
if train_seq.shape[0] != control_seq.shape[0]:
|
|
92
|
+
raise ValueError("train_seq and control_seq must have the same length.")
|
|
93
|
+
|
|
94
|
+
seq_len = train_seq.shape[1] if multi_sequence else train_seq.shape[0]
|
|
95
|
+
if spinup >= seq_len:
|
|
96
|
+
raise ValueError(
|
|
97
|
+
"spinup must be less than the length of the training sequence."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if initial_res_state is None:
|
|
101
|
+
default = model.driver.default_state()
|
|
102
|
+
if multi_sequence:
|
|
103
|
+
n_traj = train_seq.shape[0]
|
|
104
|
+
initial_res_state = jnp.broadcast_to(
|
|
105
|
+
default[None], (n_traj,) + default.shape
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
initial_res_state = default
|
|
109
|
+
|
|
110
|
+
if target_seq is None:
|
|
111
|
+
tot_seq = train_seq
|
|
112
|
+
tot_control_seq = control_seq
|
|
113
|
+
target_seq = train_seq[:, 1:, :] if multi_sequence else train_seq[1:, :]
|
|
114
|
+
else:
|
|
115
|
+
if multi_sequence:
|
|
116
|
+
tot_seq = jnp.concatenate([train_seq, target_seq[:, -1:, :]], axis=1)
|
|
117
|
+
tot_control_seq = jnp.concatenate(
|
|
118
|
+
[control_seq, control_seq[:, -1:, :]], axis=1
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
tot_seq = jnp.vstack((train_seq, target_seq[-1:]))
|
|
122
|
+
tot_control_seq = jnp.vstack((control_seq, control_seq[-1:]))
|
|
123
|
+
|
|
124
|
+
if multi_sequence:
|
|
125
|
+
tot_res_seq = eqx.filter_vmap(model.force)(
|
|
126
|
+
tot_seq, tot_control_seq, initial_res_state
|
|
127
|
+
)
|
|
128
|
+
res_seq = tot_res_seq[:, :-1]
|
|
129
|
+
res_seq_train = eqx.filter_vmap(model.readout.prepare_train)(res_seq)
|
|
130
|
+
train_target = eqx.filter_vmap(model.readout.prepare_target)(target_seq)
|
|
131
|
+
res_seq_train = res_seq_train[:, spinup:].reshape(
|
|
132
|
+
(-1,) + res_seq_train.shape[2:]
|
|
133
|
+
)
|
|
134
|
+
train_target = train_target[:, spinup:].reshape((-1,) + train_target.shape[2:])
|
|
135
|
+
else:
|
|
136
|
+
tot_res_seq = model.force(
|
|
137
|
+
tot_seq, tot_control_seq, initial_res_state, **force_kwargs
|
|
138
|
+
)
|
|
139
|
+
res_seq = tot_res_seq[:-1]
|
|
140
|
+
res_seq_train = model.readout.prepare_train(res_seq)
|
|
141
|
+
train_target = model.readout.prepare_target(target_seq)
|
|
142
|
+
res_seq_train = res_seq_train[spinup:]
|
|
143
|
+
train_target = train_target[spinup:]
|
|
144
|
+
|
|
145
|
+
if model.readout.chunks > 0:
|
|
146
|
+
if batch_size is None:
|
|
147
|
+
cmat = _solve_all_ridge_reg(res_seq_train, train_target, beta)
|
|
148
|
+
else:
|
|
149
|
+
cmat = _solve_all_ridge_reg_batched(
|
|
150
|
+
res_seq_train, train_target, beta, batch_size
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
cmat = ridge_regression(res_seq_train, train_target, beta)
|
|
154
|
+
|
|
155
|
+
new_readout = model.readout.set_wout(cmat)
|
|
156
|
+
model = eqx.tree_at(lambda m: m.readout, model, new_readout)
|
|
157
|
+
|
|
158
|
+
return model, tot_res_seq
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def train_ESNController(
|
|
162
|
+
model: ESNController,
|
|
163
|
+
train_seq: Array,
|
|
164
|
+
control_seq: Array,
|
|
165
|
+
target_seq: Array | None = None,
|
|
166
|
+
spinup: int = 0,
|
|
167
|
+
initial_res_state: Array | None = None,
|
|
168
|
+
beta: float = 8e-8,
|
|
169
|
+
multi_sequence: bool = False,
|
|
170
|
+
) -> tuple[ESNController, Array]:
|
|
171
|
+
"""Training function for ESNController.
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
model : ESNController
|
|
176
|
+
ESNController model to train.
|
|
177
|
+
train_seq : Array
|
|
178
|
+
Training input sequence for reservoir, (shape=(seq_len, data_dim)).
|
|
179
|
+
control_seq : Array
|
|
180
|
+
Control input sequence for reservoir, (shape=(seq_len, control_dim)).
|
|
181
|
+
target_seq : Array
|
|
182
|
+
Target sequence for training reservoir, (shape=(seq_len, data_dim)).
|
|
183
|
+
If None, defaults to train_seq[1:].
|
|
184
|
+
initial_res_state : Array
|
|
185
|
+
Initial reservoir state, (shape=(res_dim,)).
|
|
186
|
+
spinup : int
|
|
187
|
+
Initial transient of reservoir states to discard.
|
|
188
|
+
beta : float
|
|
189
|
+
Tikhonov regularization parameter.
|
|
190
|
+
multi_sequence : bool
|
|
191
|
+
If True, treat train_seq and control_seq as batches of trajectories
|
|
192
|
+
with shape (n_traj, seq_len, ...). See train_RCController for details.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
model : ESNController
|
|
197
|
+
Trained ESN controller model.
|
|
198
|
+
res_seq : Array
|
|
199
|
+
Training sequence of reservoir states.
|
|
200
|
+
"""
|
|
201
|
+
if not isinstance(model, ESNController):
|
|
202
|
+
raise TypeError("Model must be an ESNController.")
|
|
203
|
+
|
|
204
|
+
return train_RCController(
|
|
205
|
+
model,
|
|
206
|
+
train_seq,
|
|
207
|
+
control_seq,
|
|
208
|
+
target_seq,
|
|
209
|
+
spinup,
|
|
210
|
+
initial_res_state,
|
|
211
|
+
beta,
|
|
212
|
+
multi_sequence=multi_sequence,
|
|
213
|
+
)
|
|
@@ -869,9 +869,7 @@ class GRUDriver(DriverBase):
|
|
|
869
869
|
super().__init__(res_dim=res_dim)
|
|
870
870
|
key = jax.random.key(seed)
|
|
871
871
|
gru = eqx.nn.GRUCell(res_dim, res_dim, key=key)
|
|
872
|
-
gru = eqx.tree_at(
|
|
873
|
-
lambda c: c.weight_ih, gru, gru.weight_ih * input_rescaling
|
|
874
|
-
)
|
|
872
|
+
gru = eqx.tree_at(lambda c: c.weight_ih, gru, gru.weight_ih * input_rescaling)
|
|
875
873
|
self.gru = gru
|
|
876
874
|
|
|
877
875
|
def advance(self, proj_vars, res_state):
|
|
@@ -391,7 +391,7 @@ class CRCForecasterBase(RCForecasterBase, ABC):
|
|
|
391
391
|
res_seq = sol.ys
|
|
392
392
|
return res_seq
|
|
393
393
|
|
|
394
|
-
def __call__(self, in_seq: Array, res_state: Array, ts: Array) -> Array:
|
|
394
|
+
def __call__(self, in_seq: Array, res_state: Array, ts: Array) -> Array: # ty: ignore[invalid-method-override]
|
|
395
395
|
"""
|
|
396
396
|
Teacher forces the reservoir, wrapper for `force` method.
|
|
397
397
|
|