OpenReservoirComputing 0.3.2__tar.gz → 0.3.3__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 (91) hide show
  1. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/JOSS/paper.md +8 -6
  2. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/PKG-INFO +1 -1
  3. openreservoircomputing-0.3.3/imgs/benchmark_results_combined.png +0 -0
  4. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/pyproject.toml +3 -0
  5. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/OpenReservoirComputing.egg-info/PKG-INFO +1 -1
  6. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/OpenReservoirComputing.egg-info/SOURCES.txt +3 -0
  7. openreservoircomputing-0.3.3/src/OpenReservoirComputing.egg-info/scm_file_list.json +86 -0
  8. openreservoircomputing-0.3.3/src/OpenReservoirComputing.egg-info/scm_version.json +8 -0
  9. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/__init__.py +1 -1
  10. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/classifier/train.py +2 -3
  11. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/control/models.py +1 -1
  12. openreservoircomputing-0.3.3/src/orc/control/train.py +213 -0
  13. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/drivers.py +1 -3
  14. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/forecaster/base.py +1 -1
  15. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/forecaster/train.py +116 -38
  16. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/classifier/test_classifier_models.py +4 -6
  17. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/control/test_control_models.py +151 -3
  18. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/forecaster/test_forecast_models.py +296 -8
  19. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/test_drivers.py +1 -1
  20. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/test_readouts.py +10 -4
  21. openreservoircomputing-0.3.2/src/orc/control/train.py +0 -164
  22. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/.github/CODEOWNERS +0 -0
  23. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/.github/workflows/docs.yml +0 -0
  24. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/.github/workflows/draft-pdf.yml +0 -0
  25. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/.github/workflows/publish.yml +0 -0
  26. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/.github/workflows/tests.yml +0 -0
  27. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/.gitignore +0 -0
  28. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/JOSS/CITATION.cff +0 -0
  29. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/JOSS/CODE_OF_CONDUCT.md +0 -0
  30. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/JOSS/joss-paper.pdf +0 -0
  31. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/JOSS/paper.bib +0 -0
  32. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/LICENSE +0 -0
  33. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/README.md +0 -0
  34. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/api/classifier.md +0 -0
  35. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/api/control.md +0 -0
  36. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/api/data.md +0 -0
  37. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/api/drivers.md +0 -0
  38. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/api/embeddings.md +0 -0
  39. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/api/forecaster.md +0 -0
  40. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/api/readouts.md +0 -0
  41. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/api/utils.md +0 -0
  42. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/contributing.md +0 -0
  43. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/getting-started/installation.md +0 -0
  44. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/getting-started/quickstart.md +0 -0
  45. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/imgs/ORC_logo_cropped.png +0 -0
  46. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/index.md +0 -0
  47. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/javascripts/mathjax.js +0 -0
  48. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/stylesheets/extra.css +0 -0
  49. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/user-guide/data.md +0 -0
  50. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/user-guide/drivers.md +0 -0
  51. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/user-guide/embeddings.md +0 -0
  52. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/user-guide/models.md +0 -0
  53. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/user-guide/overview.md +0 -0
  54. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/docs/user-guide/readouts.md +0 -0
  55. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/examples/benchmark_combined.py +0 -0
  56. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/examples/classification.ipynb +0 -0
  57. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/examples/continuous_rc.ipynb +0 -0
  58. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/examples/control.ipynb +0 -0
  59. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/examples/data_library.ipynb +0 -0
  60. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/examples/jit_compatibility.ipynb +0 -0
  61. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/examples/ks.ipynb +0 -0
  62. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/examples/lorenz.ipynb +0 -0
  63. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/examples/rc_background.ipynb +0 -0
  64. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/imgs/ORC_logo_cropped.png +0 -0
  65. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/imgs/architecture_paper.pdf +0 -0
  66. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/imgs/readme_example_forecast.png +0 -0
  67. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/mkdocs.yml +0 -0
  68. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/setup.cfg +0 -0
  69. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/OpenReservoirComputing.egg-info/dependency_links.txt +0 -0
  70. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/OpenReservoirComputing.egg-info/requires.txt +0 -0
  71. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/OpenReservoirComputing.egg-info/top_level.txt +0 -0
  72. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/classifier/__init__.py +0 -0
  73. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/classifier/base.py +0 -0
  74. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/classifier/models.py +0 -0
  75. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/control/__init__.py +0 -0
  76. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/control/base.py +0 -0
  77. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/data/__init__.py +0 -0
  78. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/data/integrators.py +0 -0
  79. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/embeddings.py +0 -0
  80. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/forecaster/__init__.py +0 -0
  81. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/forecaster/models.py +0 -0
  82. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/readouts.py +0 -0
  83. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/utils/__init__.py +0 -0
  84. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/utils/numerics.py +0 -0
  85. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/utils/regressions.py +0 -0
  86. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/src/orc/utils/visualization.py +0 -0
  87. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/data/test_integrators.py +0 -0
  88. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/test_embeddings.py +0 -0
  89. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/utils/test_numerics.py +0 -0
  90. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/utils/test_regressions.py +0 -0
  91. {openreservoircomputing-0.3.2 → openreservoircomputing-0.3.3}/tests/utils/test_visualization.py +0 -0
