pyFDN.train.losses package#

Submodules#

pyFDN.train.losses.base module#

The two loss families, and how they compose.

A training objective is a weighted sum of losses, built with + and *:

loss = pyFDN.FlatMagnitude() + 0.2 * pyFDN.Sparsity(A)

Losses come in two families, distinguished by what they read:

Both are called with the response, so the trainer needs no special case; a parameter loss simply ignores it.

class pyFDN.train.losses.base.Loss[source]#

Bases: ABC

Base class for training losses. Combine with + and scale with *.

check(model)[source]#

Preflight against the model it will train, before the first step.

Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.

Return type:

None

property name: str#

Short label, used as the key in pyFDN.TrainLog.loss_log.

terms()[source]#

Flatten into (weight, loss) leaves. The trainer registers these.

Return type:

list[tuple[float, Loss]]

class pyFDN.train.losses.base.ParameterLoss(ref)[source]#

Bases: Loss

A loss on one model parameter, referenced by ref.

Implement penalty(); the response argument is ignored.

property name: str#

Short label, used as the key in pyFDN.TrainLog.loss_log.

abstractmethod penalty(value)[source]#

The penalty on the parameter’s mapped value.

Return type:

Tensor

class pyFDN.train.losses.base.ResponseLoss[source]#

Bases: Loss

A loss on the impulse response. Implement __call__().

class pyFDN.train.losses.base.Scaled(weight, loss)[source]#

Bases: Loss

weight * loss. Built by Loss.__mul__(), rarely by hand.

check(model)[source]#

Preflight against the model it will train, before the first step.

Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.

Return type:

None

property name: str#

Short label, used as the key in pyFDN.TrainLog.loss_log.

terms()[source]#

Flatten into (weight, loss) leaves. The trainer registers these.

Return type:

list[tuple[float, Loss]]

class pyFDN.train.losses.base.Sum(losses)[source]#

Bases: Loss

A sum of losses. Built by Loss.__add__(), rarely by hand.

check(model)[source]#

Preflight against the model it will train, before the first step.

Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.

Return type:

None

property name: str#

Short label, used as the key in pyFDN.TrainLog.loss_log.

terms()[source]#

Flatten into (weight, loss) leaves. The trainer registers these.

Return type:

list[tuple[float, Loss]]

pyFDN.train.losses.distances module#

class pyFDN.train.losses.distances.AbsoluteError[source]#

Bases: Distance

L1: Elementwise absolute difference: |pred - target|.

class pyFDN.train.losses.distances.CircularDistance[source]#

Bases: Distance

Circular angular metric: 1 - cos(pred - target) bounded in [0, 2].

class pyFDN.train.losses.distances.CompressedEnergyDistance(power=0.5, floor_db=-100.0)[source]#

Bases: Distance

Reference-normalized, compressed squared error per cumulation direction.

Each (direction, channel, freq, frame) surface is normalized by the reference’s total energy (one constant per direction, so relative levels between paths survive) and compressed with power above a floor_db floor before comparing.

class pyFDN.train.losses.distances.ConstantTargetDistance(target_value)[source]#

Bases: Distance

Squared error of pred against a constant (ignores the reference).

For the targetless losses that fit to a constant rather than a reference IR, e.g. FlatMagnitude (|H| = target) and FlatSpectrogram (normalized spectra equal to 1).

class pyFDN.train.losses.distances.Distance[source]#

Bases: ABC

Base class for pairwise element comparison metrics.

class pyFDN.train.losses.distances.FlatnessRatioDistance(target_flatness=None)[source]#

Bases: Distance

Squared error of the spectral flatness, per channel.

The flatness of a magnitude spectrum \(|H|\) over its frequency axis (dim 0) is the ratio of its geometric to its arithmetic mean,

\[F = \frac{\exp\big(\overline{\log |H|}\big)}{\overline{|H|}},\]

1 for a perfectly flat spectrum and towards 0 for a peaky one. A silent spectrum (arithmetic mean at the numerical floor) has flatness 0, so it is never mistaken for a flat one.

