API reference

Likelihoods

Likelihoods for gapped/windowed stationary Gaussian data.

Four tiers, in decreasing order of fidelity and cost:

  • TimeDomainExact — the exact likelihood of the observed samples (marginalization of a stationary Gaussian over the gap samples = row/column deletion). No window appears anywhere. With two noise components entering linearly, one simultaneous diagonalization (“pencil”) gives an O(m) likelihood per evaluation with a closed-form determinant.

  • FullCovariance — complex-normal likelihood of the windowed, band-restricted rfft data with the DENSE windowed covariance Sigma(lam) = 10^(lam_0) C_0 + 10^(lam_1) C_1; same two-component pencil trick, reduced to the rank retained by the gaps.

  • DiagonalLikelihood (convolved) — Whittle-like likelihood with the EXACT diagonal of the windowed covariance (leakage-corrected PSD).

  • DiagonalLikelihood (whittle) — the raw PSD on the band, optionally rescaled by the window power W_2 (“normalizing constant” approximation).

Common conventions

  • Noise parameters lam = (lam_0, lam_1, ...): log10 deviations of the component POWERS (PSDs), Sigma(lam) = sum_k 10^(lam_k) C_k; truth at 0. (v0.1 used amplitude deviations, 10^(2 lam): lam_new = 2 lam_old.)

  • Residuals are multichannel, shape (nch, .) — channels independent with identical covariances (e.g. LISA A and E).

  • Frequency-domain classes expose transform(x_td) mapping time series to the internally normalized data vector, and loglike_td for convenience; loglike(resid, lam) takes already-transformed residuals.

  • Log-likelihoods contain constant offsets that depend on the internal normalization s_ref; every DIFFERENCE of log-likelihoods (between parameter points, or between templates) is independent of it.

class gaplike.likelihood.DiagonalLikelihood(variances)[source]

Bases: _FDLikelihood

Complex-normal likelihood with a diagonal covariance model var_j(lam) = sum_k 10^(lam_k) v_kj on the analysis band.

classmethod convolved(window, components, dt, f_lo, f_hi, s_ref=None)[source]

Exact leading diagonal of the windowed covariance, per component (O(n log n) each; no dense matrix is ever formed).

classmethod whittle(window, components, dt, f_lo, f_hi, scale_by_window_power=False, s_ref=None)[source]

Raw one-sided PSD on the band (no leakage correction); with scale_by_window_power=True every component is multiplied by W_2 = sum(w^2)/n (the ‘normalizing constant’ approximation).

variances(lam)[source]
loglike(resid, lam)[source]

resid: (nch, n_band) transformed residuals.

class gaplike.likelihood.FullCovariance(components, rtol_rank=1e-08)[source]

Bases: _FDLikelihood

Dense-covariance complex-normal likelihood, Sigma(lam) = 10^(lam_0) C_0 + 10^(lam_1) C_1 (exactly two components), diagonalized ONCE in the rank retained by the gaps:

  • eigendecomposition of C_0 + C_1 truncated at rtol_rank discards the singular directions annihilated by the gaps,

  • C_1 is whitened and the remaining symmetric pencil diagonalized, producing a fixed projector Tproj with Tproj C_0 Tproj^H = diag(mu), Tproj C_1 Tproj^H = I,

  • every likelihood evaluation is then O(rank) per channel with a closed-form log-determinant.

Extending to K > 2 components (or nonlinearly-parametrized shapes) would require a fresh factorization per evaluation — use the convolved diagonal or the time-domain solver instead.

classmethod from_window(window, components, dt, f_lo, f_hi, rtol_rank=1e-08, s_ref=None)[source]

Build the two banded windowed covariance matrices from a window and two PSD components (callables or two-sided grids), then diagonalize.

property r
variances(lam)[source]
loglike(resid, lam)[source]

resid: (nch, n_band) transformed residuals.

class gaplike.likelihood.TimeDomainExact(mask, components, dt)[source]

Bases: object

EXACT likelihood of gapped stationary data: the observed samples of a stationary Gaussian are jointly Gaussian with covariance Sigma_OO(lam) = 10^(2 lam_0) C_0,OO + 10^(2 lam_1) C_1,OO — the stationary (circulant) covariance with the gap rows/columns deleted. No window or taper appears anywhere; scatter/width and model/true width ratios are 1 by construction.

With two components entering linearly, one simultaneous diagonalization (cost O(m^3), once) reduces every likelihood evaluation to O(m) with a closed-form determinant — practical for m up to ~10^4 observed samples.

Parameters:
  • mask (bool or float array, length n) – Observed-sample mask (or gate; strictly-positive entries = observed).

  • components (sequence of two callables or ndarrays) – The two PSD components, as callables of f or two-sided grids.

  • dt (float) – Sample cadence [s].

transform(x_td)[source]

Time series (..., n) -> observed samples (..., m).

loglike_td(resid_td, lam)[source]
variances(lam)[source]
loglike(resid_obs, lam)[source]

resid_obs: (nch, m) real residuals on the observed samples. (Constant -nch m log(2 pi)/2 omitted.)

fisher_noise(nch=1, lam=(0.0, 0.0))[source]

2x2 noise-parameter Fisher matrix at lam.

fisher_signal(dh_obs, lam=(0.0, 0.0))[source]

Signal Fisher matrix; dh_obs: (nch, m, npar) template derivatives on the observed samples.

score_noise(resid_obs, lam=(0.0, 0.0))[source]
mle_noise(resid_obs, lam=(0.0, 0.0))[source]
mle_signal(dh_obs, resid_obs, lam=(0.0, 0.0), rcond=1e-12)[source]

Matrix-free solver

Matrix-free preconditioned conjugate gradients for gapped stationary data.

The exact likelihood of the observed samples of a stationary Gaussian needs quadratic forms in the inverse of

Sigma_OO = R Sigma R^T,

the stationary (circulant) covariance restricted to the observed samples (rows/columns of the gaps deleted). Sigma_OO is neither circulant nor Toeplitz, and at large N it cannot even be stored: with an 80% duty cycle it reaches 5.5 GB at N = 2^15 and ~350 GB at N = 2^18.

Conjugate gradients sidesteps the matrix entirely. It solves Sigma_OO u = b given only the action of Sigma_OO, which costs three steps, two of them FFTs:

embed with zeros -> multiply by the circulant eigenvalues -> restrict.

The identity R Sigma R^T = Sigma_OO makes this exact, not approximate: the zero-fill is the embedding R^T, never a statement about the data. Convergence is governed by the conditioning after preconditioning with the gap-free (Whittle) covariance — the same FFT pair, dividing by the eigenvalues instead — which removes the dynamic range of the PSD and leaves only the geometry of the gaps. Per iteration: four length-N FFTs.

Nothing here assumes anything about the noise model beyond stationarity — in particular, none of the two-component (TM/OMS) structure that the closed-form routes in gaplike.likelihood exploit. This is the general route: it survives spline-parametrized spectra, extra components, anything that defeats a simultaneous diagonalization.

The log-determinant is not provided here: pair the quadratic form with the closed-form determinant of gaplike.likelihood.TimeDomainExact when there are exactly two components (as in the companion paper), or with stochastic Lanczos quadrature in general.

Conventions match gaplike.covariance.restricted_autocov(): the circulant eigenvalues are eig = S2 / (2 dt) with S2 the two-sided PSD on the full DFT grid, so gamma = Re ifft(eig) is the autocovariance.

gaplike.cg.circulant_eigenvalues(components, n, dt)[source]

DFT eigenvalues of the stationary circulant covariance for a sum of PSD components (callables of f or two-sided grids): sum_k S2_k/(2 dt).

gaplike.cg.sigma_oo(eig, obs)[source]