@@ -44,7 +44,7 @@ OpenReservoirComputing (ORC) is a Python library for reservoir computing (RC) bu
44
44
 
45
45
  # Statement of Need
46
46
 
47
- Time-series prediction, classification, and control are fundamental tasks across science and engineering, arising in applications from climate modeling and fluid dynamics to robotics and neuroscience. Deep learning approaches to these tasks typically require large datasets, long training times, and expensive tuning of optimization hyperparameters. RC offers a compelling alternative. Since only the readout layer is trained via a single ridge regression, RC models can be trained in a fraction of the time required by comparable recurrent neural networks, often with less data and fewer hyperparameters to tune [@lukosevicius2009reservoir,@pathak2018model,@wyder2025common]. This makes RC particularly attractive for rapid prototyping, real-time applications, and data-limited settings. However, realizing these benefits in practice requires software that is both efficient and adaptable.
47
+ Time-series prediction, classification, and control are fundamental tasks across science and engineering, arising in applications from climate modeling and fluid dynamics to robotics and neuroscience. Deep learning approaches to these tasks typically require large datasets, long training times, and expensive tuning of optimization hyperparameters. RC offers a compelling alternative. Since only the readout layer is trained via a single ridge regression, RC models can be trained in a fraction of the time required by comparable recurrent neural networks, often with less data and fewer hyperparameters to tune [@lukosevicius2009reservoir;@pathak2018model;@wyder2025common]. This makes RC particularly attractive for rapid prototyping, real-time applications, and data-limited settings. However, realizing these benefits in practice requires software that is both efficient and adaptable.
48
48
 
49
49
  ORC's built-in models provide an easy entry point for users new to the field. In particular, a new user can supply their own time-series data to instantiate, train, and forecast in three simple lines of code.
50
50
 
@@ -58,7 +58,7 @@ U_pred = esn.forecast(fcast_len=1000, res_state=R[-1])
58
58
 
59
59
  Built-in visualization tools make it easy to evaluate model performance. Varying the hyperparameters of built-in models lets users explore how RC performance depends on configuration choices. ORC's JAX foundation makes scaling to higher-dimensional parallel reservoir architectures equally simple.
60
60
 
61
- Much RC research is aimed at designing performant reservoir architectures. ORC makes this easy through its use of abstract base classes for Embedding, Driver, and Readout layers. Users need only define forward pass logic to integrate a new reservoir topology or readout strategy, while reusing the rest of the framework. This modular approach also enables ablation studies on how different components affect RC performance. Because of ORC's functional approach in JAX, built-in and user-created models provide end-to-end differentiability by default. This enables gradient-based optimization of input sequences for control problems. This also makes ORC well suited to integrate with deep learning models such as those presented in [@ozalp2023reconstruction; @ozalp2025real].
61
+ Much RC research is aimed at designing performant reservoir architectures. ORC makes this easy through its use of abstract base classes for Embedding, Driver, and Readout layers. Users need only define forward pass logic to integrate a new reservoir topology or readout strategy, while reusing the rest of the framework. This modular approach also enables ablation studies on how different components affect RC performance. Because of ORC's functional approach in JAX, built-in and user-created models provide end-to-end differentiability by default. This enables gradient-based optimization of input sequences for control problems. This also makes ORC well suited to integrate with deep learning models such as those presented by Özalp et al. [-@ozalp2023reconstruction; -@ozalp2025real].
62
62
 
63
63
  # State of the Field
64
64
  | | **ORC** | **ReservoirPy** | **RC.jl** |
