OpenReservoirComputing 0.1.0__py3-none-any.whl
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.1.0.dist-info/METADATA +168 -0
- openreservoircomputing-0.1.0.dist-info/RECORD +28 -0
- openreservoircomputing-0.1.0.dist-info/WHEEL +5 -0
- openreservoircomputing-0.1.0.dist-info/licenses/LICENSE +201 -0
- openreservoircomputing-0.1.0.dist-info/top_level.txt +1 -0
- orc/__init__.py +29 -0
- orc/classifier/__init__.py +6 -0
- orc/classifier/base.py +6 -0
- orc/classifier/models.py +6 -0
- orc/classifier/train.py +6 -0
- orc/control/__init__.py +15 -0
- orc/control/base.py +362 -0
- orc/control/models.py +139 -0
- orc/control/train.py +90 -0
- orc/data/__init__.py +27 -0
- orc/data/integrators.py +753 -0
- orc/drivers.py +851 -0
- orc/embeddings.py +459 -0
- orc/forecaster/__init__.py +20 -0
- orc/forecaster/base.py +487 -0
- orc/forecaster/models.py +416 -0
- orc/forecaster/train.py +301 -0
- orc/readouts.py +742 -0
- orc/tuning/__init__.py +6 -0
- orc/utils/__init__.py +6 -0
- orc/utils/numerics.py +115 -0
- orc/utils/regressions.py +73 -0
- orc/utils/visualization.py +193 -0
orc/forecaster/models.py
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"""Discrete and continuous ESN implementations with standard driver."""
|
|
2
|
+
|
|
3
|
+
import diffrax
|
|
4
|
+
import jax
|
|
5
|
+
import jax.numpy as jnp
|
|
6
|
+
|
|
7
|
+
from orc.drivers import ParallelESNDriver
|
|
8
|
+
from orc.embeddings import EnsembleLinearEmbedding, ParallelLinearEmbedding
|
|
9
|
+
from orc.forecaster.base import CRCForecasterBase, RCForecasterBase
|
|
10
|
+
from orc.readouts import (
|
|
11
|
+
EnsembleLinearReadout,
|
|
12
|
+
ParallelLinearReadout,
|
|
13
|
+
ParallelQuadraticReadout,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
jax.config.update("jax_enable_x64", True)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ESNForecaster(RCForecasterBase):
|
|
20
|
+
"""
|
|
21
|
+
Basic implementation of ESN for forecasting.
|
|
22
|
+
|
|
23
|
+
Attributes
|
|
24
|
+
----------
|
|
25
|
+
res_dim : int
|
|
26
|
+
Reservoir dimension.
|
|
27
|
+
data_dim : int
|
|
28
|
+
Input/output dimension.
|
|
29
|
+
driver : ParallelESNDriver
|
|
30
|
+
Driver implmenting the Echo State Network dynamics.
|
|
31
|
+
readout : BaseReadout
|
|
32
|
+
Trainable linear readout layer.
|
|
33
|
+
embedding : ParallelLinearEmbedding
|
|
34
|
+
Untrainable linear embedding layer.
|
|
35
|
+
|
|
36
|
+
Methods
|
|
37
|
+
-------
|
|
38
|
+
force(in_seq, res_state)
|
|
39
|
+
Teacher forces the reservoir with sequence in_seq and init. cond. res_state.
|
|
40
|
+
forecast(fcast_len, res_state)
|
|
41
|
+
Perform a forecast of fcast_len steps from res_state.
|
|
42
|
+
forecast_from_IC(fcast_len, spinup_data)
|
|
43
|
+
Forecast from a sequence of spinup data.
|
|
44
|
+
set_readout(readout)
|
|
45
|
+
Replace readout layer.
|
|
46
|
+
set_embedding(embedding)
|
|
47
|
+
Replace embedding layer.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
res_dim: int
|
|
51
|
+
data_dim: int
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
data_dim: int,
|
|
56
|
+
res_dim: int,
|
|
57
|
+
leak_rate: float = 0.6,
|
|
58
|
+
bias: float = 1.6,
|
|
59
|
+
embedding_scaling: float = 0.08,
|
|
60
|
+
Wr_density: float = 0.02,
|
|
61
|
+
Wr_spectral_radius: float = 0.8,
|
|
62
|
+
dtype: type = jnp.float64,
|
|
63
|
+
seed: int = 0,
|
|
64
|
+
chunks: int = 1,
|
|
65
|
+
locality: int = 0,
|
|
66
|
+
quadratic: bool = False,
|
|
67
|
+
periodic: bool = True,
|
|
68
|
+
use_sparse_eigs: bool = True,
|
|
69
|
+
) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Initialize the ESN model.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
data_dim : int
|
|
76
|
+
Dimension of the input data.
|
|
77
|
+
res_dim : int
|
|
78
|
+
Dimension of the reservoir adjacency matrix Wr.
|
|
79
|
+
leak_rate : float
|
|
80
|
+
Integration leak rate of the reservoir dynamics.
|
|
81
|
+
bias : float
|
|
82
|
+
Bias term for the reservoir dynamics.
|
|
83
|
+
embedding_scaling : float
|
|
84
|
+
Scaling factor for the embedding layer.
|
|
85
|
+
Wr_density : float
|
|
86
|
+
Density of the reservoir adjacency matrix Wr.
|
|
87
|
+
Wr_spectral_radius : float
|
|
88
|
+
Largest eigenvalue of the reservoir adjacency matrix Wr.
|
|
89
|
+
dtype : type
|
|
90
|
+
Data type of the model (jnp.float64 is highly recommended).
|
|
91
|
+
seed : int
|
|
92
|
+
Random seed for generating the PRNG key for the reservoir computer.
|
|
93
|
+
chunks : int
|
|
94
|
+
Number of parallel reservoirs, must evenly divide data_dim.
|
|
95
|
+
locality : int
|
|
96
|
+
Overlap in adjacent parallel reservoirs.
|
|
97
|
+
quadratic : bool
|
|
98
|
+
Use quadratic nonlinearity in output, default False.
|
|
99
|
+
periodic : bool
|
|
100
|
+
Periodic BCs for embedding layer.
|
|
101
|
+
use_sparse_eigs : bool
|
|
102
|
+
Whether to use sparse eigensolver for setting the spectral radius of wr.
|
|
103
|
+
Default is True, which is recommended to save memory and compute time. If
|
|
104
|
+
False, will use dense eigensolver which may be more accurate.
|
|
105
|
+
"""
|
|
106
|
+
# Initialize the random key and reservoir dimension
|
|
107
|
+
self.res_dim = res_dim
|
|
108
|
+
self.seed = seed
|
|
109
|
+
self.data_dim = data_dim
|
|
110
|
+
key = jax.random.PRNGKey(seed)
|
|
111
|
+
key_driver, key_readout, key_embedding = jax.random.split(key, 3)
|
|
112
|
+
|
|
113
|
+
# init in embedding, driver and readout
|
|
114
|
+
embedding = ParallelLinearEmbedding(
|
|
115
|
+
in_dim=data_dim,
|
|
116
|
+
res_dim=res_dim,
|
|
117
|
+
seed=key_embedding[0],
|
|
118
|
+
scaling=embedding_scaling,
|
|
119
|
+
chunks=chunks,
|
|
120
|
+
locality=locality,
|
|
121
|
+
periodic=periodic,
|
|
122
|
+
)
|
|
123
|
+
driver = ParallelESNDriver(
|
|
124
|
+
res_dim=res_dim,
|
|
125
|
+
seed=key_driver[0],
|
|
126
|
+
leak=leak_rate,
|
|
127
|
+
bias=bias,
|
|
128
|
+
density=Wr_density,
|
|
129
|
+
spectral_radius=Wr_spectral_radius,
|
|
130
|
+
chunks=chunks,
|
|
131
|
+
dtype=dtype,
|
|
132
|
+
use_sparse_eigs=use_sparse_eigs,
|
|
133
|
+
)
|
|
134
|
+
if quadratic:
|
|
135
|
+
readout = ParallelQuadraticReadout(
|
|
136
|
+
out_dim=data_dim, res_dim=res_dim, seed=key_readout[0], chunks=chunks
|
|
137
|
+
)
|
|
138
|
+
else:
|
|
139
|
+
readout = ParallelLinearReadout(
|
|
140
|
+
out_dim=data_dim, res_dim=res_dim, seed=key_readout[0], chunks=chunks
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
super().__init__(
|
|
144
|
+
driver=driver,
|
|
145
|
+
readout=readout,
|
|
146
|
+
embedding=embedding,
|
|
147
|
+
dtype=dtype,
|
|
148
|
+
seed=seed,
|
|
149
|
+
)
|
|
150
|
+
self.chunks = chunks
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class CESNForecaster(CRCForecasterBase):
|
|
154
|
+
"""
|
|
155
|
+
Basic implementation of a Continuous ESN for forecasting.
|
|
156
|
+
|
|
157
|
+
Attributes
|
|
158
|
+
----------
|
|
159
|
+
res_dim : int
|
|
160
|
+
Reservoir dimension.
|
|
161
|
+
data_dim : int
|
|
162
|
+
Input/output dimension.
|
|
163
|
+
driver : ParallelESNDriver
|
|
164
|
+
Driver implementing the Echo State Network dynamics
|
|
165
|
+
in continuous time.
|
|
166
|
+
readout : BaseReadout
|
|
167
|
+
Trainable linear readout layer.
|
|
168
|
+
embedding : ParallelLinearEmbedding
|
|
169
|
+
Untrainable linear embedding layer.
|
|
170
|
+
|
|
171
|
+
Methods
|
|
172
|
+
-------
|
|
173
|
+
force(in_seq, res_state)
|
|
174
|
+
Teacher forces the reservoir with sequence in_seq and init. cond. res_state.
|
|
175
|
+
forecast(fcast_len, res_state)
|
|
176
|
+
Perform a forecast of fcast_len steps from res_state.
|
|
177
|
+
forecast_from_IC(fcast_len, spinup_data)
|
|
178
|
+
Forecast from a sequence of spinup data.
|
|
179
|
+
set_readout(readout)
|
|
180
|
+
Replace readout layer.
|
|
181
|
+
set_embedding(embedding)
|
|
182
|
+
Replace embedding layer.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
res_dim: int
|
|
186
|
+
data_dim: int
|
|
187
|
+
|
|
188
|
+
def __init__(
|
|
189
|
+
self,
|
|
190
|
+
data_dim: int,
|
|
191
|
+
res_dim: int,
|
|
192
|
+
time_const: float = 50.0,
|
|
193
|
+
bias: float = 1.6,
|
|
194
|
+
embedding_scaling: float = 0.08,
|
|
195
|
+
Wr_density: float = 0.02,
|
|
196
|
+
Wr_spectral_radius: float = 0.8,
|
|
197
|
+
dtype: type = jnp.float64,
|
|
198
|
+
seed: int = 0,
|
|
199
|
+
chunks: int = 1,
|
|
200
|
+
locality: int = 0,
|
|
201
|
+
quadratic: bool = False,
|
|
202
|
+
periodic: bool = True,
|
|
203
|
+
use_sparse_eigs: bool = True,
|
|
204
|
+
solver: diffrax.AbstractSolver = None,
|
|
205
|
+
stepsize_controller: diffrax.AbstractAdaptiveStepSizeController = None,
|
|
206
|
+
) -> None:
|
|
207
|
+
"""
|
|
208
|
+
Initialize the CESN model.
|
|
209
|
+
|
|
210
|
+
Parameters
|
|
211
|
+
----------
|
|
212
|
+
data_dim : int
|
|
213
|
+
Dimension of the input data.
|
|
214
|
+
res_dim : int
|
|
215
|
+
Dimension of the reservoir adjacency matrix Wr.
|
|
216
|
+
time_const : float
|
|
217
|
+
Time constant of the reservoir dynamics.
|
|
218
|
+
bias : float
|
|
219
|
+
Bias term for the reservoir dynamics.
|
|
220
|
+
embedding_scaling : float
|
|
221
|
+
Scaling factor for the embedding layer.
|
|
222
|
+
Wr_density : float
|
|
223
|
+
Density of the reservoir adjacency matrix Wr.
|
|
224
|
+
Wr_spectral_radius : float
|
|
225
|
+
Largest eigenvalue of the reservoir adjacency matrix Wr.
|
|
226
|
+
dtype : type
|
|
227
|
+
Data type of the model (jnp.float64 is highly recommended).
|
|
228
|
+
seed : int
|
|
229
|
+
Random seed for generating the PRNG key for the reservoir computer.
|
|
230
|
+
chunks : int
|
|
231
|
+
Number of parallel reservoirs, must evenly divide data_dim.
|
|
232
|
+
locality : int
|
|
233
|
+
Overlap in adjacent parallel reservoirs.
|
|
234
|
+
quadratic : bool
|
|
235
|
+
Use quadratic nonlinearity in output, default False.
|
|
236
|
+
periodic : bool
|
|
237
|
+
Periodic BCs for embedding layer.
|
|
238
|
+
use_sparse_eigs : bool
|
|
239
|
+
Whether to use sparse eigensolver for setting the spectral radius of wr.
|
|
240
|
+
Default is True, which is recommended to save memory and compute time. If
|
|
241
|
+
False, will use dense eigensolver which may be more accurate.
|
|
242
|
+
"""
|
|
243
|
+
# Initialize the random key and reservoir dimension
|
|
244
|
+
self.res_dim = res_dim
|
|
245
|
+
self.seed = seed
|
|
246
|
+
self.data_dim = data_dim
|
|
247
|
+
key = jax.random.PRNGKey(seed)
|
|
248
|
+
key_driver, key_readout, key_embedding = jax.random.split(key, 3)
|
|
249
|
+
|
|
250
|
+
# init in embedding, driver and readout
|
|
251
|
+
embedding = ParallelLinearEmbedding(
|
|
252
|
+
in_dim=data_dim,
|
|
253
|
+
res_dim=res_dim,
|
|
254
|
+
seed=key_embedding[0],
|
|
255
|
+
scaling=embedding_scaling,
|
|
256
|
+
chunks=chunks,
|
|
257
|
+
locality=locality,
|
|
258
|
+
periodic=periodic,
|
|
259
|
+
)
|
|
260
|
+
driver = ParallelESNDriver(
|
|
261
|
+
res_dim=res_dim,
|
|
262
|
+
seed=key_driver[0],
|
|
263
|
+
time_const=time_const,
|
|
264
|
+
bias=bias,
|
|
265
|
+
density=Wr_density,
|
|
266
|
+
spectral_radius=Wr_spectral_radius,
|
|
267
|
+
chunks=chunks,
|
|
268
|
+
mode="continuous",
|
|
269
|
+
dtype=dtype,
|
|
270
|
+
use_sparse_eigs=use_sparse_eigs,
|
|
271
|
+
)
|
|
272
|
+
if quadratic:
|
|
273
|
+
readout = ParallelQuadraticReadout(
|
|
274
|
+
out_dim=data_dim, res_dim=res_dim, seed=key_readout[0], chunks=chunks
|
|
275
|
+
)
|
|
276
|
+
else:
|
|
277
|
+
readout = ParallelLinearReadout(
|
|
278
|
+
out_dim=data_dim, res_dim=res_dim, seed=key_readout[0], chunks=chunks
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
if solver is None:
|
|
282
|
+
solver = diffrax.Tsit5()
|
|
283
|
+
if stepsize_controller is None:
|
|
284
|
+
stepsize_controller = diffrax.PIDController(
|
|
285
|
+
rtol=1e-3, atol=1e-6, icoeff=1.0
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
super().__init__(
|
|
289
|
+
driver=driver,
|
|
290
|
+
readout=readout,
|
|
291
|
+
embedding=embedding,
|
|
292
|
+
dtype=dtype,
|
|
293
|
+
seed=seed,
|
|
294
|
+
solver=solver,
|
|
295
|
+
stepsize_controller=stepsize_controller,
|
|
296
|
+
)
|
|
297
|
+
self.chunks = chunks
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class EnsembleESNForecaster(RCForecasterBase):
|
|
301
|
+
"""
|
|
302
|
+
Ensembled ESNs for forecasting.
|
|
303
|
+
|
|
304
|
+
Attributes
|
|
305
|
+
----------
|
|
306
|
+
res_dim : int
|
|
307
|
+
Reservoir dimension.
|
|
308
|
+
data_dim : int
|
|
309
|
+
Input/output dimension.
|
|
310
|
+
driver : ParallelESNDriver
|
|
311
|
+
Driver implmenting the Echo State Network dynamics.
|
|
312
|
+
readout : EnsembleLinearReadout
|
|
313
|
+
Trainable linear readout layer.
|
|
314
|
+
embedding : EnsembleLinearEmbedding
|
|
315
|
+
Untrainable linear embedding layer.
|
|
316
|
+
|
|
317
|
+
Methods
|
|
318
|
+
-------
|
|
319
|
+
force(in_seq, res_state)
|
|
320
|
+
Teacher forces the reservoir with sequence in_seq and init. cond. res_state.
|
|
321
|
+
forecast(fcast_len, res_state)
|
|
322
|
+
Perform a forecast of fcast_len steps from res_state.
|
|
323
|
+
forecast_from_IC(fcast_len, spinup_data)
|
|
324
|
+
Forecast from a sequence of spinup data.
|
|
325
|
+
set_readout(readout)
|
|
326
|
+
Replace readout layer.
|
|
327
|
+
set_embedding(embedding)
|
|
328
|
+
Replace embedding layer.
|
|
329
|
+
"""
|
|
330
|
+
|
|
331
|
+
res_dim: int
|
|
332
|
+
data_dim: int
|
|
333
|
+
|
|
334
|
+
def __init__(
|
|
335
|
+
self,
|
|
336
|
+
data_dim: int,
|
|
337
|
+
res_dim: int,
|
|
338
|
+
leak_rate: float = 0.6,
|
|
339
|
+
bias: float = 1.6,
|
|
340
|
+
embedding_scaling: float = 0.08,
|
|
341
|
+
Wr_density: float = 0.02,
|
|
342
|
+
Wr_spectral_radius: float = 0.8,
|
|
343
|
+
dtype: type = jnp.float64,
|
|
344
|
+
seed: int = 0,
|
|
345
|
+
chunks: int = 1,
|
|
346
|
+
use_sparse_eigs: bool = True,
|
|
347
|
+
) -> None:
|
|
348
|
+
"""
|
|
349
|
+
Initialize the ESN model.
|
|
350
|
+
|
|
351
|
+
Parameters
|
|
352
|
+
----------
|
|
353
|
+
data_dim : int
|
|
354
|
+
Dimension of the input data.
|
|
355
|
+
res_dim : int
|
|
356
|
+
Dimension of the reservoir adjacency matrix Wr.
|
|
357
|
+
leak_rate : float
|
|
358
|
+
Integration leak rate of the reservoir dynamics.
|
|
359
|
+
bias : float
|
|
360
|
+
Bias term for the reservoir dynamics.
|
|
361
|
+
embedding_scaling : float
|
|
362
|
+
Scaling factor for the embedding layer.
|
|
363
|
+
Wr_density : float
|
|
364
|
+
Density of the reservoir adjacency matrix Wr.
|
|
365
|
+
Wr_spectral_radius : float
|
|
366
|
+
Largest eigenvalue of the reservoir adjacency matrix Wr.
|
|
367
|
+
dtype : type
|
|
368
|
+
Data type of the model (jnp.float64 is highly recommended).
|
|
369
|
+
seed : int
|
|
370
|
+
Random seed for generating the PRNG key for the reservoir computer.
|
|
371
|
+
chunks : int
|
|
372
|
+
Number of parallel reservoirs, must evenly divide data_dim.
|
|
373
|
+
use_sparse_eigs : bool
|
|
374
|
+
Whether to use sparse eigensolver for setting the spectral radius of wr.
|
|
375
|
+
Default is True, which is recommended to save memory and compute time. If
|
|
376
|
+
False, will use dense eigensolver which may be more accurate.
|
|
377
|
+
"""
|
|
378
|
+
# Initialize the random key and reservoir dimension
|
|
379
|
+
self.res_dim = res_dim
|
|
380
|
+
self.seed = seed
|
|
381
|
+
self.data_dim = data_dim
|
|
382
|
+
key = jax.random.PRNGKey(seed)
|
|
383
|
+
key_driver, key_readout, key_embedding = jax.random.split(key, 3)
|
|
384
|
+
|
|
385
|
+
# init in embedding, driver and readout
|
|
386
|
+
embedding = EnsembleLinearEmbedding(
|
|
387
|
+
in_dim=data_dim,
|
|
388
|
+
res_dim=res_dim,
|
|
389
|
+
seed=key_embedding[0],
|
|
390
|
+
scaling=embedding_scaling,
|
|
391
|
+
chunks=chunks,
|
|
392
|
+
)
|
|
393
|
+
driver = ParallelESNDriver(
|
|
394
|
+
res_dim=res_dim,
|
|
395
|
+
seed=key_driver[0],
|
|
396
|
+
leak=leak_rate,
|
|
397
|
+
bias=bias,
|
|
398
|
+
density=Wr_density,
|
|
399
|
+
spectral_radius=Wr_spectral_radius,
|
|
400
|
+
chunks=chunks,
|
|
401
|
+
dtype=dtype,
|
|
402
|
+
use_sparse_eigs=use_sparse_eigs,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
readout = EnsembleLinearReadout(
|
|
406
|
+
out_dim=data_dim, res_dim=res_dim, seed=key_readout[0], chunks=chunks
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
super().__init__(
|
|
410
|
+
driver=driver,
|
|
411
|
+
readout=readout,
|
|
412
|
+
embedding=embedding,
|
|
413
|
+
dtype=dtype,
|
|
414
|
+
seed=seed,
|
|
415
|
+
)
|
|
416
|
+
self.chunks = chunks
|
orc/forecaster/train.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""Training functions for reservoir computer forecasters."""
|
|
2
|
+
|
|
3
|
+
import equinox as eqx
|
|
4
|
+
import jax.numpy as jnp
|
|
5
|
+
from jaxtyping import Array
|
|
6
|
+
|
|
7
|
+
from orc.forecaster.models import CESNForecaster, EnsembleESNForecaster, ESNForecaster
|
|
8
|
+
from orc.readouts import ParallelNonlinearReadout
|
|
9
|
+
from orc.utils.regressions import (
|
|
10
|
+
_solve_all_ridge_reg,
|
|
11
|
+
_solve_all_ridge_reg_batched,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def train_ESNForecaster(
|
|
16
|
+
model: ESNForecaster,
|
|
17
|
+
train_seq: Array,
|
|
18
|
+
target_seq: Array = None,
|
|
19
|
+
spinup: int = 0,
|
|
20
|
+
initial_res_state: Array = None,
|
|
21
|
+
beta: float = 8e-8,
|
|
22
|
+
batch_size: int = None,
|
|
23
|
+
) -> tuple[ESNForecaster, Array]:
|
|
24
|
+
"""Training function for ESNForecaster.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
model : ESNForecaster
|
|
29
|
+
ESNForecaster model to train.
|
|
30
|
+
train_seq : Array
|
|
31
|
+
Training input sequence for reservoir, (shape=(seq_len, data_dim)).
|
|
32
|
+
target_seq : Array
|
|
33
|
+
Target sequence for training reservoir, (shape=(seq_len, data_dim)).
|
|
34
|
+
initial_res_state : Array
|
|
35
|
+
Initial reservoir state, (shape=(chunks, res_dim,)).
|
|
36
|
+
spinup : int
|
|
37
|
+
Initial transient of reservoir states to discard.
|
|
38
|
+
beta : float
|
|
39
|
+
Tikhonov regularization parameter.
|
|
40
|
+
batch_size : int, optional
|
|
41
|
+
Number of parallel reservoirs to process in each batch for ridge regression.
|
|
42
|
+
If None (default), processes all reservoirs at once. Use smaller values
|
|
43
|
+
to reduce memory usage for large numbers of parallel reservoirs.
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
model : ESNForecaster
|
|
48
|
+
Trained ESN model.
|
|
49
|
+
res_seq : Array
|
|
50
|
+
Training sequence of reservoir states.
|
|
51
|
+
"""
|
|
52
|
+
# Check that model is an ESN
|
|
53
|
+
if not isinstance(model, ESNForecaster):
|
|
54
|
+
raise TypeError("Model must be an ESNForecaster.")
|
|
55
|
+
|
|
56
|
+
# check that spinup is less than the length of the training sequence
|
|
57
|
+
if spinup >= train_seq.shape[0]:
|
|
58
|
+
raise ValueError(
|
|
59
|
+
"spinup must be less than the length of the training sequence."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if initial_res_state is None:
|
|
63
|
+
initial_res_state = jnp.zeros(
|
|
64
|
+
(
|
|
65
|
+
model.embedding.chunks,
|
|
66
|
+
model.res_dim,
|
|
67
|
+
),
|
|
68
|
+
dtype=model.dtype,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if target_seq is None:
|
|
72
|
+
tot_seq = train_seq
|
|
73
|
+
target_seq = train_seq[1:, :]
|
|
74
|
+
train_seq = train_seq[:-1, :]
|
|
75
|
+
else:
|
|
76
|
+
tot_seq = jnp.vstack((train_seq, target_seq[-1:]))
|
|
77
|
+
|
|
78
|
+
tot_res_seq = model.force(tot_seq, initial_res_state)
|
|
79
|
+
res_seq = tot_res_seq[:-1]
|
|
80
|
+
if isinstance(model.readout, ParallelNonlinearReadout):
|
|
81
|
+
res_seq_train = model.readout.nonlinear_transform(res_seq)
|
|
82
|
+
else:
|
|
83
|
+
res_seq_train = res_seq
|
|
84
|
+
|
|
85
|
+
if batch_size is None:
|
|
86
|
+
cmat = _solve_all_ridge_reg(
|
|
87
|
+
res_seq_train[spinup:],
|
|
88
|
+
target_seq[spinup:].reshape(
|
|
89
|
+
res_seq[spinup:].shape[0], res_seq.shape[1], -1
|
|
90
|
+
),
|
|
91
|
+
beta,
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
cmat = _solve_all_ridge_reg_batched(
|
|
95
|
+
res_seq_train[spinup:],
|
|
96
|
+
target_seq[spinup:].reshape(
|
|
97
|
+
res_seq[spinup:].shape[0], res_seq.shape[1], -1
|
|
98
|
+
),
|
|
99
|
+
beta,
|
|
100
|
+
batch_size,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def where(m):
|
|
104
|
+
return m.readout.wout
|
|
105
|
+
|
|
106
|
+
model = eqx.tree_at(where, model, cmat)
|
|
107
|
+
|
|
108
|
+
return model, tot_res_seq
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def train_CESNForecaster(
|
|
112
|
+
model: CESNForecaster,
|
|
113
|
+
train_seq: Array,
|
|
114
|
+
t_train: Array,
|
|
115
|
+
target_seq: Array = None,
|
|
116
|
+
spinup: int = 0,
|
|
117
|
+
initial_res_state: Array = None,
|
|
118
|
+
beta: float = 8e-8,
|
|
119
|
+
batch_size: int = None,
|
|
120
|
+
) -> tuple[CESNForecaster, Array]:
|
|
121
|
+
"""Training function for CESNForecaster.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
model : CESNForecaster
|
|
126
|
+
CESNForecaster model to train.
|
|
127
|
+
train_seq : Array
|
|
128
|
+
Training input sequence for reservoir, (shape=(seq_len, data_dim)).
|
|
129
|
+
t_train : Array
|
|
130
|
+
time vector corresponding to the training sequence, (shape=(seq_len,)).
|
|
131
|
+
target_seq : Array
|
|
132
|
+
Target sequence for training reservoir, (shape=(seq_len, data_dim)).
|
|
133
|
+
initial_res_state : Array
|
|
134
|
+
Initial reservoir state, (shape=(chunks, res_dim,)).
|
|
135
|
+
spinup : int
|
|
136
|
+
Initial transient of reservoir states to discard.
|
|
137
|
+
beta : float
|
|
138
|
+
Tikhonov regularization parameter.
|
|
139
|
+
batch_size : int, optional
|
|
140
|
+
Number of parallel reservoirs to process in each batch for ridge regression.
|
|
141
|
+
If None (default), processes all reservoirs at once. Use smaller values
|
|
142
|
+
to reduce memory usage for large numbers of parallel reservoirs.
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
model : CESNForecaster
|
|
147
|
+
Trained CESN model.
|
|
148
|
+
res_seq : Array
|
|
149
|
+
Training sequence of reservoir states.
|
|
150
|
+
"""
|
|
151
|
+
# check that model is continuous
|
|
152
|
+
if not isinstance(model, CESNForecaster):
|
|
153
|
+
raise TypeError("Model must be a CESNForecaster.")
|
|
154
|
+
|
|
155
|
+
# check that train_seq and t_train have the same length
|
|
156
|
+
if train_seq.shape[0] != t_train.shape[0]:
|
|
157
|
+
raise ValueError("train_seq and t_train must have the same length.")
|
|
158
|
+
|
|
159
|
+
# check that spinup is less than the length of the training sequence
|
|
160
|
+
if spinup >= train_seq.shape[0]:
|
|
161
|
+
raise ValueError(
|
|
162
|
+
"spinup must be less than the length of the training sequence."
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
if initial_res_state is None:
|
|
166
|
+
initial_res_state = jnp.zeros(
|
|
167
|
+
(
|
|
168
|
+
model.embedding.chunks,
|
|
169
|
+
model.res_dim,
|
|
170
|
+
),
|
|
171
|
+
dtype=model.dtype,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if target_seq is None:
|
|
175
|
+
tot_seq = train_seq
|
|
176
|
+
target_seq = train_seq[1:, :]
|
|
177
|
+
train_seq = train_seq[:-1, :]
|
|
178
|
+
else:
|
|
179
|
+
tot_seq = jnp.vstack((train_seq, target_seq[-1:]))
|
|
180
|
+
|
|
181
|
+
tot_res_seq = model.force(tot_seq, initial_res_state, ts=t_train)
|
|
182
|
+
res_seq = tot_res_seq[:-1]
|
|
183
|
+
if isinstance(model.readout, ParallelNonlinearReadout):
|
|
184
|
+
res_seq_train = eqx.filter_vmap(model.readout.nonlinear_transform)(res_seq)
|
|
185
|
+
else:
|
|
186
|
+
res_seq_train = res_seq
|
|
187
|
+
|
|
188
|
+
if batch_size is None:
|
|
189
|
+
cmat = _solve_all_ridge_reg(
|
|
190
|
+
res_seq_train[spinup:],
|
|
191
|
+
target_seq[spinup:].reshape(
|
|
192
|
+
res_seq[spinup:].shape[0], res_seq.shape[1], -1
|
|
193
|
+
),
|
|
194
|
+
beta,
|
|
195
|
+
)
|
|
196
|
+
else:
|
|
197
|
+
cmat = _solve_all_ridge_reg_batched(
|
|
198
|
+
res_seq_train[spinup:],
|
|
199
|
+
target_seq[spinup:].reshape(
|
|
200
|
+
res_seq[spinup:].shape[0], res_seq.shape[1], -1
|
|
201
|
+
),
|
|
202
|
+
beta,
|
|
203
|
+
batch_size,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def where(m):
|
|
207
|
+
return m.readout.wout
|
|
208
|
+
|
|
209
|
+
model = eqx.tree_at(where, model, cmat)
|
|
210
|
+
|
|
211
|
+
return model, tot_res_seq
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def train_EnsembleESNForecaster(
|
|
215
|
+
model: EnsembleESNForecaster,
|
|
216
|
+
train_seq: Array,
|
|
217
|
+
target_seq: Array | None = None,
|
|
218
|
+
spinup: int = 0,
|
|
219
|
+
initial_res_state: Array | None = None,
|
|
220
|
+
beta: float = 8e-8,
|
|
221
|
+
batch_size: int | None = None,
|
|
222
|
+
) -> tuple[ESNForecaster, Array]:
|
|
223
|
+
"""Training function for ESNForecaster.
|
|
224
|
+
|
|
225
|
+
Parameters
|
|
226
|
+
----------
|
|
227
|
+
model : ESNForecaster
|
|
228
|
+
ESNForecaster model to train.
|
|
229
|
+
train_seq : Array
|
|
230
|
+
Training input sequence for reservoir, (shape=(seq_len, data_dim)).
|
|
231
|
+
target_seq : Array
|
|
232
|
+
Target sequence for training reservoir, (shape=(seq_len, data_dim)).
|
|
233
|
+
initial_res_state : Array
|
|
234
|
+
Initial reservoir state, (shape=(chunks, res_dim,)).
|
|
235
|
+
spinup : int
|
|
236
|
+
Initial transient of reservoir states to discard.
|
|
237
|
+
beta : float
|
|
238
|
+
Tikhonov regularization parameter.
|
|
239
|
+
batch_size : int, optional
|
|
240
|
+
Number of parallel reservoirs to process in each batch for ridge regression.
|
|
241
|
+
If None (default), processes all reservoirs at once. Use smaller values
|
|
242
|
+
to reduce memory usage for large numbers of parallel reservoirs.
|
|
243
|
+
|
|
244
|
+
Returns
|
|
245
|
+
-------
|
|
246
|
+
model : ESNForecaster
|
|
247
|
+
Trained ESN model.
|
|
248
|
+
res_seq : Array
|
|
249
|
+
Training sequence of reservoir states.
|
|
250
|
+
"""
|
|
251
|
+
# Check that model is an ESN
|
|
252
|
+
if not isinstance(model, EnsembleESNForecaster):
|
|
253
|
+
raise TypeError("Model must be an EnsembleESNForecaster.")
|
|
254
|
+
|
|
255
|
+
# check that spinup is less than the length of the training sequence
|
|
256
|
+
if spinup >= train_seq.shape[0]:
|
|
257
|
+
raise ValueError(
|
|
258
|
+
"spinup must be less than the length of the training sequence."
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if initial_res_state is None:
|
|
262
|
+
initial_res_state = jnp.zeros(
|
|
263
|
+
(
|
|
264
|
+
model.embedding.chunks,
|
|
265
|
+
model.res_dim,
|
|
266
|
+
),
|
|
267
|
+
dtype=model.dtype,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
if target_seq is None:
|
|
271
|
+
tot_seq = train_seq
|
|
272
|
+
target_seq = train_seq[1:, :]
|
|
273
|
+
train_seq = train_seq[:-1, :]
|
|
274
|
+
else:
|
|
275
|
+
tot_seq = jnp.vstack((train_seq, target_seq[-1:]))
|
|
276
|
+
|
|
277
|
+
tot_res_seq = model.force(tot_seq, initial_res_state)
|
|
278
|
+
res_seq = tot_res_seq[:-1]
|
|
279
|
+
res_seq_train = res_seq
|
|
280
|
+
|
|
281
|
+
repeated_target_seq = jnp.repeat(target_seq[:, None, :], model.chunks, axis=1)
|
|
282
|
+
if batch_size is None:
|
|
283
|
+
cmat = _solve_all_ridge_reg(
|
|
284
|
+
res_seq_train[spinup:],
|
|
285
|
+
repeated_target_seq[spinup:],
|
|
286
|
+
beta,
|
|
287
|
+
)
|
|
288
|
+
else:
|
|
289
|
+
cmat = _solve_all_ridge_reg_batched(
|
|
290
|
+
res_seq_train[spinup:],
|
|
291
|
+
repeated_target_seq[spinup:],
|
|
292
|
+
beta,
|
|
293
|
+
batch_size,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
def where(m):
|
|
297
|
+
return m.readout.wout
|
|
298
|
+
|
|
299
|
+
model = eqx.tree_at(where, model, cmat)
|
|
300
|
+
|
|
301
|
+
return model, tot_res_seq
|