LinearOperator applying Sigma_OO = R Sigma R^T matrix-free: zero-embed, multiply by eig in Fourier space, restrict. Two FFTs.

gaplike.cg.whittle_preconditioner(eig, obs, floor_rel=1e-06)[source]

Inverse of the gap-free (Whittle) covariance applied by the same FFT pair, as a preconditioner. floor_rel floors the eigenvalues at a fraction of their maximum, taming transfer-function nulls (e.g. the TDI2 zeros) that the restricted operator does not share.

gaplike.cg.solve(eig, obs, b, rtol=1e-08, x0=None, maxiter=None, floor_rel=1e-06, precondition=True)[source]

Solve Sigma_OO u = b by (preconditioned) conjugate gradients.

Parameters:
  • eig (ndarray, shape (n,)) – Circulant eigenvalues of the full covariance.

  • obs (ndarray) – Observed-sample indices, or a boolean/gate mask over the full grid.

  • b (ndarray, shape (m,)) – Right-hand side, on the observed samples.

  • rtol (float, optional) – Relative residual tolerance ||Sigma_OO u - b|| / ||b||.

  • x0 (ndarray, optional) – Warm start – e.g. the previously accepted solution in an MCMC, which typically cuts the iteration count by around 20%.

  • maxiter (int, optional) – Iteration cap; None leaves it to SciPy.

  • floor_rel (float, optional) – Preconditioner eigenvalue floor, as a fraction of the maximum.

  • precondition (bool, optional) – Set False to run unpreconditioned – far slower, and useful mainly for measuring what the preconditioner buys.

Returns:

  • u (ndarray, shape (m,)) – Solution of Sigma_OO u = b.

  • iterations (int) – Number of iterations taken.

Raises:

RuntimeError – If the iteration does not reach rtol within maxiter.

gaplike.cg.quad_form(eig, obs, b, **kw)[source]

b^T Sigma_OO^{-1} b by CG. b may be (m,) or multichannel (nch, m) (channels independent, identical covariance — the forms are summed). Returns (value, [iterations per solve]).

gaplike.cg.dense_restricted(eig, obs, block_bytes=200000000.0)[source]

Dense Sigma_OO built from the autocovariance, in row blocks so the integer lag array never rivals the matrix itself. Reference/benchmark only: this is exactly the object the CG route exists to avoid.

class gaplike.cg.RestrictedCG(mask, components, dt, rtol=1e-08, floor_rel=1e-06)[source]

Bases: object

Quadratic forms in Sigma_OO(lam)^{-1} for a component-parametrized spectrum Sigma(lam) = sum_k 10^(lam_k) C_k, evaluated matrix-free.

The companion of gaplike.likelihood.TimeDomainExact: same model, same parametrization, any number of components, no O(m^3) setup and no O(m^2) storage — but no determinant (see the module docstring).

Parameters:
  • mask (ndarray, shape (n,)) – Observed-sample mask or gate; strictly-positive entries are observed.

  • components (sequence of callable or ndarray) – PSD components: callables of f, or precomputed two-sided grids. Any number of them – unlike the pencil classes, which need exactly two.

  • dt (float) – Sample cadence [s].

  • rtol (float, optional) – Relative residual tolerance passed to solve().

  • floor_rel (float, optional) – Preconditioner eigenvalue floor, as a fraction of the maximum.

eigenvalues(lam)[source]
solve(lam, b, x0=None, maxiter=None)[source]
quad_form(lam, resid_obs, maxiter=None)[source]

resid_obs: (nch, m) residuals on the observed samples. Returns (sum_ch r^T Sigma_OO^{-1} r, [iterations per solve]).

Log-determinants

Stochastic Lanczos quadrature (SLQ) log-determinants for gapped data.

The exact time-domain likelihood needs, besides the quadratic form that gaplike.cg provides matrix-free, the log-determinant

log|Sigma_OO(theta)|,

which changes with every noise-parameter update. With exactly two PSD components the closed form of gaplike.likelihood.TimeDomainExact applies; for anything richer — in particular the ratio-spline spectra this module also provides — there is no closed form, and the dense route costs O(m^3). This module supplies the general, matrix-free alternative:

  • logdet_slq Hutchinson + Lanczos quadrature estimate of

    tr log Sigma_OO (Ubaru, Chen & Saad 2017).

  • logdet_diff_slq the quantity an MCMC accept ratio actually

    needs, log|Sigma_OO(theta')| - log|Sigma_OO(theta)|, estimated with SHARED probe vectors so that the estimator sees only the (small) operator difference, not the two (large) absolute determinants.

  • logdet_dense Cholesky ground truth, for validation at small

    m (this is the object everything else exists to avoid).

  • circulant_logdet the gap-free (Whittle) determinant in closed

    form — context lines and sanity checks.

  • ratio_spline a flexible PSD component

    S_ref(f) * exp(sum_k c_k B_k(log10 f)) that plugs into circulant_eigenvalues like any other component.

Probes are Rademacher: for a fixed symmetric operator D the estimator variance is (2/M) * (||D||_F^2 - sum_i D_ii^2) — the diagonal does not contribute. For the difference operator log Sigma(2) - log Sigma(1) under a smooth spectral perturbation most of the energy sits on the diagonal, which is why shared-probe differences can be dramatically better than differencing two absolute estimates; measuring that gain on realistic spectra is the point of paper/fig_det_flexible.py.

Everything is deterministic given rng; no state is kept between calls.

gaplike.slq.rademacher(m, n_probes, rng)[source]

(n_probes, m) array of +-1 probe vectors.

gaplike.slq.lanczos_tridiag(matvec, v0, k, reorth=True)[source]

k Lanczos steps from v0 with full reorthogonalisation.

Returns (alpha, beta, V) — diagonal, off-diagonal and the basis actually built (early breakdown truncates all three). Memory is m * k doubles; at the m, k of this study that is trivial and full reorthogonalisation keeps the Ritz values honest.

gaplike.slq.quad_log(matvec, z, k, eig_floor=0.0)[source]

Lanczos-quadrature estimate of z^T log(A) z for SPD A.

eig_floor > 0 clips non-positive Ritz values (finite-precision guards near-singular operators); the number clipped is returned for monitoring.

gaplike.slq.logdet_slq(eig, obs, n_probes, k, rng, eig_floor=0.0, batched=True, workers=-1, max_bytes=1000000000.0, reorth='none')[source]

SLQ estimate of log|Sigma_OO|.

Parameters:
  • eig ((n,) circulant eigenvalues of the full covariance) – (gaplike.cg.circulant_eigenvalues())

  • obs (observed-sample indices, or mask/gate over the full grid)

  • n_probes (Rademacher probes and Lanczos steps per probe)

  • k (Rademacher probes and Lanczos steps per probe)

  • rng (numpy.random.Generator)

  • dict (Returns a)

  • estimates

  • scatter) (for empirical)

  • matvecs

  • clipped.

  • batched (batched=True (default) executes all probes together --- one)

  • step (real FFT per Lanczos)

  • identical (multithreaded via workers ---)

  • probes

  • mathematics (identical)

  • only. (floating-point-order differences)

  • chunked). (max_bytes caps the stored Lanczos bases (probes are)

gaplike.slq.logdet_diff_slq(eig1, eig2, obs, n_probes, k, rng, shared=True, eig_floor=0.0, batched=True, workers=-1, max_bytes=1000000000.0, reorth='none')[source]

log|Sigma_OO(eig2)| - log|Sigma_OO(eig1)| by SLQ.

shared=True uses the same Rademacher probes on both operators (the estimator of the difference operator); shared=False draws fresh probes for each — the naive differencing of two absolute estimates, kept for comparison. Same return structure as logdet_slq() with per_probe holding per-probe differences; batched as there.

gaplike.slq.logdet_dense(eig, obs)[source]

Ground truth log|Sigma_OO| by dense Cholesky (O(m^3); small m only). Returns (logdet, seconds_spent_in_factorisation).

gaplike.slq.circulant_logdet(eig, skip_nonpositive=False)[source]

Closed-form log|Sigma| of the FULL (gap-free) circulant — the Whittle determinant. Reference/context only: restriction to the observed samples has no closed form (that is the whole point).

NB the library’s grid convention zeroes the DC bin (mean removal), so eigenvalue grids built from callables contain an exact zero and the unrestricted determinant is -inf by design; skip_nonpositive=True sums the strictly positive (in-band) bins instead, which is the Whittle practice.

gaplike.slq.ratio_spline(S_ref, knots, coeffs, degree=3)[source]

Flexible PSD component S_ref(f) * exp(spline(log10 f)).

S_ref is any PSD callable of f; coeffs are the log-ratio B-spline coefficients on knots (from spline_knots()). The spline argument is clipped to the knot span, so the ratio extrapolates flat — deliberately: tails behave like the reference. coeffs = 0 reproduces S_ref exactly. The return is a callable component accepted anywhere gaplike takes PSD components.

class gaplike.slq.ComplementFactor(eig, obs, dc_scale=0.001)[source]

Bases: object

One g x g Cholesky of (Sigma_c^-1)_GG and everything it unlocks: the exact log-determinant (.logdet) and exact quadratic forms (quad_form()) of Sigma_OO.

The library’s grid convention zeroes the DC bin, making the full circulant singular; the identity is therefore evaluated on a model with the DC eigenvalue set to dc_scale * max(eig), and the exact rank-one downdate (matrix determinant lemma / Sherman–Morrison along the constant mode) removes it again EXACTLY — the floor is a conditioning choice, not an approximation. Any zeroed bin other than DC is not handled and raises.

Parameters:
  • eig ((n,) circulant eigenvalues of the full covariance)

  • obs (observed-sample indices, or mask/gate over the full grid)

  • dc_scale (DC floor as a fraction of the largest eigenvalue)

quad_form(d)[source]

Exact d^T Sigma_OO^{-1} d (d: (m,) or (nch, m)) from the cached factor; the DC downdate is applied exactly.

gaplike.slq.logdet_complement(eig, obs, dc_scale=0.001)[source]

Exact log|Sigma_OO| via the complement identity (see ComplementFactor); cost is one g x g Cholesky on the GAP samples.

gaplike.slq.spline_knots(f_lo, f_hi, n_coeff, degree=3)[source]

Clamped knot vector in log10 f for n_coeff B-spline basis functions of the given degree spanning [f_lo, f_hi].

Gap patterns, gates and windows

Gap patterns, gates and effective windows.

Conventions

  • A mask is a boolean array of length n: True = sample observed, False = sample lost to a gap.

  • A gate is a float array in [0, 1]: 0 inside gaps, with optionally tapered (Planck) edges.

  • The effective window is gate * segment_window; it multiplies the time series before any Fourier transform. The exact time-domain likelihood (gaplike.likelihood.TimeDomainExact) uses the mask only — no window ever appears there.

All builders accept arbitrary patterns: explicit intervals, periodic combs, random (Poisson) gaps, or any user-supplied boolean mask.

gaplike.gaps.mask_from_intervals(n, dt, gaps)[source]

Boolean mask with the samples inside each (t_start, t_end) interval (seconds, gap = removed data) set to False.

gaplike.gaps.periodic_mask(n, gap_samples, period_samples, offset=0)[source]

Rectangular comb: gap_samples consecutive samples removed at the start of every period_samples block (the paper’s ‘drastic gaps’).

gaplike.gaps.random_mask(n, dt, rate_per_day, duration_s, duration_jitter_s=0.0, rng=None)[source]

Poisson-distributed gaps: rate_per_day expected gaps per day, each of duration_s seconds (Gaussian-jittered by duration_jitter_s).

gaplike.gaps.mask_from_gate(gate)[source]

Boolean mask of the strictly-zero samples of a gate.

gaplike.gaps.duty_cycle(mask)[source]

Fraction of observed samples.

gaplike.gaps.window_power(w)[source]

Mean squared window, W_2 = sum(w^2)/n — the normalizing constant of the scaled-Whittle (‘normalizing constant’) approximation.

gaplike.gaps.gap_intervals(mask, dt)[source]

List of gap intervals (t_start, t_end) in seconds from a mask.

gaplike.gaps.planck_edge(x, taper)[source]

C-infinity ramp 0 -> 1 over [0, taper].

gaplike.gaps.gate_from_gaps(gaps, taper, n=None, dt=None, t=None)[source]

Gate with gaps given as (t_center, duration) pairs and Planck-tapered edges extending over taper OUTSIDE each gap.

Times can be in any unit as long as gaps, taper and the time grid agree: pass either n, dt (grid t_k = k*dt) or an explicit t. Overlapping tapers multiply.

gaplike.gaps.gate_from_mask(mask, dt, taper_s=0.0)[source]

Gate from an ARBITRARY boolean mask: 0 on removed samples, Planck ramp over taper_s seconds as a function of the distance to the nearest removed sample (taper_s = 0 gives the rectangular gate).

gaplike.gaps.segment_window(n, alpha=0.05)[source]

Tukey segment-edge taper (alpha = 0 gives all ones).

gaplike.gaps.effective_window(gate, segment=None)[source]

w_eff = segment * gate (segment defaults to all ones).

Covariances

Windowed frequency-domain covariances and restricted time-domain autocovariances of stationary noise.

Frequency-domain data convention

The banded data vector of a windowed real series x is

y_j = sqrt(2 dt / n) * rfft(w * x)_j , j in the analysis band,

so that E[y y^H] carries one-sided-PSD units and, for w = 1, its diagonal is the PSD. full_covariance returns exactly E[y y^H] for stationary noise of two-sided spectrum S2; convolved_diag returns its exact diagonal on the full one-sided grid at O(n log n) cost.

gaplike.covariance.band_indices(n, dt, f_lo, f_hi)[source]

Indices of the rfft bins strictly inside (f_lo, f_hi).

gaplike.covariance.rfft_scale(n, dt)[source]

Periodogram normalization sqrt(2 dt / n) of the rfft.

gaplike.covariance.transform(x_td, window, idx, dt)[source]

Banded, periodogram-normalized rfft of the windowed series (x_td: shape (..., n)).

gaplike.covariance.convolved_diag(window, S2)[source]

Exact diagonal of the windowed covariance on the one-sided grid: circular convolution of |W|^2/n^2 with the two-sided PSD, O(n log n).

gaplike.covariance.full_covariance(window, S2, idx)[source]

Dense windowed covariance E[y y^H] restricted to the rfft bins idx (band-restricted Algorithm 1 of Burke et al. 2025): the inner sum runs over the ENTIRE two-sided grid, so out-of-band leakage into the band is fully retained. Cost O(len(idx)^2 n) via one matrix product; never forms an n x n object.

gaplike.covariance.restricted_autocov(S2, obs, dt)[source]

Stationary autocovariance matrix restricted to the observed samples: C[i, j] = gamma[(obs_i - obs_j) mod n] with gamma = Re ifft(S2) / (2 dt) (circulant embedding of the Toeplitz covariance; exact for data generated on the circulant grid).

Power spectral densities

Noise power spectral densities.

A noise model is a sequence of components: callables S_k(f) returning the one-sided PSD of component k at its reference amplitude. The modelled spectrum is

S(f; lam) = sum_k 10^(2 lam_k) S_k(f),

with lam_k the (dimensionless) log10 deviations of the component amplitudes from their reference values — the noise parameters inferred by every likelihood in gaplike.likelihood.

The built-in model is the two-component (test-mass + OMS) LISA TDI-2 A/E spectrum (SciRDv1 shapes). Any user-defined callables work the same way; components may also be passed to the likelihoods directly as precomputed grids.

gaplike.psd.lisa_tdi2_ae(tm_asd=3e-15, oms_asd=1.5e-11, arm_m=2500000000.0, generation=2)[source]

The two A/E-channel PSD components of second-generation TDI.

Returns {"tm": S_tm(f), "oms": S_oms(f)} — callables giving the channel PSD (fractional frequency) of each single-link noise, transferred through the TDI combination, at the reference amplitudes tm_asd [m s^-2 Hz^-1/2] and oms_asd [m Hz^-1/2].

gaplike.psd.two_sided_grid(S, n, dt)[source]

Two-sided PSD on the full DFT grid fftfreq(n, dt) (0 at DC).

gaplike.psd.one_sided_grid(S, n, dt)[source]

One-sided PSD on the rfft grid rfftfreq(n, dt) (0 at DC).

gaplike.psd.as_two_sided(component, n, dt)[source]

Normalize a component (callable or precomputed length-n two-sided grid) to a two-sided grid.

Simulation

Simulation of stationary Gaussian noise in the time domain.

gaplike.simulate.noise_td(S_onesided, dt, rng, nch=1)[source]

(nch, n) independent stationary noise realizations with one-sided PSD S_onesided on the rfft grid (length n//2 + 1, n even, S[0] ignored / DC-free).

Exact circulant sampling: independent complex-normal rfft coefficients of variance S * n / (4 dt) per real/imaginary part (real Nyquist bin), inverse-transformed to the time domain.

Waveforms

Waveform interfaces.

The package is waveform-agnostic: a Waveform wraps ANY callable theta -> h~(f) returning the multichannel continuous-Fourier-transform waveform on the rfft grid rfftfreq(n, dt) (numpy sign convention), shape (nch, n//2 + 1). From there:

  • .td(theta) gives the physical time-domain template via the CFT->DFT rule h = irfft(h~ / dt) (so Whittle SNRs match the continuous integral),

  • any gaplike.likelihood object consumes .td output through its transform / loglike_td methods.

An adapter for lisabeta massive-black-hole-binary TDI waveforms is provided (lisabeta_mbhb_ae()); lisabeta is an optional dependency imported only inside the factory.

class gaplike.waveform.Waveform(fd_func, n, dt)[source]

Bases: object

Wrap a frequency-domain waveform callable.

Parameters:
  • fd_func (callable) – theta -> (nch, n//2 + 1) complex CFT waveform on the rfft grid.

  • n (int) – Length of the time grid.

  • dt (float) – Sample cadence [s].

fd(theta)[source]

Frequency-domain CFT template (nch, n//2 + 1).

td(theta)[source]

Physical time-domain template (nch, n): irfft(h~ / dt).

gaplike.waveform.central_derivs(f, theta, steps)[source]

Generic central finite differences of f(theta) (any array output): returns a list of df/dtheta_a for the parameters with steps[a] not None; entries with steps[a] is None are returned as None (caller supplies analytic derivatives).

gaplike.waveform.default_mbhb_params(x)[source]

Physical vector -> lisabeta parameter dict.

x = (Mtot, q, chi1, chi2, log10 dL[Gpc], iota, phi, lambda, beta, psi, tc_frac) with L-frame angles; tc_frac (element 10) is applied separately as a frequency-domain phase.

gaplike.waveform.lisabeta_mbhb_ae(n, dt, f_lo, f_hi, wf_kw=None, to_params=None, tc_index=10)[source]

Waveform for the (A, E) TDI channels of an MBHB, evaluated by lisabeta on the analysis band only and summed over all modes of the approximant (default IMRPhenomHM).

tc_index: index of the coalescence-time parameter (as a fraction of the segment), applied as the FD phase exp(-2 i pi f tc T) common to every mode; None disables it. to_params maps the parameter vector to a lisabeta dict (default default_mbhb_params()).