@@ -81,12 +81,12 @@ First, ORC was *designed* on top of JAX [@jax2018github] and Equinox [@kidger202
81
81
  Second, ORC has a different built-in feature set. ORC supports continuous-time reservoir dynamics via Diffrax [@kidger2021on], allowing users to define reservoir equations as ordinary differential equations solved with adaptive-step integrators. ORC also supports novel architectures such as Taylor-expanded and GRU-based drivers alongside standard echo state networks. ReservoirPy does not provide these functionalities.
82
82
  Third, ORC models compose seamlessly with other deep learning models in Equinox, whereas ReservoirPy is suited to standalone RC tasks.
83
83
 
84
- Other open source libraries for RC include Pytorch-ESN [@nardo2018pytorchesn] and ReservoirComputing.jl [@martinuzzi2022reservoircomputing]. Pytorch-ESN allows for integration with other PyTorch models, but is not as widely adopted and offers a much more limited feature set than ORC or ReservoirPy. For instance, it does not support sparse matrix operations or parallel reservoirs. ReservoirComputing.jl is a widely used Julia library for RC that influenced many of ORC's design choices, but lacks Python ML ecosystem integration. Table 1 summarizes the functionality of ORC, ReservoirPy, and ReservoirComputing.jl.
84
+ Other open source libraries for RC include PyTorch-ESN [@nardo2018pytorchesn] and ReservoirComputing.jl [@martinuzzi2022reservoircomputing]. PyTorch-ESN allows for integration with other PyTorch models, but is not as widely adopted and offers a much more limited feature set than ORC or ReservoirPy. For instance, it does not support sparse matrix operations or parallel reservoirs. ReservoirComputing.jl is a widely used Julia library for RC that influenced many of ORC's design choices, but lacks Python ML ecosystem integration. Table 1 summarizes the functionality of ORC, ReservoirPy, and ReservoirComputing.jl.
85
85
 
86
86
 
87
87
  # Software Design
88
88
 
89
- ![ORC three-layer pipeline architecture. Each reservoir computer (RC) is decomposed into (i) an ``embedding'' function that lifts a low-dimensional signal $u_t$ to the reservoir dimension, (ii) a ``driver'' function that propagates the reservoir state, and (iii) a ``readout'' that maps back to a target $y$. For control and forecasting RCs, the target $y$ is typically $u_{t+1}$ (either in the presence of a forcing term or not) and for classification the target $y$ is a label. \label{fig:architecture}](../imgs/architecture_paper.pdf)
89
+ ![ORC three-layer pipeline architecture. Each reservoir computer (RC) is decomposed into (i) an embedding function that lifts a low-dimensional signal $u_t$ to the reservoir dimension, (ii) a driver function that propagates the reservoir state, and (iii) a readout that maps back to a target $y$. For control and forecasting RCs, the target $y$ is typically $u_{t+1}$ (either in the presence of a forcing term or not) and for classification the target $y$ is a label. \label{fig:architecture}](../imgs/architecture_paper.pdf)
90
90
 
91
91
  ORC models are decomposed into three components, illustrated in \autoref{fig:architecture}: (i) an embedding $f_E$ that lifts a low-dimensional input signal $u_t$ to a high-dimensional space, (ii) a driver $f_R$ that propagates the high-dimensional state $r_t$, and (iii) a readout $f_O$ that maps the latent state back to an approximation of some low-dimensional signal $y_t$. Depending on the task at hand, $y_t$ may be a future time-step of $u_t$, a label associated with input data, or some other target signal. ORC differs from many existing approaches that unify (i) and (ii). Separating the embedding from the reservoir state propagation allows for cleaner application of RC to non-standard tasks, such as acting as a surrogate model for model predictive control.
92
92
 
@@ -104,12 +104,14 @@ ORC also includes a data generation module with ODE and PDE integrators for stan
104
104
 
105
105
  ![ORC with GPU acceleration enables significantly faster performance than ReservoirPy, even when using ReservoirPy's JAX backend. Panel (a) shows the time per forecast step of RC models with varying reservoir dimension trained to forecast the Lorenz system, while panel (b) shows training time for RC models with fixed reservoir dimension of 2000 but a varying number of training samples. Performance of the two libraries with and without GPU acceleration are shown. GPU results were obtained on an NVIDIA A40 GPU and CPU results were obtained with an Apple M2 chip. \label{fig:comp}](../imgs/benchmark_results_combined.png)
106
106
 
107
- ORC enables easy reimplementation of architectures that integrate RC with larger neural networks [@ozalp2023reconstruction; @ozalp2025real], and makes it easier to iterate on these ideas than any existing library. We also benchmark ORC against ReservoirPy across reservoir sizes and the number of training samples \autoref{fig:comp}. We find that with GPU acceleration, ORC scales far more favorably than ReservoirPy. Moreover, ORC is the only current package that supports training RC for control tasks, enabled by its efficiency and the end-to-end differentiability that JAX and Equinox provide. RC-based control has recently been shown to be advantageous over other forms of RNN-based MPC [@williams2024reservoir]. ORC's performance also makes it well suited for the parallel architectures needed to extend RC to higher-dimensional settings.
107
+ ORC enables easy reimplementation of architectures that integrate RC with larger neural networks [@ozalp2023reconstruction; @ozalp2025real], and makes it easier to iterate on these ideas than any existing library. We also benchmark ORC against ReservoirPy across reservoir sizes and numbers of training samples, as shown in \autoref{fig:comp}. We find that with GPU acceleration, ORC scales far more favorably than ReservoirPy. Moreover, ORC is the only current package that supports training RC for control tasks, enabled by its efficiency and the end-to-end differentiability that JAX and Equinox provide. RC-based control has recently been shown to be advantageous over other forms of RNN-based MPC [@williams2024reservoir]. ORC's performance also makes it well suited for the parallel architectures needed to extend RC to higher-dimensional settings.
108
108
 
109
109
 
110
110
  # AI Usage Disclosure
111
111
 
112
- Claude (Anthropic) was used for code assistance during code debugging, proofreading this paper, and the generation of the architecture visualization SVG. ChatGPT (OpenAI) was used to generate the ORC logo. All generated code was reviewed, tested, and validated by the authors.
112
+ Claude (Anthropic, Opus 4-4.8) was used for code assistance during debugging, for proofreading and editing the text of this paper, and for generating the architecture visualization SVG (\autoref{fig:architecture}). ChatGPT (OpenAI, GPT-4o) was used to generate the ORC logo. All AI-assisted outputs, including code, paper text, and figures, were reviewed, edited, and validated by the authors before inclusion.
113
113
 
114
114
  # Acknowledgements
115
115
  The authors acknowledge support from the NSF AI Institute in Dynamic Systems (grant number 2112085). The authors also thank Anastasia Bizyaeva, Noa Kaplan, and Ling-Wei Kong for insightful conversations.
116
+
117
+ # References
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenReservoirComputing
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: GPU accelerated implementations of common RC architectures
5
5
  Author-email: "Jan P. Williams" <jmpw1@uw.edu>, Dima Tretiak <dtretiak@uw.edu>
6
6
  Maintainer-email: "Jan P. Williams" <jmpw1@uw.edu>
@@ -52,6 +52,9 @@ testpaths = "tests"
52
52
  addopts = "--ignore=scripts --ignore=docs"
53
53
  env = ["MPLBACKEND=Agg"]
54
54
 
55
+ [tool.ruff]
56
+ exclude = ["examples"]
57
+
55
58
  [tool.ruff.lint]
56
59
  select = ["E", "W", "F", "D", "UP", "B", "A", "C4", "N", "SIM", "I"]
57
60
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenReservoirComputing
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: GPU accelerated implementations of common RC architectures
5
5
  Author-email: "Jan P. Williams" <jmpw1@uw.edu>, Dima Tretiak <dtretiak@uw.edu>
6
6
  Maintainer-email: "Jan P. Williams" <jmpw1@uw.edu>
@@ -46,11 +46,14 @@ examples/lorenz.ipynb
46
46
  examples/rc_background.ipynb
47
47
  imgs/ORC_logo_cropped.png
48
48
  imgs/architecture_paper.pdf
49
+ imgs/benchmark_results_combined.png
49
50
  imgs/readme_example_forecast.png
50
51
  src/OpenReservoirComputing.egg-info/PKG-INFO
51
52
  src/OpenReservoirComputing.egg-info/SOURCES.txt
52
53
  src/OpenReservoirComputing.egg-info/dependency_links.txt
53
54
  src/OpenReservoirComputing.egg-info/requires.txt
55
+ src/OpenReservoirComputing.egg-info/scm_file_list.json
56
+ src/OpenReservoirComputing.egg-info/scm_version.json
54
57
  src/OpenReservoirComputing.egg-info/top_level.txt
55
58
  src/orc/__init__.py
56
59
  src/orc/drivers.py
@@ -0,0 +1,86 @@
1
+ {
2
+ "files": [
3
+ "mkdocs.yml",
4
+ "pyproject.toml",
5
+ "LICENSE",
6
+ "README.md",
7
+ ".gitignore",
8
+ ".github/CODEOWNERS",
9
+ ".github/workflows/draft-pdf.yml",
10
+ ".github/workflows/tests.yml",
11
+ ".github/workflows/publish.yml",
12
+ ".github/workflows/docs.yml",
13
+ "JOSS/paper.bib",
14
+ "JOSS/CODE_OF_CONDUCT.md",
15
+ "JOSS/joss-paper.pdf",
16
+ "JOSS/CITATION.cff",
17
+ "JOSS/paper.md",
18
+ "examples/jit_compatibility.ipynb",
19
+ "examples/benchmark_combined.py",
20
+ "examples/continuous_rc.ipynb",
21
+ "examples/classification.ipynb",
22
+ "examples/control.ipynb",
23
+ "examples/data_library.ipynb",
24
+ "examples/lorenz.ipynb",
25
+ "examples/rc_background.ipynb",
26
+ "examples/ks.ipynb",
27
+ "tests/test_drivers.py",
28
+ "tests/test_embeddings.py",
29
+ "tests/test_readouts.py",
30
+ "tests/utils/test_numerics.py",
31
+ "tests/utils/test_visualization.py",
32
+ "tests/utils/test_regressions.py",
33
+ "tests/control/test_control_models.py",
34
+ "tests/classifier/test_classifier_models.py",
35
+ "tests/data/test_integrators.py",
36
+ "tests/forecaster/test_forecast_models.py",
37
+ "imgs/readme_example_forecast.png",
38
+ "imgs/benchmark_results_combined.png",
39
+ "imgs/architecture_paper.pdf",
40
+ "imgs/ORC_logo_cropped.png",
41
+ "src/orc/readouts.py",
42
+ "src/orc/embeddings.py",
43
+ "src/orc/__init__.py",
44
+ "src/orc/drivers.py",
45
+ "src/orc/utils/regressions.py",
46
+ "src/orc/utils/__init__.py",
47
+ "src/orc/utils/numerics.py",
48
+ "src/orc/utils/visualization.py",
49
+ "src/orc/control/train.py",
50
+ "src/orc/control/__init__.py",
51
+ "src/orc/control/models.py",
52
+ "src/orc/control/base.py",
53
+ "src/orc/classifier/train.py",
54
+ "src/orc/classifier/__init__.py",
55
+ "src/orc/classifier/models.py",
56
+ "src/orc/classifier/base.py",
57
+ "src/orc/data/integrators.py",
58
+ "src/orc/data/__init__.py",
59
+ "src/orc/forecaster/train.py",
60
+ "src/orc/forecaster/__init__.py",
61
+ "src/orc/forecaster/models.py",
62
+ "src/orc/forecaster/base.py",
63
+ "docs/contributing.md",
64
+ "docs/index.md",
65
+ "docs/examples",
66
+ "docs/getting-started/installation.md",
67
+ "docs/getting-started/quickstart.md",
68
+ "docs/user-guide/overview.md",
69
+ "docs/user-guide/readouts.md",
70
+ "docs/user-guide/data.md",
71
+ "docs/user-guide/embeddings.md",
72
+ "docs/user-guide/models.md",
73
+ "docs/user-guide/drivers.md",
74
+ "docs/stylesheets/extra.css",
75
+ "docs/imgs/ORC_logo_cropped.png",
76
+ "docs/api/forecaster.md",
77
+ "docs/api/classifier.md",
78
+ "docs/api/readouts.md",
79
+ "docs/api/control.md",
80
+ "docs/api/data.md",
81
+ "docs/api/embeddings.md",
82
+ "docs/api/utils.md",
83
+ "docs/api/drivers.md",
84
+ "docs/javascripts/mathjax.js"
85
+ ]
86
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.3.3",
3
+ "distance": 0,
4
+ "node": "gcfeb341f30a1ad48016b4daee4b959efb2d13a5a",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-07-23"
8
+ }
@@ -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)
@@ -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