Reference
Layers¶
The nn.Module components used to build models: single-step reservoir
cells, the stateful sequence layers that wrap them, readout layers, and
transform layers. All are importable directly from resdag.layers.
cells
¶
ReservoirCell
¶
Abstract base for a single-timestep reservoir state update.
Owns all trainable (or frozen) parameters. Sequence iteration is
handled by the enclosing :class:BaseReservoirLayer, not by the cell
itself.
Notes
Subclasses must implement :attr:state_size, :attr:output_size,
:meth:init_state, and :meth:forward.
inputs[0] passed to :meth:forward is always the feedback slice.
Additional elements are driving inputs in the order they were passed to
the layer's forward.
For cells where the output and the state are the same tensor (e.g.
:class:ESNCell), output_size == state_size. For cells where they
differ (e.g. :class:NGCell whose output is a feature vector but whose
state is a delay buffer), the two properties return different values.
See Also
resdag.layers.esn.ESNCell : Concrete ESN cell implementation. resdag.layers.cells.ngrc_cell.NGCell : NG-RC cell implementation. resdag.layers.base.BaseReservoirLayer : Layer that drives the cell.
output_size
abstractmethod
property
¶
output_size: int
Dimensionality of the per-step output vector.
init_state
abstractmethod
¶
Return a zero initial state tensor.
| PARAMETER | DESCRIPTION |
|---|---|
batch_size
|
Number of samples in the batch.
TYPE:
|
device
|
Target device.
TYPE:
|
dtype
|
Target dtype.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Zero-filled initial state. |
Source code in src/resdag/layers/cells/base_cell.py
forward
abstractmethod
¶
Compute the per-step output and next state from current inputs and state.
| PARAMETER | DESCRIPTION |
|---|---|
inputs
|
Per-timestep input slices, one per input stream, each of shape
|
state
|
Current state tensor.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
output
|
Per-step output of shape
TYPE:
|
new_state
|
Updated state tensor.
TYPE:
|
Source code in src/resdag/layers/cells/base_cell.py
project_inputs
¶
Optional sequence-level fast path: precompute input contributions.
Cells whose pre-activation splits into an input-dependent part and a state-dependent part (e.g. the leaky ESN) can compute the input-dependent part for the whole sequence in one batched matmul, leaving only the recurrent term inside the time loop. This cuts the per-step kernel count roughly in half — the difference between the GPU being slower and faster than the CPU for typical reservoir sizes.
| PARAMETER | DESCRIPTION |
|---|---|
inputs
|
Full input sequences, one per stream, each of shape
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor or None
|
Precomputed projection of shape |
Source code in src/resdag/layers/cells/base_cell.py
step
¶
Single-step update consuming a slice of :meth:project_inputs.
Only called by the layer when :meth:project_inputs returned a
tensor. Subclasses implementing the fast path must override both
methods together.
| PARAMETER | DESCRIPTION |
|---|---|
projected_t
|
Per-timestep slice of the precomputed projection, shape
TYPE:
|
state
|
Current state tensor.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
output
|
Per-step output.
TYPE:
|
new_state
|
Updated state tensor.
TYPE:
|
Source code in src/resdag/layers/cells/base_cell.py
validate_state
¶
validate_state(state: Tensor) -> None
Validate that state matches the layout this cell expects.
The base implementation enforces the 2-D (batch, state_size)
contract used by classical RNN-style cells (e.g. :class:ESNCell).
Cells with a different state layout — for example
:class:~resdag.layers.cells.ngrc_cell.NGCell whose state is a 3-D
delay buffer — override this method to check their own shape.
| PARAMETER | DESCRIPTION |
|---|---|
state
|
Candidate state tensor to validate.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Source code in src/resdag/layers/cells/base_cell.py
ESNCell
¶
ESNCell(reservoir_size: int, feedback_size: int, input_size: int | None = None, spectral_radius: float | None = None, bias: bool = True, bias_scaling: float = 1.0, activation: str = 'tanh', leak_rate: float | Tensor | Sequence[float] = 1.0, noise: float = 0.0, trainable: bool = False, feedback_initializer: InitializerSpec = None, input_initializer: InitializerSpec = None, topology: TopologySpec = None, seed: SeedLike = None)
Bases: ReservoirCell
Single-timestep leaky Echo State Network update.
Owns all weight matrices and bias. Sequence iteration is delegated to
the enclosing :class:ESNLayer.
The state update follows the standard leaky-integrator ESN equation (Jaeger 2001; Lukoševičius 2012):
.. math::
h_t = (1 - \alpha)\,h_{t-1} + \alpha\,f(W_{fb}\,x_{fb,t}
+ W_{in}\,x_{in,t} + W_{rec}\,h_{t-1} + b)
where :math:f is the activation function, :math:\alpha is the leak
rate, :math:W_{fb} is the feedback weight matrix, :math:W_{in} is the
(optional) input weight matrix, :math:W_{rec} is the recurrent
weight matrix, and :math:b is a fixed random bias drawn from
:math:\mathcal{U}(-\beta, \beta) with :math:\beta = bias_scaling.
The bias breaks the odd symmetry of tanh dynamics: without it,
negated inputs produce exactly negated states, which constrains the
representations the readout can draw from.
| PARAMETER | DESCRIPTION |
|---|---|
reservoir_size
|
Number of reservoir units (hidden state dimension).
TYPE:
|
feedback_size
|
Dimension of feedback signal. Required for all ESN cells.
TYPE:
|
input_size
|
Dimension of driving inputs. If
TYPE:
|
spectral_radius
|
Target spectral radius for recurrent weights. If
TYPE:
|
bias
|
Whether to include a bias term.
TYPE:
|
bias_scaling
|
Scale of the random bias: entries are drawn from
TYPE:
|
activation
|
Activation function for reservoir dynamics.
TYPE:
|
leak_rate
|
Leaky integration rate in A per-neuron leak rate — a 1-D tensor or sequence of length
TYPE:
|
noise
|
Standard deviation of additive Gaussian state noise injected after the activation, following the classical ESN regularizer (Jaeger 2001; Lukoševičius 2012): .. math:: where :math:
TYPE:
|
trainable
|
If
TYPE:
|
feedback_initializer
|
Initializer for the feedback weight matrix. Accepts a registry
name,
TYPE:
|
input_initializer
|
Initializer for the input weight matrix. Same formats as
TYPE:
|
topology
|
Structure of the recurrent weight matrix: a registry name (graph or
matrix topology), any matrix-building callable, a
TYPE:
|
seed
|
Reproducibility seed that deterministically fixes every reservoir
parameter — the recurrent (topology) matrix, the feedback and input
weights (including the default |
| ATTRIBUTE | DESCRIPTION |
|---|---|
weight_feedback |
Feedback weight matrix of shape
TYPE:
|
weight_input |
Input weight matrix of shape
TYPE:
|
weight_hh |
Recurrent weight matrix of shape
TYPE:
|
bias_h |
Bias vector of shape
TYPE:
|
See Also
resdag.layers.esn.ESNLayer : Layer that sequences this cell. resdag.layers.base.ReservoirCell : Abstract cell interface.
Source code in src/resdag/layers/cells/esn_cell.py
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | |
leak_rate
property
writable
¶
float or torch.Tensor : Leaky-integration rate(s) read fresh every step.
A live knob: assigning to it (directly or through
ESNLayer.leak_rate = ...) validates the value and takes effect on the
next forward. Returns the scalar float for a homogeneous leak
(1.0 means no leaking, standard ESN) or the (reservoir_size,)
buffer for a per-neuron heterogeneous leak. Every entry stays in
(0, 1].
noise
property
writable
¶
noise: float
float : Std-dev of additive train-mode state noise (>= 0).
A live knob validated on assignment; 0.0 disables noise. Noise is
applied only when the cell is in training mode (see _apply_noise).
trainable
property
writable
¶
trainable: bool
bool : Whether the reservoir weights participate in backpropagation.
This is a live switch, not a construction-time-only flag. Assigning
to it flips requires_grad_ on every cell parameter
(weight_hh, weight_feedback, weight_input, bias_h) so a
post-construction cell.trainable = True genuinely unfreezes the
reservoir for a BPTT rollout — the documented
reservoir.trainable = True recipe now takes effect instead of being
a silent no-op. The getter reflects the current state.
output_size
property
¶
output_size: int
Dimensionality of the per-step output (equals state_size for ESN).
spectral_radius_achieved
property
¶
spectral_radius_achieved: float
float : Realized largest absolute eigenvalue of weight_hh.
Returns the spectral radius actually present in the recurrent matrix
after initialization/scaling, as opposed to the requested
:attr:spectral_radius target. Computed lazily via the shared
:func:resdag.init.topology.estimate_spectral_radius (power iteration /
sparse eigs / tiny-N dense fallback), so it stays cheap even for
large reservoirs and is GPU-resident for dense matrices.
For a freshly built cell with a non-None spectral_radius this
sits within the estimator's tolerance of the target; it differs once
the recurrent weights are trained or otherwise modified.
init_state
¶
Return a zero hidden state of shape (batch_size, reservoir_size).
Source code in src/resdag/layers/cells/esn_cell.py
forward
¶
Compute the next ESN state for a single timestep.
| PARAMETER | DESCRIPTION |
|---|---|
inputs
|
Per-timestep input slices. |
state
|
Current hidden state of shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
output
|
Next hidden state of shape
TYPE:
|
new_state
|
Same tensor as output (state and output are identical for ESN).
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the feedback feature dimension does not match
|
Notes
When self.noise > 0 and the cell is in training mode, additive
Gaussian noise is injected into the post-activation state (see the
noise constructor parameter). This matches the noise applied in
the :meth:step fast path, so the two paths stay consistent.
Source code in src/resdag/layers/cells/esn_cell.py
project_inputs
¶
Precompute all input-dependent pre-activation terms.
Computes W_fb x_fb + W_in x_in + b for every timestep at once.
Works on full (batch, timesteps, features) sequences (the layer's
fast path) and on single-step (batch, features) slices (reused by
:meth:forward).
| PARAMETER | DESCRIPTION |
|---|---|
inputs
|
Input streams; |
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Input projection with the same leading dimensions as the inputs
and trailing dimension |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If feature dimensions do not match the cell configuration, or a
driving input is supplied to a cell built without |
Source code in src/resdag/layers/cells/esn_cell.py
step
¶
Recurrent-only single-step update for the projected fast path.
addmm fuses the recurrent matmul with the precomputed input
projection into one kernel; with activation and leak that is three
kernel launches per timestep instead of six.
| PARAMETER | DESCRIPTION |
|---|---|
projected_t
|
Slice of :meth:
TYPE:
|
state
|
Current hidden state, shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
output
|
Next hidden state.
TYPE:
|
new_state
|
Same tensor as output.
TYPE:
|
Source code in src/resdag/layers/cells/esn_cell.py
NGCell
¶
NGCell(input_dim: int, k: int = 2, s: int = 1, p: int = 2, cumulative: bool = False, include_constant: bool = True, include_linear: bool = True)
Bases: ReservoirCell
Single-timestep NG-RC feature construction cell.
This is NOT a recurrent cell — it owns no weight matrices and has no
recurrent dynamics. The "state" is a FIFO delay buffer of the last
(k-1)*s input vectors. The cell wraps feedforward feature
construction (time-delayed inputs + polynomial monomials) behind a
forward(x, state) -> (features, new_state) interface so that it
composes with the same DAG infrastructure as :class:ESNCell.
| PARAMETER | DESCRIPTION |
|---|---|
input_dim
|
Dimensionality of a single input vector.
TYPE:
|
k
|
Number of delay taps (including the current input).
TYPE:
|
s
|
Spacing between delay taps, in timesteps.
TYPE:
|
p
|
Polynomial degree for nonlinear feature construction. See Exact-degree convention ( Cumulative-degree convention ( .. note::
When
TYPE:
|
cumulative
|
Degree convention for the nonlinear block (see
TYPE:
|
include_constant
|
Whether to prepend a constant
TYPE:
|
include_linear
|
Whether to include the linear delay-embedded features
TYPE:
|
| ATTRIBUTE | DESCRIPTION |
|---|---|
feature_dim |
Total dimension of the output feature vector In the default exact-degree mode (
TYPE:
|
state_size |
Number of rows in the delay buffer:
TYPE:
|
monomial_indices |
Long tensor of shape
TYPE:
|
delay_indices |
Long tensor of shape
TYPE:
|
Examples:
Basic usage (k=2, s=1, p=2):
>>> cell = NGCell(input_dim=3)
>>> x = torch.randn(4, 3) # (batch=4, d=3)
>>> state = cell.init_state(4, x.device, x.dtype) # (4, 1, 3)
>>> features, new_state = cell([x], state)
>>> features.shape
torch.Size([4, 28])
>>> new_state.shape
torch.Size([4, 1, 3])
No-delay mode (k=1):
>>> cell = NGCell(input_dim=3, k=1)
>>> state = cell.init_state(1, 'cpu', torch.float32)
>>> features, new_state = cell([torch.randn(1, 3)], state)
>>> features.shape
torch.Size([1, 10])
See Also
resdag.layers.reservoirs.ngrc.NGReservoirLayer : Sequence wrapper for this cell.
Source code in src/resdag/layers/cells/ngrc_cell.py
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | |
init_state
¶
Return a zero-filled initial delay buffer.
| PARAMETER | DESCRIPTION |
|---|---|
batch_size
|
Number of samples in the batch.
TYPE:
|
device
|
Target device for the buffer. |
dtype
|
Target dtype for the buffer.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Zero tensor of shape |
Source code in src/resdag/layers/cells/ngrc_cell.py
forward
¶
Compute the NG-RC feature vector for a single timestep.
| PARAMETER | DESCRIPTION |
|---|---|
inputs
|
Per-timestep input slices. |
state
|
Delay buffer of shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
features
|
Output feature vector
TYPE:
|
new_state
|
Updated delay buffer of shape
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Source code in src/resdag/layers/cells/ngrc_cell.py
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | |
forward_sequence
¶
Compute NG-RC features for a whole sequence in a single vectorized pass.
This is the batch-forward fast path used by
:meth:~resdag.layers.reservoirs.ngrc.NGReservoirLayer.forward. It is
numerically identical to scanning :meth:forward step-by-step
(0.0 max-abs-diff, including the warmup region), but contains no
per-timestep Python loop: the delay-embedded linear features are built
from shifted, front-zero-padded slices of the input, every monomial is
gathered over the full (batch, timesteps, *) tensor at once, and the
constant-ones block is allocated a single time.
| PARAMETER | DESCRIPTION |
|---|---|
x
|
Input sequence of shape
TYPE:
|
state
|
Incoming delay buffer of shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
features
|
Output features
TYPE:
|
new_state
|
Updated delay buffer of shape
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
See Also
NGCell.forward : Single-step counterpart driving the streaming path.
Source code in src/resdag/layers/cells/ngrc_cell.py
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | |
validate_state
¶
validate_state(state: Tensor) -> None
Validate the 3-D delay-buffer layout used by NG-RC.
| PARAMETER | DESCRIPTION |
|---|---|
state
|
Candidate state of shape
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Source code in src/resdag/layers/cells/ngrc_cell.py
reservoirs
¶
BaseReservoirLayer
¶
BaseReservoirLayer(cell: ReservoirCell)
Abstract base that owns the sequence loop and all state-management methods.
Subclasses create a :class:ReservoirCell and pass it to this
constructor. The sequence loop (iterating over timesteps and calling the
cell) lives here; the cell handles the per-step computation.
| PARAMETER | DESCRIPTION |
|---|---|
cell
|
Concrete cell instance that performs the single-step update.
TYPE:
|
| ATTRIBUTE | DESCRIPTION |
|---|---|
cell |
The wrapped single-step cell.
TYPE:
|
state |
Current reservoir state, or
TYPE:
|
detach_state_between_calls |
If
TYPE:
|
See Also
resdag.layers.esn.ESNLayer : Concrete ESN layer built on this base. resdag.layers.base.ReservoirCell : Abstract cell interface.
Source code in src/resdag/layers/reservoirs/base_reservoir.py
forward_stateless
¶
forward_stateless(inputs: list[Tensor], state: Tensor, *, compile: bool = False) -> tuple[Tensor, Tensor]
Run the time loop purely: thread state in and out, touch no self state.
This is the functional core of the reservoir. It takes the initial
state as an explicit argument, threads it through the per-timestep
loop, and returns the per-step outputs together with the final state.
It never reads or writes :attr:state, performs no in-place tensor
writes (per-step outputs are collected with :func:torch.stack), and
the loop body contains no data-dependent Python branch. Those three
properties are what make it torch.compile-scan, :func:torch.func.vmap,
and TorchScript/ONNX-export friendly, unlike the stateful
:meth:forward wrapper that drives it.
| PARAMETER | DESCRIPTION |
|---|---|
inputs
|
Feedback tensor first, then at most one driving-input tensor, each
of shape |
state
|
Initial reservoir state, shape
TYPE:
|
compile
|
Opt-in compiled/chunked engine for the teacher-forced sequence loop
(warmup, The compiled path is inference-only: it transparently falls back
to the eager loop when any parameter requires grad (a differentiable
BPTT rollout must stay eager), when there is no projected fast path
(e.g. NG-RC), on |
| RETURNS | DESCRIPTION |
|---|---|
outputs
|
Per-step outputs, shape
TYPE:
|
new_state
|
Final reservoir state after the last timestep.
TYPE:
|
Notes
The cross-call detach applied by :meth:forward (truncated BPTT) lives
in that wrapper, outside this method — gradients flow through the
returned new_state here, so callers that backpropagate through state
carried across calls are free to do so.
Source code in src/resdag/layers/reservoirs/base_reservoir.py
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | |
step_stateless
¶
Single-timestep analogue of :meth:forward_stateless: state in, state out.
Where :meth:forward_stateless consumes whole
(batch, timesteps, features) sequences and runs the time loop, this
consumes a single (batch, features) slice per stream and performs
exactly one cell update. It is the per-step primitive that the
flattened autoregressive engine
(:meth:resdag.core.ESNModel.forecast) drives, sidestepping the
sequence-loop bookkeeping (torch.stack, shape[1] reads) that
:meth:forward / :meth:forward_stateless pay even for a length-1
sequence.
| PARAMETER | DESCRIPTION |
|---|---|
inputs
|
Feedback slice first, then at most one driving-input slice, each of
shape |
state
|
Current reservoir state, shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
output
|
Per-step output, shape
TYPE:
|
new_state
|
Updated reservoir state.
TYPE:
|
Notes
The project-vs-fallback choice is the same per-cell static decision made
in :meth:forward_stateless (no data-dependent branch in the hot path),
so this method stays torch.compile-fullgraph friendly. Like
:meth:forward_stateless it never reads or writes :attr:state, and it
applies no cross-call detach — the forecast engine runs under
:func:torch.no_grad, so the threaded state never carries a graph.
Source code in src/resdag/layers/reservoirs/base_reservoir.py
forward
¶
Process an input sequence through the reservoir.
Computes reservoir states for each timestep using the feedback
signal and optional driving inputs. This is a thin stateful wrapper
over the pure :meth:forward_stateless: it validates inputs, lazily
initialises :attr:state, runs the functional loop, stores the
returned state, and applies the cross-call detach.
| PARAMETER | DESCRIPTION |
|---|---|
feedback
|
Feedback signal of shape
TYPE:
|
*driving_inputs
|
Optional driving input of shape
TYPE:
|
compile
|
Route the teacher-forced sequence loop through the opt-in
compiled/chunked engine. Forwarded verbatim to
:meth: |
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Reservoir states for all timesteps, shape
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Notes
The layer maintains internal state across forward calls. Use
:meth:reset_state to clear the state between independent sequences.
For a pure, self-free variant (used by the compile-scan, vmap, and
export paths) call :meth:forward_stateless directly.
Source code in src/resdag/layers/reservoirs/base_reservoir.py
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 | |
reference_device_dtype
¶
Resolve the canonical device and dtype of this reservoir's weights.
The reference is the first floating-point parameter or buffer of the
inner cell (the same scan used when lazily allocating a zero state),
falling back to cpu / float32 for weightless cells such as the
NG-RC reservoir. This is the device/dtype an incoming forward pass is
expected to use, so callers restoring a saved state should coerce it to
this reference to avoid a silent re-initialisation in
:meth:_maybe_init_state.
| RETURNS | DESCRIPTION |
|---|---|
device
|
Device of the cell's first floating-point tensor, or
TYPE:
|
dtype
|
Dtype of the cell's first floating-point tensor, or
TYPE:
|
Source code in src/resdag/layers/reservoirs/base_reservoir.py
reset_state
¶
reset_state(batch_size: int | None = None) -> None
Reset internal state to zero.
| PARAMETER | DESCRIPTION |
|---|---|
batch_size
|
If provided, initialize state with this batch size using the
cell's device and dtype. If
TYPE:
|
Examples:
>>> layer.reset_state() # Lazy initialization
>>> layer.reset_state(batch_size=4) # Explicit zero state
Source code in src/resdag/layers/reservoirs/base_reservoir.py
get_state
¶
get_state() -> Tensor | None
Get a copy of the current internal state.
| RETURNS | DESCRIPTION |
|---|---|
Tensor or None
|
Clone of the current state tensor, or |
Examples:
Source code in src/resdag/layers/reservoirs/base_reservoir.py
set_state
¶
set_state(state: Tensor) -> None
Set the internal state to a specific tensor.
Validation is delegated to self.cell.validate_state(state) so
each cell type owns its own state-shape contract (2-D for ESNCell,
3-D delay buffer for NGCell, etc.).
Batch-size contract
The restored state's batch size (state.shape[0]) pins the batch size
the next forward pass must use. A restored state is treated as
deliberate: if a subsequent forward is called with a different batch
size, :meth:_maybe_init_state raises a :class:RuntimeError instead of
silently zero-reinitialising and discarding the state you restored. To
forecast at a different batch size, restore (or :meth:reset_state and
re-warm) a state of the matching batch size; to opt back into automatic
batch-size re-initialisation, call :meth:reset_state. Device and dtype
are not pinned — the state moves with the module under .to() and is
cast to match an incoming forward.
Autograd contract
The stored state honours :attr:detach_state_between_calls, exactly as
the cross-call truncated-BPTT detach in :meth:forward does. Under the
default (True) a restored state is treated as a fresh initial
condition: it is stored as state.detach().clone() so it carries no
grad_fn and a later forward + backward cannot try to backprop
through state's already-freed graph (the "backward through the graph
a second time" / retained-memory failure that
:attr:detach_state_between_calls exists to prevent). Set
detach_state_between_calls=False to store state.clone() with its
graph intact, when you intentionally backprop through state carried in
across calls (and manage the retained graph yourself).
| PARAMETER | DESCRIPTION |
|---|---|
state
|
New state tensor. Its batch size (
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the cell's :meth: |
See Also
reset_state : Clear the state and opt back into automatic batch-size re-initialisation.
Examples:
>>> saved = layer.get_state() # captured at batch size B
>>> # ... process data ...
>>> layer.set_state(saved) # next forward must also use batch B
Source code in src/resdag/layers/reservoirs/base_reservoir.py
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 | |
set_random_state
¶
set_random_state(batch_size: int | None = None, device: device | None = None, dtype: dtype | None = None) -> None
Set the internal state to random (standard-normal) values.
| PARAMETER | DESCRIPTION |
|---|---|
batch_size
|
If provided, lazily initialise the state with this batch size
before filling it with random values. Uses
TYPE:
|
device
|
Target device when lazily initialising. Ignored if the state is already initialised.
TYPE:
|
dtype
|
Target dtype when lazily initialising. Ignored if the state is already initialised.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
RuntimeError
|
If the state has not been initialised and |
Examples:
>>> # Lazy: initialise then randomise in one call
>>> layer.set_random_state(batch_size=4)
>>> # Already-initialised state: just randomise in place
>>> layer.set_random_state()
Source code in src/resdag/layers/reservoirs/base_reservoir.py
ESNLayer
¶
ESNLayer(reservoir_size: int, feedback_size: int, input_size: int | None = None, spectral_radius: float | None = None, bias: bool = True, bias_scaling: float = 1.0, activation: str = 'tanh', leak_rate: float | Tensor | Sequence[float] = 1.0, noise: float = 0.0, trainable: bool = False, feedback_initializer: InitializerSpec = None, input_initializer: InitializerSpec = None, topology: TopologySpec = None, seed: SeedLike = None)
Bases: BaseReservoirLayer
Stateful RNN reservoir layer for Echo State Networks.
Public-facing class. Internally creates an :class:ESNCell that owns
all parameters and performs the single-step update; the sequence loop
and state management live in the parent :class:BaseReservoirLayer.
| PARAMETER | DESCRIPTION |
|---|---|
reservoir_size
|
Number of reservoir units (hidden state dimension).
TYPE:
|
feedback_size
|
Dimension of feedback signal. Required for all reservoirs.
TYPE:
|
input_size
|
Dimension of driving inputs. If
TYPE:
|
spectral_radius
|
Target spectral radius for recurrent weights. Controls the
"memory" and stability of the reservoir. If
TYPE:
|
bias
|
Whether to include a bias term.
TYPE:
|
bias_scaling
|
Scale of the random bias: entries are drawn from
TYPE:
|
activation
|
Activation function for reservoir dynamics.
TYPE:
|
leak_rate
|
Leaky integration rate in
TYPE:
|
noise
|
Standard deviation of additive Gaussian state noise injected after the
activation (the classical ESN readout-conditioning regularizer).
Active only in training mode (like dropout) and a no-op under
:meth:
TYPE:
|
trainable
|
If
TYPE:
|
feedback_initializer
|
Initializer for the feedback weight matrix. Accepts a registry
name,
TYPE:
|
input_initializer
|
Initializer for the input weight matrix. Same formats as
TYPE:
|
topology
|
Structure of the recurrent weight matrix. Accepts a registry name
(graph or matrix topology),
TYPE:
|
seed
|
Reproducibility seed that deterministically fixes the entire
reservoir — topology (recurrent) matrix, feedback weights, input
weights, and bias — so two layers built with the same seed are
identical down to the last entry. Accepts a plain |
| ATTRIBUTE | DESCRIPTION |
|---|---|
state |
Current reservoir state of shape
TYPE:
|
Examples:
Basic feedback-only reservoir:
>>> reservoir = ESNLayer(reservoir_size=500, feedback_size=10)
>>> feedback = torch.randn(4, 50, 10) # (batch, time, features)
>>> output = reservoir(feedback)
>>> print(output.shape)
torch.Size([4, 50, 500])
Reservoir with driving input:
>>> reservoir = ESNLayer(
... reservoir_size=500,
... feedback_size=10,
... input_size=5,
... spectral_radius=0.95
... )
>>> feedback = torch.randn(4, 50, 10)
>>> driving = torch.randn(4, 50, 5)
>>> output = reservoir(feedback, driving)
Using graph topology by name:
>>> reservoir = ESNLayer(
... reservoir_size=500,
... feedback_size=10,
... topology="erdos_renyi",
... spectral_radius=0.9
... )
Using topology with custom parameters (tuple format):
>>> reservoir = ESNLayer(
... reservoir_size=500,
... feedback_size=10,
... topology=("watts_strogatz", {"k": 6, "p": 0.3}),
... feedback_initializer=("pseudo_diagonal", {"input_scaling": 0.5}),
... spectral_radius=0.95
... )
Fully reproducible reservoir via the seed argument — recurrent matrix,
feedback/input weights, and bias all match:
>>> a = ESNLayer(50, feedback_size=3, topology="erdos_renyi", seed=42)
>>> b = ESNLayer(50, feedback_size=3, topology="erdos_renyi", seed=42)
>>> torch.equal(a.weight_hh, b.weight_hh)
True
>>> torch.equal(a.weight_feedback, b.weight_feedback)
True
>>> torch.equal(a.bias_h, b.bias_h)
True
A :class:torch.Generator works too (e.g. an HPO per-trial generator):
>>> g1 = torch.Generator().manual_seed(7)
>>> g2 = torch.Generator().manual_seed(7)
>>> a = ESNLayer(50, feedback_size=3, seed=g1)
>>> b = ESNLayer(50, feedback_size=3, seed=g2)
>>> torch.equal(a.weight_feedback, b.weight_feedback)
True
The reservoir is also reproducible under torch.manual_seed without an
explicit seed:
>>> torch.manual_seed(0)
>>> a = ESNLayer(50, feedback_size=3, topology="erdos_renyi")
>>> torch.manual_seed(0)
>>> b = ESNLayer(50, feedback_size=3, topology="erdos_renyi")
>>> torch.equal(a.weight_hh, b.weight_hh)
True
Stateful processing across batches:
>>> out1 = reservoir(data1) # State initialized
>>> out2 = reservoir(data2) # State carries over
>>> reservoir.reset_state() # Manual reset
>>> out3 = reservoir(data3) # Fresh state
See Also
resdag.layers.cells.esn_cell.ESNCell : Underlying single-step cell. resdag.layers.reservoirs.base_reservoir.BaseReservoirLayer : Base providing state management. resdag.init.topology.TopologyInitializer : Base class for topology init. resdag.init.input_feedback.InputFeedbackInitializer : Input init base. resdag.core.ESNModel : Model composition using reservoir layers.
Source code in src/resdag/layers/reservoirs/esn.py
NGReservoir
¶
Bases: NGReservoirLayer
Deprecated alias for :class:NGReservoirLayer.
Retained for backward compatibility. Instantiating this class emits a
:class:DeprecationWarning and constructs an :class:NGReservoirLayer;
the two are otherwise identical (NGReservoir is a subclass, so
isinstance(NGReservoir(...), NGReservoirLayer) holds).
.. deprecated::
Use :class:NGReservoirLayer instead. NGReservoir will be removed
in a future release.
See Also
NGReservoirLayer : The canonical class this aliases.
Source code in src/resdag/layers/reservoirs/ngrc.py
readouts
¶
Readout Layers¶
This module provides readout layer implementations for ESN models.
| CLASS | DESCRIPTION |
|---|---|
ReadoutLayer |
Base per-timestep linear layer with fitting interface. |
CGReadoutLayer |
Readout with Conjugate Gradient ridge regression solver. |
RidgeReadoutLayer |
Direct ridge readout via Cholesky ( |
CholeskyReadoutLayer |
Single-shot Cholesky ridge readout; the streaming-path direct solver. |
SVDReadoutLayer |
Readout solved via SVD with Tikhonov filter factors; robust to
rank-deficient Gram matrices and |
PinvReadoutLayer |
Least-squares readout via |
IncrementalRidgeReadoutLayer |
Streaming ridge readout: |
RLSReadout |
True-online Recursive Least Squares readout with a forgetting factor:
|
FISTAReadoutLayer |
Sparse / robust readout: L1 (lasso) or elastic-net regression solved with FISTA (accelerated proximal gradient), batched over all outputs. |
The old names ``IncrementalRidgeReadout`` and ``FISTAReadout`` are retained as |
|
deprecated aliases of ``IncrementalRidgeReadoutLayer`` / ``FISTAReadoutLayer``. |
|
Examples:
>>> from resdag.layers.readouts import CGReadoutLayer
>>> readout = CGReadoutLayer(
... in_features=200,
... out_features=3,
... alpha=1e-6,
... name="output",
... )
>>> # Fit using ESNTrainer or directly
>>> readout.fit(states, targets)
>>> output = readout(states)
See Also
resdag.training.ESNTrainer : Trainer that uses these readouts. resdag.layers.ESNLayer : ESN layer for generating states.
ReadoutLayer
¶
ReadoutLayer(in_features: int, out_features: int, bias: bool = True, name: str | None = None, trainable: bool = False)
Bases: Linear
Per-timestep linear layer with custom fitting for ESN training.
This layer extends :class:torch.nn.Linear with:
- Per-timestep application to sequence tensors
(B, T, F) - Named identification for multi-readout architectures
- Custom
fit()interface for classical ESN training
The layer applies the same linear transformation independently to each timestep in a sequence:
.. code-block:: text
Input: (B, T, F_in) -> Reshape to (B*T, F_in)
Apply: linear(x) = x @ W.T + b
Output: (B*T, F_out) -> Reshape to (B, T, F_out)
This matches classical ESN semantics where readouts are fitted across the entire sequence at once using ridge regression.
| PARAMETER | DESCRIPTION |
|---|---|
in_features
|
Size of input features.
TYPE:
|
out_features
|
Size of output features.
TYPE:
|
bias
|
Whether to include a bias term.
TYPE:
|
name
|
Name for this readout layer. Used for identification in
multi-readout architectures and by :class:
TYPE:
|
trainable
|
If True, weights are trainable via backpropagation. If False, weights are frozen (standard ESN behavior).
TYPE:
|
| ATTRIBUTE | DESCRIPTION |
|---|---|
weight |
Weight matrix of shape
TYPE:
|
bias |
Bias vector of shape
TYPE:
|
name |
Name of this readout layer.
TYPE:
|
is_fitted |
True if
TYPE:
|
Examples:
Basic usage:
>>> readout = ReadoutLayer(in_features=100, out_features=10)
>>> x = torch.randn(2, 20, 100) # (batch, seq_len, features)
>>> y = readout(x)
>>> print(y.shape)
torch.Size([2, 20, 10])
Named readout for multi-output architectures:
>>> readout1 = ReadoutLayer(100, 10, name="position")
>>> readout2 = ReadoutLayer(100, 3, name="velocity")
See Also
CGReadoutLayer : Readout with Conjugate Gradient solver. resdag.training.ESNTrainer : Trainer for fitting readouts.
Source code in src/resdag/layers/readouts/base.py
trainable
property
writable
¶
trainable: bool
bool : Whether the readout weights participate in backpropagation.
A live switch: assigning to it flips requires_grad_ on every
readout parameter (weight and, when present, bias). A
post-construction readout.trainable = True therefore genuinely
unfreezes the readout for a gradient-based rollout rather than silently
shadowing a construction-time-only flag. The getter reflects the
current state.
is_fitted
property
¶
is_fitted: bool
bool : True if fit() has been called successfully.
Backed by a registered buffer (so the flag is preserved across
state_dict / load_state_dict round-trips) but read from a plain
Python mirror kept coherent with it. Reading the mirror avoids the
buffer .item() device->host sync — negligible once, but ruinous
inside a per-step autoregressive forecast loop and a torch.compile
graph-break.
forward
¶
Apply linear transformation to input.
Handles both 2D (batch, features) and 3D (batch, seq_len, features)
inputs. For 3D inputs, applies the linear transformation independently
to each timestep.
| PARAMETER | DESCRIPTION |
|---|---|
input
|
Input tensor of shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Output tensor of shape |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If input has neither 2 nor 3 dimensions. |
Examples:
>>> readout = ReadoutLayer(100, 10)
>>> x_2d = torch.randn(4, 100)
>>> y_2d = readout(x_2d) # (4, 10)
>>> x_3d = torch.randn(4, 50, 100)
>>> y_3d = readout(x_3d) # (4, 50, 10)
Source code in src/resdag/layers/readouts/base.py
fit
¶
Fit readout weights to the given (states, targets) pair.
This base implementation handles the bookkeeping that every readout
needs: shape normalisation from (B, T, F) → (B*T, F) plus
sample-count and out-features validation. The actual algebraic
solve is delegated to :meth:_fit_impl, which subclasses override.
| PARAMETER | DESCRIPTION |
|---|---|
states
|
Input states of shape
TYPE:
|
targets
|
Target outputs of shape
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
NotImplementedError
|
The base |
ValueError
|
If |
Notes
After fit() returns successfully, :attr:is_fitted is True.
See Also
CGReadoutLayer : Concrete implementation using Conjugate Gradient.
Source code in src/resdag/layers/readouts/base.py
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | |
RidgeReadoutLayer
¶
RidgeReadoutLayer(in_features: int, out_features: int, bias: bool = True, name: str | None = None, trainable: bool = False, alpha: float = 1e-06, solver: str = 'cholesky', use_float64: bool = True, gram_dtype: dtype | None = None)
Bases: ReadoutLayer
Readout layer with a direct ridge-regression solver.
For the readout widths typical of reservoir computing
(in_features = 100–2000) the (F, F) Gram matrix factorises
exactly and almost instantly. A direct Cholesky solve is both faster and
exact (up to floating point), whereas :class:CGReadoutLayer only
approximates the solution and can under-converge on the ill-conditioned
concatenated-input readouts produced by ott_esn / power_augmented.
The solver minimises the same ridge objective as :class:CGReadoutLayer,
.. math::
\lVert X W - Y \rVert_2^2 + \alpha \lVert W \rVert_2^2 ,
by forming the regularised normal equations (XᵀX + αI) W = Xᵀy and
solving them with one of:
solver='cholesky'(default) —torch.linalg.choleskyfollowed bytorch.linalg.cholesky_solve. Exploits the symmetric positive definiteness ofXᵀX + αI(guaranteed foralpha > 0) and is the fastest, most numerically stable option for well-conditioned fits.solver='solve'—torch.linalg.solve(LU). Slightly slower but does not require positive definiteness, so it toleratesalpha == 0on a full-rank Gram.
Solver-selection guide
- Default to
RidgeReadoutLayer(solver='cholesky')for any well-conditioned readout withalpha > 0andF <= 2000. It is the fastest exact option and matches :class:CGReadoutLayerto< 1e-8. - Use
solver='solve'when you needalpha == 0(pure least squares) and the Gram is full rank but you don't want the SVD machinery. - If the Gram is rank deficient (high-degree NG-RC feature maps, more
features than samples), a Cholesky/LU solve will fail or be unstable —
reach for :class:
SVDReadoutLayer(filter factors) or :class:PinvReadoutLayer(lstsq / pinv) instead.
| PARAMETER | DESCRIPTION |
|---|---|
in_features
|
Size of input features (reservoir state dimension).
TYPE:
|
out_features
|
Size of output features (prediction dimension).
TYPE:
|
bias
|
Whether to include a bias term. When
TYPE:
|
name
|
Name for this readout layer. Used for identification in multi-readout
architectures and as the key into
TYPE:
|
trainable
|
If
TYPE:
|
alpha
|
L2 regularization strength. Must be non-negative.
TYPE:
|
solver
|
Direct solver to use for the normal equations.
TYPE:
|
use_float64
|
If
TYPE:
|
gram_dtype
|
Dtype for forming the Gram matrix and right-hand side (the heavy
TYPE:
|
| ATTRIBUTE | DESCRIPTION |
|---|---|
weight |
Weight matrix of shape
TYPE:
|
bias |
Bias vector of shape
TYPE:
|
alpha |
L2 regularization strength.
TYPE:
|
solver |
The selected direct solver.
TYPE:
|
Examples:
>>> readout = RidgeReadoutLayer(in_features=200, out_features=3, alpha=1e-6)
>>> states = torch.randn(8, 50, 200) # (batch, time, features)
>>> targets = torch.randn(8, 50, 3)
>>> readout.fit(states, targets)
>>> output = readout(states)
>>> output.shape
torch.Size([8, 50, 3])
See Also
CGReadoutLayer : Iterative Conjugate Gradient ridge readout. SVDReadoutLayer : SVD filter-factor readout for rank-deficient Gram. PinvReadoutLayer : lstsq / pseudo-inverse readout. resdag.training.ESNTrainer : Trainer that uses this for fitting.
Source code in src/resdag/layers/readouts/ridge_readout.py
CholeskyReadoutLayer
¶
CholeskyReadoutLayer(in_features: int, out_features: int, bias: bool = True, name: str | None = None, trainable: bool = False, alpha: float = 1e-06, use_float64: bool = True, gram_dtype: dtype | None = None)
Bases: ReadoutLayer
Readout layer that solves ridge regression via a Cholesky factorisation.
The regularised normal equations (XᵀX + αI) W = Xᵀy are formed (with
analytic centering when a bias is fitted) and solved with
:func:torch.linalg.cholesky followed by :func:torch.cholesky_solve. The
(F, F) system is symmetric positive definite for alpha > 0, so the
Cholesky factorisation is the fastest, most numerically stable exact solve
for the readout widths typical of reservoir computing
(in_features = 100–2000).
Unlike :class:CGReadoutLayer, which only approximates the solution
iteratively, this layer returns the exact ridge solution (up to floating
point) in a single shot. It is the single-batch counterpart of the
streaming :class:IncrementalRidgeReadoutLayer: both ultimately Cholesky-solve
the same regularised Gram system, so a full-batch CholeskyReadoutLayer
fit and an accumulated IncrementalRidgeReadoutLayer fit over the same data
agree to within floating-point tolerance.
The solver minimises the same ridge objective as :class:CGReadoutLayer,
.. math::
\lVert X W - Y \rVert_2^2 + \alpha \lVert W \rVert_2^2 .
| PARAMETER | DESCRIPTION |
|---|---|
in_features
|
Size of input features (reservoir state dimension).
TYPE:
|
out_features
|
Size of output features (prediction dimension).
TYPE:
|
bias
|
Whether to include a bias term. When
TYPE:
|
name
|
Name for this readout layer. Used for identification in multi-readout
architectures and as the key into
TYPE:
|
trainable
|
If
TYPE:
|
alpha
|
L2 regularization strength. Must be non-negative.
TYPE:
|
use_float64
|
If
TYPE:
|
gram_dtype
|
Dtype for forming the Gram matrix and right-hand side (the heavy
TYPE:
|
| ATTRIBUTE | DESCRIPTION |
|---|---|
weight |
Weight matrix of shape
TYPE:
|
bias |
Bias vector of shape
TYPE:
|
alpha |
L2 regularization strength.
TYPE:
|
Examples:
>>> readout = CholeskyReadoutLayer(in_features=200, out_features=3, alpha=1e-6)
>>> states = torch.randn(8, 50, 200) # (batch, time, features)
>>> targets = torch.randn(8, 50, 3)
>>> readout.fit(states, targets)
>>> output = readout(states)
>>> output.shape
torch.Size([8, 50, 3])
See Also
CGReadoutLayer : Iterative Conjugate Gradient ridge readout.
RidgeReadoutLayer : Direct ridge readout with a solver switch.
IncrementalRidgeReadoutLayer : Streaming partial_fit / finalize counterpart.
resdag.training.ESNTrainer : Trainer that uses this for fitting.
Source code in src/resdag/layers/readouts/cholesky_readout.py
CGReadoutLayer
¶
CGReadoutLayer(in_features: int, out_features: int, bias: bool = True, name: str | None = None, trainable: bool = False, max_iter: int = 100, tol: float = 1e-05, alpha: float = 1e-06, use_float64: bool = True, gram_dtype: dtype | None = None)
Bases: ReadoutLayer
Readout layer with Conjugate Gradient ridge regression solver.
This layer extends :class:ReadoutLayer with an efficient Conjugate
Gradient (CG) solver for fitting weights via ridge regression. The CG
solver is:
- Memory efficient (doesn't form full normal equations matrix)
- GPU accelerated
- Numerically stable (uses float64 internally)
- Supports batched time-series data
The solver finds weights W that minimize:
.. math::
||XW - Y||^2 + \alpha ||W||^2
where :math:\alpha is the regularization strength.
| PARAMETER | DESCRIPTION |
|---|---|
in_features
|
Size of input features (reservoir state dimension).
TYPE:
|
out_features
|
Size of output features (prediction dimension).
TYPE:
|
bias
|
Whether to include a bias term.
TYPE:
|
name
|
Name for this readout layer. Used for identification in
multi-readout architectures and by :class:
TYPE:
|
trainable
|
If True, weights are trainable via backpropagation.
TYPE:
|
alpha
|
L2 regularization strength. Must be non-negative. Larger values provide more regularization (smoother outputs, less overfitting).
TYPE:
|
max_iter
|
Maximum number of CG iterations. Must be a positive integer.
TYPE:
|
tol
|
Convergence tolerance for the CG solver. Must be positive. Each output
column is considered converged when its residual-norm-squared falls
below
TYPE:
|
use_float64
|
If
TYPE:
|
gram_dtype
|
Dtype for forming the Gram matrix and right-hand side (the heavy
TYPE:
|
| ATTRIBUTE | DESCRIPTION |
|---|---|
weight |
Weight matrix of shape
TYPE:
|
bias |
Bias vector of shape
TYPE:
|
alpha |
L2 regularization strength.
TYPE:
|
max_iter |
Maximum CG iterations.
TYPE:
|
tol |
Convergence tolerance.
TYPE:
|
Examples:
Basic usage:
>>> readout = CGReadoutLayer(in_features=100, out_features=10, alpha=1e-6)
>>> states = torch.randn(32, 50, 100) # (batch, time, features)
>>> targets = torch.randn(32, 50, 10)
>>> readout.fit(states, targets)
>>> output = readout(states)
>>> print(output.shape)
torch.Size([32, 50, 10])
With custom regularization:
>>> readout = CGReadoutLayer(100, 10, alpha=1e-4) # Stronger regularization
>>> readout.fit(states, targets)
See Also
ReadoutLayer : Base readout layer class. resdag.training.ESNTrainer : Trainer that uses this for fitting.
Source code in src/resdag/layers/readouts/cg_readout.py
SVDReadoutLayer
¶
SVDReadoutLayer(in_features: int, out_features: int, bias: bool = True, name: str | None = None, trainable: bool = False, alpha: float = 0.0, rcond: float = 1e-15, use_float64: bool = True, gram_dtype: dtype | None = None)
Bases: ReadoutLayer
Readout layer solved via SVD with Tikhonov filter factors.
The (optionally centered) design matrix X is decomposed as
X = U diag(s) Vᵀ and the ridge solution is reconstructed with
filter factors
.. math::
W = V \, \operatorname{diag}\!\left(\frac{s}{s^2 + \alpha}\right)
U^\top Y .
For alpha > 0 this is exactly the ridge solution; for alpha == 0 it
collapses to the minimum-norm least-squares solution, with singular values
at or below an rcond cutoff dropped so a rank-deficient Gram (more
features than independent samples, or the near-collinear columns produced
by high-degree NG-RC feature maps) is handled gracefully instead of
blowing up the way a Cholesky / normal-equations solve would.
Working from the SVD of X directly (rather than the Gram XᵀX)
squares the effective condition number's square root away, so the small
singular values that an iterative CG solver under-resolves are recovered
faithfully — giving a strictly lower train residual on the ill-conditioned
fits where CG stalls.
Solver-selection guide
- Reach for :class:
SVDReadoutLayerwhen the Gram is rank deficient or severely ill-conditioned:alpha == 0least squares, high-degree NG-RC feature maps, or wide concatenated-input readouts where CG under-converges. It is the most robust of the readouts. - For a well-conditioned readout with
alpha > 0andF <= 2000, :class:RidgeReadoutLayer(solver='cholesky') is faster and equally accurate — prefer it there. - :class:
PinvReadoutLayeris the closely-related lstsq / pinv route; SVD here gives explicit control over the Tikhonov filter andrcond.
| PARAMETER | DESCRIPTION |
|---|---|
in_features
|
Size of input features (reservoir state dimension).
TYPE:
|
out_features
|
Size of output features (prediction dimension).
TYPE:
|
bias
|
Whether to include a bias term. When
TYPE:
|
name
|
Name for this readout layer. Used for identification in multi-readout
architectures and as the key into
TYPE:
|
trainable
|
If
TYPE:
|
alpha
|
Tikhonov (L2) regularization strength. Must be non-negative.
TYPE:
|
rcond
|
Relative cutoff for small singular values. Singular values at or below
TYPE:
|
use_float64
|
If
TYPE:
|
gram_dtype
|
Dtype for forming / casting the design matrix. Default
TYPE:
|
| ATTRIBUTE | DESCRIPTION |
|---|---|
weight |
Weight matrix of shape
TYPE:
|
bias |
Bias vector of shape
TYPE:
|
alpha |
Tikhonov regularization strength.
TYPE:
|
rcond |
Relative singular-value cutoff.
TYPE:
|
Examples:
>>> readout = SVDReadoutLayer(in_features=200, out_features=3, alpha=0.0)
>>> states = torch.randn(8, 50, 200) # (batch, time, features)
>>> targets = torch.randn(8, 50, 3)
>>> readout.fit(states, targets)
>>> output = readout(states)
>>> output.shape
torch.Size([8, 50, 3])
See Also
RidgeReadoutLayer : Cholesky / solve direct readout (faster, full rank). PinvReadoutLayer : lstsq / pseudo-inverse readout. CGReadoutLayer : Iterative Conjugate Gradient ridge readout. resdag.training.ESNTrainer : Trainer that uses this for fitting.
Source code in src/resdag/layers/readouts/svd_readout.py
PinvReadoutLayer
¶
PinvReadoutLayer(in_features: int, out_features: int, bias: bool = True, name: str | None = None, trainable: bool = False, rcond: float = 1e-15, solver: str = 'lstsq', use_float64: bool = True, gram_dtype: dtype | None = None)
Bases: ReadoutLayer
Readout layer solved via least squares / pseudo-inverse.
Solves the (optionally centered) least-squares problem
min_W ||X W - Y|| directly, without forming the Gram matrix, using one
of:
solver='lstsq'(default) —torch.linalg.lstsqwith thegelsddriver, a divide-and-conquer SVD-based least-squares solver that returns the minimum-norm solution for rank-deficientXand honours thercondcutoff.solver='pinv'— form the Moore–Penrose pseudo-inversetorch.linalg.pinv(X, rcond=...)and apply it toY. Equivalent in result but materialises the(F, N)pseudo-inverse explicitly.
This is a pure least-squares readout: there is no Tikhonov penalty
(alpha). Regularisation comes only from the rcond truncation of
small singular values. If you need an explicit ridge penalty use
:class:RidgeReadoutLayer or :class:SVDReadoutLayer (which exposes both
alpha and rcond).
Solver-selection guide
- Reach for :class:
PinvReadoutLayerfor unregularised least squares on a possibly rank-deficient design matrix when you want the minimum-norm solution and don't need a Tikhonovalpha. solver='lstsq'(default) is the right choice almost always — it never materialises the pseudo-inverse. Usesolver='pinv'only if you specifically want the pseudo-inverse matrix itself.- For an explicit ridge penalty, prefer :class:
SVDReadoutLayer(filter factors,alpha+rcond) or :class:RidgeReadoutLayer(Cholesky, fast, full rank).
| PARAMETER | DESCRIPTION |
|---|---|
in_features
|
Size of input features (reservoir state dimension).
TYPE:
|
out_features
|
Size of output features (prediction dimension).
TYPE:
|
bias
|
Whether to include a bias term. When
TYPE:
|
name
|
Name for this readout layer. Used for identification in multi-readout
architectures and as the key into
TYPE:
|
trainable
|
If
TYPE:
|
rcond
|
Relative cutoff for small singular values. Singular values at or below
TYPE:
|
solver
|
Whether to solve with
TYPE:
|
use_float64
|
If
TYPE:
|
gram_dtype
|
Dtype for forming / casting the design matrix. Default
TYPE:
|
| ATTRIBUTE | DESCRIPTION |
|---|---|
weight |
Weight matrix of shape
TYPE:
|
bias |
Bias vector of shape
TYPE:
|
rcond |
Relative singular-value cutoff.
TYPE:
|
solver |
The selected least-squares solver.
TYPE:
|
Examples:
>>> readout = PinvReadoutLayer(in_features=200, out_features=3)
>>> states = torch.randn(8, 50, 200) # (batch, time, features)
>>> targets = torch.randn(8, 50, 3)
>>> readout.fit(states, targets)
>>> output = readout(states)
>>> output.shape
torch.Size([8, 50, 3])
See Also
SVDReadoutLayer : SVD filter-factor readout with explicit alpha.
RidgeReadoutLayer : Cholesky / solve direct ridge readout.
CGReadoutLayer : Iterative Conjugate Gradient ridge readout.
resdag.training.ESNTrainer : Trainer that uses this for fitting.
Source code in src/resdag/layers/readouts/pinv_readout.py
IncrementalRidgeReadout
¶
Bases: IncrementalRidgeReadoutLayer
Deprecated alias for :class:IncrementalRidgeReadoutLayer.
Retained for backward compatibility. Instantiating this class emits a
:class:DeprecationWarning and constructs an
:class:IncrementalRidgeReadoutLayer; the two are otherwise identical
(IncrementalRidgeReadout is a subclass, so
isinstance(IncrementalRidgeReadout(...), IncrementalRidgeReadoutLayer)
holds).
.. deprecated::
Use :class:IncrementalRidgeReadoutLayer instead.
IncrementalRidgeReadout will be removed in a future release.
See Also
IncrementalRidgeReadoutLayer : The canonical class this aliases.
Source code in src/resdag/layers/readouts/incremental_ridge.py
transforms
¶
Transform Layers¶
Specialized transformation layers for advanced ESN architectures, including state augmentation, feature manipulation, and regularization.
| CLASS | DESCRIPTION |
|---|---|
Concatenate |
Concatenates multiple inputs along the feature dimension. |
ExtendedSquare |
Appends the elementwise square of every feature ( |
FeaturePartitioner |
Partitions input features into overlapping groups. |
NLAT2 |
T₂ nonlinear algebraic transform ( |
NLAT3 |
T₃ nonlinear algebraic transform ( |
Pad |
Appends a constant column to the feature dimension ( |
PartialSquare |
Squares a leading fraction (or count) of features in place. |
Power |
Per-feature exponentiation by a fixed power. |
SelectiveDropout |
Per-feature dropout with selectivity control. |
SelectiveExponentiation |
Per-feature exponentiation transformation (squares even-indexed units;
|
Standardize |
Per-feature z-score standardization with buffer-stored mean/std,
|
Examples:
>>> from resdag.layers.transforms import Concatenate, SelectiveExponentiation
>>> import torch
>>>
>>> concat = Concatenate()
>>> x1 = torch.randn(4, 100, 50)
>>> x2 = torch.randn(4, 100, 50)
>>> combined = concat(x1, x2) # (4, 100, 100)
Concatenate
¶
Bases: Module
Concatenate multiple tensors along the feature (last) dimension.
This layer takes any number of input tensors and concatenates them along the last dimension. Useful for combining outputs from parallel branches in multi-reservoir or ensemble architectures.
All input tensors must have the same shape except for the last dimension (features).
| ATTRIBUTE | DESCRIPTION |
|---|---|
None |
This layer has no learnable parameters.
|
Examples:
Basic concatenation:
>>> import torch
>>> from resdag.layers.transforms import Concatenate
>>>
>>> concat = Concatenate()
>>> x1 = torch.randn(32, 50, 100) # (batch, time, features1)
>>> x2 = torch.randn(32, 50, 200) # (batch, time, features2)
>>> y = concat(x1, x2)
>>> print(y.shape)
torch.Size([32, 50, 300])
Multiple inputs:
>>> x3 = torch.randn(32, 50, 50)
>>> y = concat(x1, x2, x3)
>>> print(y.shape)
torch.Size([32, 50, 350])
See Also
FeaturePartitioner : Splits features into overlapping partitions.
forward
¶
Concatenate input tensors along the last dimension.
| PARAMETER | DESCRIPTION |
|---|---|
*inputs
|
Variable number of input tensors to concatenate. All tensors must have the same shape except for the last dimension.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Concatenated tensor with combined feature dimension. |
| RAISES | DESCRIPTION |
|---|---|
RuntimeError
|
If tensors have incompatible shapes for concatenation. |
Examples:
>>> concat = Concatenate()
>>> a = torch.randn(4, 10, 20)
>>> b = torch.randn(4, 10, 30)
>>> result = concat(a, b)
>>> print(result.shape)
torch.Size([4, 10, 50])
Source code in src/resdag/layers/transforms/concatenate.py
FeaturePartitioner
¶
Bases: Module
A layer that partitions the feature dimension into overlapping slices.
This layer is useful for dividing input features into structured regions while maintaining smooth transitions between partitions. Commonly used in parallel reservoir architectures where different reservoirs process different feature subspaces.
Behavior:
- Splits the feature dimension into partitions groups
- Each partition overlaps with its neighbors by overlap units
- Applies circular wrapping: last overlap features wrap to start, and vice versa
Args: partitions: Number of partitions to divide the feature dimension into overlap: Overlap size (in feature units) for each partition
Input Shape:
(..., features) — rank-agnostic on the feature (last) dimension. Both
(batch, features) and (batch, sequence_length, features) are accepted;
the leading dimensions are preserved. The 2-D rank lets the layer sit in
the autoregressive forecast path, where the flattened engine feeds
single-step slices.
Output:
List of partitions tensors, each with the same leading dimensions as
the input and a last dimension of
partition_width = features // partitions + 2 * overlap
Raises:
ValueError: At construction, if partitions < 1 or overlap < 0.
ValueError: At forward, if features % partitions != 0 (unless partitions == 1).
ValueError: At forward, if overlap >= features // partitions (invalid overlap size).
Warns:
UserWarning: At construction, if partitions == 1 and overlap > 0,
since the single-partition fast path returns the input unchanged and
the overlap is silently ignored.
Example: >>> partitioner = FeaturePartitioner(partitions=2, overlap=1) >>> x = torch.arange(12).reshape(1, 1, 12).float() >>> outputs = partitioner(x) >>> len(outputs) 2 >>> outputs[0].shape torch.Size([1, 1, 8]) # 12//2 + 2*1 = 8
Configuration is validated eagerly so misconfiguration surfaces at
construction rather than deep inside a forward pass (the layer lives in
an eagerly-resolved symbolic graph). The divisibility and
overlap-vs-width checks stay in :meth:forward, where the runtime
feature count is known.
Args: partitions: Number of partitions (must be a positive integer) overlap: Overlap size between adjacent partitions (must be non-negative)
Raises:
ValueError: If partitions < 1 or overlap < 0.
Warns:
UserWarning: If partitions == 1 and overlap > 0 — the
single-partition fast path returns the input unchanged, so the
overlap has no effect.
Source code in src/resdag/layers/transforms/feature_partitioner.py
forward
¶
Split the feature dimension into overlapping partitions with circular wrapping.
Operates purely on the feature (last) dimension, so it is rank-agnostic:
both 2-D (batch, features) and 3-D (batch, sequence_length, features)
inputs are accepted (the latter is the usual sequence layout; the former
is what the flattened single-step forecast engine feeds per step). The
leading dimensions are preserved unchanged.
Args: input: Input tensor whose last dimension is the feature dimension, e.g. (batch, features) or (batch, sequence_length, features)
Returns:
List of length self.partitions, each with the same leading
dimensions as input and a last dimension of partition_width
Raises: ValueError: If feature dimension is not divisible by partitions ValueError: If overlap is too large relative to partition size
Source code in src/resdag/layers/transforms/feature_partitioner.py
Power
¶
Bases: Module
Exponentiate every feature to a fixed power.
Applies the chosen power element-wise along the last dimension. Used in power-augmented ESN architectures to enrich reservoir states before readout.
| PARAMETER | DESCRIPTION |
|---|---|
exponent
|
Power applied to each element of the input tensor.
TYPE:
|
sign_preserving
|
If
TYPE:
|
Notes
With sign_preserving=False (the default), :func:torch.pow has two
edge cases that silently corrupt readout inputs on common reservoir states:
- A negative base with a non-integer exponent produces
nanin both the forward and backward pass (the realpowis undefined there). For example,Power(0.5)on[[-4.0, 4.0]]yields[[nan, 2.0]]. - A zero base with a negative exponent produces
inf(division by zero). For example,Power(-1.0)on[[0.0, 2.0]]yields[[inf, 0.5]].
Tanh reservoir states live in [-1, 1] and routinely include negative
and zero values, so a non-integer exponent on raw states is a plausible
augmentation choice that silently emits nan/inf with no diagnostic.
Use sign_preserving=True to apply sign(x) * abs(x) ** exponent,
which stays finite for negative bases (the zero base with a negative
exponent is still inf — abs(0) ** -1 diverges — and is unaffected
by sign preservation). For integer exponents the two modes are identical
for non-negative bases and differ only in sign for negative bases raised to
an odd integer power. Every integer exponent — even ones map negatives to
non-negative values, odd ones (including the power_augmented default
exponent=3.0) preserve sign — is safe in either mode.
Examples:
>>> layer = Power(exponent=2.0)
>>> x = torch.tensor([[1.0, 2.0, 3.0, 4.0]])
>>> layer(x)
tensor([[ 1., 4., 9., 16.]])
Sign-preserving mode keeps negative bases finite under a fractional
exponent, where plain :func:torch.pow would return nan:
>>> layer = Power(exponent=0.5, sign_preserving=True)
>>> x = torch.tensor([[-4.0, 4.0, -9.0, 9.0]])
>>> layer(x)
tensor([[-2., 2., -3., 3.]])
| PARAMETER | DESCRIPTION |
|---|---|
exponent
|
Value the input is raised to for every element.
TYPE:
|
sign_preserving
|
If
TYPE:
|
Source code in src/resdag/layers/transforms/power.py
forward
¶
Raise the input to self.exponent.
| PARAMETER | DESCRIPTION |
|---|---|
input
|
Tensor of shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Same shape as input, with each element raised to |
Source code in src/resdag/layers/transforms/power.py
SelectiveDropout
¶
Bases: Module
Layer that zeros out specific features based on a fixed mask.
This layer zeros out features at specified indices across all timesteps and batches. Unlike standard dropout which is stochastic, this layer uses a fixed mask provided at initialization. Useful for ablation studies and feature importance analysis.
Args: mask: Boolean mask where True indicates features to zero out. Can be array-like (list, numpy array) or torch.Tensor of shape (features,)
Input Shape:
(batch, features) or (batch, timesteps, features) — the mask is applied
on the feature (last) dimension, so both ranks are accepted. The
autoregressive :meth:resdag.core.ESNModel.forecast engine drives the
graph one timestep at a time, which is why per-step layers must accept
the singleton-time slice as well as the full sequence.
Output Shape: Same shape as the input.
Example: >>> import numpy as np >>> mask = np.array([False, True, False, True]) # Drop indices 1 and 3 >>> layer = SelectiveDropout(mask) >>> x = torch.randn(2, 5, 4) >>> y = layer(x) >>> # Features at indices 1 and 3 are zeroed out
Args: mask: Boolean mask indicating which features to zero out (True = drop)
Raises: ValueError: If mask is not 1-dimensional
Source code in src/resdag/layers/transforms/selective_dropout.py
forward
¶
Apply selective dropout using the stored mask.
Handles both 2-D (batch, features) and 3-D
(batch, timesteps, features) inputs — the mask broadcasts over the
feature (last) dimension, mirroring :class:~resdag.layers.readouts.ReadoutLayer.
Accepting the 2-D rank lets the layer sit in the autoregressive forecast
path, where the flattened engine feeds single-step slices.
Args: input: Input tensor of shape (batch, features) or (batch, timesteps, features)
Returns: Input tensor (same shape) with masked features set to zero
Raises: ValueError: If input is neither 2-D nor 3-D, or the mask size does not match the feature dimension
Source code in src/resdag/layers/transforms/selective_dropout.py
SelectiveExponentiation
¶
Bases: Module
Exponentiate even or odd feature indices based on parity.
If index is even, even positions in the last dimension are raised to
exponent; if index is odd, odd positions are exponentiated. Other
elements are unchanged. Used in Ott-style state-augmented ESNs.
With index=0, exponent=2 this is the T₁ / NLAT1 nonlinear algebraic
transform — squaring every even-0-based feature — of Chattopadhyay,
Hassanzadeh & Subramanian (2020) and Pathak et al. (2017), matching
NLAT1 in ReservoirComputing.jl (which squares the odd entries of a
1-based feature-major state; those are the even-0-based features here). Its
T₂/T₃ siblings are :class:~resdag.layers.transforms.NLAT2 and
:class:~resdag.layers.transforms.NLAT3.
The transform is implemented with a :func:torch.where gate so that
unselected positions never enter the pow node: they pass through with a
gradient of 1 and are never poisoned by the pow backward (which is
inf/nan at x = 0 for exponent < 1). The base fed to pow
is also masked to a safe constant at unselected positions, which keeps
unselected gradients finite even when those positions hold negative bases
under a non-integer exponent.
The parity mask depends only on (feature_dim, device) — never on the
input values or dtype — so it is computed once per (dim, device) pair and
cached. This keeps the autoregressive forecast loop (which calls this
layer once per step) off the per-step arange/parity/ones_like
allocation path. The cache is a plain dict that stays out of state_dict
and named_buffers; it is rebuilt lazily when a new dim or device is seen.
For the common integer exponent=2 with sign_preserving=False the
forward reduces to where(mask, x * x, x) (x * x is bit-identical to
pow(x, 2) and finite everywhere, so the safe-base masking is
unnecessary); the full safe-base path is retained for every other case.
| PARAMETER | DESCRIPTION |
|---|---|
index
|
Parity selector: even
TYPE:
|
exponent
|
Power applied to the selected positions.
TYPE:
|
sign_preserving
|
If
TYPE:
|
Notes
With sign_preserving=False and a non-integer exponent, selected
positions holding a negative value produce nan in both the forward
and backward pass — this is the mathematically correct behaviour of a real
pow and matches :func:torch.pow. Unselected positions are always
finite. For integer exponents (including the default ott_esn path,
exponent=2.0) the two modes are identical for non-negative bases and
differ only in sign for negative bases raised to an odd integer power.
Examples:
>>> layer = SelectiveExponentiation(index=2, exponent=2.0)
>>> x = torch.tensor([[1.0, 2.0, 3.0, 4.0]])
>>> layer(x)
tensor([[1., 2., 9., 4.]])
Sign-preserving mode keeps negative selected bases finite under a fractional exponent:
>>> layer = SelectiveExponentiation(index=0, exponent=0.5, sign_preserving=True)
>>> x = torch.tensor([[-4.0, 2.0, -9.0, 3.0]])
>>> layer(x)
tensor([[-2., 2., -3., 3.]])
| PARAMETER | DESCRIPTION |
|---|---|
index
|
Determines which feature indices are exponentiated (even vs odd).
TYPE:
|
exponent
|
Power applied to selected positions.
TYPE:
|
sign_preserving
|
If
TYPE:
|
Source code in src/resdag/layers/transforms/selective_exponentiation.py
forward
¶
Apply selective exponentiation along the last dimension.
| PARAMETER | DESCRIPTION |
|---|---|
input
|
Tensor of shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Same shape as input; selected indices are raised to |
Source code in src/resdag/layers/transforms/selective_exponentiation.py
Standardize
¶
Standardize(num_features: int, mean: Tensor | None = None, std: Tensor | None = None, eps: float = 1e-08)
Bases: Module
Per-feature standardization (z-score) with a learnable-free inverse.
Centers and scales each feature of the last dimension using stored
statistics, applying (x - mean) / std in :meth:forward and the exact
inverse x * std + mean in :meth:inverse. The mean and std
vectors are registered buffers of shape (num_features,), so they are
part of the state_dict and move with .to()/dtype casts and the
:meth:~resdag.core.ESNModel.save_full round trip.
Statistics may be supplied at construction or estimated from data with
:meth:fit. Until either happens the layer initializes to mean = 0 and
std = 1 (an identity transform).
A small eps is added to std before division to avoid blow-up on
constant (zero-variance) features; the same eps-padded scale is used by
:meth:inverse, so inverse(forward(x)) reconstructs x to within
floating-point tolerance for every feature.
| PARAMETER | DESCRIPTION |
|---|---|
num_features
|
Size of the feature (last) dimension this layer standardizes.
TYPE:
|
mean
|
Initial per-feature mean of shape
TYPE:
|
std
|
Initial per-feature standard deviation of shape
TYPE:
|
eps
|
Numerical-stability floor added to
TYPE:
|
| ATTRIBUTE | DESCRIPTION |
|---|---|
mean |
Registered buffer holding the per-feature mean
TYPE:
|
std |
Registered buffer holding the per-feature standard deviation
TYPE:
|
Examples:
Fit statistics from a batch, then standardize and invert:
>>> import torch
>>> from resdag.layers.transforms import Standardize
>>>
>>> layer = Standardize(num_features=3)
>>> x = torch.randn(8, 100, 3) * 5.0 + 2.0 # (batch, time, features)
>>> layer.fit(x)
Standardize(num_features=3, eps=1e-08)
>>> z = layer(x) # ~zero mean, ~unit std per feature
>>> torch.allclose(layer.inverse(z), x, atol=1e-5)
True
Inside a composable pipeline (statistics travel with save_full/.to):
>>> import pytorch_symbolic as ps
>>> from resdag import ESNModel, ESNLayer, CGReadoutLayer, Standardize
>>>
>>> norm = Standardize(num_features=3)
>>> norm.fit(torch.randn(4, 200, 3))
Standardize(num_features=3, eps=1e-08)
>>> inp = ps.Input((200, 3))
>>> normed = norm(inp)
>>> reservoir = ESNLayer(100, feedback_size=3)(normed)
>>> readout = CGReadoutLayer(100, 3, name="output")(reservoir)
>>> model = ESNModel(inp, readout)
See Also
Power : Per-feature exponentiation. SelectiveExponentiation : Parity-selective exponentiation.
| PARAMETER | DESCRIPTION |
|---|---|
num_features
|
Size of the feature dimension to standardize.
TYPE:
|
mean
|
Initial per-feature mean of shape
TYPE:
|
std
|
Initial per-feature standard deviation of shape
TYPE:
|
eps
|
Stability floor added to
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Source code in src/resdag/layers/transforms/standardize.py
fit
¶
fit(input: Tensor) -> Standardize
Estimate per-feature mean and std from data and store them.
Statistics are reduced over every dimension except the last, so an
input of shape (batch, time, features) (or any number of leading
dims) yields per-feature vectors of length num_features. The
population standard deviation (unbiased=False) is used so a single
observation does not produce a nan. The buffers are updated in
place and inherit the device/dtype of input.
| PARAMETER | DESCRIPTION |
|---|---|
input
|
Data of shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Standardize
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the last dimension of input does not equal |
Source code in src/resdag/layers/transforms/standardize.py
forward
¶
Standardize the last dimension as (x - mean) / (std + eps).
| PARAMETER | DESCRIPTION |
|---|---|
input
|
Tensor of shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Same shape as input, centered and scaled per feature. Gradients flow through unchanged (the stored statistics are constants). |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the last dimension of input does not equal |
Source code in src/resdag/layers/transforms/standardize.py
inverse
¶
Invert the transform as x * (std + eps) + mean.
The exact algebraic inverse of :meth:forward, using the same
eps-padded scale so that inverse(forward(x)) == x up to
floating-point error. Use it to map standardized model outputs (e.g.
forecasts) back to the original data scale.
| PARAMETER | DESCRIPTION |
|---|---|
input
|
Standardized tensor of shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Same shape as input, mapped back to the original feature scale. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the last dimension of input does not equal |