Parameters:

target_flatness (float | None) – Fit the prediction’s flatness to this constant, in [0, 1], and ignore the reference feature. None (default) compares against the flatness of the reference feature instead.

static flatness(magnitude)[source]#

Geometric over arithmetic mean along dim 0, 0 for a silent spectrum.

Return type:

Tensor

class pyFDN.train.losses.distances.MaskedSquaredError(floor_db=-45.0)[source]#

Bases: Distance

Squared error over the entries where the reference is above floor_db.

The mask follows the reference, so only the part of the curve carrying signal counts; entries below the floor (noise) are ignored. Returns the masked entries only, as a 1-D tensor, so any plain reduction (e.g. Rms) averages over them alone.

class pyFDN.train.losses.distances.RelativeError(p=1.0)[source]#

Bases: Distance

Elementwise \(|pred - target|^p\) over the reference’s mean \(|target|^p\).

Followed by Mean this is the normalized \(L^p\) error \(\overline{|pred - target|^p} / \overline{|target|^p}\), a scale-free number whatever the feature’s units.

class pyFDN.train.losses.distances.SquaredError[source]#

Bases: Distance

L2: Elementwise squared difference: (pred - target)^2.

pyFDN.train.losses.features module#

class pyFDN.train.losses.features.CumulativeEnergySurface(window=1024, hop=None, directions=('descending',))[source]#

Bases: Feature

Doubly-cumulated short-time power, one surface per direction.

Power is integrated backwards in time, then along frequency in each of directions ("descending" and/or "ascending"). Returns a (n_directions, n_channels, n_freq, n_frames) tensor.

class pyFDN.train.losses.features.EchoDensityProfile(win_duration=0.02, hop=None, kappa=(100.0, 100000.0))[source]#

Bases: Feature

Differentiable normalized echo density profile (Abel & Huang).

In a Hann window of win_duration seconds around each analysis point, the fraction of samples whose magnitude exceeds the window’s weighted RMS \(\sigma\), normalized by \(\operatorname{erfc}(1/\sqrt{2})\), the fraction expected of Gaussian noise – so the profile reaches about 1 once the reverberation is diffuse. The hard threshold is replaced by \(\operatorname{sigmoid}(\kappa(|h| - \sigma))\) so gradients flow; \(\kappa\) rises linearly from kappa[0] to kappa[1] over the response, to stay sharp as the decaying signal gets smaller.

\(\kappa\) acts on absolute amplitude, so the profile depends on level: below about -60 dB the sigmoid softens and the profile reads high (noise at -80 dB gives about 1.16). Compare responses normalized to the same level.

Returns a (n_channels, n_frames) tensor, one point every hop samples.

class pyFDN.train.losses.features.EnergyDecayCurve(window=4096, hop=None, bands=None)[source]#

Bases: Feature

Octave-band normalized Schroeder decay curves in dB.

Short-time power per octave band, backward-integrated (Schroeder) and normalized to each band’s own value at t=0.

class pyFDN.train.losses.features.Feature[source]#

Bases: ABC

Base class for extracting a representation from an impulse response.

class pyFDN.train.losses.features.Magnitude(channels='none')[source]#

Bases: Feature

Frequency-domain magnitude spectrum via rfft.

class pyFDN.train.losses.features.MelEnergyDecayRelief(n_fft=512, win_length=256, hop_length=128, n_mels=64, top_db=80.0)[source]#

Bases: Feature

Mel-band energy decay relief in dB, one relief per channel.

The mel power spectrogram (torchaudio’s MelSpectrogram, power 2), backward-integrated over frames (Schroeder) and converted to dB with a floor top_db below the maximum over all channels (as torchaudio’s AmplitudeToDB). Returns a (n_channels, n_mels, n_frames) tensor.

class pyFDN.train.losses.features.MelMagnitude(n_mels=128, f_min=0.0, f_max=None, channels='none')[source]#

Bases: Feature

Mel-scaled magnitude spectrum via rfft + triangular filterbank.

Both the time-domain waveform and frequency-domain magnitude are computed via FFT and reduced to n_mels mel bands using torchaudio’s triangular mel filterbank, weighting the fit towards low frequencies where mel spacing is finer.

Parameters:
  • n_mels (int) – Number of mel bands.

  • f_min (float) – Mel filterbank frequency range in Hz. f_max defaults to Nyquist.

  • f_max (float | None) – Mel filterbank frequency range in Hz. f_max defaults to Nyquist.

  • channels (str) – How the output channels are combined. "none" (default) keeps each input/output pair separate.

class pyFDN.train.losses.features.PeakEnvelope(release_time=0.00028)[source]#

Bases: Feature

Smoothed peak envelope of the squared response, one per channel.

A peak follower on \(h^2\), \(a[n] = \max(h[n]^2, \alpha\, a[n-1])\) with \(\alpha = e^{-1/(f_s T)}\) for a release time \(T\), then zero-phase low-pass filtered (a least-squares FIR, passband to 3.2 kHz, stopband from 4.8 kHz, about 1 ms long at any rate) and floored at zero. Returns a (n_channels, n_samples) tensor.

class pyFDN.train.losses.features.Phase(channels='none')[source]#

Bases: Feature

Frequency-domain phase spectrum via rfft, in \((-\pi, \pi]\).

With channels="sum" or "mean" the complex spectra of the output channels are combined first and the phase is taken of the result, i.e. the phase of the summed response (the phase of the mean is the same). Summing the phase angles themselves would not be the phase of anything.

The whole-response phase of a reverberant IR varies extremely fast with frequency and is undefined where the magnitude vanishes, yet every bin counts equally; a phase loss built on it is best reserved for short or early responses.

class pyFDN.train.losses.features.PhaseSpectrogramFeature(nfft=(256, 512, 1024, 2048), overlap=0.75)[source]#

Bases: Feature

Multi-resolution STFT phase (circular distance ready).

For each window size, extracts STFT phase and stacks across windows.

Parameters:
  • nfft (tuple[int, ...]) – STFT window sizes.

  • overlap (float) – Fractional overlap between frames.

class pyFDN.train.losses.features.SpectrogramFeature(nfft=(256, 512, 1024, 2048), overlap=0.75)[source]#

Bases: Feature

Multi-resolution Welch spectrogram (smoothed magnitude across windows).

For each window size in nfft, computes the STFT magnitude, averages over frames (Welch estimate), normalizes by per-frequency mean, and returns. This feature is resolution-independent (unlike single-FFT magnitude).

Parameters:
  • nfft (tuple[int, ...]) – STFT window sizes, each no longer than the model’s nfft.

  • overlap (float) – Fractional overlap between frames (0.75 -> hop of quarter window).

class pyFDN.train.losses.features.Waveform[source]#

Bases: Feature

Raw time-domain waveform feature.

pyFDN.train.losses.match module#

class pyFDN.train.losses.match.Match(target, feature, distance=<pyFDN.train.losses.distances.SquaredError object>, reduction=<pyFDN.train.losses.reductions.Mean object>)[source]#

Bases: ResponseLoss

Compares a model’s impulse response to a target using three steps:

1. Feature: Extracts representations from both the model response and the target (e.g., raw waveform, magnitude spectrum, or phase). 2. Distance: Computes the differences between the extracted features (e.g., squared error or circular phase difference). 3. Reduction: Aggregates the elementwise differences into a scalar loss tensor (e.g., mean or sum).

The reference target is aligned in length, shape, device, and dtype using _CachedTarget, and its feature is computed once and cached until the loss is called on a response of a different shape, device, dtype or sample rate.

Without a target (target=None) the distance is called with an all-zeros tensor in place of the reference feature; targetless losses use a distance that compares against a constant of its own and ignores it.

Parameters:

pyFDN.train.losses.parameter module#

Costs on a model parameter, rather than on its response.

Each takes a ParamRef naming the parameter it acts on, so it works on any FDN structure:

A = pyFDN.param(model, "feedback")
loss = pyFDN.FlatMagnitude() + 0.2 * pyFDN.Sparsity(A)
class pyFDN.train.losses.parameter.L1(ref)[source]#

Bases: ParameterLoss

Mean absolute value of a parameter – pushes it toward sparse.

penalty(value)[source]#

The penalty on the parameter’s mapped value.

Return type:

Tensor

class pyFDN.train.losses.parameter.L2(ref)[source]#

Bases: ParameterLoss

Mean squared value of a parameter – plain weight decay.

penalty(value)[source]#

The penalty on the parameter’s mapped value.

Return type:

Tensor

class pyFDN.train.losses.parameter.Sparsity(ref)[source]#

Bases: ParameterLoss

Density penalty on a square mixing matrix, after Optimizing Tiny Colorless FDNs (Dal Santo et al.).

Rewards a dense matrix – 0 when \(|A|\) is maximally dense (its entries all \(1/\sqrt{N}\), i.e. best mixing) and 1 when fully sparse. Registered with a positive weight it therefore pushes the feedback matrix away from the sparse, poorly-mixing corners of SO(N) that a magnitude-only objective is otherwise happy to sit in.

Parameters:

ref (ParamRef) – The matrix to penalize, e.g. pyFDN.param(model, "feedback"). Must be square.

penalty(value)[source]#

The penalty on the parameter’s mapped value.

Return type:

Tensor

pyFDN.train.losses.reductions module#

class pyFDN.train.losses.reductions.Mean[source]#

Bases: Reduction

Arithmetic mean reduction.

class pyFDN.train.losses.reductions.MeanPerGroup(feature=None)[source]#

Bases: Reduction

Mean averaged per first-dimension group, then across groups.

Each group is normalized by its actual element count, giving equal weight to each group regardless of its size (unlike Mean() which weights by total element count).

Multi-resolution features (SpectrogramFeature, PhaseSpectrogramFeature) pad every resolution to a common shape and record each resolution’s number of real (unpadded) entries in _group_counts on every call. Pass that feature here so each group is divided by its real count; the padding must then contribute zero distance (it does for the distances those features are used with). Without such a feature, each group is averaged over its full, padded size.

class pyFDN.train.losses.reductions.MeanRmsOverGroups[source]#

Bases: Reduction

Mean of per-group RMS values, groups split along dim 0.

class pyFDN.train.losses.reductions.Reduction[source]#

Bases: ABC

Base class for aggregation strategies.

class pyFDN.train.losses.reductions.Rms[source]#

Bases: Reduction

Root-mean-square of squared errors: sqrt(mean(x)) over all entries.

class pyFDN.train.losses.reductions.Sum[source]#

Bases: Reduction

Sum reduction.

pyFDN.train.losses.spectral module#

Losses on the spectrum of the impulse response.

class pyFDN.train.losses.spectral.AsymmetricFlatMagnitude(*, peak_power=4.0)[source]#

Bases: ResponseLoss

Flatness that punishes peaks far harder than dips – colorless.

The asymmetric sibling of FlatMagnitude: a resonant peak rings audibly at its own pitch while a dip of the same size is largely inaudible, so this measures \(|H|\) against the response’s own RMS and raises the two sides of the deviation to different powers,

\[d[f] = \frac{|H[f]|}{\sqrt{\langle |H|^2 \rangle_f}} - 1, \qquad \mathcal{L} = \Big\langle \big(d^{+}\big)^{p} + \big(d^{-}\big)^{2} \Big\rangle_f,\]

with peak_power \(p \ge 2\). Flat stays the unique minimum at every peak_power and the loss is gain-invariant (add Energy to pin the level). The exponent, not a weight, is what makes it bite, and the linear magnitude (not dB) is deliberate; see the design note for why, and for what a higher exponent costs in convergence.

Parameters:

