Skip to content

Pure E/B: fixed-quadrature operator with exact covariance - #373

Merged
cailmdaley merged 10 commits into
developfrom
feat/pure-eb-operator
Oct 1, 2026
Merged

cailmdaley merged 10 commits into
developfrom
feat/pure-eb-operator

Conversation

@cailmdaley

@cailmdaley cailmdaley commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

What. The pure-E/B estimator becomes one data-independent matrix on the fine ξ± grid, K = (I₆⊗P)·M.

  • M is the Schneider (2022) transform with fixed Gauss-Legendre weights (cosmo-numba get_pure_EB_operator), evaluated at every fine node on the regular log grid (TreeCorr rnom) with [tmin, tmax] at their extent.
  • P is a pair-weighted (TreeCorr Σwᵢwⱼ) average into reporting bins. Requested edges snap to the nearest fine edges, so each reporting bin is a union of fine bins; the snapped edges are returned.
  • The covariance is K C_ξ Kᵀ for any ξ± covariance: an analytic one, or the fine-grid jackknife from TreeCorr. The results carry npatch (None = analytic), which sets the Hartlap factor in every χ²/PTE.

Why. The adaptive dqags quadrature chooses its subdivision from the data, so the old estimator was weakly non-linear and its quadrature error rectified noise into spurious B-modes. A fixed rule makes it exactly linear, so the covariance is exact and needs no Monte Carlo. Pooling whole fine bins with the pair weight reproduces what TreeCorr measures on the snapped bins, so ξ± = E ± B + amb holds bin by bin.

Removed. pure_eb_from_xi (adaptive, pointwise), pure_eb_covariance_mc and its n_samples/chunk plumbing, the separate pure-E/B jackknife, and papers/bmodes precompute_pure_eb_chunk/gather_pure_eb_chunks (replaced by pure_eb_modes.py). cv_pure_eb now reads only the integration part.

Verified.

  • The transform runs on the regular log grid (TreeCorr rnom) its interpolator assumes; on the Paper II inputs it agrees with the Lightcone baseline (which fed meanr) to <0.025σ. On rnom, the previous and current cosmo-numba operators agree to round-off.
  • On synthetic catalogues: K·C_jk·Kᵀ equals TreeCorr's jackknife of the operator modes to 1e-15, and P reproduces TreeCorr's ξ±/meanr on the snapped bins to round-off.
  • b_modes, sacc_io and cosmo_val tests and the workflow DAG tests pass.

Downstream.

  • cosmo-numba is pinned to cailmdaley/cosmo-numba@a64cb2e (fixed-quadrature + covariance branches, to be proposed upstream to aguinot/cosmo-numba).
  • Pure-E/B numbers regenerated from this branch will move: the estimator is new, an analytic covariance carries no Hartlap factor, and the combined-χ² Hartlap now uses the full vector length.
  • cosmo_val's pure-E/B now integrates over the full integration grid (0.08–300′ in its config) rather than starting at the reporting minimum; ξ−^B PTEs move at the ±0.02 level vs a 0.5′ floor.
  • Scale cuts snap to the nearest reporting edge (bins_from_scale_cut), so a cut selects the same bins on every fine grid; COSEBIs keep bins_from_edges.

🤖 Generated with Claude Code

cailmdaley and others added 10 commits October 1, 2026 04:51
The pure-E/B estimator is one data-independent matrix K = (I_6 ⊗ P)·M on
the fine ξ± grid: the Schneider (2022) transform with fixed Gauss-Legendre
weights (cosmo-numba get_pure_EB_operator), evaluated at the fine nodes
inside the reporting range with [tmin, tmax] at the fine-grid extent, then
averaged into the reporting bins with pair-count weights. Its covariance is
K C_ξ Kᵀ, exact for an analytic or a jackknife ξ± covariance; `npatch`
travels with the results (None for analytic) and sets the Hartlap factor of
every χ² built on them, with the combined ξ+/ξ− χ² debiased over its full
length.

calculate_pure_eb_correlation works from fine-grid arrays; the reported
ξ±, θ and variances are the same pair-count average, so ξ± = E ± B + amb
holds bin by bin. The adaptive pointwise path, the Monte-Carlo covariance
and the separate pure-E/B jackknife are removed. sacc_io gains
get_xi_npairs; cosmo-numba is pinned to the fork commit carrying the
operator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rule reads only the integration-grid ξ± part (with its pair counts)
and the CosmoCov ξ± covariance on that grid, and calls
calculate_pure_eb_correlation. The Monte-Carlo parameters and the
reporting-part input go; the covariance takes seconds, so the rule's
threads and runtime shrink. The summary labels the covariance from the
npz's npatch record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…chunks

