diff --git a/README.md b/README.md index 5e1b991..017b247 100644 --- a/README.md +++ b/README.md @@ -202,9 +202,12 @@ earlier. --cov artifacts/cov_delta_full.npy \ --out-delta artifacts/delta_ell_full.npy \ --out-summary artifacts/summary_full.json - ``` + ``` The resulting JSON contains the Δ vector length and the stabilized - χ ("z") statistic for the null test. + χ ("z") statistic for the null test. When reusing an older + covariance that has one or two extra high-ℓ bins, add + `--trim-covariance` to drop those trailing rows/columns so the + matrix matches the Δ bandpowers. 6. **Assemble the science cross-spectrum.** Average the two orderings, compare to theory, and compute per-bin significances: diff --git a/docs/issues/phase5-simulations-theory.md b/docs/issues/phase5-simulations-theory.md index 25a110d..181a4f1 100644 --- a/docs/issues/phase5-simulations-theory.md +++ b/docs/issues/phase5-simulations-theory.md @@ -38,4 +38,4 @@ issues: ## Verification - `pytest` (with simulation dependencies available) executes the regression suite, confirming both the loader and covariance estimator satisfy the roadmap acceptance criteria for Phase 5.【F:tests/test_simulation_covariance.py†L12-L36】【F:tests/test_theory_loader.py†L17-L157】 -- Manual production run: `scripts/run_null_sims.py --theory artifacts/theory_planck_smica_lensing.npz --nsims 1000 --nside 2048` completed after approximately three days on a 12-thread Zen 5 workstation and produced `artifacts/cov_delta_full.npy` with shape 69×69 when invoked with the legacy CLI binning (`--disable-prereg --nlb 50`), matching the publication configuration logged by the CLI summary. +- Manual production run: `scripts/run_null_sims.py --theory artifacts/theory_planck_smica_lensing.npz --nsims 1000 --nside 2048` completed after approximately three days on a 12-thread Zen 5 workstation and produced `artifacts/cov_delta_full.npy` with shape 69×69 when invoked with the legacy CLI binning (`--disable-prereg --nlb 50`), matching the publication configuration logged by the CLI summary. Later science stages can reuse that covariance either by running both ordering scripts with the same CLI geometry or, when only a couple of trailing high-ℓ bins are extra, by trimming them via `scripts/compute_commutator.py --trim-covariance`. diff --git a/scripts/compute_commutator.py b/scripts/compute_commutator.py index 0937249..07c556c 100755 --- a/scripts/compute_commutator.py +++ b/scripts/compute_commutator.py @@ -70,6 +70,15 @@ def main(): ap.add_argument("--cov", default="artifacts/cov_delta.npy") ap.add_argument("--out-delta", default="artifacts/delta_ell.npy") ap.add_argument("--out-summary", default="artifacts/summary.json") + ap.add_argument( + "--trim-covariance", + action="store_true", + help=( + "Allow dropping trailing bins from a larger covariance matrix so it matches " + "the Δ bandpowers. Use only when you are certain the first bins share the " + "same geometry." + ), + ) args = ap.parse_args() A = np.load(args.order_a)["cl"] @@ -82,43 +91,57 @@ def main(): z = None cov_path = Path(args.cov) + covariance_trimmed = False if cov_path.exists(): C = np.load(cov_path) - if C.shape[0] != C.shape[1] or C.shape[0] != delta.size: - meta_a = _load_order_metadata(Path(args.order_a)) - meta_b = _load_order_metadata(Path(args.order_b)) - order_details = [] - if meta_a: - order_details.append( - f"{Path(args.order_a).name}: nbins={meta_a.get('nbins')} " - f"nside={meta_a.get('nside')}" + if C.ndim != 2: + raise ValueError(f"Covariance must be 2-D; received array with shape {C.shape}") + if C.shape[0] != C.shape[1]: + raise ValueError(f"Covariance must be square; received shape {C.shape}") + if C.shape[0] != delta.size: + if args.trim_covariance and C.shape[0] > delta.size: + C = C[: delta.size, : delta.size] + covariance_trimmed = True + else: + meta_a = _load_order_metadata(Path(args.order_a)) + meta_b = _load_order_metadata(Path(args.order_b)) + order_details = [] + if meta_a: + order_details.append( + f"{Path(args.order_a).name}: nbins={meta_a.get('nbins')} " + f"nside={meta_a.get('nside')}" + ) + if meta_b: + order_details.append( + f"{Path(args.order_b).name}: nbins={meta_b.get('nbins')} " + f"nside={meta_b.get('nside')}" + ) + hint_lines = [ + ( + "Ensure the null simulations and bandpower orderings use the same " + "binning and mask." + ), + ( + "Re-run scripts/run_null_sims.py with the prereg configuration or CLI " + "parameters that match your orderings." + ), + ( + "To reuse a legacy covariance produced with the CLI defaults, rerun both " + "ordering scripts with --disable-prereg and matching --nlb/--lmax " + "settings." + ), + ] + hint = " ".join(hint_lines) + context = "; ".join(order_details) if order_details else "" + if context: + hint = f"{hint} ({context})" + extra_hint = "" + if C.shape[0] > delta.size: + extra_hint = " Pass --trim-covariance to drop unmatched high-ℓ bins." + raise ValueError( + f"Covariance shape {C.shape} incompatible with delta {delta.shape}. " + f"{hint}{extra_hint}" ) - if meta_b: - order_details.append( - f"{Path(args.order_b).name}: nbins={meta_b.get('nbins')} " - f"nside={meta_b.get('nside')}" - ) - hint_lines = [ - ( - "Ensure the null simulations and bandpower orderings use the same binning " - "and mask." - ), - ( - "Re-run scripts/run_null_sims.py with the prereg configuration or CLI " - "parameters that match your orderings." - ), - ( - "To reuse a legacy covariance produced with the CLI defaults, rerun both " - "ordering scripts with --disable-prereg and matching --nlb/--lmax settings." - ), - ] - hint = " ".join(hint_lines) - context = "; ".join(order_details) if order_details else "" - if context: - hint = f"{hint} ({context})" - raise ValueError( - f"Covariance shape {C.shape} incompatible with delta {delta.shape}. {hint}" - ) z = _stable_z(delta, C) summary = { @@ -132,6 +155,7 @@ def main(): "outputs": { "delta": args.out_delta, }, + "covariance_trimmed": covariance_trimmed, } _save_json(summary, Path(args.out_summary)) print(json.dumps({"msg": f"delta bins={delta.size} z={summary['z']}"}, sort_keys=True))