qube.memory_budget module

Static memory-budget calculator for QUBE runs (harmonic and pixel-direct paths). Also exposes the qube-memory-budget console script declared in qube-qml’s pyproject.toml.

Predict QUBE pipeline memory consumption from basis dimensions.

Companion to the algebraic memory analysis in Paper I §6 — analogous to ECLIPSE_Memory.f90 but covering both QUBE pipeline paths.

Two paths are supported:

  • harmonic: Tegmark-style V projection + SMW kernel. Persistent state is dominated by L (n_pix²), V_N_inv (n_modes × n_pix), and V_Ninv_VT (n_modes²).

  • pixel-direct: Gjerløw-style direct pixel-space C^{-1}. No V, no SMW. Persistent state is dominated by Cov_T (Core retains it; Core only nullifies noise_cov1 for non-pixel-direct modes) and basis._N (asfortranarray F-order copy).

For each path the calculator splits stage cost into:

  • Persistent state at stage exit (audit-backed, ~1% accurate at the calibration points below).

  • Transient state at stage peak (modelled where the algebra is known; assumed simultaneously alive at the peak — conservative when transients fire sequentially).

StageBudget.peak_bytes = persistent_bytes + Σ transient_bytes.

Allocator overhead, BLAS workspace, and the empirical fisher.compute.derivative_cache transient (queue item E for the harmonic path; ~``n_params × n_pix²`` for the pixel-direct path) are not modelled in detail. Expect ~5–10% allocator/BLAS slack at production scale, and an additive Python/numpy overhead floor (~0.2–0.5 GiB) that dominates at small n_pix.

Calibration points:

  • Harmonic at eclipse-QU (n_pix=59136, n_modes=33274, lmax_signal=256, lmax=128; mem_eclipse_qu_20114986.out, commit d11ab0b): basis_setup persistent 57.21 GiB predicted vs 57.80 GiB measured; basis_setup peak 112.6 GiB predicted vs 113.2 GiB measured.

  • Pixel-direct at QU_nside64 fsky=0.1 (n_pix=9800, lmax=128, basis_lmax=256 default → switch implicit; mem_nc_20103507.out, commit ccabffd): basis_setup persistent (above covariance) 1.43 GiB predicted vs 1.44 GiB measured.

class qube.memory_budget.BudgetConfig(n_pix, n_modes, lmax_signal, lmax=None, release_pixel_projector=True)[source]

Bases: object

Inputs to the QUBE harmonic-path budget.

n_pix: total pixel count (2 × n_pix_observed for QU spin-2;

n_pix_observed for T spin-0; sum for TQU).

n_modes: total mode count after V projection. The calculator does not

derive this from lmax_signal because mode counting depends on spin and m=0 exclusions.

lmax_signal: signal-cov ceiling (Layer A; max ℓ in V), not the

parameter-grid lmax.

lmax: inference window upper (Layer B). None or equal to lmax_signal

disables the switch optimisation. Below lmax_signal activates _compute_effective_noise and adds T (separate from V_Ninv_VT) plus S_fixed and the corr intermediate to basis_setup.

release_pixel_projector: True matches PR #16’s Fisher path (V freed

after SMW build). False reproduces the legacy keep-V behaviour.

n_pix: int
n_modes: int
lmax_signal: int
lmax: int | None = None
release_pixel_projector: bool = True
property has_switch: bool
__init__(n_pix, n_modes, lmax_signal, lmax=None, release_pixel_projector=True)
class qube.memory_budget.PixelDirectBudgetConfig(n_pix, lmax_signal, n_bins, n_params, has_switch=True)[source]

Bases: object

Inputs to the QUBE pixel-direct path budget.

n_pix: total pixel count (same convention as BudgetConfig). lmax_signal: signal-cov ceiling (Layer A). Used for the auto-picker

informational fields and to decide whether the implicit-switch S_fixed transient is allocated by Core (lmax_signal > params.lmax → switch implicit).

n_bins: number of bandpower bins in the analysis. Drives the

per-parameter cinv_times_dcb dict size during fisher_run.

n_params: number of derivative parameters. n_bins × n_spectra in

practice (e.g. n_bins=6 × 3 spectra = 18 at eclipse-QU). Used for the empirical fisher_run derivative-product transient.

has_switch: True if Core’s setup_computation_basis enters the

S_fixed branch before discovering the path is pixel-direct (i.e. params.lmax < lmax_signal). At default benchmark configs (lmax_signal=4·nside, params.lmax=2·nside) this is True. The S_fixed buffer is allocated, populated, then dereferenced — but the allocator pool keeps it resident through basis_setup exit.

n_pix: int
lmax_signal: int
n_bins: int
n_params: int
has_switch: bool = True
__init__(n_pix, lmax_signal, n_bins, n_params, has_switch=True)
class qube.memory_budget.StageBudget(name, persistent=<factory>, transient=<factory>)[source]

Bases: object

Memory cost for one pipeline stage.

name: str
persistent: dict[str, int]
transient: dict[str, int]
property persistent_bytes: int
property transient_bytes: int
property peak_bytes: int
__init__(name, persistent=<factory>, transient=<factory>)
class qube.memory_budget.QUBEBudget(config, stages, path='harmonic')[source]

Bases: object

Full pipeline budget across all four QUBE stages.

config: BudgetConfig | PixelDirectBudgetConfig
stages: list[StageBudget]
path: Literal['harmonic', 'pixel_direct'] = 'harmonic'
property lifetime_peak_bytes: int
stage(name)[source]
Return type:

StageBudget

format_table()[source]
Return type:

str

__init__(config, stages, path='harmonic')
qube.memory_budget.predict_qube_budget(config)[source]
Return type:

QUBEBudget

qube.memory_budget.predict_pixel_direct_budget(config)[source]

Predict QUBE memory budget on the pixel-direct path.

Return type:

QUBEBudget