pure_eb_modes.py replaces the chunk scatter/gather (precompute_pure_eb_chunk,
gather_pure_eb_chunks): one call to calculate_pure_eb_correlation on the
fine ξ± and its Gaussian covariance, written to {version}_pure_eb.npz.
The PTE and data-vector scripts drop the MC Hartlap factor, the n(z)
covariance comparison drops its MC-noise band, and the n_samples /
n_chunks config keys go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TreeCorr's ξ± and meanr in a bin are averages over pairs weighted by
w_i w_j, so pooling fine bins with their `weight` reproduces the
reporting-bin measurement exactly when the fine edges nest the reporting
edges; npairs does so only for an unweighted catalogue. The operator,
calculate_pure_eb_correlation, the mixin, cv_pure_eb and pure_eb_modes.py
take `weight_int`; sacc_io.get_xi_npairs becomes get_xi_weight (parts
already carry the tag). A new test checks the nesting identity on a
weighted catalogue with exact binning; the fixture and operator pins are
regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Assigning fine bins to reporting bins by meanr sends a fine bin that
straddles a reporting edge wholly to one side, so P cannot reproduce
TreeCorr's pair average there. The library now takes the fine-grid edges,
snaps each requested reporting edge to the nearest fine edge, pools whole
fine bins, and returns the edges it used; the results' left/right edges,
the pure-E/B npz and the B-mode summary carry those snapped edges.
Edges that snap together or reach outside the fine grid raise.

Callers pass the fine edges: the mixin from gg_int's edges, cv_pure_eb and
pure_eb_modes.py from the integration grid spec. The reproduction test now
covers requested edges that do not nest, measuring each snapped bin
directly with exact binning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pure-E/B PTE intermediate now carries the reporting edges its
matrices are indexed on, and config_space_pte_matrices resolves the
fiducial windows on them rather than on a nominal geomspace grid. A PTE
file that records no edges was binned on the nominal grid, which stays
its window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e node

cosmo-numba's get_pure_EB_operator (schneider2022, local_from_int=True)
now builds the fixed-quadrature transform. It is evaluated at every fine
node: the zero-padded ξ− window extends from the evaluation grid, so this
keeps the transform a function of the fine grid alone. P then selects its
rows; NaN rows at the grid edges are dropped first and only a NaN row that
P uses raises.

The new operator clamps a θ-dependent integration limit to the
interpolator's extrapolation bound, as the reference does, where the
previous pin extrapolated the stencil polynomial; the operator pins move
accordingly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Exact containment on snapped reporting edges makes a cut placed on a
nominal edge select different bins on different fine grids: [12, 83]′ gave
bins 9–15 on a 0.5–300′ grid but 9–14 on cosmo_val's 0.08–300′ one.
bins_from_scale_cut snaps each end of a cut to the nearest reporting edge
in log θ, the rule the edges themselves were snapped by, so the same cut
selects the same bins on every grid. _get_pte_from_scale_cut (hence the
B-mode summaries), the pure-E/B plots and papers/bmodes
config_space_pte_matrices use it; COSEBIs keep bins_from_edges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cosmo-numba's interpolator places its samples on a regular grid in log θ,
so handing it TreeCorr meanr put every sample at its nominal position
while the evaluation limit sat at the true meanr; where a spacing exceeded
the mean step the limit fell past the extrapolation bound, and the old and
new cosmo-numba operators resolved that knife-edge differently. The
transform now runs on the geometric centres of the log-uniform fine edges
(TreeCorr rnom) and is evaluated there; meanr enters only the reported θ.
The edges are checked to be log-uniform, and pure_eb_operator no longer
takes theta_int. On rnom the d78a189 and a64cb2e operators agree to
round-off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cailmdaley
cailmdaley merged commit ad52bba into develop Oct 1, 2026
5 of 6 checks passed
@cailmdaley
cailmdaley deleted the feat/pure-eb-operator branch October 1, 2026 23:24
cailmdaley added a commit that referenced this pull request Oct 2, 2026
Pure E/B takes #373's design: one fixed linear operator on the
integration-grid ξ± part with covariance K C Kᵀ; the MC covariance and the
chunked precompute go. The blinding stays: cv_pure_eb and calculate_pure_eb
read the sealed integration part through sacc_io.xi_correlation (which
carries the pair weights, so get_xi_weight is not added), and the pure-E/B
part is saved derived_from that part. papers/bmodes/pure_eb_modes.py reads
the SACC part; the B-mode blinding test uses the operator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant