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.
Files changed (89) hide show
  1. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/.gitignore +2 -4
  2. {openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info → openreservoircomputing-0.4.0}/PKG-INFO +32 -55
  3. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/README.md +20 -9
  4. openreservoircomputing-0.4.0/pyproject.toml +132 -0
  5. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/__init__.py +1 -1
  6. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/classifier/train.py +2 -3
  7. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/control/base.py +1 -3
  8. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/control/models.py +1 -1
  9. openreservoircomputing-0.4.0/src/orc/control/train.py +213 -0
  10. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/drivers.py +1 -3
  11. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/forecaster/base.py +1 -1
  12. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/forecaster/train.py +116 -38
  13. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/classifier/test_classifier_models.py +4 -6
  14. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/control/test_control_models.py +151 -3
  15. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/forecaster/test_forecast_models.py +296 -8
  16. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/test_drivers.py +1 -1
  17. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/test_readouts.py +10 -4
  18. openreservoircomputing-0.3.2/.github/CODEOWNERS +0 -1
  19. openreservoircomputing-0.3.2/.github/workflows/docs.yml +0 -26
  20. openreservoircomputing-0.3.2/.github/workflows/draft-pdf.yml +0 -28
  21. openreservoircomputing-0.3.2/.github/workflows/publish.yml +0 -46
  22. openreservoircomputing-0.3.2/.github/workflows/tests.yml +0 -63
  23. openreservoircomputing-0.3.2/JOSS/CITATION.cff +0 -39
  24. openreservoircomputing-0.3.2/JOSS/CODE_OF_CONDUCT.md +0 -85
  25. openreservoircomputing-0.3.2/JOSS/joss-paper.pdf +0 -0
  26. openreservoircomputing-0.3.2/JOSS/paper.bib +0 -149
  27. openreservoircomputing-0.3.2/JOSS/paper.md +0 -115
  28. openreservoircomputing-0.3.2/PKG-INFO +0 -165
  29. openreservoircomputing-0.3.2/docs/api/classifier.md +0 -31
  30. openreservoircomputing-0.3.2/docs/api/control.md +0 -31
  31. openreservoircomputing-0.3.2/docs/api/data.md +0 -39
  32. openreservoircomputing-0.3.2/docs/api/drivers.md +0 -19
  33. openreservoircomputing-0.3.2/docs/api/embeddings.md +0 -19
  34. openreservoircomputing-0.3.2/docs/api/forecaster.md +0 -59
  35. openreservoircomputing-0.3.2/docs/api/readouts.md +0 -35
  36. openreservoircomputing-0.3.2/docs/api/utils.md +0 -36
  37. openreservoircomputing-0.3.2/docs/contributing.md +0 -68
  38. openreservoircomputing-0.3.2/docs/getting-started/installation.md +0 -83
  39. openreservoircomputing-0.3.2/docs/getting-started/quickstart.md +0 -162
  40. openreservoircomputing-0.3.2/docs/imgs/ORC_logo_cropped.png +0 -0
  41. openreservoircomputing-0.3.2/docs/index.md +0 -137
  42. openreservoircomputing-0.3.2/docs/javascripts/mathjax.js +0 -26
  43. openreservoircomputing-0.3.2/docs/stylesheets/extra.css +0 -78
  44. openreservoircomputing-0.3.2/docs/user-guide/data.md +0 -93
  45. openreservoircomputing-0.3.2/docs/user-guide/drivers.md +0 -186
  46. openreservoircomputing-0.3.2/docs/user-guide/embeddings.md +0 -151
  47. openreservoircomputing-0.3.2/docs/user-guide/models.md +0 -454
  48. openreservoircomputing-0.3.2/docs/user-guide/overview.md +0 -108
  49. openreservoircomputing-0.3.2/docs/user-guide/readouts.md +0 -185
  50. openreservoircomputing-0.3.2/imgs/ORC_logo_cropped.png +0 -0
  51. openreservoircomputing-0.3.2/imgs/architecture_paper.pdf +0 -0
  52. openreservoircomputing-0.3.2/imgs/readme_example_forecast.png +0 -0
  53. openreservoircomputing-0.3.2/mkdocs.yml +0 -129
  54. openreservoircomputing-0.3.2/pyproject.toml +0 -113
  55. openreservoircomputing-0.3.2/setup.cfg +0 -4
  56. openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info/SOURCES.txt +0 -86
  57. openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info/dependency_links.txt +0 -1
  58. openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info/requires.txt +0 -47
  59. openreservoircomputing-0.3.2/src/OpenReservoirComputing.egg-info/top_level.txt +0 -1
  60. openreservoircomputing-0.3.2/src/orc/control/train.py +0 -164
  61. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/LICENSE +0 -0
  62. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/benchmark_combined.py +0 -0
  63. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/classification.ipynb +0 -0
  64. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/continuous_rc.ipynb +0 -0
  65. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/control.ipynb +0 -0
  66. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/data_library.ipynb +0 -0
  67. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/jit_compatibility.ipynb +0 -0
  68. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/ks.ipynb +0 -0
  69. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/lorenz.ipynb +0 -0
  70. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/examples/rc_background.ipynb +0 -0
  71. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/classifier/__init__.py +0 -0
  72. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/classifier/base.py +0 -0
  73. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/classifier/models.py +0 -0
  74. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/control/__init__.py +0 -0
  75. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/data/__init__.py +0 -0
  76. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/data/integrators.py +0 -0
  77. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/embeddings.py +0 -0
  78. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/forecaster/__init__.py +0 -0
  79. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/forecaster/models.py +0 -0
  80. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/readouts.py +0 -0
  81. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/utils/__init__.py +0 -0
  82. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/utils/numerics.py +0 -0
  83. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/utils/regressions.py +0 -0
  84. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/src/orc/utils/visualization.py +0 -0
  85. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/data/test_integrators.py +0 -0
  86. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/test_embeddings.py +0 -0
  87. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/utils/test_numerics.py +0 -0
  88. {openreservoircomputing-0.3.2 → openreservoircomputing-0.4.0}/tests/utils/test_regressions.py +0 -0
  89. {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
- # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
101
- # This is especially recommended for binary packages to ensure reproducibility, and is more
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.2
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
- Project-URL: Repository, https://github.com/Jan-Williams/OpenReservoirComputing
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
- Description-Content-Type: text/markdown
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 == "gpu"
54
- Provides-Extra: docs
55
- Requires-Dist: mkdocs; extra == "docs"
56
- Requires-Dist: mkdocs-material; extra == "docs"
57
- Requires-Dist: mkdocs-autorefs; extra == "docs"
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! To get started, clone the repo and install the developer dependencies of ORC.
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
- From the root directory of the repository, create an editable install for your given hardware.
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
- CPU:
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
- pip install -e ".[dev]"
119
+ uv sync --extra gpu
145
120
  ```
146
121
 
147
- GPU:
122
+ To also install the documentation toolchain:
148
123
  ```bash
149
- pip install -e ".[dev, gpu]"
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
- 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!
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! To get started, clone the repo and install the developer dependencies of ORC.
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
- From the root directory of the repository, create an editable install for your given hardware.
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
- CPU:
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
- pip install -e ".[dev]"
92
+ uv sync --extra gpu
84
93
  ```
85
94
 
86
- GPU:
95
+ To also install the documentation toolchain:
87
96
  ```bash
88
- pip install -e ".[dev, gpu]"
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)).
@@ -152,5 +152,5 @@ class ESNController(RCControllerBase):
152
152
  seed=seed,
153
153
  alpha_1=alpha_1,
154
154
  alpha_2=alpha_2,
155
- alpha_3=alpha_3
155
+ alpha_3=alpha_3,
156
156
  )
@@ -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: # ty: ignore[invalid-method-override]
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