GWForge.fisher.stepsize

Choosing finite-difference steps, and measuring what limits them.

Two things bound a usable step from opposite sides. Too large and the difference stops being local; too small and it drowns in the waveform model’s own numerical noise. The constants here are measured rather than guessed, and the measurements are recorded beside them because they are the whole justification.

calibrate_step() handles the upper bound by targeting a fixed fractional change in the waveform. estimate_noise() handles the lower one, using the ECNoise procedure of Moré and Wild, Estimating Computational Noise, SIAM J. Sci. Comput. 33(3):1292–1314 (2011): a smooth function’s k-th finite differences shrink like \(h^k\) while noise does not shrink at all, so the level at which the difference table stops changing is the noise.

Attributes

Functions

estimate_noise(values)

Estimate the numerical noise of a computed function from its values.

sample_around(evaluate, half_width[, points])

Evaluate a function at equally spaced points centred on zero.

calibrate_step(evaluate, name, value, initial[, target])

Rescale a step so one displacement changes the waveform by target.

Module Contents

GWForge.fisher.stepsize.TARGET_FRACTIONAL_CHANGE = 0.01[source]
GWForge.fisher.stepsize.NOISE_MARGIN = 100.0[source]
GWForge.fisher.stepsize.NOISE_SAMPLE_POINTS = 9[source]
GWForge.fisher.stepsize.estimate_noise(values)[source]

Estimate the numerical noise of a computed function from its values.

Implements the ECNoise procedure of Moré and Wild, Estimating Computational Noise, SIAM J. Sci. Comput. 33(3):1292–1314 (2011), which is the standard way to measure how much of a simulated function’s output is numerical scatter rather than signal.

The idea is that for a smooth \(f\) the \(k\)-th finite difference over a spacing \(h\) shrinks like \(h^k\), while iid noise does not shrink at all – it merely gets amplified by a known binomial factor. So build the difference table and look for the level at which the estimates stop changing:

\[\sigma_k^2 = \frac{\gamma_k}{m + 1 - k} \sum_i \big(\Delta^k f_i\big)^2, \qquad \gamma_k = \frac{(k!)^2}{(2k)!}.\]
Parameters:

values (array_like) – Function values at equally spaced points. At least four, preferably NOISE_SAMPLE_POINTS. The spacing itself never enters.

Returns:

(noise, status). status is 1 when the noise was detected, 2 when the spacing was too small (half the first differences came out exactly zero – widen it by ~100), and 3 when it was too large (no level settled, or the values already differ in their first digit – narrow it by ~100). noise is 0.0 unless status is 1.

Return type:

tuple

GWForge.fisher.stepsize.sample_around(evaluate, half_width, points=NOISE_SAMPLE_POINTS)[source]

Evaluate a function at equally spaced points centred on zero.

The spacing convention follows Moré and Wild’s driver: half_width is the half-width of the span, not the gap between points.

Parameters:
  • evaluate (callable) – evaluate(offset) -> float.

  • half_width (float) – Half the total span.

  • points (int) – Number of evaluations.

Return type:

numpy.ndarray

GWForge.fisher.stepsize.calibrate_step(evaluate, name, value, initial, target=TARGET_FRACTIONAL_CHANGE)[source]

Rescale a step so one displacement changes the waveform by target.

The fractional change is linear in the step over many decades, so a single probe fixes the scale; a second probe confirms it and catches the non-linear case. See TARGET_FRACTIONAL_CHANGE for why the target is what it is.

Parameters:
  • evaluate (callable) – evaluate(step) -> float returning \(\lVert\Delta h\rVert / \lVert h\rVert\) for that displacement.

  • name (str) – Parameter name, used for the bound check and for logging.

  • value (float) – Fiducial value.

  • initial (float) – Starting step.

  • target (float) – Desired fractional change.

Returns:

(step, noise_ratio). The step is bounded. The noise ratio is always 0.0 here – measuring it is WaveformDerivatives._apply_noise_estimate()’s job, which uses estimate_noise(). Falls back to initial if the waveform does not depend on the parameter at all.

Return type:

tuple