peak_power (float) – Exponent on the peak side; dips are always quadratic. Must be at least 2 (the symmetric-shape reference, still peak-biased since a peak is unbounded and a dip is not). 4 is the default; 6 is slower but steadier. The advantage over FlatMagnitude is not unconditional – measure your own case. Loss values are not comparable across peak_power or with FlatMagnitude; compare the responses.

Notes

A lossless FDN has every pole exactly on the unit circle, where the frequency-domain evaluation breaks down; check() warns if the model was built without the alias_decay_db that avoids it.

check(model)[source]#

Preflight against the model it will train, before the first step.

Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.

Return type:

None

class pyFDN.train.losses.spectral.FlatMagnitude(target=1.0, *, channels='sum')[source]#

Bases: Match

Mean squared error of \(|H|\) against a flat target – colorless.

Fits the magnitude spectrum of the (rectangularly truncated) impulse response to a constant, after Differentiable FDNs for Colorless Reverberation (Dal Santo et al.). Its frequency resolution is the model’s nfft, which makes the fit sensitive to it; see the design note and FlatSpectrogram for a resolution-independent alternative.

Parameters:
  • target (float) – The flat magnitude to fit. The default of 1 matches pyFDN.build_fdn()’s normalized input/output gains, which put the initial \(|H|\) near unity.

  • channels (Literal['sum', 'mean', 'none']) – How the output channels are combined before the comparison. "sum" (default) reproduces FLAMO’s mse_loss convention. "none" fits each input/output pair to flat on its own, the well-posed choice for a multi-output FDN.

Notes

The optimization crosses long plateaus on this objective; train_fdn’s default patience=10 stops inside one. Raise it (~100) for a converged fit. A lossless FDN has every pole exactly on the unit circle, where the frequency-domain evaluation breaks down; check() warns if the model was built without the alias_decay_db that avoids it.

check(model)[source]#

Preflight against the model it will train, before the first step.

Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.

Return type:

None

class pyFDN.train.losses.spectral.FlatSpectrogram(*, nfft=(256, 512, 1024, 2048), overlap=0.75)[source]#

Bases: Match

Flatness measured on multi-resolution smoothed spectra – colorless.

The multi-scale sibling of FlatMagnitude, and the one whose frequency resolution is its own business rather than the model’s. For each analysis window \(n\), the short-time magnitudes are averaged over frames into a smoothed (Welch) spectral estimate, which is then normalized by its own mean and fitted to flat:

\[P_n[f] = \sqrt{\big\langle |S_n[t, f]|^2 \big\rangle_t}, \qquad \mathcal{L} = \frac{1}{|W|} \sum_{n \in W} \Big\langle \Big( \frac{P_n[f]}{\langle P_n \rangle_f} - 1 \Big)^2 \Big\rangle_f\]

A short window smooths heavily and constrains the broad spectral tilt; a long one resolves individual modes. Because each scale is normalized by its own mean, the loss is invariant to overall gain – it fits spectral shape only, and needs no assumption that \(|H| \approx 1\). Averaging over frames before measuring flatness is the whole design (per-frame flatness rewards an impulsive, comb-filtered IR); see the design note.

Parameters:
  • nfft (tuple[int, ...]) – STFT window sizes, each no longer than the model’s nfft. The default spans a factor of eight, which is what makes the objective multi-scale; one window alone is just a smoothed FlatMagnitude.

  • overlap (float) – Fractional overlap between frames (0.75 -> hop of a quarter window).

Notes

The loss value is not comparable with FlatMagnitude’s (the smoothing removes most of the mode-to-mode fluctuation), but it is far more stable against the model’s nfft.

class pyFDN.train.losses.spectral.MatchMagnitude(target, *, channels='none')[source]#

Bases: Match

Mean squared error of \(|H|\) against a reference impulse response.

The magnitude-only sibling of MatchImpulseResponse: fits the spectral envelope while ignoring phase.

Parameters:
  • target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

  • channels (Literal['sum', 'mean', 'none']) – How the output channels are combined before the comparison. "none" (default) fits each input/output pair on its own.

class pyFDN.train.losses.spectral.MatchMelMagnitude(target, *, n_mels=128, f_min=0.0, f_max=None, channels='none')[source]#

Bases: Match

Mel-scaled mean squared error of \(|H|\) against a reference.

The mel sibling of MatchMagnitude: both spectra are reduced to n_mels mel bands (via torchaudio’s triangular filterbank) before comparing, weighting the fit towards the low frequencies where mel spacing resolves more finely, at the cost of resolution in the high ones.

Parameters:
  • target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

  • n_mels (int) – Number of mel bands. Default 128.

  • f_min (float) – Mel filterbank frequency range in Hz. f_max defaults to Nyquist.

  • f_max (float | None) – Mel filterbank frequency range in Hz. f_max defaults to Nyquist.

  • channels (Literal['sum', 'mean', 'none']) – How the output channels are combined before the comparison. "none" (default) fits each input/output pair on its own, the well-posed choice for a multi-output FDN.

class pyFDN.train.losses.spectral.MatchMelSpectrogram(target, *, nfft=(256, 512, 1024), device=None, **kwargs)[source]#

Bases: _FlamoSpectrogramLoss

Mel-scaled multi-resolution STFT distance (FLAMO’s mel_mss_loss).

class pyFDN.train.losses.spectral.MatchPhase(target, *, channels='none')[source]#

Bases: Match

Circular distance of \(\angle H\) against a reference impulse response.

The per-bin distance is \(1 - \cos(\Delta\phi)\), circular so the \(\pm\pi\) wrap costs nothing extra.

Parameters:
  • target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

  • channels (Literal['sum', 'mean', 'none']) – "none" (default) compares each input/output pair on its own; "sum" and "mean" compare the phase of the summed output channels (both give the same phase).

Notes

The whole-response phase of a reverberant IR varies extremely fast with frequency and is undefined where \(|H|\) vanishes, yet every bin counts equally, so the loss landscape is very rough for long responses. Prefer it for short filters or the early part of a response, or use MatchPhaseSpectrogram.

class pyFDN.train.losses.spectral.MatchPhaseSpectrogram(target, *, nfft=(256, 512, 1024, 2048), overlap=0.75)[source]#

Bases: Match

Circular distance of the STFT phase against a reference, multi-resolution.

The windowed sibling of MatchPhase: rather than a single FFT over the whole response, phase is compared frame by frame at each of nfft’s window sizes and averaged across scales – what MatchSpectrogram is to MatchMagnitude, but for phase. The per-bin distance is \(1 - \cos(\Delta\phi)\), circular so the \(\pm\pi\) wrap costs nothing extra.

Parameters:
  • target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

  • nfft (tuple[int, ...]) – STFT window sizes, each no longer than the model’s nfft.

  • overlap (float) – Fractional overlap between frames (0.75 -> hop of a quarter window).

class pyFDN.train.losses.spectral.MatchSpectrogram(target, *, nfft=(256, 512, 1024), device=None, **kwargs)[source]#

Bases: _FlamoSpectrogramLoss

Multi-resolution STFT distance to a reference impulse response.

Wraps FLAMO’s mss_loss. nfft is the tuple of STFT window sizes; the loss’s own frequency resolution therefore comes from these, independent of the model’s nfft.

class pyFDN.train.losses.spectral.SpectralFlatness(target=1.0, *, channels='none')[source]#

Bases: Match

Spectral flatness measure of \(|H|\) fit to flat – colorless.

The ratio of the geometric to the arithmetic mean of \(|H|\), the classic flatness measure: 1 for a perfectly flat spectrum, towards 0 for a peaky one. Unlike FlatMagnitude, both means scale the same way with an overall gain, so the loss is gain-invariant without needing to assume \(|H| \approx 1\). A silent response has flatness 0, the opposite end from flat.

Parameters:
  • target (float) – The flatness to fit, in [0, 1] (1 is perfectly flat). Default 1.0.

  • channels (Literal['sum', 'mean', 'none']) – How the output channels are combined before the flatness ratio is computed. "none" (default) scores each input/output path on its own flatness.

Notes

A lossless FDN has every pole exactly on the unit circle, where the frequency-domain evaluation breaks down; check() warns if the model was built without the alias_decay_db that avoids it.

check(model)[source]#

Preflight against the model it will train, before the first step.

Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.

Return type:

None

pyFDN.train.losses.temporal module#

Losses on the impulse response in the time domain.

class pyFDN.train.losses.temporal.Energy(target=1.0)[source]#

Bases: ResponseLoss

Squared deviation of the response’s total energy from target.

A blunt level anchor: useful next to a magnitude-only loss to stop an objective that is invariant to overall gain from drifting.

class pyFDN.train.losses.temporal.MatchCumulativeEnergy(target, *, window=1024, hop=None, power=0.5, floor_db=-100.0, frequency='descending')[source]#

Bases: Match

Doubly-cumulated energy against a reference – decay and colour, no bands.

Takes the short-time power spectrum of both signals and integrates it twice, backwards in time and downwards in frequency:

\[E[f, t] = \sum_{t' \ge t} \; \sum_{f' \ge f} \big| S[f', t'] \big|^2\]

so \(E[f, t]\) is the energy still to come after time \(t\) in the band above \(f\), and \(E[0, 0]\) is the total energy. The loss is the RMS difference of the two surfaces after a compressive power.

Cumulating twice carries both things a fit needs: read down the \(t = 0\) edge and you have the integrated spectrum (the colour), read across the \(f = 0\) edge and you have the full-band decay, and the interior ties them together band by band – without the arbitrary quantization of octave bands. The compressive power (rather than a logarithm) keeps the surface’s six-orders-of-magnitude dynamic range visible to the loss while staying bounded. See the design note for the reasoning behind the defaults.

Parameters:
  • target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

  • window (int) – STFT window and hop in samples. Unlike MatchEnergyDecay this loss needs no window long enough to resolve an octave – it never splits into octaves – so the default is short.

  • hop (int | None) – STFT window and hop in samples. Unlike MatchEnergyDecay this loss needs no window long enough to resolve an octave – it never splits into octaves – so the default is short.

  • power (float) – The compression exponent \(p \in (0, 1]\) applied to the normalized surface. 1 is no compression (raw energies), 0.5 the default, 0.25 stronger.

  • floor_db (float) – Hard floor on the normalized surface, in energy dB below the reference’s total energy. It bounds the gradient of the compression near zero and keeps the fit off the numerical floor of the render; clamp means no gradient flows from anything below it.

  • frequency (str) – Which way the frequency cumulation runs, and with it the loss’s balance between the ends of the spectrum. The default "descending" (the plain reading of “energy above this frequency”) gives a low band little gradient; "both" scores both directions and averages, and is what to reach for when the fit has to find a decay it was not given. See the design note for the comparison.

Notes

The surface is normalized by the reference’s total energy, one constant – not each surface by its own – so a level error is a genuine error of the loss rather than something it is blind to. Fitting shape only, with the level left to another term, is the one thing this loss deliberately does not do.

check(model)[source]#

Preflight against the model it will train, before the first step.

Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.

Return type:

None

class pyFDN.train.losses.temporal.MatchEchoDensity(target, *, win_duration=0.02, hop=None, kappa=(100.0, 100000.0))[source]#

Bases: Match

Mean squared error of the differentiable echo density profile.

Scores how fast the response builds up to a diffuse, Gaussian-like tail – the one property of a reverberator that neither a spectrogram nor an energy decay sees. See EchoDensityProfile.

Contributed by Riccardo Giampiccolo.

Parameters:
  • target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

  • win_duration (float) – Analysis window in seconds.

  • hop (int | None) – Samples between profile points; defaults to a quarter window. 1 is the textbook profile, at a memory cost of nfft * window per channel.

  • kappa (float | tuple[float, float]) – Sigmoid slope, or its linear ramp from start to end of the response.

class pyFDN.train.losses.temporal.MatchEnergyDecay(target, *, window=4096, hop=None, bands=None, floor_db=-45.0)[source]#

