Embeddings¶
Declarative embedding pipelines for observation processing.
Overview¶
The falcon.embeddings package provides tools for building observation embedding
networks via declarative YAML configuration. Embeddings map raw observations to
lower-dimensional summary statistics before they enter the estimator.
Declarative Configuration¶
Embedding networks are defined in YAML using the _target_ / _input_ system:
_target_: Import path of thenn.Moduleclass to instantiate_input_: List of observation node names (or nested sub-embeddings) that feed into this module
Basic Embedding¶
Multi-Input Embedding¶
Nested Pipeline¶
Sub-embeddings can be nested arbitrarily deep. Each _input_ entry can itself
be a _target_ / _input_ block:
embedding:
_target_: model.Concatenate
_input_:
- _target_: timm.create_model
model_name: resnet18
pretrained: true
num_classes: 0
_input_:
_target_: model.Unsqueeze
_input_: [image]
- _target_: torch.nn.Linear
in_features: 64
out_features: 32
_input_: [metadata]
Built-in Utilities¶
Normalization¶
LazyOnlineNorm¶
Online normalization with lazy initialization and optional momentum adaptation. Normalizes inputs to zero mean and unit variance using exponential moving averages.
DiagonalWhitener¶
Diagonal whitening with optional Hartley transform preprocessing and
momentum-based running statistics. dim is required and must equal
the number of features in the flattened input.
embedding:
_target_: falcon.embeddings.DiagonalWhitener
_input_: [x]
dim: 64 # must match flattened input size
ToeplitzWhitener¶
Whitening for 1D signals with Toeplitz-structured covariance (e.g. stationary time series). Uses a Hartley-space EMA to track and whiten correlations efficiently without storing the full covariance matrix.
hartley_transform¶
A standalone function for computing the discrete Hartley transform, useful as a preprocessing step.
Dimensionality Reduction¶
DynamicSVD¶
Streaming SVD with Procrustes-stabilized output and optional whitening. Maintains an eigenbasis via momentum-blended updates; Procrustes alignment keeps output coefficients stable across updates, making it safe to feed directly into a neural network.
embedding:
_target_: falcon.embeddings.DynamicSVD
_input_: [x]
n_components: 32 # output dimension
buffer_size: 128 # samples accumulated before each SVD update (default: 4 × n_components)
momentum: 0.1 # blending weight for each SVD update
Class Reference¶
instantiate_embedding
¶
Instantiate embedding pipeline from config.
When embedding_config is None, returns a pass-through embedding that
concatenates all condition tensors along the last dimension.
Source code in src/falcon/embeddings/builder.py
EmbeddingWrapper
¶
Bases: Module
Sequential execution of modules with shared data dictionary.
Source code in src/falcon/embeddings/builder.py
DiagonalWhitener
¶
Bases: Module
dim: number of features (last dimension of x) momentum: how much of the new batch stats to use (PyTorch-style) eps: small constant for numerical stability use_fourier: if True, apply Hartley transform before whitening
Source code in src/falcon/embeddings/norms.py
update
¶
Update running mean and variance from current batch x: Tensor of shape (batch_size, dim)
Source code in src/falcon/embeddings/norms.py
__call__
¶
Apply whitening: (x - mean) / std If use_fourier, whitening happens in Hartley space and is transformed back.
Source code in src/falcon/embeddings/norms.py
ToeplitzWhitener
¶
Bases: Module
Whitener for 1D time series assuming stationary (Toeplitz) noise covariance.
Estimates per-frequency variance via EMA in Hartley space and whitens by dividing by the estimated std. Noise is assumed zero-mean.
update(noise) — update EMA variance from a batch of noise samples call(x) — whiten x (Hartley → divide by std → inverse Hartley)
Source code in src/falcon/embeddings/norms.py
update
¶
Update EMA variance from noise samples of shape (batch_size, T).
Source code in src/falcon/embeddings/norms.py
__call__
¶
Whiten x of shape (batch_size, T).
hartley_transform
¶
Hartley transform: H(x) = Re(FFT(x)) - Im(FFT(x)) It is its own inverse: H(H(x)) = x
DynamicSVD
¶
DynamicSVD(n_components=10, buffer_size=None, momentum=0.1, wiener=True, whitener=None, fit_on_signal=False)
Bases: Module
Streaming SVD with Procrustes-stabilized output and optional whitening.
Maintains eigenbasis (V, Λ) via momentum-blended, eigenvalue-scaled SVD updates. Procrustes alignment ensures output coefficients have stable meaning across updates — critical when feeding into a neural network.
Optionally wraps a whitener (e.g. DiagonalWhitener). When a whitener is provided, update(x, signal) computes noise = x - signal and updates the whitener from it, so the whitener normalizes the noise to unit variance. forward() applies whitening at inference without updating statistics. Without a whitener the same noise estimate is reduced to a single scalar variance (see σ² below), which is the right trade when the noise is white but not worth a per-feature model.
Inputs with more than two dimensions are flattened to (batch_size, D) internally, so image-shaped data can be fed in directly.
The signal stream (a training-only scaffold; see falcon graph configs)
has two independent uses:
- it is always the noise estimate for the whitener, as above;
- with fit_on_signal=True it is also the stream the eigenbasis is fitted
from, while projections in forward() stay on x.
Update (when buffer full): U = [ √(1-α) · diag(√Λ_old) · V_old ; √(α/M) · X_white ] SVD(U) → V_new, Λ_new = S² Procrustes(V_new, R_old @ V_old) → R_new
forward(x) → stable k-dim coefficients: 1. c = X_white @ V.T (project onto eigenbasis) 2. c *= λ/(λ+σ²) (Wiener filter, diagonal) 3. c /= √λ (express in units of the coefficient's own standard deviation) 4. c_out = c @ R.T (rotate to stable frame)
Steps 2-3 are gated together by wiener=True and applied as the single factor √λ/(λ+σ²). The resulting coefficient has standard deviation √(λ/(λ+σ²)) = √(SNR/(SNR+1)): order 1 for directions measured well above the noise, shrinking to 0 for those buried in it. So the output is SNR-weighted, not flatly unit-variance -- unit scale is only the λ >> σ² asymptote, and suppressing the rest is the point.
wiener=False returns the raw projection instead, whose scale is √(λ+σ²) and therefore varies by orders of magnitude across components. That is plain PCA scores; it is rarely what you want to hand to a network.
reconstruct() gates on the same flag but applies only step 2, since it returns to data space where step 3's rescaling would be wrong.
σ² is the noise variance in the units the eigenvalues are measured in: - with a whitener, the noise is normalized per feature, so σ² = 1; - without one, a single scalar is estimated from x - signal in update() under a white-noise assumption, so the denominator stays commensurate with λ instead of comparing raw signal power against an arbitrary 1; - with neither a whitener nor a signal, nothing can be estimated and it falls back to 1.
Step 2 is the textbook Wiener gain only when λ is signal power, i.e. when fit_on_signal=True. Fitting on x instead gives λ ≈ λ_signal + σ², so the gain becomes (λ_s+σ²)/(λ_s+2σ²) >= 1/2 and a noise-only direction is passed through at half amplitude rather than suppressed.
Source code in src/falcon/embeddings/svd.py
update
¶
Accumulate a batch; trigger SVD update when buffer is full.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
Input data, shape (batch_size, D) or (batch_size, ...). |
required |
signal
|
Optional[Tensor]
|
True signal estimate, same shape as x. When provided, noise = x - signal updates the whitener if one is attached, or the scalar noise-variance estimate if not. With fit_on_signal=True it is additionally what the eigenbasis is fitted from, in place of x. |
None
|
Source code in src/falcon/embeddings/svd.py
forward
¶
Project to stable k-dimensional coefficients.
When in training mode, automatically accumulates x into the buffer and triggers an SVD update when the buffer is full. This makes DynamicSVD usable as a drop-in nn.Module embedding without a separate update() call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
Input data, shape (batch_size, D). |
required |
signal
|
Optional[Tensor]
|
Passed through to update() during training, where it feeds the whitener's noise estimate and, with fit_on_signal=True, the eigenbasis fit. Never used for the projection itself, so it is not needed at inference. |
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Coefficients of shape (batch_size, k). With wiener=True each has |
Tensor
|
standard deviation √(SNR/(SNR+1)) -- order 1 where the direction is |
Tensor
|
well measured, near 0 where it is noise-dominated. Returns random |
Tensor
|
noise before the first SVD update (avoids all-zero gradients). |
Source code in src/falcon/embeddings/svd.py
reconstruct
¶
Wiener-filter and reconstruct in whitened D-dimensional space.