Bases: Match

RMS dB error of the octave-band energy decay curves against a reference.

The loss that sees the decay – and the one to add when the decay is a trained parameter (a AttenuationFilter in the post_delay hook). A magnitude spectrogram distance is not a substitute for fitting a decay; see the design note.

Each band’s Schroeder curve is normalized to its own value at \(t=0\), so the loss reads the decay and nothing else – level is left to whatever else is in the objective.

The value is in dB, which puts it many orders of magnitude above a spectrogram distance: weight accordingly, and read TrainLog.loss_log (which stores every term unweighted) to see what each term is worth.

Parameters:
  • target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

  • window (int) – STFT window and hop in samples for the band energies. The default 4096 (85 ms at 48 kHz) resolves the 63 Hz octave; shorter windows leave the low bands with too few bins to be worth reading.

  • hop (int | None) – STFT window and hop in samples for the band energies. The default 4096 (85 ms at 48 kHz) resolves the 63 Hz octave; shorter windows leave the low bands with too few bins to be worth reading.

  • bands (Any) – Band edges in Hz; defaults to the octave bands from 44 Hz to 11.3 kHz.

  • floor_db (float) – Only the part of each band’s curve where the target is still above this level is compared. Past it a measurement is reading its own noise floor, and fitting that would fit the microphone.

check(model)[source]#

Preflight against the model it will train, before the first step.

Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.

Return type:

None

class pyFDN.train.losses.temporal.MatchEnvelope(target, *, release_time=0.00028, p=2.0)[source]#

Bases: Match

Normalized \(L^p\) error of the smoothed peak envelope.

\(\overline{|e - \hat e|^p} / \overline{|\hat e|^p}\) between the envelopes of prediction and reference, where the envelope is a fast peak follower on \(h^2\), zero-phase low-passed. Sees the arrival and level of individual reflections that an energy decay integrates away. See PeakEnvelope.

Contributed by Riccardo Giampiccolo.

Parameters:
  • target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

  • release_time (float) – Release time of the peak follower in seconds.

  • p (float) – Exponent of the error.

class pyFDN.train.losses.temporal.MatchImpulseResponse(target)[source]#

Bases: Match

Mean squared error against a reference impulse response, sample by sample.

The strictest of the matching losses – it fits phase as well as magnitude, which for a reverberator is usually more than you want. Reach for MatchSpectrogram unless you are fitting an early part or a short filter.

Parameters:

target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

class pyFDN.train.losses.temporal.MatchMelEnergyDecayRelief(target, *, fft_sizes=(1024, 2048, 512), win_lengths=(600, 1200, 240), hop_lengths=(120, 240, 50), n_mels=64, p=1.0)[source]#

Bases: ResponseLoss

Multi-resolution mel energy decay relief (EDR) distance to a reference.

At each STFT resolution, the mel power spectrogram of both signals is backward-integrated over time and taken to dB (with an 80 dB floor), and the normalized \(L^p\) error \(\overline{|E - \hat E|^p} / \overline{|\hat E|^p}\) is averaged over the resolutions. Decay and colour at once, like MatchCumulativeEnergy, but banded on the mel scale.

Contributed by Riccardo Giampiccolo. The defaults are his, tuned at 16 kHz; at 48 kHz the same STFT sizes span a third of the time.

Parameters:
  • target (Any) – Reference IR, shape (n_samples,), (n_samples, n_out) or (n_samples, n_out, n_in). Zero-padded or truncated to the model’s nfft.

  • fft_sizes (tuple[int, ...]) – One STFT resolution per entry; all three the same length.

  • win_lengths (tuple[int, ...]) – One STFT resolution per entry; all three the same length.

  • hop_lengths (tuple[int, ...]) – One STFT resolution per entry; all three the same length.

  • n_mels (int) – Number of mel bands.

  • p (float) – Exponent of the error.

Module contents#

Training losses for FDNs – see pyFDN.train.losses.base for how they compose into an objective.