diff --git a/.superpowers/sdd/final-fix-report.md b/.superpowers/sdd/final-fix-report.md new file mode 100644 index 000000000..9d900bb5f --- /dev/null +++ b/.superpowers/sdd/final-fix-report.md @@ -0,0 +1,117 @@ +# Challenge #194 Final Fix Report + +## Scope + +Applied the requested final Day-0 fix wave on branch `challenge/194` with no scope expansion. + +## Root Cause + +`ModelSpec` previously accepted positive finite `sigma` values so small that `1.0 + sigma` rounded back to exactly `1.0`. Downstream Hurwitz-zeta and reference-tail formulas assume exponent strictly greater than `1.0`, so that boundary admitted pole and division-by-zero behavior. + +## RED -> GREEN + +### RED tests added first + +- `tests/test_model.py` + - rejects a positive `sigma` with `1.0 + sigma == 1.0` + - accepts `math.ulp(1.0)`, `0.8`, `1.0`, and `1.1`, and checks `periodic_kernel()` stays finite + - adds direct invalid-input tests for `distance_classes()` and `canonical_edge()` +- `tests/test_kernel.py` + - checks `kernel_weight_sum()` equals the multiplicity-weighted `periodic_kernel()` table + - adds a regression proving the periodic-image kernel differs from bare minimum-image `r^-(1+sigma)` while matching the Hurwitz-zeta expression +- `tests/test_oracle.py` + - checks oracle public symbols are exported from the package root and listed in `__all__` + +### RED command + +```bash +./.venv/bin/python -m pytest tests/test_model.py tests/test_kernel.py tests/test_oracle.py -q +``` + +Result: + +```text +.F........................... [100%] +=================================== FAILURES =================================== +___ test_model_spec_rejects_positive_sigma_when_one_plus_sigma_rounds_to_one ___ + +E Failed: DID NOT RAISE + +1 failed, 28 passed in 5.48s +``` + +### GREEN implementation + +- Updated `src/long_range_percolation/model.py` to reject `sigma` unless: + - `sigma` is finite + - `sigma > 0.0` + - `math.isfinite(1.0 + sigma)` + - `(1.0 + sigma) > 1.0` +- Removed the unused `math` import from `src/long_range_percolation/kernel.py` + +### GREEN command + +```bash +./.venv/bin/python -m pytest tests/test_model.py tests/test_kernel.py tests/test_oracle.py -q +``` + +Result: + +```text +............................. [100%] +29 passed in 11.27s +``` + +## Full Verification + +### Full suite + +```bash +./.venv/bin/python -m pytest -q +``` + +Result: + +```text +........................................................................ [100%] +72 passed in 40.84s +``` + +### Diff formatting + +```bash +git diff --check +``` + +Result: + +```text +[no output] +``` + +### Working tree artifact check before report/commit + +```bash +git status --short +``` + +Result: + +```text + M tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/kernel.py + M tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/model.py + M tracks/qmc/solutions/frustration-free/challenge-194/tests/test_kernel.py + M tracks/qmc/solutions/frustration-free/challenge-194/tests/test_model.py + M tracks/qmc/solutions/frustration-free/challenge-194/tests/test_oracle.py +``` + +No unrelated untracked artifacts were produced by this fix wave. + +## Files Changed + +- `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/model.py` +- `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/kernel.py` +- `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_model.py` +- `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_kernel.py` +- `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_oracle.py` +- `.superpowers/sdd/final-fix-report.md` diff --git a/docs/superpowers/plans/2026-07-30-challenge-194-p0-analysis-p1-protocol.md b/docs/superpowers/plans/2026-07-30-challenge-194-p0-analysis-p1-protocol.md new file mode 100644 index 000000000..570791939 --- /dev/null +++ b/docs/superpowers/plans/2026-07-30-challenge-194-p0-analysis-p1-protocol.md @@ -0,0 +1,320 @@ +# Challenge 194 P0 Analysis and P1 Protocol Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Download and locally verify the immutable P0 Pilot, produce a deterministic P0 analysis, and publish a frozen P1 refinement protocol without running P1. + +**Architecture:** Keep transfer, scientific aggregation, deterministic window selection, and protocol publication in separate units. All readers consume only a verified P0 root; all outputs are canonical bounded JSON published once and bound to the P0 run-spec/progress hashes. + +**Tech Stack:** Python 3.12, NumPy, h5py, pytest, rsync, existing `long_range_percolation` artifact and Pilot verifiers. + +## Global Constraints + +- P0 input contains exactly 96 verified trajectories. +- P0 progress SHA256 is `ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f`. +- P0 data remain immutable and exploratory. +- Whole trajectories are the independent units for means and standard errors. +- P1 selection uses only nonzero P0 checkpoints. +- P1 uses the existing `pilot` phase, a new grid namespace/master seed, and 16 replicas per `(sigma, L)`. +- P1 grids contain exactly nine ordered binary64 points serialized with `float.hex()`. +- Missing common brackets for `sigma <= 1` fail closed and request a versioned P0 extension. +- Sigma `1.1` is a crossover control and never receives a transition claim. + +--- + +### Task 1: Reproducible P0 Transfer and Local Verification + +**Files:** +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/download_pilot.sh` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/README.md` +- Test: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_download_pilot.py` + +**Interfaces:** +- Consumes: remote Pilot root, local destination, existing `scripts/run_pilot.py verify`. +- Produces: byte-preserving local Pilot root that passes semantic verification. + +- [ ] **Step 1: Write failing shell-contract tests** + +Test that the script requires absolute source/destination arguments, invokes +`rsync` with archive/checksum/partial-safe flags, refuses an existing +nonempty destination unless it is the same resumable root, and invokes the +exact local verifier after transfer. + +- [ ] **Step 2: Run the transfer tests and verify RED** + +Run: +`uv run --with pytest pytest tests/test_download_pilot.py -q` + +Expected: failure because `scripts/download_pilot.sh` does not exist. + +- [ ] **Step 3: Implement the transfer script** + +The script accepts: + +```text +download_pilot.sh +``` + +It uses `rsync --archive --checksum --partial --itemize-changes`, never +deletes remote or local files, writes scheduler/transfer logs outside the +immutable root, then runs: + +```bash +PYTHONPATH=/src scripts/run_pilot.py verify \ + --run-spec /run_spec.json +``` + +- [ ] **Step 4: Run focused tests and shell syntax** + +Run: + +```bash +uv run --with pytest pytest tests/test_download_pilot.py -q +bash -n scripts/download_pilot.sh +``` + +Expected: all pass. + +- [ ] **Step 5: Download and verify the real P0 root** + +Remote: +`wuzh02-jiangweiqi:/work/share/giggleliu/jiangweiqi/results/challenge-194/pilot-p0-739880d` + +Local: +`results/challenge-194/pilot-p0-739880d` + +Expected verifier result: +`{"cells": 96, "status": "verified", "trajectories": 96}`. + +- [ ] **Step 6: Commit** + +Commit message: +`Add reproducible P0 download verification` + +### Task 2: Bounded P0 Aggregation + +**Files:** +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py` + +**Interfaces:** +- Consumes: `Path` to verified P0 `run_spec.json`. +- Produces: + - `PilotEstimate` immutable records; + - `aggregate_p0(run_spec: Path) -> dict[str, object]`; + - canonical analysis document with exact source hashes. + +- [ ] **Step 1: Write failing aggregation tests** + +Create tiny verified test trajectories for two sizes, two replicas, and three +couplings. Assert exact grouping by `(sigma, length, kappa)`, arithmetic mean, +sample standard error with `ddof=1`, replica/request identities, fixed +observable-column names, deterministic ordering, and rejection of missing or +duplicate replicas. + +- [ ] **Step 2: Verify RED** + +Run: +`uv run --with pytest pytest tests/test_pilot_analysis.py -q` + +Expected: import failure for `pilot_analysis`. + +- [ ] **Step 3: Implement streaming aggregation** + +Use `load_pilot_run_spec` and `load_verified_trajectory`; hold one trajectory +at a time. Extract `Q_G`, four-sector crossing, `S1/L`, and `S2/L`. Accumulate +bounded per-cell vectors for eight replicas and emit: + +```python +{ + "sigma_hex": float(sigma).hex(), + "length": length, + "kappa_hex": float(kappa).hex(), + "replica_count": 8, + "means": {...}, + "standard_errors": {...}, + "request_sha256": [...], +} +``` + +- [ ] **Step 4: Add source binding** + +The document includes schema version, P0 run-spec SHA256, P0 progress SHA256, +source revision, analysis-plan SHA256, and an analysis-document SHA256 over +the unsigned canonical document. + +- [ ] **Step 5: Run aggregation tests** + +Run: +`uv run --with pytest pytest tests/test_pilot_analysis.py -q` + +Expected: all pass. + +- [ ] **Step 6: Commit** + +Commit message: +`Add bounded P0 observable aggregation` + +### Task 3: Deterministic Bracket Selection + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py` + +**Interfaces:** +- Consumes: validated P0 aggregate document. +- Produces: + - `select_p1_brackets(analysis: Mapping[str, object]) -> dict[str, object]`; + - explicit P0-extension failure document when a required bracket is absent. + +- [ ] **Step 1: Write failing selector tests** + +Cover: + +- a unique common `Q_G`/crossing-probability interval; +- multiple marked intervals selecting the narrowest, then lower coupling; +- no common interval for `sigma <= 1` producing `requires_p0_extension=true`; +- sigma `1.1` selecting maximum absolute crossing-probability slope; +- exact rejection of zero-coupling selection, reordered couplings, NaN, and + missing largest-size estimates. + +- [ ] **Step 2: Verify RED** + +Run the selector tests and confirm failure because the selector is absent. + +- [ ] **Step 3: Implement the frozen rule** + +Use the two largest lengths. Mark adjacent intervals exactly as specified in +the design. Never interpolate a transition estimate during P0 selection. +Serialize selected endpoint values with `float.hex()`, estimator evidence, and +tie-break metadata. + +- [ ] **Step 4: Verify GREEN** + +Run: +`uv run --with pytest pytest tests/test_pilot_analysis.py -q` + +Expected: all aggregation and selector tests pass. + +- [ ] **Step 5: Commit** + +Commit message: +`Freeze deterministic P1 bracket selection` + +### Task 4: Immutable P1 Protocol Publication + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py` + +**Interfaces:** +- Consumes: verified P0 root and selected brackets. +- Produces: + - immutable `p0_analysis.json`; + - immutable `p1_protocol.json`; + - `build_p1_protocol(...) -> dict[str, object]`. + +- [ ] **Step 1: Write failing protocol tests** + +Assert four sigma entries, three lengths, 16 fresh replicas, exact nine-point +grids, recursive bisection ordering, no P0 replica reuse, unique request/RNG +identities, canonical paths, protocol hash, no-clobber publication, and +rejection when any `sigma <= 1` bracket requires extension. + +- [ ] **Step 2: Verify RED** + +Run: + +```bash +uv run --with pytest pytest \ + tests/test_pilot_analysis.py tests/test_analyze_pilot_cli.py -q +``` + +Expected: protocol/CLI symbols are absent. + +- [ ] **Step 3: Implement recursive binary64 grids** + +Generate midpoint levels in order, deduplicate by binary64 bit identity, sort +strictly, require exactly nine points including endpoints, and store only +canonical hex strings in the protocol. + +- [ ] **Step 4: Implement fresh RNG assignment** + +Freeze a new master seed and P1 grid ID in the protocol. Derive every stream +identity using existing `derive_stream_material`, require uniqueness across +all P1 requests, and hash the complete ordered assignment. + +- [ ] **Step 5: Implement atomic CLI publication** + +Commands: + +```text +analyze-pilot.py analyze --run-spec ... --output p0_analysis.json +analyze-pilot.py build-p1 --analysis ... --output p1_protocol.json +analyze-pilot.py verify --analysis ... --p1-protocol ... +``` + +Use bounded descriptor reads and `_publish_json_once`; existing outputs verify +byte-for-byte or fail. + +- [ ] **Step 6: Run focused and full tests** + +Run: + +```bash +uv run --with pytest pytest \ + tests/test_pilot_analysis.py tests/test_analyze_pilot_cli.py -q +uv run --with pytest pytest -q +``` + +Expected: focused and full suites pass. + +- [ ] **Step 7: Analyze real P0 and publish P1 protocol** + +Run all three CLI commands against the downloaded P0 root. Record selected +windows, document hashes, and whether a P0 extension is required. + +- [ ] **Step 8: Commit** + +Commit message: +`Publish deterministic P1 refinement protocol` + +### Task 5: P0/P1 Documentation Boundary + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/README.md` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py` + +**Interfaces:** +- Consumes: implemented transfer/analysis/protocol commands. +- Produces: exact collaborator workflow and documentation contract tests. + +- [ ] **Step 1: Write failing documentation tests** + +Assert README contains exact P0 transfer, verify, analysis, P1 protocol, hash +inspection, and failure/extension commands without claiming P1 was executed. + +- [ ] **Step 2: Update documentation** + +Document the frozen selection rule, exploratory/confirmatory separation, +resource provenance, current hashes, restart behavior, and exact commands. + +- [ ] **Step 3: Run verification** + +Run: + +```bash +uv run --with pytest pytest tests/test_runtime.py -q +uv run --with pytest pytest -q +git diff --check +``` + +Expected: all pass and no whitespace errors. + +- [ ] **Step 4: Commit** + +Commit message: +`Document P0 analysis and P1 protocol workflow` diff --git a/docs/superpowers/plans/2026-07-30-challenge-194-p0-extension-v2.md b/docs/superpowers/plans/2026-07-30-challenge-194-p0-extension-v2.md new file mode 100644 index 000000000..a35a4629e --- /dev/null +++ b/docs/superpowers/plans/2026-07-30-challenge-194-p0-extension-v2.md @@ -0,0 +1,1072 @@ +# Challenge 194 Standalone Coarse-Grid P0 Extension v2 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build, submit, verify, and analyze the preregistered 192-cell standalone coarse-grid P0 extension v2, then publish an exploratory P1-v2 protocol only if all seven fail-closed authorization checks pass. + +**Architecture:** Generalize the release-ready v1 extension contracts, builders, runners, immutable publication, verified snapshots, and transfer machinery around an explicit versioned campaign contract; do not fork the scientific engine or selector. Finish the protocol/run-spec/worker critical path first and deploy that exact clean commit, then implement bounded v2 aggregation and authenticated authorization evidence locally while Slurm runs. Authorization v3 copies untouched authenticated P0 controls, uses only standalone v2 blocked-sigma rows, deeply recomputes every source, and feeds the byte-identical selector physics through a new schema adapter. + +**Tech Stack:** Python 3.12, NumPy, h5py, pytest, Ruff 0.16.0, Bash, Git bundles, rsync, Slurm via `scripts/harness_slurm.sh`, and the existing `long_range_percolation` Pilot/artifact/counter-RNG APIs. + +## Global Constraints + +- Repository: `/home/footman/code/quantum.harness-challenge-194`, branch `challenge/194`. +- Approved design: `docs/superpowers/specs/2026-07-30-challenge-194-p0-extension-v2-design.md` at commit `b67725339a1b0bb9a86a0b8711ae2bb980188f1c`. +- Design-file SHA256: `724403246992a9b31d462a85c69aa893aaf5dea2244451e58685c2c2994a917a`. +- Never modify or stage `.superpowers/sdd/task-1-report.md`, `.superpowers/sdd/progress.md`, generated `results/`, or unrelated dirty files. +- Original P0 hashes: run spec `d17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840`, progress `ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f`, analysis document `e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8`, analysis file `44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b`, bracket document `fb3df666044bf9531443fc00c5c2c2d489512b4162864b3a92ffc2e756832403`, source revision `739880d9ccdcffbfc8a15310250349bd11d63bbb`. +- V1 hashes: protocol document `a37ab41f3224594e61f4eebbe292975aeec449b9ecb7893e3e54f18d82d53321`, protocol file `e363a60f842b11b32972c7a68ec1c5f237741bc45bc79ab8bf93f51f6760d84d`, run spec `c1ca9b6c8ba751919c6d9337fe1cd4c09a57ed9b99abbb9d3ebfed7f89c3d32e`, progress `c78d1fb03daf19297ef9e0617410c68a6a364bffc2f2888dfa9067e7e8d6b65f`, analysis document `79232574d314348c29a40cd2fbb7690e96f3cae5f26843bd4f1cf07cb6a1f45b`, analysis file `d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5`, source revision `9308087c5c609519234da48136b88cdd60f79667`. +- Combined-v2 hashes: analysis document `36f85c40e9159ef2e69742672c261769fb28d2f3c947780ba63e4ef5fe5975c3`, analysis file `6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929`, bracket document `098f19d8883097d5f1f274ce759416328c086958fa5301c034a0b46dcbd562df`, bracket file `7a84d545b4526d94aa6f93ca4f0d264dcf01e518f2f9b04383921634786c9962`. +- Correctness hashes: report `036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8`, validation run spec `5b3eea4c460e14a57aec9df606447137d787a5c66dd7e98e1dffdcf566f430e2`, protocol `c7e980eeadaf8ed75e4d20cebb1e2c5d5f57a1cfc329afa7678ae586f5b7f488`, check registry `6e25ea41899544f2a9de3589beb1ee94b1f3dc505638b8f8e5164a4322b56a1d`, scientific engine `457fa669da897e59b03681039db6121fde4d7be9295bb46a743c8448875b3ee9`. +- V2 schemas: `challenge-194-p0-extension-protocol-v2`, `challenge-194-p0-extension-run-spec-v2`, `challenge-194-p0-extension-progress-v2`, `challenge-194-p0-extension-analysis-v2`, `challenge-194-p0-authorization-analysis-v3`, `challenge-194-p1-brackets-v3`, and conditional `challenge-194-p1-protocol-v2`. +- Sigmas: `0x1.ccccccccccccdp-1`, `0x1.0000000000000p+0`; lengths: `1024`, `16384`, `262144`; replicas: integers `40..71`; loop order: sigma, length, replica. +- Master seed `19_420_263_729`; phase `"pilot"`; namespace `"pilot-p0-extension-v2"`. +- Sigma `0.9` grid: `["0x0.0p+0","0x1.270b400000000p-1","0x1.5416800000000p-1","0x1.97a7600000000p-1","0x1.e848000000000p-1"]`; grid SHA256 `28155d7f982584787089f4a80d617783bd82b84e2ed833df3dcaa98955254d24`. +- Sigma `1.0` grid: `["0x0.0p+0","0x1.b0b85e0000000p-1","0x1.d8cb280000000p-1","0x1.14785e0000000p+0","0x1.3c8b280000000p+0"]`; grid SHA256 `b9abfff153302b8556312fbc5a59e6a8e7c98d8bd3c301cb90252c85a5c473f4`. +- Cardinality: 192 cells, 192 trajectories, five checkpoints each, 960 checkpoints, 30 v2 estimate rows, and 126 authorization rows. +- V2 identities must be disjoint from P0 replicas `0..7`, P1 `8..23`, and v1 `24..39`; compare complete request and stream-material registries, not labels alone. +- Preserve every v1 schema, artifact byte, public v1 function, exact P0/combined bracket output, and selector physics function body. Schema adapters may change; `_transition_evidence`, `_select_transition_bracket`, `_select_crossover_bracket`, thresholds, candidate ordering, tie-breaks, and zero rule may not. +- Authorization uses P0 sigma `0.8`/`1.1` rows byte-for-byte and standalone v2 sigma `0.9`/`1.0` rows only. Never union, pool, or interpolate P0/v1 blocked-sigma points. +- Canonical finite JSON, exact `float.hex()`, bounded descriptor reads, atomic no-clobber publication, immutable restart, external transfer state/logs, and fail-closed path/ABA checks remain mandatory. +- Wuzh02 only: `wzacnormal03`, one CPU, 1800 MiB, 40 minutes, no GPU, private node-local Numba cache, at most 40 concurrent cells with a lower account-limit cap when required. +- V2 and any P1 are exploratory only. This plan may publish a P1-v2 protocol but must not execute a P1 cell. + +--- + +## File Map + +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py`: immutable v1/v2 campaign contracts, authenticated v2 protocol builder, exact source-axis grid copy, and shared protocol validation. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py`: exact-schema contract registry and generic extension run-spec/cell/pending/merge/verify operations while preserving v1 public wrappers. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py`: generic contract-bound extension aggregation and authorization-v3 selector adapter; selector physics bodies remain unchanged. +- Create `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_authorization.py`: deep source authentication, authorization-analysis-v3, bracket-v3, seven-check evaluation, and P1-protocol-v2 construction. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py`: v2 protocol, analysis, authorization, selection, and conditional P1 commands. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py`: v2 run-spec build and exact-schema runtime dispatch. +- Create `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_slurm_common.sh`: shared sanitization/cache/launcher functions used by v1 and v2 wrappers. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh` and `pilot_extension_build_slurm.sh`: delegate common mechanics without changing v1 behavior. +- Create `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_v2_array_slurm.sh` and `pilot_extension_v2_build_slurm.sh`: thin v2 resource/task/path wrappers over the common shell library. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py`, `test_pilot.py`, `test_pilot_analysis.py`, `test_analyze_pilot_cli.py`, and `test_runtime.py`: v2 TDD plus exact v1/P0 regression locks. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md` before submission: frozen v2 execution contract. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/README.md`: exact build, submit, harvest, authorization, and conditional handoff commands. + +Dependency direction remains acyclic: `pilot_extension.py` may import selector parsing primitives from `pilot_analysis.py`; `pilot_analysis.py` imports extension constants only inside functions; `pilot_authorization.py` orchestrates both modules and neither imports it. + +## Critical Path + +Tasks 1–2 produce the exact submission revision. Task 3 deploys and submits immediately. Tasks 4–6 run locally while Slurm executes the frozen Task 2 commit. Task 7 harvests and verifies immutable evidence. Task 8 executes the seven-check gate and conditionally publishes P1-v2 without executing it. Task 9 records the separate post-pass P1-execution handoff. + +Do not modify `PILOT_PLAN.md`, scientific-engine files, v2 protocol/run-spec logic, shell worker logic, or the Task 2 submission commit after remote run-spec construction. Local Tasks 4–6 may add analysis-only code that authenticates the historical submission revision. + +### Task 1: Generalized Campaign Contract and Authenticated V2 Protocol + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py` + +**Interfaces:** +- Produces `P0ExtensionCampaign`, `V1_EXTENSION`, and `V2_EXTENSION`. +- Produces `build_p0_extension_v2_protocol(sources: V2ProtocolSources) -> dict[str, object]`. +- Produces `validate_p0_extension_v2_protocol(protocol: Mapping[str, object], sources: V2ProtocolSources, *, expected_source_revision: str) -> None`. +- Produces CLI `build-p0-extension-v2` with explicit P0/v1/combined trust inputs. + +- [ ] **Step 1: Write exact failing contract and source-authentication tests** + +Add: + +```python +@dataclass(frozen=True) +class V2ProtocolSources: + p0_analysis: Mapping[str, object] + p0_evidence_root: Path + v1_analysis: Mapping[str, object] + v1_run_spec: Path + v1_protocol: Mapping[str, object] + combined_v2_analysis: Mapping[str, object] + +def test_v2_protocol_copies_exact_authenticated_axes_and_is_disjoint(): + sources = _real_v2_protocol_sources() + protocol = extension.build_p0_extension_v2_protocol(sources) + assert protocol["schema_version"] == "challenge-194-p0-extension-protocol-v2" + assert protocol["replicas"] == list(range(40, 72)) + assert protocol["cell_count"] == 192 + entries = {entry["sigma_hex"]: entry for entry in protocol["sigma_entries"]} + assert entries[(0.9).hex()]["kappas"] == V2_GRIDS[(0.9).hex()] + assert entries[(1.0).hex()]["kappas"] == V2_GRIDS[(1.0).hex()] + assert {cell["request_sha256"] for cell in protocol["cells"]}.isdisjoint( + _all_p0_v1_p1_request_hashes(sources) + ) +``` + +Add validly rehashed mutations for every source/file/document hash, source +revision, design hash, grid string/order/hash, sigma/length/replica order, +seed/phase/namespace, request, stream material, and cell path. Each must reach +the intended semantic validator and raise a field-specific `RuntimeError`. + +- [ ] **Step 2: Run RED** + +```bash +cd /home/footman/code/quantum.harness-challenge-194/tracks/qmc/solutions/frustration-free/challenge-194 +uv run --with pytest pytest \ + tests/test_pilot_extension.py -q -k "v2_protocol or v2_source" +``` + +Expected: FAIL because `V2ProtocolSources` and +`build_p0_extension_v2_protocol` do not exist; existing v1 tests remain green. + +- [ ] **Step 3: Generalize v1 protocol mechanics without changing v1 output** + +Implement: + +```python +@dataclass(frozen=True) +class P0ExtensionCampaign: + protocol_schema: str + run_spec_schema: str + progress_schema: str + analysis_schema: str + production_kind: str + sigmas: tuple[float, ...] + lengths: tuple[int, ...] + replicas: tuple[int, ...] + master_seed: int + phase: str + grid_namespace: str + grids: Mapping[str, tuple[str, ...]] + grid_hashes: Mapping[str, str] + cell_count: int + +V2_EXTENSION = P0ExtensionCampaign( + protocol_schema="challenge-194-p0-extension-protocol-v2", + run_spec_schema="challenge-194-p0-extension-run-spec-v2", + progress_schema="challenge-194-p0-extension-progress-v2", + analysis_schema="challenge-194-p0-extension-analysis-v2", + production_kind="p0-extension-v2", + sigmas=(0.9, 1.0), + lengths=(2**10, 2**14, 2**18), + replicas=tuple(range(40, 72)), + master_seed=19_420_263_729, + phase="pilot", + grid_namespace="pilot-p0-extension-v2", + grids=V2_GRIDS, + grid_hashes=V2_GRID_HASHES, + cell_count=192, +) +``` + +Move repeated cell/request/stream construction into private helpers accepting +`P0ExtensionCampaign`. Keep `build_p0_extension_protocol` and +`validate_p0_extension_protocol` as v1 wrappers and assert their real protocol +bytes/hash remain unchanged. + +- [ ] **Step 4: Authenticate v2 inputs and build the protocol** + +`build_p0_extension_v2_protocol` must: + +1. deeply verify P0 root and exact P0 analysis; +2. deeply verify v1 root, recompute v1 analysis, and require byte identity; +3. semantically recompute combined-v2 and require byte identity; +4. copy the five exact strings from authenticated combined axes; +5. verify the two fixed grid hashes and exact design hash; +6. reconstruct P0/v1/reserved-P1 identities before assigning 192 v2 cells; +7. emit purpose `exploratory-grid-topology-sensitivity-and-p1-authorization-only`. + +No caller-provided digest may substitute for a verified source. + +- [ ] **Step 5: Add immutable CLI publication** + +Register: + +```text +build-p0-extension-v2 + --p0-analysis PATH + --p0-evidence-root DIR + --v1-analysis PATH + --v1-run-spec PATH + --v1-protocol PATH + --combined-v2-analysis PATH + --output PATH +``` + +All paths are required, absolute after resolution, canonical, and non-symlink. +First invocation returns `published`; byte-identical retry returns +`verified-existing`; changed installed bytes fail without replacement. + +- [ ] **Step 6: Run GREEN and commit** + +```bash +uv run --with pytest pytest \ + tests/test_pilot_extension.py tests/test_analyze_pilot_cli.py -q +uv run --with ruff==0.16.0 ruff check --ignore SIM102,TRY004,UP017,PYI025,F401 \ + src/long_range_percolation/pilot_extension.py \ + scripts/analyze_pilot.py tests/test_pilot_extension.py tests/test_analyze_pilot_cli.py +uv run python -m compileall -q \ + src/long_range_percolation/pilot_extension.py scripts/analyze_pilot.py +git diff --check +``` + +Expected: all commands exit 0; v1 real protocol hash remains +`a37ab41...`; original and combined bracket hashes remain exact. + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py +git commit -m "Add authenticated P0 extension v2 protocol" +``` + +### Task 2: V2 Runtime, Shared Slurm Wrappers, and Submission Release Gate + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_slurm_common.sh` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_build_slurm.sh` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_v2_array_slurm.sh` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_v2_build_slurm.sh` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/README.md` + +**Interfaces:** +- Produces `build_registered_extension_run_spec(..., campaign: P0ExtensionCampaign, sources: V2ProtocolSources)`. +- Preserves all v1 public run functions; adds `load/run/pending/merge/verify_p0_extension_v2`. +- Produces CLI `build-extension-v2-spec`. +- Produces exact 192-task v2 build/worker wrappers sharing v1 shell mechanics. + +- [ ] **Step 1: Write RED runtime and wrapper tests** + +```python +def test_v2_run_spec_and_runtime_dispatch_are_exact(tmp_path: Path): + path = pilot._write_test_extension_run_spec( + tmp_path / "v2", campaign=extension.V2_EXTENSION + ) + spec = pilot.load_p0_extension_v2_run_spec( + path, verify_current_environment=False + ) + assert spec["schema_version"] == extension.V2_EXTENSION.run_spec_schema + assert spec["cell_count"] == 192 + assert pilot.pending_p0_extension_v2_cells( + path, verify_current_environment=False + ) == list(range(192)) + with pytest.raises(RuntimeError, match="v1"): + pilot.load_p0_extension_run_spec(path, verify_current_environment=False) + +def test_v2_worker_contract(): + text = (SCRIPTS / "pilot_extension_v2_array_slurm.sh").read_text() + assert "#SBATCH --cpus-per-task=1" in text + assert "#SBATCH --mem=1800M" in text + assert "#SBATCH --time=00:40:00" in text + assert "SLURM_ARRAY_TASK_ID > 192" in text +``` + +Test complete restart boundaries, duplicate workers, `.partial`/`.intent`, +swapped cells, unknown fields, exact progress schema, 192-only merge, and v1 +96-cell behavior. + +- [ ] **Step 2: Run RED** + +```bash +uv run --with pytest pytest \ + tests/test_pilot.py tests/test_pilot_extension.py tests/test_runtime.py -q \ + -k "extension_v2 or v2_worker or v1_extension_regression" +``` + +Expected: FAIL for missing v2 runtime/wrappers; all selected v1 regression +tests pass. + +- [ ] **Step 3: Register the exact v2 runtime contract** + +Extend `_contract_for_schema` with `V2_EXTENSION` and refactor extension public +functions through private contract-taking operations. Keep: + +```python +def build_p0_extension_v2_run_spec( + output_root: Path, + validation_report: Path, + protocol: Mapping[str, object], + sources: V2ProtocolSources, +) -> dict[str, object]: ... + +def load_p0_extension_v2_run_spec( + path: Path, verify_current_environment: bool = True +) -> dict[str, object]: ... +``` + +Add corresponding run/pending/merge/verify wrappers. Never infer version from +filename or accept a boolean downgrade to test schema. + +- [ ] **Step 4: Add `build-extension-v2-spec`** + +Require the protocol, validation report, all `V2ProtocolSources` paths, +output root, and exact `output_root / "run_spec.json"`. The test parses stdout +and requires `status == "ready"`, `cells == 192`, the exact resolved run-spec +path, a 64-character lowercase hexadecimal `run_spec_sha256`, and equality +between that digest and a fresh SHA256 of the canonical unsigned run spec. It +does not hard-code a hash that cannot exist before the future submission +revision is known. + +- [ ] **Step 5: Share shell mechanics and create thin v2 wrappers** + +Move environment removal, thread pins, canonical path checks, private cache +creation, and exact Python launch into `pilot_extension_slurm_common.sh`. +V1 wrappers source it and retain their exact scientific paths and `1..96` +mapping. V2 worker accepts canonical decimal IDs `1..192`, maps `ID-1`, and +executes `run-cell` against the authenticated run spec. + +V2 build wrapper derives these fixed paths from the results root: + +```text +p0_analysis.json +pilot-p0-739880d +p0_extension_v1_protocol.json +pilot-p0-extension-v1/run_spec.json +p0_extension_v1_analysis.json +p0_combined_analysis_v2.json +p0_extension_v2_protocol.json +pilot-p0-extension-v2/run_spec.json +validation-prod-877ab93/report/report.json +``` + +It runs `build-p0-extension-v2` then `build-extension-v2-spec`. Existing +different bytes or any missing/hash-mismatched input fail closed. + +- [ ] **Step 6: Freeze docs before run-spec construction** + +Add exact constants, schemas, artifacts, resources, smoke IDs +`1,65,97,161`, remaining array, concurrency cap, restart, transfer, seven +checks, and exploratory boundary to `PILOT_PLAN.md` and README. State no v2 +data or P1-v2 protocol exists yet. + +- [ ] **Step 7: Run the submission release gate** + +```bash +bash -n \ + scripts/pilot_extension_slurm_common.sh \ + scripts/pilot_extension_array_slurm.sh \ + scripts/pilot_extension_build_slurm.sh \ + scripts/pilot_extension_v2_array_slurm.sh \ + scripts/pilot_extension_v2_build_slurm.sh +uv run --with pytest pytest -q +uv run --with ruff==0.16.0 ruff check --ignore SIM102,TRY004,UP017,PYI025,F401 \ + src/long_range_percolation/pilot.py \ + src/long_range_percolation/pilot_extension.py \ + scripts/run_pilot.py scripts/analyze_pilot.py \ + tests/test_pilot.py tests/test_pilot_extension.py \ + tests/test_analyze_pilot_cli.py tests/test_runtime.py +uv run --with ruff==0.16.0 ruff format --check \ + src/long_range_percolation/pilot.py \ + src/long_range_percolation/pilot_extension.py \ + scripts/run_pilot.py scripts/analyze_pilot.py \ + tests/test_pilot.py tests/test_pilot_extension.py \ + tests/test_analyze_pilot_cli.py tests/test_runtime.py +git diff --check +``` + +Expected: shell checks silent, pytest zero failures, Ruff `All checks passed!`, +format clean, diff check silent. Verify selector function-source bytes and all +four immutable analysis/bracket files remain unchanged. + +- [ ] **Step 8: Commit and record the immutable submission revision** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_slurm_common.sh \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_build_slurm.sh \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_v2_array_slurm.sh \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_v2_build_slurm.sh \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py \ + tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md \ + tracks/qmc/solutions/frustration-free/challenge-194/README.md +git commit -m "Prepare standalone P0 extension v2 campaign" +git rev-parse HEAD +``` + +Expected: one clean submission commit; record its full SHA as `SUBMIT_SHA`. + +### Task 3: Clean Wuzh02 Deployment, Smoke Gate, and Early Submission + +**Files:** +- Read only: `skills/using-slurm/profiles/wuzh02-jiangweiqi.toml` +- Read only: `scripts/harness_slurm.sh` +- Generated outside Git: bundle/deployment, immutable v2 root, and external scheduler logs. + +**Interfaces:** +- Consumes exact Task 2 `SUBMIT_SHA`. +- Produces build job ID, smoke job ID, full-array job ID, immutable remote run spec, and external no-clobber logs. + +- [ ] **Step 1: Set exact paths and precheck** + +```bash +cd /home/footman/code/quantum.harness-challenge-194 +export HARNESS_CLUSTER_PROFILE=wuzh02-jiangweiqi +export SUBMIT_SHA="$(git rev-parse HEAD)" +export SHORT_SHA="${SUBMIT_SHA:0:7}" +export REMOTE_RESULTS="/work/share/giggleliu/jiangweiqi/results/challenge-194" +export REMOTE_REPO="/work/share/giggleliu/jiangweiqi/quantum.harness-p0-extension-v2-${SHORT_SHA}" +export REMOTE_BUNDLE="/work/share/giggleliu/jiangweiqi/challenge-194-p0-extension-v2-${SHORT_SHA}.bundle" +export LOCAL_BUNDLE="/tmp/challenge-194-p0-extension-v2-${SHORT_SHA}.bundle" +export REMOTE_ROOT="${REMOTE_RESULTS}/pilot-p0-extension-v2" +export REMOTE_PYTHON="/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194/.venv/bin/python" +scripts/harness_slurm.sh precheck +scripts/harness_slurm.sh probe-partitions +``` + +Expected: Wuzh02 SSH passes, `wzacnormal03` is available, and the only local +dirty path is the protected pre-existing scratch report. Stop on any other +dirty path. + +- [ ] **Step 2: Ship committed bytes and required immutable inputs no-clobber** + +Create and install the Git bundle: + +```bash +git bundle create "${LOCAL_BUNDLE}" challenge/194 +BUNDLE_SHA256="$(sha256sum "${LOCAL_BUNDLE}" | awk '{print $1}')" +BUNDLE_STAGE="${REMOTE_BUNDLE}.upload-${SUBMIT_SHA}-$(date -u +%Y%m%dT%H%M%S%N)-$$" +ssh wuzh02-jiangweiqi "test ! -e '${REMOTE_BUNDLE}' && test ! -e '${REMOTE_REPO}' && test ! -e '${BUNDLE_STAGE}'" +scp "${LOCAL_BUNDLE}" "wuzh02-jiangweiqi:${BUNDLE_STAGE}" +ssh wuzh02-jiangweiqi " + set -euo pipefail + test \"\$(sha256sum '${BUNDLE_STAGE}' | awk '{print \$1}')\" = '${BUNDLE_SHA256}' + ln -- '${BUNDLE_STAGE}' '${REMOTE_BUNDLE}' + sync -f -- '${REMOTE_BUNDLE}' + test \"\$(sha256sum '${REMOTE_BUNDLE}' | awk '{print \$1}')\" = '${BUNDLE_SHA256}' + rm -- '${BUNDLE_STAGE}' + git clone '${REMOTE_BUNDLE}' '${REMOTE_REPO}' + git -C '${REMOTE_REPO}' checkout --detach '${SUBMIT_SHA}' + test -z \"\$(git -C '${REMOTE_REPO}' status --porcelain)\" +" +``` + +Use this exact no-clobber helper for required JSON files: + +```bash +publish_remote_file() { + local local_path="$1" remote_path="$2" expected_sha="$3" + local stage + test "$(sha256sum "${local_path}" | awk '{print $1}')" = "${expected_sha}" + if ssh wuzh02-jiangweiqi "test -e '${remote_path}'"; then + ssh wuzh02-jiangweiqi \ + "test \"\$(sha256sum '${remote_path}' | awk '{print \$1}')\" = '${expected_sha}'" + return + fi + stage="${remote_path}.upload-${SUBMIT_SHA}-$(date -u +%Y%m%dT%H%M%S%N)-$$" + ssh wuzh02-jiangweiqi "test ! -e '${stage}'" + scp "${local_path}" "wuzh02-jiangweiqi:${stage}" + ssh wuzh02-jiangweiqi " + set -euo pipefail + test \"\$(sha256sum '${stage}' | awk '{print \$1}')\" = '${expected_sha}' + ln -- '${stage}' '${remote_path}' + sync -f -- '${remote_path}' + test \"\$(sha256sum '${remote_path}' | awk '{print \$1}')\" = '${expected_sha}' + rm -- '${stage}' + " +} + +publish_remote_file results/challenge-194/p0_analysis.json \ + "${REMOTE_RESULTS}/p0_analysis.json" \ + 44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b +publish_remote_file results/challenge-194/p0_extension_v1_protocol.json \ + "${REMOTE_RESULTS}/p0_extension_v1_protocol.json" \ + e363a60f842b11b32972c7a68ec1c5f237741bc45bc79ab8bf93f51f6760d84d +publish_remote_file results/challenge-194/p0_extension_v1_analysis.json \ + "${REMOTE_RESULTS}/p0_extension_v1_analysis.json" \ + d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5 +publish_remote_file results/challenge-194/p0_combined_analysis_v2.json \ + "${REMOTE_RESULTS}/p0_combined_analysis_v2.json" \ + 6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929 +``` + +Existing final paths with different bytes and failed staging paths remain +preserved outside immutable run roots and fail closed. + +Verify exact remote roots, correctness input, and offline interpreter: + +```bash +ssh wuzh02-jiangweiqi " + set -euo pipefail + test \"\$(sha256sum '${REMOTE_RESULTS}/pilot-p0-739880d/run_spec.json' | awk '{print \$1}')\" = d17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840 + test \"\$(sha256sum '${REMOTE_RESULTS}/pilot-p0-739880d/progress.json' | awk '{print \$1}')\" = ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f + test \"\$(sha256sum '${REMOTE_RESULTS}/pilot-p0-extension-v1/run_spec.json' | awk '{print \$1}')\" = c1ca9b6c8ba751919c6d9337fe1cd4c09a57ed9b99abbb9d3ebfed7f89c3d32e + test \"\$(sha256sum '${REMOTE_RESULTS}/pilot-p0-extension-v1/progress.json' | awk '{print \$1}')\" = c78d1fb03daf19297ef9e0617410c68a6a364bffc2f2888dfa9067e7e8d6b65f + test \"\$(sha256sum '${REMOTE_RESULTS}/validation-prod-877ab93/report/report.json' | awk '{print \$1}')\" = 036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8 + test -x '${REMOTE_PYTHON}' +" +``` + +- [ ] **Step 3: Build the remote protocol and run spec** + +Feasibility-check, then submit: + +```bash +HARNESS_REPO_REMOTE="${REMOTE_REPO}" scripts/harness_slurm.sh submit \ + --test-only \ + --script tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_v2_build_slurm.sh \ + --run-spec "${REMOTE_RESULTS}/p0_combined_analysis_v2.json" \ + --entrypoint "${REMOTE_REPO}" --command "${REMOTE_PYTHON}" \ + --partition wzacnormal03 --time 00:10:00 --cpus 1 \ + --extra "--mem=1800M" +``` + +Submit the identical command without `--test-only`, capture `BUILD_JOB_ID`, +wait for success, then run remote `pending`. Parse its canonical JSON and +require `status == "pending"`, `count == 192`, and +`cell_indices == list(range(192))`. + +- [ ] **Step 4: Run the exact four-cell smoke gate** + +```bash +HARNESS_REPO_REMOTE="${REMOTE_REPO}" scripts/harness_slurm.sh submit \ + --test-only \ + --script tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_v2_array_slurm.sh \ + --run-spec "${REMOTE_ROOT}/run_spec.json" \ + --entrypoint "${REMOTE_REPO}" --command "${REMOTE_PYTHON}" \ + --partition wzacnormal03 --time 00:40:00 --cpus 1 \ + --extra "--mem=1800M --array=1,65,97,161%4" +``` + +Submit only after feasibility succeeds. Capture `SMOKE_JOB_ID`. All four jobs +must exit 0, publish verified complete trajectories/manifests, leave no +partial/intent files, and pass deep `pending`/cell verification. Otherwise +stop; retry only infrastructure failure under identical identities. + +- [ ] **Step 5: Determine safe concurrency and submit remaining cells** + +Set `ACCOUNT_CAP` from the profile/account limit. Use: + +```bash +if (( ACCOUNT_CAP < 1 )); then exit 64; fi +if (( ACCOUNT_CAP > 40 )); then ARRAY_CAP=40; else ARRAY_CAP="${ACCOUNT_CAP}"; fi +ARRAY_EXPR="2-64,66-96,98-160,162-192%${ARRAY_CAP}" +HARNESS_REPO_REMOTE="${REMOTE_REPO}" scripts/harness_slurm.sh submit \ + --test-only \ + --script tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_v2_array_slurm.sh \ + --run-spec "${REMOTE_ROOT}/run_spec.json" \ + --entrypoint "${REMOTE_REPO}" --command "${REMOTE_PYTHON}" \ + --partition wzacnormal03 --time 00:40:00 --cpus 1 \ + --extra "--mem=1800M --array=${ARRAY_EXPR}" +``` + +After feasibility succeeds, rerun the displayed command without +`--test-only`. Capture `ARRAY_JOB_ID`. Expected: 188 remaining tasks, no more +than `ARRAY_CAP` concurrent cells; lowering the cap changes no scientific +identity. + +- [ ] **Step 6: Monitor without treating scheduler success as evidence** + +Use `status` and `classify` for all three IDs. Inspect pending reason, one +startup log, memory/time classification, and final `sacct`. Do not delete logs, +resubmit altered resources, or infer scientific acceptance. Task 7 performs +the evidence gate. + +### Task 4: Bounded Standalone V2 Aggregation While Slurm Runs + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py` + +**Interfaces:** +- Produces `aggregate_p0_extension_v2(run_spec: Path, protocol: Mapping[str, object]) -> dict[str, object]`. +- Produces test-only `_aggregate_test_registered_extension(run_spec: Path, protocol: Mapping[str, object], *, campaign: P0ExtensionCampaign) -> dict[str, object]`. +- Preserves `aggregate_p0_extension` v1 bytes/output. + +- [ ] **Step 1: Add RED bounded aggregation tests** + +Use tiny contract fixtures and assert exact sigma/length/kappa/request order, +`ddof=1`, one live trajectory at a time, 32 replicas, 30 production rows, +protocol/run/progress hashes, retained verified snapshot, and rejection of +swaps, stale markers, forged progress, extras, and unknown schema. + +```python +def test_v2_aggregation_has_exact_standalone_shape(tmp_path: Path): + protocol, run_spec = _write_test_v2_extension(tmp_path) + analysis = pilot_analysis._aggregate_test_registered_extension( + run_spec, protocol, campaign=extension.V2_EXTENSION + ) + assert analysis["schema_version"] == extension.V2_EXTENSION.analysis_schema + assert len(analysis["estimates"]) == 30 + assert { + row["replica_count"] for row in analysis["estimates"] + } == {32} + assert [ + (row["sigma_hex"], row["length"], row["kappa_hex"]) + for row in analysis["estimates"] + ] == [ + (sigma.hex(), length, kappa) + for sigma in extension.V2_EXTENSION.sigmas + for length in extension.V2_EXTENSION.lengths + for kappa in extension.V2_EXTENSION.grids[sigma.hex()] + ] +``` + +- [ ] **Step 2: Run RED** + +```bash +uv run --with pytest pytest tests/test_pilot_analysis.py -q -k extension_v2 +``` + +Expected: FAIL because `aggregate_p0_extension_v2` is absent. + +- [ ] **Step 3: Generalize the internal aggregator by campaign** + +Implement private `_aggregate_registered_extension(..., campaign)` and keep +separate exact public v1/v2 wrappers. V2 requires shape `(32, 5, 4)` per +sigma/length group, five ten-column checkpoints per trajectory, 30 rows, and +schema `challenge-194-p0-extension-analysis-v2`. + +- [ ] **Step 4: Run GREEN and commit** + +```bash +uv run --with pytest pytest tests/test_pilot_analysis.py tests/test_pilot.py -q +uv run --with ruff==0.16.0 ruff check --ignore SIM102,TRY004,UP017,PYI025,F401 \ + src/long_range_percolation/pilot_analysis.py tests/test_pilot_analysis.py +git diff --check +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py +git commit -m "Add bounded P0 extension v2 aggregation" +``` + +Expected: all tests pass; v1 analysis recomputation remains byte-identical. + +### Task 5: Authenticated Authorization Evidence, Selector V3, and P1-V2 Builder + +**Files:** +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_authorization.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_authorization.py` + +**Interfaces:** +- Produces `AuthorizationSources`. +- Produces `build_p0_authorization_evidence(sources: AuthorizationSources) -> dict[str, object]`. +- Produces `select_authorized_p1_brackets(analysis: Mapping[str, object], sources: AuthorizationSources) -> dict[str, object]`. +- Produces `build_authorized_p1_v2_protocol(analysis, brackets, sources) -> dict[str, object]`. + +- [ ] **Step 1: Write RED source-recomputation and no-union tests** + +```python +@dataclass(frozen=True) +class AuthorizationSources: + p0_analysis: Mapping[str, object] + p0_evidence_root: Path + v1_analysis: Mapping[str, object] + v1_run_spec: Path + v1_protocol: Mapping[str, object] + combined_v2_analysis: Mapping[str, object] + v2_analysis: Mapping[str, object] + v2_run_spec: Path + v2_protocol: Mapping[str, object] + +def test_authorization_uses_controls_and_standalone_v2_only(): + result = build_p0_authorization_evidence(_authorization_sources()) + entries = {entry["sigma_hex"]: entry for entry in result["sigma_entries"]} + assert entries[(0.8).hex()]["source_role"] == "p0-control" + assert entries[(0.9).hex()]["source_role"] == "v2-standalone" + assert entries[(1.0).hex()]["source_role"] == "v2-standalone" + assert entries[(1.1).hex()]["source_role"] == "p0-control" + assert result["estimate_count"] == 126 + assert all( + row["replica_count"] == 32 + for sigma in ((0.9).hex(), (1.0).hex()) + for row in entries[sigma]["estimates"] + ) +``` + +Add a malicious self-signed authorization JSON with valid outer digest but +forged P0 controls/v2 means; validation must recompute sources and reject it. +Add P0/v1 blocked-point injection and pooling tests; both must fail before +selection. + +- [ ] **Step 2: Run RED** + +```bash +uv run --with pytest pytest tests/test_pilot_authorization.py -q +``` + +Expected: collection fails because `pilot_authorization` is absent. + +- [ ] **Step 3: Implement deep source authentication** + +Reverify P0 root/analysis, v1 root/protocol/analysis, semantic combined-v2, +v2 root/protocol/analysis, exact design, source revisions, request +disjointness, and all fixed hashes. Recompute analyses from roots and require +canonical byte identity before constructing any authorization row. + +- [ ] **Step 4: Build authorization-analysis-v3** + +Copy P0 `0.8`/`1.1` entries byte-for-byte and v2 `0.9`/`1.0` entries +byte-for-byte into ordered per-sigma entries with explicit source roles. +Require 16/5/5/16 couplings per length and exactly 126 rows. Record every +source file/document/run/progress/protocol hash and unsigned canonical digest. + +- [ ] **Step 5: Add schema adapter without changing selector physics** + +Add only authorization-v3 normalization in `pilot_analysis.py`; call unchanged +selection bodies. Emit bracket schema `challenge-194-p1-brackets-v3`. +Regression tests compare exact function source bytes and exact historical +bracket documents before/after. + +- [ ] **Step 6: Implement conditional P1-v2 construction** + +Require four selected statuses, exact control windows, bracket-v3 byte +identity, `requires_p0_extension is False`, and all trusted sources. +Preserve P1 seed `19_420_261_729`, replicas `8..23`, phase `"pilot"`, four +sigmas, three lengths, and nine selector-derived points. Emit +`challenge-194-p1-protocol-v2`; do not execute cells. + +- [ ] **Step 7: Run GREEN and commit** + +```bash +uv run --with pytest pytest \ + tests/test_pilot_authorization.py tests/test_pilot_analysis.py -q +uv run --with ruff==0.16.0 ruff check --ignore SIM102,TRY004,UP017,PYI025,F401 \ + src/long_range_percolation/pilot_authorization.py \ + src/long_range_percolation/pilot_analysis.py \ + tests/test_pilot_authorization.py tests/test_pilot_analysis.py +git diff --check +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_authorization.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_authorization.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py +git commit -m "Add authenticated P0 authorization evidence" +``` + +### Task 6: Analysis CLI, Documentation, and Full Local Analysis Gate + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/README.md` + +**Interfaces:** +- Produces `analyze-extension-v2`, `authorize-v2`, `select-authorization-v3`, and `build-p1-v2`. + +- [ ] **Step 1: Add RED immutable CLI tests** + +For every command test required explicit trusted paths, canonical bounded +reads, first publish, byte-identical retry, changed-byte refusal, self-signed +source refusal, missing output on scientific failure, and no legacy mixed +arguments. + +```python +@pytest.mark.parametrize( + "command", + [ + "analyze-extension-v2", + "authorize-v2", + "select-authorization-v3", + "build-p1-v2", + ], +) +def test_v2_commands_publish_once_and_require_trusted_sources( + command: str, tmp_path: Path +): + arguments = _complete_v2_cli_arguments(command, tmp_path) + assert analyze_cli.main(arguments) == 0 + output = Path(arguments[arguments.index("--output") + 1]) + installed = output.read_bytes() + assert analyze_cli.main(arguments) == 0 + assert output.read_bytes() == installed + tampered = _validly_rehashed_self_signed_source(arguments, tmp_path) + assert analyze_cli.main(tampered) == 1 + assert output.read_bytes() == installed +``` + +- [ ] **Step 2: Run RED** + +```bash +uv run --with pytest pytest tests/test_analyze_pilot_cli.py -q -k "v2 or authorization" +``` + +Expected: argparse rejects the four absent commands. + +- [ ] **Step 3: Implement exact commands** + +```text +analyze-extension-v2 --run-spec PATH --protocol PATH --output PATH +authorize-v2 --p0-analysis PATH --p0-evidence-root DIR --v1-analysis PATH --v1-run-spec PATH --v1-protocol PATH --combined-v2-analysis PATH --v2-analysis PATH --v2-run-spec PATH --v2-protocol PATH --output PATH +select-authorization-v3 --analysis PATH --p0-analysis PATH --p0-evidence-root DIR --v1-analysis PATH --v1-run-spec PATH --v1-protocol PATH --combined-v2-analysis PATH --v2-analysis PATH --v2-run-spec PATH --v2-protocol PATH --output PATH +build-p1-v2 --analysis PATH --brackets PATH --p0-analysis PATH --p0-evidence-root DIR --v1-analysis PATH --v1-run-spec PATH --v1-protocol PATH --combined-v2-analysis PATH --v2-analysis PATH --v2-run-spec PATH --v2-protocol PATH --output PATH +``` + +No command trusts `--analysis` or `--brackets` alone. Each rebuilds trusted +evidence in memory before publication. + +- [ ] **Step 4: Document exact local workflow** + +Use artifact names from the design. State prominently that v1 points are +preserved but not unioned, all data are exploratory, and P1 execution is a +separate reviewed plan. + +- [ ] **Step 5: Run full local gate and commit** + +```bash +uv run --with pytest pytest -q +uv run --with ruff==0.16.0 ruff check --ignore SIM102,TRY004,UP017,PYI025,F401 \ + src/long_range_percolation/pilot.py \ + src/long_range_percolation/pilot_extension.py \ + src/long_range_percolation/pilot_analysis.py \ + src/long_range_percolation/pilot_authorization.py \ + scripts/run_pilot.py scripts/analyze_pilot.py \ + tests/test_pilot.py tests/test_pilot_extension.py \ + tests/test_pilot_analysis.py tests/test_pilot_authorization.py \ + tests/test_analyze_pilot_cli.py tests/test_runtime.py +uv run --with ruff==0.16.0 ruff format --check \ + src/long_range_percolation/pilot.py \ + src/long_range_percolation/pilot_extension.py \ + src/long_range_percolation/pilot_analysis.py \ + src/long_range_percolation/pilot_authorization.py \ + scripts/run_pilot.py scripts/analyze_pilot.py \ + tests/test_pilot.py tests/test_pilot_extension.py \ + tests/test_pilot_analysis.py tests/test_pilot_authorization.py \ + tests/test_analyze_pilot_cli.py tests/test_runtime.py +bash -n scripts/pilot_extension*.sh scripts/download_pilot.sh +git diff --check +``` + +Expected: zero failures, Ruff clean, formatting clean, shell checks and diff +check silent. + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py \ + tracks/qmc/solutions/frustration-free/challenge-194/README.md +git commit -m "Document P0 extension v2 authorization workflow" +``` + +### Task 7: Harvest, Merge, Download, and Deep Verify + +**Files:** +- Generated outside Git: remote/local v2 roots and sibling transfer/scheduler logs. + +**Interfaces:** +- Consumes `BUILD_JOB_ID`, `SMOKE_JOB_ID`, `ARRAY_JOB_ID`, `SUBMIT_SHA`. +- Produces exact local verified 192-cell/192-trajectory v2 root. + +- [ ] **Step 1: Classify jobs and query pending cells** + +Run `status` and `classify` for all IDs, then remote `pending`. Expected count +0. For infrastructure failures only, obtain approval and resubmit exact failed +task IDs under the unchanged run spec; never alter resources/science silently. + +- [ ] **Step 2: Merge and verify remotely** + +```bash +ssh wuzh02-jiangweiqi " + set -euo pipefail + export PYTHONPATH='${REMOTE_REPO}/tracks/qmc/solutions/frustration-free/challenge-194/src' + '${REMOTE_PYTHON}' '${REMOTE_REPO}/tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py' merge \ + --run-spec '${REMOTE_ROOT}/run_spec.json' + '${REMOTE_PYTHON}' '${REMOTE_REPO}/tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py' verify \ + --run-spec '${REMOTE_ROOT}/run_spec.json' +" +``` + +Expected: +`{"cells":192,"status":"verified","trajectories":192}`. + +- [ ] **Step 3: Download through hardened external state** + +```bash +cd /home/footman/code/quantum.harness-challenge-194/tracks/qmc/solutions/frustration-free/challenge-194 +scripts/download_pilot.sh \ + wuzh02-jiangweiqi \ + /work/share/giggleliu/jiangweiqi/results/challenge-194/pilot-p0-extension-v2 \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-extension-v2 \ + /home/footman/code/quantum.harness-challenge-194/.venv/bin/python +``` + +Expected exact 192/192 verifier JSON. Claims, source/completion records, +diagnostics, and uniquely named logs remain siblings outside the root. + +- [ ] **Step 4: Reverify and repeat completed download** + +Run local `run_pilot.py verify`, record run/progress hashes, then repeat the +download command. Expected: verifier succeeds, no rsync runs, and root, +completion record, and immutable bytes remain unchanged. + +### Task 8: Publish Evidence, Execute Seven Checks, and Conditionally Publish P1-V2 + +**Files:** +- Generated outside Git: `p0_extension_v2_analysis.json`, `p0_authorization_analysis_v3.json`, `p0_authorization_brackets_v3.json`, and conditional `p1_protocol_v2.json`. +- Modify after evidence only: `tracks/qmc/solutions/frustration-free/challenge-194/README.md`. + +**Interfaces:** +- Consumes every authenticated source and the verified v2 root. +- Produces a recorded seven-check decision and, only on pass, an immutable P1-v2 protocol. + +Set the exact local paths once: + +```bash +cd /home/footman/code/quantum.harness-challenge-194/tracks/qmc/solutions/frustration-free/challenge-194 +RESULTS=/home/footman/code/quantum.harness-challenge-194/results/challenge-194 +P0_ANALYSIS="${RESULTS}/p0_analysis.json" +P0_ROOT="${RESULTS}/pilot-p0-739880d" +V1_ANALYSIS="${RESULTS}/p0_extension_v1_analysis.json" +V1_RUN_SPEC="${RESULTS}/pilot-p0-extension-v1/run_spec.json" +V1_PROTOCOL="${RESULTS}/p0_extension_v1_protocol.json" +COMBINED_V2="${RESULTS}/p0_combined_analysis_v2.json" +V2_ROOT="${RESULTS}/pilot-p0-extension-v2" +V2_RUN_SPEC="${V2_ROOT}/run_spec.json" +V2_PROTOCOL="${RESULTS}/p0_extension_v2_protocol.json" +V2_ANALYSIS="${RESULTS}/p0_extension_v2_analysis.json" +AUTH_ANALYSIS="${RESULTS}/p0_authorization_analysis_v3.json" +AUTH_BRACKETS="${RESULTS}/p0_authorization_brackets_v3.json" +P1_V2="${RESULTS}/p1_protocol_v2.json" +AUTH_ARGS=( + --p0-analysis "${P0_ANALYSIS}" + --p0-evidence-root "${P0_ROOT}" + --v1-analysis "${V1_ANALYSIS}" + --v1-run-spec "${V1_RUN_SPEC}" + --v1-protocol "${V1_PROTOCOL}" + --combined-v2-analysis "${COMBINED_V2}" + --v2-analysis "${V2_ANALYSIS}" + --v2-run-spec "${V2_RUN_SPEC}" + --v2-protocol "${V2_PROTOCOL}" +) +``` + +- [ ] **Step 1: Publish and byte-verify v2 analysis** + +Run twice: + +```bash +uv run python scripts/analyze_pilot.py analyze-extension-v2 \ + --run-spec "${V2_RUN_SPEC}" --protocol "${V2_PROTOCOL}" \ + --output "${V2_ANALYSIS}" +``` + +Expected: `published`, then +`verified-existing`; exactly 30 rows, replica count 32, and exact +protocol/run/progress/source bindings. + +- [ ] **Step 2: Publish and byte-verify authorization analysis** + +Run twice: + +```bash +uv run python scripts/analyze_pilot.py authorize-v2 \ + "${AUTH_ARGS[@]}" --output "${AUTH_ANALYSIS}" +``` + +Expected: +`published`, then `verified-existing`; exactly 126 rows; source roles +P0/v2/v2/P0; 16/5/5/16 coupling axes; no P0/v1 blocked-sigma request ID. + +- [ ] **Step 3: Publish and independently reproduce bracket-v3** + +Run twice: + +```bash +uv run python scripts/analyze_pilot.py select-authorization-v3 \ + --analysis "${AUTH_ANALYSIS}" "${AUTH_ARGS[@]}" \ + --output "${AUTH_BRACKETS}" +``` + +Expected: identical canonical bytes and +`verified-existing` on retry. Independently reconstruct in a fresh process +from all trusted roots; require byte identity. + +- [ ] **Step 4: Evaluate the seven checks conjunctively** + +```text +1. Protocol/design/implementation/correctness/P0/v1/combined inputs authenticate. +2. V2 root verifies exactly 192 cells and 192 trajectories. +3. V2 analysis recomputes byte-identically with 30 rows and replica count 32. +4. Authorization recomputes byte-identically with untouched controls, standalone v2 blocked sigmas, 126 rows, and no blocked-sigma union. +5. Sigma 0.9 and 1.0 are selected on nonzero intervals marked by both estimators. +6. Sigma 0.8 is [0x1.f400000000000p-2,0x1.3880000000000p-1] and sigma 1.1 is [0x1.312d000000000p+0,0x1.7d78400000000p+0]. +7. requires_p0_extension is false and independent bracket recomputation is byte-identical. +``` + +If any check fails, assert `p1_protocol_v2.json` is absent, record unresolved, +and stop. No post-hoc change is allowed. + +- [ ] **Step 5: Conditionally publish P1-v2 protocol only** + +Only on all-seven pass, run twice: + +```bash +uv run python scripts/analyze_pilot.py build-p1-v2 \ + --analysis "${AUTH_ANALYSIS}" --brackets "${AUTH_BRACKETS}" \ + "${AUTH_ARGS[@]}" --output "${P1_V2}" +``` + +Run with every trusted source and +bracket. Expected: `published`, then `verified-existing`; schema +`challenge-194-p1-protocol-v2`; 192 P1 cells; replicas `8..23`; seed +`19_420_261_729`; no P1 cell executed. + +- [ ] **Step 6: Record evidence in one documentation-only commit** + +Add exact observed hashes, job IDs, seven-check outcomes, and P1 +present/absent status to README. Run Task 6 full gate, then: + +```bash +git add tracks/qmc/solutions/frustration-free/challenge-194/README.md +git commit -m "Record P0 extension v2 boundary evidence" +``` + +Expected: only README committed; generated results and protected scratch files +remain unstaged. + +### Task 9: Post-Pass P1 Execution Handoff + +**Files:** +- Modify only on all-seven pass: `tracks/qmc/solutions/frustration-free/challenge-194/README.md` +- Future separate plan path: `docs/superpowers/plans/2026-07-30-challenge-194-p1-v2-execution.md` + +**Interfaces:** +- Consumes verified `p1_protocol_v2.json` identity and Task 8 evidence hashes. +- Produces a handoff boundary, not P1 execution. + +- [ ] **Step 1: Record the next-plan prerequisites** + +Document that the future separate P1 plan must bind: + +1. exact P1-v2 protocol file/document hashes; +2. authorization-analysis-v3 and bracket-v3 hashes; +3. four selector-derived nine-point grids; +4. P1 seed `19_420_261_729`, replicas `8..23`, phase `"pilot"`; +5. clean deployment, cell cardinality, resources, restart/transfer; +6. extended-observable implementation and tests if required by the existing + production design; +7. exploratory-only claim boundary and untouched confirmatory RNG phase. + +- [ ] **Step 2: Stop before P1 execution** + +Do not build a P1 run spec, submit a P1 job, create a P1 cell root, or run any +P1 trajectory in this plan. If Task 8 fails, record that the handoff is +inapplicable because P1-v2 is absent. + +## Plan Completion Criteria + +- Tasks 1–2 produce and fully test the shortest safe submission revision. +- Task 3 deploys that exact clean commit, passes four-cell smoke, and submits + all 188 remaining cells at concurrency `min(40, account limit)`. +- Tasks 4–6 complete local analysis/authorization code while Slurm runs. +- Task 7 proves 192/192 evidence independently of scheduler status. +- Task 8 either publishes a verified P1-v2 protocol after all seven checks or + preserves the fail-closed unresolved state. +- Task 9 defines the separate next execution boundary and executes no P1 cell. +- Every implementation task has focused RED/GREEN evidence and a local commit; + no push, protected-file edit, generated-result commit, post-hoc scientific + change, or confirmatory claim is part of this plan. diff --git a/docs/superpowers/plans/2026-07-30-challenge-194-p0-extension.md b/docs/superpowers/plans/2026-07-30-challenge-194-p0-extension.md new file mode 100644 index 000000000..c6b8221f5 --- /dev/null +++ b/docs/superpowers/plans/2026-07-30-challenge-194-p0-extension.md @@ -0,0 +1,1535 @@ +# Challenge 194 P0 Extension Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build, execute, verify, and analyze the approved 96-cell versioned P0 extension, then publish P1 only if the unchanged selector passes the combined-evidence gate. + +**Architecture:** Add an authenticated extension protocol and run-spec schema beside the frozen P0 path, while reusing the existing cell runner, artifact verifier, restart machinery, and bounded snapshot implementation through explicit schema dispatch. Publish extension, combined-analysis, and bracket artifacts immutably; the combined schema permits a separate coupling axis per sigma and feeds the existing interval-marking and tie-break functions without relaxing them. + +**Tech Stack:** Python 3.12, NumPy, h5py, pytest, Ruff, Bash, rsync, Git bundles, Slurm through `scripts/harness_slurm.sh`, and the existing `long_range_percolation` Pilot/artifact/counter-RNG APIs. + +## Global Constraints + +- Work from `/home/footman/code/quantum.harness-challenge-194` on `challenge/194`; do not modify `.superpowers/sdd/task-1-report.md` or `.superpowers/sdd/progress.md`. +- The approved design is `docs/superpowers/specs/2026-07-30-challenge-194-p0-extension-design.md` at commit `be57e93e7db7ce987a5643bc3ab2035d2b75dce9`. +- The committed design-file SHA256 is `5426e3007e9d83039f371ca6a9372f1868ef9d5447b66a12b1643ecf72907aba`. +- Existing P0 root: `results/challenge-194/pilot-p0-739880d`; verifier result: `{"cells":96,"status":"verified","trajectories":96}`. +- Existing P0 run-spec SHA256: `d17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840`. +- Existing P0 progress SHA256: `ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f`. +- Existing P0 analysis embedded SHA256: `e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8`. +- Existing P0 analysis canonical-file SHA256: `44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b`. +- Existing bracket SHA256: `fb3df666044bf9531443fc00c5c2c2d489512b4162864b3a92ffc2e756832403`. +- Existing P0 source/orchestration revision: `739880d9ccdcffbfc8a15310250349bd11d63bbb`. +- Extension schemas are exactly `challenge-194-p0-extension-protocol-v1`, `challenge-194-p0-extension-run-spec-v1`, `challenge-194-p0-extension-progress-v1`, and `challenge-194-p0-extension-analysis-v1`. +- Combined schemas are exactly `challenge-194-p0-combined-analysis-v2` and `challenge-194-p1-brackets-v2`. +- Extension sigmas are exactly `0x1.ccccccccccccdp-1` and `0x1.0000000000000p+0`; lengths are exactly `1024`, `16384`, and `262144`; replicas are exactly `24..39`. +- Loop order is sigma, length, replica: exactly 96 cells, 96 trajectories, 17 checkpoints per trajectory, 1,632 trajectory checkpoints, and 102 extension estimate rows. +- Extension master seed is `19_420_262_729`, phase is `"pilot"`, and grid namespace is `"pilot-p0-extension-v1"`. +- Sigma `0.9` range is `0x1.f400000000000p-2` through `0x1.312d000000000p+0`; grid hash is `76dc7e07639ed085873a8f291cc2aaee0e8942ddac8efce3982743dd67491071`. +- Sigma `1.0` range is `0x1.3880000000000p-1` through `0x1.dcd6500000000p+0`; grid hash is `d40b4a2afac533d74965513513fff1870918831000b2e040063ca2a0e29ad091`. +- Extension replica labels, request digests, and RNG material must be disjoint from P0 replicas `0..7` and reserved P1 replicas `8..23`; any collision fails publication. +- The basic ten-column trajectory schema, scientific engine, realization policy, stopping policy, correctness registry, capability waiver, and exploratory `"pilot"` phase remain unchanged. +- The frozen selector still uses lengths `16384` and `262144`, excludes zero coupling, marks the same `Q_G` and closed `[0.25,0.75]` four-sector intervals, and uses the same narrowest/lower-coupling and maximum-slope/lower-coupling tie-breaks. +- No interpolation, uncertainty rescue, threshold change, nearest-interval fallback, manual candidate choice, adaptive extension, extended observables, P1 execution, or confirmatory use is allowed. +- Every JSON artifact is canonical finite UTF-8 JSON with sorted keys, compact separators, and one trailing newline; publication is atomic, immutable, and no-clobber. +- Heavy execution is Wuzh02-only on `wzacnormal03`: one CPU, 1800 MiB, 40 minutes, no GPU, and one private node-local Numba cache per cell. +- Existing `.partial` and `.intent` files are preserved and block restart; completed cells are deeply verified; retries use the identical run spec. +- P1 remains absent unless all six design acceptance checks pass. A scientifically unresolved extension is a valid fail-closed outcome. + +--- + +## File Map + +- Create `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py`: extension constants, range/grid derivation, protocol validation/building, and extension-analysis/combined-analysis records. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py`: schema dispatch, parameterized request reconstruction, extension run-spec construction, progress schema dispatch, and generic verified snapshots without weakening the public P0 loader. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py`: extension aggregation, combined evidence, per-sigma selector normalization, bracket-v2 validation, and conditional P1 input support. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py`: immutable extension protocol, extension analysis, combine, select, and P1 handoff commands. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py`: `build-extension-spec` and production schema-dispatched cell/pending/merge/verify commands. +- Create `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh`: exact 96-task extension worker contract. +- Create `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_build_slurm.sh`: clean compute-node protocol/run-spec builder. +- Create `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py`: protocol, run-spec, execution, restart, schema separation, wrapper, and adversarial tests. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py`: extension aggregation, combination, selector-v2, acceptance, and P1 regression tests. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py`: all new immutable CLI commands and failure boundaries. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py`: documentation and exact cluster-command contracts. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md`: freeze versioned extension identities, resources, restart, and acceptance before run-spec construction. +- Modify `tracks/qmc/solutions/frustration-free/challenge-194/README.md`: exact collaborator build, submit, fetch, analyze, combine, and conditional P1 commands. + +`pilot_extension.py` may import the existing selector primitives from +`pilot_analysis.py`. New `pilot_analysis.py` functions must therefore import +extension constants and validators inside function bodies, never through a +module-scope reverse import; this keeps the dependency direction acyclic. + +## Fastest Safe Execution Order + +Tasks 1–4 form the submission critical path and each ends in a local commit. +Task 5 deploys the exact Task 4 commit and submits the campaign. Tasks 6–9 +then proceed locally while Slurm runs. Task 10 harvests and verifies all 96 +cells. Task 11 publishes combined evidence and performs the P1 gate. Do not +change `PILOT_PLAN.md`, `uv.lock`, or scientific-engine files after Task 4; +those bytes are bound into the run spec. + +### Task 1: Extension Range and Protocol Core + +**Files:** +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py` + +**Interfaces:** +- Consumes: validated P0 analysis mapping and existing `periodic_kernel`, `TrajectoryRequest`, `request_digest`, `derive_stream_material`, and selector evidence functions. +- Produces: `build_p0_extension_protocol(p0_analysis: Mapping[str, object]) -> dict[str, object]` and `validate_p0_extension_protocol(p0_analysis: Mapping[str, object], protocol: Mapping[str, object]) -> None`. + +- [ ] **Step 1: Add exact range, component, grid, and real-evidence tests** + +Add constants for both expected grids directly in the test and construct a +source fixture from the immutable `p0_analysis.json`. The focused assertions +must include the distant `Q_G` components and prove that generated grids, not +copied output constants, produce the design hashes: + +```python +P0_ANALYSIS = ( + Path(__file__).resolve().parents[5] + / "results/challenge-194/p0_analysis.json" +) +EXPECTED_SPANS = { + (0.9).hex(): ((4, 7), (0.48828125).hex(), float.fromhex("0x1.312d000000000p+0").hex()), + (1.0).hex(): ((5, 9), float.fromhex("0x1.3880000000000p-1").hex(), float.fromhex("0x1.dcd6500000000p+0").hex()), +} +EXPECTED_GRIDS = { + (0.9).hex(): [ + "0x1.f400000000000p-2", "0x1.1085a00000000p-1", + "0x1.270b400000000p-1", "0x1.3d90e00000000p-1", + "0x1.5416800000000p-1", "0x1.6a9c200000000p-1", + "0x1.8121c00000000p-1", "0x1.97a7600000000p-1", + "0x1.ae2d000000000p-1", "0x1.c4b2a00000000p-1", + "0x1.db38400000000p-1", "0x1.f1bde00000000p-1", + "0x1.0421c00000000p+0", "0x1.0f64900000000p+0", + "0x1.1aa7600000000p+0", "0x1.25ea300000000p+0", + "0x1.312d000000000p+0", + ], + (1.0).hex(): [ + "0x1.3880000000000p-1", "0x1.6092ca0000000p-1", + "0x1.88a5940000000p-1", "0x1.b0b85e0000000p-1", + "0x1.d8cb280000000p-1", "0x1.006ef90000000p+0", + "0x1.14785e0000000p+0", "0x1.2881c30000000p+0", + "0x1.3c8b280000000p+0", "0x1.50948d0000000p+0", + "0x1.649df20000000p+0", "0x1.78a7570000000p+0", + "0x1.8cb0bc0000000p+0", "0x1.a0ba210000000p+0", + "0x1.b4c3860000000p+0", "0x1.c8cceb0000000p+0", + "0x1.dcd6500000000p+0", + ], +} + +def test_extension_ranges_are_derived_from_exact_real_p0(): + source = json.loads(P0_ANALYSIS.read_text(encoding="utf-8")) + derived = extension.derive_p0_extension_ranges(source) + assert derived[(0.9).hex()]["four_sector_components"] == [[5, 5]] + assert derived[(0.9).hex()]["q_g_components"] == [[6, 6], [13, 14]] + assert derived[(1.0).hex()]["four_sector_components"] == [[6, 7]] + assert derived[(1.0).hex()]["q_g_components"] == [[8, 8], [12, 14]] + for sigma_hex, (guard_indices, lower, upper) in EXPECTED_SPANS.items(): + assert derived[sigma_hex]["guard_interval_indices"] == list(guard_indices) + assert derived[sigma_hex]["lower_kappa_hex"] == lower + assert derived[sigma_hex]["upper_kappa_hex"] == upper + +def test_extension_grids_are_recursive_binary64_and_hash_bound(): + source = json.loads(P0_ANALYSIS.read_text(encoding="utf-8")) + protocol = extension.build_p0_extension_protocol(source) + entries = {entry["sigma_hex"]: entry for entry in protocol["sigma_entries"]} + assert {sigma: entry["kappas"] for sigma, entry in entries.items()} == EXPECTED_GRIDS + assert entries[(0.9).hex()]["grid_sha256"] == extension.EXTENSION_GRID_HASHES[(0.9).hex()] + assert entries[(1.0).hex()]["grid_sha256"] == extension.EXTENSION_GRID_HASHES[(1.0).hex()] +``` + +- [ ] **Step 2: Run the focused tests and verify RED** + +Run: + +```bash +cd /home/footman/code/quantum.harness-challenge-194/tracks/qmc/solutions/frustration-free/challenge-194 +uv run --with pytest pytest tests/test_pilot_extension.py -q +``` + +Expected: collection fails with +`ModuleNotFoundError: No module named 'long_range_percolation.pilot_extension'`. + +- [ ] **Step 3: Implement exact constants and recursive derivation** + +Create these public constants and helpers. `_marked_components` must reject an +empty list only at the caller, and `_component_gap` treats touching components +as distance zero: + +```python +EXTENSION_PROTOCOL_SCHEMA = "challenge-194-p0-extension-protocol-v1" +EXTENSION_RUN_SPEC_SCHEMA = "challenge-194-p0-extension-run-spec-v1" +EXTENSION_PROGRESS_SCHEMA = "challenge-194-p0-extension-progress-v1" +EXTENSION_ANALYSIS_SCHEMA = "challenge-194-p0-extension-analysis-v1" +COMBINED_ANALYSIS_SCHEMA = "challenge-194-p0-combined-analysis-v2" +COMBINED_BRACKET_SCHEMA = "challenge-194-p1-brackets-v2" +EXTENSION_SIGMAS = (0.9, 1.0) +EXTENSION_LENGTHS = (2**10, 2**14, 2**18) +EXTENSION_REPLICAS = tuple(range(24, 40)) +EXTENSION_MASTER_SEED = 19_420_262_729 +EXTENSION_PHASE = "pilot" +EXTENSION_GRID_NAMESPACE = "pilot-p0-extension-v1" +EXTENSION_GRID_HASHES = MappingProxyType({ + (0.9).hex(): "76dc7e07639ed085873a8f291cc2aaee0e8942ddac8efce3982743dd67491071", + (1.0).hex(): "d40b4a2afac533d74965513513fff1870918831000b2e040063ca2a0e29ad091", +}) + +def _marked_components(indices: Sequence[int]) -> tuple[tuple[int, int], ...]: + ordered = tuple(sorted(set(indices))) + if tuple(indices) != ordered: + raise RuntimeError("marked interval indices are not canonical") + components: list[tuple[int, int]] = [] + for index in ordered: + if components and index == components[-1][1] + 1: + components[-1] = (components[-1][0], index) + else: + components.append((index, index)) + return tuple(components) + +def _component_gap(left: tuple[int, int], right: tuple[int, int]) -> int: + if left[1] < right[0]: + return right[0] - left[1] - 1 + if right[1] < left[0]: + return left[0] - right[1] - 1 + return 0 + +def _recursive_binary64_grid_17(lower: float, upper: float) -> tuple[float, ...]: + if not math.isfinite(lower) or not math.isfinite(upper) or lower <= 0.0 or upper <= lower: + raise RuntimeError("extension grid endpoints are invalid") + points = [lower, upper] + for _level in range(4): + previous = sorted(points) + points.extend(left + (right - left) / 2.0 for left, right in pairwise(previous)) + ordered = tuple(sorted({value.hex(): value for value in points}.values())) + if len(ordered) != 17 or ordered[0] != lower or ordered[-1] != upper: + raise RuntimeError("extension span cannot produce 17 binary64 points") + return ordered +``` + +`derive_p0_extension_ranges` must call the existing validated selector parser +and `_transition_evidence`, form components, choose the lowest crossing +component and nearest/lower `Q_G` component, add one guard interval on each +side, and reject a missing guard: + +```python +def derive_p0_extension_ranges( + p0_analysis: Mapping[str, object], +) -> dict[str, dict[str, object]]: + sigmas, lengths, kappas, values = _selector_estimates(p0_analysis) + selected_lengths = (lengths[-2], lengths[-1]) + result: dict[str, dict[str, object]] = {} + for sigma in EXTENSION_SIGMAS: + if sigma not in sigmas: + raise RuntimeError("blocked sigma is missing from P0 analysis") + q_indices: list[int] = [] + crossing_indices: list[int] = [] + for interval_index in range(1, len(kappas) - 1): + q_marked, crossing_marked, _evidence = _transition_evidence( + sigma, selected_lengths, kappas, values, interval_index + ) + q_indices.extend([interval_index] if q_marked else []) + crossing_indices.extend([interval_index] if crossing_marked else []) + q_components = _marked_components(q_indices) + crossing_components = _marked_components(crossing_indices) + if not q_components or not crossing_components: + raise RuntimeError("extension estimator component is missing") + crossing = crossing_components[0] + q_component = min( + q_components, + key=lambda component: (_component_gap(component, crossing), component[0]), + ) + union_lower = min(crossing[0], q_component[0]) + union_upper = max(crossing[1], q_component[1]) + guard_lower = union_lower - 1 + guard_upper = union_upper + 1 + if guard_lower < 1 or guard_upper + 1 >= len(kappas): + raise RuntimeError("extension range lacks adjacent P0 guards") + lower = kappas[guard_lower] + upper = kappas[guard_upper + 1] + grid = _recursive_binary64_grid_17(lower, upper) + result[sigma.hex()] = { + "sigma_hex": sigma.hex(), + "lengths": list(selected_lengths), + "q_g_components": [list(component) for component in q_components], + "four_sector_components": [list(component) for component in crossing_components], + "selected_q_g_component": list(q_component), + "selected_four_sector_component": list(crossing), + "guard_interval_indices": [guard_lower, guard_upper], + "lower_kappa_hex": lower.hex(), + "upper_kappa_hex": upper.hex(), + "kappas": [value.hex() for value in grid], + } + return result +``` + +- [ ] **Step 4: Implement protocol requests, identities, hashes, and validation** + +`build_p0_extension_protocol` must require the exact source hashes from Global +Constraints, compute both exact grid IDs, build kernels and 96 requests in +canonical order, compare all request/RNG hashes against P0 hashes, reject +master-seed or replica overlap with P0/P1, and hash the unsigned document. +The document keys are fixed: + +```python +{ + "schema_version": EXTENSION_PROTOCOL_SCHEMA, + "source_p0_run_spec_sha256": P0_RUN_SPEC_SHA256, + "source_p0_progress_sha256": P0_PROGRESS_SHA256, + "source_p0_analysis_document_sha256": P0_ANALYSIS_DOCUMENT_SHA256, + "source_p0_bracket_document_sha256": P0_BRACKET_DOCUMENT_SHA256, + "design_sha256": _file_sha256(_design_path()), + "source_revision": _current_revision(), + "grid_namespace": EXTENSION_GRID_NAMESPACE, + "master_seed": EXTENSION_MASTER_SEED, + "phase": EXTENSION_PHASE, + "purpose": "exploratory-p0-extension-only", + "lengths": list(EXTENSION_LENGTHS), + "replicas": list(EXTENSION_REPLICAS), + "loop_order": ["sigma", "length", "replica"], + "sigma_entries": sigma_entries, + "cells": cells, + "cell_count": 96, + "rng_assignment_sha256": _sha256(_canonical_bytes({"assignments": assignments})), + "protocol_sha256": protocol_sha256, +} +``` + +Each cell has the same path fields as `PilotCell`, its sigma-specific 17 +couplings, and the exact grid ID from the design. Validation reconstructs +every kernel, request, stream, cell ID, path, and aggregate assignment hash; +it accepts no unknown fields. + +- [ ] **Step 5: Add adversarial protocol tests** + +Parameterize mutations for source hashes, component order, noncanonical +binary64, grid order, grid hash, design hash, cell order, missing/duplicate +replicas, request digest, RNG digest, P0 collision, and P1 identity overlap. +Each mutation must recompute superficial outer hashes and still fail the +semantic validator with a specific `RuntimeError`. + +- [ ] **Step 6: Run focused tests and static checks** + +Run: + +```bash +uv run --with pytest pytest tests/test_pilot_extension.py -q +uv run --with ruff ruff check src/long_range_percolation/pilot_extension.py tests/test_pilot_extension.py +uv run --with ruff ruff format --check src/long_range_percolation/pilot_extension.py tests/test_pilot_extension.py +uv run python -m compileall -q src/long_range_percolation/pilot_extension.py tests/test_pilot_extension.py +``` + +Expected: every command exits `0`; pytest reports no failures, Ruff prints +`All checks passed!`, format reports both files formatted, and compileall is +silent. + +- [ ] **Step 7: Commit Task 1** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py +git commit -m "Add versioned P0 extension protocol" +``` + +Expected: commit succeeds with exactly the two Task 1 files. + +### Task 2: Immutable Extension Protocol CLI + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py` + +**Interfaces:** +- Consumes: exact canonical P0 analysis file plus an explicit absolute canonical + P0 evidence root containing the frozen run spec and progress. +- Produces: `build-p0-extension --analysis PATH --p0-evidence-root PATH --output PATH`, + returning `published` or `verified-existing`. + +- [ ] **Step 1: Write failing CLI publication tests** + +Add a test that invokes `build-p0-extension` twice, verifies identical bytes, +then changes the generated protocol and proves the installed file is not +replaced: + +```python +def test_build_p0_extension_publishes_once_and_rejects_different_bytes( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +): + source = _analysis_document(complete=True) + source_path = tmp_path / "p0_analysis.json" + evidence_root = tmp_path / "pilot-p0-739880d" + output = tmp_path / "p0_extension_v1_protocol.json" + source_path.write_bytes(_canonical_bytes(source)) + protocol = {"schema_version": extension.EXTENSION_PROTOCOL_SCHEMA, "protocol_sha256": "a" * 64} + monkeypatch.setattr(CLI, "build_p0_extension_protocol", lambda _source, _root: protocol) + assert CLI.main(["build-p0-extension", "--analysis", str(source_path), "--p0-evidence-root", str(evidence_root), "--output", str(output)]) == 0 + installed = output.read_bytes() + assert json.loads(capsys.readouterr().out)["publication"] == "published" + assert CLI.main(["build-p0-extension", "--analysis", str(source_path), "--p0-evidence-root", str(evidence_root), "--output", str(output)]) == 0 + assert output.read_bytes() == installed + assert json.loads(capsys.readouterr().out)["publication"] == "verified-existing" + monkeypatch.setattr(CLI, "build_p0_extension_protocol", lambda _source, _root: {**protocol, "protocol_sha256": "b" * 64}) + assert CLI.main(["build-p0-extension", "--analysis", str(source_path), "--p0-evidence-root", str(evidence_root), "--output", str(output)]) == 1 + assert output.read_bytes() == installed +``` + +- [ ] **Step 2: Run the CLI test and verify RED** + +Run: + +```bash +uv run --with pytest pytest tests/test_analyze_pilot_cli.py::test_build_p0_extension_publishes_once_and_rejects_different_bytes -q +``` + +Expected: argparse exits because `build-p0-extension` is not a registered +command. + +- [ ] **Step 3: Add the parser and immutable command** + +Import `EXTENSION_PROTOCOL_SCHEMA` and `build_p0_extension_protocol`. Register +two required `Path` arguments and use the existing bounded canonical reader +and `_publish_or_verify`: + +```python +extension = commands.add_parser("build-p0-extension") +extension.add_argument("--analysis", type=Path, required=True) +extension.add_argument("--p0-evidence-root", type=Path, required=True) +extension.add_argument("--output", type=Path, required=True) + +if arguments.command == "build-p0-extension": + source = _mapping_document(arguments.analysis.resolve(), "P0 analysis document") + document = build_p0_extension_protocol(source, arguments.p0_evidence_root) + publication = _publish_or_verify( + arguments.output.resolve(), document, EXTENSION_PROTOCOL_SCHEMA + ) + result = { + "status": "ready", + "publication": publication, + "output": str(arguments.output.resolve()), + "protocol_sha256": document["protocol_sha256"], + } +``` + +- [ ] **Step 4: Run CLI and protocol regressions** + +Run: + +```bash +uv run --with pytest pytest tests/test_analyze_pilot_cli.py tests/test_pilot_extension.py -q +``` + +Expected: all tests pass; the existing `build-p1` test still refuses the +unextended P0 analysis and leaves its output absent. + +- [ ] **Step 5: Commit Task 2** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py +git commit -m "Publish immutable P0 extension protocol" +``` + +### Task 3: Extension Run Spec and Shared Runtime + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot.py` + +**Interfaces:** +- Consumes: validated extension protocol, absolute output root, approved validation report. +- Produces: `build_p0_extension_run_spec(output_root: Path, validation_report: Path, protocol: Mapping[str, object]) -> dict[str, object]`, `load_p0_extension_run_spec(path: Path, verify_current_environment: bool = True) -> dict[str, object]`, and schema-dispatched cell/pending/merge/verify behavior. + +- [ ] **Step 1: Write failing run-spec and schema-separation tests** + +Cover exact outer fields, copied 96-cell assignment, correctness/runtime/design +binding, canonical paths, extension progress schema, and public loader +separation: + +```python +def test_extension_run_spec_is_bound_and_p0_loader_stays_strict(tmp_path: Path): + protocol = _extension_protocol_fixture() + run_spec = pilot._write_test_extension_run_spec(tmp_path / "extension", protocol=protocol) + loaded = pilot.load_p0_extension_run_spec(run_spec, verify_current_environment=False) + assert loaded["schema_version"] == extension.EXTENSION_RUN_SPEC_SCHEMA + assert loaded["source_extension_protocol_sha256"] == protocol["protocol_sha256"] + assert loaded["cells"] == protocol["cells"] + with pytest.raises(RuntimeError, match="P0 run spec"): + pilot.load_pilot_run_spec(run_spec, verify_current_environment=False) + +def test_extension_small_cell_restart_and_merge_use_extension_progress(tmp_path: Path): + run_spec = pilot._write_test_extension_run_spec(tmp_path / "extension", tiny=True) + first = pilot._run_test_registered_pilot_cell(run_spec, 0) + second = pilot._run_test_registered_pilot_cell(run_spec, 0) + assert first == second + merged = pilot._merge_test_registered_pilot_progress(run_spec) + assert merged["schema_version"] == extension.EXTENSION_PROGRESS_SCHEMA +``` + +- [ ] **Step 2: Run focused tests and verify RED** + +Run: + +```bash +uv run --with pytest pytest \ + tests/test_pilot_extension.py \ + tests/test_pilot.py::test_public_loader_never_downgrades_frozen_p0 -q +``` + +Expected: failures report missing extension run-spec builders/loaders while +the existing P0 loader test still passes. + +- [ ] **Step 3: Parameterize request reconstruction without changing P0 bytes** + +Change `PilotCell.request` and stream derivation to take explicit identity +parameters; update all P0 call sites with the existing constants: + +```python +def request(self, *, master_seed: int, phase: str) -> TrajectoryRequest: + return TrajectoryRequest( + length=self.length, + sigma=self.sigma, + sigma_grid_id=self.sigma_grid_id, + kappas=np.asarray(self.kappas, dtype=np.float64), + master_seed=master_seed, + phase=phase, + replica=self.replica, + kernel_sha256=self.kernel_sha256, + ) + +def _stream_hashes( + length: int, + sigma_grid_id: str, + replica: int, + *, + master_seed: int, + phase: str, +) -> tuple[str, ...]: + return tuple( + derive_stream_material( + StreamIdentity( + master_seed=master_seed, + phase=phase, + length=length, + sigma_grid_id=sigma_grid_id, + replica=replica, + stream_id=stream, + ) + ).material_sha256 + for stream in range(STREAM_COUNT) + ) +``` + +Run the existing exact P0 registry test immediately after this mechanical +change. Expected: it passes without a changed P0 run-spec hash. + +- [ ] **Step 4: Add explicit production schema dispatch** + +Introduce an immutable internal contract selected only by exact schema: + +```python +@dataclass(frozen=True) +class PilotRunContract: + run_spec_schema: str + progress_schema: str + master_seed: int + phase: str + production_kind: str + +P0_CONTRACT = PilotRunContract( + RUN_SPEC_SCHEMA, MERGED_SCHEMA, PILOT_MASTER_SEED, PILOT_PHASE, "p0" +) +EXTENSION_CONTRACT = PilotRunContract( + EXTENSION_RUN_SPEC_SCHEMA, + EXTENSION_PROGRESS_SCHEMA, + EXTENSION_MASTER_SEED, + EXTENSION_PHASE, + "p0-extension-v1", +) + +def _contract_for_schema(schema: object) -> PilotRunContract: + if schema == RUN_SPEC_SCHEMA: + return P0_CONTRACT + if schema == EXTENSION_RUN_SPEC_SCHEMA: + return EXTENSION_CONTRACT + raise RuntimeError("registered Pilot run-spec schema is not supported") +``` + +Keep `load_pilot_run_spec` hard-bound to `RUN_SPEC_SCHEMA`. Add +`load_p0_extension_run_spec` hard-bound to `EXTENSION_RUN_SPEC_SCHEMA`. +Internal worker, pending, merge, verify, and snapshot paths use +`_contract_for_schema`; no boolean may downgrade a production schema to a +test schema. + +- [ ] **Step 5: Build and validate the extension run spec** + +`build_p0_extension_run_spec` validates the protocol first, requires absolute +paths and a clean source, verifies correctness, records runtime capability, +copies the exact protocol cells, and adds only these extension-specific outer +fields: + +```python +{ + "schema_version": EXTENSION_RUN_SPEC_SCHEMA, + "artifact_root": ".", + "protocol": protocol_without_cells, + "cells": protocol["cells"], + "cell_count": 96, + "source_extension_protocol_sha256": protocol["protocol_sha256"], + "source_p0_analysis_document_sha256": protocol["source_p0_analysis_document_sha256"], + "design_sha256": protocol["design_sha256"], + "correctness_report_sha256": correctness["correctness_report_sha256"], + "correctness_run_spec_sha256": correctness["correctness_run_spec_sha256"], + "correctness_approval_registry_sha256": correctness["correctness_approval_registry_sha256"], + "correctness_approval_revision": CORRECTNESS_APPROVAL_REVISION, + "validation_source_revision": correctness["validation_source_revision"], + "validated_engine_modules": dict(correctness["validated_engine_modules"]), + "validated_engine_sha256": correctness["validated_engine_sha256"], + "validation_runtime_capability_sha256": correctness["validation_runtime_capability_sha256"], + "orchestration_revision": source["source_revision"], + "clean_tree": True, + "uv_lock_sha256": _lock_hash(), + "runtime_capability": runtime, + "runtime_capability_sha256": runtime_sha256, + "analysis_plan_sha256": _analysis_plan_hash(), + "rng_assignment_sha256": protocol["rng_assignment_sha256"], + "capability_waiver": capability_waiver, + "merged_progress_path": MERGED_NAME, + "run_spec_sha256": run_spec_sha256, +} +``` + +Validation reconstructs protocol semantics, each request with the contract +seed/phase, all paths, correctness evidence, runtime, analysis-plan bytes, and +outer document hash. Existing P0 expected fields and validation stay exact. + +- [ ] **Step 6: Extend restart, merge, verify, and snapshot tests** + +Reuse tiny extension fixtures to prove duplicate execution, trajectory/batch/ +progress/outer-marker restart, `.partial` and `.intent` preservation, swapped +cell/root rejection, exactly 96 production cells, no extras at merge, and +extension progress schema. Add a P0 regression that feeds an internally +rehashed extension to `load_pilot_run_spec` and confirms rejection. + +- [ ] **Step 7: Run focused and runtime regressions** + +Run: + +```bash +uv run --with pytest pytest tests/test_pilot_extension.py tests/test_pilot.py -q +uv run --with ruff ruff check --ignore SIM102,TRY004,UP017,PYI025,F401 \ + src/long_range_percolation/pilot.py \ + src/long_range_percolation/pilot_extension.py \ + tests/test_pilot.py tests/test_pilot_extension.py +uv run python -m compileall -q \ + src/long_range_percolation/pilot.py \ + src/long_range_percolation/pilot_extension.py +``` + +Expected: all tests pass, Ruff reports no new finding, and compileall is +silent. + +- [ ] **Step 8: Commit Task 3** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py +git commit -m "Add authenticated P0 extension runtime" +``` + +### Task 4: Build/Worker CLIs, Slurm Wrappers, and Bound Documentation + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh` +- Create: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_build_slurm.sh` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/README.md` + +**Interfaces:** +- Consumes: extension protocol, canonical P0 analysis, explicit P0 evidence + root, approved validation report, exact clean checkout, Slurm array task ID. +- Produces: `build-extension-spec`; schema-dispatched worker commands; exact one-CPU/1800-MiB/40-minute wrappers. + +- [ ] **Step 1: Write failing CLI and wrapper contract tests** + +Tests must assert: + +```python +def test_extension_wrapper_has_exact_resources_and_task_map(): + text = (ROOT / "scripts/pilot_extension_array_slurm.sh").read_text() + assert "#SBATCH --cpus-per-task=1" in text + assert "#SBATCH --mem=1800M" in text + assert "#SBATCH --time=00:40:00" in text + assert "SLURM_ARRAY_TASK_ID < 1 || SLURM_ARRAY_TASK_ID > 96" in text + assert "CELL_INDEX=$((SLURM_ARRAY_TASK_ID - 1))" in text + assert "scripts/run_pilot.py run-cell" in text + +def test_build_extension_spec_requires_protocol_and_exact_output_path(): + parser = run_pilot_cli._parser() + args = parser.parse_args([ + "build-extension-spec", + "--protocol", "/tmp/p0_extension_v1_protocol.json", + "--validation-report", "/tmp/report.json", + "--analysis", "/tmp/p0_analysis.json", + "--p0-evidence-root", "/tmp/pilot-p0-739880d", + "--output-root", "/tmp/pilot-p0-extension-v1", + "--run-spec", "/tmp/pilot-p0-extension-v1/run_spec.json", + ]) + assert args.command == "build-extension-spec" +``` + +- [ ] **Step 2: Run focused tests and verify RED** + +Run: + +```bash +uv run --with pytest pytest tests/test_pilot_extension.py tests/test_runtime.py -q +``` + +Expected: failures report absent wrappers and absent +`build-extension-spec`. + +- [ ] **Step 3: Implement `run_pilot.py` extension construction and dispatch** + +Register `build-extension-spec` with required protocol, validation report, +canonical P0 analysis, explicit P0 evidence root, output root, and run-spec +paths. Require +`run_spec == output_root / "run_spec.json"`, load protocol with the bounded +canonical reader, call `build_p0_extension_run_spec`, and print: + +```python +{ + "status": "ready", + "cells": 96, + "run_spec": str(run_spec), + "run_spec_sha256": document["run_spec_sha256"], +} +``` + +`run-cell`, `pending`, `merge`, and `verify` must call registered-schema +dispatch. Preserve the exact existing P0 JSON outputs. + +- [ ] **Step 4: Create the extension worker wrapper** + +Copy the existing environment-sanitization and safe cache logic, change only +the displayed campaign name, add exact SBATCH resources, retain task IDs +`1..96`, and execute: + +```bash +#!/bin/bash +#SBATCH --cpus-per-task=1 +#SBATCH --mem=1800M +#SBATCH --time=00:40:00 +set -euo pipefail + +: "${HARNESS_RUN_SPEC:?Set HARNESS_RUN_SPEC to the immutable extension run_spec.json}" +: "${SLURM_ARRAY_TASK_ID:?Run as a Slurm array task}" +: "${HARNESS_ENTRYPOINT:?Set the exact deployed repository root}" +: "${HARNESS_COMMAND:?Set the exact offline Python executable}" +CHALLENGE_194_REPO_ROOT="${HARNESS_ENTRYPOINT}" +CHALLENGE_194_PYTHON="${HARNESS_COMMAND}" +if [[ ! "${SLURM_ARRAY_TASK_ID}" =~ ^[0-9]+$ ]] || + (( SLURM_ARRAY_TASK_ID < 1 || SLURM_ARRAY_TASK_ID > 96 )); then + exit 64 +fi +CELL_INDEX=$((SLURM_ARRAY_TASK_ID - 1)) +``` + +The remainder must be the tested P0 sanitization/cache implementation, ending +with exact deployed `PYTHONPATH` and `scripts/run_pilot.py run-cell`. + +- [ ] **Step 5: Create the compute-node build wrapper** + +The build wrapper requests one CPU, 1800 MiB, and ten minutes; requires +`HARNESS_RUN_SPEC` to be the exact P0 analysis path, `HARNESS_ENTRYPOINT` to +be the deployed repository root, and `HARNESS_COMMAND` to be the offline +Python. It derives all remaining fixed paths from the P0 analysis parent, +applies the same environment sanitation/cache contract, verifies the exact +canonical P0 analysis SHA256, and then runs: + +```bash +P0_ANALYSIS_PATH="${HARNESS_RUN_SPEC}" +CHALLENGE_194_REPO_ROOT="${HARNESS_ENTRYPOINT}" +CHALLENGE_194_PYTHON="${HARNESS_COMMAND}" +RESULTS_ROOT="$(dirname "${P0_ANALYSIS_PATH}")" +P0_EVIDENCE_ROOT="${RESULTS_ROOT}/pilot-p0-739880d" +EXTENSION_PROTOCOL_PATH="${RESULTS_ROOT}/p0_extension_v1_protocol.json" +VALIDATION_REPORT_PATH="${RESULTS_ROOT}/validation-prod-877ab93/report/report.json" +EXTENSION_ROOT="${RESULTS_ROOT}/pilot-p0-extension-v1" +"${CHALLENGE_194_PYTHON}" scripts/analyze_pilot.py build-p0-extension \ + --analysis "${P0_ANALYSIS_PATH}" \ + --p0-evidence-root "${P0_EVIDENCE_ROOT}" \ + --output "${EXTENSION_PROTOCOL_PATH}" +"${CHALLENGE_194_PYTHON}" scripts/run_pilot.py build-extension-spec \ + --protocol "${EXTENSION_PROTOCOL_PATH}" \ + --validation-report "${VALIDATION_REPORT_PATH}" \ + --analysis "${P0_ANALYSIS_PATH}" \ + --p0-evidence-root "${P0_EVIDENCE_ROOT}" \ + --output-root "${EXTENSION_ROOT}" \ + --run-spec "${EXTENSION_ROOT}/run_spec.json" +``` + +The run spec and entrypoint must be absolute, canonical, and contain no symlink +components. `HARNESS_COMMAND` must be an absolute lexically canonical launcher +that resolves to an absolute regular executable; retain and execute that +lexical path so a standard final `.venv/bin/python` symlink preserves venv +identity. The wrapper fails if the extension root exists with different bytes. + +- [ ] **Step 6: Freeze `PILOT_PLAN.md` before run-spec construction** + +Document all Global Constraints, exact grids and hashes, component rule, +schemas, artifact names, one-CPU/1800-MiB/40-minute resources, restart rules, +three submission batches, and six acceptance checks. Update README with the +same commands but state that no extension data or P1 protocol exists yet. + +- [ ] **Step 7: Run shell, docs, CLI, and core regressions** + +Run: + +```bash +bash -n scripts/pilot_extension_array_slurm.sh scripts/pilot_extension_build_slurm.sh +uv run --with pytest pytest \ + tests/test_pilot_extension.py tests/test_pilot.py \ + tests/test_runtime.py tests/test_analyze_pilot_cli.py -q +git diff --check +``` + +Expected: shell syntax is silent, all tests pass, and diff check is silent. + +- [ ] **Step 8: Commit Task 4 and record the submission revision** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_build_slurm.sh \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py \ + tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md \ + tracks/qmc/solutions/frustration-free/challenge-194/README.md +git commit -m "Prepare P0 extension cluster campaign" +git rev-parse HEAD +``` + +Expected: commit succeeds and `git rev-parse HEAD` yields the immutable +submission revision used throughout Task 5. + +### Task 5: Deploy and Submit the 96-Cell Campaign + +**Files:** +- Read only: `skills/using-slurm/profiles/wuzh02-jiangweiqi.toml` +- Read only: `scripts/harness_slurm.sh` +- Generated outside Git: remote bundle, remote clean deployment, extension protocol/run root, and scheduler logs. + +**Interfaces:** +- Consumes: exact Task 4 commit, Wuzh02 profile, remote approved correctness report, local immutable P0 analysis. +- Produces: clean remote deployment, immutable run spec, build job record, and three resource-safe array job IDs. + +- [ ] **Step 1: Define and verify exact deployment variables** + +Run from repository root: + +```bash +export HARNESS_CLUSTER_PROFILE=wuzh02-jiangweiqi +export SUBMIT_SHA="$(git rev-parse HEAD)" +export PROFILE="skills/using-slurm/profiles/wuzh02-jiangweiqi.toml" +export REMOTE_REPO="/work/share/giggleliu/jiangweiqi/quantum.harness-p0-extension-v3" +export REMOTE_BUNDLE="/work/share/giggleliu/jiangweiqi/challenge-194-p0-extension-v3.bundle" +export REMOTE_RESULTS="/work/share/giggleliu/jiangweiqi/results/challenge-194" +export REMOTE_ROOT="${REMOTE_RESULTS}/pilot-p0-extension-v1" +export REMOTE_ANALYSIS="${REMOTE_RESULTS}/p0_analysis.json" +export REMOTE_P0_EVIDENCE="${REMOTE_RESULTS}/pilot-p0-739880d" +export REMOTE_PROTOCOL="${REMOTE_RESULTS}/p0_extension_v1_protocol.json" +export REMOTE_VALIDATION="${REMOTE_RESULTS}/validation-prod-877ab93/report/report.json" +export REMOTE_PYTHON="/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194/.venv/bin/python" +export LOCAL_BUNDLE="/tmp/challenge-194-p0-extension-v3.bundle" +export REMOTE_BUNDLE_STAGE="${REMOTE_BUNDLE}.upload-${SUBMIT_SHA}-$(date -u +%Y%m%dT%H%M%S%N)-$$" +scripts/harness_slurm.sh precheck +scripts/harness_slurm.sh probe-partitions +``` + +The `v3` deployment and bundle names are fresh immutable namespaces for this +submission. Preserve the failed `v1` and `v2` deployments, bundles, job logs, and all +other diagnostics without deletion or overwrite. The absent shared +`REMOTE_PROTOCOL` and `REMOTE_ROOT` result paths remain the preregistered +version-1 scientific artifact paths. + +Expected: profile resolves to Wuzh02, SSH is `true`, the only dirty path is the +pre-existing `.superpowers/sdd/task-1-report.md`, and `wzacnormal03` is +available. Ratify `wzacnormal03`; stop if the dirty-path set differs. + +- [ ] **Step 2: Ship only committed bytes with a Git bundle** + +Run: + +```bash +git bundle create "${LOCAL_BUNDLE}" challenge/194 +BUNDLE_SHA256="$(sha256sum "${LOCAL_BUNDLE}" | awk '{print $1}')" +ssh wuzh02-jiangweiqi " + set -euo pipefail + test ! -e '${REMOTE_BUNDLE}' + test ! -e '${REMOTE_REPO}' + test ! -e '${REMOTE_BUNDLE_STAGE}' +" +scp "${LOCAL_BUNDLE}" "wuzh02-jiangweiqi:${REMOTE_BUNDLE_STAGE}" +ssh wuzh02-jiangweiqi " + set -euo pipefail + test \"\$(sha256sum '${REMOTE_BUNDLE_STAGE}' | awk '{print \$1}')\" = '${BUNDLE_SHA256}' + ln -- '${REMOTE_BUNDLE_STAGE}' '${REMOTE_BUNDLE}' + sync -f -- '${REMOTE_BUNDLE}' + test \"\$(sha256sum '${REMOTE_BUNDLE}' | awk '{print \$1}')\" = '${BUNDLE_SHA256}' + rm -- '${REMOTE_BUNDLE_STAGE}' +" +scp results/challenge-194/p0_analysis.json "wuzh02-jiangweiqi:${REMOTE_ANALYSIS}" +ssh wuzh02-jiangweiqi " + set -euo pipefail + test ! -e '${REMOTE_REPO}' + git clone '${REMOTE_BUNDLE}' '${REMOTE_REPO}' + git -C '${REMOTE_REPO}' checkout --detach '${SUBMIT_SHA}' + test -z \"\$(git -C '${REMOTE_REPO}' status --porcelain)\" + test \"\$(sha256sum '${REMOTE_ANALYSIS}' | awk '{print \$1}')\" = '44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b' + test \"\$(sha256sum '${REMOTE_P0_EVIDENCE}/run_spec.json' | awk '{print \$1}')\" = 'd17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840' + test \"\$(sha256sum '${REMOTE_P0_EVIDENCE}/progress.json' | awk '{print \$1}')\" = 'ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f' + test \"\$(sha256sum '${REMOTE_VALIDATION}' | awk '{print \$1}')\" = '036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8' + test -x '${REMOTE_PYTHON}' +" +``` + +`ln` is the atomic no-replace publication primitive: a concurrent or existing +final bundle makes it fail without changing either object. +Preserve the staging path on any failure for diagnosis; remove it only after +hard-link installation, +file sync, and final SHA256 verification all succeed. Never delete or overwrite +an existing final bundle, deployment, staging diagnostic, or failed-attempt +artifact. + +Expected: clean detached deployment at exactly `SUBMIT_SHA`; both hashes and +offline Python checks pass. Existing bundle/deployment paths fail closed. + +- [ ] **Step 3: Feasibility-check and submit the build job** + +Use the profile override for the new clean deployment. First run `--test-only` +with the exact environment exports in `--extra`, then submit the identical +command without `--test-only`: + +```bash +HARNESS_REPO_REMOTE="${REMOTE_REPO}" scripts/harness_slurm.sh submit \ + --test-only \ + --script tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_build_slurm.sh \ + --run-spec "${REMOTE_ANALYSIS}" \ + --entrypoint "${REMOTE_REPO}" --command "${REMOTE_PYTHON}" \ + --partition wzacnormal03 --time 00:10:00 --cpus 1 \ + --extra "--mem=1800M" +``` + +Expected: Slurm accepts one CPU, 1800 MiB, and ten minutes. Submit only after +reviewing the estimate. Capture the real build job ID, wait for completion, +then verify remotely: + +```bash +ssh wuzh02-jiangweiqi " + set -euo pipefail + '${REMOTE_PYTHON}' '${REMOTE_REPO}/tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py' pending \ + --run-spec '${REMOTE_ROOT}/run_spec.json' +" +``` + +Expected: canonical JSON with status `pending`, count `96`, and cell indices +`0..95`. + +- [ ] **Step 4: Smoke-submit task IDs 1–2** + +Feasibility-check, then submit: + +```bash +HARNESS_REPO_REMOTE="${REMOTE_REPO}" scripts/harness_slurm.sh submit \ + --test-only \ + --script tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh \ + --run-spec "${REMOTE_ROOT}/run_spec.json" \ + --entrypoint "${REMOTE_REPO}" --command "${REMOTE_PYTHON}" \ + --partition wzacnormal03 --time 00:40:00 --cpus 1 \ + --extra "--mem=1800M --array=1-2%2" +``` + +Expected: feasibility succeeds. Submit the same command without +`--test-only`, capture the job ID, monitor pending-to-running, inspect one +startup log, and classify after completion. Both cells must have verified +success manifests before continuing. + +- [ ] **Step 5: Submit remaining light/medium and heavy batches concurrently** + +After smoke success, feasibility-check and submit these two exact arrays: + +```text +Light/medium task IDs: 3-32,49-80 with concurrency cap 16 +Heavy task IDs: 33-48,81-96 with concurrency cap 8 +``` + +Use the Step 4 command with only `--array` changed to +`3-32,49-80%16` and `33-48,81-96%8`. Expected: 62 light/medium tasks and 32 +heavy tasks, with at most 24 concurrent cells, 24 CPUs, and 43,200 MiB +requested across both arrays. Capture both job IDs, partition, wall time, +array expression, and submit SHA. + +- [ ] **Step 6: Monitor without treating scheduler state as evidence** + +For every job ID: + +```bash +scripts/harness_slurm.sh status BUILD_OR_ARRAY_JOB_ID +scripts/harness_slurm.sh classify pilot-p0-extension-v1 BUILD_OR_ARRAY_JOB_ID +``` + +Replace `BUILD_OR_ARRAY_JOB_ID` with each captured numeric ID. Check pending +reason within three minutes, one startup log after running, and `sacct` +classification at completion. Do not retry OOM, timeout, or logic failures +without user ratification. Successful scheduler state does not satisfy the +scientific gate; Task 10 does. + +### Task 6: Bounded Extension Aggregation + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py` + +**Interfaces:** +- Consumes: absolute verified extension `run_spec.json` and validated extension protocol mapping. +- Produces: `aggregate_p0_extension(run_spec: Path, protocol: Mapping[str, object]) -> dict[str, object]`. + +- [ ] **Step 1: Write failing aggregation and snapshot tests** + +Create tiny two-sigma, three-length, two-replica, three-coupling extension +fixtures. Assert one trajectory live at a time, exact grouping, `ddof=1`, +request order, source protocol/run/progress hashes, 102 production rows, +forged progress/manifest rejection, root/progress swap rejection, bounded +preflight, and cleanup behavior. + +- [ ] **Step 2: Verify RED** + +Run: + +```bash +uv run --with pytest pytest tests/test_pilot_analysis.py -q -k p0_extension +``` + +Expected: tests fail because `aggregate_p0_extension` is absent. + +- [ ] **Step 3: Generalize the retained verified snapshot by contract** + +Make `_open_verified_pilot_analysis_snapshot` dispatch the exact registered +run-spec schema and preserve all descriptor, resource, stale-owner, capacity, +global-byte-cap, and marker-last cleanup checks. Rename no version-2 stale +snapshot grammar; add the run kind to new snapshot names so P0 and extension +snapshots cannot collide. + +- [ ] **Step 4: Implement extension aggregation** + +Use the existing `_group_estimates` path with the per-cell sigma-specific +couplings. Emit: + +```python +{ + "schema_version": EXTENSION_ANALYSIS_SCHEMA, + "source_extension_protocol_sha256": protocol["protocol_sha256"], + "extension_run_spec_sha256": _sha256(snapshot.run_spec_payload), + "extension_progress_sha256": _sha256(snapshot.progress_payload), + "source_revision": spec["orchestration_revision"], + "analysis_plan_sha256": spec["analysis_plan_sha256"], + "observable_columns": dict(OBSERVABLE_COLUMNS), + "estimates": estimates, + "analysis_document_sha256": digest, +} +``` + +Require exact 2×3×16 cell order and 17 finite ten-column rows per trajectory. +Retain one trajectory and one 16×17×4 group array at a time. + +- [ ] **Step 5: Run focused and provenance regressions** + +Run: + +```bash +uv run --with pytest pytest tests/test_pilot_analysis.py tests/test_pilot.py -q +uv run --with ruff ruff check --ignore SIM102,TRY004,UP017,PYI025,F401 \ + src/long_range_percolation/pilot.py \ + src/long_range_percolation/pilot_analysis.py \ + tests/test_pilot_analysis.py +``` + +Expected: all tests pass and Ruff reports no new finding. + +- [ ] **Step 6: Commit Task 6** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py +git commit -m "Add bounded P0 extension aggregation" +``` + +### Task 7: Combined Evidence and Exact Pooling + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py` + +**Interfaces:** +- Consumes: validated P0 and extension analysis mappings. +- Produces: `combine_p0_evidence(p0_analysis: Mapping[str, object], extension_analysis: Mapping[str, object]) -> dict[str, object]`. + +- [ ] **Step 1: Write failing union, pooling, and cardinality tests** + +Use explicit replica arrays to derive independent expected means/SEs. Assert +16 rows for sigma `0.8` and `1.1`, 31 rows for sigma `0.9` and `1.0`, three +lengths, 282 total rows, counts 8/16/24, source order P0 then extension, +request uniqueness, and immutable source bindings. + +- [ ] **Step 2: Verify RED** + +Run: + +```bash +uv run --with pytest pytest tests/test_pilot_analysis.py -q -k combine_p0 +``` + +Expected: tests fail because `combine_p0_evidence` is absent. + +- [ ] **Step 3: Implement deterministic sufficient-moment pooling** + +For each observable and shared endpoint, convert each source standard error +back to its sample second central moment and combine in fixed P0-then-extension +order: + +```python +def _pool_estimates( + left_n: int, + left_mean: float, + left_se: float, + right_n: int, + right_mean: float, + right_se: float, +) -> tuple[int, float, float]: + total = left_n + right_n + delta = right_mean - left_mean + mean = left_mean + delta * right_n / total + left_m2 = (left_n - 1) * left_n * left_se * left_se + right_m2 = (right_n - 1) * right_n * right_se * right_se + pooled_m2 = left_m2 + right_m2 + delta * delta * left_n * right_n / total + sample_variance = pooled_m2 / (total - 1) + standard_error = math.sqrt(sample_variance / total) + if not all(math.isfinite(value) for value in (mean, standard_error)): + raise RuntimeError("combined estimate is nonfinite") + return total, mean, standard_error +``` + +Tests compare this result with direct concatenated whole-replica fixtures. +No checkpoint is counted as a replica. + +- [ ] **Step 4: Implement the per-sigma combined schema** + +Emit ordered `sigma_entries`, each containing its exact `kappas`, lengths, and +length-major estimate rows. Preserve P0 estimates byte-for-byte for sigma +`0.8`/`1.1`, use extension-only estimates at new points, and pool only the two +shared endpoints for each blocked sigma. Bind both source analysis hashes, +run/progress hashes, source revisions, observable columns, ordered request +hashes, and unsigned canonical-document hash. + +- [ ] **Step 5: Add adversarial combination tests** + +Reject source hash changes, wrong extension grid, overlap other than two +endpoints, duplicate requests, missing length/replica, reordered entries, +noncanonical float hex, nonfinite moments, observable-column mismatch, and an +internally rehashed document that claims 282 rows but has a different shape. + +- [ ] **Step 6: Run focused tests and commit** + +Run: + +```bash +uv run --with pytest pytest tests/test_pilot_analysis.py -q +git diff --check +``` + +Expected: all tests pass and diff check is silent. + +Commit: + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py +git commit -m "Combine verified P0 extension evidence" +``` + +### Task 8: Frozen Selector v2 and P1 Gate + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py` + +**Interfaces:** +- Consumes: P0-analysis-v1 or combined-analysis-v2 mapping. +- Produces: unchanged `select_p1_brackets(analysis: Mapping[str, object]) -> dict[str, object]`, bracket-v2 for combined input, and conditional `build_p1_protocol`. + +- [ ] **Step 1: Write failing per-sigma selector and invariance tests** + +Create combined fixtures with different coupling axes. Assert selected +nonzero common intervals for `0.9`/`1.0`, exact unchanged sigma `0.8` and +`1.1` windows, bracket-v2 source binding, byte-identical repeated selection, +and fail-closed output when one blocked sigma remains unresolved. + +- [ ] **Step 2: Verify RED** + +Run: + +```bash +uv run --with pytest pytest tests/test_pilot_analysis.py -q -k "combined_selector or p1_accepts_combined" +``` + +Expected: combined schema is rejected as unsupported. + +- [ ] **Step 3: Normalize both schemas without changing selection functions** + +Refactor only the input adapter. Return a tuple of per-sigma axes and values; +call existing `_transition_evidence`, `_select_transition_bracket`, and +`_select_crossover_bracket` unchanged: + +```python +@dataclass(frozen=True) +class SelectorSigmaEvidence: + sigma: float + lengths: tuple[int, ...] + kappas: tuple[float, ...] + values: Mapping[tuple[float, int, float], tuple[float, float]] + +def _selector_sigma_evidence( + analysis: Mapping[str, object], +) -> tuple[SelectorSigmaEvidence, ...]: + if analysis.get("schema_version") == ANALYSIS_SCHEMA: + return _selector_v1_evidence(analysis) + if analysis.get("schema_version") == COMBINED_ANALYSIS_SCHEMA: + return _selector_v2_evidence(analysis) + raise RuntimeError("analysis schema version is not supported") +``` + +For v1, retain `BRACKET_SCHEMA`; for combined v2, emit +`COMBINED_BRACKET_SCHEMA`. Both use the same evidence and tie-break payloads. + +- [ ] **Step 4: Gate P1 on combined evidence** + +`build_p1_protocol` accepts v1 only for backward-compatible tests and v2 for +the real handoff. For v2 it requires bracket-v2, all four statuses selected, +exact sigma `0.8` and `1.1` preserved windows, and +`requires_p0_extension is False`. P1 constants remain unchanged: +`P1_MASTER_SEED=19_420_261_729`, replicas `8..23`, four sigmas, three lengths, +nine points per selected interval, and `pilot-p1-v1`. + +- [ ] **Step 5: Run original-selector and P1 regression locks** + +Run: + +```bash +uv run --with pytest pytest tests/test_pilot_analysis.py -q +``` + +Expected: all tests pass; the exact original real-P0 bracket hash remains +`fb3df666044bf9531443fc00c5c2c2d489512b4162864b3a92ffc2e756832403`, +and original P0 still blocks P1 for `0.9, 1.0`. + +- [ ] **Step 6: Commit Task 8** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py +git commit -m "Rerun frozen selector on combined evidence" +``` + +### Task 9: Analysis CLI, Documentation, and Full Local Gate + +**Files:** +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py` +- Modify: `tracks/qmc/solutions/frustration-free/challenge-194/README.md` + +**Interfaces:** +- Produces: `analyze-extension`, `combine`, `select`, and combined-aware `build-p1`. + +- [ ] **Step 1: Write failing immutable command tests** + +For each new command, test `published`, `verified-existing`, changed-byte +rejection, malformed canonical input rejection, and no output on scientific +failure. `build-p1` must leave `p1_protocol.json` absent when the combined +bracket remains unresolved. + +- [ ] **Step 2: Verify RED** + +Run: + +```bash +uv run --with pytest pytest tests/test_analyze_pilot_cli.py -q +``` + +Expected: new command names are rejected by argparse. + +- [ ] **Step 3: Implement exact command dispatch** + +Register and implement: + +```text +analyze-extension --run-spec PATH --protocol PATH --output PATH +combine --p0-analysis PATH --extension-analysis PATH --p0-evidence-root DIR --extension-run-spec PATH --extension-protocol PATH --output PATH +select --analysis PATH --p0-analysis PATH --extension-analysis PATH --p0-evidence-root DIR --extension-run-spec PATH --extension-protocol PATH --output PATH +build-p1 --analysis PATH --p0-analysis PATH --extension-analysis PATH --p0-evidence-root DIR --extension-run-spec PATH --extension-protocol PATH --output PATH +``` + +All reads use `_read_canonical_json`; all writes use `_publish_or_verify`. +`select` publishes the schema returned by `select_p1_brackets`. `build-p1` +reruns selection in memory and refuses unresolved evidence before opening its +output target. For combined-v2, all five source/root/protocol inputs are +mandatory: the P0 root is deeply verified against its two frozen hashes and +exact historical analysis bytes, while extension analysis is recomputed from +the deeply verified exact extension root and immutable protocol and must be +byte-identical to the supplied analysis. Source JSON document hashes alone +never authenticate either path. + +- [ ] **Step 4: Document exact local artifact workflow** + +README commands use: + +```text +results/challenge-194/p0_extension_v1_protocol.json +results/challenge-194/pilot-p0-extension-v1/run_spec.json +results/challenge-194/p0_extension_v1_analysis.json +results/challenge-194/p0_combined_analysis_v2.json +results/challenge-194/p0_combined_brackets_v2.json +results/challenge-194/p1_protocol.json +``` + +State that source analyses must first be recomputed against their verified run +roots and return `verified-existing` before `combine`. + +- [ ] **Step 5: Run the full local gate** + +Run: + +```bash +cd /home/footman/code/quantum.harness-challenge-194/tracks/qmc/solutions/frustration-free/challenge-194 +uv run --with pytest pytest -q +uv run --with ruff ruff check --ignore SIM102,TRY004,UP017,PYI025,F401 \ + src/long_range_percolation/pilot.py \ + src/long_range_percolation/pilot_extension.py \ + src/long_range_percolation/pilot_analysis.py \ + scripts/run_pilot.py scripts/analyze_pilot.py \ + tests/test_pilot.py tests/test_pilot_extension.py \ + tests/test_pilot_analysis.py tests/test_analyze_pilot_cli.py tests/test_runtime.py +uv run --with ruff ruff format --check \ + src/long_range_percolation/pilot.py \ + src/long_range_percolation/pilot_extension.py \ + src/long_range_percolation/pilot_analysis.py \ + scripts/run_pilot.py scripts/analyze_pilot.py \ + tests/test_pilot.py tests/test_pilot_extension.py \ + tests/test_pilot_analysis.py tests/test_analyze_pilot_cli.py tests/test_runtime.py +bash -n scripts/download_pilot.sh scripts/pilot_extension_array_slurm.sh scripts/pilot_extension_build_slurm.sh +git diff --check +``` + +Expected: full pytest has zero failures, Ruff has no new findings, format is +clean, shell syntax is silent, and diff check is silent. + +- [ ] **Step 6: Commit Task 9** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py \ + tracks/qmc/solutions/frustration-free/challenge-194/README.md +git commit -m "Document P0 extension evidence workflow" +``` + +### Task 10: Harvest, Merge, Download, and Verify + +**Files:** +- Generated outside Git: remote/local extension root, sibling transfer state, scheduler/transfer logs. + +**Interfaces:** +- Consumes: three array job IDs and immutable remote run root. +- Produces: exact local verified 96-cell/96-trajectory extension root. + +- [ ] **Step 1: Classify all array outcomes and list pending cells** + +Run `status` and `classify` for all captured array IDs, then: + +```bash +HARNESS_CLUSTER_PROFILE=wuzh02-jiangweiqi \ + scripts/harness_slurm.sh pending-cells pilot-p0-extension-v1 +``` + +Expected: no pending cell IDs. If any cell failed, classify it as OOM, +walltime, nonzero exit, or logic failure; obtain user ratification before +resubmitting only those exact task IDs under the unchanged run spec. + +- [ ] **Step 2: Merge and verify remotely** + +Run: + +```bash +ssh wuzh02-jiangweiqi " + set -euo pipefail + export PYTHONPATH='/work/share/giggleliu/jiangweiqi/quantum.harness-p0-extension-v3/tracks/qmc/solutions/frustration-free/challenge-194/src' + '/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194/.venv/bin/python' \ + '/work/share/giggleliu/jiangweiqi/quantum.harness-p0-extension-v3/tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py' merge \ + --run-spec '/work/share/giggleliu/jiangweiqi/results/challenge-194/pilot-p0-extension-v1/run_spec.json' + '/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194/.venv/bin/python' \ + '/work/share/giggleliu/jiangweiqi/quantum.harness-p0-extension-v3/tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py' verify \ + --run-spec '/work/share/giggleliu/jiangweiqi/results/challenge-194/pilot-p0-extension-v1/run_spec.json' +" +``` + +Expected final verifier JSON: +`{"cells":96,"status":"verified","trajectories":96}`. + +- [ ] **Step 3: Download with hardened immutable transfer** + +Run from the solution directory: + +```bash +scripts/download_pilot.sh \ + wuzh02-jiangweiqi \ + /work/share/giggleliu/jiangweiqi/results/challenge-194/pilot-p0-extension-v1 \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-extension-v1 \ + /home/footman/code/quantum.harness-challenge-194/.venv/bin/python +``` + +Expected: checksummed transfer followed by exact 96/96 verifier JSON. +Transfer claims, logs, source, and verified completion remain sibling state +outside the immutable root. + +- [ ] **Step 4: Independently reverify locally and rerun completed download** + +Run local `run_pilot.py verify`, then repeat Step 3. Expected: both return +exact 96/96 JSON; the second transfer performs no rsync and does not change +the root or completion record. + +### Task 11: Publish Extension/Combined Evidence and Perform P1 Handoff + +**Files:** +- Generated outside Git: four immutable analysis/protocol JSON artifacts. +- Modify only after evidence exists: `tracks/qmc/solutions/frustration-free/challenge-194/README.md` if recording exact hashes. + +**Interfaces:** +- Consumes: verified P0 and extension roots and exact source analyses. +- Produces: extension analysis, combined analysis, combined brackets, and conditionally P1 protocol. + +- [ ] **Step 1: Recompute and byte-verify original P0 analysis** + +Run: + +```bash +uv run python scripts/analyze_pilot.py analyze \ + --run-spec /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-739880d/run_spec.json \ + --output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json +``` + +Expected: `publication` is `verified-existing` and embedded hash is +`e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8`. + +- [ ] **Step 2: Publish and byte-verify extension analysis** + +Run `analyze-extension` twice with the local extension run spec, exact +extension protocol, and +`results/challenge-194/p0_extension_v1_analysis.json`. Expected: first result +is `published`, second is `verified-existing`, there are exactly 102 estimate +rows, and source run/progress/protocol hashes match verified inputs. + +- [ ] **Step 3: Publish and byte-verify combined evidence** + +Run `combine` twice with `p0_analysis.json`, +`p0_extension_v1_analysis.json`, and output +`p0_combined_analysis_v2.json`. Expected: first is `published`, second is +`verified-existing`, there are exactly 282 rows, blocked sigmas each have 31 +couplings per length, and shared endpoints have replica count 24. + +- [ ] **Step 4: Publish and independently rerun frozen selection** + +Run `select` twice with combined analysis and output +`p0_combined_brackets_v2.json`. Expected: first is `published`, second is +`verified-existing`; compare both invocations byte-for-byte. + +- [ ] **Step 5: Evaluate the six acceptance checks** + +Require: + +```text +1. Protocol verifies against exact P0 evidence and committed design. +2. Extension root verifies 96 cells and 96 trajectories. +3. Extension and combined analyses verify all canonical/source/semantic bindings. +4. Sigma 0.9 and 1.0 are selected on nonzero intervals marked by both estimators. +5. Sigma 0.8 remains [0x1.f400000000000p-2,0x1.3880000000000p-1] and sigma 1.1 remains [0x1.312d000000000p+0,0x1.7d78400000000p+0]. +6. requires_p0_extension is false and independent bracket recomputation is byte-identical. +``` + +If any check fails, assert that `results/challenge-194/p1_protocol.json` is +absent, record the unresolved result, and stop. Do not alter sampling or +selection. + +- [ ] **Step 6: Conditionally publish P1 protocol** + +Only when all six checks pass, run: + +```bash +uv run python scripts/analyze_pilot.py build-p1 \ + --analysis /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_combined_analysis_v2.json \ + --output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p1_protocol.json +uv run python scripts/analyze_pilot.py verify \ + --analysis /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_combined_analysis_v2.json \ + --p1-protocol /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p1_protocol.json +``` + +Expected on pass: first command reports `published`, second reports +`verified`, P1 still has four sigmas × three lengths × 16 replicas = 192 +cells, and no P1 cell is executed. + +- [ ] **Step 7: Record exact evidence hashes in one final local commit** + +Update README only with observed immutable hashes and gate outcome; do not +commit generated results. Run the full Task 9 local gate, then: + +```bash +git add tracks/qmc/solutions/frustration-free/challenge-194/README.md +git commit -m "Record P0 extension boundary evidence" +``` + +Expected: one documentation-only commit. Leave `.superpowers/sdd/task-1-report.md` +and `.superpowers/sdd/progress.md` untouched. + +## Plan Completion Criteria + +- Tasks 1–4 produce the shortest safe submission path and an exact clean + submission revision. +- Task 5 submits all 96 cells in smoke, light/medium, and heavy batches without + exceeding 24 concurrent one-core cells. +- Tasks 6–9 finish analysis code and local verification while Slurm runs. +- Task 10 closes the compute boundary with fetched semantic manifests, not + scheduler status. +- Task 11 either publishes a verified P1 protocol without running it or + preserves the fail-closed unresolved state. +- Every implementation task has focused RED/GREEN evidence and a local commit; + no push is part of this plan. diff --git a/docs/superpowers/specs/2026-07-30-challenge-194-p0-extension-design.md b/docs/superpowers/specs/2026-07-30-challenge-194-p0-extension-design.md new file mode 100644 index 000000000..31011d92e --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-challenge-194-p0-extension-design.md @@ -0,0 +1,504 @@ +# Challenge 194 Versioned P0 Extension Design + +## Decision and scope + +The approved extension is a new exploratory `pilot-p0-extension-v1` +campaign. It samples only sigma `0.9` and `1.0`, at all three P0 lengths, +with 16 fresh replicas and one fixed 17-point binary64 grid per sigma. The +extension is designed only to resolve the existing mismatch between the +two frozen P1 bracket estimators. It does not alter P0, relax the selector, +run P1, add extended observables, or authorize a scientific claim. + +This document is the complete design. Implementation, cluster submission, +data generation, and P1 publication are out of scope. + +## Binding existing evidence + +The extension is derived from, and must remain bound to, these immutable +inputs: + +- verified P0 root: + `results/challenge-194/pilot-p0-739880d`; +- P0 verifier result: + `{"cells": 96, "status": "verified", "trajectories": 96}`; +- P0 run-spec SHA256: + `d17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840`; +- P0 merged-progress SHA256: + `ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f`; +- immutable analysis: + `results/challenge-194/p0_analysis.json`; +- embedded analysis-document SHA256: + `e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8`; +- complete canonical analysis-file SHA256: + `44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b`; +- current frozen bracket-document SHA256: + `fb3df666044bf9531443fc00c5c2c2d489512b4162864b3a92ffc2e756832403`; +- P0 source/orchestration revision: + `739880d9ccdcffbfc8a15310250349bd11d63bbb`. + +The current selector uses lengths `16384` and `262144`. It selects sigma +`0.8` on +`[0x1.f400000000000p-2, 0x1.3880000000000p-1]` and the sigma `1.1` +crossover on +`[0x1.312d000000000p+0, 0x1.7d78400000000p+0]`, but fails closed for +sigma `0.9` and `1.0` with +`no_nonzero_interval_marked_by_both_estimators`. Consequently +`p1_protocol.json` does not exist. + +The protocol builder must verify all hashes above before publishing an +extension protocol. The extension protocol also records the SHA256 of the +committed bytes of this design and the implementation source revision. A +hash, schema, source, environment, or path mismatch is fatal. + +## Exact range derivation from real P0 + +### Original P0 coupling indices + +The nonzero part of the original ordered P0 grid relevant to this design is: + +| Index | Exact binary64 coupling | +|---:|---| +| 4 | `0x1.f400000000000p-2` | +| 5 | `0x1.3880000000000p-1` | +| 6 | `0x1.86a0000000000p-1` | +| 7 | `0x1.e848000000000p-1` | +| 8 | `0x1.312d000000000p+0` | +| 9 | `0x1.7d78400000000p+0` | +| 10 | `0x1.dcd6500000000p+0` | +| 12 | `0x1.74876e8000000p+1` | +| 13 | `0x1.d1a94a2000000p+1` | +| 14 | `0x1.2309ce5400000p+2` | +| 15 | `0x1.6bcc41e900000p+2` | + +An interval index `i` means the closed interval from coupling `i` to +coupling `i + 1`. + +### Deterministic component rule + +The extension range is derived without changing the frozen selector: + +1. Recompute all original-P0 interval marks using the current selector's + exact rules and the two largest lengths. +2. Group contiguous marked intervals separately for `Q_G` and four-sector + crossing. +3. For each blocked sigma, choose the four-sector component at the lowest + coupling. Choose the `Q_G` component with the smallest interval-index gap + to that four-sector component, breaking an equal gap by lower coupling. +4. Take the closed span of those two components. +5. Add exactly one original-P0 interval immediately below and one immediately + above that span. +6. Use the resulting outer endpoints for four recursive binary64 midpoint + levels, producing 17 ordered points. + +The component rule is used only to preregister the extension range. The final +selector still examines every adjacent nonzero interval in the combined +evidence. In particular, this rule does not discard candidates from final P1 +selection. + +This component rule handles the exact high-coupling P0 evidence explicitly. +At sigma `0.9`, `Q_G` also marks the disconnected component `13..14`; at +sigma `1.0`, it also marks `12..14`. Their endpoint differences are at +binary64 saturation scale, neither component is marked by the four-sector +estimator, and both are farther from the lowest four-sector component than +the selected `Q_G` component. They therefore do not enlarge the extension +range, but they remain available to the frozen selector after evidence is +combined. + +### Sigma 0.9 + +Exact sigma identity: `0x1.ccccccccccccdp-1`. + +- Four-sector marked component: interval `5`, from + `0x1.3880000000000p-1` to `0x1.86a0000000000p-1`. +- Nearest `Q_G` marked component: interval `6`, from + `0x1.86a0000000000p-1` to `0x1.e848000000000p-1`. +- Estimator union: intervals `5..6`. +- Added left guard: interval `4`. +- Added right guard: interval `7`. +- Final extension span: intervals `4..7`, with endpoints + `0x1.f400000000000p-2` and `0x1.312d000000000p+0`. + +The exact 17-point grid is: + +```text +[ + "0x1.f400000000000p-2", + "0x1.1085a00000000p-1", + "0x1.270b400000000p-1", + "0x1.3d90e00000000p-1", + "0x1.5416800000000p-1", + "0x1.6a9c200000000p-1", + "0x1.8121c00000000p-1", + "0x1.97a7600000000p-1", + "0x1.ae2d000000000p-1", + "0x1.c4b2a00000000p-1", + "0x1.db38400000000p-1", + "0x1.f1bde00000000p-1", + "0x1.0421c00000000p+0", + "0x1.0f64900000000p+0", + "0x1.1aa7600000000p+0", + "0x1.25ea300000000p+0", + "0x1.312d000000000p+0" +] +``` + +### Sigma 1.0 + +Exact sigma identity: `0x1.0000000000000p+0`. + +- Four-sector marked component: intervals `6..7`, from + `0x1.86a0000000000p-1` to `0x1.312d000000000p+0`. +- Nearest `Q_G` marked component: interval `8`, from + `0x1.312d000000000p+0` to `0x1.7d78400000000p+0`. +- Estimator union: intervals `6..8`. +- Added left guard: interval `5`. +- Added right guard: interval `9`. +- Final extension span: intervals `5..9`, with endpoints + `0x1.3880000000000p-1` and `0x1.dcd6500000000p+0`. + +The exact 17-point grid is: + +```text +[ + "0x1.3880000000000p-1", + "0x1.6092ca0000000p-1", + "0x1.88a5940000000p-1", + "0x1.b0b85e0000000p-1", + "0x1.d8cb280000000p-1", + "0x1.006ef90000000p+0", + "0x1.14785e0000000p+0", + "0x1.2881c30000000p+0", + "0x1.3c8b280000000p+0", + "0x1.50948d0000000p+0", + "0x1.649df20000000p+0", + "0x1.78a7570000000p+0", + "0x1.8cb0bc0000000p+0", + "0x1.a0ba210000000p+0", + "0x1.b4c3860000000p+0", + "0x1.c8cceb0000000p+0", + "0x1.dcd6500000000p+0" +] +``` + +For each span, generation starts with the two endpoints and repeats four +levels of `left + (right - left) / 2.0` over the current sorted adjacent +pairs. Values are deduplicated by exact `float.hex()` identity, sorted by +numeric value, and required to yield exactly 17 points with unchanged +endpoints. The protocol stores only the canonical hex strings and hashes each +ordered grid. The grid hash is SHA256 of canonical +`{"kappas":[]}` plus one trailing newline. The exact +hashes are: + +- sigma `0.9`: + `76dc7e07639ed085873a8f291cc2aaee0e8942ddac8efce3982743dd67491071`; +- sigma `1.0`: + `d40b4a2afac533d74965513513fff1870918831000b2e040063ca2a0e29ad091`. + +## Alternatives considered + +### Selected: one targeted 17-point grid per blocked sigma + +This design fixes both grids before any new data exist, covers the complete +gap between the nearest estimator-marked components, and includes one +original-P0 guard interval on each side. It costs 96 trajectories and 1,632 +trajectory checkpoints. It is broad enough to detect modest finite-size +drift without spending samples on sigma values that already passed the +selector. + +### Full four-sigma P0 replacement + +A replacement using four sigmas, three lengths, and 16 fresh replicas would +cost 192 trajectories and 3,264 checkpoints at the same 17-point density. +It would duplicate adequate evidence for sigma `0.8` and `1.1`, create an +unnecessary choice between old and replacement evidence, and increase +cluster and review cost without addressing a broader failure. It is rejected. + +### Adaptive or disjoint estimator-centered refinements + +Two small grids, or a first refinement followed by a data-dependent second +grid, could reduce work when the estimators rapidly align. They would leave +an unsampled gap or let extension data choose later sampling locations, +creating another exploratory decision and publication round. A single +precommitted 17-point span is easier to authenticate, restart, combine, and +audit. The adaptive/disjoint approach is rejected. + +## Frozen protocol identities and cardinality + +The extension protocol schema is +`challenge-194-p0-extension-protocol-v1`. +The corresponding run-spec, merged-progress, extension-analysis, combined- +analysis, and combined-bracket schemas are respectively: + +- `challenge-194-p0-extension-run-spec-v1`; +- `challenge-194-p0-extension-progress-v1`; +- `challenge-194-p0-extension-analysis-v1`; +- `challenge-194-p0-combined-analysis-v2`; +- `challenge-194-p1-brackets-v2`. + +- Sigmas, in order: + `0x1.ccccccccccccdp-1`, + `0x1.0000000000000p+0`. +- Lengths, in order: `1024`, `16384`, `262144`. +- Replicas, in order: integers `24..39`. +- Loop order: sigma, length, replica. +- Cells: `2 * 3 * 16 = 96`. +- Trajectories: exactly 96, one per cell. +- Couplings per trajectory: exactly 17, selected by sigma. +- Trajectory checkpoints: `96 * 17 = 1,632`. +- Aggregate extension estimate rows: `2 * 3 * 17 = 102`. +- Phase: existing exploratory phase string `"pilot"`. +- Master seed: `19_420_262_729`. +- Grid namespace: `"pilot-p0-extension-v1"`. + +Replica labels do not overlap P0 `0..7` or the reserved P1 labels `8..23`. +The new master seed and sigma grid IDs provide an additional disjoint RNG +identity boundary. The exact per-sigma grid IDs are: + +```text +pilot-p0-extension-v1|sigma-f64=0x1.ccccccccccccdp-1|source-analysis=e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8|range=0x1.f400000000000p-2:0x1.312d000000000p+0 +pilot-p0-extension-v1|sigma-f64=0x1.0000000000000p+0|source-analysis=e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8|range=0x1.3880000000000p-1:0x1.dcd6500000000p+0 +``` + +Every request digest and every counter-RNG stream material digest must be +unique within the extension and disjoint from the verified P0 assignments +and the deterministically reserved P1 assignments. Any collision blocks +protocol publication. + +The extension uses the existing ten-column basic observable schema and the +same trajectory realization/stopping policy as P0. It aggregates `Q_G`, +four-sector crossing, `S1/L`, and `S2/L`. Adding the future extended +observable schema is a separate P1 task and is not part of this extension. + +## Interfaces and compatibility boundaries + +Implementation will add versioned entry points while preserving every +existing P0 and P1 behavior: + +- `build_p0_extension_protocol(p0_analysis: Mapping[str, object]) -> dict[str, object]` + validates the exact P0 analysis, recomputes the ranges and grids, assigns + RNG identities, and produces the canonical extension protocol. +- `build_p0_extension_run_spec(output_root: Path, validation_report: Path, + protocol: Mapping[str, object]) -> dict[str, object]` produces the 96-cell + immutable run spec using the existing approved scientific engine and + correctness registry. +- Existing cell execution, pending, merge, and download-verification machinery + is generalized by schema dispatch. The current production P0 loader remains + strict and must not accept an extension as P0. +- `aggregate_p0_extension(run_spec: Path, + protocol: Mapping[str, object]) -> dict[str, object]` uses the same retained, + bounded verified-snapshot mechanism as `aggregate_p0` and emits the 102 + extension estimates. +- `combine_p0_evidence(p0_analysis: Mapping[str, object], + extension_analysis: Mapping[str, object]) -> dict[str, object]` produces a + versioned per-sigma combined document. +- `select_p1_brackets(analysis: Mapping[str, object]) -> dict[str, object]` + gains schema dispatch for the combined document, but calls the same interval + marking, candidate selection, and tie-break logic. The original P0-analysis + path and its exact output remain regression locked. +- `build_p1_protocol(analysis: Mapping[str, object], + brackets: Mapping[str, object] | None = None) -> dict[str, object]` accepts + the verified combined document only after the acceptance gate below. Its + existing four-sigma, three-length, 16-replica P1 cardinality, master seed, + grid namespace, and fail-closed behavior do not change. + +The CLI grammar is below. Uppercase names are argparse metavariables for the +absolute paths whose concrete artifact names are fixed in the publication +section; they are not undecided protocol values. + +```text +analyze_pilot.py build-p0-extension --analysis P0_ANALYSIS_PATH --output EXTENSION_PROTOCOL_PATH +run_pilot.py build-extension-spec --protocol EXTENSION_PROTOCOL_PATH --validation-report APPROVED_VALIDATION_REPORT_PATH --output-root EXTENSION_ROOT --run-spec EXTENSION_RUN_SPEC_PATH +run_pilot.py run-cell --run-spec EXTENSION_RUN_SPEC_PATH --cell-index CELL_INDEX +run_pilot.py pending --run-spec EXTENSION_RUN_SPEC_PATH +run_pilot.py merge --run-spec EXTENSION_RUN_SPEC_PATH +run_pilot.py verify --run-spec EXTENSION_RUN_SPEC_PATH +analyze_pilot.py analyze-extension --run-spec EXTENSION_RUN_SPEC_PATH --protocol EXTENSION_PROTOCOL_PATH --output EXTENSION_ANALYSIS_PATH +analyze_pilot.py combine --p0-analysis P0_ANALYSIS_PATH --extension-analysis EXTENSION_ANALYSIS_PATH --output COMBINED_ANALYSIS_PATH +analyze_pilot.py select --analysis COMBINED_ANALYSIS_PATH --output COMBINED_BRACKETS_PATH +analyze_pilot.py build-p1 --analysis COMBINED_ANALYSIS_PATH --output P1_PROTOCOL_PATH +``` + +Every command requires canonical absolute paths. Schema dispatch is based on +authenticated document content, never a filename or user-selected permissive +flag. Test-only flexible schemas remain inaccessible from production CLIs. + +## Evidence combination + +The combined schema is `challenge-194-p0-combined-analysis-v2`. It has a +separate ordered coupling axis for each sigma, allowing the two extension +grids to coexist with the common original P0 grid without fabricating a +rectangular grid. + +For sigma `0.8` and `1.1`, the combined document retains the original 16 +couplings and eight-replica estimates unchanged. For sigma `0.9` and `1.0`, +it takes the exact sorted binary64 union of the 16 original points and the 17 +extension points. Each extension shares exactly its two endpoints with P0 and +has 15 new interior points, so each blocked sigma has 31 distinct couplings. +Across all three lengths, the combined document therefore contains exactly +`3 * (16 + 31 + 31 + 16) = 282` estimate rows. + +At an extension-only coupling, the estimate has 16 replicas. At an +original-only coupling, it has eight. At either shared endpoint, P0's eight +and the extension's 16 independent whole trajectories are pooled in fixed +source order, P0 then extension, for 24 replicas. Means and `ddof=1` sample +standard errors are recomputed from the verified whole-trajectory values; +checkpoint rows are never treated as independent replicas. Request hashes are +stored in the same fixed order and must be unique. + +Combination revalidates both source analysis digests and both verified run +roots. It records both run-spec hashes, both progress hashes, both analysis +document hashes, ordered request identities, observable columns, source +revisions, and its own unsigned canonical-document SHA256. It never modifies +or replaces either source analysis. + +The selector normalizes each sigma entry to its own strictly increasing +coupling sequence, excludes the zero-coupling interval exactly as before, +uses only lengths `16384` and `262144`, and applies the unchanged rules: + +1. mark `Q_G` sign-change intervals; +2. mark intervals where either length's four-sector endpoints span the + closed range `[0.25, 0.75]`; +3. for sigma at most one, select the narrowest interval marked by both, + then the lower coupling; +4. for sigma `1.1`, select maximum absolute largest-size four-sector slope, + then the lower coupling. + +No interpolation, uncertainty-based rescue, nearest-interval fallback, +threshold adjustment, or manual candidate choice is permitted. + +## Artifact and publication boundaries + +The planned immutable artifacts are: + +1. `results/challenge-194/p0_extension_v1_protocol.json` + — range derivation, exact grids, identities, source hashes, and complete + ordered 96-cell assignment. +2. Remote and downloaded root + `results/challenge-194/pilot-p0-extension-v1/` + — `run_spec.json`, 96 cell trees, and merged `progress.json`. +3. `results/challenge-194/p0_extension_v1_analysis.json` + — 102 authenticated aggregate estimates. +4. `results/challenge-194/p0_combined_analysis_v2.json` + — 282 source-bound combined estimates. +5. `results/challenge-194/p0_combined_brackets_v2.json` + — the rerun frozen-selector result and its canonical hash. +6. `results/challenge-194/p1_protocol.json` + — still absent unless all acceptance checks pass. + +JSON uses sorted keys, compact separators, finite values only, UTF-8, and one +trailing newline. Each artifact contains a schema version and an internal +SHA256 over the unsigned canonical document. Publication uses the existing +atomic no-clobber boundary: an absent target may be installed once; an +existing byte-identical target returns `verified-existing`; different +existing bytes fail and are never replaced. + +Transfer state, claims, and transfer logs are sibling paths outside the +immutable downloaded run root, following the current hardened P0 download +contract. Source trees and published analysis artifacts are never edited in +place. + +## Cluster resources and restart behavior + +Heavy execution remains Wuzh02-only. The extension uses one single-core Slurm +array with tasks `1..96`, task `n` mapping to cell index `n - 1`, and +scheduler-managed concurrency. + +Each task requests exactly: + +- one CPU; +- 1800 MiB memory; +- 40 minutes wall time; +- one private node-local Numba cache; +- no GPU. + +The 40-minute request allows for the increase from 16 to 17 checkpoints per +trajectory. It is a scheduling choice, not a claim that the waived +120-second/4-GiB capability gate passed. The implementation must retain the +current environment sanitization, one-thread pins, approved offline Python, +scientific-source checks, and uniquely created mode-restricted Numba cache. + +Cell layout remains: + +```text +cells//run/{request.json,environment.json,kernel/, + seed-manifest.json,capability.json,trajectories/,batches/, + progress.json,manifest.json} +cells//manifest.json +``` + +Restart is allowed only against the identical extension protocol, run spec, +source revision, runtime contract, request, and RNG assignment. An existing +completed cell is deeply verified and skipped. A completely published +trajectory may resume missing batch, progress, or outer-manifest boundaries. +Duplicate workers serialize at the cell directory; the loser succeeds only +after verifying the winner's exact output. + +Any surviving `.partial` or `.intent`, malformed marker, hash mismatch, +unexpected path, cell swap, shared-directory substitution, or source/runtime +drift fails closed and remains for diagnosis. It is never automatically +deleted. A timeout or infrastructure failure is retried only as the same cell +under the same immutable run spec; changing grid, seed, replica, source, or +scientific settings requires a new versioned protocol and root. + +Merge requires exactly 96 successful cells and 96 trajectories, no extras, +and complete canonical ordering. Download uses the existing checksummed, +partial-safe, no-delete transfer and local semantic verifier. Analysis begins +only after local verification succeeds. + +## Verification and acceptance gate + +Implementation tests must establish: + +- exact range derivation from the immutable real-P0 fixture, including the + distant high-coupling `Q_G` components; +- the two exact 17-point grids above, generated rather than copied; +- exact protocol axes, 96-cell order, 1,632 checkpoints, 102 extension rows, + fresh identities, and collision rejection; +- production-schema separation between P0, extension, combined analysis, and + P1; +- bounded one-trajectory-at-a-time extension aggregation and retained + snapshot authentication; +- exact 24-replica pooling at shared endpoints and exact 282-row combined + cardinality; +- immutable publication, byte-identical rerun verification, and no-clobber + rejection; +- all existing P0 aggregation, original selector, P1 builder refusal, + restart, transfer, and artifact tests remain unchanged and passing; +- adversarial rejection of reordered grids, noncanonical floats, missing or + duplicate replicas, forged source hashes, RNG collisions, nonfinite means, + stale manifests, swapped roots, and partial markers. + +The operational gate passes only when all of the following are true: + +1. The extension protocol verifies against the exact existing P0 evidence and + committed design. +2. The downloaded extension root verifies exactly 96 cells and 96 + trajectories under the immutable protocol. +3. The extension analysis and combined analysis verify by schema, canonical + bytes, internal hashes, source hashes, request identities, and semantic + recomputation. +4. Rerunning the frozen selector on the combined evidence returns `selected` + for both sigma `0.9` and `1.0`, with a nonzero adjacent interval marked by + both estimators. +5. The rerun reproduces the exact existing sigma `0.8` transition bracket + `[0x1.f400000000000p-2, 0x1.3880000000000p-1]` and sigma `1.1` crossover + bracket + `[0x1.312d000000000p+0, 0x1.7d78400000000p+0]`. +6. The bracket document says `requires_p0_extension=false`, and a fresh + independent recomputation is byte-identical. + +Only after all six checks pass may the existing P1 builder publish +`p1_protocol.json`. If either blocked sigma still lacks a common marked +interval, or if any other check fails, P1 remains absent and blocked. The +result is reported as unresolved; no extra points, threshold changes, +interpolation, or manual bracket may be added under version 1. + +## Explicit non-goals + +- No P0 or P0-analysis mutation. +- No sigma `0.8` or `1.1` extension trajectories. +- No adaptive second extension. +- No selector, threshold, tie-break, or zero-coupling relaxation. +- No P1 execution or confirmatory sampling. +- No extended-observable implementation. +- No transition, critical-point, exponent, scaling, or universality claim. diff --git a/docs/superpowers/specs/2026-07-30-challenge-194-p0-extension-v2-design.md b/docs/superpowers/specs/2026-07-30-challenge-194-p0-extension-v2-design.md new file mode 100644 index 000000000..cf323f085 --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-challenge-194-p0-extension-v2-design.md @@ -0,0 +1,515 @@ +# Challenge 194 Standalone Coarse-Grid P0 Extension v2 Design + +## Decision, scope, and claim boundary + +The approved next campaign is the versioned exploratory +`pilot-p0-extension-v2`. It samples only sigma `0.9` and `1.0`, uses the +original P0 lengths, and applies the existing frozen P1 selector physics +byte-for-byte to a new standalone five-point coarse grid for each blocked +sigma. + +The purpose is narrowly preregistered as: + +1. test the observed sensitivity of the mean-based four-sector mark to + coupling-grid topology; and +2. authorize exploratory P1 only if the unchanged selector passes its existing + fail-closed rule. + +**P0 extension v2, the authorization decision, and any resulting P1 are +exploratory. They cannot support a transition, critical-point, exponent, +scaling, density-jump, universality, or other physical claim. Later +confirmatory sampling with a disjoint RNG phase, frozen production protocol, +and preregistered analysis remains mandatory.** + +This document freezes design only. Implementation, deployment, sampling, +artifact publication, P1 execution, and scientific claims are out of scope. + +## Authenticated immutable inputs + +Construction and every later authorization operation must require explicit +absolute canonical non-symlink paths to the relevant artifacts. Filenames, +checkout-local discovery, and self-declared hashes are not trust anchors. + +### Original P0 + +- evidence root: `results/challenge-194/pilot-p0-739880d`; +- verified cardinality: 96 cells and 96 trajectories; +- source revision: + `739880d9ccdcffbfc8a15310250349bd11d63bbb`; +- `run_spec.json` file SHA256: + `d17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840`; +- `progress.json` file SHA256: + `ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f`; +- `p0_analysis.json` document SHA256: + `e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8`; +- `p0_analysis.json` file SHA256: + `44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b`; +- original bracket document SHA256: + `fb3df666044bf9531443fc00c5c2c2d489512b4162864b3a92ffc2e756832403`. + +The historical P0 analysis remains immutable. It is authenticated by its exact +bytes and exact evidence root, not recomputed under a plan revision whose +committed bytes differ from the plan bound by the historical run spec. + +### P0 extension v1 and combined-v2 evidence + +- v1 design SHA256: + `5426e3007e9d83039f371ca6a9372f1868ef9d5447b66a12b1643ecf72907aba`; +- protocol document SHA256: + `a37ab41f3224594e61f4eebbe292975aeec449b9ecb7893e3e54f18d82d53321`; +- protocol file SHA256: + `e363a60f842b11b32972c7a68ec1c5f237741bc45bc79ab8bf93f51f6760d84d`; +- source revision: + `9308087c5c609519234da48136b88cdd60f79667`; +- v1 run-spec file SHA256: + `c1ca9b6c8ba751919c6d9337fe1cd4c09a57ed9b99abbb9d3ebfed7f89c3d32e`; +- v1 progress file SHA256: + `c78d1fb03daf19297ef9e0617410c68a6a364bffc2f2888dfa9067e7e8d6b65f`; +- v1 analysis document SHA256: + `79232574d314348c29a40cd2fbb7690e96f3cae5f26843bd4f1cf07cb6a1f45b`; +- v1 analysis file SHA256: + `d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5`; +- combined-v2 analysis document SHA256: + `36f85c40e9159ef2e69742672c261769fb28d2f3c947780ba63e4ef5fe5975c3`; +- combined-v2 analysis file SHA256: + `6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929`; +- combined-v2 bracket document SHA256: + `098f19d8883097d5f1f274ce759416328c086958fa5301c034a0b46dcbd562df`; +- combined-v2 bracket file SHA256: + `7a84d545b4526d94aa6f93ca4f0d264dcf01e518f2f9b04383921634786c9962`. + +The v1 root must again verify as exactly 96 cells and 96 trajectories. +Extension-v2 construction must deeply authenticate original P0, v1, and +combined-v2 evidence and require semantic recomputation of combined-v2 from +its two source analyses. A copied combined JSON file alone is insufficient. + +### Correctness package and design binding + +The implementation must retain the checked-in +`pilot_correctness_approval.json` trust boundary: + +- approval/source revision: + `877ab9393f320bfe31ff74a26c3db1fb205d7ef3`; +- report SHA256: + `036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8`; +- validation run-spec SHA256: + `5b3eea4c460e14a57aec9df606447137d787a5c66dd7e98e1dffdcf566f430e2`; +- protocol SHA256: + `c7e980eeadaf8ed75e4d20cebb1e2c5d5f57a1cfc329afa7678ae586f5b7f488`; +- check-registry SHA256: + `6e25ea41899544f2a9de3589beb1ee94b1f3dc505638b8f8e5164a4322b56a1d`; +- scientific-engine aggregate SHA256: + `457fa669da897e59b03681039db6121fde4d7be9295bb46a743c8448875b3ee9`. + +The v2 protocol must bind the SHA256 of the committed bytes of this design and +the exact clean implementation revision. That design hash is computed and +frozen by implementation after this spec commit; it is not self-declared in +this document. + +## Quantitative rationale + +The immutable combined-v2 selector remains unresolved for sigma `0.9` and +`1.0`, while sigma `0.8` and `1.1` retain their exact frozen windows. + +For sigma `0.9`, combined-v2 has a primary `Q_G` marked component from +`0x1.6a9c200000000p-1` through `0x1.97a7600000000p-1`, but no four-sector +marked interval. On the adjacent interval +`[0x1.6a9c200000000p-1, 0x1.8121c00000000p-1]`, the four-sector means are: + +- length `16384`: `0.7500 ± 0.1118` to `0.9375 ± 0.0625`; +- length `262144`: `0.4375 ± 0.1281` to `0.8125 ± 0.1008`. + +The rise is present, but no single fine-grid interval starts at or below +`0.25` and ends at or above `0.75`. The proposed coarse interval +`[0x1.5416800000000p-1, 0x1.97a7600000000p-1]` has, in the authenticated +combined-v2 means, a positive-to-negative `Q_G(16384)-Q_G(262144)` sign change +and a four-sector rise spanning the selector's closed target range. + +For sigma `1.0`, the four-sector component is +`[0x1.e848000000000p-1, 0x1.006ef90000000p+0]`, while the primary `Q_G` +component begins at its upper endpoint and continues through +`0x1.2881c30000000p+0`. The components are adjacent but share no interval. +At `0x1.006ef90000000p+0`, the `Q_G` size difference is +`+0.1391 ± 0.0740`; at `0x1.14785e0000000p+0` it is +`-0.00294 ± 0.0230`. The proposed coarse interval +`[0x1.d8cb280000000p-1, 0x1.14785e0000000p+0]` spans both the observed +four-sector rise and the observed `Q_G` sign change. + +These observations motivate a grid-topology sensitivity experiment. They do +not establish that v2 will pass, do not authorize interpolation, and do not +turn the exploratory data into physical evidence. + +## Frozen v2 scientific protocol + +### Axes and exact grids + +Sigma order is: + +```text +[ + "0x1.ccccccccccccdp-1", + "0x1.0000000000000p+0" +] +``` + +Length order is `[1024, 16384, 262144]`, exactly the original P0 lengths. + +The sigma `0.9` grid is exactly: + +```text +[ + "0x0.0p+0", + "0x1.270b400000000p-1", + "0x1.5416800000000p-1", + "0x1.97a7600000000p-1", + "0x1.e848000000000p-1" +] +``` + +Its canonical grid SHA256, over canonical +`{"kappas":[]}` plus one trailing newline, is +`28155d7f982584787089f4a80d617783bd82b84e2ed833df3dcaa98955254d24`. + +The sigma `1.0` grid is exactly: + +```text +[ + "0x0.0p+0", + "0x1.b0b85e0000000p-1", + "0x1.d8cb280000000p-1", + "0x1.14785e0000000p+0", + "0x1.3c8b280000000p+0" +] +``` + +Its canonical grid SHA256 is +`b9abfff153302b8556312fbc5a59e6a8e7c98d8bd3c301cb90252c85a5c473f4`. + +Every value above is an exact canonical `float.hex()` value already present +on the deeply authenticated combined-v2 axis; zero also occurs on the +authenticated original P0 axis. No rounded decimal value, arithmetic +regeneration, midpoint rule, or later substitution is permitted. Protocol +construction must load the authenticated source axes, copy these exact +strings, prove membership, prove strict numeric order, and prove the two grid +hashes. + +Zero coupling remains an invariant checkpoint and the interval beginning at +zero remains ineligible for selection under the unchanged selector. + +### Replica and RNG identity + +- replica labels, in order: integers `40..71`, exactly 32 labels; +- master seed: `19_420_263_729`; +- phase: `"pilot"`; +- grid namespace: `"pilot-p0-extension-v2"`; +- loop order: sigma, length, replica; +- one trajectory per cell. + +Replica labels are disjoint from original P0 `0..7`, reserved P1 `8..23`, and +v1 `24..39`. The master seed is distinct from original P0 +`19_420_260_729`, P1 `19_420_261_729`, and v1 `19_420_262_729`. + +The per-sigma grid identities are exactly: + +```text +pilot-p0-extension-v2|sigma-f64=0x1.ccccccccccccdp-1|source-combined-analysis=36f85c40e9159ef2e69742672c261769fb28d2f3c947780ba63e4ef5fe5975c3|grid-sha256=28155d7f982584787089f4a80d617783bd82b84e2ed833df3dcaa98955254d24 +pilot-p0-extension-v2|sigma-f64=0x1.0000000000000p+0|source-combined-analysis=36f85c40e9159ef2e69742672c261769fb28d2f3c947780ba63e4ef5fe5975c3|grid-sha256=b9abfff153302b8556312fbc5a59e6a8e7c98d8bd3c301cb90252c85a5c473f4 +``` + +Every request digest and counter-RNG stream-material digest must be unique +within v2 and disjoint from the deeply verified original P0 and v1 +assignments and deterministically reconstructed reserved P1 assignments. Any +collision blocks protocol publication. + +### Cardinality and observables + +- cells and trajectories: `2 * 3 * 32 = 192`; +- checkpoints per trajectory: exactly 5; +- total trajectory checkpoints: `192 * 5 = 960`; +- standalone v2 estimate rows: `2 * 3 * 5 = 30`; +- authorization-evidence rows: `30 + 2 * 3 * 16 = 126`. + +The scientific engine, ten-column trajectory schema, realization policy, +stopping policy, and basic observables are unchanged. V2 aggregates `Q_G`, +four-sector crossing, `S1/L`, and `S2/L`. Checkpoints from one monotone +trajectory remain correlated and are never counted as replicas. + +## Standalone evidence and selector boundary + +### No union of blocked-sigma points + +For sigma `0.9` and `1.0`, authorization uses only the 32-replica standalone +v2 estimates on the five-point v2 grid. It must not union, pool, interpolate, +or otherwise combine those blocked-sigma estimates with original P0 or v1 +points. + +The original P0, v1, and combined-v2 artifacts remain immutable, preserved, +authenticated inputs. Their omission from the blocked-sigma selector axis is +an explicit preregistered grid-topology sensitivity boundary, not deletion or +replacement of evidence. V1 remains reportable as an unresolved exploratory +result. + +### Untouched controls + +The new authorization-evidence document contains four ordered sigma entries: + +1. sigma `0.8`: byte-for-byte copied estimates, lengths, and 16-point axis + from authenticated original P0; +2. sigma `0.9`: standalone v2 estimates only; +3. sigma `1.0`: standalone v2 estimates only; +4. sigma `1.1`: byte-for-byte copied estimates, lengths, and 16-point axis + from authenticated original P0. + +Control estimates are not recomputed, pooled, rounded, or rewritten. The +authorization builder deeply authenticates their P0 root and analysis bytes +and requires the exact existing sigma `0.8` transition window +`[0x1.f400000000000p-2, 0x1.3880000000000p-1]` and sigma `1.1` crossover +window +`[0x1.312d000000000p+0, 0x1.7d78400000000p+0]`. + +### Byte-identical frozen selector physics + +Schema normalization may be added outside the selector, but the existing +selector physics remains byte-identical: + +1. use the two largest lengths, `16384` and `262144`; +2. mark each adjacent interval containing a sign change in + `mean Q_G(16384) - mean Q_G(262144)`; +3. independently mark an interval if either length's four-sector endpoint + means span the closed range `[0.25, 0.75]`; +4. for sigma at most one, retain intervals marked by both estimators, select + the narrowest, and break equal-width ties by lower coupling; +5. for sigma `1.1`, select the maximum absolute largest-size four-sector + slope, breaking ties by lower coupling; +6. exclude the zero-coupling interval exactly as before. + +The implementation must leave the current transition-evidence, transition +selection, crossover selection, candidate ordering, thresholds, tie-breaks, +and zero rule function bodies byte-for-byte unchanged. Regression tests must +prove exact original P0 and combined-v2 bracket reproduction before testing +the new authorization schema. + +There is no uncertainty rescue, endpoint confidence interval, interpolation, +nearest-component fallback, manual candidate choice, threshold change, or +post-hoc grid change. + +## Versioned schemas and immutable artifacts + +The exact new schema names are: + +- `challenge-194-p0-extension-protocol-v2`; +- `challenge-194-p0-extension-run-spec-v2`; +- `challenge-194-p0-extension-progress-v2`; +- `challenge-194-p0-extension-analysis-v2`; +- `challenge-194-p0-authorization-analysis-v3`; +- `challenge-194-p1-brackets-v3`; +- conditional `challenge-194-p1-protocol-v2`. + +The exact artifact names are: + +1. `results/challenge-194/p0_extension_v2_protocol.json`; +2. `results/challenge-194/pilot-p0-extension-v2/run_spec.json`; +3. `results/challenge-194/pilot-p0-extension-v2/progress.json`; +4. the immutable 192-cell tree below that run root; +5. `results/challenge-194/p0_extension_v2_analysis.json`; +6. `results/challenge-194/p0_authorization_analysis_v3.json`; +7. `results/challenge-194/p0_authorization_brackets_v3.json`; +8. conditionally, and only after every acceptance check passes, + `results/challenge-194/p1_protocol_v2.json`. + +The v2 protocol records all authenticated input file and document hashes, +design hash, implementation revision, axes, grids and grid hashes, complete +ordered cell assignment, request identities, RNG identities, correctness +package, runtime contract, purpose, and its own document hash. + +The v2 analysis binds the protocol, run spec, merged progress, source +revision, design, observable columns, ordered request identities, replica +count 32, 30 estimates, and its own document hash. + +Authorization analysis v3 binds the exact P0 and v2 analyses and evidence +roots plus the authenticated v1 and combined-v2 inputs that justified this +preregistration. It records the per-sigma source role (`p0-control` or +`v2-standalone`), separate ordered axes, 126 estimates, request identities, +all source hashes, and its own document hash. It is reconstructed +semantically from trusted sources; supplied authorization JSON is never +trusted alone. + +Canonical JSON uses finite values only, sorted keys, compact separators, +UTF-8, and exactly one trailing newline. Every publication is atomic and +no-clobber: an absent target may be created once; a byte-identical existing +target returns `verified-existing`; different existing bytes fail without +replacement. + +## Construction, execution, restart, and transfer + +Protocol construction, run-spec construction, analysis, authorization, +selection, and conditional P1 construction must use explicit trusted inputs. +Production CLIs must not accept test schemas or infer evidence from the +checkout. Mixed v1/v2 flags, omitted trust roots, extraneous source arguments, +or a schema/path mismatch fail closed. + +Each cell retains the existing layout: + +```text +cells//run/{request.json,environment.json,kernel/, + seed-manifest.json,capability.json,trajectories/,batches/, + progress.json,manifest.json} +cells//manifest.json +``` + +Restart is permitted only for the identical protocol, run spec, source, +runtime, request, kernel, grid, seed, replica, and RNG assignment. Completed +cells are deeply verified and skipped. A complete trajectory may resume only +missing batch, inner-progress, run-manifest, or outer-manifest publication. +Duplicate workers serialize at the cell directory and a loser succeeds only +after verifying the winner's exact artifact. + +Surviving `.partial` or `.intent` files, malformed markers, unexpected paths, +source drift, runtime drift, swapped cells, ancestor substitution, hash +mismatch, or stale manifests remain preserved for diagnosis and fail closed. +They are never deleted or repaired automatically. Merge requires exactly 192 +successful cells and 192 trajectories, no extras, in canonical order. + +Transfer uses the existing checksummed, partial-safe, no-delete download +contract. Claims, source markers, completion state, diagnostics, and logs are +sibling paths outside the immutable run root. A completed download is deeply +reverified without invoking transfer again. Local analysis starts only after +the downloaded root passes semantic verification. + +## Wuzh02 deployment and resource contract + +Heavy execution is Wuzh02-only. Deployment must use one exact clean committed +repository revision containing the implementation and this design. The +repository-root offline interpreter is: + +```text +/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194/.venv/bin/python +``` + +Build and worker processes retain the existing influential-environment +sanitization, one-thread pins, approved scientific-source checks, exact lock +and runtime hashes, and a newly created private node-local Numba cache. No +login-node capability is presumed portable. + +Each cell requests exactly: + +- one CPU; +- 1800 MiB memory; +- 40 minutes wall time; +- no GPU; +- one private node-local Numba cache. + +The 40-minute allocation is a scheduling ceiling, not a passed performance +gate. At most 40 cells may run concurrently, and only when account, partition, +and scheduler limits permit; otherwise scheduler-managed concurrency is +lowered without changing any scientific identity. + +Slurm array IDs are canonical decimal integers `1..192` and map to cell index +`ID - 1`. Signs, whitespace, leading-zero aliases, non-digits, overflow-sized +values, and out-of-range values fail before arithmetic. + +### Smoke gate + +Before releasing the remaining array, execute exactly cell indices +`0`, `64`, `96`, and `160`: the first replica at length `1024` and length +`262144` for each sigma. Concurrency is at most four. + +The smoke gate passes only if all four jobs: + +1. exit successfully under the exact clean deployment; +2. publish one complete immutable trajectory and both manifests; +3. pass immediate deep semantic verification, including request, grid, + kernel, environment, RNG, and artifact hashes; +4. show no `.partial`, `.intent`, unexpected path, memory failure, timeout, + oversubscription, or runtime/source drift. + +Failure stops release of all remaining cells. Infrastructure retries reuse +the exact same cell identities and artifacts. A scientific, schema, identity, +or provenance failure requires a new reviewed design version; it is not +retried with altered settings. + +After smoke approval, remaining cells may be submitted with an array +concurrency cap of 40. Smoke cells are verified and skipped rather than +resampled if included in a complete immutable array specification. + +## Verification and acceptance + +Implementation tests must prove: + +- exact authentication and semantic recomputation of every input listed here; +- exact five-point source-axis membership, ordering, strings, and grid hashes; +- exact sigma, length, replica, seed, phase, namespace, and loop order; +- 192 unique cells, 192 trajectories, 960 checkpoints, 30 v2 estimates, and + 126 authorization estimates; +- disjoint request and RNG identities across P0, P1, v1, and v2; +- standalone blocked-sigma evidence with no union or pooling; +- byte-identical P0 controls and exact preserved control brackets; +- byte-identical selector physics and exact P0/combined-v2 regression output; +- one-trajectory-at-a-time bounded aggregation and authenticated snapshots; +- immutable publication, byte-identical retry, restart, transfer, and + no-clobber behavior; +- fail-closed rejection of forged hashes, self-signed sources, reordered or + rounded grids, duplicate/missing replicas, nonfinite moments, source swaps, + partial markers, stale manifests, ABA replacement, and unexpected paths. + +The operational acceptance rule is conjunctive: + +1. the v2 protocol validates against this exact committed design, exact clean + implementation, correctness package, and all authenticated P0/v1/combined + inputs; +2. the downloaded v2 root verifies exactly 192 cells and 192 trajectories; +3. standalone v2 analysis recomputes byte-identically with exactly 30 + estimates and replica count 32; +4. authorization analysis v3 recomputes byte-identically with exact untouched + P0 controls, standalone v2 blocked sigmas, exactly 126 estimates, and no + blocked-sigma P0/v1 union; +5. the byte-identical frozen selector returns `selected` for both sigma `0.9` + and `1.0`, each on a nonzero adjacent interval marked by both estimators; +6. sigma `0.8` and `1.1` reproduce their exact existing transition and + crossover windows; +7. authorization brackets v3 say `requires_p0_extension=false`, and a fresh + independent authenticated recomputation is byte-identical. + +Only if all seven checks pass may `p1_protocol_v2.json` be published. Its +scientific P1 axes, reserved replicas `8..23`, master seed +`19_420_261_729`, `"pilot"` phase, selector-derived nine-point windows, and +exploratory-only purpose remain unchanged from the existing P1 design; the +new schema records authorization-analysis-v3 and brackets-v3 source hashes. +Publishing the protocol does not authorize local or cluster P1 execution +without a separate reviewed execution step. + +If any check fails, P1 remains absent and unresolved. No extra replica, +coupling, size, sigma, interpolation, threshold, selector change, manual +window, or adaptive follow-up is permitted under v2. The failed immutable +result is reported as such. + +## Rejected alternatives + +### More replicas on the v1 17-point grids + +Adding replicas at the same fine points could reduce standard errors but +cannot change the adjacency topology. Sigma `0.9` currently has no +four-sector marked interval because the rise is split across fine intervals; +sigma `1.0` has adjacent, non-overlapping estimator components. Joint rescue +by mean movement alone is not supported strongly enough to justify a larger +repeat campaign. This alternative is rejected. + +### Richer finite-size or `2^20` exploration + +Adding intermediate sizes, more sigmas, or length `2^20` would better diagnose +finite-size drift but would cost substantially more, would not directly test +the observed grid-topology failure, and would cross the existing `2^20` +information-gain/runtime gate. Such a campaign may be preregistered later as +exploratory finite-size science while accepting unresolved P1. It is rejected +for this narrowly scoped authorization extension. + +## Explicit non-goals + +- no mutation or replacement of P0, v1, combined-v2, or their analyses; +- no union of P0/v1 fine points into blocked-sigma authorization axes; +- no sigma `0.8` or `1.1` v2 sampling; +- no new length, observable, estimator, threshold, tie-break, or selector; +- no adaptive sampling or post-hoc change; +- no P1 execution in this design; +- no confirmatory use of P0, v1, v2, or P1; +- no physical claim of any kind. diff --git a/docs/superpowers/specs/2026-07-30-challenge-194-production-pipeline-design.md b/docs/superpowers/specs/2026-07-30-challenge-194-production-pipeline-design.md new file mode 100644 index 000000000..fbd9b058d --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-challenge-194-production-pipeline-design.md @@ -0,0 +1,179 @@ +# Challenge 194 Production Pipeline Design + +## Goal + +Turn the verified 96-cell P0 Pilot into an immutable P1 refinement, a +preregistered confirmatory production campaign, finite-size-scaling results, +figures, and a reproducible final report without allowing exploratory data to +enter confirmatory likelihoods. + +## Scope and ordering + +The work is split into four independently reviewable subsystems: + +1. P0 download, local verification, deterministic analysis, and P1 protocol. +2. Extended observable schema and P1 execution. +3. Confirmatory preregistration and cluster production. +4. Scaling analysis, figures, report, and Task 12 public API/documentation. + +Each subsystem has its own TDD plan and immutable publication boundary. A +later subsystem cannot run until all upstream artifacts verify by hash and +schema. + +## Existing immutable evidence + +- Scientific-engine revision: `877ab9393f320bfe31ff74a26c3db1fb205d7ef3`. +- Correctness report SHA256: + `036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8`. +- Correctness run-spec SHA256: + `5b3eea4c460e14a57aec9df606447137d787a5c66dd7e98e1dffdcf566f430e2`. +- P0 orchestration revision: `739880d9ccdcffbfc8a15310250349bd11d63bbb`. +- P0 merged progress SHA256: + `ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f`. +- P0 contains exactly 96 verified trajectories: four sigma values, three + lengths, and eight fresh Pilot replicas. + +The remote P0 tree is copied without modification. Local verification must +pass before analysis reads any trajectory. + +## P0 analysis and deterministic P1 selection + +For each sigma and nonzero P0 coupling, aggregate whole-trajectory estimates +of `Q_G`, four-sector crossing probability, `S1/L`, and `S2/L`. Report the +mean, sample standard error, and exact contributing request hashes. + +The P1 bracket is selected from adjacent P0 coupling intervals by this frozen +rule: + +1. Use the two largest P0 sizes. +2. Mark intervals containing a sign change in the difference of their mean + `Q_G` values. +3. Mark intervals where either size's four-sector crossing probability spans + the closed range `[0.25, 0.75]` between adjacent checkpoints. +4. For `sigma <= 1`, select the narrowest interval marked by both estimators. + If no common interval exists, fail closed and issue a versioned P0-extension + protocol instead of choosing post hoc. +5. For `sigma = 1.1`, select the interval with the largest absolute + finite-difference change in the largest-size crossing probability, breaking + ties by lower coupling. Label it a crossover refinement only. + +P1 uses nine points per selected interval: both endpoints and seven recursively +bisected interior points. Every point is serialized with `float.hex()` and the +ordered grid is hashed. P1 uses the same three lengths as P0 and 16 fresh +replicas per `(sigma, L)`, under the existing exploratory `pilot` phase with a +new P1 grid namespace and master seed. P0 replicas are never reused. + +## Extended observables + +The current ten-column basic observable matrix remains unchanged. A versioned +extended measurement group is added at every registered checkpoint: + +- exact-small and logarithmic-large finite-cluster size bins; +- bond-length histogram; +- pair connectivity at preregistered logarithmic separations; +- finite-cluster connectivity with the largest component removed; +- finite-cluster structure factors for modes `m = 0,...,8`. + +The full `S1/L` distribution is formed from whole trajectories; it is not +approximated by an in-trajectory histogram. Extended arrays use frozen shapes, +explicit little-endian dtypes, bounded byte counts, and hashes included in the +trajectory and batch manifests. Existing schema versions remain readable but +cannot satisfy P1 or confirmatory loaders. + +Measurements remain `O(L)` per checkpoint up to fixed registered bin/mode +factors. The Python reference and Numba implementation receive independent +small-system oracles. Any scientific-engine change triggers the full 120-cell +correctness gate and a new approval registry before P1. + +## P1 execution and acceptance + +P1 is exploratory. It runs as immutable single-threaded Slurm cells with fresh +RNG identities, atomic HDF5/JSON publication, no-clobber semantics, and +manifest-based local download. + +P1 succeeds only when: + +- both primary estimators bracket a common change region for `sigma <= 1`; +- sigma `1.1` shows size drift without being labeled a transition; +- extended observables pass semantic reload and bounded consistency checks; +- all cells complete or are resumed under the identical source and run spec. + +The P1 analysis freezes confirmatory windows but contributes no samples to +confirmatory fits. + +## Confirmatory preregistration + +The confirmatory protocol is committed before any confirmatory trajectory is +generated. It freezes: + +- retained sigma/coupling windows, with at least 12 near-critical sigma-one + couplings; +- lengths `2^10, 2^12, 2^14, 2^16, 2^18` for retained sigmas; +- sigma-one intermediate lengths `2^11, 2^13, 2^15, 2^17`; +- at least eight independent streams per immutable trajectory batch; +- a disjoint `confirmatory` RNG phase and master seed; +- deterministic thinning, realization ceilings, and stopping checks; +- the exact analysis-plan SHA256. + +Sampling stops only at completed batch boundaries when both dimensionless +observable standard errors are at most `0.01` near transition and relative +spectral-scale error is at most `8%`, or when the preregistered ceiling is +reached. `L=2^20` remains excluded unless the documented information-gain and +projected eight-hour gates pass. + +## Analysis + +Primary transition estimators are pairwise `Q_G(L)`/`Q_G(2L)` crossings and +four-sector crossing-probability crossings. Their extrapolated 95% intervals +must overlap; otherwise the transition is reported unresolved. + +At sigma one, fit the complete `S1/L` distribution with registered one- versus +two-component diagnostics. A stable antimode permits equal-weight +pseudotransition and peak-separation analysis. Maximum slope is diagnostic, +not a primary transition estimator. + +Finite-cluster scales use + +`F_L(k)^-1 = a0 + a_sigma |q|^sigma + a2 q^2` + +and `xi_sigma = (a_sigma/a0)^(1/sigma)`. Unresolved coefficients become +censored bounds. Sigma-one scaling compares exactly four models: fixed `2/3`, +free essential exponent, algebraic, and fixed `2/3` with one logarithmic +correction. + +Nested bootstrap resamples whole streams and then immutable batches, refitting +transition locations, spectral scales, and nonlinear models. Sigma `0.8` and +`0.9` are continuous-side controls. Sigma `1.1` is a negative control and +never receives a finite critical-point claim. + +## Figures and report + +Generated artifacts include crossing plots with simultaneous uncertainty, +sigma-one `S1/L` distributions, spectral-scale fits with censored points, +four-model comparisons, deletion/sensitivity summaries, coarse sigma controls, +and resource/convergence diagnostics. + +Every table and figure records source batch hashes, run-spec hash, analysis +plan hash, source revision, and generation command. The final report states +the kernel convention, validation evidence, resources, transition interval, +density-jump evidence, scaling verdict, controls, limitations, and exact +reproduction commands. Accepted outcomes are support, falsification on +accessible scales, or an honest inconclusive result. + +## Public API and documentation + +Task 12 exports the stable model, RNG, alias, observable, request/result, +reference, and production entry points from `long_range_percolation`. +README commands cover environment verification, P0/P1/confirmatory execution, +immutable download verification, analysis, plotting, restart behavior, and +the performance-gate waiver without describing it as passed. + +## Failure handling + +- Hash, schema, provenance, path, semantic, or source drift fails closed. +- Partial/intent markers are preserved for diagnosis and never silently + deleted. +- Exploratory and confirmatory namespaces cannot be merged. +- Missing brackets create a new versioned exploratory request. +- Unresolved fits remain censored or inconclusive; no post-hoc model or window + selection is allowed. diff --git a/scripts/tests/test_cluster_profile.py b/scripts/tests/test_cluster_profile.py index e9101d3bc..5319e5bd3 100644 --- a/scripts/tests/test_cluster_profile.py +++ b/scripts/tests/test_cluster_profile.py @@ -222,6 +222,234 @@ def all_keys(value): assert forbidden_keys.isdisjoint(all_keys(profile)) +def test_public_xh5_profile_is_safe_complete_and_parseable(capsys): + root = cp.Path(__file__).resolve().parents[2] + path = root / "skills/using-slurm/profiles/xh5-jiangweiqi.toml" + mirror_path = root / ".agents/skills/using-slurm/profiles/xh5-jiangweiqi.toml" + profile = cp.load_profile(path) + + assert cp.validate(profile) == [] + assert mirror_path.read_bytes() == path.read_bytes() + assert profile["identity"] == { + "name": "xh5-jiangweiqi", + "purpose": "XH5 CPU compute service", + "maintainer": "QuantumBFS", + } + assert profile["connection"] == { + "repo_path_remote": ( + "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" + ), + "login_shell": False, + "ssh": {"alias": "xh5-jiangweiqi"}, + } + assert profile["scheduler"] == { + "type": "slurm", + "default_partition": "xhacnormalb", + "account": "giggleliu", + "qos": "user_jiangweiqi", + } + assert profile["partitions"] == [ + { + "name": "xhacnormalb", + "class": "default-cpu", + "cores": 128, + "memory": "513500M", + "def_mem_per_cpu": "3931M", + "max_wall": "333-00:00:00", + } + ] + assert profile["filesystem"] == { + "home": "/work/home/jiangweiqi", + "scratch": "/work/share/giggleliu/jiangweiqi/results", + "project": ( + "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" + ), + "quota": "", + } + assert profile["network"] == { + "internet_from_login": False, + "internet_from_compute": False, + } + assert profile["region"] == {"region": "mainland_china"} + + limits = cp.get_limits(profile) + assert limits.hard == { + "max_walltime": "24:00:00", + "max_nodes": 1, + "max_cpus": 128, + "max_array_size": 200, + } + assert limits.soft == { + "warn_walltime": "08:00:00", + "warn_cpus": 64, + "unusual_partitions": [], + } + assert limits.allowed_roots == [ + "/work/share/giggleliu/jiangweiqi/results", + ( + "/work/share/giggleliu/jiangweiqi/" + "quantum.harness-challenge-194/results" + ), + ] + + rc = cp.main( + [ + "--field", + "scheduler.account", + "--profile", + str(path), + ] + ) + assert rc == 0 + assert capsys.readouterr().out.strip() == "giggleliu" + + forbidden_keys = { + "host", + "hostname", + "user", + "username", + "port", + "key", + "key_path", + "identity_file", + "password", + "token", + "secret", + } + + def all_keys(value): + if isinstance(value, dict): + for key, child in value.items(): + yield key.lower() + yield from all_keys(child) + elif isinstance(value, list): + for child in value: + yield from all_keys(child) + + assert forbidden_keys.isdisjoint(all_keys(profile)) + raw = path.read_text(encoding="utf-8").lower() + for forbidden_fragment in ("~/.ssh", "private key", "identityfile"): + assert forbidden_fragment not in raw + assert ( + "/work/share/giggleliu/jiangweiqi/quantum.harness/.venv/bin/python" + in raw + ) + + +def test_public_wuzh02_profile_is_safe_complete_and_parseable(capsys): + root = cp.Path(__file__).resolve().parents[2] + path = root / "skills/using-slurm/profiles/wuzh02-jiangweiqi.toml" + mirror_path = root / ".agents/skills/using-slurm/profiles/wuzh02-jiangweiqi.toml" + profile = cp.load_profile(path) + + assert cp.validate(profile) == [] + assert mirror_path.read_bytes() == path.read_bytes() + assert profile["identity"] == { + "name": "wuzh02-jiangweiqi", + "purpose": "Wuzh02 CPU compute service", + "maintainer": "QuantumBFS", + } + assert profile["connection"] == { + "repo_path_remote": ( + "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" + ), + "login_shell": False, + "ssh": {"alias": "wuzh02-jiangweiqi"}, + } + assert profile["scheduler"] == { + "type": "slurm", + "default_partition": "wzacnormal03", + "account": "giggleliu", + "qos": "user_jiangweiqi", + } + assert profile["partitions"] == [ + { + "name": "wzacnormal03", + "class": "default-cpu", + "cores": 128, + "memory": "255500M", + "def_mem_per_cpu": "1916M", + "max_wall": "333-00:00:00", + } + ] + assert profile["filesystem"] == { + "home": "/work/home/jiangweiqi", + "scratch": "/work/share/giggleliu/jiangweiqi/results", + "project": ( + "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" + ), + "quota": "", + } + assert profile["network"] == { + "internet_from_login": False, + "internet_from_compute": False, + } + assert profile["region"] == {"region": "mainland_china"} + + limits = cp.get_limits(profile) + assert limits.hard == { + "max_walltime": "24:00:00", + "max_nodes": 1, + "max_cpus": 128, + "max_array_size": 200, + } + assert limits.soft == { + "warn_walltime": "08:00:00", + "warn_cpus": 64, + "unusual_partitions": [], + } + assert limits.allowed_roots == [ + "/work/share/giggleliu/jiangweiqi/results", + ( + "/work/share/giggleliu/jiangweiqi/" + "quantum.harness-challenge-194/results" + ), + ] + + rc = cp.main( + [ + "--partition", + "wzacnormal03", + "--field", + "def_mem_per_cpu", + "--profile", + str(path), + ] + ) + assert rc == 0 + assert capsys.readouterr().out.strip() == "1916M" + + forbidden_keys = { + "host", + "hostname", + "user", + "username", + "port", + "key", + "key_path", + "identity_file", + "password", + "token", + "secret", + } + + def all_keys(value): + if isinstance(value, dict): + for key, child in value.items(): + yield key.lower() + yield from all_keys(child) + elif isinstance(value, list): + for child in value: + yield from all_keys(child) + + assert forbidden_keys.isdisjoint(all_keys(profile)) + raw = path.read_text(encoding="utf-8").lower() + for forbidden_fragment in ("~/.ssh", "private key", "identityfile"): + assert forbidden_fragment not in raw + for required_fragment in ("portable cpython 3.12", "manylinux2014", "1800m"): + assert required_fragment in raw + + # --------------------------------------------------------------------------- # # CLI # --------------------------------------------------------------------------- # diff --git a/scripts/tests/test_harness_slurm.py b/scripts/tests/test_harness_slurm.py index 269b288c2..69cd287f6 100644 --- a/scripts/tests/test_harness_slurm.py +++ b/scripts/tests/test_harness_slurm.py @@ -143,6 +143,50 @@ def test_wait_rejects_unknown_flag(): assert "unknown flag" in r.stderr +def test_xh5_profile_precheck_dry_run_shape(): + profile = REPO / "skills" / "using-slurm" / "profiles" / "xh5-jiangweiqi.toml" + result = run( + ["precheck"], + env={ + "HARNESS_PROFILE_FILE": str(profile), + "HARNESS_SLURM_DRYRUN": "1", + }, + ) + assert result.returncode == 0, result.stderr + assert f"profile: {profile}" in result.stdout + assert "alias: xh5-jiangweiqi" in result.stdout + assert ( + "repo: " + "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" + ) in result.stdout + assert "ssh_ok: dryrun" in result.stdout + + +def test_wuzh02_profile_precheck_dry_run_shape(): + profile = ( + REPO + / "skills" + / "using-slurm" + / "profiles" + / "wuzh02-jiangweiqi.toml" + ) + result = run( + ["precheck"], + env={ + "HARNESS_PROFILE_FILE": str(profile), + "HARNESS_SLURM_DRYRUN": "1", + }, + ) + assert result.returncode == 0, result.stderr + assert f"profile: {profile}" in result.stdout + assert "alias: wuzh02-jiangweiqi" in result.stdout + assert ( + "repo: " + "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" + ) in result.stdout + assert "ssh_ok: dryrun" in result.stdout + + # --------------------------------------------------------------------------- # # smoke-verdict # --------------------------------------------------------------------------- # diff --git a/skills/using-slurm/profiles/wuzh02-jiangweiqi.toml b/skills/using-slurm/profiles/wuzh02-jiangweiqi.toml new file mode 100644 index 000000000..3d9dc4c77 --- /dev/null +++ b/skills/using-slurm/profiles/wuzh02-jiangweiqi.toml @@ -0,0 +1,79 @@ +[identity] +name = "wuzh02-jiangweiqi" +purpose = "Wuzh02 CPU compute service" +maintainer = "QuantumBFS" + +[connection] +repo_path_remote = "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" +login_shell = false + +[connection.ssh] +alias = "wuzh02-jiangweiqi" + +[scheduler] +type = "slurm" +default_partition = "wzacnormal03" +account = "giggleliu" +qos = "user_jiangweiqi" + +[[partitions]] +name = "wzacnormal03" +class = "default-cpu" +cores = 128 +memory = "255500M" +def_mem_per_cpu = "1916M" +max_wall = "333-00:00:00" + +[filesystem] +home = "/work/home/jiangweiqi" +scratch = "/work/share/giggleliu/jiangweiqi/results" +project = "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" +quota = "" + +[network] +internet_from_login = false +internet_from_compute = false + +[region] +region = "mainland_china" + +[limits.hard] +max_walltime = "24:00:00" +max_nodes = 1 +max_cpus = 128 +max_array_size = 200 + +[limits.soft] +warn_walltime = "08:00:00" +warn_cpus = 64 +unusual_partitions = [] + +[limits.paths] +allowed_roots = [ + "/work/share/giggleliu/jiangweiqi/results", + "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194/results", +] + +[[gotchas]] +symptom = "The cluster has no preinstalled project Python runtime" +cause = "Wuzh02 login and compute nodes are offline" +fix = "bootstrap portable CPython 3.12 and the exact manylinux2014 wheels offline" + +[[gotchas]] +symptom = "Source or wheel bootstrap attempts contact the public internet" +cause = "Neither login nor compute nodes have internet access" +fix = "transfer source with a git bundle and stage all exact wheels before bootstrap" + +[[gotchas]] +symptom = "The Challenge 194 validation cell exceeds default per-CPU memory" +cause = "wzacnormal03 has DefMemPerCPU 1916M" +fix = "request one CPU and 1800M per validation cell, below the 1916M default" + +[commands] +squeue = "squeue -u $USER" +sacct = "sacct --format=JobID,State,ExitCode,MaxRSS,Elapsed" +sinfo = "sinfo -o '%P %a %.10l %.6D %.6t'" +quota_command = "sacctmgr -n -P show assoc where user=$USER format=Account,Partition,QOS,GrpTRES,MaxTRES" + +[notes] +text = "Use wzacnormal03 for the 120-cell Challenge 194 CPU array: one CPU, 1800M, and ten minutes per cell. Bootstrap the portable offline runtime before submission." diff --git a/skills/using-slurm/profiles/xh5-jiangweiqi.toml b/skills/using-slurm/profiles/xh5-jiangweiqi.toml new file mode 100644 index 000000000..505c8ca8a --- /dev/null +++ b/skills/using-slurm/profiles/xh5-jiangweiqi.toml @@ -0,0 +1,79 @@ +[identity] +name = "xh5-jiangweiqi" +purpose = "XH5 CPU compute service" +maintainer = "QuantumBFS" + +[connection] +repo_path_remote = "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" +login_shell = false + +[connection.ssh] +alias = "xh5-jiangweiqi" + +[scheduler] +type = "slurm" +default_partition = "xhacnormalb" +account = "giggleliu" +qos = "user_jiangweiqi" + +[[partitions]] +name = "xhacnormalb" +class = "default-cpu" +cores = 128 +memory = "513500M" +def_mem_per_cpu = "3931M" +max_wall = "333-00:00:00" + +[filesystem] +home = "/work/home/jiangweiqi" +scratch = "/work/share/giggleliu/jiangweiqi/results" +project = "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194" +quota = "" + +[network] +internet_from_login = false +internet_from_compute = false + +[region] +region = "mainland_china" + +[limits.hard] +max_walltime = "24:00:00" +max_nodes = 1 +max_cpus = 128 +max_array_size = 200 + +[limits.soft] +warn_walltime = "08:00:00" +warn_cpus = 64 +unusual_partitions = [] + +[limits.paths] +allowed_roots = [ + "/work/share/giggleliu/jiangweiqi/results", + "/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194/results", +] + +[[gotchas]] +symptom = "The cluster has the approved scientific Python packages but no uv or internet" +cause = "XH5 login and compute nodes are offline" +fix = "pass /work/share/giggleliu/jiangweiqi/quantum.harness/.venv/bin/python through harness --command" + +[[gotchas]] +symptom = "Source or wheel synchronization attempts contact the public internet" +cause = "Neither login nor compute nodes have internet access" +fix = "transfer source with a git bundle and stage dependencies as offline wheels" + +[[gotchas]] +symptom = "The Challenge 194 validation array exceeds the intended CPU service request" +cause = "Generic site defaults do not encode the frozen validation cell resources" +fix = "use 120 array cells with one CPU and 3800M per cell, below the 3931M DefMemPerCPU" + +[commands] +squeue = "squeue -u $USER" +sacct = "sacct --format=JobID,State,ExitCode,MaxRSS,Elapsed" +sinfo = "sinfo -o '%P %a %.10l %.6D %.6t'" +quota_command = "sacctmgr -n -P show assoc where user=$USER format=Account,Partition,QOS,GrpTRES,MaxTRES" + +[notes] +text = "Use xhacnormalb for the 120-cell Challenge 194 CPU array: one CPU, 3800M, and ten minutes per cell. Use the shared offline Python runtime and dedicated challenge repository." diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/DESIGN.md b/tracks/qmc/solutions/frustration-free/challenge-194/DESIGN.md new file mode 100644 index 000000000..ae1e561cf --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/DESIGN.md @@ -0,0 +1,426 @@ +# Challenge 194 design + +## Scientific objective + +Determine what can be concluded, with controlled finite-size and model-selection +uncertainty, about the one-dimensional long-range `q=1` random-cluster model: + +1. identify and simulate the pinned finite-ring model without importing + thresholds from different short-distance or boundary conventions; +2. locate or bound the transition across a small decay-exponent map; +3. at the marginal point `sigma = 1`, test whether the finite-cluster + crossover scale supports Cardy's formal `q -> 1` continuation + `nu_tilde = 2/3`; +4. separate a giant-cluster plateau from finite-cluster connectivity before + making any statement about `eta`. + +The accepted conclusions are support, falsification on accessible scales, a +different model identification, or an explicitly inconclusive result. + +## Pinned model + +For even `L`, vertices are `Z/LZ`. Every unordered pair `{i, j}` is sampled +once and independently: + +```text +p_ij(kappa, sigma) = 1 - exp[-kappa J_L,sigma(i-j)] +J_L,sigma(r) = sum_{n in Z} |r + nL|^[-(1+sigma)]. +``` + +There are no loops, duplicate edges, distance cutoff, Kac normalization, or +independently tuned nearest-neighbor probability. At `q=1`, the FK cluster +weight is one, so the measure is an independent Bernoulli product measure. + +For `s = 1 + sigma` and `1 <= d <= L/2`, + +```text +J_L,sigma(d) = + L^(-s) [zeta(s, d/L) + zeta(s, 1-d/L)]. +``` + +At `sigma = 1`, + +```text +J_L,1(d) = (pi/L)^2 csc^2(pi d/L). +``` + +Distance classes have multiplicity `L` for `d < L/2` and `L/2` for the +antipodal class `d = L/2`. + +This model is not Gori et al.'s minimum-image `C/r^(1+sigma)` model. Its +finite-size thresholds and corrections must be determined independently. + +## Theoretical anchors and claim boundary + +At `sigma = 1`, the infinite-volume asymptotic tail coefficient is `kappa`. +The rigorous literature implies: + +- no percolation for `kappa <= 1`; +- if the percolation density `theta` is nonzero, then + `kappa theta^2 >= 1`; +- a finite threshold exists on the pinned diagonal, but its value is not + known exactly; +- the onset of `theta` is necessarily discontinuous; +- strictly subcritical pair connectivity retains an algebraic `r^-2` tail. + +These results do not establish Cardy's `nu_tilde = 2/3`, a critical `eta`, or +an exact `kappa_c`. The density jump is compatible with an essential +divergence of a finite-cluster crossover scale on the subcritical side. + +For `sigma > 1`, the infinite-volume decay exponent is greater than two and +there is no finite-`kappa` percolation transition. `sigma = 1.1` is therefore +a negative control whose pseudocritical drift must not be reported as a +finite transition. + +## Implementation architecture + +All committed implementation stays under this challenge directory. + +```text +challenge-194/ +├── README.md +├── DESIGN.md +├── PLAN.md +├── pyproject.toml +├── uv.lock +├── references/ +├── src/long_range_percolation/ +│ ├── kernel.py +│ ├── union_find.py +│ ├── oracle.py +│ ├── sampler.py +│ ├── observables.py +│ ├── artifacts.py +│ └── analysis.py +├── tests/ +└── scripts/ +``` + +Python owns mathematical reference routines, orchestration, immutable +artifacts, and analysis. A Numba-compiled production kernel owns the +large-system edge process and union-find updates. A standalone C++17 backend +is considered only if a preregistered Numba performance gate fails. + +No implementation is copied from the external ONMC repository. It is a +GPL-3.0 algorithm reference for long-range sampling and is not an oracle for +the pinned model. + +## Kernel layer + +The production kernel table is computed once for each exact `(L, sigma)` and +reused across all `kappa` values. + +- `sigma = 1`: analytic `csc^2` expression using a stable `sin(pi x)` + evaluation. +- other `sigma`: symmetric Hurwitz-zeta expression. +- independent oracle: high-precision direct image summation with a bounded + tail remainder. + +Kernel artifacts include `L`, canonical binary `sigma`, implementation +version, source revision, array hash, and analytic-identity residuals. + +## Sampling layers + +### Quadratic oracle + +For every unordered pair `i < j`, compute its distance class, form +`p = -expm1(-kappa J_d)`, sample one uniform random number, and stream open +edges into union-find. This costs `O(L^2)` time and `O(L)` memory and is used +only for exact and cross-backend validation, primarily through `L = 256`. + +### Geometric-skipping sampler + +Within one distance class, closed edges before the next open edge follow a +geometric distribution because `1 - p_d = exp(-kappa J_d)`. This gives an +unbiased `O(L + E_open alpha(L))` sampler and is the first accelerated +implementation because it is simple to audit independently. + +### Poisson/Newman-Ziff sweep + +The production path couples all `kappa` values in one realization. +Associate each edge with an independent Poisson process of rate `J_d`. The +edge is open at coupling `kappa` if its first event time is at most `kappa`. + +The total event rate is + +```text +Lambda = sum_edges J_e + = L zeta(1+sigma) [1 - L^(-(1+sigma))]. +``` + +Distance classes are sampled from an alias table with weight `M_d J_d`, and +an edge within the class is selected uniformly. Events are generated in +increasing `kappa`; duplicate events on already-open edges are ignored. +Union-find is updated incrementally and observables are recorded at the +frozen `kappa` grid. + +This produces exact Bernoulli marginals for every retained coupling while +sharing work across the full coupling scan. Because couplings within one +trajectory are correlated, the complete trajectory is one resampling unit. + +### Randomness + +Use a pinned counter-based generator keyed by: + +```text +(master_seed, L, sigma_grid_id, replica, stream_id) +``` + +Uniform-to-integer conversion uses rejection rather than modulo reduction. +Thread scheduling must not alter streams. RNG version, compiler/JIT version, +floating-point mode, and seed keys are stored in every artifact. + +## Exact validation gate + +Production runs are forbidden until all of the following pass: + +1. distance-class multiplicities sum to `L(L-1)/2`; +2. general kernel agrees with high-precision image summation; +3. every `sigma = 1` kernel entry agrees with the `csc^2` identity; +4. the global kernel sum agrees with + `L zeta(1+sigma) [1 - L^(-(1+sigma))]`; +5. no-edge probability agrees with + `exp[-kappa sum_edges J_e]`; +6. open-edge mean and variance agree with the independent Bernoulli sums; +7. all graphs for `L <= 6` reproduce exact product-measure and component + probabilities; +8. quadratic, geometric-skipping, and Poisson-sweep samplers agree for + `L <= 256` on edge frequencies, bond-length histograms, component + partitions, `S1`, and `S2`; +9. `kappa = 0`, large-`kappa`, and antipodal-edge limits pass; +10. minimum-image and image-summed kernels are demonstrated to disagree at + finite `L`, preventing accidental convention substitution. + +Statistical comparisons use preregistered simultaneous tolerances rather +than requiring bitwise equality between independent samplers. + +## Observables + +### Every realization and retained coupling + +- largest and second-largest component fractions `S1/L`, `S2/L`; +- geometric cumulant + `Q_G = sum_C |C|^4 / (sum_C |C|^2)^2`; +- a pinned four-sector crossing indicator: one component intersects all four + fixed quarter-ring arcs; +- open-edge count; +- component moments needed for consistency checks. + +### Thinned measurements + +- full `S1/L` histogram; +- exact-small and logarithmic-large finite-cluster size bins; +- bond-length histogram; +- pair connectivity at registered logarithmic separations; +- finite-cluster connectivity; +- finite-cluster structure factors for modes `m = 0, ..., 8`. + +Expensive correlation measurements use a deterministic thinning schedule +bound to the replica ID. Measurement work must remain `O(L)` per selected +graph and must not silently dominate the sampler. + +## Connectivity definitions + +The translationally averaged connectivity is + +```text +G_L(r) = E[L^-1 sum_i 1{i connected to i+r}]. +``` + +For a deterministic largest-cluster tie rule, define its pair contribution +per realization and subtract it before ensemble averaging. Subtracting +`E[S1/L]^2` is not equivalent and is forbidden. + +Report separately: + +- direct decay exponent from `G(r) ~ r^(-eta_dir)`; +- Fisher convention `eta_F = eta_dir + 1` in one dimension; +- finite-cluster connected decay; +- giant-cluster plateau; +- largest-cluster fraction. + +No `eta` claim is accepted without naming the convention and subtraction. + +## Transition location + +The two primary dimensionless estimators are: + +1. pairwise `Q_G(L)` and `Q_G(2L)` crossings; +2. four-sector crossing-probability crossings. + +Their extrapolated 95% intervals must overlap. Otherwise the transition is +reported as unresolved. + +At `sigma = 1`, the complete `S1/L` distribution is additionally tested for +one versus two components. If a stable antimode exists, an equal-weight +pseudotransition and its peak separation are tracked. The maximum slope of +`S1/L` is not a primary transition estimator. + +For `sigma > 1`, only crossover drift is reported; no finite critical point +is fitted. + +## Finite-cluster scales + +An ordinary second-moment correlation length is invalid because subcritical +connectivity has an algebraic tail. + +### Primary spectral crossover + +For finite clusters, measure + +```text +F_L(k) = L^-1 E[sum_{C != C1} |sum_{x in C} exp(ikx)|^2]. +``` + +Fit registered low-momentum mode sets to + +```text +F_L(k)^-1 = a0 + a_sigma |q|^sigma + a2 q^2, +q_m = 2 sin(pi m/L), +``` + +and define `xi_sigma = (a_sigma/a0)^(1/sigma)` only when both coefficients +are positive and resolved. Otherwise store a censored bound. Mode sets +`1:4`, `1:8`, and `2:8` are registered sensitivity checks. + +This estimator must first recover known scales from synthetic propagators +with nonanalytic tails, exponential crossovers, and pure algebraic controls. + +### Secondary mass cutoff + +Fit the site-weighted finite-cluster distribution to a power law with a +registered cutoff over controlled mass windows. The resulting `s_c` is a +systematic transition-scale check. It is converted to a spatial length only +if an independently stable mass-length relation is demonstrated. + +## Parameter and sampling plan + +### Pilot + +- `sigma = 0.8, 0.9, 1.0, 1.1`; +- `L = 2^10, 2^14, 2^18`; +- geometric grid `kappa_j = 0.25 * 1.25^j` with `kappa_j <= 6`; +- separate pilot seeds; +- bracket the common change region of both transition observables; +- benchmark oracle, geometric skipping, and Poisson sweep. + +### Frozen production + +- all retained `sigma`: `L = 2^10, 2^12, 2^14, 2^16, 2^18`; +- `sigma = 1`: add intermediate powers `2^11, 2^13, 2^15, 2^17`; +- at least 12 retained near-critical couplings at `sigma = 1`; +- at least 8 independent streams per cell or trajectory batch; +- `L = 2^20` only if the registered information-gain and runtime gate passes. + +Pilot data are excluded from confirmatory likelihoods. + +Sampling stops at a cell when both dimensionless-observable standard errors +are at most `0.01` near transition and the relative spectral-scale error is +at most `8%`, or when the registered realization ceiling is reached. + +## Preregistered scaling hypotheses + +For subcritical reduced coupling + +```text +t = (kappa_c - kappa) / kappa_c, +y = log(xi), +``` + +compare exactly four primary models: + +1. fixed Cardy continuation: `y = a + A t^(-2/3)`; +2. free essential: `y = a + A t^(-nu_tilde)`; +3. algebraic: `y = a - nu log(t)`; +4. fixed `2/3` with one registered logarithmic correction. + +`kappa_c` is refit in every bootstrap replicate and constrained by the +independent transition interval. The primary model score is leave-one- +coupling-block-out predictive performance. AICc and parametric-bootstrap +goodness-of-fit are secondary diagnostics. + +Supporting fixed `2/3` requires: + +- acceptable bootstrap goodness-of-fit; +- no residual trend in coupling or size; +- predictive performance statistically competitive with the best model; +- algebraic scaling loses by more than two score standard errors; +- the free-essential interval contains `2/3`; +- the log-correction interval contains zero; +- all registered deletion tests preserve the conclusion. + +Deletion tests raise the minimum size, delete each size, alter the coupling +window by one point, delete nearest/farthest critical points, switch spectral +mode sets, switch to the secondary scale only when the independently stable +mass-length criterion above passes, and propagate the full transition-location +uncertainty. + +Failure of stability yields an inconclusive result, not a post-hoc preferred +fit. + +## Uncertainty + +Independent graph trajectories are partitioned into immutable batches. Use +a nested bootstrap that resamples seed streams, then batches within streams, +and reruns transition interpolation, scale extraction, and nonlinear fits. +Couplings from the same monotone trajectory remain grouped. + +Report percentile intervals, covariance-aware fit diagnostics, and +simultaneous bands for primary curves. Raw batches, not only aggregated +means, are retained. + +## Artifacts and publication + +Artifacts are written to challenge-specific ignored result directories: + +```text +tracks/qmc/results/frustration-free/challenge-194// +``` + +Every run stores: + +- immutable request/configuration; +- kernel hash and validation report; +- source revision and dirty-state rejection; +- environment lock hash; +- seed manifest; +- raw batch files; +- progress/completion state; +- analysis plan hash; +- derived analysis and figure hashes. + +Files are staged, validated, fsynced, and atomically renamed. Existing valid +artifacts are never silently overwritten. Resume accepts only artifacts whose +configuration, software, and dependency hashes match. + +## Failure and stopping rules + +- Stop before production if exact enumeration or sampler agreement fails. +- Stop a backend if its measured throughput misses the frozen production + budget; switch backends only through a recorded capability gate. +- Do not extend to `L = 2^20` unless the largest current sizes are censored, + competing hypotheses differ by more than one combined standard error + there, and the projected run is below eight hours. +- Stop exponent discrimination as inconclusive if fewer than eight uncensored + scale points remain, transition uncertainty dominates the exponent + interval, or candidate predictions differ by less than two combined + standard errors over the attainable range. +- Never reinterpret a drifting `sigma > 1` crossover as a transition. + +## Minimum deliverable + +Within the hackathon window, the minimum scientifically valid result is: + +1. exact and accelerated samplers passing the full validation gate; +2. a bounded `sigma = 1` transition interval from two dimensionless + observables; +3. direct evidence for or against a density jump; +4. finite-cluster scale estimates with censored points retained; +5. fixed-`2/3`, free-essential, algebraic, and log-corrected comparisons with + deletion tests; +6. coarse controls at `sigma = 0.8, 0.9, 1.1`; +7. an honest support, falsification-on-accessible-scales, or inconclusive + verdict. + +The design does not require a positive `2/3` result. Reproducible +falsification or a demonstrated identifiability limit is an accepted outcome. diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md b/tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md new file mode 100644 index 000000000..1fdfb2a4e --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/PILOT_PLAN.md @@ -0,0 +1,316 @@ +# Challenge 194 Physical Pilot Plan + +## Boundary + +Pilot P0 is a post-engine, exploratory window-selection phase. Its output may +select the deterministic P1 refinement window, but it is not confirmatory data +and authorizes no transition, critical-point, exponent, scaling, or universality +claim. Zero coupling is retained as an invariant checkpoint and is excluded +from later interpolation. + +## Frozen P0 protocol + +- Sigma order: binary64 values `0.8`, `0.9`, `1.0`, `1.1`, serialized and + reconstructed only with `float.hex()` and `float.fromhex()`. +- Length order: `2**10`, `2**14`, `2**18`. +- Replica order: integers `0..7`. +- Canonical nesting: sigma, then length, then replica, exactly 96 cells. +- Couplings: exact binary64 + `[0.0] + [0.25 * 1.25**j for j in range(15)]`, serialized as hex strings. +- Master seed: `19_420_260_729`; phase namespace: `"pilot"`. +- Sigma identity: + `pilot-p0-v1|sigma-f64=`. +- One trajectory, one existing-artifact run directory, and one immutable outer + success marker per cell. + +The canonical JSON bytes of this document are not substituted for the document +itself: `analysis_plan_sha256` is the SHA256 of the committed bytes of this +file. + +## Correctness and provenance + +Pilot construction requires the passing immutable production-v1 correctness +package approved at orchestration revision +`877ab9393f320bfe31ff74a26c3db1fb205d7ef3`: its report, exact 120-cell +validation run spec and check registry, embedded historical validation-source +revision, runtime evidence, and lock hash. The report source must exactly match +the validation run spec; it is not falsely rewritten to the approval revision. +Later orchestration commits may change the current clean revision without +changing scientific semantics. + +The checked-in canonical approval registry is +`pilot_correctness_approval.json`. It authenticates the Wuzh02 +`validation-prod-877ab93` package at +`validation-prod-877ab93/report/report.json`, with report SHA256 +`036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8`, +validation run-spec SHA256 +`5b3eea4c460e14a57aec9df606447137d787a5c66dd7e98e1dffdcf566f430e2`, +protocol SHA256 +`c7e980eeadaf8ed75e4d20cebb1e2c5d5f57a1cfc329afa7678ae586f5b7f488`, +check-registry SHA256 +`6e25ea41899544f2a9de3589beb1ee94b1f3dc505638b8f8e5164a4322b56a1d`, +and scientific-module aggregate +`457fa669da897e59b03681039db6121fde4d7be9295bb46a743c8448875b3ee9`. +The registry's canonical bytes are independently pinned in code to SHA256 +`29dc5d04fd18728ee46fffe90c70d98caa61032005974f354e2b4e0e6018a7ab`; +the registry cannot redefine its own trusted digest. No merely structurally +valid alternate report can authorize Pilot. + +The frozen scientific whitelist is: + +- `model.py`, `kernel.py`; +- `counter_rng.py`, `alias.py`, `edge_set.py`; +- `observables.py`, `production_union_find.py`; +- `trajectory.py`, `poisson_reference.py`, `poisson_sweep.py`. + +Each path is rooted at `src/long_range_percolation/`. Every file SHA256 and +their canonical aggregate are checked against the correctness run spec and the +current checkout. Any drift blocks Pilot. The current clean orchestration +revision is separately recorded. The run spec also binds the correctness +report hash, validation run-spec hash, `uv.lock`, compute-node runtime +capability, complete 96-cell RNG assignment, every request and kernel hash, +and this plan. + +## Capability waiver and resources + +The user waived Task 10's 120-second/4-GiB capability gate and Task 11 +optimization only after the correctness gate. This waiver must never be called +a pass. Its record is: + +- reason: `user-waived-after-correctness-gate`; +- benchmark status: `cancelled-without-capability-report`; +- immutable UTC build timestamp. + +Runtime capability is generated when the run spec is built and rechecked by +each worker on its compute node. Login-node capability is not presumed portable +to a compute kernel. + +The approved Wuzh02 offline runtime is the repository-root interpreter used by +successful P0 job `41506576`: +`/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194/.venv/bin/python`. +No nested solution-local `.venv` is part of the deployment contract. + +Heavy trajectories run only on clusters. The original P0 campaign used one +single-core Slurm array with tasks `1-96` and 1800 MiB per task. The versioned +P0 extension uses the exact resources frozen below. Local work is limited to +bounded tiny protocols used by private test helpers. + +The build-spec compute step and every array worker use the same influential +environment contract. Before Python starts, inherited `NUMBA_*`, `PYTHONHOME`, +`PYTHONUSERBASE`, `PYTHONPATH`, `PYTHONSTARTUP`, `PYTHONINSPECT`, +`PYTHONWARNINGS`, `PYTHONBREAKPOINT`, `PYTHONSAFEPATH`, `LD_PRELOAD`, +`LD_LIBRARY_PATH`, `LD_AUDIT`, and `LIBRARY_PATH` are removed. Then +`NUMBA_DISABLE_JIT=0`, `NUMBA_NUM_THREADS=1`, `PYTHONNOUSERSITE=1`, +`PYTHONHASHSEED=0`, `PYTHONUNBUFFERED=1`, and +`OMP_NUM_THREADS=OPENBLAS_NUM_THREADS=MKL_NUM_THREADS=NUMEXPR_NUM_THREADS=VECLIB_MAXIMUM_THREADS=1` +are pinned. The only restored `PYTHONPATH` is the absolute committed `src` +directory. Build deployment must apply that exact cleanup/pinning fragment, +using a private node-local absolute non-symlink `NUMBA_CACHE_DIR`. Each cache +leaf must be absent before launch, created exactly once with mode restricted by +`umask 077`, owned by the task user, writable, canonical, and empty immediately +after creation; pre-existing empty directories are rejected as well as +non-empty directories and symlinks. The Slurm wrapper applies these rules +independently to every worker, and the documented build-spec command must do +the same. + +Extension protocol and run-spec construction both require the explicit +absolute canonical non-symlink P0 evidence root +`${RESULTS_ROOT}/pilot-p0-739880d`. Its descriptor-bound `run_spec.json` and +`progress.json` must match the frozen P0 hashes. No construction or validation +path may infer these gitignored artifacts from the source checkout; canonical +`p0_analysis.json` remains a separate explicit input. + +The compute-node build command uses a numeric Slurm job ID and the same +single-owner creation rule (after the environment cleanup above): + +```bash +[[ "${SLURM_JOB_ID}" =~ ^[0-9]+$ ]] || exit 64 +CACHE_BASE="${SLURM_TMPDIR:?}" +[[ "${CACHE_BASE}" == /* && ! -L "${CACHE_BASE}" && + -d "${CACHE_BASE}" && -w "${CACHE_BASE}" ]] || exit 73 +[[ "$(realpath -s -- "${CACHE_BASE}")" == "$(realpath -e -- "${CACHE_BASE}")" ]] || + exit 73 +export NUMBA_CACHE_DIR="${CACHE_BASE%/}/challenge-194-pilot-build-${SLURM_JOB_ID}" +umask 077 +mkdir -- "${NUMBA_CACHE_DIR}" || exit 73 +[[ ! -L "${NUMBA_CACHE_DIR}" && -O "${NUMBA_CACHE_DIR}" && + -d "${NUMBA_CACHE_DIR}" && -w "${NUMBA_CACHE_DIR}" && + "$(realpath -e -- "${NUMBA_CACHE_DIR}")" == "${NUMBA_CACHE_DIR}" ]] || + exit 73 +shopt -s nullglob dotglob +CACHE_ENTRIES=("${NUMBA_CACHE_DIR}"/*) +shopt -u nullglob dotglob +(( ${#CACHE_ENTRIES[@]} == 0 )) || exit 73 +``` + +## Restart and publication + +Each cell owns: + +```text +cells//run/{request.json,environment.json,kernel/, + seed-manifest.json,capability.json,trajectories/,batches/, + progress.json,manifest.json} +cells//manifest.json +``` + +All run-spec paths are relative to the downloaded Pilot root. Existing success +cells are deeply verified. A complete trajectory may resume batch, progress, +and outer-marker publication. Surviving `.partial` or `.intent` files block +recovery and are never deleted. Duplicate workers serialize at the cell +directory: one publishes without clobbering; the loser succeeds only after +full verification. Different cells may be created concurrently: the shared +`cells/` descriptor remains bound to the same directory, owner, mode, device, +and inode while its legitimate child-count and timestamp metadata may change. +Immutable ancestors and each exact target cell retain strict generation +binding; shared-directory substitution and cell swap/restore still fail. + +## Frozen versioned P0 extension + +The only approved extension is `pilot-p0-extension-v1`. It is bound to design +SHA256 `5426e3007e9d83039f371ca6a9372f1868ef9d5447b66a12b1643ecf72907aba`, +P0 run-spec SHA256 +`d17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840`, +P0 progress SHA256 +`ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f`, +P0 analysis document SHA256 +`e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8`, +canonical analysis-file SHA256 +`44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b`, +bracket SHA256 +`fb3df666044bf9531443fc00c5c2c2d489512b4162864b3a92ffc2e756832403`, +and P0 revision `739880d9ccdcffbfc8a15310250349bd11d63bbb`. + +Its schemas are exactly: + +- `challenge-194-p0-extension-protocol-v1`; +- `challenge-194-p0-extension-run-spec-v1`; +- `challenge-194-p0-extension-progress-v1`; +- `challenge-194-p0-extension-analysis-v1`; +- `challenge-194-p0-combined-analysis-v2`; +- `challenge-194-p1-brackets-v2`. + +The axes are sigmas `0x1.ccccccccccccdp-1` and +`0x1.0000000000000p+0`, lengths `1024`, `16384`, `262144`, and replicas +`24..39`, in sigma/length/replica order. This gives exactly 96 cells and +trajectories, 17 checkpoints per trajectory, 1,632 checkpoints, and 102 +extension estimate rows. The master seed is `19_420_262_729`, phase is +`"pilot"`, and grid namespace is `"pilot-p0-extension-v1"`. Replica, request, +and RNG identities must be disjoint from P0 replicas `0..7` and reserved P1 +replicas `8..23`. + +The deterministic component rule recomputes the unchanged selector's original +P0 marks using lengths `16384` and `262144`, groups contiguous marked +intervals separately, chooses the lowest-coupling four-sector component and +the nearest `Q_G` component (equal gaps choose lower coupling), takes their +closed union, and adds exactly one original-P0 guard interval on each side. +Four recursive binary64 midpoint levels then produce exactly 17 ordered +points. The sigma `0.9` grid is: + +```text +0x1.f400000000000p-2, 0x1.1085a00000000p-1, +0x1.270b400000000p-1, 0x1.3d90e00000000p-1, +0x1.5416800000000p-1, 0x1.6a9c200000000p-1, +0x1.8121c00000000p-1, 0x1.97a7600000000p-1, +0x1.ae2d000000000p-1, 0x1.c4b2a00000000p-1, +0x1.db38400000000p-1, 0x1.f1bde00000000p-1, +0x1.0421c00000000p+0, 0x1.0f64900000000p+0, +0x1.1aa7600000000p+0, 0x1.25ea300000000p+0, +0x1.312d000000000p+0 +``` + +Its grid SHA256 is +`76dc7e07639ed085873a8f291cc2aaee0e8942ddac8efce3982743dd67491071`. +The sigma `1.0` grid is: + +```text +0x1.3880000000000p-1, 0x1.6092ca0000000p-1, +0x1.88a5940000000p-1, 0x1.b0b85e0000000p-1, +0x1.d8cb280000000p-1, 0x1.006ef90000000p+0, +0x1.14785e0000000p+0, 0x1.2881c30000000p+0, +0x1.3c8b280000000p+0, 0x1.50948d0000000p+0, +0x1.649df20000000p+0, 0x1.78a7570000000p+0, +0x1.8cb0bc0000000p+0, 0x1.a0ba210000000p+0, +0x1.b4c3860000000p+0, 0x1.c8cceb0000000p+0, +0x1.dcd6500000000p+0 +``` + +Its grid SHA256 is +`d40b4a2afac533d74965513513fff1870918831000b2e040063ca2a0e29ad091`. +The basic ten-column trajectory schema, scientific engine, realization and +stopping policy, correctness registry, capability waiver, and exploratory +phase are unchanged. No interpolation, uncertainty rescue, threshold change, +nearest-interval fallback, manual choice, adaptive extension, new observable, +P1 execution, or confirmatory use is permitted. + +The immutable artifact names are +`p0_extension_v1_protocol.json`, `pilot-p0-extension-v1/run_spec.json`, +`pilot-p0-extension-v1/progress.json`, `p0_extension_v1_analysis.json`, +`p0_combined_analysis_v2.json`, `p0_combined_brackets_v2.json`, and, +conditionally, `p1_protocol.json`. Canonical finite UTF-8 JSON uses sorted +keys, compact separators, one trailing newline, atomic publication, and +no-clobber verification. + +Wuzh02 execution uses `wzacnormal03`: one CPU, 1800 MiB, 40-minute wall time, +no GPU, and a private node-local Numba cache per worker. Only canonical decimal IDs +`1..96` are accepted, and they map exactly to cell indices `0..95`; signs, +leading-zero aliases, whitespace, non-digits, overflow-sized values, and all +out-of-range values fail before arithmetic. The three submission batches are +smoke `1-2%2`, light/medium `3-32,49-80%16`, and heavy `33-48,81-96%8`. + +Restart requires the identical protocol, run spec, source, runtime, request, +and RNG assignment. Completed cells are deeply verified. A published +trajectory may resume only missing batch, progress, or outer-manifest +publication. Duplicate workers serialize. Surviving `.partial` or `.intent` +files, malformed markers, substitutions, drift, or unexpected paths are +preserved and fail closed. Merge requires exactly 96 cells and trajectories. + +The six acceptance checks are: + +1. The protocol verifies against the exact P0 evidence and committed design. +2. The downloaded root verifies exactly 96 cells and 96 trajectories. +3. Extension and combined analyses pass canonical, hash, identity, source, + schema, and semantic recomputation. +4. Both sigma `0.9` and `1.0` obtain a nonzero adjacent interval marked by + both unchanged estimators. +5. Sigma `0.8` and `1.1` reproduce their exact existing transition and + crossover brackets. +6. The bracket says `requires_p0_extension=false` and an independent + recomputation is byte-identical. + +P1 remains absent unless all six acceptance checks pass. An unresolved +extension is valid and cannot trigger extra points or a relaxed selector. + +## Deterministic P1 selection and boundary + +P1 may be defined only after P0 is downloaded, verified, and aggregated into a +source-hash-bound analysis document. The frozen selector is: + +1. Use the two largest P0 sizes. +2. Mark each adjacent nonzero-coupling interval containing a sign change in + the difference of the two sizes' mean `Q_G`. +3. Independently mark an interval when either size's four-sector crossing + probability spans the closed range `[0.25, 0.75]`. +4. For sigma at most one, retain only intervals marked by both estimators, + select the narrowest interval, and break equal-width ties by lower coupling. + No common interval requires a new versioned P0 extension; it never permits + post-hoc interpolation or a fabricated bracket. +5. For sigma `1.1`, select the interval with maximum absolute finite-difference + slope of the largest-size crossing probability, breaking ties by lower + coupling. This is labeled only as crossover refinement. + +Every selected window would produce nine ordered binary64 points: the exact +endpoints and seven recursively bisected interior points. The separately +hashed P1 protocol uses master seed `19_420_261_729`, the existing `"pilot"` +phase, a new `pilot-p1-v1` grid identity, and fresh replicas `8..23`. P0 +replicas are not reused. + +P0 and P1 remain exploratory. Neither can enter confirmatory likelihoods or +authorize transition, critical-point, exponent, scaling, or universality +claims. Any later confirmatory phase must be preregistered, use a disjoint RNG +phase namespace, and use untouched data. + +The current immutable P0 analysis selects windows for sigma `0.8` and the +sigma `1.1` crossover control, but sigma `0.9` and `1.0` have no common +nonzero interval. Therefore P1 publication and execution are blocked pending +execution, download, and verification of the frozen extension above. diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/PLAN.md b/tracks/qmc/solutions/frustration-free/challenge-194/PLAN.md new file mode 100644 index 000000000..ab89573d4 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/PLAN.md @@ -0,0 +1,1386 @@ +# Challenge 194 Day-0 Validation Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use +> superpowers:subagent-driven-development (recommended) or +> superpowers:executing-plans to implement this plan task-by-task. Steps use +> checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build a reproducible, independently validated finite-ring kernel, +union-find graph representation, exact small-system oracle, and accelerated +geometric-skipping sampler for the pinned `q=1` long-range percolation model. + +**Architecture:** A small Python package separates model validation, kernel +evaluation, graph connectivity, independent quadratic sampling, exact graph +enumeration, and accelerated distance-class sampling. The accelerated sampler +is accepted only through deterministic identities and distribution-level +comparison with the independent oracle; no critical fitting or production +cluster run is included in this plan. + +**Tech Stack:** CPython 3.12, NumPy 2.2.6, SciPy 1.15.3, h5py 3.14.0, +pytest, uv. + +## Global constraints + +- Work only under + `tracks/qmc/solutions/frustration-free/challenge-194/`. +- The pinned model is + `p_ij = 1 - exp[-kappa J_L,sigma(i-j)]` with the full periodic image sum. +- `L` is an even integer at least two, `sigma > 0`, and `kappa >= 0`. +- Every unordered edge appears once; the antipodal class has `L/2` edges. +- No cutoff, Kac normalization, independent nearest-neighbor parameter, or + minimum-image substitution is allowed. +- The quadratic oracle and accelerated sampler must not share edge-selection + logic beyond immutable model/kernel inputs. +- Use `numpy.random.Generator` only for Day-0 statistical validation. The + counter-based production RNG belongs to the later production-sampler plan. +- All tests are written and observed failing before implementation. +- No generated data, virtual environment, cache, or external code is committed. +- Every commit names only files owned by its task; never use `git add .`. + +--- + +## File map + +- `pyproject.toml` — isolated Python 3.12 environment and pytest settings. +- `src/long_range_percolation/__init__.py` — public Day-0 API. +- `src/long_range_percolation/model.py` — immutable model specification, + distance classes, and canonical edges. +- `src/long_range_percolation/kernel.py` — production and high-precision + reference periodic kernels. +- `src/long_range_percolation/union_find.py` — deterministic disjoint-set + implementation and component summaries. +- `src/long_range_percolation/oracle.py` — independent `O(L^2)` Bernoulli + sampler. +- `src/long_range_percolation/enumeration.py` — exact graph probabilities for + `L <= 6`. +- `src/long_range_percolation/geometric.py` — distance-class geometric-skipping + sampler. +- `src/long_range_percolation/validation.py` — analytic and statistical + validation report. +- `scripts/validate_day0.py` — one-command Day-0 acceptance CLI. +- `tests/test_model.py` — class cardinality and edge canonicalization. +- `tests/test_kernel.py` — image sums, analytic identities, and global sums. +- `tests/test_union_find.py` — deterministic partition behavior. +- `tests/test_oracle.py` — oracle limits and edge probabilities. +- `tests/test_enumeration.py` — exact product-measure distributions. +- `tests/test_geometric.py` — accelerated sampler limits and independence. +- `tests/test_validation.py` — acceptance report and fail-closed behavior. +- `README.md` — setup, validation command, scope, and non-claims. + +--- + +### Task 1: Package boundary and pinned model + +**Files:** +- Create: `pyproject.toml` +- Create: `src/long_range_percolation/__init__.py` +- Create: `src/long_range_percolation/model.py` +- Create: `tests/test_model.py` + +**Interfaces:** +- Produces: + - `ModelSpec(length: int, sigma: float, kappa: float)` + - `distance_classes(length: int) -> tuple[DistanceClass, ...]` + - `canonical_edge(length: int, distance: int, offset: int) -> tuple[int, int]` + - `iter_unordered_edges(length: int) -> Iterator[tuple[int, int]]` +- `DistanceClass` fields are `distance: int` and `multiplicity: int`. + +- [ ] **Step 1: Write the package metadata** + +```toml +[project] +name = "challenge-194-long-range-percolation" +version = "0.1.0" +requires-python = "==3.12.*" +dependencies = [ + "h5py==3.14.0", + "numpy==2.2.6", + "scipy==1.15.3", +] + +[dependency-groups] +dev = ["pytest>=8.3,<9"] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/long_range_percolation"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "-ra" +``` + +- [ ] **Step 2: Write failing model tests** + +```python +import pytest + +from long_range_percolation.model import ( + ModelSpec, + canonical_edge, + distance_classes, + iter_unordered_edges, +) + + +def test_model_spec_rejects_non_even_or_nonphysical_parameters(): + for values in [ + {"length": 3, "sigma": 1.0, "kappa": 1.0}, + {"length": 2, "sigma": 0.0, "kappa": 1.0}, + {"length": 2, "sigma": 1.0, "kappa": -1.0}, + {"length": 2, "sigma": float("nan"), "kappa": 1.0}, + {"length": 2, "sigma": 1.0, "kappa": float("inf")}, + ]: + with pytest.raises(ValueError): + ModelSpec(**values) + + +def test_distance_classes_count_every_unordered_edge_once(): + for length in (2, 4, 6, 32): + classes = distance_classes(length) + assert sum(item.multiplicity for item in classes) == length * (length - 1) // 2 + assert classes[-1].distance == length // 2 + assert classes[-1].multiplicity == length // 2 + + +def test_canonical_edges_match_direct_unordered_enumeration(): + length = 8 + from_classes = { + canonical_edge(length, item.distance, offset) + for item in distance_classes(length) + for offset in range(item.multiplicity) + } + assert from_classes == set(iter_unordered_edges(length)) + assert len(from_classes) == length * (length - 1) // 2 +``` + +- [ ] **Step 3: Run tests and observe the missing-module failure** + +Run: + +```bash +uv sync --project . --python 3.12 +uv run --project . pytest tests/test_model.py -q +``` + +Expected: collection fails because `long_range_percolation.model` does not +exist. + +- [ ] **Step 4: Implement the immutable model and canonical edge map** + +```python +from __future__ import annotations + +from dataclasses import dataclass +import math +from typing import Iterator + + +def _strict_int(value: object, name: str) -> int: + if isinstance(value, bool) or not isinstance(value, int): + raise ValueError(f"{name} must be an integer") + return value + + +@dataclass(frozen=True) +class ModelSpec: + length: int + sigma: float + kappa: float + + def __post_init__(self) -> None: + length = _strict_int(self.length, "length") + if length < 2 or length % 2: + raise ValueError("length must be even and at least two") + if ( + isinstance(self.sigma, bool) + or not isinstance(self.sigma, (int, float)) + or not math.isfinite(float(self.sigma)) + or float(self.sigma) <= 0.0 + ): + raise ValueError("sigma must be finite and positive") + if ( + isinstance(self.kappa, bool) + or not isinstance(self.kappa, (int, float)) + or not math.isfinite(float(self.kappa)) + or float(self.kappa) < 0.0 + ): + raise ValueError("kappa must be finite and nonnegative") + + +@dataclass(frozen=True) +class DistanceClass: + distance: int + multiplicity: int + + +def distance_classes(length: int) -> tuple[DistanceClass, ...]: + length = _strict_int(length, "length") + if length < 2 or length % 2: + raise ValueError("length must be even and at least two") + return tuple( + DistanceClass( + distance=distance, + multiplicity=length if distance < length // 2 else length // 2, + ) + for distance in range(1, length // 2 + 1) + ) + + +def canonical_edge(length: int, distance: int, offset: int) -> tuple[int, int]: + matching = {item.distance: item for item in distance_classes(length)} + if distance not in matching: + raise ValueError("distance is outside the canonical range") + if isinstance(offset, bool) or not isinstance(offset, int): + raise ValueError("offset must be an integer") + if not 0 <= offset < matching[distance].multiplicity: + raise ValueError("offset is outside the distance class") + left = offset + right = (offset + distance) % length + return (left, right) if left < right else (right, left) + + +def iter_unordered_edges(length: int) -> Iterator[tuple[int, int]]: + distance_classes(length) + for left in range(length): + for right in range(left + 1, length): + yield left, right +``` + +Export these symbols from `src/long_range_percolation/__init__.py`. + +- [ ] **Step 5: Run the model tests** + +Run: + +```bash +uv run --project . pytest tests/test_model.py -q +``` + +Expected: `3 passed`. + +- [ ] **Step 6: Commit** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/pyproject.toml \ + tracks/qmc/solutions/frustration-free/challenge-194/uv.lock \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/__init__.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/model.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_model.py +GIT_AUTHOR_NAME=Codex GIT_AUTHOR_EMAIL=codex@local \ +GIT_COMMITTER_NAME=Codex GIT_COMMITTER_EMAIL=codex@local \ +git commit -m "Add pinned Challenge 194 model" +``` + +--- + +### Task 2: Periodic image-summed kernel + +**Files:** +- Create: `src/long_range_percolation/kernel.py` +- Create: `tests/test_kernel.py` +- Modify: `src/long_range_percolation/__init__.py` + +**Interfaces:** +- Consumes: `ModelSpec`, `distance_classes`. +- Produces: + - `periodic_kernel(length: int, sigma: float) -> np.ndarray` + - `periodic_kernel_reference(length: int, sigma: float, images: int) -> tuple[np.ndarray, np.ndarray]` + - `kernel_weight_sum(length: int, sigma: float) -> float` + - `edge_probabilities(spec: ModelSpec, kernel: np.ndarray) -> np.ndarray` +- The reference function returns `(partial_sum, rigorous_tail_bound)` entrywise. + +- [ ] **Step 1: Write failing analytic and reference tests** + +```python +import numpy as np +import pytest +from scipy.special import zeta + +from long_range_percolation.kernel import ( + edge_probabilities, + kernel_weight_sum, + periodic_kernel, + periodic_kernel_reference, +) +from long_range_percolation.model import ModelSpec, distance_classes + + +def test_sigma_one_kernel_matches_cosecant_identity(): + for length in (4, 6, 32, 256): + distances = np.arange(1, length // 2 + 1, dtype=np.float64) + expected = (np.pi / length) ** 2 / np.sin(np.pi * distances / length) ** 2 + np.testing.assert_allclose( + periodic_kernel(length, 1.0), + expected, + rtol=2e-14, + atol=2e-14, + ) + + +def test_hurwitz_kernel_is_enclosed_by_direct_image_sum(): + values = periodic_kernel(12, 0.8) + partial, bound = periodic_kernel_reference(12, 0.8, images=100_000) + assert np.all(np.abs(values - partial) <= bound) + + +def test_reference_error_and_bound_shrink_with_more_images(): + values = periodic_kernel(12, 0.8) + coarse, coarse_bound = periodic_kernel_reference(12, 0.8, images=100) + fine, fine_bound = periodic_kernel_reference(12, 0.8, images=200) + assert np.all(np.abs(values - fine) < np.abs(values - coarse)) + assert np.all(fine_bound < coarse_bound) + + +def test_global_kernel_sum_identity(): + for length, sigma in [(4, 0.8), (12, 1.0), (32, 1.1)]: + values = periodic_kernel(length, sigma) + measured = sum( + item.multiplicity * values[item.distance - 1] + for item in distance_classes(length) + ) + expected = length * zeta(1.0 + sigma, 1.0) * ( + 1.0 - length ** (-(1.0 + sigma)) + ) + assert measured == pytest.approx(expected, rel=2e-13) + + +def test_edge_probabilities_use_stable_exponential_form(): + spec = ModelSpec(length=4, sigma=1.0, kappa=1e-16) + probability = edge_probabilities(spec, periodic_kernel(4, 1.0))[0] + assert probability > 0.0 + assert probability == pytest.approx( + spec.kappa * periodic_kernel(4, 1.0)[0], + rel=1e-15, + ) +``` + +- [ ] **Step 2: Run tests and observe missing functions** + +Run: + +```bash +uv run --project . pytest tests/test_kernel.py -q +``` + +Expected: collection fails because `kernel.py` does not exist. + +- [ ] **Step 3: Implement production and reference kernels** + +```python +from __future__ import annotations + +import math + +import numpy as np +from scipy.special import zeta + +from .model import ModelSpec + + +def periodic_kernel(length: int, sigma: float) -> np.ndarray: + ModelSpec(length=length, sigma=sigma, kappa=0.0) + distances = np.arange(1, length // 2 + 1, dtype=np.float64) + if float(sigma) == 1.0: + angles = np.pi * distances / length + return (np.pi / length) ** 2 / np.sin(angles) ** 2 + exponent = 1.0 + float(sigma) + fraction = distances / length + return length ** (-exponent) * ( + zeta(exponent, fraction) + zeta(exponent, 1.0 - fraction) + ) + + +def periodic_kernel_reference( + length: int, + sigma: float, + images: int, +) -> tuple[np.ndarray, np.ndarray]: + ModelSpec(length=length, sigma=sigma, kappa=0.0) + if isinstance(images, bool) or not isinstance(images, int) or images < 1: + raise ValueError("images must be a positive integer") + exponent = 1.0 + float(sigma) + distances = np.arange(1, length // 2 + 1, dtype=np.float64) + partial = np.zeros_like(distances) + for image in range(-images, images + 1): + displacement = np.abs(distances + image * length) + partial += displacement ** (-exponent) + half_index = images + 0.5 + tail = np.full_like( + distances, + 2.0 * length ** (-exponent) * ( + half_index ** (-exponent) + + half_index ** (1.0 - exponent) / (exponent - 1.0) + ), + ) + return partial, tail + + +def kernel_weight_sum(length: int, sigma: float) -> float: + ModelSpec(length=length, sigma=sigma, kappa=0.0) + exponent = 1.0 + float(sigma) + return float( + length * zeta(exponent, 1.0) * (1.0 - length ** (-exponent)) + ) + + +def edge_probabilities(spec: ModelSpec, kernel: np.ndarray) -> np.ndarray: + values = np.asarray(kernel, dtype=np.float64) + if values.shape != (spec.length // 2,): + raise ValueError("kernel shape does not match model length") + if not np.all(np.isfinite(values)) or np.any(values <= 0.0): + raise ValueError("kernel must contain finite positive values") + return -np.expm1(-spec.kappa * values) +``` + +The tail bound uses both omitted half-lines and the monotone-series +sum-versus-integral bound. Add a test that doubles `images` and requires both +the partial-sum error and the stated bound to decrease. + +- [ ] **Step 4: Run kernel tests** + +Run: + +```bash +uv run --project . pytest tests/test_kernel.py -q +``` + +Expected: `5 passed`. + +- [ ] **Step 5: Commit** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/__init__.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/kernel.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_kernel.py +GIT_AUTHOR_NAME=Codex GIT_AUTHOR_EMAIL=codex@local \ +GIT_COMMITTER_NAME=Codex GIT_COMMITTER_EMAIL=codex@local \ +git commit -m "Implement periodic long-range kernel" +``` + +--- + +### Task 3: Deterministic union-find and graph sample type + +**Files:** +- Create: `src/long_range_percolation/union_find.py` +- Create: `src/long_range_percolation/sample.py` +- Create: `tests/test_union_find.py` +- Modify: `src/long_range_percolation/__init__.py` + +**Interfaces:** +- Produces: + - `UnionFind(length: int)` + - `UnionFind.union(left: int, right: int) -> bool` + - `UnionFind.labels() -> np.ndarray` + - `UnionFind.component_sizes() -> np.ndarray` + - `GraphSample(length: int, edges: np.ndarray, labels: np.ndarray)` +- `GraphSample.edges` has shape `(n_edges, 2)`, dtype `int64`, and canonical + sorted endpoints. + +- [ ] **Step 1: Write failing partition and validation tests** + +```python +import numpy as np +import pytest + +from long_range_percolation.sample import GraphSample +from long_range_percolation.union_find import UnionFind + + +def test_union_find_returns_deterministic_labels_and_sizes(): + union_find = UnionFind(6) + for left, right in [(4, 5), (1, 2), (0, 2), (3, 5)]: + union_find.union(left, right) + np.testing.assert_array_equal(union_find.labels(), [0, 0, 0, 3, 3, 3]) + np.testing.assert_array_equal(union_find.component_sizes(), [3, 3]) + + +def test_graph_sample_rejects_duplicate_or_noncanonical_edges(): + labels = np.arange(4) + with pytest.raises(ValueError, match="canonical"): + GraphSample(4, np.array([[2, 1]]), labels) + with pytest.raises(ValueError, match="duplicate"): + GraphSample(4, np.array([[0, 1], [0, 1]]), labels) +``` + +- [ ] **Step 2: Run tests and observe missing modules** + +Run: + +```bash +uv run --project . pytest tests/test_union_find.py -q +``` + +Expected: collection fails because `union_find.py` and `sample.py` do not +exist. + +- [ ] **Step 3: Implement union-by-size with deterministic tie-breaking** + +```python +from __future__ import annotations + +import numpy as np + + +class UnionFind: + def __init__(self, length: int): + if isinstance(length, bool) or not isinstance(length, int) or length < 1: + raise ValueError("length must be a positive integer") + self.parent = np.arange(length, dtype=np.int64) + self.size = np.ones(length, dtype=np.int64) + + def find(self, node: int) -> int: + if isinstance(node, bool) or not isinstance(node, int): + raise ValueError("node must be an integer") + if not 0 <= node < self.parent.size: + raise ValueError("node is out of range") + root = node + while self.parent[root] != root: + root = int(self.parent[root]) + while self.parent[node] != node: + parent = int(self.parent[node]) + self.parent[node] = root + node = parent + return root + + def union(self, left: int, right: int) -> bool: + root_left = self.find(left) + root_right = self.find(right) + if root_left == root_right: + return False + if ( + self.size[root_left] < self.size[root_right] + or ( + self.size[root_left] == self.size[root_right] + and root_left > root_right + ) + ): + root_left, root_right = root_right, root_left + self.parent[root_right] = root_left + self.size[root_left] += self.size[root_right] + return True + + def labels(self) -> np.ndarray: + roots = np.array([self.find(i) for i in range(self.parent.size)]) + minimum = {} + for node, root in enumerate(roots.tolist()): + minimum[root] = min(node, minimum.get(root, node)) + return np.array([minimum[int(root)] for root in roots], dtype=np.int64) + + def component_sizes(self) -> np.ndarray: + _, counts = np.unique(self.labels(), return_counts=True) + return np.sort(counts.astype(np.int64))[::-1] +``` + +Implement `GraphSample` as a frozen dataclass: + +```python +from __future__ import annotations + +from dataclasses import dataclass + +import numpy as np + +from .union_find import UnionFind + + +@dataclass(frozen=True) +class GraphSample: + length: int + edges: np.ndarray + labels: np.ndarray + + def __post_init__(self) -> None: + if ( + isinstance(self.length, bool) + or not isinstance(self.length, int) + or self.length < 1 + ): + raise ValueError("length must be a positive integer") + edges = np.array(self.edges, dtype=np.int64, copy=True) + labels = np.array(self.labels, dtype=np.int64, copy=True) + if edges.ndim != 2 or edges.shape[1:] != (2,): + raise ValueError("edges must have shape (n_edges, 2)") + if labels.shape != (self.length,): + raise ValueError("labels must have shape (length,)") + if edges.size and ( + np.any(edges < 0) or np.any(edges >= self.length) + ): + raise ValueError("edge endpoint is out of range") + if edges.size and np.any(edges[:, 0] >= edges[:, 1]): + raise ValueError("edges must have canonical increasing endpoints") + edge_tuples = [tuple(edge) for edge in edges.tolist()] + if edge_tuples != sorted(edge_tuples): + raise ValueError("edges must be sorted") + if len(edge_tuples) != len(set(edge_tuples)): + raise ValueError("duplicate edges are forbidden") + union_find = UnionFind(self.length) + for left, right in edge_tuples: + union_find.union(left, right) + if not np.array_equal(labels, union_find.labels()): + raise ValueError("labels do not match the edge-induced partition") + edges.setflags(write=False) + labels.setflags(write=False) + object.__setattr__(self, "edges", edges) + object.__setattr__(self, "labels", labels) +``` + +- [ ] **Step 4: Run union-find tests** + +Run: + +```bash +uv run --project . pytest tests/test_union_find.py -q +``` + +Expected: `2 passed`. + +- [ ] **Step 5: Commit** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/__init__.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/sample.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/union_find.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_union_find.py +GIT_AUTHOR_NAME=Codex GIT_AUTHOR_EMAIL=codex@local \ +GIT_COMMITTER_NAME=Codex GIT_COMMITTER_EMAIL=codex@local \ +git commit -m "Add deterministic percolation components" +``` + +--- + +### Task 4: Independent quadratic Bernoulli oracle + +**Files:** +- Create: `src/long_range_percolation/oracle.py` +- Create: `tests/test_oracle.py` +- Modify: `src/long_range_percolation/__init__.py` + +**Interfaces:** +- Consumes: `ModelSpec`, `periodic_kernel`, `UnionFind`, `GraphSample`. +- Produces: + - `sample_quadratic(spec: ModelSpec, rng: np.random.Generator) -> GraphSample` + - `expected_open_edges(spec: ModelSpec) -> float` + - `variance_open_edges(spec: ModelSpec) -> float` + - `no_edge_probability(spec: ModelSpec) -> float` + +- [ ] **Step 1: Write failing deterministic-limit and moment tests** + +```python +import numpy as np +import pytest + +from long_range_percolation.model import ModelSpec +from long_range_percolation.oracle import ( + expected_open_edges, + no_edge_probability, + sample_quadratic, + variance_open_edges, +) + + +def test_quadratic_oracle_exact_limits(): + empty = sample_quadratic( + ModelSpec(8, 1.0, 0.0), + np.random.default_rng(1), + ) + assert empty.edges.shape == (0, 2) + np.testing.assert_array_equal(empty.labels, np.arange(8)) + + full = sample_quadratic( + ModelSpec(8, 1.0, 1e6), + np.random.default_rng(1), + ) + assert full.edges.shape == (28, 2) + np.testing.assert_array_equal(full.labels, np.zeros(8, dtype=np.int64)) + + +def test_oracle_edge_count_matches_analytic_moments(): + spec = ModelSpec(8, 0.9, 0.7) + counts = np.array( + [ + sample_quadratic(spec, np.random.default_rng(seed)).edges.shape[0] + for seed in range(30_000) + ] + ) + assert counts.mean() == pytest.approx( + expected_open_edges(spec), + abs=5.0 * np.sqrt(variance_open_edges(spec) / counts.size), + ) + assert counts.var(ddof=1) == pytest.approx( + variance_open_edges(spec), + rel=0.05, + ) + + +def test_no_edge_probability_uses_total_kernel_weight(): + spec = ModelSpec(6, 1.0, 0.4) + observed = np.mean( + [ + sample_quadratic(spec, np.random.default_rng(seed)).edges.size == 0 + for seed in range(40_000) + ] + ) + assert observed == pytest.approx(no_edge_probability(spec), abs=0.01) +``` + +- [ ] **Step 2: Run tests and observe missing functions** + +Run: + +```bash +uv run --project . pytest tests/test_oracle.py -q +``` + +Expected: collection fails because `oracle.py` does not exist. + +- [ ] **Step 3: Implement the independently structured oracle** + +```python +from __future__ import annotations + +import math + +import numpy as np + +from .kernel import edge_probabilities, kernel_weight_sum, periodic_kernel +from .model import ModelSpec +from .sample import GraphSample +from .union_find import UnionFind + + +def _distance(left: int, right: int, length: int) -> int: + separation = right - left + return min(separation, length - separation) + + +def sample_quadratic( + spec: ModelSpec, + rng: np.random.Generator, +) -> GraphSample: + if not isinstance(rng, np.random.Generator): + raise ValueError("rng must be numpy.random.Generator") + probabilities = edge_probabilities( + spec, + periodic_kernel(spec.length, spec.sigma), + ) + union_find = UnionFind(spec.length) + edges = [] + for left in range(spec.length): + for right in range(left + 1, spec.length): + probability = probabilities[_distance(left, right, spec.length) - 1] + if rng.random() < probability: + edges.append((left, right)) + union_find.union(left, right) + edge_array = np.asarray(edges, dtype=np.int64).reshape(-1, 2) + return GraphSample(spec.length, edge_array, union_find.labels()) + + +def _class_probabilities(spec: ModelSpec) -> tuple[np.ndarray, np.ndarray]: + from .model import distance_classes + + multiplicity = np.array( + [item.multiplicity for item in distance_classes(spec.length)], + dtype=np.float64, + ) + probability = edge_probabilities( + spec, + periodic_kernel(spec.length, spec.sigma), + ) + return multiplicity, probability + + +def expected_open_edges(spec: ModelSpec) -> float: + multiplicity, probability = _class_probabilities(spec) + return float(multiplicity @ probability) + + +def variance_open_edges(spec: ModelSpec) -> float: + multiplicity, probability = _class_probabilities(spec) + return float(multiplicity @ (probability * (1.0 - probability))) + + +def no_edge_probability(spec: ModelSpec) -> float: + return math.exp(-spec.kappa * kernel_weight_sum(spec.length, spec.sigma)) +``` + +- [ ] **Step 4: Run oracle tests** + +Run: + +```bash +uv run --project . pytest tests/test_oracle.py -q +``` + +Expected: `3 passed`. + +- [ ] **Step 5: Commit** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/__init__.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/oracle.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_oracle.py +GIT_AUTHOR_NAME=Codex GIT_AUTHOR_EMAIL=codex@local \ +GIT_COMMITTER_NAME=Codex GIT_COMMITTER_EMAIL=codex@local \ +git commit -m "Add independent quadratic graph oracle" +``` + +--- + +### Task 5: Exact graph enumeration through `L = 6` + +**Files:** +- Create: `src/long_range_percolation/enumeration.py` +- Create: `tests/test_enumeration.py` +- Modify: `src/long_range_percolation/__init__.py` + +**Interfaces:** +- Produces: + - `GraphOutcome(mask: int, probability: float, open_edges: int, component_sizes: tuple[int, ...])` + - `enumerate_graphs(spec: ModelSpec) -> Iterator[GraphOutcome]` + - `exact_partition_distribution(spec: ModelSpec) -> dict[tuple[int, ...], float]` +- Enumeration rejects `L > 6`. + +- [ ] **Step 1: Write failing normalization and known-case tests** + +```python +import math + +import pytest + +from long_range_percolation.enumeration import ( + enumerate_graphs, + exact_partition_distribution, +) +from long_range_percolation.model import ModelSpec +from long_range_percolation.oracle import ( + expected_open_edges, + no_edge_probability, +) + + +def test_all_graph_probabilities_normalize_and_reproduce_analytic_moments(): + for length in (2, 4, 6): + spec = ModelSpec(length, 0.9, 0.6) + outcomes = list(enumerate_graphs(spec)) + assert len(outcomes) == 2 ** (length * (length - 1) // 2) + assert math.fsum(item.probability for item in outcomes) == pytest.approx(1.0) + assert math.fsum( + item.probability * item.open_edges for item in outcomes + ) == pytest.approx(expected_open_edges(spec)) + assert outcomes[0].probability == pytest.approx(no_edge_probability(spec)) + + +def test_two_site_partition_probabilities_are_exact(): + spec = ModelSpec(2, 1.0, 0.3) + distribution = exact_partition_distribution(spec) + closed = no_edge_probability(spec) + assert distribution[(1, 1)] == pytest.approx(closed) + assert distribution[(2,)] == pytest.approx(1.0 - closed) + + +def test_enumeration_rejects_lengths_above_six(): + with pytest.raises(ValueError, match="at most six"): + list(enumerate_graphs(ModelSpec(8, 1.0, 1.0))) + + +def test_zero_coupling_assigns_unit_mass_to_empty_graph(): + outcomes = list(enumerate_graphs(ModelSpec(4, 1.0, 0.0))) + assert outcomes[0].probability == 1.0 + assert all(item.probability == 0.0 for item in outcomes[1:]) +``` + +- [ ] **Step 2: Run tests and observe missing module** + +Run: + +```bash +uv run --project . pytest tests/test_enumeration.py -q +``` + +Expected: collection fails because `enumeration.py` does not exist. + +- [ ] **Step 3: Implement mask enumeration using independent edge probabilities** + +```python +from __future__ import annotations + +from dataclasses import dataclass +import math +from typing import Iterator + +from .kernel import edge_probabilities, periodic_kernel +from .model import ModelSpec, iter_unordered_edges +from .union_find import UnionFind + + +@dataclass(frozen=True) +class GraphOutcome: + mask: int + probability: float + open_edges: int + component_sizes: tuple[int, ...] + + +def enumerate_graphs(spec: ModelSpec) -> Iterator[GraphOutcome]: + if spec.length > 6: + raise ValueError("exact enumeration supports length at most six") + probabilities = edge_probabilities( + spec, + periodic_kernel(spec.length, spec.sigma), + ) + edges = list(iter_unordered_edges(spec.length)) + if spec.kappa == 0.0: + for mask in range(1 << len(edges)): + yield GraphOutcome( + mask=mask, + probability=1.0 if mask == 0 else 0.0, + open_edges=0 if mask == 0 else mask.bit_count(), + component_sizes=( + tuple([1] * spec.length) + if mask == 0 + else _component_sizes_for_mask(spec.length, edges, mask) + ), + ) + return + for mask in range(1 << len(edges)): + union_find = UnionFind(spec.length) + log_probability = 0.0 + open_count = 0 + for index, (left, right) in enumerate(edges): + separation = right - left + distance = min(separation, spec.length - separation) + probability = float(probabilities[distance - 1]) + if mask & (1 << index): + log_probability += math.log(probability) + open_count += 1 + union_find.union(left, right) + else: + log_probability += math.log1p(-probability) + yield GraphOutcome( + mask=mask, + probability=math.exp(log_probability), + open_edges=open_count, + component_sizes=tuple(union_find.component_sizes().tolist()), + ) + + +def exact_partition_distribution( + spec: ModelSpec, +) -> dict[tuple[int, ...], float]: + result: dict[tuple[int, ...], float] = {} + for outcome in enumerate_graphs(spec): + result[outcome.component_sizes] = ( + result.get(outcome.component_sizes, 0.0) + outcome.probability + ) + return result +``` + +Add this helper above `enumerate_graphs`: + +```python +def _component_sizes_for_mask( + length: int, + edges: list[tuple[int, int]], + mask: int, +) -> tuple[int, ...]: + union_find = UnionFind(length) + for index, (left, right) in enumerate(edges): + if mask & (1 << index): + union_find.union(left, right) + return tuple(union_find.component_sizes().tolist()) +``` + +- [ ] **Step 4: Run enumeration tests** + +Run: + +```bash +uv run --project . pytest tests/test_enumeration.py -q +``` + +Expected: `4 passed`, including the added `kappa = 0` test. + +- [ ] **Step 5: Commit** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/__init__.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/enumeration.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_enumeration.py +GIT_AUTHOR_NAME=Codex GIT_AUTHOR_EMAIL=codex@local \ +GIT_COMMITTER_NAME=Codex GIT_COMMITTER_EMAIL=codex@local \ +git commit -m "Add exact small-graph product oracle" +``` + +--- + +### Task 6: Geometric-skipping accelerated sampler + +**Files:** +- Create: `src/long_range_percolation/geometric.py` +- Create: `tests/test_geometric.py` +- Modify: `src/long_range_percolation/__init__.py` + +**Interfaces:** +- Consumes: `ModelSpec`, `distance_classes`, `canonical_edge`, + `periodic_kernel`, `UnionFind`, `GraphSample`. +- Produces: + - `sample_geometric(spec: ModelSpec, rng: np.random.Generator) -> GraphSample` +- The implementation does not call `sample_quadratic` or iterate all + unordered pairs. + +- [ ] **Step 1: Write failing structure and limit tests** + +```python +import inspect + +import numpy as np +import pytest + +import long_range_percolation.geometric as geometric_module +from long_range_percolation.geometric import sample_geometric +from long_range_percolation.model import ModelSpec + + +def test_geometric_sampler_does_not_call_quadratic_oracle(): + source = inspect.getsource(geometric_module) + assert "sample_quadratic" not in source + assert "iter_unordered_edges" not in source + + +def test_geometric_sampler_exact_limits_and_antipodal_uniqueness(): + empty = sample_geometric( + ModelSpec(8, 1.0, 0.0), + np.random.default_rng(4), + ) + assert empty.edges.shape == (0, 2) + + full = sample_geometric( + ModelSpec(8, 1.0, 1e6), + np.random.default_rng(4), + ) + assert full.edges.shape == (28, 2) + antipodal = [ + edge for edge in full.edges.tolist() + if min((edge[1] - edge[0]) % 8, (edge[0] - edge[1]) % 8) == 4 + ] + assert len(antipodal) == 4 + + +def test_geometric_sampler_is_seed_reproducible(): + spec = ModelSpec(32, 0.8, 0.7) + first = sample_geometric(spec, np.random.default_rng(20260729)) + second = sample_geometric(spec, np.random.default_rng(20260729)) + np.testing.assert_array_equal(first.edges, second.edges) + np.testing.assert_array_equal(first.labels, second.labels) + + +def test_geometric_sampler_rejects_unregistered_rng_objects(): + with pytest.raises(ValueError, match="numpy.random.Generator"): + sample_geometric(ModelSpec(8, 1.0, 0.7), object()) +``` + +- [ ] **Step 2: Run tests and observe missing module** + +Run: + +```bash +uv run --project . pytest tests/test_geometric.py -q +``` + +Expected: collection fails because `geometric.py` does not exist. + +- [ ] **Step 3: Implement independent distance-class skipping** + +```python +from __future__ import annotations + +import math + +import numpy as np + +from .kernel import periodic_kernel +from .model import ModelSpec, canonical_edge, distance_classes +from .sample import GraphSample +from .union_find import UnionFind + + +def sample_geometric( + spec: ModelSpec, + rng: np.random.Generator, +) -> GraphSample: + if not isinstance(rng, np.random.Generator): + raise ValueError("rng must be numpy.random.Generator") + if spec.kappa == 0.0: + return GraphSample( + spec.length, + np.empty((0, 2), dtype=np.int64), + np.arange(spec.length, dtype=np.int64), + ) + rates = spec.kappa * periodic_kernel(spec.length, spec.sigma) + union_find = UnionFind(spec.length) + edges: list[tuple[int, int]] = [] + for item in distance_classes(spec.length): + rate = float(rates[item.distance - 1]) + offset = 0 + while offset < item.multiplicity: + uniform = 1.0 - rng.random() + skipped = int(math.floor(-math.log(uniform) / rate)) + offset += skipped + if offset >= item.multiplicity: + break + edge = canonical_edge(spec.length, item.distance, offset) + edges.append(edge) + union_find.union(*edge) + offset += 1 + edge_array = np.asarray(sorted(edges), dtype=np.int64).reshape(-1, 2) + return GraphSample(spec.length, edge_array, union_find.labels()) +``` + +- [ ] **Step 4: Run geometric tests** + +Run: + +```bash +uv run --project . pytest tests/test_geometric.py -q +``` + +Expected: `4 passed`, including rejection of unregistered RNG objects. + +- [ ] **Step 5: Commit** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/__init__.py \ + tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/geometric.py \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_geometric.py +GIT_AUTHOR_NAME=Codex GIT_AUTHOR_EMAIL=codex@local \ +GIT_COMMITTER_NAME=Codex GIT_COMMITTER_EMAIL=codex@local \ +git commit -m "Add accelerated distance-class sampler" +``` + +--- + +### Task 7: Independent sampler acceptance and Day-0 documentation + +**Files:** +- Create: `tests/test_day0_acceptance.py` +- Create: `README.md` + +**Interfaces:** +- Consumes all prior Day-0 interfaces. +- Produces no reusable production API. This task is an acceptance gate. + +- [ ] **Step 1: Write the independent statistical acceptance tests** + +```python +from collections import Counter + +import numpy as np +import pytest +from scipy.stats import binomtest + +from long_range_percolation.enumeration import exact_partition_distribution +from long_range_percolation.geometric import sample_geometric +from long_range_percolation.kernel import ( + edge_probabilities, + periodic_kernel, +) +from long_range_percolation.model import ModelSpec, distance_classes +from long_range_percolation.oracle import sample_quadratic + + +def _edge_distance(edge: tuple[int, int], length: int) -> int: + separation = edge[1] - edge[0] + return min(separation, length - separation) + + +def _distance_open_counts(samples, length: int) -> Counter[int]: + counts: Counter[int] = Counter() + for sample in samples: + counts.update( + _edge_distance(tuple(edge), length) + for edge in sample.edges.tolist() + ) + return counts + + +@pytest.mark.parametrize("length", [4, 8, 32]) +def test_geometric_distance_frequencies_match_exact_probabilities(length): + spec = ModelSpec(length, 1.0, 0.7) + n_samples = 20_000 + samples = [ + sample_geometric(spec, np.random.default_rng(100_000 + index)) + for index in range(n_samples) + ] + counts = _distance_open_counts(samples, length) + probabilities = edge_probabilities( + spec, + periodic_kernel(length, spec.sigma), + ) + classes = distance_classes(length) + alpha = 0.001 / len(classes) + for item in classes: + trials = n_samples * item.multiplicity + result = binomtest( + counts[item.distance], + trials, + probabilities[item.distance - 1], + ) + assert result.pvalue > alpha + + +@pytest.mark.parametrize("length", [4, 6]) +def test_oracle_and_geometric_partition_histograms_match_exact_distribution( + length, +): + spec = ModelSpec(length, 0.9, 0.6) + exact = exact_partition_distribution(spec) + n_samples = 40_000 + for sampler, seed_offset in [ + (sample_quadratic, 0), + (sample_geometric, 1_000_000), + ]: + observed: Counter[tuple[int, ...]] = Counter() + for index in range(n_samples): + sample = sampler( + spec, + np.random.default_rng(seed_offset + index), + ) + _, counts = np.unique(sample.labels, return_counts=True) + observed[tuple(sorted(counts.tolist(), reverse=True))] += 1 + for partition, probability in exact.items(): + standard_error = np.sqrt( + max(probability * (1.0 - probability), 1e-12) / n_samples + ) + assert observed[partition] / n_samples == pytest.approx( + probability, + abs=6.0 * standard_error + 1.0 / n_samples, + ) + + +def test_accelerated_and_quadratic_samples_use_independent_seed_streams(): + spec = ModelSpec(32, 1.0, 0.7) + quadratic = sample_quadratic(spec, np.random.default_rng(11)) + geometric = sample_geometric(spec, np.random.default_rng(12)) + assert not np.array_equal(quadratic.edges, geometric.edges) +``` + +- [ ] **Step 2: Run the acceptance tests** + +Run: + +```bash +uv run --project . pytest tests/test_day0_acceptance.py -q +``` + +Expected: all six parameterized acceptance cases pass. + +- [ ] **Step 3: Write the challenge README** + +Create `README.md` with the following complete sections: + +```markdown +# Challenge 194: long-range q=1 random-cluster model + +This directory implements the pinned independent-edge finite-ring model from +QuantumBFS/quantum.harness issue #194. It does not use Gori et al.'s +minimum-image `C/r^(1+sigma)` convention. + +## Scope + +The current Day-0 milestone validates the periodic kernel, canonical edge +classes, deterministic union-find, exact graph enumeration through `L=6`, +an independent quadratic Bernoulli oracle, and a geometric-skipping sampler. +It makes no transition or critical-exponent claim. + +## Setup + +```bash +uv sync \ + --project tracks/qmc/solutions/frustration-free/challenge-194 \ + --python 3.12 +``` + +## Verify + +```bash +uv run \ + --project tracks/qmc/solutions/frustration-free/challenge-194 \ + pytest -q +``` + +The accelerated sampler is accepted only when it agrees with analytic edge +probabilities and exact small-system partition distributions. Generated +production data do not exist at this milestone. + +## Design and references + +- `DESIGN.md` pins the scientific and statistical protocol. +- `PLAN.md` records the test-driven implementation sequence. +- `references/README.md` records source URLs and SHA256 hashes. +``` + +- [ ] **Step 4: Run the complete Day-0 suite** + +Run from the challenge directory: + +```bash +uv run --project . pytest -q +``` + +Expected: all tests pass with no failures. + +- [ ] **Step 5: Verify repository hygiene** + +Run: + +```bash +git status --short +git diff --check +git check-ignore \ + tracks/qmc/results/frustration-free/challenge-194/ +``` + +Expected: only owned source/test/documentation changes are visible; the +challenge result directory is ignored; `git diff --check` exits zero. + +- [ ] **Step 6: Commit** + +```bash +git add \ + tracks/qmc/solutions/frustration-free/challenge-194/README.md \ + tracks/qmc/solutions/frustration-free/challenge-194/tests/test_day0_acceptance.py +GIT_AUTHOR_NAME=Codex GIT_AUTHOR_EMAIL=codex@local \ +GIT_COMMITTER_NAME=Codex GIT_COMMITTER_EMAIL=codex@local \ +git commit -m "Validate Challenge 194 graph generation" +``` + +--- + +## Day-0 completion gate + +The subproject is complete only when: + +1. the full local test suite passes from a fresh `uv sync`; +2. analytic kernel, edge-count, and exact-enumeration gates pass; +3. independent distance-frequency and partition-distribution acceptance tests + pass for both samplers; +4. the challenge result directory is ignored by Git; +5. the working tree is clean after the final commit; +6. an independent reviewer confirms that oracle and accelerated samplers do + not share edge-selection logic. + +After this gate, write and review a separate production-sampler plan covering +the Poisson/Newman-Ziff sweep, counter-based RNG, compiled performance path, +incremental observables, immutable raw batches, and local-versus-cluster +resource calibration. diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/PRODUCTION_DESIGN.md b/tracks/qmc/solutions/frustration-free/challenge-194/PRODUCTION_DESIGN.md new file mode 100644 index 000000000..49bf5cdb1 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/PRODUCTION_DESIGN.md @@ -0,0 +1,368 @@ +# Challenge 194 Production Design + +## Scope and phase boundaries + +This design advances the validated Day-0 implementation through four +independently gated phases: + +1. production Poisson/Newman-Ziff engine, restartable artifacts, and + correctness/performance calibration; +2. exploratory pilot at the preregistered sigma, size, and kappa grids; +3. frozen production sampling, using cluster capacity only after the local + performance gate passes; +4. transition, density-jump, finite-cluster-scale, uncertainty, figure, and + report analysis. + +The phases are sequential. Pilot execution is forbidden until the production +engine passes the three-way sampler gate. Confirmatory production is forbidden +until the pilot freezes the retained coupling windows and analysis-plan hash. +Pilot batches never enter confirmatory likelihoods. + +## Backend decision + +The primary implementation is a Numba-compiled, single-trajectory engine. +Python remains responsible for mathematical references, orchestration, +validation, artifact publication, analysis, and plotting. + +A standalone C++17 backend is authorized only if the measured Numba engine +fails either part of this frozen gate on one complete `L = 2^18` trajectory +through the full pilot kappa grid, including basic observables: + +- steady-state wall time at most 120 seconds on one CPU core; +- peak resident memory at most 4 GiB. + +Compilation time is measured and reported separately. The gate uses fresh +subprocesses, reports median and maximum values over five steady-state runs, +and does not hide failed or outlying runs. A C++ fallback requires a recorded +capability report and must pass the same scientific validation suite. + +## Module boundaries + +The implementation remains under this challenge directory and adds focused +modules: + +```text +src/long_range_percolation/ +├── counter_rng.py # Philox counter streams and unbiased bounded integers +├── alias.py # immutable distance-class alias table +├── poisson_sweep.py # monotone event process and duplicate suppression +├── observables.py # incremental and checkpoint graph measurements +├── artifacts.py # schemas, hashes, atomic publication, resume +├── benchmark.py # correctness and resource capability gates +├── pilot.py # frozen pilot request construction +└── analysis.py # transition, scale, bootstrap, and sensitivity fits +scripts/ +├── validate_production.py +├── benchmark_production.py +├── run_pilot.py +├── run_production.py +└── analyze_production.py +``` + +No implementation is copied from ONMC. The existing quadratic and geometric +samplers remain independent scientific oracles. + +## Counter-based randomness + +The production engine uses Philox4x32-10 with published Random123 test vectors. +Each trajectory receives a disjoint key derived from: + +```text +(master_seed, phase, L, sigma_grid_id, replica, stream_id) +``` + +The derivation is canonical, versioned, and hashed. Pilot and confirmatory +phases use distinct namespaces. Thread count, job-array order, retries, and +machine scheduling cannot change a trajectory stream. + +The engine records the Philox algorithm/version, key material hash, initial +counter, terminal counter, and conversion version. Floating uniforms use a +fixed open-interval mapping. Uniform integers use rejection sampling, never +modulo reduction. Alias-column, alias-threshold, edge-offset, and exponential +draws use registered stream identifiers so refactoring one draw family cannot +silently perturb another. + +## Alias table and event process + +For each exact `(L, sigma)`, construct the immutable class weights + +```text +w_d = M_d J_d +Lambda = sum_d w_d +``` + +and a Walker alias table in deterministic distance order. The table stores +the kernel hash, class multiplicities, normalized-weight residual, and alias +invariants. Tests compare alias frequencies with exact `w_d/Lambda` under one +simultaneous threshold. + +For each trajectory: + +1. initialize `kappa = 0`; +2. advance by `Delta kappa ~ Exp(Lambda)`; +3. draw a distance class from the alias table; +4. draw one canonical edge offset uniformly in that class; +5. encode the edge as a unique unsigned 64-bit class-offset identifier; +6. ignore the event if that edge identifier is already open; +7. otherwise insert it, join its endpoints, and update incremental moments; +8. record requested observables whenever the event time crosses a frozen + coupling. + +Repeated events are required by the Poisson construction and are not treated +as errors. Open-edge membership uses a deterministic open-addressed hash set +whose load factor never exceeds 0.70. Growth is deterministic and preserves +the trajectory stream. Capacity, probes, duplicate fraction, and rehash count +are diagnostics. + +This event process gives exact independent Bernoulli marginals +`1 - exp(-kappa J_e)` at every retained coupling while coupling the entire +trajectory across kappa. + +## Incremental connectivity and observables + +The Numba union-find stores parent, component size, and a four-bit quarter-ring +arc mask. Successful joins update in constant amortized time: + +- open-edge count; +- component count; +- `sum_C |C|^2`; +- `sum_C |C|^4`; +- largest-component size; +- whether any component intersects all four fixed quarter-ring arcs. + +At every retained coupling, one `O(L)` root scan computes: + +- deterministic largest and second-largest component sizes; +- `S1/L` and `S2/L`; +- `Q_G = sum |C|^4 / (sum |C|^2)^2`; +- four-sector crossing indicator; +- consistency checks against incremental moments. + +Expensive measurements run only on a deterministic thinning schedule bound to +the trajectory ID: + +- full `S1/L` histogram contribution; +- exact-small/logarithmic-large finite-cluster bins; +- bond-length histogram; +- pair connectivity at registered logarithmic separations; +- finite-cluster connectivity; +- finite-cluster structure factors for modes `m = 0,...,8`. + +The largest cluster uses a deterministic tie rule. Its pair contribution is +removed per realization before ensemble averaging; subtracting +`E[S1/L]^2` is forbidden. Measurement timing is reported separately and must +not dominate unthinned sampling. + +## Restartable and immutable artifacts + +Results live under: + +```text +tracks/qmc/results/frustration-free/challenge-194// +``` + +The run bundle contains: + +```text +request.json +environment.json +kernel/ +seed-manifest.json +capability.json +batches/ +progress.json +analysis-plan.json +derived/ +figures/ +manifest.json +``` + +One trajectory is the smallest restart unit. The 120-second performance gate +makes mid-trajectory checkpoints unnecessary; interruption loses at most one +trajectory. Each completed trajectory or fixed-size trajectory batch is +written to a unique partial file, flushed, fsynced, semantically reloaded, +hashed, and atomically renamed. The parent directory is fsynced after publish. + +`progress.json` is regenerated only from verified immutable batches. Resume +requires exact agreement of request hash, source revision, clean-tree status, +environment lock hash, kernel hash, analysis-plan hash, RNG version, and seed +assignment. Existing valid artifacts are never overwritten. Stale, extra, +partially published, or hash-mismatched files fail closed. + +The raw batch schema stores every trajectory as the resampling unit and keeps +all retained couplings from that trajectory together. Aggregates never replace +raw batches. + +## Production correctness gate + +The Poisson engine must pass before any pilot: + +1. published Philox vectors and counter/stream separation; +2. unbiased bounded-integer tests at difficult non-power-of-two bounds; +3. alias frequencies versus exact class weights; +4. Poisson event-count and interarrival distributions; +5. exact edge-frequency marginals at every validation coupling; +6. no-edge probability and open-edge count mean/variance; +7. bond-length histograms and component partitions; +8. `S1`, `S2`, `Q_G`, four-sector crossing, and component moments; +9. all-graph distributions for `L <= 6`; +10. three-way agreement among quadratic, geometric, and Poisson samplers for + `L <= 256`; +11. `kappa = 0`, saturated coupling, antipodal class, tiny/huge finite + parameter, hash-growth, and duplicate-event limits; +12. identical trajectory output across process counts and scheduling orders. + +Statistical checks use fixed seeds and preregistered familywise thresholds. +No failed seed is replaced. Exact invariants use exact or deterministic +floating tolerances. The gate publishes a machine-readable report with raw +counts and margins to every threshold. + +## Performance calibration + +Correctness and performance are separate gates. The benchmark harness runs in +fresh subprocesses and records: + +- JIT compilation time; +- steady-state wall and CPU time; +- peak RSS; +- events generated and unique edges opened per second; +- union operations per second; +- duplicate fraction and hash probes; +- basic-observable and thinned-measurement cost; +- output bytes per trajectory. + +Benchmark points are `L = 2^10, 2^14, 2^18` for all four pilot sigma values. +The quadratic oracle is benchmarked only through `L = 256`; geometric and +Poisson engines are compared at every feasible benchmark size. + +The Numba backend proceeds only if correctness passes and the frozen +`L = 2^18` gate is met. Optimization may change layout, compilation strategy, +and batching, but not model semantics, RNG mapping, retained observables, or +artifact schemas without repeating validation. + +## Pilot + +The exploratory pilot is frozen as: + +- `sigma = 0.8, 0.9, 1.0, 1.1`; +- `L = 2^10, 2^14, 2^18`; +- `kappa_j = 0.25 * 1.25^j`, retaining values at most `6`; +- separate pilot-only seed namespace; +- at least eight independent trajectory streams per `(sigma, L)` pilot cell; +- complete basic observables at every coupling; +- deterministic thinned measurements. + +Local execution first covers `L = 2^10` and the calibrated benchmark cells. +After the performance gate, remaining pilot cells are emitted as immutable +cluster jobs. The pilot succeeds only if the common change region of both +`Q_G` crossings and four-sector crossing probabilities is bracketed for +`sigma <= 1`, and the `sigma = 1.1` drift is visible without being labeled a +transition. + +If a bracket is missing, the grid may be extended only by a new versioned +pilot request; old pilot data remain immutable and excluded from confirmatory +fits. + +## Cluster policy + +Cluster execution begins only after local scientific validation and Numba +optimization are complete. Jobs are parallelized by `(sigma, L, trajectory +batch)`; no two jobs share writable output. Each worker is single-threaded, +and nodes are filled with as many independent workers as allowed by measured +RSS and core count, with no BLAS/Numba oversubscription. + +Before submission, a profile-specific dry run verifies CPU, memory, walltime, +Python/Numba compatibility, filesystem atomic rename behavior, and output +paths. The first real array is monitored through startup, first batch +publication, and semantic reload before scaling to the full allocation. + +Once verified, use all suitable idle CPU capacity within account and partition +limits. Large arrays remain restartable per trajectory batch. Cluster results +are fetched by manifest, hashes are rechecked locally, and only verified +batches enter analysis. + +## Frozen confirmatory production + +After the pilot, freeze: + +- retained coupling windows and at least 12 near-critical sigma-one points; +- all retained sigma sizes `2^10, 2^12, 2^14, 2^16, 2^18`; +- sigma-one intermediate sizes `2^11, 2^13, 2^15, 2^17`; +- at least eight independent streams per trajectory batch; +- stopping ceilings and deterministic thinning; +- analysis-plan hash. + +Sampling for a cell stops when both dimensionless-observable standard errors +are at most `0.01` near transition and relative spectral-scale error is at +most `8%`, or at the preregistered realization ceiling. Stopping decisions +use completed immutable batches only. + +`L = 2^20` remains excluded unless the information-gain and projected +eight-hour gates in `DESIGN.md` pass. + +## Analysis + +Transition location uses two primary estimators: + +1. pairwise `Q_G(L)` and `Q_G(2L)` crossings; +2. four-sector crossing-probability crossings. + +Their extrapolated 95% intervals must overlap or the transition is reported +unresolved. For `sigma = 1`, the full `S1/L` distribution is tested for one +versus two components; a stable antimode permits an equal-weight +pseudotransition and peak-separation analysis. Maximum slope is not a primary +transition estimator. + +Finite-cluster scale extraction follows the preregistered spectral model: + +```text +F_L(k)^-1 = a0 + a_sigma |q|^sigma + a2 q^2 +xi_sigma = (a_sigma/a0)^(1/sigma) +``` + +Unresolved coefficients produce censored bounds. Synthetic propagators must +validate the extractor before physical fits. + +At `sigma = 1`, compare exactly the four registered models: fixed `2/3`, +free essential exponent, algebraic, and fixed `2/3` with one logarithmic +correction. Every nested bootstrap resamples streams and then batches, keeps +whole monotone trajectories together, and refits transition location, scales, +and nonlinear models. Deletion tests follow `DESIGN.md` without post-hoc model +selection. + +`sigma = 0.8` and `0.9` are continuous-side controls. `sigma = 1.1` is a +negative control: only crossover drift is reported, never a finite critical +point. + +## Figures and final report + +Final artifacts include: + +- transition crossings with simultaneous uncertainty bands; +- `S1/L` distributions and density-jump diagnostics at `sigma = 1`; +- finite-cluster spectral-scale extraction with censored points; +- fixed-`2/3`, free-essential, algebraic, and logarithmic-correction + comparisons; +- deletion/sensitivity summaries; +- coarse sigma controls; +- benchmark and convergence diagnostics. + +Every figure is bound to source batch hashes and analysis-plan hash. The final +report states setup, kernel convention, resource usage, validation status, +transition interval, density-jump evidence, scaling verdict, controls, +limitations, and exact reproduction commands. + +The accepted outcomes are support, falsification on accessible scales, or an +honest inconclusive result. No positive physics conclusion is required. + +## Failure rules + +- Any three-way sampler disagreement blocks pilot execution. +- Any artifact/hash/provenance mismatch blocks resume and analysis. +- Missing overlap between the two transition estimators yields “unresolved.” +- Fewer than eight uncensored scale points, transition-dominated exponent + uncertainty, or indistinguishable candidate predictions yield + “inconclusive.” +- A drifting `sigma > 1` crossover is never reinterpreted as a transition. +- Numba performance-gate failure triggers a recorded optimization pass and + then, if still failing, a separately validated C++17 backend. diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/PRODUCTION_ENGINE_PLAN.md b/tracks/qmc/solutions/frustration-free/challenge-194/PRODUCTION_ENGINE_PLAN.md new file mode 100644 index 000000000..b604472e8 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/PRODUCTION_ENGINE_PLAN.md @@ -0,0 +1,1696 @@ +# Challenge 194 Production Engine Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build and gate the first post-Day-0 subproject: a deterministic, Numba-compiled Philox/Poisson/Newman-Ziff engine with independently testable semantics, restartable immutable artifacts, and a fail-closed local correctness/performance decision. + +**Architecture:** Python owns canonical request validation, independent mathematical references, stream derivation, artifact publication, validation, and benchmark orchestration. Numba owns only fixed-dtype numerical kernels: Philox draws, alias sampling, open-address membership, incremental union-find, and the monotone event loop; independent Python and compiled implementations meet only at immutable kernel/request inputs and validation outputs. + +**Tech Stack:** CPython 3.12, NumPy 2.2.6, SciPy 1.15.3, h5py 3.14.0, the latest Python-3.12/NumPy-2.2.6-compatible Numba resolved by `uv add numba` and then exactly pinned in `pyproject.toml` and `uv.lock`, pytest, uv. + +## Global Constraints + +- All committed implementation stays under this challenge directory. +- No implementation is copied from ONMC. +- The existing quadratic and geometric samplers remain independent scientific oracles. +- The production engine uses Philox4x32-10 with published Random123 test vectors. +- Each trajectory receives a disjoint key derived from: + +```text +(master_seed, phase, L, sigma_grid_id, replica, stream_id) +``` + +- The derivation is canonical, versioned, and hashed. +- Pilot and confirmatory phases use distinct namespaces. +- Thread count, job-array order, retries, and machine scheduling cannot change a trajectory stream. +- Floating uniforms use a fixed open-interval mapping. +- Uniform integers use rejection sampling, never modulo reduction. +- Alias-column, alias-threshold, edge-offset, and exponential draws use registered stream identifiers so refactoring one draw family cannot silently perturb another. +- For each exact `(L, sigma)`, construct the immutable class weights + +```text +w_d = M_d J_d +Lambda = sum_d w_d +``` + +- Repeated events are required by the Poisson construction and are not treated as errors. +- Open-edge membership uses a deterministic open-addressed hash set whose load factor never exceeds 0.70. +- Optimization may change layout, compilation strategy, and batching, but not model semantics, RNG mapping, retained observables, or artifact schemas without repeating validation. +- One trajectory is the smallest restart unit. +- Existing valid artifacts are never overwritten. +- Stale, extra, partially published, or hash-mismatched files fail closed. +- The raw batch schema stores every trajectory as the resampling unit and keeps all retained couplings from that trajectory together. +- Aggregates never replace raw batches. +- Statistical checks use fixed seeds and preregistered familywise thresholds. +- No failed seed is replaced. +- Exact invariants use exact or deterministic floating tolerances. +- The gate publishes a machine-readable report with raw counts and margins to every threshold. +- Correctness and performance are separate gates. +- steady-state wall time at most 120 seconds on one CPU core; +- peak resident memory at most 4 GiB. +- Compilation time is measured and reported separately. +- The gate uses fresh subprocesses, reports median and maximum values over five steady-state runs, and does not hide failed or outlying runs. +- A standalone C++17 backend is authorized only if the measured Numba engine fails either part of this frozen gate on one complete `L = 2^18` trajectory through the full pilot kappa grid, including basic observables. +- Pilot execution is forbidden until the production engine passes the three-way sampler gate. +- Any three-way sampler disagreement blocks pilot execution. +- Numba performance-gate failure triggers a recorded optimization pass and then, if still failing, a separately validated C++17 backend. +- This plan ends at the Numba capability decision. It excludes pilot runs, cluster submission, confirmatory production, physics fits, figures, final reporting, and C++ implementation. +- All implementation tasks use strict RED then GREEN test cycles. +- Every local commit stages only the paths named by its task; `git add .` is forbidden. + +--- + +## File Map + +- `pyproject.toml` — exact Numba runtime pin after resolver selection. +- `uv.lock` — complete resolved environment, including Numba and llvmlite. +- `src/long_range_percolation/runtime.py` — runtime fingerprint and Numba capability metadata. +- `src/long_range_percolation/counter_rng.py` — canonical stream derivation plus pure-Python and Numba Philox4x32-10 primitives. +- `src/long_range_percolation/alias.py` — immutable deterministic distance-class Walker table and compiled draw primitive. +- `src/long_range_percolation/edge_set.py` — full-range `uint64` open-address set and diagnostics. +- `src/long_range_percolation/observables.py` — basic observable schema and deterministic root-scan summaries. +- `src/long_range_percolation/production_union_find.py` — array-only incremental union-find state and compiled updates. +- `src/long_range_percolation/poisson_reference.py` — independently structured exact monotone Python semantics. +- `src/long_range_percolation/poisson_sweep.py` — Numba event engine and immutable trajectory result. +- `src/long_range_percolation/validation.py` — fixed validation protocol, raw statistics, threshold margins, and gate report. +- `src/long_range_percolation/artifacts.py` — canonical schemas, semantic reload, fsync, atomic publication, and restart reconstruction. +- `src/long_range_percolation/benchmark.py` — subprocess protocol, metrics, frozen gate, optimization ledger, and backend decision. +- `src/long_range_percolation/__init__.py` — production-engine public API exports. +- `tests/data/random123_philox4x32_10.json` — published Random123 vectors with source citation. +- `tests/test_runtime.py` — pinning, fresh-process import, and capability metadata. +- `tests/test_counter_rng.py` — vectors, stream separation, open uniforms, rejection accounting, and numeric boundaries. +- `tests/test_alias.py` — deterministic construction, invariants, and simultaneous frequency gate. +- `tests/test_edge_set.py` — full `uint64` domain, growth, probes, and stream-preserving behavior. +- `tests/test_production_union_find.py` — incremental moments, deterministic ties, sector masks, and root-scan agreement. +- `tests/test_poisson_reference.py` — independent event semantics and analytic Poisson/Bernoulli laws. +- `tests/test_poisson_sweep.py` — scripted semantic equivalence, Numba execution, extremes, and scheduling invariance. +- `tests/test_validation.py` — fixed family definitions, raw margins, all-graph and three-way acceptance. +- `tests/test_artifacts.py` — crash consistency, immutable publication, corruption rejection, and reconstruction. +- `tests/test_benchmark.py` — fresh subprocesses, warmup separation, five-run aggregation, frozen thresholds, and fallback report. +- `scripts/validate_production.py` — one-command correctness report. +- `scripts/benchmark_production.py` — one-command compilation/steady-state capability report. +- `scripts/decide_production_backend.py` — fail-closed optimization/revalidation and Numba-versus-C++ decision report. +- `README.md` — post-Day-0 setup, local gates, artifact locations, and phase boundary. + +All paths in tasks below are relative to +`tracks/qmc/solutions/frustration-free/challenge-194/`. Commands run from that +directory unless a command explicitly starts with `git -C`. + +--- + +## Shared Interface and Type Contract + +The following names and types are frozen across tasks. Host dataclasses may +contain strings and dictionaries; every `@numba.njit` boundary receives only +scalars and contiguous NumPy arrays with the stated dtype. + +```python +from dataclasses import dataclass +from pathlib import Path +from typing import Literal, Sequence + +import numpy as np +import numpy.typing as npt + +Phase = Literal["validation", "benchmark", "pilot", "confirmatory"] +U8 = npt.NDArray[np.uint8] +U32 = npt.NDArray[np.uint32] +U64 = npt.NDArray[np.uint64] +I64 = npt.NDArray[np.int64] +F64 = npt.NDArray[np.float64] + +STREAM_ALIAS_COLUMN: int = 0 +STREAM_ALIAS_THRESHOLD: int = 1 +STREAM_EDGE_OFFSET: int = 2 +STREAM_EXPONENTIAL: int = 3 +STREAM_COUNT: int = 4 + +@dataclass(frozen=True) +class StreamIdentity: + master_seed: int + phase: Phase + length: int + sigma_grid_id: str + replica: int + stream_id: int + +@dataclass(frozen=True) +class StreamMaterial: + key: U32 # shape (2,) + initial_counter: U32 # shape (4,) + material_sha256: str + +@dataclass(frozen=True) +class AliasTable: + probability: F64 # shape (L // 2,) + alias: I64 # shape (L // 2,) + multiplicity: U64 # shape (L // 2,) + class_weight: F64 # shape (L // 2,) + total_rate: float + kernel_sha256: str + normalized_residual: float + +@dataclass(frozen=True) +class BasicObservables: + open_edges: int + component_count: int + largest_size: int + second_largest_size: int + s1_fraction: float + s2_fraction: float + sum_size_sq: float + sum_size_fourth: float + q_g: float + four_sector_crossing: bool + +@dataclass(frozen=True) +class TrajectoryRequest: + length: int + sigma: float + sigma_grid_id: str + kappas: F64 # finite, sorted, unique, nonnegative + master_seed: int + phase: Phase + replica: int + kernel_sha256: str + +@dataclass(frozen=True) +class TrajectoryResult: + request_sha256: str + observables: F64 # shape (n_kappa, 10), fixed column order + terminal_counters: U32 # shape (STREAM_COUNT, 4) + draw_counts: U64 # shape (STREAM_COUNT, 3): words, blocks, rejections + event_count: int + duplicate_count: int + hash_diagnostics: U64 # capacity, size, total probes, max probe, rehashes +``` + +`TrajectoryResult.observables` columns are, in order: +`open_edges`, `component_count`, `largest_size`, `second_largest_size`, +`s1_fraction`, `s2_fraction`, `sum_size_sq`, `sum_size_fourth`, `q_g`, +`four_sector_crossing`. Integer-valued columns are exactly representable for +the scoped sizes; moments are `float64` because `L^4` exceeds `uint64` at +`L = 2^18`. + +--- + +### Task 1: Resolve and Pin Numba Runtime + +**Files:** +- Modify: `pyproject.toml` +- Modify: `uv.lock` +- Create: `src/long_range_percolation/runtime.py` +- Create: `tests/test_runtime.py` + +**Interfaces:** +- Produces `runtime_capability() -> dict[str, object]`. +- The dictionary has exactly `schema_version`, `python`, `implementation`, + `platform`, `machine`, `numpy`, `scipy`, `h5py`, `numba`, `llvmlite`, + `cpu_name`, `cpu_features`, `threading_layer`, `numba_disable_jit`, + `fastmath`, and `boundscheck`. +- `schema_version == "challenge-194-runtime-v1"`, `fastmath is False`, and + `boundscheck is True` for correctness commands. + +- [ ] **Step 1: Write the failing runtime tests** + +```python +import importlib.metadata +import json +import subprocess +import sys + +from long_range_percolation.runtime import runtime_capability + + +def test_numba_is_exactly_pinned_and_imports_in_fresh_python(): + declared = open("pyproject.toml", encoding="utf-8").read() + version = importlib.metadata.version("numba") + assert f'"numba=={version}"' in declared + completed = subprocess.run( + [sys.executable, "-c", "import numba, numpy; print(numba.__version__)"], + check=True, capture_output=True, text=True, + ) + assert completed.stdout.strip() == version + + +def test_runtime_capability_is_complete_and_json_stable(): + first = runtime_capability() + second = runtime_capability() + assert first == second + assert first["schema_version"] == "challenge-194-runtime-v1" + assert first["fastmath"] is False + assert first["boundscheck"] is True + assert json.loads(json.dumps(first, sort_keys=True)) == first +``` + +- [ ] **Step 2: Run the tests and verify RED** + +Run: `uv run pytest tests/test_runtime.py -q` + +Expected: collection fails with +`ModuleNotFoundError: No module named 'long_range_percolation.runtime'`. + +- [ ] **Step 3: Resolve, then pin the actual compatible Numba version** + +Run: + +```bash +uv add numba +uv run python -c 'import importlib.metadata as m; print(m.version("numba")); print(m.version("llvmlite"))' +NUMBA_VERSION="$(uv run python -c 'import importlib.metadata as m; print(m.version("numba"))')" +uv add "numba==$NUMBA_VERSION" +uv lock +uv sync --frozen +``` + +Expected: the first `uv add numba` succeeds for Python 3.12 and NumPy 2.2.6; +the second `uv add` replaces the resolver's range with equality to the exact +installed version; `uv sync --frozen` makes no lockfile changes. No version is +guessed before resolution. + +- [ ] **Step 4: Implement deterministic capability capture** + +```python +from __future__ import annotations + +import importlib.metadata +import os +import platform +import sys + +import numba + + +def runtime_capability() -> dict[str, object]: + return { + "schema_version": "challenge-194-runtime-v1", + "python": platform.python_version(), + "implementation": sys.implementation.name, + "platform": platform.platform(), + "machine": platform.machine(), + "numpy": importlib.metadata.version("numpy"), + "scipy": importlib.metadata.version("scipy"), + "h5py": importlib.metadata.version("h5py"), + "numba": importlib.metadata.version("numba"), + "llvmlite": importlib.metadata.version("llvmlite"), + "cpu_name": numba.config.CPU_NAME or "", + "cpu_features": numba.config.CPU_FEATURES or "", + "threading_layer": os.environ.get("NUMBA_THREADING_LAYER", ""), + "numba_disable_jit": bool(numba.config.DISABLE_JIT), + "fastmath": False, + "boundscheck": True, + } +``` + +- [ ] **Step 5: Run GREEN and the complete Day-0 regression suite** + +Run: + +```bash +uv sync --frozen +uv run pytest tests/test_runtime.py -q +uv run pytest -q +``` + +Expected: runtime tests pass; every pre-existing Day-0 test remains green. + +- [ ] **Step 6: Commit the resolved runtime** + +```bash +git add pyproject.toml uv.lock \ + src/long_range_percolation/runtime.py tests/test_runtime.py +git commit -m "Pin production Numba runtime" +``` + +--- + +### Task 2: Philox4x32-10 and Stream Contract + +**Files:** +- Create: `tests/data/random123_philox4x32_10.json` +- Create: `src/long_range_percolation/counter_rng.py` +- Create: `tests/test_counter_rng.py` + +**Interfaces:** +- Produces `derive_stream_material(identity: StreamIdentity) -> StreamMaterial`. +- Produces `philox4x32_10_reference(counter: U32, key: U32) -> U32`. +- Produces Numba-callable + `philox4x32_10(counter: U32, key: U32, out: U32) -> None`. +- Produces Numba-callable + `next_u32(counter, key, block, lane_and_valid, accounting) -> np.uint32`. +- Produces Numba-callable + `uniform_open(counter, key, block, lane_and_valid, accounting) -> float`. +- Produces Numba-callable + `bounded_u32(bound, counter, key, block, lane_and_valid, accounting) -> np.uint32`. +- `lane_and_valid` is `uint8[2]` (`lane`, `valid`); `accounting` is + `uint64[3]` (`words`, `blocks`, `rejections`). + +- [ ] **Step 1: Add published vectors and failing vector tests** + +Create the JSON fixture with source +`https://github.com/DEShawResearch/random123/blob/main/tests/kat_vectors` +and these published Philox4x32-10 cases: + +```json +{ + "algorithm": "Philox4x32-10", + "source": "https://github.com/DEShawResearch/random123/blob/main/tests/kat_vectors", + "vectors": [ + { + "counter": ["00000000", "00000000", "00000000", "00000000"], + "key": ["00000000", "00000000"], + "output": ["6627e8d5", "e169c58d", "bc57ac4c", "9b00dbd8"] + }, + { + "counter": ["ffffffff", "ffffffff", "ffffffff", "ffffffff"], + "key": ["ffffffff", "ffffffff"], + "output": ["408f276d", "41c83b0e", "a20bc7c6", "6d5451fd"] + } + ] +} +``` + +```python +def test_reference_and_numba_philox_match_published_vectors(): + vectors = json.loads(Path("tests/data/random123_philox4x32_10.json").read_text()) + for case in vectors["vectors"]: + counter = np.array([int(x, 16) for x in case["counter"]], np.uint32) + key = np.array([int(x, 16) for x in case["key"]], np.uint32) + expected = np.array([int(x, 16) for x in case["output"]], np.uint32) + np.testing.assert_array_equal(philox4x32_10_reference(counter, key), expected) + actual = np.empty(4, np.uint32) + philox4x32_10(counter, key, actual) + np.testing.assert_array_equal(actual, expected) +``` + +- [ ] **Step 2: Add failing stream, conversion, and rejection tests** + +```python +def test_stream_identity_is_canonical_and_domain_separated(): + base = StreamIdentity(7, "validation", 256, "sigma-1-binary", 3, 0) + materials = [ + derive_stream_material(dataclasses.replace(base, stream_id=stream)) + for stream in range(STREAM_COUNT) + ] + assert len({item.material_sha256 for item in materials}) == STREAM_COUNT + assert len({item.key.tobytes() + item.initial_counter.tobytes() + for item in materials}) == STREAM_COUNT + repeated = derive_stream_material(base) + np.testing.assert_array_equal(repeated.key, materials[0].key) + np.testing.assert_array_equal(repeated.initial_counter, materials[0].initial_counter) + assert repeated.material_sha256 == materials[0].material_sha256 + changed = derive_stream_material(dataclasses.replace(base, phase="benchmark")) + assert changed.material_sha256 != materials[0].material_sha256 + + +def test_uniform_open_excludes_endpoints_for_extreme_words(): + assert u32_to_open(np.uint32(0)) == 2.0 ** -33 + assert u32_to_open(np.uint32(0xFFFFFFFF)) == 1.0 - 2.0 ** -33 + + +@pytest.mark.parametrize("bound", [1, 3, 7, 2**31 - 1, 2**31, 2**32 - 1]) +def test_bounded_u32_matches_reference_and_accounts_for_every_rejection(bound): + threshold = ((1 << 32) - bound) % bound + words = [max(0, threshold - 1), threshold, 0xFFFFFFFF] + ref_value, ref_state = bounded_u32_from_words_reference(bound, words) + value, state = bounded_u32_from_words_compiled(bound, words) + assert value == ref_value + assert state.words == ref_state.words + assert state.rejections == ref_state.rejections + assert 0 <= value < bound +``` + +Also test carry from counters +`[0xffffffff, 0xffffffff, 0xffffffff, 0]` and rejection sequences that consume +multiple Philox blocks. The reference generator must be ordinary Python and +must not call a jitted function. The two named `bounded_u32_from_words_*` +helpers are test-only adapters defined in `tests/test_counter_rng.py`; each +returns `(value, Accounting(words: int, blocks: int, rejections: int))` and +raises `AssertionError` if the supplied finite word tape is exhausted. + +- [ ] **Step 3: Run the RNG tests and verify RED** + +Run: `uv run pytest tests/test_counter_rng.py -q` + +Expected: collection fails because `counter_rng.py` does not exist. + +- [ ] **Step 4: Implement stream derivation and pure reference Philox** + +Use canonical JSON with sorted keys, ASCII separators `(",", ":")`, and +integer range checks `master_seed, replica in [0, 2**64)` and +`stream_id in [0, STREAM_COUNT)`. Hash +`b"challenge-194-philox-stream-v1\0" + canonical_json` with SHA-256. Decode +digest bytes `[0:8]` as two little-endian key words, bytes `[8:24]` as four +little-endian initial-counter words, and retain the full digest hex as +`material_sha256`. Construct all four materials before a trajectory and reject +any duplicate `(key, initial_counter)` pair. + +```python +PHILOX_M0 = np.uint32(0xD2511F53) +PHILOX_M1 = np.uint32(0xCD9E8D57) +PHILOX_W0 = np.uint32(0x9E3779B9) +PHILOX_W1 = np.uint32(0xBB67AE85) +RNG_VERSION = "philox4x32-10/open32-v1/bounded-reject-v1" + + +def u32_to_open(word: np.uint32) -> float: + return (float(word) + 0.5) * (2.0 ** -32) +``` + +Implement each Philox round with explicit 32-bit masks after Python integer +multiplication. Keep this reference separate from the compiled multiply-high +helper. + +- [ ] **Step 5: Implement Numba primitives with explicit fixed dtypes** + +Use `@numba.njit(cache=True, boundscheck=True, fastmath=False)`. Compute +`product = np.uint64(multiplier) * np.uint64(word)`, low with +`np.uint32(product & np.uint64(0xffffffff))`, and high with +`np.uint32(product >> np.uint64(32))`. Increment the four-word counter +little-endian with carry after materializing each block. + +For `bounded_u32`, require `1 <= bound <= 0xffffffff`, set +`threshold = ((1 << 32) - bound) % bound` using `uint64`, reject while +`word < threshold`, increment `rejections` for each rejected word, and return +`word % bound` only after acceptance. `words` increments for every consumed +word, including rejected words; `blocks` increments for every generated +four-word block. This modulo after rejection is required and is not biased +modulo reduction. + +- [ ] **Step 6: Run GREEN, disabled-JIT parity, and full regressions** + +Run: + +```bash +uv run pytest tests/test_counter_rng.py -q +NUMBA_DISABLE_JIT=1 uv run pytest tests/test_counter_rng.py -q +uv run pytest -q +``` + +Expected: published vectors, boundary cases, accounting, and both execution +modes pass; Day-0 tests remain green. + +- [ ] **Step 7: Commit RNG files** + +```bash +git add src/long_range_percolation/counter_rng.py \ + tests/test_counter_rng.py tests/data/random123_philox4x32_10.json +git commit -m "Add deterministic Philox counter streams" +``` + +--- + +### Task 3: Deterministic Distance-Class Alias Table + +**Files:** +- Create: `src/long_range_percolation/alias.py` +- Create: `tests/test_alias.py` + +**Interfaces:** +- Produces + `build_distance_alias(length: int, sigma: float, kernel: F64, kernel_sha256: str) -> AliasTable`. +- Produces Numba-callable + `draw_alias(probability: F64, alias: I64, column_word: np.uint32, threshold_word: np.uint32) -> int`. +- Construction order is increasing distance; worklists are FIFO arrays, not + Python sets or heaps. + +- [ ] **Step 1: Write failing deterministic and invariant tests** + +```python +def test_alias_table_is_deterministic_and_read_only(): + kernel = periodic_kernel(256, 0.9) + first = build_distance_alias(256, 0.9, kernel, sha256(kernel.tobytes()).hexdigest()) + second = build_distance_alias(256, 0.9, kernel.copy(), first.kernel_sha256) + np.testing.assert_array_equal(first.probability, second.probability) + np.testing.assert_array_equal(first.alias, second.alias) + assert not first.probability.flags.writeable + assert not first.alias.flags.writeable + + +def test_alias_invariants_cover_antipodal_class_and_finite_extremes(): + for length, sigma in [(2, 1.0), (256, math.ulp(1.0)), (256, 128.0)]: + kernel = periodic_kernel(length, sigma) + table = build_distance_alias(length, sigma, kernel, digest(kernel)) + assert np.all((0.0 <= table.probability) & (table.probability <= 1.0)) + assert np.all((0 <= table.alias) & (table.alias < length // 2)) + assert table.multiplicity[-1] == length // 2 + assert table.total_rate == pytest.approx(kernel_weight_sum(length, sigma), rel=2e-13) + assert abs(table.normalized_residual) <= 8 * np.finfo(float).eps +``` + +- [ ] **Step 2: Write the failing simultaneous frequency test** + +Use `n = 2_000_000`, fixed Philox identity +`(194, validation, 256, "alias-sigma-0.9", 0, stream)`, and simultaneous +threshold + +```python +epsilon = math.sqrt(math.log(2 * class_count / 0.001) / (2 * n)) +assert max(abs(observed / n - expected)) <= epsilon +``` + +Store every class's observed count, expected probability, absolute error, +threshold, and `margin = threshold - absolute_error` in the assertion message. + +- [ ] **Step 3: Run alias tests and verify RED** + +Run: `uv run pytest tests/test_alias.py -q` + +Expected: collection fails because `alias.py` does not exist. + +- [ ] **Step 4: Implement deterministic Walker construction** + +Validate exact shape `(length // 2,)`, finite positive kernel values, and +matching SHA-256. Form multiplicities in `uint64`, class weights in `float64`, +and `total_rate = math.fsum(float(x) for x in class_weight)`. Normalize by +`total_rate`, scale by class count, enqueue indices with scaled weight `< 1` +into the small FIFO and all others into the large FIFO, then process in queue +order. Clamp only final roundoff within eight ulps of `[0, 1]`; reject larger +violations. Freeze defensive copies. + +`draw_alias` maps `column_word` with multiply-high +`(uint64(column_word) * uint64(n)) >> 32`, maps `threshold_word` through +`u32_to_open`, and returns the column when the open uniform is +`<= probability[column]`, otherwise `alias[column]`. It consumes exactly the +two supplied words and does not own stream state. + +- [ ] **Step 5: Run GREEN and regressions** + +Run: + +```bash +uv run pytest tests/test_alias.py -q +uv run pytest -q +``` + +Expected: deterministic invariants and the one simultaneous frequency family +pass. + +- [ ] **Step 6: Commit alias implementation** + +```bash +git add src/long_range_percolation/alias.py tests/test_alias.py +git commit -m "Add deterministic distance alias table" +``` + +--- + +### Task 4: Full-Range uint64 Open-Address Edge Set + +**Files:** +- Create: `src/long_range_percolation/edge_set.py` +- Create: `tests/test_edge_set.py` + +**Interfaces:** +- Produces host constructor + `allocate_edge_set(expected_size: int) -> tuple[U64, U8, U64]`. +- Produces Numba-callable + `edge_set_insert(keys: U64, occupied: U8, diagnostics: U64, value: np.uint64) -> tuple[U64, U8, bool]`. +- Produces `encode_edge_id(class_start: U64, distance_index: int, offset: int) -> np.uint64`. +- Diagnostics are `[capacity, size, total_probes, max_probe, rehashes]`. +- Occupancy is separate from keys, so every `uint64`, including `0` and + `2**64 - 1`, is representable. + +- [ ] **Step 1: Write failing sentinel, growth, and probe tests** + +```python +def test_edge_set_accepts_entire_uint64_domain_without_sentinel_collision(): + values = np.array([0, 1, 2**63, 2**64 - 2, 2**64 - 1], np.uint64) + keys, occupied, diagnostics = allocate_edge_set(1) + for value in values: + keys, occupied, inserted = edge_set_insert(keys, occupied, diagnostics, value) + assert inserted + for value in values: + keys, occupied, inserted = edge_set_insert(keys, occupied, diagnostics, value) + assert not inserted + assert diagnostics[1] == len(values) + assert diagnostics[0] & (diagnostics[0] - 1) == 0 + assert diagnostics[1] / diagnostics[0] <= 0.70 + + +def test_growth_is_deterministic_and_does_not_consume_rng(): + values = np.arange(10_000, dtype=np.uint64) * np.uint64(0x9E3779B97F4A7C15) + first = insert_all(values) + second = insert_all(values) + assert first.diagnostics.tolist() == second.diagnostics.tolist() + np.testing.assert_array_equal(first.keys, second.keys) + np.testing.assert_array_equal(first.occupied, second.occupied) +``` + +Add collision-crafted tests that assert `total_probes >= size`, +`max_probe > 1`, and `rehashes > 0`, plus edge-ID tests proving all +distance-class ranges are disjoint and cover exactly +`[0, L*(L-1)//2)` for `L in (2, 8, 256)`. + +- [ ] **Step 2: Run tests and verify RED** + +Run: `uv run pytest tests/test_edge_set.py -q` + +Expected: collection fails because `edge_set.py` does not exist. + +- [ ] **Step 3: Implement fixed-dtype hashing and deterministic growth** + +Use the SplitMix64 finalizer, power-of-two capacities, linear probing, and an +`occupied: uint8[capacity]` array. Count one probe for every inspected slot. +Before inserting a new value, grow when +`10 * (size + 1) > 7 * capacity`; double capacity, reinsert occupied slots in +ascending old-slot order, and include rehash probes in `total_probes`. Reject +capacity overflow before allocation. The hash set receives no RNG state and +cannot affect draw accounting. + +Build `class_start` by a checked `uint64` prefix sum of class multiplicities. +`encode_edge_id` validates the distance index and offset on the host; the +compiled event loop uses the prevalidated expression +`class_start[distance_index] + uint64(offset)`. + +- [ ] **Step 4: Run GREEN under JIT and disabled JIT** + +Run: + +```bash +uv run pytest tests/test_edge_set.py -q +NUMBA_DISABLE_JIT=1 uv run pytest tests/test_edge_set.py -q +``` + +Expected: full-range keys, deterministic growth, and diagnostics pass in both +modes. + +- [ ] **Step 5: Commit edge set** + +```bash +git add src/long_range_percolation/edge_set.py tests/test_edge_set.py +git commit -m "Add deterministic uint64 edge set" +``` + +--- + +### Task 5: Incremental Union-Find and Basic Observables + +**Files:** +- Create: `src/long_range_percolation/observables.py` +- Create: `src/long_range_percolation/production_union_find.py` +- Create: `tests/test_production_union_find.py` + +**Interfaces:** +- Produces + `allocate_union_find(length: int) -> tuple[I64, I64, U8, F64, I64]`. +- State arrays are `parent`, `size`, `sector_mask`, `moments`, `counts`. +- `moments == [sum_size_sq, sum_size_fourth]` as `float64`. +- `counts == [open_edges, component_count, largest_size]` as `int64`. +- Produces Numba-callable + `union_incremental(parent, size, sector_mask, moments, counts, left, right) -> bool`. +- Produces + `scan_basic_observables(parent, size, sector_mask, moments, counts) -> BasicObservables`. + +- [ ] **Step 1: Write failing controlled-union tests** + +```python +def test_incremental_state_matches_exact_partition_after_every_edge(): + state = allocate_union_find(8) + edges = [(0, 1), (2, 3), (1, 2), (4, 5), (6, 7), (5, 6), (0, 7), (0, 7)] + python_uf = UnionFind(8) + for index, edge in enumerate(edges, start=1): + merged = union_incremental(*state, *edge) + python_uf.union(*edge) + sizes = python_uf.component_sizes() + summary = scan_basic_observables(*state) + assert summary.open_edges == index + assert summary.component_count == len(sizes) + assert summary.sum_size_sq == float(sum(int(s) ** 2 for s in sizes)) + assert summary.sum_size_fourth == float(sum(int(s) ** 4 for s in sizes)) + assert merged is (index != len(edges)) +``` + +Add tests for deterministic equal-size root ties, duplicated edges increasing +`open_edges` only when the caller indicates a unique insertion, `L=2`, and +`L=2**18` moment initialization without integer overflow. + +- [ ] **Step 2: Write failing root-scan observable tests** + +For hand-built partitions, assert largest and second-largest sizes use +descending size then smallest-root tie order, `Q_G = sum4 / sum2**2`, and +four-sector crossing is true exactly when one root's OR-reduced four-bit mask +equals `0b1111`. Assign vertex `i` to sector +`min(3, (4 * i) // L)`; this exact formula is shared by host initialization +and tests. + +- [ ] **Step 3: Run tests and verify RED** + +Run: `uv run pytest tests/test_production_union_find.py -q` + +Expected: collection fails because the production modules do not exist. + +- [ ] **Step 4: Implement array-only incremental connectivity** + +Initialize `parent = arange(L, int64)`, `size = ones(L, int64)`, masks from +the fixed formula, `moments = [float(L), float(L)]`, and +`counts = [0, L, 1]`. Implement iterative path halving and union by size with +smaller-root tie break. + +On a successful merge of sizes `a, b`, update: + +```python +moments[0] += float((a + b) ** 2 - a ** 2 - b ** 2) +moments[1] += float((a + b) ** 4 - a ** 4 - b ** 4) +counts[1] -= 1 +counts[2] = max(counts[2], a + b) +``` + +The compiled expression must cast each size to `float64` before fourth powers +to avoid `int64` overflow. The event engine increments `counts[0]` exactly +once after a successful edge-set insertion, before calling union; a union that +finds an existing path does not mean a duplicate edge. + +The root scan walks indices in ascending order, considers only +`parent[i] == i`, and checks incremental moments with deterministic tolerance +`32 * eps * max(1, exact_scan_value)`. A mismatch raises in the host wrapper +and returns a nonzero status code inside Numba. + +- [ ] **Step 5: Run GREEN and all regressions** + +Run: + +```bash +uv run pytest tests/test_production_union_find.py -q +uv run pytest -q +``` + +Expected: all incremental and root-scan checks pass without changing Day-0 +`UnionFind`. + +- [ ] **Step 6: Commit production connectivity** + +```bash +git add src/long_range_percolation/observables.py \ + src/long_range_percolation/production_union_find.py \ + tests/test_production_union_find.py +git commit -m "Add incremental production observables" +``` + +--- + +### Task 6: Independent Monotone Poisson Reference + +**Files:** +- Create: `src/long_range_percolation/poisson_reference.py` +- Create: `tests/test_poisson_reference.py` + +**Interfaces:** +- Produces + `validate_trajectory_request(request: TrajectoryRequest) -> None`. +- Produces + `run_poisson_reference(request: TrajectoryRequest, kernel: F64) -> TrajectoryResult`. +- The reference uses Python `set[int]`, cumulative class weights with + `bisect`, the existing Day-0 `UnionFind` only for checkpoint reconstruction, + and reference Philox functions. It must not import `alias`, `edge_set`, + `production_union_find`, or `poisson_sweep`. + +- [ ] **Step 1: Write failing request and independence tests** + +```python +def test_request_rejects_nonfinite_unsorted_or_duplicate_couplings(): + for values in ([0.2, 0.1], [0.1, 0.1], [math.nan], [math.inf], [-1.0]): + with pytest.raises(ValueError): + validate_trajectory_request(make_request(kappas=values)) + + +def test_reference_does_not_import_compiled_selection_or_connectivity(): + source = inspect.getsource(poisson_reference) + for forbidden in ("alias", "edge_set", "production_union_find", "poisson_sweep"): + assert forbidden not in source +``` + +- [ ] **Step 2: Write failing exact-semantic tests** + +Inject a finite scripted sequence of exponential uniforms, class uniforms, and +offset integers through a private test-only stream adapter. Assert: + +1. event times are `kappa += -log(u_open) / Lambda`; +2. every crossed requested coupling receives the state before the later event; +3. duplicate event IDs increment `event_count` and `duplicate_count` but do + not alter connectivity or `open_edges`; +4. one event may cross multiple requested couplings; +5. `kappa=0` records the empty graph without consuming any stream; +6. a positive largest coupling consumes exactly one final exponential draw to + establish that the next event lies beyond it, but consumes no class, + threshold, or offset draw for that overshooting event. + +- [ ] **Step 3: Write failing analytic law tests** + +With fixed identities and no seed replacement, test event counts as +`Poisson(kappa_max * Lambda)`, open-edge frequencies as +`1-exp(-kappa*J_d)`, no-edge probability as `exp(-kappa*Lambda)`, and +open-edge mean/variance from Day-0 analytic functions. Use one familywise +alpha `0.001`, divide it by the exact number of asserted scalar laws before +sampling, and include raw observed, expected, threshold, and signed margin in +every failure message. + +- [ ] **Step 4: Run tests and verify RED** + +Run: `uv run pytest tests/test_poisson_reference.py -q` + +Expected: collection fails because `poisson_reference.py` does not exist. + +- [ ] **Step 5: Implement the independent reference** + +Validate all finite numeric extremes before allocation: `L` is an even Python +integer at least two; `sigma` is finite, positive, and +`1.0 + sigma > 1.0`; kappas satisfy the interface contract; `replica` and +`master_seed` fit `uint64`; kernel shape is exact, finite, positive, and its +bytes match `kernel_sha256`. + +Build cumulative `M_d * J_d` with `math.fsum`; choose a class by +`bisect_left(cumulative, u_open * Lambda)` with a final-index clamp only for +roundoff; choose an offset with reference rejection-based bounded integers. +Decode endpoints independently from `canonical_edge`: for +`d < L/2`, use `left=offset`, `right=(offset+d)%L`; for the antipodal class, +require `offset < L/2`. Canonicalize endpoint order only when reconstructing a +checkpoint. + +- [ ] **Step 6: Run GREEN and Day-0 regressions** + +Run: + +```bash +uv run pytest tests/test_poisson_reference.py -q +uv run pytest -q +``` + +Expected: semantic, analytic, independence, and existing tests pass. + +- [ ] **Step 7: Commit the reference** + +```bash +git add src/long_range_percolation/poisson_reference.py \ + tests/test_poisson_reference.py +git commit -m "Add independent monotone Poisson reference" +``` + +--- + +### Task 7: Numba Poisson/Newman-Ziff Engine + +**Files:** +- Create: `src/long_range_percolation/poisson_sweep.py` +- Create: `tests/test_poisson_sweep.py` +- Modify: `src/long_range_percolation/__init__.py` + +**Interfaces:** +- Produces + `run_poisson_numba(request: TrajectoryRequest, kernel: F64, alias: AliasTable) -> TrajectoryResult`. +- Internal compiled entry: + +```python +_run_poisson_kernel( + length: int, + kappas: F64, + total_rate: float, + alias_probability: F64, + alias_index: I64, + multiplicity: U64, + class_start: U64, + keys: U64, + occupied: U8, + hash_diagnostics: U64, + parent: I64, + size: I64, + sector_mask: U8, + moments: F64, + counts: I64, + counters: U32, + keys_by_stream: U32, + blocks: U32, + lane_valid: U8, + draw_counts: U64, + output: F64, +) -> tuple[int, int, int] +``` + +Shapes are fixed by the shared contract: +`counters (4,4)`, `keys_by_stream (4,2)`, `blocks (4,4)`, +`lane_valid (4,2)`, `draw_counts (4,3)`. +Return values are `(status, event_count, duplicate_count)`. + +- [ ] **Step 1: Write failing scripted semantic-equivalence tests** + +Factor a test-only `_run_scripted_events` compiled kernel that receives arrays +of interarrival uniforms, class indices, and offsets. Compare every checkpoint +column against a separate Python reconstruction for duplicate events, +antipodal edges, multi-coupling crossings, and a final event after +`kappa_max`. This test isolates event semantics from random class-selection +statistics. + +- [ ] **Step 2: Write failing RNG scheduling tests** + +```python +def test_draw_families_are_isolated_and_accounted(): + result = run_poisson_numba(request, kernel, table) + expected_exponential_words = result.event_count + int(request.kappas[-1] > 0.0) + assert result.draw_counts[STREAM_EXPONENTIAL, 0] == expected_exponential_words + assert result.draw_counts[STREAM_ALIAS_COLUMN, 0] == result.event_count + assert result.draw_counts[STREAM_ALIAS_THRESHOLD, 0] == result.event_count + assert result.draw_counts[STREAM_EDGE_OFFSET, 0] >= result.event_count + assert result.draw_counts[STREAM_EDGE_OFFSET, 2] == ( + result.draw_counts[STREAM_EDGE_OFFSET, 0] - result.event_count + ) +``` + +Run identical trajectory IDs sequentially, reversed, with +`multiprocessing.get_context("spawn").Pool(2)`, and after a forced retry. +Assert byte-identical `TrajectoryResult` arrays. Assert changing only one +registered stream's initial counter changes that family's draws without +changing any other terminal counter. + +- [ ] **Step 3: Write failing finite-extreme and saturation tests** + +Cover `kappa=0`, `L=2`, antipodal-only edge selection, the smallest positive +finite sigma accepted by `ModelSpec`, `sigma=128`, `kappa_max` just below +float overflow, duplicate-heavy saturation, and hash growth. Require finite +outputs or a specific preflight `ValueError`; no NaN, wraparound, silent +infinite loop, or allocation after a failed preflight is accepted. + +- [ ] **Step 4: Run tests and verify RED** + +Run: `uv run pytest tests/test_poisson_sweep.py -q` + +Expected: collection fails because `poisson_sweep.py` does not exist. + +- [ ] **Step 5: Implement the compiled event loop** + +Preflight on the host, allocate all arrays, derive all four streams, and reject +stream-material collisions. In the compiled loop: + +1. record all zero couplings from initialized state; +2. draw one exponential open uniform and compute + `delta = -log(u) / total_rate`; +3. stop without class/offset draws when `current_kappa + delta > kappa_max`; +4. draw alias column and threshold from their distinct streams; +5. draw bounded offset from the offset stream; +6. increment event count; +7. insert the encoded ID into the edge set; +8. on a duplicate, increment duplicate count only; +9. on a new edge, increment open-edge count, decode endpoints, and union; +10. record every coupling smaller than the next event time; +11. after the loop, fill remaining checkpoints from the final state. + +Use `fastmath=False`, checked status codes for nonfinite time/moment values, +and no Python containers inside compiled code. Host code converts nonzero +status to a stable exception before constructing `TrajectoryResult`. + +- [ ] **Step 6: Run GREEN, compile inspection, and regressions** + +Run: + +```bash +uv run pytest tests/test_poisson_sweep.py -q +uv run python -c 'from long_range_percolation.poisson_sweep import assert_nopython_signatures; assert_nopython_signatures()' +NUMBA_DISABLE_JIT=1 uv run pytest tests/test_poisson_sweep.py -q +uv run pytest -q +``` + +Expected: normal and disabled-JIT semantics pass; the signature check confirms +every production kernel has at least one nopython signature and no object-mode +fallback. + +- [ ] **Step 7: Commit the Numba engine** + +```bash +git add src/long_range_percolation/poisson_sweep.py \ + src/long_range_percolation/__init__.py tests/test_poisson_sweep.py +git commit -m "Add Numba Poisson sweep engine" +``` + +--- + +### Task 8: Fixed Three-Way Correctness Gate Through L=256 + +**Files:** +- Create: `src/long_range_percolation/validation.py` +- Create: `tests/test_validation.py` +- Create: `scripts/validate_production.py` + +**Interfaces:** +- Produces `ValidationProtocol.production_v1() -> ValidationProtocol`. +- Produces + `run_production_validation(protocol: ValidationProtocol, output: Path) -> dict[str, object]`. +- The report schema is `challenge-194-validation-v1` and every check stores + `family`, `case_id`, `raw`, `expected`, `threshold`, `margin`, and `passed`. + +- [ ] **Step 1: Write failing protocol-freeze tests** + +Freeze the protocol in code: + +```python +VALIDATION_PROTOCOL_VERSION = "challenge-194-validation-v1" +FAMILYWISE_ALPHA = 0.001 +LENGTHS = (4, 6, 8, 16, 32, 64, 128, 256) +SIGMAS = (0.8, 1.0, 1.1) +KAPPAS = (0.0, 0.25, 0.7, 2.0, 6.0) +SAMPLES_BY_LENGTH = { + 4: 32768, 6: 32768, 8: 32768, 16: 16384, + 32: 8192, 64: 4096, 128: 2048, 256: 1024, +} +SAMPLERS = ("quadratic", "geometric", "poisson-reference", "poisson-numba") +MASTER_SEEDS = tuple(range(194_000_000, 194_032_768)) +``` + +The first three-way family uses quadratic, geometric, and Numba Poisson. +Python Poisson is a fourth independent diagnostic and cannot rescue a failed +three-way case. Assert the family denominators are computed solely from these +constants before draws and serialized in the report. + +- [ ] **Step 2: Write failing exact and deterministic families** + +Require exact/deterministic checks for published Philox vectors, stream +separation, bounded-integer accounting, alias invariants, edge-ID uniqueness, +hash full-range/growth, all graph distributions for `L<=6`, `kappa=0`, +saturated coupling, antipodal counts, tiny/huge finite parameters, duplicate +limits, incremental/root-scan moments, and process-order identity. Each exact +check uses `threshold=0`, with `margin=0` on equality or a negative numeric +distance on failure. + +- [ ] **Step 3: Write failing statistical family implementation** + +Use these fixed simultaneous rules, separately Bonferroni-adjusted within each +named family at familywise alpha `0.001`: + +- Bernoulli edge/class frequencies: exact two-sided `scipy.stats.binomtest`; +- Poisson event counts: exact two-sided `scipy.stats.poisson` tail probability + with the doubled smaller tail capped at one; +- scalar sampler-pair means (`open_edges`, `S1/L`, `S2/L`, `Q_G`, crossing, + normalized second/fourth moments): paired-independent permutation test with + exactly 49,999 Philox-derived label permutations; +- bond-length and component-partition histograms: fixed-bin multinomial + likelihood-ratio statistic calibrated by exactly 49,999 parametric + multinomial replicates from the pooled null; +- all-graph `L<=6` probabilities: exact binomial tests against enumeration. + +The threshold for p-value checks is +`FAMILYWISE_ALPHA / frozen_family_denominator`; store +`margin = pvalue - threshold`. Store all bins, counts, seeds, test statistic, +replicate count, and p-value. Zero expected bins are exact invariants rather +than divisions. No seed is discarded or regenerated. + +- [ ] **Step 4: Write failing report and CLI tests** + +The CLI accepts only: + +```text +--output PATH +--protocol production-v1 +--jobs INTEGER +``` + +`--jobs` changes scheduling only. Test `jobs=1` versus `jobs=2` for identical +canonical report payload after excluding measured elapsed time. Exit zero only +when all exact, reference, and three-way families pass; exit 2 on a scientific +failure and still atomically publish all raw margins. + +- [ ] **Step 5: Run tests and verify RED** + +Run: `uv run pytest tests/test_validation.py -q` + +Expected: collection fails because `validation.py` does not exist. + +- [ ] **Step 6: Implement gate without sharing oracle selection logic** + +Adapters may normalize each sampler's output into edge IDs, partitions, and +basic observables, but may not share random draws or edge-selection routines. +Poisson reference and Numba use distinct stream identities. Quadratic and +geometric retain `numpy.random.Generator` and independent seed ranges. Add a +source-structure test proving the four sampler modules do not import one +another. + +Canonical JSON uses sorted keys, separators `(",", ":")`, `allow_nan=False`, +and decimal hexadecimal strings for all binary64 protocol values. Include +runtime capability, source revision, clean-tree status, protocol hash, raw +counts, all margins, and an overall pass flag. + +- [ ] **Step 7: Run focused GREEN and the reduced smoke protocol** + +Run: + +```bash +uv run pytest tests/test_validation.py -q +uv run scripts/validate_production.py \ + --protocol production-v1 --jobs 1 \ + --output ../../../../results/frustration-free/challenge-194/validation-smoke/report.json +``` + +Expected: unit tests pass. The full command publishes a complete report and +returns either zero for a scientifically passing gate or 2 with preserved raw +failures; infrastructure errors return 1. During implementation, tests use a +constructor with reduced sample counts, while the CLI refuses reduced counts. + +- [ ] **Step 8: Commit validation gate** + +```bash +git add src/long_range_percolation/validation.py \ + tests/test_validation.py scripts/validate_production.py +git commit -m "Add fixed production validation gate" +``` + +--- + +### Task 9: Immutable Atomic Trajectory and Batch Artifacts + +**Files:** +- Create: `src/long_range_percolation/artifacts.py` +- Create: `tests/test_artifacts.py` + +**Interfaces:** +- Produces + `publish_trajectory(run_dir: Path, request: TrajectoryRequest, result: TrajectoryResult, provenance: dict[str, object]) -> Path`. +- Produces + `publish_batch_manifest(run_dir: Path, batch_id: str, trajectory_paths: Sequence[Path]) -> Path`. +- Produces + `load_verified_trajectory(path: Path, expected: dict[str, str]) -> TrajectoryResult`. +- Produces + `reconstruct_progress(run_dir: Path, expected: dict[str, str]) -> dict[str, object]`. +- Trajectories are HDF5; immutable batch manifests and progress are canonical + JSON. A batch manifest references trajectory hashes and never aggregates + away a trajectory. + +- [ ] **Step 1: Write failing schema round-trip and immutability tests** + +```python +def test_trajectory_round_trip_preserves_complete_resampling_unit(tmp_path): + path = publish_trajectory(tmp_path, request, result, provenance) + loaded = load_verified_trajectory(path, expected_hashes) + np.testing.assert_array_equal(loaded.observables, result.observables) + np.testing.assert_array_equal(loaded.terminal_counters, result.terminal_counters) + np.testing.assert_array_equal(loaded.draw_counts, result.draw_counts) + with pytest.raises(FileExistsError): + publish_trajectory(tmp_path, request, result, provenance) +``` + +Assert every requested coupling and all ten basic observables for one +trajectory are in one HDF5 file. Assert fixed little-endian dtypes, exact +dataset shapes, schema/RNG/conversion versions, request/kernel/source/lock +hashes, clean-tree flag, initial and terminal counters, key-material hashes, +draw accounting, hash diagnostics, and whole-file SHA-256 sidecar fields. + +- [ ] **Step 2: Write failing crash-consistency tests** + +Monkeypatch each boundary (`flush`, file `fsync`, semantic reload, hash, +`os.replace`, directory `fsync`) to fail in turn. Before rename, assert no +final path exists and a uniquely named `.partial` remains detectable. After +rename but before directory fsync, assert a durable publication-intent marker +makes reconstruction fail closed. Existing valid files are never removed or +overwritten. + +- [ ] **Step 3: Write failing corruption and restart tests** + +Test truncated HDF5, changed dataset byte, changed request hash, wrong source +revision, dirty-tree provenance, wrong lock/kernel/analysis-plan/RNG hash, +unknown extra final file, stale `.partial`, duplicate trajectory ID, batch +manifest with missing member, and extra valid trajectory absent from all batch +manifests. Every case raises `ArtifactIntegrityError`. + +For a valid directory, delete `progress.json`, call `reconstruct_progress`, +and assert byte-identical regenerated progress from verified immutable +trajectory and batch files only. + +- [ ] **Step 4: Run tests and verify RED** + +Run: `uv run pytest tests/test_artifacts.py -q` + +Expected: collection fails because `artifacts.py` does not exist. + +- [ ] **Step 5: Implement staged publication** + +Use unique partial names produced by +`f".trajectory-{trajectory_id}.{os.getpid()}.{uuid.uuid4().hex}.partial"`. +Before publication, create and fsync a unique intent file containing the +partial/final names and expected semantic hashes, then fsync the directory. +Write HDF5 with track times disabled, flush HDF5, +`os.fsync(file.fileno())`, close, reopen through the semantic loader, hash +bytes, `os.replace(partial, final)`, and fsync the parent directory opened +with `os.O_DIRECTORY`. Only then unlink the intent and fsync the directory +again. Reconstruction rejects every surviving intent marker, so interruption +at any boundary cannot expose a final file as committed. The immutable batch +manifest records the verified whole-file trajectory hashes. Never derive +progress from filenames alone. + +Canonical run paths are: + +```text +request.json +environment.json +kernel/ +seed-manifest.json +capability.json +trajectories/ +batches/ +progress.json +manifest.json +``` + +This subproject does not create `analysis-plan.json`, `derived/`, or +`figures/`; expected hashes may include an explicit +`analysis_plan_sha256 = "not-created-pre-pilot"` value. + +- [ ] **Step 6: Run GREEN and filesystem regressions** + +Run: + +```bash +uv run pytest tests/test_artifacts.py -q +uv run pytest -q +``` + +Expected: every injected crash or corruption fails closed; clean publication +and reconstruction pass. + +- [ ] **Step 7: Commit artifact layer** + +```bash +git add src/long_range_percolation/artifacts.py tests/test_artifacts.py +git commit -m "Add immutable trajectory artifacts" +``` + +--- + +### Task 10: Fresh-Subprocess Performance Gate + +**Files:** +- Create: `src/long_range_percolation/benchmark.py` +- Create: `tests/test_benchmark.py` +- Create: `scripts/benchmark_production.py` + +**Interfaces:** +- Produces + `BenchmarkProtocol.production_v1() -> BenchmarkProtocol`. +- Produces + `run_benchmark(protocol: BenchmarkProtocol, output: Path) -> dict[str, object]`. +- Worker modes are `compile`, `steady`, and `measure-observables`. +- Report schema is `challenge-194-benchmark-v1`. + +- [ ] **Step 1: Write failing frozen-protocol tests** + +Freeze: + +```python +BENCHMARK_LENGTHS = (2**10, 2**14, 2**18) +BENCHMARK_SIGMAS = (0.8, 0.9, 1.0, 1.1) +BENCHMARK_KAPPAS = tuple( + value for value in (0.25 * 1.25**j for j in range(32)) if value <= 6.0 +) +STEADY_RUNS = 5 +WALL_LIMIT_SECONDS = 120.0 +RSS_LIMIT_BYTES = 4 * 1024**3 +GATE_LENGTH = 2**18 +``` + +Assert these values are serialized as binary64 hex strings and cannot be +overridden by CLI flags. Quadratic is benchmarked only through `L=256`; +geometric and Poisson are attempted at every feasible benchmark point, with a +recorded timeout or allocation failure retained rather than omitted. + +- [ ] **Step 2: Write failing subprocess and warmup-separation tests** + +Mock the worker executable and assert: + +1. compilation runs in one fresh subprocess with an empty unique + `NUMBA_CACHE_DIR`; +2. each of five steady runs uses a separate fresh subprocess and a populated, + read-only copy of the compile cache; +3. each steady worker imports modules and executes one untimed `L=2` + signature warmup before starting `perf_counter_ns`; +4. timed work is exactly one target trajectory through all frozen kappas with + basic observables; +5. worker JSON separates startup, cache load/warmup, compile, sampling, + observable, artifact-serialization, wall, CPU, and peak RSS; +6. parent wall timeout and nonzero exits produce visible failed run records. + +- [ ] **Step 3: Write failing metric and gate tests** + +Require raw per-run values for events, unique edges, unions, duplicates, total +and maximum probes, rehashes, bytes, wall, CPU, and RSS. Aggregate median and +maximum without dropping outliers. Gate each `(sigma, L=2**18)` cell on +`max_wall_seconds <= 120.0` and `max_peak_rss_bytes <= 4*1024**3`; the Numba +backend passes only if all four sigma cells and the correctness report pass. +Compilation and warmup are reported but excluded from the 120-second value. + +- [ ] **Step 4: Run tests and verify RED** + +Run: `uv run pytest tests/test_benchmark.py -q` + +Expected: collection fails because `benchmark.py` does not exist. + +- [ ] **Step 5: Implement the worker and orchestrator** + +Use `subprocess.run` with explicit environment: + +```python +{ + "NUMBA_NUM_THREADS": "1", + "OMP_NUM_THREADS": "1", + "OPENBLAS_NUM_THREADS": "1", + "MKL_NUM_THREADS": "1", + "VECLIB_MAXIMUM_THREADS": "1", + "NUMEXPR_NUM_THREADS": "1", + "PYTHONHASHSEED": "0", +} +``` + +On Linux, workers collect `resource.getrusage(RUSAGE_SELF).ru_maxrss * 1024`, +pin to one available CPU with `os.sched_setaffinity`, and report the selected +CPU. If affinity cannot be set, capability fails closed. The parent validates +one JSON object per worker, preserves stdout/stderr and exit status, and +atomically publishes the report through the artifact helper. + +- [ ] **Step 6: Implement and test CLI behavior** + +CLI: + +```text +benchmark_production.py --validation-report PATH --output PATH +``` + +No size, sigma, kappa, repeat, wall, or RSS override is accepted. Exit zero +only when correctness and the frozen gate pass; exit 2 for a measured gate +failure with a complete report; exit 1 for infrastructure failure with +captured diagnostics. + +Run: + +```bash +uv run pytest tests/test_benchmark.py -q +uv run scripts/benchmark_production.py \ + --validation-report ../../../../results/frustration-free/challenge-194/validation-smoke/report.json \ + --output ../../../../results/frustration-free/challenge-194/benchmark-smoke/capability.json +``` + +Expected: unit tests pass. The real command records compile plus all five raw +steady runs at every frozen point and returns according to the measured gate; +it never presents a smoke or reduced protocol as the frozen capability gate. + +- [ ] **Step 7: Commit benchmark harness** + +```bash +git add src/long_range_percolation/benchmark.py \ + tests/test_benchmark.py scripts/benchmark_production.py +git commit -m "Add frozen production benchmark gate" +``` + +--- + +### Task 11: Optimization/Revalidation and Fail-Closed Backend Decision + +**Files:** +- Modify: `src/long_range_percolation/benchmark.py` +- Modify: `tests/test_benchmark.py` +- Create: `scripts/decide_production_backend.py` + +**Interfaces:** +- Produces + `decide_backend(baseline: Path, optimized: Path | None, validation: Path, output: Path) -> dict[str, object]`. +- Decision values are exactly `numba-approved`, `numba-optimization-required`, + `cpp17-fallback-authorized`, and `blocked-invalid-evidence`. +- This task writes a report only; it creates no C++ source, build file, binding, + or C++ test. + +- [ ] **Step 1: Write failing decision-table tests** + +```python +@pytest.mark.parametrize( + ("correct", "baseline_pass", "optimized_present", "optimized_pass", "decision"), + [ + (True, True, False, False, "numba-approved"), + (True, False, False, False, "numba-optimization-required"), + (True, False, True, True, "numba-approved"), + (True, False, True, False, "cpp17-fallback-authorized"), + (False, True, False, False, "blocked-invalid-evidence"), + (False, False, True, False, "blocked-invalid-evidence"), + ], +) +def test_backend_decision_table( + tmp_path, correct, baseline_pass, optimized_present, optimized_pass, decision +): + validation, baseline, optimized = write_decision_fixture( + tmp_path, + correctness_passed=correct, + baseline_passed=baseline_pass, + include_optimized=optimized_present, + optimized_passed=optimized_pass, + ) + report = decide_backend( + baseline, + optimized, + validation, + tmp_path / "decision.json", + ) + assert report["decision"] == decision +``` + +Also reject missing raw runs, fewer than five runs, hidden failures, mismatched +protocol/runtime/source/schema hashes, changed RNG mapping, changed artifact +schema, changed observable columns, or an optimization report that lacks a +fresh full validation report. + +`write_decision_fixture` is a test-only helper in `tests/test_benchmark.py`. +It writes canonical minimal validation and capability JSON using the exact +schemas and hashes defined in Tasks 8 and 10, emits five raw steady runs for +each frozen gate cell, and returns +`tuple[Path, Path, Path | None]`. It changes only the four booleans named in +its signature, so every row exercises one decision-table condition. + +- [ ] **Step 2: Define the bounded optimization ledger** + +The first failed baseline produces +`numba-optimization-required`. An optimization attempt may change only: +array layout, initial hash capacity estimate, allocation reuse, compilation +specialization, and host batching. It must record exact source diff hash and +one or more of those registered categories. It may not change Philox/version, +stream IDs, key derivation, open-uniform conversion, bounded-integer mapping, +event ordering, alias construction, duplicate semantics, edge encoding, +observable definitions/order, retained kappas, or artifact schemas. + +After any optimization, require in order: + +```text +full production validation -> fresh full benchmark -> backend decision +``` + +A second measured failure authorizes only a C++17 planning subproject; no C++ +implementation is part of this task. + +- [ ] **Step 3: Run tests and verify RED** + +Run: +`uv run pytest tests/test_benchmark.py -q -k 'decision or optimization'` + +Expected: failures show `decide_backend` and the decision CLI are absent. + +- [ ] **Step 4: Implement canonical evidence verification and decision report** + +Report keys are exactly: + +```python +{ + "schema_version": "challenge-194-backend-decision-v1", + "decision": decision, + "validation_sha256": validation_sha256, + "baseline_capability_sha256": baseline_sha256, + "optimized_capability_sha256": optimized_sha256_or_none, + "frozen_wall_limit_seconds": 120.0, + "frozen_rss_limit_bytes": 4 * 1024**3, + "failed_cells": failed_cells, + "optimization_categories": optimization_categories, + "semantic_contract_hashes": semantic_contract_hashes, + "cpp17_implementation_present": False, +} +``` + +Any malformed, inconsistent, scientifically failed, or noncanonical input +yields `blocked-invalid-evidence`, a nonzero CLI exit, and preserved reasons. +An authorized fallback report says only that a separately planned C++17 +backend may begin and must pass the same scientific suite. + +- [ ] **Step 5: Run GREEN and full regressions** + +Run: + +```bash +uv run pytest tests/test_benchmark.py -q +uv run pytest -q +uv run scripts/decide_production_backend.py --help +``` + +Expected: decision-table and evidence-integrity tests pass; help lists +`--validation`, `--baseline`, optional `--optimized`, and `--output` only. + +- [ ] **Step 6: Commit decision logic** + +```bash +git add src/long_range_percolation/benchmark.py \ + tests/test_benchmark.py scripts/decide_production_backend.py +git commit -m "Add fail-closed backend decision" +``` + +--- + +### Task 12: Public API, Documentation, and Completion Gate + +**Files:** +- Modify: `src/long_range_percolation/__init__.py` +- Modify: `README.md` +- Modify: `tests/test_runtime.py` + +**Interfaces:** +- Exports `AliasTable`, `BasicObservables`, `StreamIdentity`, + `TrajectoryRequest`, `TrajectoryResult`, `build_distance_alias`, + `derive_stream_material`, `run_poisson_numba`, and + `run_poisson_reference`. +- Documentation exposes correctness, benchmark, and decision commands only; + it contains no pilot, cluster, confirmatory, fit, figure, report-analysis, or + C++ build command. + +- [ ] **Step 1: Write failing export and documentation-scope tests** + +```python +def test_production_public_api_is_explicit(): + expected = { + "AliasTable", "BasicObservables", "StreamIdentity", + "TrajectoryRequest", "TrajectoryResult", "build_distance_alias", + "derive_stream_material", "run_poisson_numba", + "run_poisson_reference", + } + assert expected <= set(long_range_percolation.__all__) + for name in expected: + assert getattr(long_range_percolation, name) is not None + + +def test_readme_stops_at_backend_decision(): + text = Path("README.md").read_text(encoding="utf-8") + assert "validate_production.py" in text + assert "benchmark_production.py" in text + assert "decide_production_backend.py" in text + for forbidden in ("run_pilot.py", "run_production.py", "analyze_production.py", + "CMakeLists.txt", "pybind11"): + assert forbidden not in text +``` + +- [ ] **Step 2: Run tests and verify RED** + +Run: `uv run pytest tests/test_runtime.py -q` + +Expected: production export and README command assertions fail. + +- [ ] **Step 3: Export the frozen API and update README** + +Document: + +1. `uv sync --frozen`; +2. exact Numba and runtime capability inspection; +3. correctness command and exit meanings; +4. benchmark command, five fresh runs, warmup exclusion, and frozen + `120 seconds / 4 GiB` rule; +5. decision command and the meaning of each decision; +6. immutable result root and restart fail-closed behavior; +7. explicit statement that passing this subproject permits later pilot + planning but does not execute a pilot or support a physics conclusion. + +- [ ] **Step 4: Run final verification** + +Run: + +```bash +uv sync --frozen +uv run pytest -q +NUMBA_DISABLE_JIT=1 uv run pytest \ + tests/test_counter_rng.py tests/test_alias.py tests/test_edge_set.py \ + tests/test_production_union_find.py tests/test_poisson_sweep.py -q +uv run python -c 'from long_range_percolation.poisson_sweep import assert_nopython_signatures; assert_nopython_signatures()' +git diff --check +``` + +Expected: all tests pass in normal mode; fixed-dtype semantic tests pass with +JIT disabled; nopython signatures exist; whitespace check exits zero. + +- [ ] **Step 5: Audit committed scope** + +Run: + +```bash +git status --short +git diff --name-only HEAD +git check-ignore \ + ../../../../results/frustration-free/challenge-194/ +``` + +Expected: only files in this plan are changed, result artifacts are ignored, +and no pilot/cluster/production-analysis/C++ file exists. + +- [ ] **Step 6: Commit completion boundary** + +```bash +git add README.md src/long_range_percolation/__init__.py tests/test_runtime.py +git commit -m "Document production engine gates" +``` + +--- + +## Production-Engine Completion Gate + +The subproject is complete only when: + +1. the dependency resolver selected Numba successfully and the exact selected + version is pinned in both `pyproject.toml` and `uv.lock`; +2. published Philox vectors, stream separation, all open-uniform boundaries, + bounded-integer rejection, and exact draw accounting pass; +3. alias, edge-set, union-find, and scripted event invariants pass in compiled + and disabled-JIT modes; +4. the independent Python Poisson semantics and compiled Numba engine remain + structurally independent; +5. all exact `L<=6` and three-way quadratic/geometric/Numba-Poisson families + through `L<=256` pass their frozen familywise thresholds with raw margins; +6. immutable trajectory/batch artifacts survive semantic reload and every + corruption/crash injection fails closed; +7. one complete trajectory at every frozen benchmark cell is measured in five + fresh steady-state subprocesses after separately reported compilation and + warmup; +8. all `L=2**18` sigma cells have maximum steady wall time at most 120 seconds + and maximum peak RSS at most 4 GiB, or one registered optimization pass is + followed by full revalidation and a fresh full benchmark; +9. the final decision is `numba-approved`, + `numba-optimization-required`, `cpp17-fallback-authorized`, or + `blocked-invalid-evidence`, with all evidence hashes and failed cells; +10. no pilot run, cluster submission, confirmatory production, physics fit, + figure, final analysis report, or C++ implementation has been added. + +## Self-Review Record + +- Requirement coverage: all ten requested scope items map to Tasks 1–11; + Task 12 closes API and phase boundaries. +- Marker scan: no unresolved implementation marker or unspecified version + remains. The Numba version is deliberately obtained from + `uv add numba`, printed, copied exactly, and locked rather than invented. +- Interface/type consistency: all cross-task names, shapes, dtypes, stream + indices, observable columns, status codes, report schemas, and decision + values match the Shared Interface and Type Contract. +- Independence review: the Python reference uses cumulative weights, Python + sets, and checkpoint reconstruction; compiled code uses Walker alias, + open-address arrays, and incremental union-find. Statistical validation, + not shared edge-selection code, joins them. +- Numeric review: preflight covers nonfinite inputs; uniforms are open; + rejection accounting includes discarded words; fourth moments use + `float64`; edge-set occupancy is separate from keys and therefore preserves + the complete `uint64` domain. +- Artifact review: flush, file fsync, semantic reload, hash, atomic rename, + directory fsync, immutable collision handling, and reconstruction from + verified files are each explicit and crash-tested. +- Benchmark review: compilation, cache load/warmup, and steady timing are + separate; five raw fresh-process runs are retained; maximum rather than + median enforces both frozen limits. +- Scope review: the plan ends with a fail-closed backend decision report and + explicitly excludes C++ implementation and every later scientific phase. +- Contradiction review: “latest compatible Numba” and “exact pin” are ordered + resolver actions; “fresh subprocess” and “warmup separation” are satisfied + by untimed in-process signature warmup inside each new steady worker; + “one trajectory restart unit” and “batch artifact” are satisfied by immutable + per-trajectory HDF5 plus immutable manifests that reference, never merge, + complete trajectories; the terminal overshoot consumes one exponential draw + but no event-selection draws, consistently in reference, compiled engine, + and accounting tests. diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/README.md b/tracks/qmc/solutions/frustration-free/challenge-194/README.md new file mode 100644 index 000000000..7135607ff --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/README.md @@ -0,0 +1,339 @@ +# Challenge 194: long-range q=1 random-cluster model + +This directory implements the pinned independent-edge finite-ring model from +QuantumBFS/quantum.harness issue #194. It does not use Gori et al.'s +minimum-image `C/r^(1+sigma)` convention. + +## Scope + +The validated production engine now supports the physical Pilot phase. Pilot +data are exploratory window-selection data only; they make no transition, +critical-point, critical-exponent, scaling, or universality claim. + +## Setup + +```bash +uv sync \ + --project tracks/qmc/solutions/frustration-free/challenge-194 \ + --python 3.12 +``` + +## Verify + +```bash +uv run \ + --project tracks/qmc/solutions/frustration-free/challenge-194 \ + pytest -q +``` + +The accelerated sampler is accepted only when it agrees with analytic edge +probabilities and exact small-system partition distributions. + +## Pilot P0 orchestration + +Build the immutable 96-cell run spec on the target compute runtime: + +```bash +uv run scripts/run_pilot.py build-spec \ + --validation-report /absolute/validation/report/report.json \ + --output-root /absolute/shared/pilot-p0 \ + --run-spec /absolute/shared/pilot-p0/run_spec.json +``` + +Inspect pending cells, run one zero-based cell, merge all completed cells, and +verify a downloaded tree: + +```bash +uv run scripts/run_pilot.py pending --run-spec /absolute/shared/pilot-p0/run_spec.json +uv run scripts/run_pilot.py run-cell --run-spec /absolute/shared/pilot-p0/run_spec.json --cell-index 0 +uv run scripts/run_pilot.py merge --run-spec /absolute/shared/pilot-p0/run_spec.json +uv run scripts/run_pilot.py verify --run-spec /downloaded/pilot-p0/run_spec.json +``` + +`scripts/pilot_array_slurm.sh` maps Slurm array IDs `1..96` to cell indices +`0..95`, requires one CPU, and isolates the Numba cache per cell. Task 10's +120-second/4-GiB capability gate and Task 11 optimization were explicitly +waived after correctness validation. The benchmark status is +`cancelled-without-capability-report`; this is not a passed performance gate. + +Download a completed Pilot root with checksummed, partial-safe `rsync`, then +run the local semantic verifier: + +```bash +scripts/download_pilot.sh \ + wuzh02-jiangweiqi \ + /work/share/giggleliu/jiangweiqi/results/challenge-194/pilot-p0-739880d \ + /absolute/local/results/challenge-194/pilot-p0-739880d \ + /absolute/path/to/challenge-194/.venv/bin/python +``` + +The local destination's real non-symlink parent must already exist, and the +destination must be absent, empty, or the same resumable download. Equivalent +absolute lexical spellings (repeated separators, `.` components, or trailing +slashes) share one normalized destination and sibling state; `/` is rejected. +The script never deletes source or destination files. It atomically claims a +sibling `.download-claim` directory and stores no-clobber source, +verified-completion, and uniquely created transfer-log files under the real +non-symlink sibling `.download-state` directory. A completed root +is only reverified: `rsync` is never invoked again. Unexpected claims are +preserved for diagnosis, and all transfer-generated logs remain outside the +immutable Pilot root. + +Legacy roots with the former sibling `.download-source` marker are verified +before either normal source or completion state is published. Failed legacy +verification writes only a read-only diagnostic under the external state +directory; retries verify again without invoking `rsync`. + +## P0 analysis and P1 publication boundary + +The frozen P0 run spec binds historical `PILOT_PLAN.md` bytes, so current +`HEAD` intentionally cannot republish its aggregation. Reproduce the installed +analysis only from the exact historical publisher revision: + +```bash +git worktree add --detach /tmp/challenge-194-p0-analysis-143d35a \ + 143d35ac52923cff2d24c43d304a75c2d04d3c66 +cd /tmp/challenge-194-p0-analysis-143d35a/tracks/qmc/solutions/frustration-free/challenge-194 +uv run python scripts/analyze_pilot.py analyze --run-spec \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-739880d/run_spec.json \ + --output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json +``` + +That exact command returns `verified-existing` with analysis-document SHA256 +`e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8`. +Current combined-v2 commands instead use the supported historical boundary: +they deeply verify all frozen P0 cells and progress while requiring both exact +P0 root hashes (`d17d3d...` run spec and `ea29a8...` progress) and the exact +canonical P0 analysis file hash. The immutable analysis's embedded SHA256 is +`e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8`; +the SHA256 of the complete canonical file is +`44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b`. +Inspect both without changing the artifact: + +```bash +sha256sum /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json +uv run python -c 'import json,sys; print(json.load(open(sys.argv[1], encoding="utf-8"))["analysis_document_sha256"])' \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json +``` + +The implemented P1 build command is: + +```bash +uv run python scripts/analyze_pilot.py build-p1 --analysis \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json \ + --output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p1_protocol.json +``` + +For the current P0 evidence it exits nonzero with +`P0 extension required before P1 publication: 0.9, 1.0`. +`p1_protocol.json does not exist`; P1 has not been published or executed. +Do not run the verifier until a future successful build has created the +protocol. After that successful build, the implemented verification command +is: + +```bash +uv run python scripts/analyze_pilot.py verify --analysis \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json \ + --p1-protocol /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p1_protocol.json +``` + +## Frozen P0 extension construction and execution + +The versioned P0 extension is complete and deeply verified: 96 cells and 96 +trajectories. It samples only sigma `0.9` and `1.0`; it does not alter +the scientific engine, relax the selector, or authorize a claim. + +The checked-in `pilot_correctness_approval.json` authenticates approval/source +revision `877ab9393f320bfe31ff74a26c3db1fb205d7ef3` and package +`validation-prod-877ab93`: report SHA256 +`036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8`, +run-spec SHA256 +`5b3eea4c460e14a57aec9df606447137d787a5c66dd7e98e1dffdcf566f430e2`, +protocol SHA256 +`c7e980eeadaf8ed75e4d20cebb1e2c5d5f57a1cfc329afa7678ae586f5b7f488`, +check-registry SHA256 +`6e25ea41899544f2a9de3589beb1ee94b1f3dc505638b8f8e5164a4322b56a1d`, +and scientific-engine SHA256 +`457fa669da897e59b03681039db6121fde4d7be9295bb46a743c8448875b3ee9`. +Wuzh02 execution uses the repository-root interpreter proven by successful P0 +job `41506576`: +`/work/share/giggleliu/jiangweiqi/quantum.harness-challenge-194/.venv/bin/python`. + +From the exact clean deployed repository, the compute-node build wrapper uses +`HARNESS_RUN_SPEC` as the canonical P0 analysis path and derives the protocol, +approved validation report, and output root from its parent: + +```bash +sbatch \ + --export=ALL,HARNESS_RUN_SPEC=/absolute/results/challenge-194/p0_analysis.json,HARNESS_ENTRYPOINT=/absolute/deployed/quantum.harness,HARNESS_COMMAND=/absolute/offline/python \ + scripts/pilot_extension_build_slurm.sh +``` + +The wrapper verifies canonical analysis-file SHA256 +`44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b` +and runs these implemented commands: + +```bash +/absolute/offline/python scripts/analyze_pilot.py build-p0-extension \ + --analysis /absolute/results/challenge-194/p0_analysis.json \ + --p0-evidence-root /absolute/results/challenge-194/pilot-p0-739880d \ + --output /absolute/results/challenge-194/p0_extension_v1_protocol.json + +/absolute/offline/python scripts/run_pilot.py build-extension-spec \ + --protocol /absolute/results/challenge-194/p0_extension_v1_protocol.json \ + --validation-report /absolute/results/challenge-194/validation-prod-877ab93/report/report.json \ + --analysis /absolute/results/challenge-194/p0_analysis.json \ + --p0-evidence-root /absolute/results/challenge-194/pilot-p0-739880d \ + --output-root /absolute/results/challenge-194/pilot-p0-extension-v1 \ + --run-spec /absolute/results/challenge-194/pilot-p0-extension-v1/run_spec.json +``` + +`--p0-evidence-root` is mandatory at both construction stages. It must be an +absolute canonical non-symlink directory containing the frozen +`pilot-p0-739880d/run_spec.json` and `progress.json` bytes. These artifacts are +never inferred from a checkout-local, gitignored `results/` tree; the canonical +P0 analysis remains the separate explicit `--analysis` input. + +After the build succeeds, submit the immutable worker root with exact +one-CPU, 1800-MiB, 40-minute resources. Slurm IDs `1..96` map to zero-based +cell indices. The wrapper accepts only canonical decimal IDs in that range +before performing arithmetic: + +```bash +sbatch --array=1-2%2 \ + --export=ALL,HARNESS_RUN_SPEC=/absolute/results/challenge-194/pilot-p0-extension-v1/run_spec.json,HARNESS_ENTRYPOINT=/absolute/deployed/quantum.harness,HARNESS_COMMAND=/absolute/offline/python \ + scripts/pilot_extension_array_slurm.sh + +sbatch --array=3-32,49-80%16 \ + --export=ALL,HARNESS_RUN_SPEC=/absolute/results/challenge-194/pilot-p0-extension-v1/run_spec.json,HARNESS_ENTRYPOINT=/absolute/deployed/quantum.harness,HARNESS_COMMAND=/absolute/offline/python \ + scripts/pilot_extension_array_slurm.sh + +sbatch --array=33-48,81-96%8 \ + --export=ALL,HARNESS_RUN_SPEC=/absolute/results/challenge-194/pilot-p0-extension-v1/run_spec.json,HARNESS_ENTRYPOINT=/absolute/deployed/quantum.harness,HARNESS_COMMAND=/absolute/offline/python \ + scripts/pilot_extension_array_slurm.sh +``` + +The same schema-dispatched CLI reports pending cells, runs cells, merges all +96 results, and verifies a downloaded extension: + +```bash +/absolute/offline/python scripts/run_pilot.py pending --run-spec /absolute/results/challenge-194/pilot-p0-extension-v1/run_spec.json +/absolute/offline/python scripts/run_pilot.py run-cell --run-spec /absolute/results/challenge-194/pilot-p0-extension-v1/run_spec.json --cell-index 0 +/absolute/offline/python scripts/run_pilot.py merge --run-spec /absolute/results/challenge-194/pilot-p0-extension-v1/run_spec.json +/absolute/offline/python scripts/run_pilot.py verify --run-spec /absolute/download/pilot-p0-extension-v1/run_spec.json +``` + +After extension evidence exists and has been downloaded, run the immutable +local analysis workflow below from this solution directory. +The extension analysis is recomputed from its verified run root. The historical +P0 analysis is authenticated by the exact dual root hashes and exact canonical +analysis bytes described above; it is not recomputed under current `HEAD`. + +```bash +uv run python scripts/run_pilot.py verify --run-spec \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-extension-v1/run_spec.json + +uv run python scripts/analyze_pilot.py analyze-extension --run-spec \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-extension-v1/run_spec.json \ + --protocol /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_extension_v1_protocol.json \ + --output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_extension_v1_analysis.json +``` + +After the extension recomputation returns `verified-existing`, combine and +select the fully authenticated evidence: + +```bash +uv run python scripts/analyze_pilot.py combine --p0-analysis \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json \ + --extension-analysis /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_extension_v1_analysis.json \ + --p0-evidence-root /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-739880d \ + --extension-run-spec /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-extension-v1/run_spec.json \ + --extension-protocol /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_extension_v1_protocol.json \ + --output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_combined_analysis_v2.json + +uv run python scripts/analyze_pilot.py select --analysis \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_combined_analysis_v2.json \ + --p0-analysis /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json \ + --extension-analysis /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_extension_v1_analysis.json \ + --p0-evidence-root /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-739880d \ + --extension-run-spec /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-extension-v1/run_spec.json \ + --extension-protocol /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_extension_v1_protocol.json \ + --output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_combined_brackets_v2.json +``` + +The combined-v2 selector and builder never trust combined JSON alone. Both +commands require the exact P0 and extension analyses and recompute full source +validation. If every combined bracket is selected, build P1 with the same +five trusted inputs: + +```bash +uv run python scripts/analyze_pilot.py build-p1 --analysis \ + /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_combined_analysis_v2.json \ + --p0-analysis /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json \ + --extension-analysis /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_extension_v1_analysis.json \ + --p0-evidence-root /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-739880d \ + --extension-run-spec /home/footman/code/quantum.harness-challenge-194/results/challenge-194/pilot-p0-extension-v1/run_spec.json \ + --extension-protocol /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_extension_v1_protocol.json \ + --output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p1_protocol.json +``` + +`combine`, combined-v2 `select`, and combined-v2 `build-p1` require all five +trusted inputs shown above. They deeply verify both roots, validate the exact +immutable extension protocol, recompute extension analysis, and require +byte-identical supplied extension analysis before validating combined evidence. +All descriptor reads are bounded and canonical; publication is immutable and +no-clobber. A byte-identical retry returns +`verified-existing`; changed installed bytes, malformed canonical input, or a +scientific refusal exits nonzero without replacing or newly creating output. +The legacy P0-analysis-v1 `build-p1 --analysis ... --output ...` form remains +available only without either combined-source option. Mixed or extraneous +source arguments fail closed. + +The completed extension root verifies 96 cells and 96 trajectories. Its +protocol file SHA256 is +`e363a60f842b11b32972c7a68ec1c5f237741bc45bc79ab8bf93f51f6760d84d`, +embedded protocol SHA256 is +`a37ab41f3224594e61f4eebbe292975aeec449b9ecb7893e3e54f18d82d53321`, +run-spec SHA256 is +`c1ca9b6c8ba751919c6d9337fe1cd4c09a57ed9b99abbb9d3ebfed7f89c3d32e`, +and progress SHA256 is +`c78d1fb03daf19297ef9e0617410c68a6a364bffc2f2888dfa9067e7e8d6b65f`. +Task 11 observed the following immutable analysis identities: + +- extension analysis: document SHA256 + `79232574d314348c29a40cd2fbb7690e96f3cae5f26843bd4f1cf07cb6a1f45b`, + file SHA256 + `d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5`, + with 102 estimate rows; +- combined analysis: document SHA256 + `36f85c40e9159ef2e69742672c261769fb28d2f3c947780ba63e4ef5fe5975c3`, + file SHA256 + `6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929`, + with 282 estimate rows; +- combined brackets: document SHA256 + `098f19d8883097d5f1f274ce759416328c086958fa5301c034a0b46dcbd562df`, + file SHA256 + `7a84d545b4526d94aa6f93ca4f0d264dcf01e518f2f9b04383921634786c9962`. + +The exact selected sigma `0.8` window remains +`[0x1.f400000000000p-2, 0x1.3880000000000p-1]`, and the sigma `1.1` +crossover window remains +`[0x1.312d000000000p+0, 0x1.7d78400000000p+0]`. Sigma `0.9` and `1.0` +both remain `requires_p0_extension` with reason +`no_nonzero_interval_marked_by_both_estimators`. Therefore acceptance checks +4 and 6 fail, `requires_p0_extension` is true, `p1_protocol.json` remains +absent, and P1 is unresolved. No sampling or selection rule was changed. + +Publication is no-clobber. Repeating `analyze` or a future successful +`build-p1` against byte-identical output returns `verified-existing`. +Different installed bytes fail closed rather than being replaced. Pilot cell +restart preserves `.partial` and `.intent` diagnostics, resumes only verified +immutable batches, and deeply verifies an existing completed cell. A completed +download is reverified without rerunning `rsync`. + +## Design and references + +- `DESIGN.md` pins the scientific and statistical protocol. +- `PLAN.md` records the test-driven implementation sequence. +- `PILOT_PLAN.md` freezes P0, provenance, resource, restart, and P1 boundaries. +- `references/README.md` records source URLs and SHA256 hashes. diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/assets/challenge-194-selector-evidence.svg b/tracks/qmc/solutions/frustration-free/challenge-194/assets/challenge-194-selector-evidence.svg new file mode 100644 index 000000000..0b6e93a51 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/assets/challenge-194-selector-evidence.svg @@ -0,0 +1,1064 @@ + + +Challenge 194 selector evidence for sigma 0.9 and 1.0 +Authenticated P0 plus extension v1 Q_G and four-sector means, standard errors, and unchanged interval marks; both sigma values remain unresolved. + +挑战194:sigma 0.9 / 1.0 的 selector 实证 +误差条:均值 ± 1 standard error;选择器只使用均值,不使用误差条救援。 + +sigma = 0.9 · Q_G +1.08 +-0.08 +0.25 +5.684 +kappa + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Q_G marks + + + + + +four-sector marks +共同标记区间:无 + +sigma = 0.9 · four-sector crossing +1.08 +-0.08 +0.25 +5.684 +kappa + +0.25 + +0.75 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Q_G marks + + + + + +four-sector marks +共同标记区间:无 + +sigma = 1.0 · Q_G +1.08 +-0.08 +0.25 +5.684 +kappa + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Q_G marks + + + + + + +four-sector marks + +共同标记区间:无 + +sigma = 1.0 · four-sector crossing +1.08 +-0.08 +0.25 +5.684 +kappa + +0.25 + +0.75 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Q_G marks + + + + + + +four-sector marks + +共同标记区间:无 + + +L=16384 + +L=262144 + +Q_G interval mark + +four-sector interval mark +状态:sigma = 0.9 与 sigma = 1.0 均为 requires_p0_extension; +原因:no_nonzero_interval_marked_by_both_estimators。 +来源 p0_combined_analysis_v2.json · SHA256 6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929 +来源 p0_combined_brackets_v2.json · SHA256 7a84d545b4526d94aa6f93ca4f0d264dcf01e518f2f9b04383921634786c9962 +P0 source p0_analysis.json · SHA256 44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b +extension-v1 source p0_extension_v1_analysis.json · SHA256 d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5 +Boundary: exploratory selector evidence only; not transition, scaling, exponent, or universality evidence. + diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/assets/challenge-194-workflow-status.svg b/tracks/qmc/solutions/frustration-free/challenge-194/assets/challenge-194-workflow-status.svg new file mode 100644 index 000000000..4ae40d967 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/assets/challenge-194-workflow-status.svg @@ -0,0 +1,47 @@ + + +Challenge 194 evidence workflow and unresolved status +Authenticated workflow ends at unresolved sigma 0.9 and 1.0 and P1 not published or run. Partial extension-v2 workspace files are outside the evidence chain. + + +挑战194:证据工作流与停止边界 + +Issue #194 +model contract + + +correctness approval +authenticated + + +P0 complete +exploratory · 96 cells + + +extension v1 complete +96 cells / 96 trajectories + + +combined selector +mean-based rules + + +sigma=0.9,1.0 +unresolved + + +P1 +未发布 / 未运行 +reason: no_nonzero_interval_marked_by_both_estimators + +当前工作区:extension-v2 局部未提交工作 +不属于本报告证据链;未声称运行 +受保护 dirty 路径数:5 +来源 pilot_correctness_approval.json · SHA256 29dc5d04fd18728ee46fffe90c70d98caa61032005974f354e2b4e0e6018a7ab +来源 p0_analysis.json · SHA256 44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b +来源 p0_extension_v1_protocol.json · SHA256 e363a60f842b11b32972c7a68ec1c5f237741bc45bc79ab8bf93f51f6760d84d +来源 p0_extension_v1_analysis.json · SHA256 d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5 +来源 p0_combined_analysis_v2.json · SHA256 6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929 +来源 p0_combined_brackets_v2.json · SHA256 7a84d545b4526d94aa6f93ca4f0d264dcf01e518f2f9b04383921634786c9962 +P0 / extension v1 are exploratory only; no P1, confirmatory, scaling, eta, or nu result follows. + diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/pilot_correctness_approval.json b/tracks/qmc/solutions/frustration-free/challenge-194/pilot_correctness_approval.json new file mode 100644 index 000000000..8e40b202c --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/pilot_correctness_approval.json @@ -0,0 +1 @@ +{"approval_revision":"877ab9393f320bfe31ff74a26c3db1fb205d7ef3","cell_count":120,"check_count":22755,"check_registry_sha256":"6e25ea41899544f2a9de3589beb1ee94b1f3dc505638b8f8e5164a4322b56a1d","protocol_sha256":"c7e980eeadaf8ed75e4d20cebb1e2c5d5f57a1cfc329afa7678ae586f5b7f488","report_sha256":"036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8","run_spec_sha256":"5b3eea4c460e14a57aec9df606447137d787a5c66dd7e98e1dffdcf566f430e2","schema_version":"challenge-194-pilot-correctness-approval-v1","scientific_engine_sha256":"457fa669da897e59b03681039db6121fde4d7be9295bb46a743c8448875b3ee9","validation_source_revision":"877ab9393f320bfe31ff74a26c3db1fb205d7ef3"} diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/pyproject.toml b/tracks/qmc/solutions/frustration-free/challenge-194/pyproject.toml new file mode 100644 index 000000000..d42e217c0 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/pyproject.toml @@ -0,0 +1,24 @@ +[project] +name = "challenge-194-long-range-percolation" +version = "0.1.0" +requires-python = "==3.12.*" +dependencies = [ + "h5py==3.14.0", + "numba==0.66.0", + "numpy==2.2.6", + "scipy==1.15.3", +] + +[dependency-groups] +dev = ["pytest>=8.3,<9"] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/long_range_percolation"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "-ra" diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/references/README.md b/tracks/qmc/solutions/frustration-free/challenge-194/references/README.md new file mode 100644 index 000000000..a07b0bbd6 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/references/README.md @@ -0,0 +1,56 @@ +# Challenge 194 references + +Downloaded on 2026-07-29 for the long-range random-cluster/percolation +challenge. The PDF hashes below bind the local copies to the cited inputs. + +## Local papers + +- `papers/gori-2017-long-range-percolation-arxiv1610.00200.pdf` + - G. Gori et al., *One-dimensional long-range percolation: A numerical study* + - + - SHA256 `95fb2bbc00550f6d500cfa45b6149223bd2e43cbb1d2ded12e196c45c0066584` +- `papers/duminil-copin-2024-long-range-1d-arxiv2011.04642.pdf` + - H. Duminil-Copin, C. Garban, and V. Tassion, + *Long-range models in 1D revisited* + - + - SHA256 `44d0d458832ac87e1bb62599c9d378b2993da2ef41d061d82a0fccd8b1eec7b0` +- `papers/luijten-2001-inverse-square-criticality-cond-mat0104175.pdf` + - E. Luijten and H. Meßingfeld, + *Criticality in one dimension with inverse square-law potentials* + - + - SHA256 `127e1879b631663e7b8ca0157bb5474bf3b7ea346d9b4b466ca3c7b2d9b1fa33` +- `papers/aizenman-newman-1986-inverse-square-percolation.pdf` + - M. Aizenman and C. M. Newman, + *Discontinuity of the percolation density in one dimensional + 1/|x-y|^2 percolation models* + - DOI + - SHA256 `07fc91386b60a8b364e779c2145880b3dcca7e5a00dea3d89c0dfd9ffcc1e051` + +The Gori arXiv source archive is stored as +`sources/gori-2017-arxiv-source.tar`, SHA256 +`a56b61aefa9cfad5be88138c397eda735f8264497e81d72a84c03b64e740669e`. + +## Citation-only inputs + +The following publisher copies were not downloaded because an open PDF was +not confirmed: + +- J. L. Cardy, *One-dimensional models with 1/r^2 interactions*, + DOI . +- C. M. Newman and L. S. Schulman, + *One dimensional 1/|j-i|^s percolation models: The existence of a + transition for s <= 2*, DOI . + +## External code + +No official implementation for Gori et al. was found. A related GPL-3.0 +long-range Monte Carlo implementation was cloned for algorithm study only: + +- repository: +- revision: `28d4b5d85e80590460ec1d80c73ea5337d2fcf93` +- local ignored path: + `tracks/qmc/results/frustration-free/challenge-194/external-code/ONMC` + +ONMC does not implement the pinned q=1 periodic-image percolation model and +must not be treated as an independent scientific oracle. No code is copied +from it into the solution without a separate license and algorithm review. diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/aizenman-newman-1986-inverse-square-percolation.pdf b/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/aizenman-newman-1986-inverse-square-percolation.pdf new file mode 100644 index 000000000..55871170c Binary files /dev/null and b/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/aizenman-newman-1986-inverse-square-percolation.pdf differ diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/duminil-copin-2024-long-range-1d-arxiv2011.04642.pdf b/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/duminil-copin-2024-long-range-1d-arxiv2011.04642.pdf new file mode 100644 index 000000000..155779b94 Binary files /dev/null and b/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/duminil-copin-2024-long-range-1d-arxiv2011.04642.pdf differ diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/gori-2017-long-range-percolation-arxiv1610.00200.pdf b/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/gori-2017-long-range-percolation-arxiv1610.00200.pdf new file mode 100644 index 000000000..4a720e562 Binary files /dev/null and b/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/gori-2017-long-range-percolation-arxiv1610.00200.pdf differ diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/luijten-2001-inverse-square-criticality-cond-mat0104175.pdf b/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/luijten-2001-inverse-square-criticality-cond-mat0104175.pdf new file mode 100644 index 000000000..f9abbe6f4 Binary files /dev/null and b/tracks/qmc/solutions/frustration-free/challenge-194/references/papers/luijten-2001-inverse-square-criticality-cond-mat0104175.pdf differ diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/references/sources/gori-2017-arxiv-source.tar b/tracks/qmc/solutions/frustration-free/challenge-194/references/sources/gori-2017-arxiv-source.tar new file mode 100644 index 000000000..c303824de Binary files /dev/null and b/tracks/qmc/solutions/frustration-free/challenge-194/references/sources/gori-2017-arxiv-source.tar differ diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py new file mode 100755 index 000000000..06d5cb538 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/analyze_pilot.py @@ -0,0 +1,378 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import sys +from collections.abc import Mapping +from pathlib import Path + +from long_range_percolation.artifacts import ( + _publish_json_once, + _read_canonical_json, + _verify_installed_bytes, +) +from long_range_percolation.pilot_analysis import ( + ANALYSIS_SCHEMA, + COMBINED_ANALYSIS_SCHEMA, + EXTENSION_ANALYSIS_SCHEMA, + P1_PROTOCOL_SCHEMA, + aggregate_p0, + aggregate_p0_extension, + build_p1_protocol, + select_p1_brackets, + validate_p1_protocol, +) +from long_range_percolation.pilot_analysis import ( + _canonical_bytes as _analysis_canonical_bytes, +) +from long_range_percolation.pilot_extension import ( + EXTENSION_PROTOCOL_SCHEMA, + build_p0_extension_protocol, + combine_p0_evidence, +) + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Publish and verify Challenge 194 Pilot analysis artifacts." + ) + commands = parser.add_subparsers(dest="command", required=True) + + analyze = commands.add_parser("analyze") + analyze.add_argument("--run-spec", type=Path, required=True) + analyze.add_argument("--output", type=Path, required=True) + + analyze_extension = commands.add_parser("analyze-extension") + analyze_extension.add_argument("--run-spec", type=Path, required=True) + analyze_extension.add_argument("--protocol", type=Path, required=True) + analyze_extension.add_argument("--output", type=Path, required=True) + + combine = commands.add_parser("combine") + combine.add_argument("--p0-analysis", type=Path, required=True) + combine.add_argument("--extension-analysis", type=Path, required=True) + combine.add_argument("--p0-evidence-root", type=Path, required=True) + combine.add_argument("--extension-run-spec", type=Path, required=True) + combine.add_argument("--extension-protocol", type=Path, required=True) + combine.add_argument("--output", type=Path, required=True) + + select = commands.add_parser("select") + select.add_argument("--analysis", type=Path, required=True) + select.add_argument("--p0-analysis", type=Path) + select.add_argument("--extension-analysis", type=Path) + select.add_argument("--p0-evidence-root", type=Path) + select.add_argument("--extension-run-spec", type=Path) + select.add_argument("--extension-protocol", type=Path) + select.add_argument("--output", type=Path, required=True) + + build = commands.add_parser("build-p1") + build.add_argument("--analysis", type=Path, required=True) + build.add_argument("--p0-analysis", type=Path) + build.add_argument("--extension-analysis", type=Path) + build.add_argument("--p0-evidence-root", type=Path) + build.add_argument("--extension-run-spec", type=Path) + build.add_argument("--extension-protocol", type=Path) + build.add_argument("--output", type=Path, required=True) + + extension = commands.add_parser("build-p0-extension") + extension.add_argument("--analysis", type=Path, required=True) + extension.add_argument("--p0-evidence-root", type=Path, required=True) + extension.add_argument("--output", type=Path, required=True) + + verify = commands.add_parser("verify") + verify.add_argument("--analysis", type=Path, required=True) + verify.add_argument("--p1-protocol", type=Path, required=True) + return parser + + +def _mapping_document(path: Path, description: str) -> Mapping[str, object]: + document = _read_canonical_json(path, description) + if not isinstance(document, Mapping): + raise TypeError(f"{description} is not a JSON object") + return document + + +def _publish_or_verify( + path: Path, + document: Mapping[str, object], + schema: str, +) -> str: + try: + _publish_json_once(path, dict(document), schema) + except FileExistsError: + _verify_installed_bytes( + path, + _analysis_canonical_bytes(document), + "published JSON artifact", + ) + return "verified-existing" + return "published" + + +def _combined_command_sources( + arguments: argparse.Namespace, + source: Mapping[str, object], +) -> tuple[ + Mapping[str, object] | None, + Mapping[str, object] | None, + Path | None, + Path | None, + Mapping[str, object] | None, +]: + p0_path = arguments.p0_analysis + extension_path = arguments.extension_analysis + p0_evidence_root = arguments.p0_evidence_root + extension_run_spec = arguments.extension_run_spec + extension_protocol_path = arguments.extension_protocol + if source.get("schema_version") == COMBINED_ANALYSIS_SCHEMA: + if ( + p0_path is None + or extension_path is None + or p0_evidence_root is None + or extension_run_spec is None + or extension_protocol_path is None + ): + raise RuntimeError( + "combined-v2 command requires explicit --p0-analysis, " + "--extension-analysis, --p0-evidence-root, " + "--extension-run-spec, and --extension-protocol" + ) + return ( + _mapping_document(p0_path.resolve(), "P0 analysis document"), + _mapping_document( + extension_path.resolve(), "P0 extension analysis document" + ), + p0_evidence_root.resolve(), + extension_run_spec.resolve(), + _mapping_document( + extension_protocol_path.resolve(), + "immutable P0 extension protocol document", + ), + ) + if source.get("schema_version") == ANALYSIS_SCHEMA: + if any( + value is not None + for value in ( + p0_path, + extension_path, + p0_evidence_root, + extension_run_spec, + extension_protocol_path, + ) + ): + raise RuntimeError( + f"v1 {arguments.command} does not accept combined trusted inputs" + ) + return None, None, None, None, None + if any( + value is not None + for value in ( + p0_path, + extension_path, + p0_evidence_root, + extension_run_spec, + extension_protocol_path, + ) + ): + raise RuntimeError("analysis schema and source arguments are incompatible") + return None, None, None, None, None + + +def _publication_schema(document: Mapping[str, object]) -> str: + schema = document.get("schema_version") + if not isinstance(schema, str): + raise RuntimeError("published document schema is malformed") + return schema + + +def main(argv: list[str] | None = None) -> int: + arguments = _parser().parse_args(argv) + try: + if arguments.command == "analyze": + document = aggregate_p0(arguments.run_spec.resolve()) + publication = _publish_or_verify( + arguments.output.resolve(), + document, + ANALYSIS_SCHEMA, + ) + result = { + "status": "analyzed", + "publication": publication, + "output": str(arguments.output.resolve()), + "analysis_document_sha256": document["analysis_document_sha256"], + } + elif arguments.command == "analyze-extension": + protocol = _mapping_document( + arguments.protocol.resolve(), "P0 extension protocol document" + ) + document = aggregate_p0_extension( + arguments.run_spec.resolve(), + protocol, + ) + publication = _publish_or_verify( + arguments.output.resolve(), + document, + EXTENSION_ANALYSIS_SCHEMA, + ) + result = { + "status": "analyzed", + "publication": publication, + "output": str(arguments.output.resolve()), + "analysis_document_sha256": document["analysis_document_sha256"], + } + elif arguments.command == "combine": + p0_source = _mapping_document( + arguments.p0_analysis.resolve(), "P0 analysis document" + ) + extension_source = _mapping_document( + arguments.extension_analysis.resolve(), + "P0 extension analysis document", + ) + extension_protocol = _mapping_document( + arguments.extension_protocol.resolve(), + "immutable P0 extension protocol document", + ) + document = combine_p0_evidence( + p0_source, + extension_source, + p0_evidence_root=arguments.p0_evidence_root.resolve(), + extension_run_spec=arguments.extension_run_spec.resolve(), + extension_protocol=extension_protocol, + ) + publication = _publish_or_verify( + arguments.output.resolve(), + document, + COMBINED_ANALYSIS_SCHEMA, + ) + result = { + "status": "combined", + "publication": publication, + "output": str(arguments.output.resolve()), + "analysis_document_sha256": document["analysis_document_sha256"], + } + elif arguments.command == "select": + source = _mapping_document( + arguments.analysis.resolve(), "P0 analysis document" + ) + ( + p0_source, + extension_source, + p0_evidence_root, + extension_run_spec, + extension_protocol, + ) = _combined_command_sources( + arguments, + source, + ) + if p0_source is None: + document = select_p1_brackets(source) + else: + document = select_p1_brackets( + source, + p0_analysis=p0_source, + extension_analysis=extension_source, + p0_evidence_root=p0_evidence_root, + extension_run_spec=extension_run_spec, + extension_protocol=extension_protocol, + ) + publication = _publish_or_verify( + arguments.output.resolve(), + document, + _publication_schema(document), + ) + result = { + "status": "selected", + "publication": publication, + "output": str(arguments.output.resolve()), + "bracket_document_sha256": document["bracket_document_sha256"], + } + elif arguments.command == "build-p0-extension": + source = _mapping_document( + arguments.analysis.resolve(), "P0 analysis document" + ) + document = build_p0_extension_protocol( + source, + arguments.p0_evidence_root, + ) + publication = _publish_or_verify( + arguments.output.resolve(), + document, + EXTENSION_PROTOCOL_SCHEMA, + ) + result = { + "status": "ready", + "publication": publication, + "output": str(arguments.output.resolve()), + "protocol_sha256": document["protocol_sha256"], + } + elif arguments.command == "build-p1": + source = _mapping_document( + arguments.analysis.resolve(), "P0 analysis document" + ) + ( + p0_source, + extension_source, + p0_evidence_root, + extension_run_spec, + extension_protocol, + ) = _combined_command_sources( + arguments, + source, + ) + if p0_source is None: + brackets = select_p1_brackets(source) + document = build_p1_protocol(source, brackets) + else: + brackets = select_p1_brackets( + source, + p0_analysis=p0_source, + extension_analysis=extension_source, + p0_evidence_root=p0_evidence_root, + extension_run_spec=extension_run_spec, + extension_protocol=extension_protocol, + ) + document = build_p1_protocol( + source, + brackets, + p0_analysis=p0_source, + extension_analysis=extension_source, + p0_evidence_root=p0_evidence_root, + extension_run_spec=extension_run_spec, + extension_protocol=extension_protocol, + ) + publication = _publish_or_verify( + arguments.output.resolve(), + document, + P1_PROTOCOL_SCHEMA, + ) + result = { + "status": "ready", + "publication": publication, + "output": str(arguments.output.resolve()), + "protocol_sha256": document["protocol_sha256"], + } + else: + source = _mapping_document( + arguments.analysis.resolve(), "P0 analysis document" + ) + protocol = _mapping_document( + arguments.p1_protocol.resolve(), "P1 protocol document" + ) + validate_p1_protocol(source, protocol) + result = { + "status": "verified", + "protocol_sha256": protocol["protocol_sha256"], + } + except Exception as error: # noqa: BLE001 - CLI converts failures to status 1 + print( + f"pilot analysis failure: {type(error).__name__}: {error}", + file=sys.stderr, + flush=True, + ) + return 1 + print(json.dumps(result, sort_keys=True), flush=True) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/benchmark_production.py b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/benchmark_production.py new file mode 100644 index 000000000..b442f64cb --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/benchmark_production.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +from long_range_percolation.benchmark import main + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/download_pilot.sh b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/download_pilot.sh new file mode 100755 index 000000000..2217a3bfd --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/download_pilot.sh @@ -0,0 +1,260 @@ +#!/bin/bash +set -euo pipefail + +usage_error() { + echo "$1" >&2 + exit 64 +} + +if (( $# != 4 )); then + usage_error "usage: download_pilot.sh " +fi + +SSH_HOST="$1" +REMOTE_ROOT="$2" +LOCAL_ROOT="$3" +PYTHON="$4" + +if [[ -z "${SSH_HOST}" ]]; then + usage_error "ssh-host must not be empty" +fi +if [[ "${REMOTE_ROOT}" != /* ]]; then + usage_error "remote root must be an absolute path" +fi +if [[ "${LOCAL_ROOT}" != /* ]]; then + usage_error "local root must be an absolute path" +fi +if ! LOCAL_ROOT="$(realpath -m -s -- "${LOCAL_ROOT}")"; then + usage_error "local root must have a valid absolute lexical form" +fi +if [[ "${LOCAL_ROOT}" == "/" ]]; then + usage_error "local root must not be the filesystem root" +fi +if [[ "${PYTHON}" != /* || ! -f "${PYTHON}" || ! -x "${PYTHON}" ]]; then + echo "python must be an absolute path to a regular executable" >&2 + exit 66 +fi + +SOURCE_ID="${SSH_HOST}:${REMOTE_ROOT%/}" +STATE_ROOT="${LOCAL_ROOT}.download-state" +CLAIM_ROOT="${LOCAL_ROOT}.download-claim" +LEGACY_SOURCE="${LOCAL_ROOT}.download-source" +EXPECTED_VERIFICATION='{"cells": 96, "status": "verified", "trajectories": 96}' + +LOCAL_PARENT="$(dirname -- "${LOCAL_ROOT}")" +if [[ -L "${LOCAL_PARENT}" || ! -d "${LOCAL_PARENT}" ]] || \ + [[ "$(realpath -s -- "${LOCAL_PARENT}")" != "$(realpath -e -- "${LOCAL_PARENT}")" ]]; then + echo "local root parent must be an existing real non-symlink directory" >&2 + exit 73 +fi +umask 077 +if ! mkdir -- "${CLAIM_ROOT}" 2>/dev/null; then + echo "another or stale Pilot download invocation claim exists: ${CLAIM_ROOT}" >&2 + exit 75 +fi +CLAIM_ID="$(stat -c '%d:%i' -- "${CLAIM_ROOT}")" +CLAIM_OWNED=1 + +release_claim() { + local current_id="" + if (( CLAIM_OWNED == 0 )); then + return 0 + fi + if [[ -L "${CLAIM_ROOT}" || ! -d "${CLAIM_ROOT}" ]]; then + echo "owned invocation claim changed; preserving it for diagnosis" >&2 + return 1 + fi + current_id="$(stat -c '%d:%i' -- "${CLAIM_ROOT}")" + if [[ "${current_id}" != "${CLAIM_ID}" ]]; then + echo "owned invocation claim identity changed; preserving it for diagnosis" >&2 + return 1 + fi + if ! rmdir -- "${CLAIM_ROOT}"; then + echo "owned invocation claim is not empty; preserving it for diagnosis" >&2 + return 1 + fi + CLAIM_OWNED=0 +} + +fail_closed() { + local status="$1" + shift + echo "$*" >&2 + release_claim || true + exit "${status}" +} + +publish_file() { + local destination="$1" + local payload="$2" + local temporary="${destination}.new.$$.${RANDOM}" + if [[ -L "${destination}" || -e "${destination}" ]]; then + fail_closed 73 "refusing to replace existing state file: ${destination}" + fi + if ! (set -o noclobber; printf '%s' "${payload}" > "${temporary}"); then + fail_closed 73 "could not exclusively create temporary state file" + fi + chmod 0444 -- "${temporary}" + if ! ln -- "${temporary}" "${destination}"; then + fail_closed 73 "could not atomically publish state file: ${destination}" + fi + rm -- "${temporary}" +} + +if [[ -L "${LOCAL_ROOT}" || ( -e "${LOCAL_ROOT}" && ! -d "${LOCAL_ROOT}" ) ]]; then + fail_closed 73 "local root must be a directory, not a file or symlink" +fi +if [[ -L "${STATE_ROOT}" || ( -e "${STATE_ROOT}" && ! -d "${STATE_ROOT}" ) ]]; then + fail_closed 73 "download state root must be a real directory" +fi +if [[ ! -e "${STATE_ROOT}" ]] && ! mkdir -- "${STATE_ROOT}"; then + fail_closed 73 "could not exclusively create download state root" +fi +if [[ -L "${STATE_ROOT}" || ! -d "${STATE_ROOT}" ]]; then + fail_closed 73 "download state root must be a real directory" +fi + +SOURCE_FILE="${STATE_ROOT}/source" +COMPLETION_FILE="${STATE_ROOT}/verified" +LOG_ROOT="${STATE_ROOT}/logs" + +for state_file in "${SOURCE_FILE}" "${COMPLETION_FILE}"; do + if [[ -L "${state_file}" ]]; then + fail_closed 73 "state files must not be symlinks: ${state_file}" + fi + if [[ -e "${state_file}" && ! -f "${state_file}" ]]; then + fail_closed 73 "state files must be regular files: ${state_file}" + fi +done +if [[ -L "${LOG_ROOT}" || ( -e "${LOG_ROOT}" && ! -d "${LOG_ROOT}" ) ]]; then + fail_closed 73 "transfer log root must be a real directory" +fi + +BOOTSTRAP_EXISTING=0 +if [[ -d "${LOCAL_ROOT}" ]]; then + shopt -s nullglob dotglob + EXISTING_ENTRIES=("${LOCAL_ROOT}"/*) + shopt -u nullglob dotglob +else + EXISTING_ENTRIES=() +fi + +if [[ -e "${SOURCE_FILE}" ]]; then + if [[ "$(<"${SOURCE_FILE}")" != "${SOURCE_ID}" ]]; then + fail_closed 73 "local root is marked for a different Pilot source" + fi +elif (( ${#EXISTING_ENTRIES[@]} != 0 )); then + if [[ -L "${LEGACY_SOURCE}" || ! -f "${LEGACY_SOURCE}" ]] || \ + [[ "$(<"${LEGACY_SOURCE}")" != "${SOURCE_ID}" ]]; then + fail_closed 73 "refusing an unmarked nonempty local root" + fi + BOOTSTRAP_EXISTING=1 +else + publish_file "${SOURCE_FILE}" "${SOURCE_ID}"$'\n' +fi + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" +SOLUTION_ROOT="$(dirname -- "${SCRIPT_DIR}")" + +verify_local_root() { + local output="" + if ! output="$( + cd "${SOLUTION_ROOT}" + PYTHONPATH="${SOLUTION_ROOT}/src" "${PYTHON}" scripts/run_pilot.py verify \ + --run-spec "${LOCAL_ROOT%/}/run_spec.json" + )"; then + return 1 + fi + if [[ "${output}" != "${EXPECTED_VERIFICATION}" ]]; then + echo "semantic verifier did not report exactly 96 cells and trajectories" >&2 + return 1 + fi + printf '%s\n' "${output}" +} + +record_legacy_verification_failure() { + local diagnostic_root="${STATE_ROOT}/diagnostics" + local diagnostic_file="" + if [[ -L "${diagnostic_root}" || \ + ( -e "${diagnostic_root}" && ! -d "${diagnostic_root}" ) ]]; then + fail_closed 73 "diagnostic state root must be a real directory" + fi + if [[ ! -e "${diagnostic_root}" ]] && ! mkdir -- "${diagnostic_root}"; then + fail_closed 73 "could not exclusively create diagnostic state root" + fi + if [[ -L "${diagnostic_root}" || ! -d "${diagnostic_root}" ]]; then + fail_closed 73 "diagnostic state root must be a real directory" + fi + diagnostic_file="${diagnostic_root}/legacy-verification-failed-$$-${RANDOM}" + publish_file \ + "${diagnostic_file}" \ + "${SOURCE_ID}"$'\nsemantic verification failed\n' +} + +if [[ -e "${COMPLETION_FILE}" ]]; then + EXPECTED_COMPLETION="${SOURCE_ID}"$'\n'"${EXPECTED_VERIFICATION}" + if [[ "$(<"${COMPLETION_FILE}")" != "${EXPECTED_COMPLETION}" ]]; then + fail_closed 73 "verified completion state does not match this source" + fi + if ! verify_local_root; then + fail_closed 74 "completed Pilot root failed semantic re-verification" + fi + release_claim || exit 73 + exit 0 +fi + +if (( BOOTSTRAP_EXISTING == 1 )); then + if ! verify_local_root; then + record_legacy_verification_failure + fail_closed 74 "legacy Pilot root failed semantic verification" + fi + publish_file "${SOURCE_FILE}" "${SOURCE_ID}"$'\n' + publish_file \ + "${COMPLETION_FILE}" \ + "${SOURCE_ID}"$'\n'"${EXPECTED_VERIFICATION}"$'\n' + release_claim || exit 73 + exit 0 +fi + +if [[ ! -e "${LOG_ROOT}" ]] && ! mkdir -- "${LOG_ROOT}"; then + fail_closed 73 "could not exclusively create transfer log root" +fi +if [[ -L "${LOG_ROOT}" || ! -d "${LOG_ROOT}" ]]; then + fail_closed 73 "transfer log root must be a real directory" +fi +shopt -s nullglob dotglob +LOG_ENTRIES=("${LOG_ROOT}"/*) +shopt -u nullglob dotglob +for log_entry in "${LOG_ENTRIES[@]}"; do + if [[ -L "${log_entry}" ]]; then + fail_closed 73 "transfer logs must not be symlinks: ${log_entry}" + fi +done +TRANSFER_LOG="${LOG_ROOT}/transfer-$$-${RANDOM}.log" +set -o noclobber +if ! exec {TRANSFER_LOG_FD}> "${TRANSFER_LOG}"; then + set +o noclobber + fail_closed 73 "could not exclusively create transfer log" +fi +set +o noclobber + +mkdir -- "${LOCAL_ROOT}" 2>/dev/null || [[ -d "${LOCAL_ROOT}" ]] || \ + fail_closed 73 "could not create local root" +if ! rsync \ + --archive \ + --checksum \ + --partial \ + --itemize-changes \ + "${SSH_HOST}:${REMOTE_ROOT%/}/" \ + "${LOCAL_ROOT%/}/" 2>&1 | tee "/dev/fd/${TRANSFER_LOG_FD}"; then + fail_closed 74 "Pilot transfer failed; resumable state was preserved" +fi +exec {TRANSFER_LOG_FD}>&- + +if ! verify_local_root; then + fail_closed 74 "downloaded Pilot root failed semantic verification" +fi +publish_file \ + "${COMPLETION_FILE}" \ + "${SOURCE_ID}"$'\n'"${EXPECTED_VERIFICATION}"$'\n' +release_claim || exit 73 diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/generate_report_figures.py b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/generate_report_figures.py new file mode 100644 index 000000000..fc293bec3 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/generate_report_figures.py @@ -0,0 +1,760 @@ +#!/usr/bin/env python3 +"""Authenticate Challenge 194 evidence and draw deterministic report SVGs.""" + +from __future__ import annotations + +import argparse +import hashlib +import html +import json +import math +import os +from collections.abc import Mapping, Sequence +from pathlib import Path + +EXPECTED_FILE_HASHES = { + "approval": "29dc5d04fd18728ee46fffe90c70d98caa61032005974f354e2b4e0e6018a7ab", + "p0_analysis": "44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b", + "extension_protocol": "e363a60f842b11b32972c7a68ec1c5f237741bc45bc79ab8bf93f51f6760d84d", + "extension_analysis": "d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5", + "combined_analysis": "6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929", + "brackets": "7a84d545b4526d94aa6f93ca4f0d264dcf01e518f2f9b04383921634786c9962", +} + +EXPECTED_EMBEDDED_HASHES = { + "p0_analysis": "e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8", + "extension_protocol": "a37ab41f3224594e61f4eebbe292975aeec449b9ecb7893e3e54f18d82d53321", + "extension_analysis": "79232574d314348c29a40cd2fbb7690e96f3cae5f26843bd4f1cf07cb6a1f45b", + "combined_analysis": "36f85c40e9159ef2e69742672c261769fb28d2f3c947780ba63e4ef5fe5975c3", + "brackets": "098f19d8883097d5f1f274ce759416328c086958fa5301c034a0b46dcbd562df", +} + +SOURCE_FILENAMES = { + "approval": "pilot_correctness_approval.json", + "p0_analysis": "p0_analysis.json", + "extension_protocol": "p0_extension_v1_protocol.json", + "extension_analysis": "p0_extension_v1_analysis.json", + "combined_analysis": "p0_combined_analysis_v2.json", + "brackets": "p0_combined_brackets_v2.json", +} + +SCHEMAS = { + "approval": "challenge-194-pilot-correctness-approval-v1", + "p0_analysis": "challenge-194-p0-analysis-v1", + "extension_protocol": "challenge-194-p0-extension-protocol-v1", + "extension_analysis": "challenge-194-p0-extension-analysis-v1", + "combined_analysis": "challenge-194-p0-combined-analysis-v2", + "brackets": "challenge-194-p1-brackets-v2", +} + +EMBEDDED_FIELDS = { + "p0_analysis": "analysis_document_sha256", + "extension_protocol": "protocol_sha256", + "extension_analysis": "analysis_document_sha256", + "combined_analysis": "analysis_document_sha256", + "brackets": "bracket_document_sha256", +} + +PANEL_SIGMAS = ((0.9).hex(), (1.0).hex()) +PANEL_LENGTHS = (16384, 262144) +UNRESOLVED_REASON = "no_nonzero_interval_marked_by_both_estimators" +OBSERVABLES = ("q_g", "four_sector_crossing") + + +class SelectorPanel: + def __init__( + self, + sigma_hex: str, + kappas: tuple[str, ...], + rows: dict[tuple[int, str], dict], + q_marks: tuple[bool, ...], + crossing_marks: tuple[bool, ...], + status: str, + reason: str, + ) -> None: + self.sigma_hex = sigma_hex + self.kappas = kappas + self.lengths = PANEL_LENGTHS + self.rows = rows + self.q_marks = q_marks + self.crossing_marks = crossing_marks + self.status = status + self.reason = reason + + +class ReportEvidence: + def __init__( + self, + documents: dict[str, dict], + file_hashes: dict[str, str], + embedded_hashes: dict[str, str], + panels: dict[str, SelectorPanel], + ) -> None: + self.documents = documents + self.file_hashes = file_hashes + self.embedded_hashes = embedded_hashes + self.panels = panels + + +def _canonical_float(hex_value: object, label: str) -> float: + if not isinstance(hex_value, str): + raise TypeError(f"{label} must be a binary64 hex string") + try: + value = float.fromhex(hex_value) + except ValueError as error: + raise ValueError(f"{label} is not valid float.hex()") from error + if not math.isfinite(value) or value.hex() != hex_value: + raise ValueError(f"{label} is not canonical finite float.hex()") + return value + + +def _finite_number(value: object, label: str, *, nonnegative: bool = False) -> float: + if isinstance(value, bool) or not isinstance(value, (int, float)): + raise TypeError(f"{label} must be numeric") + result = float(value) + if not math.isfinite(result) or (nonnegative and result < 0.0): + raise ValueError(f"{label} is not an allowed finite value") + return result + + +def _selector_marks( + rows: Mapping[tuple[int, str], dict], + kappas: Sequence[str], +) -> tuple[tuple[bool, ...], tuple[bool, ...]]: + q_marks: list[bool] = [] + crossing_marks: list[bool] = [] + for index in range(len(kappas) - 1): + q_differences = [] + for endpoint in (index, index + 1): + kappa = kappas[endpoint] + q_differences.append( + rows[(PANEL_LENGTHS[0], kappa)]["means"]["q_g"] + - rows[(PANEL_LENGTHS[1], kappa)]["means"]["q_g"] + ) + q_marks.append(min(q_differences) <= 0.0 <= max(q_differences)) + crossing_marks.append( + any( + min( + rows[(length, kappas[index])]["means"]["four_sector_crossing"], + rows[(length, kappas[index + 1])]["means"]["four_sector_crossing"], + ) + <= 0.25 + and max( + rows[(length, kappas[index])]["means"]["four_sector_crossing"], + rows[(length, kappas[index + 1])]["means"]["four_sector_crossing"], + ) + >= 0.75 + for length in PANEL_LENGTHS + ) + ) + return tuple(q_marks), tuple(crossing_marks) + + +def load_evidence(paths: Mapping[str, Path]) -> ReportEvidence: + """Load only the six exact authenticated inputs and reconstruct selectors.""" + if set(paths) != set(EXPECTED_FILE_HASHES): + missing = sorted(set(EXPECTED_FILE_HASHES) - set(paths)) + extra = sorted(set(paths) - set(EXPECTED_FILE_HASHES)) + raise ValueError(f"required sources differ: missing={missing}, extra={extra}") + + documents: dict[str, dict] = {} + file_hashes: dict[str, str] = {} + for key, expected_digest in EXPECTED_FILE_HASHES.items(): + path = Path(paths[key]) + raw = path.read_bytes() + digest = hashlib.sha256(raw).hexdigest() + if digest != expected_digest: + raise ValueError(f"{SOURCE_FILENAMES[key]} SHA256 mismatch: {digest}") + try: + document = json.loads(raw) + except (UnicodeDecodeError, json.JSONDecodeError) as error: + raise ValueError(f"{SOURCE_FILENAMES[key]} is not valid JSON") from error + if not isinstance(document, dict): + raise TypeError(f"{SOURCE_FILENAMES[key]} must contain an object") + if document.get("schema_version") != SCHEMAS[key]: + raise ValueError(f"{SOURCE_FILENAMES[key]} schema mismatch") + documents[key] = document + file_hashes[key] = digest + + for key, field in EMBEDDED_FIELDS.items(): + if documents[key].get(field) != EXPECTED_EMBEDDED_HASHES[key]: + raise ValueError(f"{SOURCE_FILENAMES[key]} embedded identity mismatch") + + extension_protocol = documents["extension_protocol"] + extension = documents["extension_analysis"] + combined = documents["combined_analysis"] + brackets = documents["brackets"] + approval = documents["approval"] + + if approval.get("report_sha256") != ( + "036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8" + ): + raise ValueError("approval report identity mismatch") + if ( + extension_protocol.get("source_p0_analysis_document_sha256") + != (EXPECTED_EMBEDDED_HASHES["p0_analysis"]) + ): + raise ValueError("extension protocol source link mismatch") + if ( + extension.get("source_extension_protocol_sha256") + != (EXPECTED_EMBEDDED_HASHES["extension_protocol"]) + ): + raise ValueError("extension analysis protocol link mismatch") + if ( + combined.get("source_p0_analysis_document_sha256") + != (EXPECTED_EMBEDDED_HASHES["p0_analysis"]) + ): + raise ValueError("combined P0 source link mismatch") + if ( + combined.get("source_extension_analysis_document_sha256") + != (EXPECTED_EMBEDDED_HASHES["extension_analysis"]) + ): + raise ValueError("combined extension source link mismatch") + if ( + brackets.get("source_analysis_document_sha256") + != (EXPECTED_EMBEDDED_HASHES["combined_analysis"]) + ): + raise ValueError("bracket source link mismatch") + if brackets.get("requires_p0_extension") is not True: + raise ValueError("unexpected selector completion state") + if any("p1" in key.lower() and value for key, value in combined.items()): + raise ValueError("combined evidence unexpectedly claims P1") + + sigma_entries = combined.get("sigma_entries") + if not isinstance(sigma_entries, list): + raise TypeError("combined analysis sigma_entries missing") + by_sigma = {entry.get("sigma_hex"): entry for entry in sigma_entries} + if set(PANEL_SIGMAS) - set(by_sigma): + raise ValueError("required sigma panels missing") + bracket_entries = brackets.get("brackets") + if not isinstance(bracket_entries, list): + raise TypeError("bracket entries missing") + bracket_by_sigma = {entry.get("sigma_hex"): entry for entry in bracket_entries} + + panels: dict[str, SelectorPanel] = {} + for sigma_hex in PANEL_SIGMAS: + _canonical_float(sigma_hex, "sigma") + entry = by_sigma[sigma_hex] + raw_kappas = entry.get("kappas") + if not isinstance(raw_kappas, list) or len(raw_kappas) < 2: + raise ValueError("panel kappa axis missing") + all_numeric_kappas = [ + _canonical_float(value, f"{sigma_hex} kappa") for value in raw_kappas + ] + if all_numeric_kappas != sorted(all_numeric_kappas): + raise ValueError("panel kappas must be ordered") + kappas = tuple( + value + for value, numeric in zip(raw_kappas, all_numeric_kappas) + if numeric != 0.0 + ) + if len(kappas) < 2: + raise ValueError("panel requires at least two nonzero kappas") + if tuple(entry.get("lengths", ())) != (1024, 16384, 262144): + raise ValueError("combined panel length axis mismatch") + + rows: dict[tuple[int, str], dict] = {} + estimates = entry.get("estimates") + if not isinstance(estimates, list): + raise TypeError("panel estimates missing") + for row in estimates: + if ( + row.get("sigma_hex") != sigma_hex + or row.get("length") not in PANEL_LENGTHS + or row.get("kappa_hex") not in kappas + ): + continue + means = row.get("means") + standard_errors = row.get("standard_errors") + if not isinstance(means, dict) or not isinstance(standard_errors, dict): + raise TypeError("means or standard_errors missing") + for observable in OBSERVABLES: + _finite_number(means.get(observable), f"{observable} mean") + _finite_number( + standard_errors.get(observable), + f"{observable} standard error", + nonnegative=True, + ) + key = (row["length"], row["kappa_hex"]) + if key in rows: + raise ValueError("duplicate plotted estimate") + rows[key] = row + expected_rows = { + (length, kappa) for length in PANEL_LENGTHS for kappa in kappas + } + if set(rows) != expected_rows: + raise ValueError("plotted estimate grid is incomplete") + + q_marks, crossing_marks = _selector_marks(rows, kappas) + if any(q and crossing for q, crossing in zip(q_marks, crossing_marks)): + raise ValueError("authenticated panel unexpectedly has a common interval") + bracket = bracket_by_sigma.get(sigma_hex, {}) + if ( + bracket.get("status") != "requires_p0_extension" + or bracket.get("reason") != UNRESOLVED_REASON + or tuple(bracket.get("lengths", ())) != PANEL_LENGTHS + ): + raise ValueError("required unresolved bracket state mismatch") + panels[sigma_hex] = SelectorPanel( + sigma_hex, + kappas, + rows, + q_marks, + crossing_marks, + bracket["status"], + bracket["reason"], + ) + + return ReportEvidence( + documents, + file_hashes, + dict(EXPECTED_EMBEDDED_HASHES), + panels, + ) + + +def _svg_text(x: float, y: float, text: str, css_class: str = "") -> str: + class_attribute = f' class="{css_class}"' if css_class else "" + return f'{html.escape(text)}' + + +def _polyline(points: Sequence[tuple[float, float]], color: str) -> str: + coordinates = " ".join(f"{x:.2f},{y:.2f}" for x, y in points) + return ( + f'' + ) + + +def _plot_panel( + panel: SelectorPanel, + observable: str, + x0: float, + y0: float, + width: float, + height: float, +) -> list[str]: + values = [] + for row in panel.rows.values(): + mean = row["means"][observable] + error = row["standard_errors"][observable] + values.extend((mean - error, mean + error)) + y_min = min(0.0, min(values)) + y_max = max(1.0 if observable == "four_sector_crossing" else 0.0, max(values)) + padding = max((y_max - y_min) * 0.08, 0.02) + y_min -= padding + y_max += padding + x_values = [float.fromhex(value) for value in panel.kappas] + x_min, x_max = min(x_values), max(x_values) + + def sx(value: float) -> float: + return x0 + (value - x_min) * width / (x_max - x_min) + + def sy(value: float) -> float: + return y0 + height - (value - y_min) * height / (y_max - y_min) + + sigma = float.fromhex(panel.sigma_hex) + label = "Q_G" if observable == "q_g" else "four-sector crossing" + output = [ + ( + f'' + ), + _svg_text(x0, y0 - 14, f"sigma = {sigma:.1f} · {label}", "panel-title"), + _svg_text(x0 - 54, y0 + 12, f"{y_max:.2f}", "tick"), + _svg_text(x0 - 54, y0 + height, f"{y_min:.2f}", "tick"), + _svg_text(x0, y0 + height + 22, f"{x_min:.4g}", "tick"), + _svg_text(x0 + width - 48, y0 + height + 22, f"{x_max:.4g}", "tick"), + _svg_text(x0 + width / 2 - 18, y0 + height + 42, "kappa", "axis-label"), + ] + if observable == "four_sector_crossing": + for guide in (0.25, 0.75): + output.append( + f'' + ) + output.append( + _svg_text(x0 + width + 8, sy(guide) + 4, f"{guide:.2f}", "tick") + ) + + colors = {16384: "#1769aa", 262144: "#d1495b"} + for length in PANEL_LENGTHS: + points = [] + for kappa_hex, x_value in zip(panel.kappas, x_values): + row = panel.rows[(length, kappa_hex)] + mean = row["means"][observable] + error = row["standard_errors"][observable] + x_coordinate = sx(x_value) + low, high = sy(mean - error), sy(mean + error) + output.extend( + ( + ( + f'' + ), + ( + f'' + ), + ( + f'' + ), + ( + f'' + ), + ) + ) + points.append((x_coordinate, sy(mean))) + output.append(_polyline(points, colors[length])) + + ribbon_y = y0 + height + 56 + for name, marks, color in ( + ("Q_G marks", panel.q_marks, "#7a5195"), + ("four-sector marks", panel.crossing_marks, "#2a9d8f"), + ): + output.append(_svg_text(x0, ribbon_y + 10, name, "ribbon-label")) + for index, marked in enumerate(marks): + if marked: + left = sx(x_values[index]) + right = sx(x_values[index + 1]) + output.append( + f'' + ) + ribbon_y += 28 + output.append(_svg_text(x0, ribbon_y + 12, "共同标记区间:无", "unresolved")) + return output + + +def render_selector_svg(evidence: ReportEvidence) -> bytes: + """Render authenticated means, ±1-SE bars, and unchanged selector marks.""" + body = [ + '', + '', + "Challenge 194 selector evidence for sigma 0.9 and 1.0", + ( + "Authenticated P0 plus extension v1 Q_G and four-sector means, " + "standard errors, and unchanged interval marks; both sigma values remain " + "unresolved." + ), + ( + "" + ), + _svg_text(70, 55, "挑战194:sigma 0.9 / 1.0 的 selector 实证", "title"), + _svg_text( + 70, + 88, + "误差条:均值 ± 1 standard error;选择器只使用均值,不使用误差条救援。", + "subtitle", + ), + ] + positions = ( + (PANEL_SIGMAS[0], "q_g", 100, 150), + (PANEL_SIGMAS[0], "four_sector_crossing", 760, 150), + (PANEL_SIGMAS[1], "q_g", 100, 770), + (PANEL_SIGMAS[1], "four_sector_crossing", 760, 770), + ) + for sigma_hex, observable, x, y in positions: + body.extend(_plot_panel(evidence.panels[sigma_hex], observable, x, y, 540, 410)) + + body.extend( + ( + '', + '', + _svg_text(125, 1485, "L=16384", "caption"), + '', + _svg_text(245, 1485, "L=262144", "caption"), + '', + _svg_text(402, 1485, "Q_G interval mark", "caption"), + '', + _svg_text(607, 1485, "four-sector interval mark", "caption"), + _svg_text( + 90, + 1525, + "状态:sigma = 0.9 与 sigma = 1.0 均为 requires_p0_extension;", + "caption", + ), + _svg_text( + 90, + 1550, + f"原因:{UNRESOLVED_REASON}。", + "caption", + ), + _svg_text( + 90, + 1595, + "来源 p0_combined_analysis_v2.json · SHA256 " + + evidence.file_hashes["combined_analysis"], + "small", + ), + _svg_text( + 90, + 1620, + "来源 p0_combined_brackets_v2.json · SHA256 " + + evidence.file_hashes["brackets"], + "small", + ), + _svg_text( + 90, + 1645, + "P0 source p0_analysis.json · SHA256 " + + evidence.file_hashes["p0_analysis"], + "small", + ), + _svg_text( + 90, + 1670, + "extension-v1 source p0_extension_v1_analysis.json · SHA256 " + + evidence.file_hashes["extension_analysis"], + "small", + ), + _svg_text( + 90, + 1720, + "Boundary: exploratory selector evidence only; not transition, scaling, exponent, or universality evidence.", + "caption", + ), + "", + ) + ) + return ("\n".join(body) + "\n").encode("utf-8") + + +def render_workflow_svg(evidence: ReportEvidence, dirty_paths: Sequence[str]) -> bytes: + """Render the evidence chain and keep dirty v2 work outside that chain.""" + steps = ( + ("Issue #194", "model contract"), + ("correctness approval", "authenticated"), + ("P0 complete", "exploratory · 96 cells"), + ("extension v1 complete", "96 cells / 96 trajectories"), + ("combined selector", "mean-based rules"), + ("sigma=0.9,1.0", "unresolved"), + ("P1", "未发布 / 未运行"), + ) + body = [ + '', + '', + "Challenge 194 evidence workflow and unresolved status", + ( + "Authenticated workflow ends at unresolved sigma 0.9 and 1.0 and " + "P1 not published or run. Partial extension-v2 workspace files are outside " + "the evidence chain." + ), + ( + "" + ), + ( + '' + '' + ), + _svg_text(60, 55, "挑战194:证据工作流与停止边界", "title"), + ] + x_positions = (55, 285, 515, 745, 975, 1205, 1435) + for index, ((main, sub), x) in enumerate(zip(steps, x_positions)): + css = "blocked" if index >= 5 else "box" + body.append( + f'' + ) + body.append(_svg_text(x + 15, 174, main, "main")) + body.append(_svg_text(x + 15, 208, sub, "sub")) + if index < len(steps) - 1: + body.append( + f'' + ) + body.extend( + ( + _svg_text( + 1205, + 280, + f"reason: {UNRESOLVED_REASON}", + "boundary", + ), + '', + _svg_text( + 570, + 385, + "当前工作区:extension-v2 局部未提交工作", + "main", + ), + _svg_text( + 570, + 420, + "不属于本报告证据链;未声称运行", + "boundary", + ), + _svg_text( + 570, + 452, + f"受保护 dirty 路径数:{len(tuple(dirty_paths))}", + "sub", + ), + _svg_text( + 60, + 545, + "来源 pilot_correctness_approval.json · SHA256 " + + evidence.file_hashes["approval"], + "caption", + ), + _svg_text( + 60, + 570, + "来源 p0_analysis.json · SHA256 " + evidence.file_hashes["p0_analysis"], + "caption", + ), + _svg_text( + 60, + 595, + "来源 p0_extension_v1_protocol.json · SHA256 " + + evidence.file_hashes["extension_protocol"], + "caption", + ), + _svg_text( + 60, + 620, + "来源 p0_extension_v1_analysis.json · SHA256 " + + evidence.file_hashes["extension_analysis"], + "caption", + ), + _svg_text( + 60, + 645, + "来源 p0_combined_analysis_v2.json · SHA256 " + + evidence.file_hashes["combined_analysis"], + "caption", + ), + _svg_text( + 60, + 670, + "来源 p0_combined_brackets_v2.json · SHA256 " + + evidence.file_hashes["brackets"], + "caption", + ), + _svg_text( + 60, + 715, + "P0 / extension v1 are exploratory only; no P1, confirmatory, scaling, eta, or nu result follows.", + "boundary", + ), + "", + ) + ) + return ("\n".join(body) + "\n").encode("utf-8") + + +def write_outputs(output_dir: Path, outputs: Mapping[str, bytes]) -> None: + """Atomically publish new files, accepting identical existing bytes only.""" + output_dir = Path(output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + for name, content in outputs.items(): + target = output_dir / name + if target.exists(): + if target.is_file() and target.read_bytes() == content: + print(f"verified-existing {target}") + continue + raise FileExistsError(f"refusing to replace different output: {target}") + temporary = None + try: + for attempt in range(100): + candidate = output_dir / f".{name}.{os.getpid()}.{attempt}.partial" + try: + descriptor = os.open( + candidate, + os.O_WRONLY | os.O_CREAT | os.O_EXCL, + 0o600, + ) + temporary = candidate + break + except FileExistsError: + continue + else: + raise FileExistsError("unable to reserve private temporary output") + with os.fdopen(descriptor, "wb") as stream: + stream.write(content) + stream.flush() + os.fsync(stream.fileno()) + try: + os.link(temporary, target) + except FileExistsError: + if target.is_file() and target.read_bytes() == content: + print(f"verified-existing {target}") + continue + raise FileExistsError(f"refusing to replace different output: {target}") + directory_descriptor = os.open(output_dir, os.O_RDONLY) + try: + os.fsync(directory_descriptor) + finally: + os.close(directory_descriptor) + print(f"created {target}") + finally: + if temporary is not None: + try: + temporary.unlink() + except FileNotFoundError: + pass + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--approval", type=Path, required=True) + parser.add_argument("--p0-analysis", type=Path, required=True) + parser.add_argument("--extension-protocol", type=Path, required=True) + parser.add_argument("--extension-analysis", type=Path, required=True) + parser.add_argument("--combined-analysis", type=Path, required=True) + parser.add_argument("--brackets", type=Path, required=True) + parser.add_argument("--output-dir", type=Path, required=True) + parser.add_argument("--verify-only", action="store_true") + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + arguments = _parser().parse_args(argv) + paths = { + "approval": arguments.approval, + "p0_analysis": arguments.p0_analysis, + "extension_protocol": arguments.extension_protocol, + "extension_analysis": arguments.extension_analysis, + "combined_analysis": arguments.combined_analysis, + "brackets": arguments.brackets, + } + evidence = load_evidence(paths) + dirty_paths = ( + ".superpowers/sdd/task-1-report.md", + "scripts/analyze_pilot.py", + "src/long_range_percolation/pilot_extension.py", + "tests/test_analyze_pilot_cli.py", + "tests/test_pilot_extension.py", + ) + outputs = { + "challenge-194-selector-evidence.svg": render_selector_svg(evidence), + "challenge-194-workflow-status.svg": render_workflow_svg(evidence, dirty_paths), + } + for name, content in outputs.items(): + print(f"{name} SHA256 {hashlib.sha256(content).hexdigest()}") + if not arguments.verify_only: + write_outputs(arguments.output_dir, outputs) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_array_slurm.sh b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_array_slurm.sh new file mode 100755 index 000000000..8c286eb63 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_array_slurm.sh @@ -0,0 +1,153 @@ +#!/bin/bash +set -euo pipefail + +: "${HARNESS_RUN_SPEC:?Set HARNESS_RUN_SPEC to the immutable run_spec.json}" +: "${SLURM_ARRAY_TASK_ID:?Run as a Slurm array task}" + +REPO_ROOT="${CHALLENGE_194_REPO_ROOT:-${HARNESS_ENTRYPOINT:-}}" +: "${REPO_ROOT:?Set CHALLENGE_194_REPO_ROOT or the harness entrypoint to the explicit shared repository path}" + +if [[ "${SLURM_CPUS_PER_TASK:-1}" != "1" ]]; then + echo "Challenge 194 Pilot cells require exactly one CPU" >&2 + exit 64 +fi +if [[ ! "${SLURM_ARRAY_TASK_ID}" =~ ^[0-9]+$ ]] || \ + (( SLURM_ARRAY_TASK_ID < 1 || SLURM_ARRAY_TASK_ID > 96 )); then + echo "SLURM_ARRAY_TASK_ID must be in the frozen range 1..96" >&2 + exit 64 +fi +for job_id_name in SLURM_ARRAY_JOB_ID SLURM_JOB_ID; do + if [[ -n "${!job_id_name:-}" && ! "${!job_id_name}" =~ ^[0-9]+$ ]]; then + echo "${job_id_name} must be numeric" >&2 + exit 64 + fi +done +CELL_INDEX=$((SLURM_ARRAY_TASK_ID - 1)) + +SOLUTION_RELATIVE="tracks/qmc/solutions/frustration-free/challenge-194" +SOLUTION_ROOT="${REPO_ROOT%/}/${SOLUTION_RELATIVE}" +if [[ ! -f "${SOLUTION_ROOT}/scripts/run_pilot.py" ]]; then + echo "Explicit Challenge 194 solution path is invalid: ${SOLUTION_ROOT}" >&2 + exit 66 +fi +if [[ ! -f "${HARNESS_RUN_SPEC}" ]]; then + echo "HARNESS_RUN_SPEC is not a regular file: ${HARNESS_RUN_SPEC}" >&2 + exit 66 +fi + +resolve_python_candidate() { + local label="$1" + local candidate="$2" + local canonical="" + local resolved="" + if [[ "${candidate}" != /* ]]; then + echo "${label} must be an absolute path" >&2 + return 66 + fi + if ! canonical="$(realpath -s -- "${candidate}" 2>/dev/null)"; then + echo "${label} is not a valid absolute path" >&2 + return 66 + fi + if ! resolved="$(realpath -e -- "${candidate}" 2>/dev/null)"; then + echo "${label} does not resolve to an existing path" >&2 + return 66 + fi + if [[ "${resolved}" != /* || ! -f "${resolved}" || ! -x "${resolved}" ]]; then + echo "${label} must resolve to a regular executable" >&2 + return 66 + fi + printf '%s\n' "${canonical}" +} + +CHALLENGE_PYTHON="" +HARNESS_PYTHON="" +if [[ "${CHALLENGE_194_PYTHON+x}" == "x" ]]; then + CHALLENGE_PYTHON="$( + resolve_python_candidate CHALLENGE_194_PYTHON "${CHALLENGE_194_PYTHON}" + )" || exit $? +fi +if [[ "${HARNESS_COMMAND+x}" == "x" ]]; then + HARNESS_PYTHON="$( + resolve_python_candidate HARNESS_COMMAND "${HARNESS_COMMAND}" + )" || exit $? +fi +if [[ -n "${CHALLENGE_PYTHON}" && -n "${HARNESS_PYTHON}" && "${CHALLENGE_PYTHON}" != "${HARNESS_PYTHON}" ]]; then + echo "interpreter conflict: CHALLENGE_194_PYTHON and HARNESS_COMMAND resolve differently" >&2 + exit 66 +fi +OFFLINE_PYTHON="${CHALLENGE_PYTHON:-${HARNESS_PYTHON}}" + +# Eliminate inherited compiler/runtime controls before pinning the approved set. +while IFS= read -r variable; do + unset "${variable}" +done < <(compgen -A variable NUMBA_) +unset PYTHONHOME PYTHONUSERBASE PYTHONPATH PYTHONSTARTUP PYTHONINSPECT \ + PYTHONWARNINGS PYTHONBREAKPOINT PYTHONSAFEPATH \ + LD_PRELOAD LD_LIBRARY_PATH LD_AUDIT LIBRARY_PATH + +export NUMBA_DISABLE_JIT=0 +export OMP_NUM_THREADS=1 +export OPENBLAS_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NUMEXPR_NUM_THREADS=1 +export VECLIB_MAXIMUM_THREADS=1 +export NUMBA_NUM_THREADS=1 +export PYTHONHASHSEED=0 +export PYTHONUNBUFFERED=1 +export PYTHONNOUSERSITE=1 + +NUMBA_CACHE_BASE="${SLURM_TMPDIR:-${TMPDIR:-/tmp}}" +if [[ "${NUMBA_CACHE_BASE}" != /* || -L "${NUMBA_CACHE_BASE}" || \ + ! -d "${NUMBA_CACHE_BASE}" || ! -w "${NUMBA_CACHE_BASE}" ]]; then + echo "node-local temporary directory must be an absolute writable directory" >&2 + exit 73 +fi +if [[ "$(realpath -s -- "${NUMBA_CACHE_BASE}")" != "$(realpath -e -- "${NUMBA_CACHE_BASE}")" ]]; then + echo "node-local temporary directory must not contain symlink components" >&2 + exit 73 +fi +NUMBA_CACHE_JOB_ID="${SLURM_ARRAY_JOB_ID:-${SLURM_JOB_ID:-no-job-id}}" +if [[ "${NUMBA_CACHE_JOB_ID}" == "no-job-id" ]]; then + echo "SLURM_ARRAY_JOB_ID or SLURM_JOB_ID is required" >&2 + exit 64 +fi +export NUMBA_CACHE_DIR="${NUMBA_CACHE_BASE%/}/challenge-194-pilot-${NUMBA_CACHE_JOB_ID}-${SLURM_ARRAY_TASK_ID}" +case "$(realpath -m -- "${NUMBA_CACHE_DIR}")" in + "$(realpath -e -- "${NUMBA_CACHE_BASE}")"/*) ;; + *) + echo "NUMBA cache path escapes node-local temporary directory" >&2 + exit 73 + ;; +esac +umask 077 +if ! mkdir -- "${NUMBA_CACHE_DIR}"; then + echo "NUMBA cache directory must be uniquely created by this task" >&2 + exit 73 +fi +if [[ -L "${NUMBA_CACHE_DIR}" || ! -d "${NUMBA_CACHE_DIR}" || \ + ! -O "${NUMBA_CACHE_DIR}" || ! -w "${NUMBA_CACHE_DIR}" || \ + "$(realpath -e -- "${NUMBA_CACHE_DIR}")" != "${NUMBA_CACHE_DIR}" ]]; then + echo "NUMBA cache directory is not a safe writable directory" >&2 + exit 73 +fi +shopt -s nullglob dotglob +CACHE_ENTRIES=("${NUMBA_CACHE_DIR}"/*) +shopt -u nullglob dotglob +if (( ${#CACHE_ENTRIES[@]} != 0 )); then + echo "new NUMBA cache directory is unexpectedly non-empty" >&2 + exit 73 +fi + +cd "${SOLUTION_ROOT}" +echo "pilot array task=${SLURM_ARRAY_TASK_ID} cell=${CELL_INDEX} host=$(hostname)" +echo "run_spec=${HARNESS_RUN_SPEC}" +if [[ -n "${OFFLINE_PYTHON}" ]]; then + export PYTHONPATH="${SOLUTION_ROOT}/src" + exec "${OFFLINE_PYTHON}" scripts/run_pilot.py run-cell \ + --run-spec "${HARNESS_RUN_SPEC}" \ + --cell-index "${CELL_INDEX}" +fi +unset PYTHONPATH +exec uv run scripts/run_pilot.py run-cell \ + --run-spec "${HARNESS_RUN_SPEC}" \ + --cell-index "${CELL_INDEX}" diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh new file mode 100755 index 000000000..f3e88f4e7 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_array_slurm.sh @@ -0,0 +1,161 @@ +#!/bin/bash +#SBATCH --cpus-per-task=1 +#SBATCH --mem=1800M +#SBATCH --time=00:40:00 +set -euo pipefail + +: "${HARNESS_RUN_SPEC:?Set HARNESS_RUN_SPEC to the immutable extension run_spec.json}" +: "${SLURM_ARRAY_TASK_ID:?Run as a Slurm array task}" +: "${HARNESS_ENTRYPOINT:?Set the exact deployed repository root}" +: "${HARNESS_COMMAND:?Set the exact offline Python executable}" +CHALLENGE_194_REPO_ROOT="${HARNESS_ENTRYPOINT}" +CHALLENGE_194_PYTHON="${HARNESS_COMMAND}" +if [[ ! "${SLURM_ARRAY_TASK_ID}" =~ ^([1-9]|[1-8][0-9]|9[0-6])$ ]]; then + exit 64 +fi +CELL_INDEX=$((SLURM_ARRAY_TASK_ID - 1)) + +if [[ "${SLURM_CPUS_PER_TASK:-1}" != "1" ]]; then + echo "Challenge 194 P0 extension cells require exactly one CPU" >&2 + exit 64 +fi +for job_id_name in SLURM_ARRAY_JOB_ID SLURM_JOB_ID; do + if [[ -n "${!job_id_name:-}" && ! "${!job_id_name}" =~ ^[0-9]+$ ]]; then + echo "${job_id_name} must be numeric" >&2 + exit 64 + fi +done + +require_canonical_path() { + local label="$1" + local candidate="$2" + local kind="$3" + local canonical="" + local resolved="" + if [[ "${candidate}" != /* ]]; then + echo "${label} must be an absolute path" >&2 + return 66 + fi + canonical="$(realpath -s -- "${candidate}")" || return 66 + resolved="$(realpath -e -- "${candidate}")" || return 66 + if [[ "${candidate}" != "${canonical}" || "${canonical}" != "${resolved}" ]]; then + echo "${label} must be canonical and contain no symlink components" >&2 + return 66 + fi + if [[ "${kind}" == "directory" && ! -d "${resolved}" ]] || + [[ "${kind}" == "file" && ! -f "${resolved}" ]] || + [[ "${kind}" == "executable" && ( ! -f "${resolved}" || ! -x "${resolved}" ) ]]; then + echo "${label} has the wrong path type" >&2 + return 66 + fi +} + +resolve_python_candidate() { + local label="$1" + local candidate="$2" + local canonical="" + local resolved="" + if [[ "${candidate}" != /* ]]; then + echo "${label} must be an absolute path" >&2 + return 66 + fi + if ! canonical="$(realpath -s -- "${candidate}" 2>/dev/null)"; then + echo "${label} is not a valid absolute path" >&2 + return 66 + fi + if [[ "${candidate}" != "${canonical}" ]]; then + echo "${label} must be lexically canonical" >&2 + return 66 + fi + if ! resolved="$(realpath -e -- "${candidate}" 2>/dev/null)"; then + echo "${label} does not resolve to an existing path" >&2 + return 66 + fi + if [[ "${resolved}" != /* || ! -f "${resolved}" || ! -x "${resolved}" ]]; then + echo "${label} must resolve to a regular executable" >&2 + return 66 + fi + printf '%s\n' "${canonical}" +} + +require_canonical_path HARNESS_RUN_SPEC "${HARNESS_RUN_SPEC}" file +require_canonical_path HARNESS_ENTRYPOINT "${CHALLENGE_194_REPO_ROOT}" directory +CHALLENGE_194_PYTHON="$( + resolve_python_candidate HARNESS_COMMAND "${CHALLENGE_194_PYTHON}" +)" || exit $? + +SOLUTION_RELATIVE="tracks/qmc/solutions/frustration-free/challenge-194" +SOLUTION_ROOT="${CHALLENGE_194_REPO_ROOT}/${SOLUTION_RELATIVE}" +if [[ ! -f "${SOLUTION_ROOT}/scripts/run_pilot.py" ]]; then + echo "Exact Challenge 194 solution path is invalid: ${SOLUTION_ROOT}" >&2 + exit 66 +fi + +# Eliminate inherited compiler/runtime controls before pinning the approved set. +while IFS= read -r variable; do + unset "${variable}" +done < <(compgen -A variable NUMBA_) +unset PYTHONHOME PYTHONUSERBASE PYTHONPATH PYTHONSTARTUP PYTHONINSPECT \ + PYTHONWARNINGS PYTHONBREAKPOINT PYTHONSAFEPATH \ + LD_PRELOAD LD_LIBRARY_PATH LD_AUDIT LIBRARY_PATH + +export NUMBA_DISABLE_JIT=0 +export OMP_NUM_THREADS=1 +export OPENBLAS_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NUMEXPR_NUM_THREADS=1 +export VECLIB_MAXIMUM_THREADS=1 +export NUMBA_NUM_THREADS=1 +export PYTHONHASHSEED=0 +export PYTHONUNBUFFERED=1 +export PYTHONNOUSERSITE=1 + +NUMBA_CACHE_BASE="${SLURM_TMPDIR:-${TMPDIR:-/tmp}}" +if [[ "${NUMBA_CACHE_BASE}" != /* || -L "${NUMBA_CACHE_BASE}" || \ + ! -d "${NUMBA_CACHE_BASE}" || ! -w "${NUMBA_CACHE_BASE}" ]]; then + echo "node-local temporary directory must be an absolute writable directory" >&2 + exit 73 +fi +if [[ "$(realpath -s -- "${NUMBA_CACHE_BASE}")" != "$(realpath -e -- "${NUMBA_CACHE_BASE}")" ]]; then + echo "node-local temporary directory must not contain symlink components" >&2 + exit 73 +fi +NUMBA_CACHE_JOB_ID="${SLURM_ARRAY_JOB_ID:-${SLURM_JOB_ID:-no-job-id}}" +if [[ "${NUMBA_CACHE_JOB_ID}" == "no-job-id" ]]; then + echo "SLURM_ARRAY_JOB_ID or SLURM_JOB_ID is required" >&2 + exit 64 +fi +export NUMBA_CACHE_DIR="${NUMBA_CACHE_BASE%/}/challenge-194-p0-extension-${NUMBA_CACHE_JOB_ID}-${SLURM_ARRAY_TASK_ID}" +case "$(realpath -m -- "${NUMBA_CACHE_DIR}")" in + "$(realpath -e -- "${NUMBA_CACHE_BASE}")"/*) ;; + *) + echo "NUMBA cache path escapes node-local temporary directory" >&2 + exit 73 + ;; +esac +umask 077 +if ! mkdir -- "${NUMBA_CACHE_DIR}"; then + echo "NUMBA cache directory must be uniquely created by this task" >&2 + exit 73 +fi +if [[ -L "${NUMBA_CACHE_DIR}" || ! -d "${NUMBA_CACHE_DIR}" || \ + ! -O "${NUMBA_CACHE_DIR}" || ! -w "${NUMBA_CACHE_DIR}" || \ + "$(realpath -e -- "${NUMBA_CACHE_DIR}")" != "${NUMBA_CACHE_DIR}" ]]; then + echo "NUMBA cache directory is not a safe writable directory" >&2 + exit 73 +fi +shopt -s nullglob dotglob +CACHE_ENTRIES=("${NUMBA_CACHE_DIR}"/*) +shopt -u nullglob dotglob +if (( ${#CACHE_ENTRIES[@]} != 0 )); then + echo "new NUMBA cache directory is unexpectedly non-empty" >&2 + exit 73 +fi + +cd "${SOLUTION_ROOT}" +echo "P0 extension array task=${SLURM_ARRAY_TASK_ID} cell=${CELL_INDEX} host=$(hostname)" +echo "run_spec=${HARNESS_RUN_SPEC}" +export PYTHONPATH="${SOLUTION_ROOT}/src" +exec "${CHALLENGE_194_PYTHON}" scripts/run_pilot.py run-cell \ + --run-spec "${HARNESS_RUN_SPEC}" \ + --cell-index "${CELL_INDEX}" diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_build_slurm.sh b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_build_slurm.sh new file mode 100755 index 000000000..ccc46c045 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/pilot_extension_build_slurm.sh @@ -0,0 +1,171 @@ +#!/bin/bash +#SBATCH --cpus-per-task=1 +#SBATCH --mem=1800M +#SBATCH --time=00:10:00 +set -euo pipefail + +: "${HARNESS_RUN_SPEC:?Set HARNESS_RUN_SPEC to the exact canonical P0 analysis path}" +: "${HARNESS_ENTRYPOINT:?Set the exact deployed repository root}" +: "${HARNESS_COMMAND:?Set the exact offline Python executable}" +: "${SLURM_JOB_ID:?Run as a Slurm job}" +P0_ANALYSIS_PATH="${HARNESS_RUN_SPEC}" +CHALLENGE_194_REPO_ROOT="${HARNESS_ENTRYPOINT}" +CHALLENGE_194_PYTHON="${HARNESS_COMMAND}" + +require_canonical_path() { + local label="$1" + local candidate="$2" + local kind="$3" + local canonical="" + local resolved="" + if [[ "${candidate}" != /* ]]; then + echo "${label} must be an absolute path" >&2 + return 66 + fi + canonical="$(realpath -s -- "${candidate}")" || return 66 + resolved="$(realpath -e -- "${candidate}")" || return 66 + if [[ "${candidate}" != "${canonical}" || "${canonical}" != "${resolved}" ]]; then + echo "${label} must be canonical and contain no symlink components" >&2 + return 66 + fi + if [[ "${kind}" == "directory" && ! -d "${resolved}" ]] || + [[ "${kind}" == "file" && ! -f "${resolved}" ]] || + [[ "${kind}" == "executable" && ( ! -f "${resolved}" || ! -x "${resolved}" ) ]]; then + echo "${label} has the wrong path type" >&2 + return 66 + fi +} + +resolve_python_candidate() { + local label="$1" + local candidate="$2" + local canonical="" + local resolved="" + if [[ "${candidate}" != /* ]]; then + echo "${label} must be an absolute path" >&2 + return 66 + fi + if ! canonical="$(realpath -s -- "${candidate}" 2>/dev/null)"; then + echo "${label} is not a valid absolute path" >&2 + return 66 + fi + if [[ "${candidate}" != "${canonical}" ]]; then + echo "${label} must be lexically canonical" >&2 + return 66 + fi + if ! resolved="$(realpath -e -- "${candidate}" 2>/dev/null)"; then + echo "${label} does not resolve to an existing path" >&2 + return 66 + fi + if [[ "${resolved}" != /* || ! -f "${resolved}" || ! -x "${resolved}" ]]; then + echo "${label} must resolve to a regular executable" >&2 + return 66 + fi + printf '%s\n' "${canonical}" +} + +require_canonical_path HARNESS_RUN_SPEC "${P0_ANALYSIS_PATH}" file +require_canonical_path HARNESS_ENTRYPOINT "${CHALLENGE_194_REPO_ROOT}" directory +CHALLENGE_194_PYTHON="$( + resolve_python_candidate HARNESS_COMMAND "${CHALLENGE_194_PYTHON}" +)" || exit $? +if [[ ! "${SLURM_JOB_ID}" =~ ^[0-9]+$ ]]; then + echo "SLURM_JOB_ID must be numeric" >&2 + exit 64 +fi +if [[ "${SLURM_CPUS_PER_TASK:-1}" != "1" ]]; then + echo "Challenge 194 P0 extension construction requires exactly one CPU" >&2 + exit 64 +fi + +SOLUTION_RELATIVE="tracks/qmc/solutions/frustration-free/challenge-194" +SOLUTION_ROOT="${CHALLENGE_194_REPO_ROOT}/${SOLUTION_RELATIVE}" +if [[ ! -f "${SOLUTION_ROOT}/scripts/analyze_pilot.py" || + ! -f "${SOLUTION_ROOT}/scripts/run_pilot.py" ]]; then + echo "Exact Challenge 194 solution path is invalid: ${SOLUTION_ROOT}" >&2 + exit 66 +fi + +RESULTS_ROOT="$(dirname "${P0_ANALYSIS_PATH}")" +P0_EVIDENCE_ROOT="${RESULTS_ROOT}/pilot-p0-739880d" +EXTENSION_PROTOCOL_PATH="${RESULTS_ROOT}/p0_extension_v1_protocol.json" +VALIDATION_REPORT_PATH="${RESULTS_ROOT}/validation-prod-877ab93/report/report.json" +EXTENSION_ROOT="${RESULTS_ROOT}/pilot-p0-extension-v1" +require_canonical_path P0_EVIDENCE_ROOT "${P0_EVIDENCE_ROOT}" directory +require_canonical_path VALIDATION_REPORT_PATH "${VALIDATION_REPORT_PATH}" file + +P0_ANALYSIS_SHA256="44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b" +if [[ "$(sha256sum -- "${P0_ANALYSIS_PATH}" | awk '{print $1}')" != "${P0_ANALYSIS_SHA256}" ]]; then + echo "P0 analysis SHA256 does not match the frozen canonical artifact" >&2 + exit 65 +fi + +# Eliminate inherited compiler/runtime controls before pinning the approved set. +while IFS= read -r variable; do + unset "${variable}" +done < <(compgen -A variable NUMBA_) +unset PYTHONHOME PYTHONUSERBASE PYTHONPATH PYTHONSTARTUP PYTHONINSPECT \ + PYTHONWARNINGS PYTHONBREAKPOINT PYTHONSAFEPATH \ + LD_PRELOAD LD_LIBRARY_PATH LD_AUDIT LIBRARY_PATH + +export NUMBA_DISABLE_JIT=0 +export OMP_NUM_THREADS=1 +export OPENBLAS_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NUMEXPR_NUM_THREADS=1 +export VECLIB_MAXIMUM_THREADS=1 +export NUMBA_NUM_THREADS=1 +export PYTHONHASHSEED=0 +export PYTHONUNBUFFERED=1 +export PYTHONNOUSERSITE=1 + +NUMBA_CACHE_BASE="${SLURM_TMPDIR:-${TMPDIR:-/tmp}}" +if [[ "${NUMBA_CACHE_BASE}" != /* || -L "${NUMBA_CACHE_BASE}" || \ + ! -d "${NUMBA_CACHE_BASE}" || ! -w "${NUMBA_CACHE_BASE}" ]]; then + echo "node-local temporary directory must be an absolute writable directory" >&2 + exit 73 +fi +if [[ "$(realpath -s -- "${NUMBA_CACHE_BASE}")" != "$(realpath -e -- "${NUMBA_CACHE_BASE}")" ]]; then + echo "node-local temporary directory must not contain symlink components" >&2 + exit 73 +fi +export NUMBA_CACHE_DIR="${NUMBA_CACHE_BASE%/}/challenge-194-p0-extension-build-${SLURM_JOB_ID}" +case "$(realpath -m -- "${NUMBA_CACHE_DIR}")" in + "$(realpath -e -- "${NUMBA_CACHE_BASE}")"/*) ;; + *) + echo "NUMBA cache path escapes node-local temporary directory" >&2 + exit 73 + ;; +esac +umask 077 +if ! mkdir -- "${NUMBA_CACHE_DIR}"; then + echo "NUMBA cache directory must be uniquely created by this job" >&2 + exit 73 +fi +if [[ -L "${NUMBA_CACHE_DIR}" || ! -d "${NUMBA_CACHE_DIR}" || \ + ! -O "${NUMBA_CACHE_DIR}" || ! -w "${NUMBA_CACHE_DIR}" || \ + "$(realpath -e -- "${NUMBA_CACHE_DIR}")" != "${NUMBA_CACHE_DIR}" ]]; then + echo "NUMBA cache directory is not a safe writable directory" >&2 + exit 73 +fi +shopt -s nullglob dotglob +CACHE_ENTRIES=("${NUMBA_CACHE_DIR}"/*) +shopt -u nullglob dotglob +if (( ${#CACHE_ENTRIES[@]} != 0 )); then + echo "new NUMBA cache directory is unexpectedly non-empty" >&2 + exit 73 +fi + +cd "${SOLUTION_ROOT}" +export PYTHONPATH="${SOLUTION_ROOT}/src" +"${CHALLENGE_194_PYTHON}" scripts/analyze_pilot.py build-p0-extension \ + --analysis "${P0_ANALYSIS_PATH}" \ + --p0-evidence-root "${P0_EVIDENCE_ROOT}" \ + --output "${EXTENSION_PROTOCOL_PATH}" +"${CHALLENGE_194_PYTHON}" scripts/run_pilot.py build-extension-spec \ + --protocol "${EXTENSION_PROTOCOL_PATH}" \ + --validation-report "${VALIDATION_REPORT_PATH}" \ + --analysis "${P0_ANALYSIS_PATH}" \ + --p0-evidence-root "${P0_EVIDENCE_ROOT}" \ + --output-root "${EXTENSION_ROOT}" \ + --run-spec "${EXTENSION_ROOT}/run_spec.json" diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py new file mode 100755 index 000000000..91a704c3b --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +from long_range_percolation.pilot import ( + PILOT_RUN_SPEC_MAX_BYTES, + RUN_SPEC_NAME, + RUN_SPEC_SCHEMA, + _read_canonical, + _registered_schema, + build_p0_extension_run_spec, + build_pilot_run_spec, + merge_p0_extension_progress, + merge_pilot_progress, + pending_p0_extension_cells, + pending_pilot_cells, + run_p0_extension_cell, + run_pilot_cell, + verify_p0_extension_download, + verify_pilot_download, +) +from long_range_percolation.pilot_extension import ( + EXTENSION_RUN_SPEC_SCHEMA, + P0_ANALYSIS_MAX_BYTES, +) + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Build, run, merge, and verify Challenge 194 Pilot P0 cells." + ) + commands = parser.add_subparsers(dest="command", required=True) + + build = commands.add_parser("build-spec") + build.add_argument("--output-root", type=Path, required=True) + build.add_argument("--run-spec", type=Path, required=True) + build.add_argument("--validation-report", type=Path, required=True) + + extension = commands.add_parser("build-extension-spec") + extension.add_argument("--protocol", type=Path, required=True) + extension.add_argument("--validation-report", type=Path, required=True) + extension.add_argument("--analysis", type=Path, required=True) + extension.add_argument("--p0-evidence-root", type=Path, required=True) + extension.add_argument("--output-root", type=Path, required=True) + extension.add_argument("--run-spec", type=Path, required=True) + + cell = commands.add_parser("run-cell") + cell.add_argument("--run-spec", type=Path, required=True) + cell.add_argument("--cell-index", type=int, required=True) + + merge = commands.add_parser("merge") + merge.add_argument("--run-spec", type=Path, required=True) + merge.add_argument("--output", type=Path) + + verify = commands.add_parser("verify") + verify.add_argument("--run-spec", type=Path, required=True) + + pending = commands.add_parser("pending") + pending.add_argument("--run-spec", type=Path, required=True) + return parser + + +def _registered_operations(run_spec: Path): + schema = _registered_schema(run_spec) + if schema == RUN_SPEC_SCHEMA: + return ( + run_pilot_cell, + pending_pilot_cells, + merge_pilot_progress, + verify_pilot_download, + ) + if schema == EXTENSION_RUN_SPEC_SCHEMA: + return ( + run_p0_extension_cell, + pending_p0_extension_cells, + merge_p0_extension_progress, + verify_p0_extension_download, + ) + raise RuntimeError("registered Pilot run-spec schema is not supported") + + +def main(argv: list[str] | None = None) -> int: + arguments = _parser().parse_args(argv) + try: + if arguments.command == "build-spec": + output_root = arguments.output_root.resolve() + run_spec = arguments.run_spec.resolve() + if run_spec != output_root / RUN_SPEC_NAME: + raise RuntimeError("--run-spec must equal /run_spec.json") + document = build_pilot_run_spec( + output_root, arguments.validation_report.resolve() + ) + result = { + "status": "ready", + "cells": document["cell_count"], + "run_spec": str(run_spec), + "run_spec_sha256": document["run_spec_sha256"], + } + elif arguments.command == "build-extension-spec": + output_root = arguments.output_root.resolve() + run_spec = arguments.run_spec.resolve() + if run_spec != output_root / RUN_SPEC_NAME: + raise RuntimeError("--run-spec must equal /run_spec.json") + protocol, _ = _read_canonical( + arguments.protocol.resolve(), + "P0 extension protocol", + maximum_size=PILOT_RUN_SPEC_MAX_BYTES, + ) + p0_analysis, _ = _read_canonical( + arguments.analysis.resolve(), + "P0 analysis document", + maximum_size=P0_ANALYSIS_MAX_BYTES, + ) + document = build_p0_extension_run_spec( + output_root, + arguments.validation_report.resolve(), + protocol, + p0_analysis, + arguments.p0_evidence_root, + ) + result = { + "status": "ready", + "cells": 96, + "run_spec": str(run_spec), + "run_spec_sha256": document["run_spec_sha256"], + } + elif arguments.command == "run-cell": + run_cell, _, _, _ = _registered_operations(arguments.run_spec.resolve()) + print(f"pilot cell {arguments.cell_index} started", flush=True) + result = { + "status": "success", + **run_cell(arguments.run_spec.resolve(), arguments.cell_index), + } + elif arguments.command == "merge": + _, _, merge_progress, _ = _registered_operations( + arguments.run_spec.resolve() + ) + document = merge_progress( + arguments.run_spec.resolve(), + arguments.output.resolve() if arguments.output else None, + ) + result = { + "status": "success", + "cells": document["cell_count"], + "trajectories": document["trajectory_count"], + } + elif arguments.command == "verify": + _, _, _, verify_download = _registered_operations( + arguments.run_spec.resolve() + ) + document = verify_download(arguments.run_spec.resolve()) + result = { + "status": "verified", + "cells": document["cell_count"], + "trajectories": document["trajectory_count"], + } + else: + _, pending_cells, _, _ = _registered_operations( + arguments.run_spec.resolve() + ) + cells = pending_cells(arguments.run_spec.resolve()) + result = { + "status": "pending", + "count": len(cells), + "cell_indices": cells, + } + except Exception as error: # noqa: BLE001 - CLI converts infrastructure failures. + print( + f"pilot infrastructure failure: {type(error).__name__}: {error}", + file=sys.stderr, + flush=True, + ) + return 1 + print(json.dumps(result, sort_keys=True), flush=True) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/validate_production.py b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/validate_production.py new file mode 100644 index 000000000..e513a695c --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/validate_production.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +from pathlib import Path +import sys + +from long_range_percolation.validation import ( + ValidationProtocol, + run_production_validation, + validate_report_payload, +) + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Run the fixed Challenge 194 production correctness gate." + ) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument( + "--protocol", + choices=("production-v1",), + required=True, + ) + parser.add_argument("--jobs", type=int, required=True) + return parser + + +def main(argv: list[str] | None = None) -> int: + arguments = _parser().parse_args(argv) + if arguments.jobs < 1: + _parser().error("--jobs must be a positive integer") + protocol = ValidationProtocol.production_v1() + protocol = ValidationProtocol( + lengths=protocol.lengths, + sigmas=protocol.sigmas, + kappas=protocol.kappas, + samples_by_length=protocol.samples_by_length, + master_seeds=protocol.master_seeds, + familywise_alpha=protocol.familywise_alpha, + permutation_replicates=protocol.permutation_replicates, + multinomial_replicates=protocol.multinomial_replicates, + jobs=arguments.jobs, + name=protocol.name, + ) + protocol.require_production() + try: + report = run_production_validation(protocol, arguments.output) + validate_report_payload(report, protocol) + except Exception as error: + try: + arguments.output.unlink() + except FileNotFoundError: + pass + print( + f"validation infrastructure failure: {type(error).__name__}: {error}", + file=sys.stderr, + flush=True, + ) + return 1 + print( + f"validation passed={report['passed']} " + f"families={report['family_count']} " + f"minimum_margin={report['minimum_margin']} " + f"output={arguments.output}", + flush=True, + ) + return 0 if report["passed"] else 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/validation_array_slurm.sh b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/validation_array_slurm.sh new file mode 100755 index 000000000..a7c6443fb --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/validation_array_slurm.sh @@ -0,0 +1,99 @@ +#!/bin/bash +set -euo pipefail + +: "${HARNESS_RUN_SPEC:?Set HARNESS_RUN_SPEC to the immutable run_spec.json}" +: "${SLURM_ARRAY_TASK_ID:?Run as a Slurm array task}" + +REPO_ROOT="${CHALLENGE_194_REPO_ROOT:-${HARNESS_ENTRYPOINT:-}}" +: "${REPO_ROOT:?Set CHALLENGE_194_REPO_ROOT or the harness entrypoint to the explicit shared repository path}" + +if [[ "${SLURM_CPUS_PER_TASK:-1}" != "1" ]]; then + echo "Challenge 194 validation cells require exactly one CPU" >&2 + exit 64 +fi +if [[ ! "${SLURM_ARRAY_TASK_ID}" =~ ^[0-9]+$ ]]; then + echo "SLURM_ARRAY_TASK_ID must be a nonnegative integer" >&2 + exit 64 +fi + +SOLUTION_RELATIVE="tracks/qmc/solutions/frustration-free/challenge-194" +SOLUTION_ROOT="${REPO_ROOT%/}/${SOLUTION_RELATIVE}" +if [[ ! -f "${SOLUTION_ROOT}/scripts/validation_shard.py" ]]; then + echo "Explicit Challenge 194 solution path is invalid: ${SOLUTION_ROOT}" >&2 + exit 66 +fi +if [[ ! -f "${HARNESS_RUN_SPEC}" ]]; then + echo "HARNESS_RUN_SPEC is not a regular file: ${HARNESS_RUN_SPEC}" >&2 + exit 66 +fi + +resolve_python_candidate() { + local label="$1" + local candidate="$2" + local canonical="" + local resolved="" + if [[ "${candidate}" != /* ]]; then + echo "${label} must be an absolute path" >&2 + return 66 + fi + if ! canonical="$(realpath -s -- "${candidate}" 2>/dev/null)"; then + echo "${label} is not a valid absolute path" >&2 + return 66 + fi + if ! resolved="$(realpath -e -- "${candidate}" 2>/dev/null)"; then + echo "${label} does not resolve to an existing path" >&2 + return 66 + fi + if [[ "${resolved}" != /* || ! -f "${resolved}" || ! -x "${resolved}" ]]; then + echo "${label} must resolve to a regular executable" >&2 + return 66 + fi + printf '%s\n' "${canonical}" +} + +CHALLENGE_PYTHON="" +HARNESS_PYTHON="" +if [[ "${CHALLENGE_194_PYTHON+x}" == "x" ]]; then + CHALLENGE_PYTHON="$( + resolve_python_candidate CHALLENGE_194_PYTHON "${CHALLENGE_194_PYTHON}" + )" || exit $? +fi +if [[ "${HARNESS_COMMAND+x}" == "x" ]]; then + HARNESS_PYTHON="$( + resolve_python_candidate HARNESS_COMMAND "${HARNESS_COMMAND}" + )" || exit $? +fi +if [[ -n "${CHALLENGE_PYTHON}" && -n "${HARNESS_PYTHON}" && "${CHALLENGE_PYTHON}" != "${HARNESS_PYTHON}" ]]; then + echo "interpreter conflict: CHALLENGE_194_PYTHON and HARNESS_COMMAND resolve differently" >&2 + exit 66 +fi +OFFLINE_PYTHON="${CHALLENGE_PYTHON:-${HARNESS_PYTHON}}" + +export OMP_NUM_THREADS=1 +export OPENBLAS_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NUMEXPR_NUM_THREADS=1 +export VECLIB_MAXIMUM_THREADS=1 +export PYTHONUNBUFFERED=1 + +NUMBA_CACHE_BASE="${SLURM_TMPDIR:-${TMPDIR:-/tmp}}" +if [[ "${NUMBA_CACHE_BASE}" != /* || ! -d "${NUMBA_CACHE_BASE}" || ! -w "${NUMBA_CACHE_BASE}" ]]; then + echo "node-local temporary directory must be an absolute writable directory" >&2 + exit 73 +fi +NUMBA_CACHE_JOB_ID="${SLURM_ARRAY_JOB_ID:-${SLURM_JOB_ID:-no-job-id}}" +export NUMBA_CACHE_DIR="${NUMBA_CACHE_BASE%/}/challenge-194-numba-${NUMBA_CACHE_JOB_ID}-${SLURM_ARRAY_TASK_ID}" +mkdir -p -- "${NUMBA_CACHE_DIR}" + +cd "${SOLUTION_ROOT}" +echo "validation array cell=${SLURM_ARRAY_TASK_ID} host=$(hostname)" +echo "run_spec=${HARNESS_RUN_SPEC}" +if [[ -n "${OFFLINE_PYTHON}" ]]; then + export PYTHONPATH="${SOLUTION_ROOT}/src" + exec "${OFFLINE_PYTHON}" scripts/validation_shard.py run-cell \ + --run-spec "${HARNESS_RUN_SPEC}" \ + --case-index "${SLURM_ARRAY_TASK_ID}" +fi +exec uv run scripts/validation_shard.py run-cell \ + --run-spec "${HARNESS_RUN_SPEC}" \ + --case-index "${SLURM_ARRAY_TASK_ID}" diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/scripts/validation_shard.py b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/validation_shard.py new file mode 100755 index 000000000..a3b21e457 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/scripts/validation_shard.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import sys + +from long_range_percolation.validation import ValidationProtocol +from long_range_percolation.validation_shards import ( + merge_validation_shards, + run_validation_cell, + run_validation_global_checks, + write_validation_run_spec, +) + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Build and execute immutable Challenge 194 validation shards." + ) + commands = parser.add_subparsers(dest="command", required=True) + + build = commands.add_parser("build-spec") + build.add_argument("--protocol", choices=("production-v1",), required=True) + build.add_argument("--output-root", type=Path, required=True) + build.add_argument("--run-spec", type=Path, required=True) + + global_checks = commands.add_parser("run-global") + global_checks.add_argument("--run-spec", type=Path, required=True) + + cell = commands.add_parser("run-cell") + cell.add_argument("--run-spec", type=Path, required=True) + cell.add_argument("--case-index", type=int, required=True) + + merge = commands.add_parser("merge") + merge.add_argument("--run-spec", type=Path, required=True) + merge.add_argument("--output", type=Path, required=True) + return parser + + +def _production_protocol() -> ValidationProtocol: + protocol = ValidationProtocol.production_v1() + protocol.require_production() + return protocol + + +def main(argv: list[str] | None = None) -> int: + arguments = _parser().parse_args(argv) + try: + if arguments.command == "build-spec": + if arguments.run_spec.parent.resolve() != ( + arguments.output_root.resolve() + ): + raise ValueError("--run-spec must be directly under --output-root") + document = write_validation_run_spec( + _production_protocol(), + arguments.output_root, + arguments.run_spec, + ) + result = { + "status": "ready", + "cells": len(document["cells"]), + "run_spec": str(arguments.run_spec), + "run_spec_sha256": document["run_spec_sha256"], + } + elif arguments.command == "run-global": + manifest = run_validation_global_checks(arguments.run_spec) + result = { + "status": "success", + "artifact": manifest["artifact_path"], + "sha256": manifest["artifact_sha256"], + } + elif arguments.command == "run-cell": + print( + f"validation cell {arguments.case_index} started", + flush=True, + ) + result = { + "status": "success", + **run_validation_cell( + arguments.run_spec, arguments.case_index + ), + } + else: + report = merge_validation_shards( + arguments.run_spec, arguments.output + ) + result = { + "status": "success" if report["passed"] else "scientific-failure", + "passed": report["passed"], + "families": report["family_count"], + "minimum_margin": report["minimum_margin"], + "output": str(arguments.output), + } + print(json.dumps(result, sort_keys=True), flush=True) + return 0 if report["passed"] else 2 + except Exception as error: + print( + f"validation shard infrastructure failure: " + f"{type(error).__name__}: {error}", + file=sys.stderr, + flush=True, + ) + return 1 + print(json.dumps(result, sort_keys=True), flush=True) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/__init__.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/__init__.py new file mode 100644 index 000000000..f71de3f41 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/__init__.py @@ -0,0 +1,51 @@ +from long_range_percolation.enumeration import ( + GraphOutcome, + enumerate_graphs, + exact_partition_distribution, +) +from long_range_percolation.geometric import sample_geometric +from long_range_percolation.kernel import ( + edge_probabilities, + kernel_weight_sum, + periodic_kernel, + periodic_kernel_reference, +) +from long_range_percolation.model import ( + DistanceClass, + ModelSpec, + canonical_edge, + distance_classes, + iter_unordered_edges, +) +from long_range_percolation.oracle import ( + expected_open_edges, + no_edge_probability, + sample_quadratic, + variance_open_edges, +) +from long_range_percolation.poisson_sweep import run_poisson_numba +from long_range_percolation.sample import GraphSample +from long_range_percolation.union_find import UnionFind + +__all__ = [ + "DistanceClass", + "GraphOutcome", + "GraphSample", + "ModelSpec", + "UnionFind", + "enumerate_graphs", + "exact_partition_distribution", + "canonical_edge", + "distance_classes", + "edge_probabilities", + "expected_open_edges", + "iter_unordered_edges", + "kernel_weight_sum", + "no_edge_probability", + "periodic_kernel", + "periodic_kernel_reference", + "run_poisson_numba", + "sample_geometric", + "sample_quadratic", + "variance_open_edges", +] diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/alias.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/alias.py new file mode 100644 index 000000000..094dfa25b --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/alias.py @@ -0,0 +1,162 @@ +from __future__ import annotations + +from dataclasses import dataclass +from hashlib import sha256 +import math + +import numba +import numpy as np +import numpy.typing as npt + +from .model import ModelSpec, distance_classes + + +F64 = npt.NDArray[np.float64] +I64 = npt.NDArray[np.int64] +U64 = npt.NDArray[np.uint64] + + +@dataclass(frozen=True) +class AliasTable: + probability: F64 + alias: I64 + multiplicity: U64 + class_weight: F64 + total_rate: float + kernel_sha256: str + normalized_residual: float + + +def _freeze(array: np.ndarray) -> np.ndarray: + frozen = np.ascontiguousarray(array).copy() + frozen.setflags(write=False) + return frozen + + +def build_distance_alias( + length: int, + sigma: float, + kernel: F64, + kernel_sha256: str, +) -> AliasTable: + ModelSpec(length=length, sigma=sigma, kappa=0.0) + values = np.asarray(kernel, dtype=np.float64) + class_count = length // 2 + if values.shape != (class_count,): + raise ValueError( + f"kernel must have exact shape ({class_count},)" + ) + values = np.ascontiguousarray(values).copy() + if not np.all(np.isfinite(values)) or np.any(values <= 0.0): + raise ValueError("kernel must contain finite positive values") + actual_sha256 = sha256(values.tobytes()).hexdigest() + if ( + not isinstance(kernel_sha256, str) + or kernel_sha256 != actual_sha256 + ): + raise ValueError("kernel SHA-256 does not match kernel values") + + multiplicity = np.fromiter( + (item.multiplicity for item in distance_classes(length)), + dtype=np.uint64, + count=class_count, + ) + class_weight = np.multiply( + multiplicity, values, dtype=np.float64 + ) + if ( + not np.all(np.isfinite(class_weight)) + or np.any(class_weight <= 0.0) + ): + raise ValueError("class weights must be finite and positive") + total_rate = math.fsum(float(value) for value in class_weight) + if not math.isfinite(total_rate) or total_rate <= 0.0: + raise ValueError("total rate must be finite and positive") + + normalized = class_weight / total_rate + normalized_residual = ( + math.fsum(float(value) for value in normalized) - 1.0 + ) + scaled = normalized * float(class_count) + probability = np.empty(class_count, dtype=np.float64) + alias = np.arange(class_count, dtype=np.int64) + + capacity = 2 * class_count + small = np.empty(capacity, dtype=np.int64) + large = np.empty(capacity, dtype=np.int64) + small_head = 0 + small_tail = 0 + large_head = 0 + large_tail = 0 + for index in range(class_count): + if scaled[index] < 1.0: + small[small_tail] = index + small_tail += 1 + else: + large[large_tail] = index + large_tail += 1 + + while small_head < small_tail and large_head < large_tail: + small_index = int(small[small_head]) + small_head += 1 + large_index = int(large[large_head]) + large_head += 1 + probability[small_index] = scaled[small_index] + alias[small_index] = large_index + scaled[large_index] -= 1.0 - scaled[small_index] + if scaled[large_index] < 1.0: + small[small_tail] = large_index + small_tail += 1 + else: + large[large_tail] = large_index + large_tail += 1 + + while small_head < small_tail: + index = int(small[small_head]) + small_head += 1 + probability[index] = 1.0 + alias[index] = index + while large_head < large_tail: + index = int(large[large_head]) + large_head += 1 + probability[index] = 1.0 + alias[index] = index + + tolerance = 8.0 * np.finfo(np.float64).eps + if np.any(probability < -tolerance) or np.any( + probability > 1.0 + tolerance + ): + raise ValueError("alias probability exceeds roundoff tolerance") + np.clip(probability, 0.0, 1.0, out=probability) + if np.any(alias < 0) or np.any(alias >= class_count): + raise ValueError("alias index is outside the distance classes") + + return AliasTable( + probability=_freeze(probability), + alias=_freeze(alias), + multiplicity=_freeze(multiplicity), + class_weight=_freeze(class_weight), + total_rate=total_rate, + kernel_sha256=actual_sha256, + normalized_residual=normalized_residual, + ) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def draw_alias( + probability: F64, + alias: I64, + column_word: np.uint32, + threshold_word: np.uint32, +) -> int: + """Draw using a column word already accepted by Lemire rejection.""" + class_count = len(probability) + column = np.int64( + ( + np.uint64(column_word) * np.uint64(class_count) + ) >> np.uint64(32) + ) + threshold = (float(threshold_word) + 0.5) * (2.0**-32) + if threshold <= probability[column]: + return column + return alias[column] diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/artifacts.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/artifacts.py new file mode 100644 index 000000000..a0056222f --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/artifacts.py @@ -0,0 +1,2072 @@ +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from contextlib import ExitStack, contextmanager +from dataclasses import dataclass +import fcntl +import hashlib +import json +import os +from pathlib import Path +import re +import stat +from typing import BinaryIO +import uuid + +try: + import resource +except ImportError: # pragma: no cover - unavailable on some non-POSIX hosts + resource = None # type: ignore[assignment] + +import h5py +import numpy as np + +from .counter_rng import ( + RNG_VERSION, + STREAM_COUNT, + StreamIdentity, + derive_stream_material, +) +from .trajectory import ( + TrajectoryRequest, + TrajectoryResult, + request_digest, + validate_trajectory_request, +) + + +TRAJECTORY_SCHEMA = "challenge-194-trajectory-artifact-v2" +TRAJECTORY_DIGEST_SCHEMA = "challenge-194-trajectory-digest-v2" +BATCH_SCHEMA = "challenge-194-batch-manifest-v2" +PROGRESS_SCHEMA = "challenge-194-progress-v2" +CONVERSION_VERSION = "challenge-194-artifact-conversion-v1" +MAX_HDF5_BYTES = 67_108_864 +MAX_KAPPA_COUNT = 4096 +MAX_DATASET_BYTES = 1_048_576 +MAX_BATCH_MEMBERS = 4096 +MAX_KERNEL_FILES = 16 +MAX_KERNEL_FILE_BYTES = 8 * 1024 * 1024 +MAX_KERNEL_TOTAL_BYTES = 32 * 1024 * 1024 +MAX_RETAINED_METADATA_DESCRIPTORS = 1 + 5 + MAX_KERNEL_FILES +FD_RESERVE = 32 +MAX_ROOT_ENTRIES = 9 +MAX_TRAJECTORY_DIRECTORY_ENTRIES = 2 * MAX_BATCH_MEMBERS +MAX_CANONICAL_TRAJECTORY_RECORD_BYTES = 273 +MAX_CANONICAL_BATCH_PROGRESS_RECORD_BYTES = 322 +MAX_JSON_SAFETY_BYTES = 65_536 +MAX_TRAJECTORY_NUMERICAL_BYTES = ( + MAX_KAPPA_COUNT * 8 + + MAX_KAPPA_COUNT * 10 * 8 + + 4 * 4 * 4 + + 4 * 3 * 8 + + 5 * 8 + + 4 * 4 * 4 + + 4 * 2 * 8 + + 4 * 64 +) +MAX_PROGRESS_RECORD_BYTES = ( + MAX_BATCH_MEMBERS + * ( + MAX_CANONICAL_TRAJECTORY_RECORD_BYTES + + MAX_CANONICAL_BATCH_PROGRESS_RECORD_BYTES + ) + + MAX_JSON_SAFETY_BYTES +) +MAX_RECONSTRUCTION_PEAK_BYTES = ( + MAX_TRAJECTORY_NUMERICAL_BYTES + MAX_PROGRESS_RECORD_BYTES +) +# Worst progress is 4,096 trajectory records plus 4,096 one-member batch +# summaries: 2,437,120 bytes, plus bounded structural/version/count fields. +# Four MiB therefore retains more than the explicit 65,536-byte safety margin. +MAX_JSON_BYTES = 4_194_304 + +_HEX256 = re.compile(r"[0-9a-f]{64}") +_HEX160 = re.compile(r"[0-9a-f]{40}") +_SAFE_BATCH_ID = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,127}") +_TRAJECTORY_NAME = re.compile(r"trajectory-([0-9a-f]{64})\.h5") +_DIGEST_NAME = re.compile(r"trajectory-([0-9a-f]{64})\.sha256\.json") +_BATCH_NAME = re.compile(r"batch-([A-Za-z0-9][A-Za-z0-9._-]{0,127})\.json") +_PROVENANCE_KEYS = frozenset( + { + "source_revision", + "clean_tree", + "uv_lock_sha256", + "runtime_capability_sha256", + "analysis_plan_sha256", + "rng_sha256", + "conversion_version", + "rng_version", + } +) +_EXPECTED_KEYS = frozenset( + { + "request_sha256", + "kernel_sha256", + "source_revision", + "uv_lock_sha256", + "runtime_capability_sha256", + "analysis_plan_sha256", + "rng_sha256", + "conversion_version", + "rng_version", + } +) +_ROOT_ENTRIES = frozenset( + { + "request.json", + "environment.json", + "kernel", + "seed-manifest.json", + "capability.json", + "trajectories", + "batches", + "progress.json", + "manifest.json", + } +) +_UPSTREAM_FILES = frozenset( + { + "request.json", + "environment.json", + "seed-manifest.json", + "capability.json", + "manifest.json", + } +) +_UPSTREAM_ENTRIES = _UPSTREAM_FILES | {"kernel"} +_OWNED_DIRECTORIES = frozenset({"trajectories", "batches"}) +_REQUIRED_ROOT_ENTRIES = _UPSTREAM_ENTRIES | _OWNED_DIRECTORIES +_ROOT_DIRECTORY_ENTRIES = _OWNED_DIRECTORIES | {"kernel"} + + +class ArtifactIntegrityError(RuntimeError): + """An immutable artifact cannot be trusted or resumed.""" + + +_SNAPSHOT_IDENTITY_TOKEN = object() + + +@dataclass(frozen=True) +class _VerifiedMetadataSnapshot: + run_dir: Path + digest: str + file_generations: tuple[ + tuple[Path, tuple[int, int, int, int, int, int, int]], ... + ] + kernel_generation: tuple[int, int, int, int, int, int, int] + kernel_names: tuple[str, ...] + _token: object + + def verify_final_boundary(self) -> None: + for path, generation in self.file_generations: + try: + current = path.lstat() + except OSError as error: + raise ArtifactIntegrityError( + "run metadata changed during reconstruction" + ) from error + if ( + not stat.S_ISREG(current.st_mode) + or _generation_tuple(current) != generation + ): + raise ArtifactIntegrityError( + "run metadata generation changed during reconstruction" + ) + kernel = self.run_dir / "kernel" + try: + current_kernel = kernel.lstat() + except OSError as error: + raise ArtifactIntegrityError( + "kernel metadata changed during reconstruction" + ) from error + names = tuple( + path.name + for path in _bounded_directory_entries( + kernel, MAX_KERNEL_FILES, "kernel metadata" + ) + ) + if ( + _generation_tuple(current_kernel) != self.kernel_generation + or names != self.kernel_names + ): + raise ArtifactIntegrityError( + "kernel metadata generation changed during reconstruction" + ) + + +def _require_private_metadata_snapshot( + value: object, + run_dir: Path, +) -> _VerifiedMetadataSnapshot: + if ( + type(value) is not _VerifiedMetadataSnapshot + or value._token is not _SNAPSHOT_IDENTITY_TOKEN + or value.run_dir != run_dir + or _HEX256.fullmatch(value.digest) is None + or not 1 <= len(value.file_generations) <= 5 + MAX_KERNEL_FILES + or not 1 <= len(value.kernel_names) <= MAX_KERNEL_FILES + or tuple(sorted(value.kernel_names)) != value.kernel_names + or len(set(value.kernel_names)) != len(value.kernel_names) + or len(value.kernel_generation) != 7 + or not all(type(item) is int for item in value.kernel_generation) + ): + raise ArtifactIntegrityError("invalid private metadata snapshot capability") + allowed_parents = {run_dir, run_dir / "kernel"} + expected_paths = { + *(run_dir / name for name in _UPSTREAM_FILES), + *(run_dir / "kernel" / name for name in value.kernel_names), + } + actual_paths = {path for path, _ in value.file_generations} + if ( + actual_paths != expected_paths + or len(actual_paths) != len(value.file_generations) + ): + raise ArtifactIntegrityError("invalid private metadata snapshot paths") + for path, generation in value.file_generations: + if ( + not isinstance(path, Path) + or path.parent not in allowed_parents + or len(generation) != 7 + or not all(type(item) is int for item in generation) + ): + raise ArtifactIntegrityError( + "invalid private metadata snapshot generation" + ) + return value + + +def _canonical_json_bytes(document: object) -> bytes: + try: + return ( + json.dumps( + document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + + b"\n" + ) + except ( + TypeError, + ValueError, + UnicodeEncodeError, + RecursionError, + OverflowError, + ) as error: + raise ArtifactIntegrityError("document is not canonical JSON") from error + + +def _checked_regular(path: Path, description: str) -> os.stat_result: + try: + metadata = path.lstat() + except OSError as error: + raise ArtifactIntegrityError(f"unable to inspect {description}") from error + if stat.S_ISLNK(metadata.st_mode): + raise ArtifactIntegrityError(f"{description} must not be a symlink") + if not stat.S_ISREG(metadata.st_mode): + raise ArtifactIntegrityError(f"{description} must be a regular file") + return metadata + + +def _open_regular( + path: Path, + description: str, + *, + maximum_size: int | None = None, +) -> tuple[int, os.stat_result]: + flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_NONBLOCK", 0) + try: + descriptor = os.open(path, flags) + except OSError as error: + raise ArtifactIntegrityError(f"unable to open {description}") from error + try: + metadata = os.fstat(descriptor) + if not stat.S_ISREG(metadata.st_mode): + raise ArtifactIntegrityError(f"{description} must be a regular file") + if maximum_size is not None and metadata.st_size > maximum_size: + raise ArtifactIntegrityError(f"{description} exceeds the byte-size limit") + return descriptor, metadata + except BaseException: + os.close(descriptor) + raise + + +def _file_identity(metadata: os.stat_result) -> tuple[int, int, int, int, int]: + return ( + metadata.st_dev, + metadata.st_ino, + metadata.st_size, + metadata.st_mtime_ns, + metadata.st_ctime_ns, + ) + + +def _generation_tuple( + metadata: os.stat_result, +) -> tuple[int, int, int, int, int, int, int]: + return ( + metadata.st_dev, + metadata.st_ino, + metadata.st_mode, + metadata.st_nlink, + metadata.st_size, + metadata.st_mtime_ns, + metadata.st_ctime_ns, + ) + + +def _soft_fd_limit() -> int | None: + if resource is None: + return None + try: + soft, _ = resource.getrlimit(resource.RLIMIT_NOFILE) + except (OSError, ValueError): + return None + return None if soft == resource.RLIM_INFINITY else int(soft) + + +def _current_open_fd_count() -> int | None: + proc = Path("/proc/self/fd") + try: + count = 0 + with os.scandir(proc) as stream: + for _ in stream: + count += 1 + if count > 1_000_000: + return None + return count + except OSError: + return None + + +def _preflight_metadata_descriptors(required: int) -> None: + soft = _soft_fd_limit() + current = _current_open_fd_count() + needed = required + FD_RESERVE + (current if current is not None else 0) + if soft is not None and soft < needed: + raise ArtifactIntegrityError( + "RLIMIT_NOFILE is insufficient for retained metadata descriptors" + ) + + +def _require_stable_descriptor( + descriptor: int, + original: os.stat_result, + description: str, +) -> os.stat_result: + try: + current = os.fstat(descriptor) + except OSError as error: + raise ArtifactIntegrityError(f"unable to restat {description}") from error + if _file_identity(current) != _file_identity(original): + raise ArtifactIntegrityError(f"{description} identity or size mutated") + return current + + +def _require_path_identity( + path: Path, + original: os.stat_result, + description: str, +) -> None: + try: + current = path.lstat() + except OSError as error: + raise ArtifactIntegrityError(f"{description} pathname identity changed") from error + if ( + stat.S_ISLNK(current.st_mode) + or current.st_dev != original.st_dev + or current.st_ino != original.st_ino + ): + raise ArtifactIntegrityError(f"{description} pathname identity changed") + + +def _read_descriptor_bounded( + descriptor: int, + maximum_size: int, + description: str, +) -> bytes: + try: + os.lseek(descriptor, 0, os.SEEK_SET) + chunks: list[bytes] = [] + remaining = maximum_size + 1 + while remaining: + block = os.read(descriptor, min(remaining, 64 * 1024)) + if not block: + break + chunks.append(block) + remaining -= len(block) + except OSError as error: + raise ArtifactIntegrityError(f"unable to read {description}") from error + payload = b"".join(chunks) + if len(payload) > maximum_size: + raise ArtifactIntegrityError(f"{description} exceeds the byte-size limit") + return payload + + +def _hash_descriptor(descriptor: int, description: str) -> tuple[str, int]: + digest = hashlib.sha256() + size = 0 + try: + os.lseek(descriptor, 0, os.SEEK_SET) + while block := os.read(descriptor, 1024 * 1024): + digest.update(block) + size += len(block) + except OSError as error: + raise ArtifactIntegrityError(f"unable to hash {description}") from error + return digest.hexdigest(), size + + +def _check_existing_path_chain(path: Path) -> None: + absolute = path.absolute() + current = Path(absolute.anchor) + for component in absolute.parts[1:]: + current = current / component + try: + metadata = current.lstat() + except FileNotFoundError: + continue + except OSError as error: + raise ArtifactIntegrityError("unable to inspect run path") from error + if stat.S_ISLNK(metadata.st_mode): + raise ArtifactIntegrityError("run path must not contain symlinks") + + +def _initialize_owned_namespaces(run_dir: Path) -> None: + marker = run_dir / f".task9-init.{os.getpid()}.{uuid.uuid4().hex}.intent" + _write_unique_fsynced( + marker, + _canonical_json_bytes( + {"schema_version": "challenge-194-task9-initialization-v1"} + ), + ) + _fsync_directory_raw(run_dir) + try: + for name in sorted(_OWNED_DIRECTORIES): + (run_dir / name).mkdir() + _fsync_directory_raw(run_dir) + marker.unlink() + _fsync_directory_raw(run_dir) + except BaseException: + raise + + +def _prepare_publication_run( + run_dir: Path, + expected: dict[str, str], +) -> tuple[Path, Path, Path, str]: + if not isinstance(run_dir, Path): + raise TypeError("run_dir must be a pathlib.Path") + _check_existing_path_chain(run_dir) + try: + mode = run_dir.lstat().st_mode + except OSError as error: + raise ArtifactIntegrityError("upstream run metadata directory is missing") from error + if stat.S_ISLNK(mode) or not stat.S_ISDIR(mode): + raise ArtifactIntegrityError("run directory must be a non-symlink directory") + metadata_digest = _verify_upstream_metadata(run_dir, expected) + assert isinstance(metadata_digest, str) + with _directory_lock(run_dir): + names = { + path.name + for path in _bounded_directory_entries( + run_dir, MAX_ROOT_ENTRIES, "run root" + ) + } + for name in _OWNED_DIRECTORIES & names: + metadata = (run_dir / name).lstat() + if stat.S_ISLNK(metadata.st_mode): + raise ArtifactIntegrityError( + f"Task 9 namespace {name} must not be a symlink" + ) + owned = names & _OWNED_DIRECTORIES + if not owned: + if names != _UPSTREAM_ENTRIES: + raise ArtifactIntegrityError( + "run layout contains unknown entries before Task 9 initialization" + ) + _initialize_owned_namespaces(run_dir) + elif owned != _OWNED_DIRECTORIES: + raise ArtifactIntegrityError("Task 9 namespace initialization is incomplete") + _verify_run_layout(run_dir, expected) + return ( + run_dir, + run_dir / "trajectories", + run_dir / "batches", + metadata_digest, + ) + + +@contextmanager +def _directory_lock(directory: Path): + flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0) + descriptor = os.open(directory, flags) + try: + fcntl.flock(descriptor, fcntl.LOCK_EX) + yield + finally: + fcntl.flock(descriptor, fcntl.LOCK_UN) + os.close(descriptor) + + +def _fsync_directory_raw(directory: Path) -> None: + flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0) + descriptor = os.open(directory, flags) + try: + os.fsync(descriptor) + finally: + os.close(descriptor) + + +def _fsync_directory(directory: Path) -> None: + _fsync_directory_raw(directory) + + +def _flush_hdf5(stream: h5py.File) -> None: + stream.flush() + + +def _fsync_file(stream: h5py.File) -> None: + handle = stream.id.get_vfd_handle() + if not isinstance(handle, int): + raise ArtifactIntegrityError("HDF5 driver did not expose a file descriptor") + os.fsync(handle) + + +def _install_no_clobber(source: Path, destination: Path) -> None: + try: + os.link(source, destination, follow_symlinks=False) + except FileExistsError: + raise FileExistsError(f"immutable artifact already exists: {destination}") + source_stat = _checked_regular(source, "staged artifact") + destination_stat = _checked_regular(destination, "installed artifact") + if ( + source_stat.st_dev != destination_stat.st_dev + or source_stat.st_ino != destination_stat.st_ino + ): + raise ArtifactIntegrityError("installed artifact inode identity mismatch") + + +def _replace(source: Path, destination: Path) -> None: + # Kept as the explicit publication boundary used by crash-injection tests. + _install_no_clobber(source, destination) + + +def _hash_file(path: Path) -> tuple[str, int]: + descriptor, original = _open_regular( + path, "trajectory", maximum_size=MAX_HDF5_BYTES + ) + try: + result = _hash_descriptor(descriptor, "trajectory") + _require_stable_descriptor(descriptor, original, "trajectory") + return result + finally: + os.close(descriptor) + + +def _write_unique_fsynced(path: Path, payload: bytes) -> None: + flags = ( + os.O_WRONLY + | os.O_CREAT + | os.O_EXCL + | getattr(os, "O_NOFOLLOW", 0) + ) + descriptor = os.open(path, flags, 0o600) + try: + view = memoryview(payload) + while view: + written = os.write(descriptor, view) + if written <= 0: + raise OSError("short write") + view = view[written:] + os.fsync(descriptor) + finally: + os.close(descriptor) + + +def _verify_installed_bytes(path: Path, payload: bytes, description: str) -> None: + descriptor, original = _open_regular( + path, description, maximum_size=MAX_JSON_BYTES + ) + try: + installed = _read_descriptor_bounded( + descriptor, MAX_JSON_BYTES, description + ) + _require_stable_descriptor(descriptor, original, description) + _require_path_identity(path, original, description) + finally: + os.close(descriptor) + if installed != payload: + raise ArtifactIntegrityError(f"{description} installed bytes mismatch") + + +def _publish_json_once( + path: Path, + document: object, + schema: str, +) -> None: + payload = _canonical_json_bytes(document) + if len(payload) > MAX_JSON_BYTES: + raise ArtifactIntegrityError("JSON publication exceeds the byte-size limit") + if ( + not isinstance(document, dict) + or document.get("schema_version") != schema + ): + raise ArtifactIntegrityError("JSON publication schema is invalid") + if schema == BATCH_SCHEMA and set(document) != { + "batch_id", + "members", + "schema_version", + }: + raise ArtifactIntegrityError("batch publication fields are not exact") + if schema == PROGRESS_SCHEMA and set(document) != { + "batch_count", + "batches", + "schema_version", + "trajectory_count", + "trajectories", + }: + raise ArtifactIntegrityError("progress publication fields are not exact") + partial = path.parent / f".{path.name}.{os.getpid()}.{uuid.uuid4().hex}.partial" + try: + _write_unique_fsynced(partial, payload) + try: + _install_no_clobber(partial, path) + except FileExistsError: + raise FileExistsError(f"immutable artifact already exists: {path}") + _verify_installed_bytes(path, payload, "published JSON artifact") + partial.unlink() + _fsync_directory(path.parent) + except BaseException: + # A partial is intentionally retained across publication failures. + raise + + +def _validate_provenance(provenance: dict[str, object]) -> None: + if not isinstance(provenance, dict) or set(provenance) != _PROVENANCE_KEYS: + raise ArtifactIntegrityError("provenance fields are not exact") + if provenance["clean_tree"] is not True: + raise ArtifactIntegrityError("dirty-tree provenance is forbidden") + if ( + not isinstance(provenance["source_revision"], str) + or _HEX160.fullmatch(provenance["source_revision"]) is None + ): + raise ArtifactIntegrityError("source revision is malformed") + for key in ( + "uv_lock_sha256", + "runtime_capability_sha256", + "rng_sha256", + ): + if not isinstance(provenance[key], str) or _HEX256.fullmatch( + provenance[key] + ) is None: + raise ArtifactIntegrityError(f"{key} is malformed") + analysis = provenance["analysis_plan_sha256"] + if analysis != "not-created-pre-pilot" and ( + not isinstance(analysis, str) or _HEX256.fullmatch(analysis) is None + ): + raise ArtifactIntegrityError("analysis plan hash is malformed") + if provenance["rng_version"] != RNG_VERSION: + raise ArtifactIntegrityError("RNG version is stale") + if provenance["conversion_version"] != CONVERSION_VERSION: + raise ArtifactIntegrityError("conversion version is stale") + + +def _validate_expected(expected: dict[str, str]) -> None: + if not isinstance(expected, dict) or set(expected) != _EXPECTED_KEYS: + raise ArtifactIntegrityError("expected dependency fields are not exact") + for key, value in expected.items(): + if not isinstance(value, str): + raise ArtifactIntegrityError(f"expected {key} is not a string") + if _HEX256.fullmatch(expected["request_sha256"]) is None: + raise ArtifactIntegrityError("expected request hash is malformed") + if _HEX256.fullmatch(expected["kernel_sha256"]) is None: + raise ArtifactIntegrityError("expected kernel hash is malformed") + if _HEX160.fullmatch(expected["source_revision"]) is None: + raise ArtifactIntegrityError("expected source revision is malformed") + for key in ("uv_lock_sha256", "runtime_capability_sha256", "rng_sha256"): + if _HEX256.fullmatch(expected[key]) is None: + raise ArtifactIntegrityError(f"expected {key} is malformed") + analysis = expected["analysis_plan_sha256"] + if analysis != "not-created-pre-pilot" and _HEX256.fullmatch(analysis) is None: + raise ArtifactIntegrityError("expected analysis plan hash is malformed") + if expected["rng_version"] != RNG_VERSION: + raise ArtifactIntegrityError("expected RNG version is stale") + if expected["conversion_version"] != CONVERSION_VERSION: + raise ArtifactIntegrityError("expected conversion version is stale") + + +def _stream_material(request: TrajectoryRequest) -> tuple[np.ndarray, np.ndarray, list[str]]: + materials = [ + derive_stream_material( + StreamIdentity( + master_seed=request.master_seed, + phase=request.phase, + length=request.length, + sigma_grid_id=request.sigma_grid_id, + replica=request.replica, + stream_id=stream_id, + ) + ) + for stream_id in range(STREAM_COUNT) + ] + hashes = [material.material_sha256 for material in materials] + if len(hashes) != len(set(hashes)): + raise ArtifactIntegrityError("derived RNG key material collides") + return ( + np.stack([material.initial_counter for material in materials]).astype( + " None: + counters, keys, material_hashes = _stream_material(request) + with h5py.File(path, "x", libver="earliest") as stream: + attributes: tuple[tuple[str, object], ...] = ( + ("schema_version", TRAJECTORY_SCHEMA), + ("rng_version", provenance["rng_version"]), + ("conversion_version", provenance["conversion_version"]), + ("request_sha256", result.request_sha256), + ("kernel_sha256", request.kernel_sha256), + ("source_revision", provenance["source_revision"]), + ("clean_tree", np.uint8(1)), + ("uv_lock_sha256", provenance["uv_lock_sha256"]), + ("runtime_capability_sha256", provenance["runtime_capability_sha256"]), + ("analysis_plan_sha256", provenance["analysis_plan_sha256"]), + ("rng_sha256", provenance["rng_sha256"]), + ("run_metadata_sha256", run_metadata_sha256), + ("length", np.uint64(request.length)), + ("sigma", np.float64(request.sigma)), + ("sigma_grid_id", request.sigma_grid_id), + ("master_seed", np.uint64(request.master_seed)), + ("phase", request.phase), + ("replica", np.uint64(request.replica)), + ("event_count", np.uint64(result.event_count)), + ("duplicate_count", np.uint64(result.duplicate_count)), + ) + for key, value in attributes: + stream.attrs[key] = value + request_group = stream.create_group("request", track_order=False) + result_group = stream.create_group("result", track_order=False) + rng_group = stream.create_group("rng", track_order=False) + request_group.create_dataset( + "kappas", data=request.kappas.astype(" h5py.Group | h5py.Dataset: + link = group.get(name, getlink=True) + if not isinstance(link, h5py.HardLink): + raise ArtifactIntegrityError(f"HDF5 link {name} is not canonical") + value = group.get(name, getlink=False) + if not isinstance(value, expected_type): + raise ArtifactIntegrityError(f"HDF5 object {name} has the wrong kind") + return value + + +def _object_address(value: h5py.Group | h5py.Dataset) -> int: + try: + return int(h5py.h5o.get_info(value.id).addr) + except (TypeError, ValueError, RuntimeError) as error: + raise ArtifactIntegrityError("unable to inspect HDF5 object identity") from error + + +def _exact_group( + group: h5py.Group, + names: set[str], + label: str, +) -> dict[str, h5py.Dataset]: + if set(group.keys()) != names or set(group.attrs.keys()): + raise ArtifactIntegrityError(f"{label} object tree is not exact") + datasets: dict[str, h5py.Dataset] = {} + for name in names: + value = _hard_link_object(group, name, h5py.Dataset) + assert isinstance(value, h5py.Dataset) + datasets[name] = value + return datasets + + +def _text_attribute(attributes: h5py.AttributeManager, key: str) -> str: + try: + value = attributes[key] + except KeyError as error: + raise ArtifactIntegrityError(f"missing HDF5 attribute: {key}") from error + try: + attribute = attributes.get_id(key) + except KeyError as error: + raise ArtifactIntegrityError(f"missing HDF5 attribute: {key}") from error + string_info = h5py.check_string_dtype(attribute.dtype) + if ( + attribute.shape != () + or string_info is None + or string_info.encoding != "utf-8" + or string_info.length is not None + or not isinstance(value, str) + ): + raise ArtifactIntegrityError( + f"HDF5 attribute {key} has noncanonical dtype representation" + ) + return value + + +def _numeric_attribute( + attributes: h5py.AttributeManager, + key: str, + dtype: str, +) -> int | float: + try: + value = attributes[key] + except KeyError as error: + raise ArtifactIntegrityError(f"missing HDF5 attribute: {key}") from error + attribute = attributes.get_id(key) + if attribute.shape != () or attribute.dtype.str != dtype: + raise ArtifactIntegrityError( + f"HDF5 attribute {key} has noncanonical dtype representation" + ) + if dtype == " None: + try: + external = dataset.external + except (OSError, RuntimeError, ValueError) as error: + raise ArtifactIntegrityError(f"unable to inspect dataset {name}") from error + if ( + dataset.dtype.str != dtype + or dataset.shape != shape + or dataset.maxshape != shape + or dataset.nbytes > MAX_DATASET_BYTES + ): + raise ArtifactIntegrityError(f"dataset {name} has stale dtype or shape") + if ( + dataset.is_virtual + or external is not None + or dataset.chunks is not None + or dataset.compression is not None + or dataset.compression_opts is not None + or dataset.shuffle + or dataset.fletcher32 + or dataset.scaleoffset is not None + or set(dataset.attrs.keys()) + or dataset.dtype.hasobject + or h5py.check_dtype(ref=dataset.dtype) is not None + or dataset.id.get_create_plist().get_layout() != h5py.h5d.CONTIGUOUS + ): + raise ArtifactIntegrityError( + f"dataset {name} uses noncanonical external, virtual, or chunked storage" + ) + + +def _read_dataset(dataset: h5py.Dataset, name: str) -> np.ndarray: + try: + value = np.asarray(dataset[...]) + except (MemoryError, OSError, RuntimeError, ValueError) as error: + raise ArtifactIntegrityError(f"unable to load bounded dataset {name}") from error + if not value.flags.c_contiguous: + value = np.ascontiguousarray(value) + return value + + +def _parse_hdf5( + stream: h5py.File, + expected: dict[str, str] | None, +) -> tuple[TrajectoryResult, dict[str, str]]: + try: + top_names = {"request", "result", "rng"} + if set(stream.keys()) != top_names or set(stream.attrs.keys()) != { + "schema_version", + "rng_version", + "conversion_version", + "request_sha256", + "kernel_sha256", + "source_revision", + "clean_tree", + "uv_lock_sha256", + "runtime_capability_sha256", + "analysis_plan_sha256", + "rng_sha256", + "run_metadata_sha256", + "length", + "sigma", + "sigma_grid_id", + "master_seed", + "phase", + "replica", + "event_count", + "duplicate_count", + }: + raise ArtifactIntegrityError("HDF5 object tree or attributes are not exact") + request_group = _hard_link_object(stream, "request", h5py.Group) + result_group = _hard_link_object(stream, "result", h5py.Group) + rng_group = _hard_link_object(stream, "rng", h5py.Group) + assert isinstance(request_group, h5py.Group) + assert isinstance(result_group, h5py.Group) + assert isinstance(rng_group, h5py.Group) + request_datasets = _exact_group(request_group, {"kappas"}, "request") + result_datasets = _exact_group( + result_group, + { + "observables", + "terminal_counters", + "draw_counts", + "hash_diagnostics", + }, + "result", + ) + rng_datasets = _exact_group( + rng_group, + {"initial_counters", "keys", "key_material_sha256"}, + "rng", + ) + objects: list[h5py.Group | h5py.Dataset] = [ + request_group, + result_group, + rng_group, + *request_datasets.values(), + *result_datasets.values(), + *rng_datasets.values(), + ] + addresses = [_object_address(value) for value in objects] + if len(addresses) != len(set(addresses)): + raise ArtifactIntegrityError("HDF5 object tree contains hard-link aliases") + if _text_attribute(stream.attrs, "schema_version") != TRAJECTORY_SCHEMA: + raise ArtifactIntegrityError("trajectory schema version is stale") + stored = {key: _text_attribute(stream.attrs, key) for key in _EXPECTED_KEYS} + run_metadata_sha256 = _text_attribute( + stream.attrs, "run_metadata_sha256" + ) + if _HEX256.fullmatch(run_metadata_sha256) is None: + raise ArtifactIntegrityError("run metadata digest is malformed") + _validate_expected(stored) + if expected is not None: + _validate_expected(expected) + for key in _EXPECTED_KEYS: + if stored[key] != expected[key]: + raise ArtifactIntegrityError( + f"trajectory dependency mismatch: {key}" + ) + if stored["rng_version"] != RNG_VERSION: + raise ArtifactIntegrityError("trajectory RNG version is stale") + if stored["conversion_version"] != CONVERSION_VERSION: + raise ArtifactIntegrityError("trajectory conversion version is stale") + if _numeric_attribute(stream.attrs, "clean_tree", "|u1") != 1: + raise ArtifactIntegrityError("dirty-tree trajectory is forbidden") + length = int(_numeric_attribute(stream.attrs, "length", " tuple[TrajectoryResult, dict[str, str], str, int]: + if expected is not None: + _validate_expected(expected) + descriptor, original = _open_regular( + path, "trajectory", maximum_size=MAX_HDF5_BYTES + ) + try: + before_hash, before_size = _hash_descriptor(descriptor, "trajectory") + if required_digest is not None and before_hash != required_digest: + raise ArtifactIntegrityError("whole-file trajectory digest mismatch") + if required_size is not None and before_size != required_size: + raise ArtifactIntegrityError("whole-file trajectory size mismatch") + os.lseek(descriptor, 0, os.SEEK_SET) + duplicate = os.dup(descriptor) + file_object: BinaryIO = os.fdopen(duplicate, "rb", closefd=True) + try: + with h5py.File(file_object, "r") as stream: + result, stored = _parse_hdf5(stream, expected) + finally: + file_object.close() + if path.parent.name != "trajectories": + raise ArtifactIntegrityError( + "trajectory is outside the canonical run namespace" + ) + run_root = path.parent.parent + if metadata_snapshot is not None: + verified_snapshot = _require_private_metadata_snapshot( + metadata_snapshot, run_root + ) + metadata_digest = verified_snapshot.digest + else: + metadata_digest = _verify_upstream_metadata( + run_root, + {key: stored[key] for key in _EXPECTED_KEYS}, + ) + assert isinstance(metadata_digest, str) + if metadata_digest != stored["run_metadata_sha256"]: + raise ArtifactIntegrityError("run metadata digest mismatch") + after_hash, after_size = _hash_descriptor(descriptor, "trajectory") + _require_stable_descriptor(descriptor, original, "trajectory") + _require_path_identity(path, original, "trajectory") + if (after_hash, after_size) != (before_hash, before_size): + raise ArtifactIntegrityError("trajectory mutated during semantic parsing") + return result, stored, before_hash, before_size + except ArtifactIntegrityError: + raise + except (OSError, RuntimeError, ValueError) as error: + raise ArtifactIntegrityError("unable to parse trajectory HDF5") from error + finally: + os.close(descriptor) + + +def _load_hdf5(path: Path, expected: dict[str, str]) -> TrajectoryResult: + return _load_hdf5_verified(path, expected)[0] + + +def _semantic_reload( + path: Path, + expected: dict[str, str], +) -> tuple[TrajectoryResult, str, int]: + result, _, digest, size = _load_hdf5_verified(path, expected) + return result, digest, size + + +def _decode_json_payload(payload: bytes, description: str) -> object: + try: + return json.loads(payload) + except ( + UnicodeDecodeError, + json.JSONDecodeError, + RecursionError, + OverflowError, + ValueError, + ) as error: + raise ArtifactIntegrityError( + f"unable to parse JSON for {description}" + ) from error + + +def _parse_canonical_json_payload(payload: bytes, description: str) -> object: + document = _decode_json_payload(payload, description) + canonical = _canonical_json_bytes(document) + if payload != canonical: + raise ArtifactIntegrityError(f"{description} is not canonical JSON") + return document + + +def _read_canonical_json(path: Path, description: str) -> object: + descriptor, original = _open_regular( + path, description, maximum_size=MAX_JSON_BYTES + ) + try: + payload = _read_descriptor_bounded(descriptor, MAX_JSON_BYTES, description) + _require_path_identity(path, original, description) + document = _decode_json_payload(payload, description) + _require_path_identity(path, original, description) + canonical = _canonical_json_bytes(document) + _require_path_identity(path, original, description) + if payload != canonical: + raise ArtifactIntegrityError(f"{description} is not canonical JSON") + _require_stable_descriptor(descriptor, original, description) + finally: + os.close(descriptor) + return document + + +def _read_digest(path: Path, trajectory_id: str) -> tuple[str, int]: + sidecar = path.with_suffix(".sha256.json") + document = _read_canonical_json(sidecar, "trajectory digest sidecar") + if not isinstance(document, dict) or set(document) != { + "schema_version", + "trajectory_id", + "trajectory_sha256", + "artifact_size", + }: + raise ArtifactIntegrityError("trajectory digest fields are not exact") + if ( + document["schema_version"] != TRAJECTORY_DIGEST_SCHEMA + or document["trajectory_id"] != trajectory_id + or not isinstance(document["artifact_size"], int) + or isinstance(document["artifact_size"], bool) + or document["artifact_size"] < 0 + or not isinstance(document["trajectory_sha256"], str) + or _HEX256.fullmatch(document["trajectory_sha256"]) is None + ): + raise ArtifactIntegrityError("trajectory digest sidecar is invalid") + return str(document["trajectory_sha256"]), int(document["artifact_size"]) + + +def _verify_trajectory( + path: Path, + trajectory_id: str, + expected: dict[str, str] | None, + metadata_snapshot: _VerifiedMetadataSnapshot | None = None, +) -> tuple[TrajectoryResult, dict[str, str], str, int]: + digest, size = _read_digest(path, trajectory_id) + return _load_hdf5_verified( + path, + expected, + required_digest=digest, + required_size=size, + metadata_snapshot=metadata_snapshot, + ) + + +def _verify_digest(path: Path, trajectory_id: str) -> tuple[str, int]: + _, _, digest, size = _verify_trajectory(path, trajectory_id, None) + return digest, size + + +def publish_trajectory( + run_dir: Path, + request: TrajectoryRequest, + result: TrajectoryResult, + provenance: dict[str, object], +) -> Path: + validate_trajectory_request(request) + if not isinstance(result, TrajectoryResult): + raise TypeError("result must be a TrajectoryResult") + trajectory_id = request_digest(request) + if result.request_sha256 != trajectory_id: + raise ArtifactIntegrityError("result belongs to a different request") + if result.observables.shape[0] != request.kappas.size: + raise ArtifactIntegrityError("result does not cover every requested coupling") + if request.kappas.size > MAX_KAPPA_COUNT: + raise ArtifactIntegrityError("kappa count exceeds the frozen resource limit") + _validate_provenance(provenance) + expected = { + key: str(value) + for key, value in provenance.items() + if key != "clean_tree" + } + expected["request_sha256"] = trajectory_id + expected["kernel_sha256"] = request.kernel_sha256 + _, trajectories, _, metadata_digest = _prepare_publication_run( + run_dir, expected + ) + final = trajectories / f"trajectory-{trajectory_id}.h5" + sidecar = final.with_suffix(".sha256.json") + unique = f"{os.getpid()}.{uuid.uuid4().hex}" + partial = trajectories / f".trajectory-{trajectory_id}.{unique}.partial" + digest_partial = trajectories / f".trajectory-{trajectory_id}.{unique}.sha256.partial" + intent = trajectories / f".trajectory-{trajectory_id}.{unique}.intent" + intent_document = { + "final_name": final.name, + "partial_name": partial.name, + "semantic_hashes": expected, + "run_metadata_sha256": metadata_digest, + "trajectory_id": trajectory_id, + } + with _directory_lock(trajectories): + if final.exists() or final.is_symlink() or sidecar.exists() or sidecar.is_symlink(): + raise FileExistsError(f"immutable trajectory already exists: {final}") + _write_unique_fsynced(intent, _canonical_json_bytes(intent_document)) + _fsync_directory(trajectories) + _write_hdf5( + partial, + request, + result, + provenance, + metadata_digest, + ) + _, trajectory_hash, artifact_size = _semantic_reload(partial, expected) + digest_document = { + "artifact_size": artifact_size, + "schema_version": TRAJECTORY_DIGEST_SCHEMA, + "trajectory_id": trajectory_id, + "trajectory_sha256": trajectory_hash, + } + _write_unique_fsynced( + digest_partial, _canonical_json_bytes(digest_document) + ) + if final.exists() or final.is_symlink() or sidecar.exists() or sidecar.is_symlink(): + raise FileExistsError(f"immutable trajectory already exists: {final}") + _replace(partial, final) + try: + _install_no_clobber(digest_partial, sidecar) + except FileExistsError as error: + raise ArtifactIntegrityError("trajectory sidecar publication raced") from error + final_stat = _checked_regular(final, "installed trajectory") + partial_stat = _checked_regular(partial, "staged trajectory") + if ( + final_stat.st_dev != partial_stat.st_dev + or final_stat.st_ino != partial_stat.st_ino + ): + raise ArtifactIntegrityError("installed trajectory inode changed") + _load_hdf5_verified( + final, + expected, + required_digest=trajectory_hash, + required_size=artifact_size, + ) + _verify_installed_bytes( + sidecar, + _canonical_json_bytes(digest_document), + "trajectory digest sidecar", + ) + partial.unlink() + digest_partial.unlink() + _fsync_directory(trajectories) + # This is the final installed-inode boundary. It deliberately occurs + # after staged-link removal and its directory fsync, while the durable + # publication intent still exists. + _verify_installed_bytes( + sidecar, + _canonical_json_bytes(digest_document), + "trajectory digest sidecar", + ) + _load_hdf5_verified( + final, + expected, + required_digest=trajectory_hash, + required_size=artifact_size, + ) + intent.unlink() + try: + _fsync_directory(trajectories) + except BaseException: + # If cleanup durability is not confirmed, restore a visible marker. + # The preceding durable directory state also still contains it after + # a real crash, but restoring it makes an I/O-error return fail closed + # without relying on a subsequent restart. + if not intent.exists(): + _write_unique_fsynced( + intent, _canonical_json_bytes(intent_document) + ) + try: + _fsync_directory(trajectories) + except BaseException as recovery_error: + raise ArtifactIntegrityError( + "intent recovery directory fsync failed; publication is uncommitted" + ) from recovery_error + raise + return final + + +def load_verified_trajectory( + path: Path, expected: dict[str, str] +) -> TrajectoryResult: + if not isinstance(path, Path): + raise TypeError("path must be a pathlib.Path") + match = _TRAJECTORY_NAME.fullmatch(path.name) + if match is None: + raise ArtifactIntegrityError("trajectory filename is not canonical") + _check_existing_path_chain(path) + result, _, _, _ = _verify_trajectory(path, match.group(1), expected) + if result.request_sha256 != match.group(1): + raise ArtifactIntegrityError("trajectory ID does not match its filename") + return result + + +def publish_batch_manifest( + run_dir: Path, + batch_id: str, + trajectory_paths: Sequence[Path], +) -> Path: + if not isinstance(batch_id, str) or _SAFE_BATCH_ID.fullmatch(batch_id) is None: + raise ValueError("batch_id is not in the safe canonical namespace") + if isinstance(trajectory_paths, (str, bytes)) or not isinstance( + trajectory_paths, Sequence + ): + raise TypeError("trajectory_paths must be a sequence of paths") + try: + member_count = len(trajectory_paths) + except (OverflowError, ValueError) as error: + raise ArtifactIntegrityError("batch member count is invalid") from error + if not 1 <= member_count <= MAX_BATCH_MEMBERS: + raise ArtifactIntegrityError("batch member count exceeds the frozen limit") + _verify_run_layout(run_dir) + trajectories = run_dir / "trajectories" + batches = run_dir / "batches" + members: list[dict[str, str]] = [] + seen: set[str] = set() + for index, path in enumerate(trajectory_paths): + if index >= MAX_BATCH_MEMBERS: + raise ArtifactIntegrityError("batch member iteration exceeds the limit") + if not isinstance(path, Path): + raise TypeError("trajectory path must be a pathlib.Path") + if path.parent != trajectories: + raise ArtifactIntegrityError("batch member is outside trajectories directory") + match = _TRAJECTORY_NAME.fullmatch(path.name) + if match is None: + raise ArtifactIntegrityError("batch member filename is not canonical") + trajectory_id = match.group(1) + if trajectory_id in seen: + raise ArtifactIntegrityError("batch contains duplicate trajectory ID") + seen.add(trajectory_id) + result, _, trajectory_hash, _ = _verify_trajectory( + path, trajectory_id, None + ) + if result.request_sha256 != trajectory_id: + raise ArtifactIntegrityError("trajectory ID does not match its filename") + members.append( + { + "path": f"trajectories/{path.name}", + "trajectory_id": trajectory_id, + "trajectory_sha256": trajectory_hash, + } + ) + if not members: + raise ArtifactIntegrityError("batch must retain at least one trajectory") + members.sort(key=lambda member: member["trajectory_id"]) + document = { + "batch_id": batch_id, + "members": members, + "schema_version": BATCH_SCHEMA, + } + final = batches / f"batch-{batch_id}.json" + with _directory_lock(batches): + if final.exists() or final.is_symlink(): + raise FileExistsError(f"immutable batch already exists: {final}") + _publish_json_once(final, document, BATCH_SCHEMA) + return final + + +def _bounded_directory_entries( + directory: Path, + maximum_entries: int, + description: str, +) -> list[Path]: + entries: list[Path] = [] + try: + with os.scandir(directory) as stream: + for entry in stream: + if len(entries) >= maximum_entries: + raise ArtifactIntegrityError( + f"{description} entry count exceeds the frozen limit" + ) + entries.append(Path(entry.path)) + except OSError as error: + raise ArtifactIntegrityError(f"unable to inspect {description}") from error + entries.sort(key=lambda path: path.name) + return entries + + +def _authoritative_metadata_bindings( + expected: dict[str, str], +) -> dict[str, tuple[tuple[str, object], ...]]: + return { + "request.json": ( + ("request_sha256", expected["request_sha256"]), + ("kernel_sha256", expected["kernel_sha256"]), + ), + "environment.json": ( + ("clean_tree", True), + ("conversion_version", CONVERSION_VERSION), + ("rng_version", RNG_VERSION), + ("runtime_capability_sha256", expected["runtime_capability_sha256"]), + ("source_revision", expected["source_revision"]), + ("uv_lock_sha256", expected["uv_lock_sha256"]), + ), + "seed-manifest.json": (("rng_sha256", expected["rng_sha256"]),), + "capability.json": ( + ("runtime_capability_sha256", expected["runtime_capability_sha256"]), + ), + "manifest.json": ( + ("analysis_plan_sha256", expected["analysis_plan_sha256"]), + ("source_revision", expected["source_revision"]), + ), + } + + +def _reject_nested_authoritative_fields( + value: object, + authoritative_fields: frozenset[str], + description: str, +) -> None: + if isinstance(value, dict): + for key, child in value.items(): + if key in authoritative_fields: + raise ArtifactIntegrityError( + f"{description} contains ambiguous nested {key}" + ) + _reject_nested_authoritative_fields( + child, authoritative_fields, description + ) + elif isinstance(value, list): + for child in value: + _reject_nested_authoritative_fields( + child, authoritative_fields, description + ) + + +def _validate_authoritative_metadata( + documents: dict[str, object], + expected: dict[str, str], +) -> None: + bindings = _authoritative_metadata_bindings(expected) + authoritative_fields = frozenset( + key for required in bindings.values() for key, _ in required + ) + for name, required in bindings.items(): + document = documents[name] + if ( + not isinstance(document, dict) + or type(document.get("schema_version")) is not str + or not document["schema_version"] + ): + raise ArtifactIntegrityError( + f"upstream metadata {name} lacks a canonical schema identity" + ) + for key, child in document.items(): + if key != "schema_version" and key not in authoritative_fields: + _reject_nested_authoritative_fields( + child, + authoritative_fields, + f"upstream metadata {name}", + ) + elif key in authoritative_fields: + _reject_nested_authoritative_fields( + child, + authoritative_fields, + f"upstream metadata {name}", + ) + for key, required_value in required: + if key not in document: + raise ArtifactIntegrityError( + f"upstream metadata {name} is missing top-level {key}" + ) + actual = document[key] + if type(actual) is not type(required_value) or actual != required_value: + raise ArtifactIntegrityError( + f"upstream metadata {name} has invalid top-level {key}" + ) + + +def _verify_upstream_metadata( + run_dir: Path, + expected: dict[str, str], + *, + _return_snapshot: bool = False, +) -> str | _VerifiedMetadataSnapshot: + _validate_expected(expected) + kernel = run_dir / "kernel" + kernel_files = _bounded_directory_entries( + kernel, MAX_KERNEL_FILES, "kernel metadata" + ) + if not kernel_files: + raise ArtifactIntegrityError("kernel metadata file count is invalid") + _preflight_metadata_descriptors(1 + len(_UPSTREAM_FILES) + len(kernel_files)) + kernel_total = 0 + for path in kernel_files: + try: + metadata = path.lstat() + except OSError as error: + raise ArtifactIntegrityError( + "unable to inspect kernel metadata file" + ) from error + if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode): + raise ArtifactIntegrityError("kernel metadata file has the wrong kind") + if metadata.st_size > MAX_KERNEL_FILE_BYTES: + raise ArtifactIntegrityError("kernel metadata file exceeds byte limit") + kernel_total += metadata.st_size + if kernel_total > MAX_KERNEL_TOTAL_BYTES: + raise ArtifactIntegrityError( + "kernel metadata total byte limit exceeded" + ) + flags = ( + os.O_RDONLY + | getattr(os, "O_DIRECTORY", 0) + | getattr(os, "O_NOFOLLOW", 0) + ) + try: + kernel_descriptor = os.open(kernel, flags) + except OSError as error: + raise ArtifactIntegrityError("unable to open kernel metadata directory") from error + with ExitStack() as stack: + stack.callback(os.close, kernel_descriptor) + kernel_original = os.fstat(kernel_descriptor) + if not stat.S_ISDIR(kernel_original.st_mode): + raise ArtifactIntegrityError("kernel metadata has the wrong kind") + expected_kernel_names = tuple(path.name for path in kernel_files) + entries: list[ + tuple[Path, str, int, os.stat_result, bool] + ] = [] + identities: set[tuple[int, int]] = set() + for name in sorted(_UPSTREAM_FILES): + path = run_dir / name + description = f"upstream metadata {name}" + descriptor, original = _open_regular( + path, description, maximum_size=MAX_JSON_BYTES + ) + stack.callback(os.close, descriptor) + entries.append((path, description, descriptor, original, True)) + for path in kernel_files: + descriptor, original = _open_regular( + path, + "kernel metadata file", + maximum_size=MAX_KERNEL_FILE_BYTES, + ) + stack.callback(os.close, descriptor) + entries.append( + (path, "kernel metadata file", descriptor, original, False) + ) + actual_kernel_total = sum( + original.st_size for _, _, _, original, is_json in entries if not is_json + ) + if actual_kernel_total > MAX_KERNEL_TOTAL_BYTES: + raise ArtifactIntegrityError( + "kernel metadata total byte limit exceeded" + ) + for _, _, _, original, _ in entries: + if original.st_nlink != 1: + raise ArtifactIntegrityError( + "upstream metadata contains a hard-link alias" + ) + identity = (original.st_dev, original.st_ino) + if identity in identities: + raise ArtifactIntegrityError( + "upstream metadata contains inode aliases" + ) + identities.add(identity) + + first_documents: dict[str, object] = {} + first_file_hashes: dict[str, str] = {} + first_kernel_hashes: dict[str, str] = {} + for path, description, descriptor, original, is_json in entries: + if is_json: + payload = _read_descriptor_bounded( + descriptor, MAX_JSON_BYTES, description + ) + document = _parse_canonical_json_payload(payload, description) + first_documents[path.name] = document + first_file_hashes[path.name] = hashlib.sha256(payload).hexdigest() + else: + digest, _ = _hash_descriptor(descriptor, description) + first_kernel_hashes[path.name] = digest + _require_stable_descriptor(descriptor, original, description) + _require_path_identity(path, original, description) + _validate_authoritative_metadata(first_documents, expected) + if expected["kernel_sha256"] not in first_kernel_hashes.values(): + raise ArtifactIntegrityError( + "kernel metadata does not contain the request digest" + ) + first_index = { + "files": first_file_hashes, + "kernel_files": first_kernel_hashes, + } + + second_documents: dict[str, object] = {} + second_file_hashes: dict[str, str] = {} + second_kernel_hashes: dict[str, str] = {} + for path, description, descriptor, original, is_json in entries: + _require_stable_descriptor(descriptor, original, description) + _require_path_identity(path, original, description) + if is_json: + payload = _read_descriptor_bounded( + descriptor, MAX_JSON_BYTES, description + ) + document = _parse_canonical_json_payload(payload, description) + second_documents[path.name] = document + second_file_hashes[path.name] = hashlib.sha256(payload).hexdigest() + else: + digest, _ = _hash_descriptor(descriptor, description) + second_kernel_hashes[path.name] = digest + _require_stable_descriptor(descriptor, original, description) + _require_path_identity(path, original, description) + _validate_authoritative_metadata(second_documents, expected) + second_index = { + "files": second_file_hashes, + "kernel_files": second_kernel_hashes, + } + if first_documents != second_documents or first_index != second_index: + raise ArtifactIntegrityError( + "upstream metadata aggregate snapshot mutated" + ) + # A mutation of an early entry while a later entry is undergoing its + # second read must not escape merely because the early entry's second + # hash already completed. Re-read exact bytes rather than relying on + # filesystem timestamp granularity. + final_documents: dict[str, object] = {} + final_file_hashes: dict[str, str] = {} + final_kernel_hashes: dict[str, str] = {} + post_hash_generations: dict[Path, tuple[int, int, int, int, int, int, int]] = {} + for path, description, descriptor, original, is_json in entries: + before_hash = os.fstat(descriptor) + if _generation_tuple(before_hash) != _generation_tuple(original): + raise ArtifactIntegrityError( + f"{description} generation mutated before final hash" + ) + _require_path_identity(path, original, description) + if is_json: + payload = _read_descriptor_bounded( + descriptor, MAX_JSON_BYTES, description + ) + final_documents[path.name] = _parse_canonical_json_payload( + payload, description + ) + final_file_hashes[path.name] = hashlib.sha256(payload).hexdigest() + else: + digest, _ = _hash_descriptor(descriptor, description) + final_kernel_hashes[path.name] = digest + after_hash = os.fstat(descriptor) + if _generation_tuple(after_hash) != _generation_tuple(before_hash): + raise ArtifactIntegrityError( + f"{description} generation mutated during final hash" + ) + post_hash_generations[path] = _generation_tuple(after_hash) + _require_path_identity(path, original, description) + _validate_authoritative_metadata(final_documents, expected) + final_index = { + "files": final_file_hashes, + "kernel_files": final_kernel_hashes, + } + if final_documents != second_documents or final_index != second_index: + raise ArtifactIntegrityError( + "upstream metadata aggregate snapshot mutated after second pass" + ) + for path, description, descriptor, _, _ in entries: + current = os.fstat(descriptor) + if _generation_tuple(current) != post_hash_generations[path]: + raise ArtifactIntegrityError( + f"{description} generation mutated after final hash" + ) + for path, description, _, _, _ in entries: + try: + current = path.lstat() + except OSError as error: + raise ArtifactIntegrityError( + f"{description} pathname identity changed" + ) from error + if ( + not stat.S_ISREG(current.st_mode) + or _generation_tuple(current) != post_hash_generations[path] + ): + raise ArtifactIntegrityError( + f"{description} pathname generation changed" + ) + kernel_current = os.fstat(kernel_descriptor) + if _generation_tuple(kernel_current) != _generation_tuple(kernel_original): + raise ArtifactIntegrityError("kernel metadata directory mutated") + try: + kernel_path_current = kernel.lstat() + except OSError as error: + raise ArtifactIntegrityError( + "kernel metadata pathname identity changed" + ) from error + if ( + kernel_path_current.st_dev != kernel_original.st_dev + or kernel_path_current.st_ino != kernel_original.st_ino + or tuple( + path.name + for path in _bounded_directory_entries( + kernel, MAX_KERNEL_FILES, "kernel metadata" + ) + ) + != expected_kernel_names + ): + raise ArtifactIntegrityError("kernel metadata membership mutated") + digest = hashlib.sha256(_canonical_json_bytes(second_index)).hexdigest() + if _return_snapshot: + return _VerifiedMetadataSnapshot( + run_dir=run_dir, + digest=digest, + file_generations=tuple( + (path, post_hash_generations[path]) + for path, _, _, _, _ in entries + ), + kernel_generation=_generation_tuple(kernel_current), + kernel_names=expected_kernel_names, + _token=_SNAPSHOT_IDENTITY_TOKEN, + ) + return digest + + +def _verify_run_layout( + run_dir: Path, + expected: dict[str, str] | None = None, +) -> None: + if not isinstance(run_dir, Path): + raise TypeError("run_dir must be a pathlib.Path") + _check_existing_path_chain(run_dir) + try: + root_stat = run_dir.lstat() + except OSError as error: + raise ArtifactIntegrityError("run layout is missing") from error + if stat.S_ISLNK(root_stat.st_mode) or not stat.S_ISDIR(root_stat.st_mode): + raise ArtifactIntegrityError("run layout root has the wrong kind") + entries = _bounded_directory_entries( + run_dir, MAX_ROOT_ENTRIES, "run root" + ) + names = {path.name for path in entries} + for path in entries: + try: + metadata = path.lstat() + except OSError as error: + raise ArtifactIntegrityError("unable to inspect run layout") from error + if stat.S_ISLNK(metadata.st_mode): + raise ArtifactIntegrityError("run layout must not contain symlinks") + if not _REQUIRED_ROOT_ENTRIES.issubset(names): + missing = sorted(_REQUIRED_ROOT_ENTRIES - names) + raise ArtifactIntegrityError(f"run layout is missing entries: {missing}") + if not names.issubset(_ROOT_ENTRIES): + unknown = sorted(names - _ROOT_ENTRIES) + raise ArtifactIntegrityError(f"unknown run artifact: {unknown}") + identities: set[tuple[int, int]] = set() + for path in entries: + try: + metadata = path.lstat() + except OSError as error: + raise ArtifactIntegrityError("unable to inspect run layout") from error + identity = (metadata.st_dev, metadata.st_ino) + if identity in identities: + raise ArtifactIntegrityError("run layout contains inode aliases") + identities.add(identity) + if path.name in _ROOT_DIRECTORY_ENTRIES: + if not stat.S_ISDIR(metadata.st_mode): + raise ArtifactIntegrityError( + f"run layout entry {path.name} has the wrong directory kind" + ) + else: + if not stat.S_ISREG(metadata.st_mode): + raise ArtifactIntegrityError( + f"run layout entry {path.name} must be a regular file" + ) + if metadata.st_nlink != 1: + raise ArtifactIntegrityError( + f"run layout entry {path.name} is a hard-link alias" + ) + if expected is not None: + _verify_upstream_metadata(run_dir, expected) + + +def _verify_reconstruction_trajectories( + paths: Sequence[Path], + expected: dict[str, str], + metadata_snapshot: object, +) -> dict[str, dict[str, str]]: + records: dict[str, dict[str, str]] = {} + internal_ids: set[str] = set() + for path in paths: + match = _TRAJECTORY_NAME.fullmatch(path.name) + if match is None: + raise ArtifactIntegrityError("trajectory filename is not canonical") + trajectory_id = match.group(1) + verified = _verify_trajectory( + path, + trajectory_id, + expected, + metadata_snapshot=metadata_snapshot, # type: ignore[arg-type] + ) + result, _, digest, _ = verified + if result.request_sha256 in internal_ids: + raise ArtifactIntegrityError("duplicate trajectory ID") + internal_ids.add(result.request_sha256) + if result.request_sha256 != trajectory_id: + raise ArtifactIntegrityError( + "trajectory ID does not match filename" + ) + if trajectory_id in records: + raise ArtifactIntegrityError("duplicate trajectory ID") + records[trajectory_id] = { + "path": f"trajectories/{path.name}", + "trajectory_id": trajectory_id, + "trajectory_sha256": digest, + } + del result, verified + return records + + +def reconstruct_progress( + run_dir: Path, expected: dict[str, str] +) -> dict[str, object]: + _validate_expected(expected) + _verify_run_layout(run_dir) + snapshot = _verify_upstream_metadata( + run_dir, expected, _return_snapshot=True + ) + assert isinstance(snapshot, _VerifiedMetadataSnapshot) + trajectories = run_dir / "trajectories" + batches = run_dir / "batches" + trajectory_files: list[Path] = [] + sidecar_ids: set[str] = set() + for path in _bounded_directory_entries( + trajectories, + MAX_TRAJECTORY_DIRECTORY_ENTRIES, + "trajectory directory", + ): + if path.is_symlink(): + raise ArtifactIntegrityError("trajectory entries must not be symlinks") + trajectory_match = _TRAJECTORY_NAME.fullmatch(path.name) + digest_match = _DIGEST_NAME.fullmatch(path.name) + if trajectory_match is not None: + trajectory_files.append(path) + elif digest_match is not None: + sidecar_ids.add(digest_match.group(1)) + elif path.name.endswith(".intent"): + raise ArtifactIntegrityError("surviving publication intent marker") + elif path.name.endswith(".partial"): + raise ArtifactIntegrityError("stale partial trajectory artifact") + else: + raise ArtifactIntegrityError(f"unknown trajectory artifact: {path.name}") + if ( + len(trajectory_files) > MAX_BATCH_MEMBERS + or len(sidecar_ids) > MAX_BATCH_MEMBERS + ): + raise ArtifactIntegrityError( + "trajectory artifact count exceeds the frozen limit" + ) + if not trajectory_files: + raise ArtifactIntegrityError("run layout has no trajectory artifacts") + trajectory_records = _verify_reconstruction_trajectories( + trajectory_files, expected, snapshot + ) + if sidecar_ids != set(trajectory_records): + raise ArtifactIntegrityError("trajectory and digest membership differ") + + manifests: list[dict[str, object]] = [] + memberships: set[str] = set() + batch_paths = _bounded_directory_entries( + batches, MAX_BATCH_MEMBERS, "batch directory" + ) + if not batch_paths: + raise ArtifactIntegrityError("run layout has no batch manifests") + if len(batch_paths) > MAX_BATCH_MEMBERS: + raise ArtifactIntegrityError("batch manifest count exceeds the frozen limit") + for path in batch_paths: + if path.is_symlink(): + raise ArtifactIntegrityError("batch entries must not be symlinks") + match = _BATCH_NAME.fullmatch(path.name) + if match is None: + if path.name.endswith(".partial"): + raise ArtifactIntegrityError("stale partial batch manifest") + raise ArtifactIntegrityError(f"unknown batch artifact: {path.name}") + document = _read_canonical_json(path, "batch manifest") + if not isinstance(document, dict) or set(document) != { + "schema_version", + "batch_id", + "members", + }: + raise ArtifactIntegrityError("batch manifest fields are not exact") + batch_id = match.group(1) + if document["schema_version"] != BATCH_SCHEMA or document["batch_id"] != batch_id: + raise ArtifactIntegrityError("batch manifest identity is invalid") + members = document["members"] + if ( + not isinstance(members, list) + or not 1 <= len(members) <= MAX_BATCH_MEMBERS + ): + raise ArtifactIntegrityError("batch manifest has no members") + if members != sorted( + members, + key=lambda member: member.get("trajectory_id", "") + if isinstance(member, dict) + else "", + ): + raise ArtifactIntegrityError("batch members are not canonical") + for member in members: + if not isinstance(member, dict) or set(member) != { + "path", + "trajectory_id", + "trajectory_sha256", + }: + raise ArtifactIntegrityError("batch member fields are not exact") + trajectory_id = member["trajectory_id"] + if not isinstance(trajectory_id, str) or _HEX256.fullmatch( + trajectory_id + ) is None: + raise ArtifactIntegrityError("batch trajectory ID is malformed") + record = trajectory_records.get(trajectory_id) + if record is None: + raise ArtifactIntegrityError("batch manifest references missing member") + if member != record: + raise ArtifactIntegrityError("batch member hash or path is stale") + if trajectory_id in memberships: + raise ArtifactIntegrityError("trajectory appears in duplicate manifests") + memberships.add(trajectory_id) + manifests.append( + { + "batch_id": batch_id, + "path": f"batches/{path.name}", + "trajectory_count": len(members), + } + ) + if memberships != set(trajectory_records): + raise ArtifactIntegrityError( + "every valid trajectory must belong to one batch manifest" + ) + manifests.sort(key=lambda record: str(record["batch_id"])) + trajectories_document = [ + trajectory_records[key] for key in sorted(trajectory_records) + ] + progress: dict[str, object] = { + "batch_count": len(manifests), + "batches": manifests, + "schema_version": PROGRESS_SCHEMA, + "trajectory_count": len(trajectories_document), + "trajectories": trajectories_document, + } + snapshot.verify_final_boundary() + progress_path = run_dir / "progress.json" + if progress_path.exists() or progress_path.is_symlink(): + existing = _read_canonical_json(progress_path, "progress") + if existing != progress: + raise ArtifactIntegrityError("existing progress is stale or corrupt") + else: + _publish_json_once(progress_path, progress, PROGRESS_SCHEMA) + return progress diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/benchmark.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/benchmark.py new file mode 100644 index 000000000..1c6f8f17e --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/benchmark.py @@ -0,0 +1,1331 @@ +from __future__ import annotations + +import argparse +from dataclasses import dataclass +import hashlib +import importlib.metadata +import json +import math +import os +from pathlib import Path +import platform +import shutil +import stat +import subprocess +import sys +import tempfile +import time +from typing import Mapping, Sequence +import uuid + + +BENCHMARK_SCHEMA = "challenge-194-benchmark-v1" +WORKER_SCHEMA = "challenge-194-benchmark-worker-v1" +BENCHMARK_LENGTHS = (2**10, 2**14, 2**18) +BENCHMARK_SIGMAS = (0.8, 0.9, 1.0, 1.1) +BENCHMARK_KAPPAS = tuple( + value for value in (0.25 * 1.25**j for j in range(32)) if value <= 6.0 +) +STEADY_RUNS = 5 +WALL_LIMIT_SECONDS = 120.0 +RSS_LIMIT_BYTES = 4 * 1024**3 +GATE_LENGTH = 2**18 +QUADRATIC_MAX_LENGTH = 256 +BACKENDS = ("quadratic", "geometric", "poisson-numba") +WORKER_MODES = ("compile", "steady", "measure-observables") +ONE_THREAD_ENVIRONMENT = { + "NUMBA_NUM_THREADS": "1", + "OMP_NUM_THREADS": "1", + "OPENBLAS_NUM_THREADS": "1", + "MKL_NUM_THREADS": "1", + "VECLIB_MAXIMUM_THREADS": "1", + "NUMEXPR_NUM_THREADS": "1", + "PYTHONHASHSEED": "0", +} + +_MAX_REPORT_BYTES = 16 * 1024 * 1024 +_MAX_VALIDATION_BYTES = 256 * 1024 * 1024 +_TIMING_FIELDS = { + "startup", + "cache_load_warmup", + "compile", + "sampling", + "observable", + "artifact_serialization", + "wall", + "cpu", +} +_METRIC_FIELDS = { + "events", + "unique_edges", + "unions", + "duplicates", + "total_probes", + "maximum_probe", + "rehashes", + "bytes", +} +_RUNTIME_CAPABILITY_FIELDS = { + "schema_version", + "python", + "implementation", + "platform", + "machine", + "numpy", + "scipy", + "h5py", + "numba", + "llvmlite", + "cpu_name", + "cpu_features", + "threading_layer", + "numba_disable_jit", + "fastmath", + "boundscheck", +} + + +@dataclass(frozen=True) +class BenchmarkProtocol: + lengths: tuple[int, ...] + sigmas: tuple[float, ...] + kappas: tuple[float, ...] + steady_runs: int + wall_limit_seconds: float + rss_limit_bytes: int + gate_length: int + backends: tuple[str, ...] + quadratic_max_length: int + validation_report: Path | None + name: str + + @classmethod + def production_v1(cls) -> BenchmarkProtocol: + return cls( + lengths=BENCHMARK_LENGTHS, + sigmas=BENCHMARK_SIGMAS, + kappas=BENCHMARK_KAPPAS, + steady_runs=STEADY_RUNS, + wall_limit_seconds=WALL_LIMIT_SECONDS, + rss_limit_bytes=RSS_LIMIT_BYTES, + gate_length=GATE_LENGTH, + backends=BACKENDS, + quadratic_max_length=QUADRATIC_MAX_LENGTH, + validation_report=None, + name="production-v1", + ) + + @classmethod + def reduced( + cls, + *, + lengths: Sequence[int], + sigmas: Sequence[float], + kappas: Sequence[float], + steady_runs: int, + gate_length: int, + wall_limit_seconds: float, + rss_limit_bytes: int, + backends: Sequence[str] = ("poisson-numba",), + validation_report: Path | None = None, + ) -> BenchmarkProtocol: + return cls( + lengths=tuple(lengths), + sigmas=tuple(float(value) for value in sigmas), + kappas=tuple(float(value) for value in kappas), + steady_runs=steady_runs, + wall_limit_seconds=float(wall_limit_seconds), + rss_limit_bytes=rss_limit_bytes, + gate_length=gate_length, + backends=tuple(backends), + quadratic_max_length=QUADRATIC_MAX_LENGTH, + validation_report=validation_report, + name="reduced", + ) + + def __post_init__(self) -> None: + if ( + not self.lengths + or any( + isinstance(value, bool) + or not isinstance(value, int) + or value < 2 + or value % 2 + for value in self.lengths + ) + or tuple(sorted(set(self.lengths))) != self.lengths + ): + raise ValueError("benchmark lengths must be sorted unique even integers") + if ( + not self.sigmas + or any(not math.isfinite(value) or value <= 0.0 for value in self.sigmas) + or len(set(self.sigmas)) != len(self.sigmas) + ): + raise ValueError("benchmark sigmas must be unique finite positive values") + if ( + not self.kappas + or any(not math.isfinite(value) or value < 0.0 for value in self.kappas) + or any( + right <= left for left, right in zip(self.kappas, self.kappas[1:]) + ) + ): + raise ValueError("benchmark kappas must be sorted unique finite values") + if ( + isinstance(self.steady_runs, bool) + or not isinstance(self.steady_runs, int) + or self.steady_runs < 1 + ): + raise ValueError("steady_runs must be a positive integer") + if ( + not math.isfinite(self.wall_limit_seconds) + or self.wall_limit_seconds <= 0.0 + ): + raise ValueError("wall limit must be finite and positive") + if ( + isinstance(self.rss_limit_bytes, bool) + or not isinstance(self.rss_limit_bytes, int) + or self.rss_limit_bytes < 1 + ): + raise ValueError("RSS limit must be a positive integer") + if self.gate_length not in self.lengths: + raise ValueError("gate length must be a benchmark length") + if ( + not self.backends + or len(set(self.backends)) != len(self.backends) + or any(value not in BACKENDS for value in self.backends) + ): + raise ValueError("benchmark backends are invalid") + if self.validation_report is not None and not isinstance( + self.validation_report, Path + ): + raise ValueError("validation_report must be a pathlib.Path") + + @property + def is_production(self) -> bool: + frozen = BenchmarkProtocol.production_v1() + return ( + self.lengths == frozen.lengths + and self.sigmas == frozen.sigmas + and self.kappas == frozen.kappas + and self.steady_runs == frozen.steady_runs + and self.wall_limit_seconds == frozen.wall_limit_seconds + and self.rss_limit_bytes == frozen.rss_limit_bytes + and self.gate_length == frozen.gate_length + and self.backends == frozen.backends + and self.quadratic_max_length == frozen.quadratic_max_length + and self.name == frozen.name + ) + + def require_production(self) -> None: + if not self.is_production: + raise ValueError("benchmark protocol is not the frozen production protocol") + + def to_document(self) -> dict[str, object]: + return { + "name": self.name, + "lengths": list(self.lengths), + "sigmas": [value.hex() for value in self.sigmas], + "kappas": [value.hex() for value in self.kappas], + "steady_runs": self.steady_runs, + "wall_limit_seconds": self.wall_limit_seconds.hex(), + "rss_limit_bytes": self.rss_limit_bytes, + "gate_length": self.gate_length, + "backends": list(self.backends), + "quadratic_max_length": self.quadratic_max_length, + } + + +def canonical_report_bytes(report: Mapping[str, object]) -> bytes: + try: + return ( + json.dumps( + report, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + + b"\n" + ) + except (TypeError, ValueError, UnicodeEncodeError, RecursionError) as error: + raise RuntimeError("benchmark report is not canonical finite JSON") from error + + +def _read_regular_bounded( + path: Path, + description: str, + *, + maximum_bytes: int = _MAX_REPORT_BYTES, +) -> bytes: + if not isinstance(path, Path): + raise RuntimeError(f"{description} path must be a pathlib.Path") + try: + metadata = path.lstat() + except OSError as error: + raise RuntimeError(f"unable to inspect {description}") from error + if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode): + raise RuntimeError(f"{description} must be a regular non-symlink file") + if metadata.st_size > maximum_bytes: + raise RuntimeError(f"{description} exceeds the byte-size limit") + flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) + try: + descriptor = os.open(path, flags) + except OSError as error: + raise RuntimeError(f"unable to open {description}") from error + try: + opened = os.fstat(descriptor) + if ( + not stat.S_ISREG(opened.st_mode) + or opened.st_dev != metadata.st_dev + or opened.st_ino != metadata.st_ino + or opened.st_size > maximum_bytes + ): + raise RuntimeError(f"{description} identity changed") + chunks: list[bytes] = [] + remaining = maximum_bytes + 1 + while remaining: + block = os.read(descriptor, min(remaining, 64 * 1024)) + if not block: + break + chunks.append(block) + remaining -= len(block) + payload = b"".join(chunks) + final = os.fstat(descriptor) + if ( + final.st_dev, + final.st_ino, + final.st_size, + final.st_mtime_ns, + final.st_ctime_ns, + ) != ( + opened.st_dev, + opened.st_ino, + opened.st_size, + opened.st_mtime_ns, + opened.st_ctime_ns, + ): + raise RuntimeError(f"{description} mutated while reading") + if len(payload) > maximum_bytes: + raise RuntimeError(f"{description} exceeds the byte-size limit") + return payload + finally: + os.close(descriptor) + + +def load_correctness_report( + path: Path, *, production: bool = False +) -> dict[str, object]: + payload = _read_regular_bounded( + path, + "validation report", + maximum_bytes=_MAX_VALIDATION_BYTES, + ) + try: + report = json.loads(payload) + except (json.JSONDecodeError, UnicodeDecodeError, RecursionError) as error: + raise RuntimeError("validation report is malformed JSON") from error + if not isinstance(report, dict): + raise RuntimeError("validation report must be a JSON object") + if report.get("schema_version") != "challenge-194-validation-v1": + raise RuntimeError("validation report schema is invalid") + checks = report.get("checks") + if ( + not isinstance(checks, list) + or not checks + or any( + not isinstance(check, dict) or check.get("passed") is not True + for check in checks + ) + or report.get("passed") is not True + ): + raise RuntimeError("validation report did not pass every correctness check") + source = report.get("source") + if not isinstance(source, dict) or source.get("clean_tree") is not True: + raise RuntimeError("validation report provenance is not from a clean tree") + if production: + from .validation import ValidationProtocol, validate_report_payload + + revision = source.get("source_revision") + if ( + not isinstance(revision, str) + or len(revision) != 40 + or any(character not in "0123456789abcdef" for character in revision) + ): + raise RuntimeError("validation report source revision is malformed") + capability = report.get("runtime_capability") + if ( + not isinstance(capability, dict) + or capability.get("schema_version") != "challenge-194-runtime-v1" + ): + raise RuntimeError("validation report runtime provenance is malformed") + try: + validate_report_payload(report, ValidationProtocol.production_v1()) + except (TypeError, ValueError, RuntimeError) as error: + raise RuntimeError( + "validation report does not match the frozen correctness protocol" + ) from error + canonical_report_bytes(report) + return report + + +def _repository_root() -> Path: + return Path(__file__).resolve().parents[7] + + +def _git(*arguments: str) -> str: + try: + completed = subprocess.run( + ["git", *arguments], + cwd=_repository_root(), + check=True, + capture_output=True, + text=True, + ) + except (OSError, subprocess.CalledProcessError) as error: + raise RuntimeError(f"unable to collect Git provenance: {error}") from error + return completed.stdout.strip() + + +def _provenance(production: bool, validation: Mapping[str, object]) -> dict[str, object]: + challenge_root = Path(__file__).resolve().parents[2] + lock = challenge_root / "uv.lock" + lock_payload = _read_regular_bounded(lock, "uv.lock") + validation_revision = str( + (validation.get("source") or {}).get("source_revision", "") + ) + if not production: + return { + "source_revision": validation_revision, + "clean_tree": None, + "validation_source_revision": validation_revision, + "uv_lock_sha256": hashlib.sha256(lock_payload).hexdigest(), + } + revision = _git("rev-parse", "HEAD") + status = _git("status", "--porcelain") + if production: + if status: + raise RuntimeError("production benchmark requires a clean repository") + try: + subprocess.run( + ["git", "merge-base", "--is-ancestor", validation_revision, revision], + cwd=_repository_root(), + check=True, + capture_output=True, + text=True, + ) + except (OSError, subprocess.CalledProcessError) as error: + raise RuntimeError( + "validation report revision is stale or not an ancestor" + ) from error + return { + "source_revision": revision, + "clean_tree": not bool(status), + "validation_source_revision": validation_revision, + "uv_lock_sha256": hashlib.sha256(lock_payload).hexdigest(), + } + + +def _host_evidence() -> dict[str, object]: + uname = os.uname() + return { + "platform": " ".join( + (uname.sysname, uname.release, uname.version, uname.machine) + ), + "machine": uname.machine, + "python": platform.python_version(), + "implementation": sys.implementation.name, + "cpu_count": os.cpu_count(), + "dependencies": { + name: importlib.metadata.version(name) + for name in ("numpy", "scipy", "numba", "llvmlite", "h5py") + }, + "one_thread_environment": dict(ONE_THREAD_ENVIRONMENT), + "rss_source": ( + "resource.getrusage(RUSAGE_SELF).ru_maxrss*1024" + if sys.platform.startswith("linux") + else "unavailable" + ), + "affinity_source": ( + "os.sched_getaffinity/os.sched_setaffinity" + if sys.platform.startswith("linux") + else "unavailable" + ), + } + + +def _worker_command( + *, + mode: str, + backend: str, + length: int, + sigma: float, + kappas: tuple[float, ...], + run_id: str, +) -> list[str]: + return [ + sys.executable, + "-m", + "long_range_percolation.benchmark", + "--worker-mode", + mode, + "--backend", + backend, + "--length", + str(length), + "--sigma-hex", + sigma.hex(), + "--kappas-hex", + ",".join(value.hex() for value in kappas), + "--run-id", + run_id, + ] + + +def _parse_one_json(stdout: str) -> dict[str, object]: + try: + decoder = json.JSONDecoder() + value, end = decoder.raw_decode(stdout) + if stdout[end:].strip(): + raise RuntimeError("worker did not emit exactly one JSON object") + except (json.JSONDecodeError, RecursionError) as error: + raise RuntimeError("worker did not emit exactly one JSON object") from error + if not isinstance(value, dict): + raise RuntimeError("worker did not emit exactly one JSON object") + return value + + +def validate_worker_payload( + payload: Mapping[str, object], + *, + expected_mode: str, + expected_backend: str, + expected_length: int, + expected_sigma: float, + expected_kappas: tuple[float, ...], + expected_run_id: str, +) -> None: + if not isinstance(payload, Mapping) or payload.get("schema_version") != WORKER_SCHEMA: + raise RuntimeError("worker schema mismatch") + expected = { + "run_id": expected_run_id, + "mode": expected_mode, + "backend": expected_backend, + "length": expected_length, + "sigma": expected_sigma.hex(), + "kappas": [value.hex() for value in expected_kappas], + } + if any(payload.get(key) != value for key, value in expected.items()): + raise RuntimeError("worker identity mismatch") + if payload.get("status") not in ("passed", "failed"): + raise RuntimeError("worker status is invalid") + timings = payload.get("timings_ns") + if ( + not isinstance(timings, Mapping) + or set(timings) != _TIMING_FIELDS + or any( + isinstance(value, bool) or not isinstance(value, int) or value < 0 + for value in timings.values() + ) + ): + raise RuntimeError("worker timings are malformed") + metrics = payload.get("metrics") + if ( + not isinstance(metrics, Mapping) + or set(metrics) != _METRIC_FIELDS + or any( + isinstance(value, bool) or not isinstance(value, int) or value < 0 + for value in metrics.values() + ) + ): + raise RuntimeError("worker metrics are malformed") + rss = payload.get("peak_rss_bytes") + cpu = payload.get("selected_cpu") + affinity = payload.get("affinity") + if ( + isinstance(rss, bool) + or not isinstance(rss, int) + or rss <= 0 + or isinstance(cpu, bool) + or not isinstance(cpu, int) + or not isinstance(affinity, list) + or affinity != [cpu] + ): + raise RuntimeError("worker required RSS or affinity telemetry is unavailable") + warmup = payload.get("warmup") + if ( + not isinstance(warmup, Mapping) + or warmup.get("length") != 2 + or warmup.get("completed_before_timing") is not True + ): + raise RuntimeError("worker warmup evidence is malformed") + runtime = payload.get("runtime_capability") + if ( + not isinstance(runtime, Mapping) + or set(runtime) != _RUNTIME_CAPABILITY_FIELDS + or runtime.get("schema_version") != "challenge-194-runtime-v1" + or runtime.get("numba_disable_jit") is not False + or runtime.get("fastmath") is not False + or runtime.get("boundscheck") is not True + ): + raise RuntimeError("worker runtime provenance is malformed") + process = payload.get("process") + if ( + not isinstance(process, Mapping) + or isinstance(process.get("pid"), bool) + or not isinstance(process.get("pid"), int) + ): + raise RuntimeError("worker process evidence is malformed") + canonical_report_bytes(payload) + + +def _failed_run( + *, + mode: str, + backend: str, + length: int, + sigma: float, + run_id: str, + kind: str, + detail: str, + stdout: str, + stderr: str, + exit_status: int | None, +) -> dict[str, object]: + return { + "schema_version": WORKER_SCHEMA, + "run_id": run_id, + "mode": mode, + "backend": backend, + "length": length, + "sigma": sigma.hex(), + "status": "failed", + "failure": {"kind": kind, "detail": detail}, + "stdout": stdout, + "stderr": stderr, + "exit_status": exit_status, + } + + +def _captured_text(value: str | bytes | None) -> str: + if value is None: + return "" + if isinstance(value, bytes): + return value.decode("utf-8", errors="backslashreplace") + return value + + +def _run_worker_process( + *, + mode: str, + backend: str, + length: int, + sigma: float, + kappas: tuple[float, ...], + run_id: str, + cache_dir: Path, + timeout_seconds: float, +) -> dict[str, object]: + command = _worker_command( + mode=mode, + backend=backend, + length=length, + sigma=sigma, + kappas=kappas, + run_id=run_id, + ) + environment = os.environ.copy() + environment.update(ONE_THREAD_ENVIRONMENT) + environment["NUMBA_CACHE_DIR"] = str(cache_dir) + environment["CHALLENGE194_PARENT_LAUNCH_NS"] = str(time.perf_counter_ns()) + try: + completed = subprocess.run( + command, + check=False, + capture_output=True, + text=True, + env=environment, + timeout=timeout_seconds, + ) + except subprocess.TimeoutExpired as error: + return _failed_run( + mode=mode, + backend=backend, + length=length, + sigma=sigma, + run_id=run_id, + kind="timeout", + detail=f"parent wall timeout after {timeout_seconds.hex()} seconds", + stdout=_captured_text(error.stdout), + stderr=_captured_text(error.stderr), + exit_status=None, + ) + except OSError as error: + return _failed_run( + mode=mode, + backend=backend, + length=length, + sigma=sigma, + run_id=run_id, + kind="spawn-failure", + detail=f"{type(error).__name__}: {error}", + stdout="", + stderr="", + exit_status=None, + ) + if completed.returncode != 0: + stdout = _captured_text(completed.stdout) + stderr = _captured_text(completed.stderr) + failure_kind = ( + "allocation-failure" + if "MemoryError" in stderr or "cannot allocate memory" in stderr.lower() + else "nonzero-exit" + ) + return _failed_run( + mode=mode, + backend=backend, + length=length, + sigma=sigma, + run_id=run_id, + kind=failure_kind, + detail=f"worker exited with status {completed.returncode}", + stdout=stdout, + stderr=stderr, + exit_status=completed.returncode, + ) + payload = _parse_one_json(completed.stdout) + validate_worker_payload( + payload, + expected_mode=mode, + expected_backend=backend, + expected_length=length, + expected_sigma=sigma, + expected_kappas=kappas, + expected_run_id=run_id, + ) + result = dict(payload) + result["stdout"] = completed.stdout + result["stderr"] = completed.stderr + result["exit_status"] = completed.returncode + return result + + +def _make_read_only(directory: Path) -> None: + for root, directories, files in os.walk(directory): + for name in files: + os.chmod(Path(root) / name, 0o444) + for name in directories: + os.chmod(Path(root) / name, 0o555) + os.chmod(directory, 0o555) + + +def _make_writable(directory: Path) -> None: + if not directory.exists(): + return + for root, directories, files in os.walk(directory): + os.chmod(root, 0o755) + for name in directories: + os.chmod(Path(root) / name, 0o755) + for name in files: + os.chmod(Path(root) / name, 0o644) + + +def aggregate_steady_runs(runs: Sequence[Mapping[str, object]]) -> dict[str, object]: + if not runs or any(run.get("status") != "passed" for run in runs): + return { + "run_count": len(runs), + "passed_run_count": sum(run.get("status") == "passed" for run in runs), + "median_wall_seconds": None, + "max_wall_seconds": None, + "median_cpu_seconds": None, + "max_cpu_seconds": None, + "median_peak_rss_bytes": None, + "max_peak_rss_bytes": None, + "metric_aggregates": {}, + "raw": [dict(run) for run in runs], + } + walls = sorted( + int(run["timings_ns"]["wall"]) / 1e9 # type: ignore[index] + for run in runs + ) + rss = sorted(int(run["peak_rss_bytes"]) for run in runs) + cpu = sorted( + int(run["timings_ns"]["cpu"]) / 1e9 # type: ignore[index] + for run in runs + ) + middle = len(walls) // 2 + if len(walls) % 2: + median_wall = walls[middle] + median_cpu = cpu[middle] + median_rss: float | int = rss[middle] + else: + median_wall = (walls[middle - 1] + walls[middle]) / 2.0 + median_cpu = (cpu[middle - 1] + cpu[middle]) / 2.0 + median_rss = (rss[middle - 1] + rss[middle]) / 2.0 + metric_values = { + name: [int(run["metrics"][name]) for run in runs] # type: ignore[index] + for name in sorted(_METRIC_FIELDS) + } + metric_aggregates: dict[str, dict[str, float | int]] = {} + for name, values in metric_values.items(): + ordered = sorted(values) + if len(ordered) % 2: + median: float | int = ordered[middle] + else: + median = (ordered[middle - 1] + ordered[middle]) / 2.0 + metric_aggregates[name] = { + "median": median, + "maximum": ordered[-1], + } + return { + "run_count": len(runs), + "passed_run_count": len(runs), + "median_wall_seconds": median_wall, + "max_wall_seconds": max(walls), + "median_cpu_seconds": median_cpu, + "max_cpu_seconds": max(cpu), + "median_peak_rss_bytes": median_rss, + "max_peak_rss_bytes": max(rss), + "raw_metrics": metric_values, + "metric_aggregates": metric_aggregates, + "raw": [dict(run) for run in runs], + } + + +def evaluate_gate( + *, + aggregates: Sequence[Mapping[str, object]], + sigmas: tuple[float, ...], + gate_length: int, + wall_limit_seconds: float, + rss_limit_bytes: int, + correctness_passed: bool, +) -> dict[str, object]: + cells: list[dict[str, object]] = [] + for sigma in sigmas: + matches = [ + value + for value in aggregates + if value.get("backend") == "poisson-numba" + and value.get("length") == gate_length + and value.get("sigma") == sigma.hex() + ] + aggregate = matches[0] if len(matches) == 1 else {} + wall = aggregate.get("max_wall_seconds") + rss = aggregate.get("max_peak_rss_bytes") + complete = aggregate.get("run_count") == aggregate.get("passed_run_count") + wall_passed = ( + complete + and isinstance(wall, (int, float)) + and not isinstance(wall, bool) + and wall <= wall_limit_seconds + ) + rss_passed = ( + complete + and isinstance(rss, int) + and not isinstance(rss, bool) + and rss <= rss_limit_bytes + ) + cells.append( + { + "sigma": sigma.hex(), + "length": gate_length, + "max_wall_seconds": wall, + "wall_limit_seconds": wall_limit_seconds.hex(), + "wall_passed": wall_passed, + "max_peak_rss_bytes": rss, + "rss_limit_bytes": rss_limit_bytes, + "rss_passed": rss_passed, + "passed": wall_passed and rss_passed, + } + ) + return { + "correctness_passed": correctness_passed, + "cells": cells, + "passed": correctness_passed + and len(cells) == len(sigmas) + and all(cell["passed"] for cell in cells), + } + + +def _benchmark_cells( + protocol: BenchmarkProtocol, +) -> list[tuple[str, int, float]]: + cells: list[tuple[str, int, float]] = [] + for backend in protocol.backends: + lengths = ( + (protocol.quadratic_max_length,) + if backend == "quadratic" + else protocol.lengths + ) + for length in lengths: + for sigma in protocol.sigmas: + cells.append((backend, length, sigma)) + return cells + + +def _publish_immutable( + output: Path, report: Mapping[str, object] +) -> None: + from .artifacts import _publish_json_once + + if not isinstance(output, Path): + raise ValueError("output must be a pathlib.Path") + output.parent.mkdir(parents=True, exist_ok=True) + if output.is_symlink(): + raise RuntimeError("refusing to publish through a symlink") + if output.exists(): + raise FileExistsError(f"immutable artifact already exists: {output}") + try: + _publish_json_once(output, dict(report), BENCHMARK_SCHEMA) + except FileExistsError as error: + raise FileExistsError( + f"immutable artifact already exists: {output}" + ) from error + + +def run_benchmark( + protocol: BenchmarkProtocol, output: Path +) -> dict[str, object]: + if not isinstance(protocol, BenchmarkProtocol): + raise ValueError("protocol must be a BenchmarkProtocol") + if protocol.validation_report is None: + raise RuntimeError("validation report is required") + if output.exists() or output.is_symlink(): + raise FileExistsError(f"immutable artifact already exists: {output}") + correctness = load_correctness_report( + protocol.validation_report, production=protocol.is_production + ) + provenance = _provenance(protocol.is_production, correctness) + correctness_payload = _read_regular_bounded( + protocol.validation_report, + "validation report", + maximum_bytes=_MAX_VALIDATION_BYTES, + ) + all_runs: list[dict[str, object]] = [] + aggregates: list[dict[str, object]] = [] + runtime_identity: Mapping[str, object] | None = None + with tempfile.TemporaryDirectory(prefix="challenge-194-benchmark-") as root_name: + root = Path(root_name) + try: + compile_cache = root / "compile" + compile_cache.mkdir() + compile_backend = ( + "poisson-numba" + if "poisson-numba" in protocol.backends + else protocol.backends[0] + ) + compile_id = uuid.uuid4().hex + compile_run = _run_worker_process( + mode="compile", + backend=compile_backend, + length=protocol.gate_length, + sigma=protocol.sigmas[0], + kappas=protocol.kappas, + run_id=compile_id, + cache_dir=compile_cache, + timeout_seconds=protocol.wall_limit_seconds + 120.0, + ) + all_runs.append(compile_run) + compile_passed = compile_run.get("status") == "passed" + if compile_passed: + runtime_identity = compile_run.get("runtime_capability") # type: ignore[assignment] + if ( + protocol.is_production + and runtime_identity != correctness.get("runtime_capability") + ): + raise RuntimeError( + "worker runtime provenance does not match correctness report" + ) + if compile_backend == "poisson-numba" and not any( + path.is_file() for path in compile_cache.rglob("*") + ): + raise RuntimeError("compile worker did not populate the Numba cache") + for cell_index, (backend, length, sigma) in enumerate( + _benchmark_cells(protocol) + ): + steady: list[dict[str, object]] = [] + if compile_passed: + for run_index in range(protocol.steady_runs): + steady_cache = root / f"steady-{cell_index}-{run_index}" + shutil.copytree(compile_cache, steady_cache) + _make_read_only(steady_cache) + steady_id = uuid.uuid4().hex + run = _run_worker_process( + mode="steady", + backend=backend, + length=length, + sigma=sigma, + kappas=protocol.kappas, + run_id=steady_id, + cache_dir=steady_cache, + timeout_seconds=protocol.wall_limit_seconds + 30.0, + ) + steady.append(run) + all_runs.append(run) + if ( + run.get("status") == "passed" + and run.get("runtime_capability") != runtime_identity + ): + raise RuntimeError("worker runtime provenance mismatch") + successful_pids = [ + value.get("process", {}).get("pid") + for value in all_runs + if value.get("status") == "passed" + ] + if len(successful_pids) != len(set(successful_pids)): + raise RuntimeError( + "benchmark workers were not fresh subprocesses" + ) + _make_writable(steady_cache) + aggregate = aggregate_steady_runs(steady) + aggregates.append( + { + "backend": backend, + "length": length, + "sigma": sigma.hex(), + **aggregate, + } + ) + finally: + _make_writable(root) + gate = evaluate_gate( + aggregates=aggregates, + sigmas=protocol.sigmas, + gate_length=protocol.gate_length, + wall_limit_seconds=protocol.wall_limit_seconds, + rss_limit_bytes=protocol.rss_limit_bytes, + correctness_passed=bool(correctness["passed"]), + ) + infrastructure_passed = all( + run.get("status") == "passed" + or ( + run.get("mode") == "steady" + and run.get("failure", {}).get("kind") + in ("timeout", "allocation-failure") + ) + for run in all_runs + ) + report: dict[str, object] = { + "schema_version": BENCHMARK_SCHEMA, + "protocol": protocol.to_document(), + "correctness": { + "path": str(protocol.validation_report), + "sha256": hashlib.sha256(correctness_payload).hexdigest(), + "passed": correctness["passed"], + "check_count": len(correctness["checks"]), + }, + "provenance": provenance, + "host": _host_evidence(), + "worker_runtime_capability": dict(runtime_identity or {}), + "runs": all_runs, + "aggregates": aggregates, + "gate": gate, + "infrastructure_passed": infrastructure_passed, + "passed": infrastructure_passed and bool(gate["passed"]), + } + payload = canonical_report_bytes(report) + if not infrastructure_passed and any( + run.get("failure", {}).get("kind") not in ("timeout", "nonzero-exit") + for run in all_runs + if run.get("status") == "failed" + ): + raise RuntimeError("benchmark infrastructure failed before complete measurement") + _publish_immutable(output, report) + return report + + +def _pin_one_cpu() -> tuple[int, list[int]]: + if not sys.platform.startswith("linux"): + raise RuntimeError("CPU affinity telemetry is required and unavailable") + if not hasattr(os, "sched_getaffinity") or not hasattr(os, "sched_setaffinity"): + raise RuntimeError("CPU affinity telemetry is required and unavailable") + available = sorted(os.sched_getaffinity(0)) + if not available: + raise RuntimeError("CPU affinity set is empty") + selected = available[0] + try: + os.sched_setaffinity(0, {selected}) + except OSError as error: + raise RuntimeError("unable to pin benchmark worker to one CPU") from error + actual = sorted(os.sched_getaffinity(0)) + if actual != [selected]: + raise RuntimeError("benchmark worker affinity pin did not take effect") + return selected, actual + + +def _peak_rss_bytes() -> int: + if not sys.platform.startswith("linux"): + raise RuntimeError("peak RSS telemetry is required and unavailable") + try: + import resource + + value = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + except (ImportError, OSError, ValueError) as error: + raise RuntimeError("peak RSS telemetry is required and unavailable") from error + result = int(value) * 1024 + if result <= 0: + raise RuntimeError("peak RSS telemetry is invalid") + return result + + +def _basic_graph_observables(labels, edges) -> tuple[int, int]: + import numpy as np + + counts = np.bincount(labels) + component_count = int(counts.size) + checksum = int( + np.sum(counts.astype(np.int64) * counts.astype(np.int64), dtype=np.int64) + ) + return component_count, checksum + int(edges.shape[0]) + + +def _execute_backend( + backend: str, + length: int, + sigma: float, + kappas: tuple[float, ...], + *, + warmup: bool, +) -> tuple[dict[str, int], int, int]: + import numpy as np + + if backend == "poisson-numba": + from .alias import build_distance_alias + from .kernel import periodic_kernel + from .poisson_sweep import run_poisson_numba + from .trajectory import TrajectoryRequest + + kernel = periodic_kernel(length, sigma) + digest = hashlib.sha256(kernel.tobytes(order="C")).hexdigest() + alias = build_distance_alias(length, sigma, kernel, digest) + request = TrajectoryRequest( + length=length, + sigma=sigma, + sigma_grid_id=f"benchmark-{sigma.hex()}", + kappas=np.asarray(kappas, dtype=np.float64), + master_seed=194_100_000 if warmup else 194_200_000, + phase="benchmark", + replica=0, + kernel_sha256=digest, + ) + started = time.perf_counter_ns() + result = run_poisson_numba(request, kernel, alias) + sampling_ns = time.perf_counter_ns() - started + observable_started = time.perf_counter_ns() + final_components = int(result.observables[-1, 1]) + unions = length - final_components + _ = float(result.observables[:, 4].sum()) + observable_ns = time.perf_counter_ns() - observable_started + metrics = { + "events": result.event_count, + "unique_edges": result.event_count - result.duplicate_count, + "unions": unions, + "duplicates": result.duplicate_count, + "total_probes": int(result.hash_diagnostics[2]), + "maximum_probe": int(result.hash_diagnostics[3]), + "rehashes": int(result.hash_diagnostics[4]), + "bytes": int( + kernel.nbytes + + alias.probability.nbytes + + alias.alias.nbytes + + alias.multiplicity.nbytes + + alias.class_weight.nbytes + + result.observables.nbytes + + result.terminal_counters.nbytes + + result.draw_counts.nbytes + + result.hash_diagnostics.nbytes + ), + } + return metrics, sampling_ns, observable_ns + + from .geometric import sample_geometric + from .model import ModelSpec + from .oracle import sample_quadratic + + rng = np.random.default_rng(194_200_000 if not warmup else 194_100_000) + events = 0 + unique_edges = 0 + unions = 0 + byte_count = 0 + observable_ns = 0 + sampling_started = time.perf_counter_ns() + sampler = sample_quadratic if backend == "quadratic" else sample_geometric + for kappa in kappas: + sample = sampler( + ModelSpec(length=length, sigma=sigma, kappa=kappa), + rng, + ) + events += int(sample.edges.shape[0]) + unique_edges += int(sample.edges.shape[0]) + observable_started = time.perf_counter_ns() + components, _ = _basic_graph_observables(sample.labels, sample.edges) + observable_ns += time.perf_counter_ns() - observable_started + unions += length - components + byte_count += sample.edges.nbytes + sample.labels.nbytes + sampling_ns = time.perf_counter_ns() - sampling_started - observable_ns + return ( + { + "events": events, + "unique_edges": unique_edges, + "unions": unions, + "duplicates": 0, + "total_probes": 0, + "maximum_probe": 0, + "rehashes": 0, + "bytes": byte_count, + }, + sampling_ns, + observable_ns, + ) + + +def _worker_main(arguments: argparse.Namespace) -> int: + launch_text = os.environ.get("CHALLENGE194_PARENT_LAUNCH_NS") + entered = time.perf_counter_ns() + startup_ns = 0 + if launch_text is not None: + try: + startup_ns = max(0, entered - int(launch_text)) + except ValueError: + startup_ns = 0 + selected_cpu, affinity = _pin_one_cpu() + import_started = time.perf_counter_ns() + from .runtime import runtime_capability + + sigma = float.fromhex(arguments.sigma_hex) + kappas = tuple( + float.fromhex(value) for value in arguments.kappas_hex.split(",") if value + ) + warmup_started = time.perf_counter_ns() + warmup_metrics, _, _ = _execute_backend( + arguments.backend, 2, sigma, kappas, warmup=True + ) + warmup_finished = time.perf_counter_ns() + import_and_warmup_ns = warmup_finished - import_started + if arguments.worker_mode == "compile": + compile_ns = import_and_warmup_ns + cache_load_warmup_ns = 0 + else: + compile_ns = 0 + cache_load_warmup_ns = import_and_warmup_ns + metrics = warmup_metrics + sampling_ns = 0 + observable_ns = 0 + serialization_ns = 0 + wall_ns = 0 + cpu_ns = 0 + if arguments.worker_mode != "compile": + wall_started = time.perf_counter_ns() + cpu_started = time.process_time_ns() + metrics, sampling_ns, observable_ns = _execute_backend( + arguments.backend, + arguments.length, + sigma, + kappas, + warmup=False, + ) + if arguments.worker_mode == "measure-observables": + observable_ns += sampling_ns + sampling_ns = 0 + serialization_started = time.perf_counter_ns() + json.dumps(metrics, sort_keys=True, separators=(",", ":")) + serialization_ns = time.perf_counter_ns() - serialization_started + cpu_ns = time.process_time_ns() - cpu_started + wall_ns = time.perf_counter_ns() - wall_started + payload = { + "schema_version": WORKER_SCHEMA, + "run_id": arguments.run_id, + "mode": arguments.worker_mode, + "backend": arguments.backend, + "length": arguments.length, + "sigma": sigma.hex(), + "kappas": [value.hex() for value in kappas], + "status": "passed", + "failure": None, + "timings_ns": { + "startup": startup_ns, + "cache_load_warmup": cache_load_warmup_ns, + "compile": compile_ns, + "sampling": sampling_ns, + "observable": observable_ns, + "artifact_serialization": serialization_ns, + "wall": wall_ns, + "cpu": cpu_ns, + }, + "metrics": metrics, + "peak_rss_bytes": _peak_rss_bytes(), + "selected_cpu": selected_cpu, + "affinity": affinity, + "warmup": {"length": 2, "completed_before_timing": True}, + "runtime_capability": runtime_capability(), + "process": { + "pid": os.getpid(), + "ppid": os.getppid(), + "python": sys.executable, + "platform": platform.platform(), + }, + } + sys.stdout.buffer.write(canonical_report_bytes(payload)) + sys.stdout.flush() + return 0 + + +def cli_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Run the frozen Challenge 194 production performance gate." + ) + parser.add_argument("--validation-report", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + return parser + + +def _worker_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument("--worker-mode", choices=WORKER_MODES, required=True) + parser.add_argument("--backend", choices=BACKENDS, required=True) + parser.add_argument("--length", type=int, required=True) + parser.add_argument("--sigma-hex", required=True) + parser.add_argument("--kappas-hex", required=True) + parser.add_argument("--run-id", required=True) + return parser + + +def main(argv: list[str] | None = None) -> int: + arguments = list(sys.argv[1:] if argv is None else argv) + if "--worker-mode" in arguments: + return _worker_main(_worker_parser().parse_args(arguments)) + parsed = cli_parser().parse_args(arguments) + frozen = BenchmarkProtocol.production_v1() + protocol = BenchmarkProtocol( + lengths=frozen.lengths, + sigmas=frozen.sigmas, + kappas=frozen.kappas, + steady_runs=frozen.steady_runs, + wall_limit_seconds=frozen.wall_limit_seconds, + rss_limit_bytes=frozen.rss_limit_bytes, + gate_length=frozen.gate_length, + backends=frozen.backends, + quadratic_max_length=frozen.quadratic_max_length, + validation_report=parsed.validation_report, + name=frozen.name, + ) + try: + report = run_benchmark(protocol, parsed.output) + except Exception as error: + print( + f"benchmark infrastructure failure: {type(error).__name__}: {error}", + file=sys.stderr, + flush=True, + ) + return 1 + print( + f"benchmark passed={report['passed']} output={parsed.output}", + flush=True, + ) + if not report["infrastructure_passed"]: + return 1 + return 0 if report["passed"] else 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/counter_rng.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/counter_rng.py new file mode 100644 index 000000000..d2c1fad03 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/counter_rng.py @@ -0,0 +1,293 @@ +from __future__ import annotations + +from dataclasses import dataclass +import hashlib +import json +from typing import Literal + +import numba +import numpy as np +import numpy.typing as npt + + +Phase = Literal["validation", "benchmark", "pilot", "confirmatory"] +U32 = npt.NDArray[np.uint32] + +STREAM_ALIAS_COLUMN: int = 0 +STREAM_ALIAS_THRESHOLD: int = 1 +STREAM_EDGE_OFFSET: int = 2 +STREAM_EXPONENTIAL: int = 3 +STREAM_COUNT: int = 4 + +PHILOX_M0 = np.uint32(0xD2511F53) +PHILOX_M1 = np.uint32(0xCD9E8D57) +PHILOX_W0 = np.uint32(0x9E3779B9) +PHILOX_W1 = np.uint32(0xBB67AE85) +RNG_VERSION = "philox4x32-10/open32-v1/bounded-reject-v1" + +_MASK32 = (1 << 32) - 1 +_UINT64_LIMIT = 1 << 64 +_STREAM_DOMAIN = b"challenge-194-philox-stream-v1\0" +_PHASES = frozenset(("validation", "benchmark", "pilot", "confirmatory")) + + +@dataclass(frozen=True) +class StreamIdentity: + master_seed: int + phase: Phase + length: int + sigma_grid_id: str + replica: int + stream_id: int + + +@dataclass(frozen=True) +class StreamMaterial: + key: U32 + initial_counter: U32 + material_sha256: str + + +def _checked_int(value: object, name: str, upper_bound: int) -> int: + if isinstance(value, bool) or not isinstance(value, int): + raise ValueError(f"{name} must be an integer") + if not 0 <= value < upper_bound: + raise ValueError(f"{name} is outside its canonical range") + return value + + +def _canonical_identity(identity: StreamIdentity) -> bytes: + if not isinstance(identity, StreamIdentity): + raise ValueError("identity must be a StreamIdentity") + master_seed = _checked_int( + identity.master_seed, "master_seed", _UINT64_LIMIT + ) + replica = _checked_int(identity.replica, "replica", _UINT64_LIMIT) + stream_id = _checked_int(identity.stream_id, "stream_id", STREAM_COUNT) + length = _checked_int(identity.length, "length", _UINT64_LIMIT) + if length < 2 or length % 2: + raise ValueError("length must be even and at least two") + if not isinstance(identity.phase, str) or identity.phase not in _PHASES: + raise ValueError("phase is not in the frozen phase namespace") + if ( + not isinstance(identity.sigma_grid_id, str) + or not identity.sigma_grid_id + or identity.sigma_grid_id != identity.sigma_grid_id.strip() + or any( + ord(character) < 0x20 or ord(character) == 0x7F + for character in identity.sigma_grid_id + ) + ): + raise ValueError( + "sigma_grid_id must be a trimmed nonempty string without " + "control characters" + ) + document = { + "length": length, + "master_seed": master_seed, + "phase": identity.phase, + "replica": replica, + "sigma_grid_id": identity.sigma_grid_id, + "stream_id": stream_id, + } + try: + return json.dumps( + document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + except UnicodeEncodeError as error: + raise ValueError("sigma_grid_id must be valid UTF-8") from error + + +def derive_stream_material(identity: StreamIdentity) -> StreamMaterial: + digest = hashlib.sha256( + _STREAM_DOMAIN + _canonical_identity(identity) + ).digest() + key = np.frombuffer(digest[0:8], dtype=" np.ndarray: + if ( + not isinstance(value, np.ndarray) + or value.dtype != np.dtype(np.uint32) + or value.shape != (length,) + or not value.flags.c_contiguous + ): + raise ValueError( + f"{name} must be a contiguous uint32 array with shape ({length},)" + ) + return value + + +def philox4x32_10_reference(counter: U32, key: U32) -> U32: + counter = _checked_u32_array(counter, "counter", 4) + key = _checked_u32_array(key, "key", 2) + c0, c1, c2, c3 = (int(word) for word in counter) + k0, k1 = (int(word) for word in key) + multiplier0 = int(PHILOX_M0) + multiplier1 = int(PHILOX_M1) + Weyl0 = int(PHILOX_W0) + Weyl1 = int(PHILOX_W1) + for _ in range(10): + product0 = multiplier0 * c0 + product1 = multiplier1 * c2 + low0 = product0 & _MASK32 + high0 = (product0 >> 32) & _MASK32 + low1 = product1 & _MASK32 + high1 = (product1 >> 32) & _MASK32 + c0, c1, c2, c3 = ( + (high1 ^ c1 ^ k0) & _MASK32, + low1, + (high0 ^ c3 ^ k1) & _MASK32, + low0, + ) + k0 = (k0 + Weyl0) & _MASK32 + k1 = (k1 + Weyl1) & _MASK32 + return np.asarray((c0, c1, c2, c3), dtype=np.uint32) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _multiply_high_low( + multiplier: np.uint32, word: np.uint32 +) -> tuple[np.uint32, np.uint32]: + product = np.uint64(multiplier) * np.uint64(word) + low = np.uint32(product & np.uint64(0xFFFFFFFF)) + high = np.uint32(product >> np.uint64(32)) + return high, low + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def philox4x32_10(counter: U32, key: U32, out: U32) -> None: + c0 = counter[0] + c1 = counter[1] + c2 = counter[2] + c3 = counter[3] + k0 = key[0] + k1 = key[1] + for _ in range(10): + high0, low0 = _multiply_high_low(np.uint32(0xD2511F53), c0) + high1, low1 = _multiply_high_low(np.uint32(0xCD9E8D57), c2) + c0, c1, c2, c3 = ( + np.uint32(high1 ^ c1 ^ k0), + low1, + np.uint32(high0 ^ c3 ^ k1), + low0, + ) + k0 = np.uint32( + (np.uint64(k0) + np.uint64(0x9E3779B9)) + & np.uint64(0xFFFFFFFF) + ) + k1 = np.uint32( + (np.uint64(k1) + np.uint64(0xBB67AE85)) + & np.uint64(0xFFFFFFFF) + ) + out[0] = c0 + out[1] = c1 + out[2] = c2 + out[3] = c3 + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _increment_counter(counter: U32) -> None: + carry = np.uint64(1) + for index in range(4): + total = np.uint64(counter[index]) + carry + counter[index] = np.uint32(total & np.uint64(0xFFFFFFFF)) + carry = total >> np.uint64(32) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def next_u32( + counter: U32, + key: U32, + block: U32, + lane_and_valid: npt.NDArray[np.uint8], + accounting: npt.NDArray[np.uint64], +) -> np.uint32: + lane = lane_and_valid[0] + valid = lane_and_valid[1] + if valid > np.uint8(1) or lane > np.uint8(3): + raise ValueError("lane_and_valid contains invalid state") + if valid == np.uint8(0): + if lane != np.uint8(0): + raise ValueError("invalid lane for an empty block") + philox4x32_10(counter, key, block) + _increment_counter(counter) + lane = np.uint8(0) + valid = np.uint8(1) + accounting[1] += np.uint64(1) + + word = block[lane] + accounting[0] += np.uint64(1) + lane += np.uint8(1) + if lane == np.uint8(4): + lane_and_valid[0] = np.uint8(0) + lane_and_valid[1] = np.uint8(0) + else: + lane_and_valid[0] = lane + lane_and_valid[1] = valid + return word + + +def u32_to_open(word: np.uint32) -> float: + if ( + isinstance(word, (bool, np.bool_)) + or not isinstance(word, (int, np.integer)) + or not 0 <= int(word) <= _MASK32 + ): + raise ValueError("word must be an unsigned 32-bit integer") + return (float(word) + 0.5) * (2.0**-32) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def uniform_open( + counter: U32, + key: U32, + block: U32, + lane_and_valid: npt.NDArray[np.uint8], + accounting: npt.NDArray[np.uint64], +) -> float: + word = next_u32(counter, key, block, lane_and_valid, accounting) + return (float(word) + 0.5) * (2.0**-32) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def bounded_u32( + bound: int, + counter: U32, + key: U32, + block: U32, + lane_and_valid: npt.NDArray[np.uint8], + accounting: npt.NDArray[np.uint64], +) -> np.uint32: + if ( + bound < 1 + or bound > 0xFFFFFFFF + or bound != np.floor(bound) + ): + raise ValueError("bound must be in [1, 2**32 - 1]") + bound_u64 = np.uint64(bound) + threshold = ( + np.uint64(1 << 32) - bound_u64 + ) % bound_u64 + while True: + word = next_u32( + counter, key, block, lane_and_valid, accounting + ) + if np.uint64(word) < threshold: + accounting[2] += np.uint64(1) + continue + return np.uint32(np.uint64(word) % bound_u64) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/edge_set.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/edge_set.py new file mode 100644 index 000000000..f1a48ecc4 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/edge_set.py @@ -0,0 +1,331 @@ +from __future__ import annotations + +import sys + +import numba +import numpy as np +import numpy.typing as npt + + +U8 = npt.NDArray[np.uint8] +U64 = npt.NDArray[np.uint64] + +_MAX_UINT64 = (1 << 64) - 1 +_MAX_CAPACITY = 1 << ((sys.maxsize // np.dtype(np.uint64).itemsize).bit_length() - 1) +_LOW32 = np.uint64(0xFFFFFFFF) + + +def validate_edge_set_state( + keys: U64, occupied: U8, diagnostics: U64 +) -> None: + arrays = ( + (keys, np.dtype(np.uint64), "keys"), + (occupied, np.dtype(np.uint8), "occupied"), + (diagnostics, np.dtype(np.uint64), "diagnostics"), + ) + for array, dtype, name in arrays: + if not isinstance(array, np.ndarray) or array.dtype != dtype: + raise ValueError(f"{name} must be a {dtype.name} NumPy array") + if array.ndim != 1: + raise ValueError(f"{name} must be one-dimensional") + if not array.flags.c_contiguous: + raise ValueError(f"{name} must be C-contiguous") + if not array.flags.writeable: + raise ValueError(f"{name} must be writable") + + if diagnostics.shape != (5,): + raise ValueError("diagnostics must have shape (5,)") + if ( + np.shares_memory(keys, occupied) + or np.shares_memory(keys, diagnostics) + or np.shares_memory(occupied, diagnostics) + ): + raise ValueError("edge-set arrays must not overlap or share memory") + + capacity = keys.size + if ( + capacity < 2 + or capacity != occupied.size + or capacity & (capacity - 1) + or int(diagnostics[0]) != capacity + ): + raise ValueError("edge-set capacity state is invalid") + if np.any(occupied > np.uint8(1)): + raise ValueError("occupied must contain only zero or one") + + occupied_count = int(np.count_nonzero(occupied)) + size = int(diagnostics[1]) + if occupied_count != size: + raise ValueError("edge-set size does not match occupied count") + if 10 * size > 7 * capacity: + raise ValueError("edge-set load exceeds 0.70") + + total_probes = int(diagnostics[2]) + max_probe = int(diagnostics[3]) + rehashes = int(diagnostics[4]) + if max_probe > total_probes: + raise ValueError("max_probe exceeds total_probes") + if size > total_probes: + raise ValueError("size exceeds total_probes") + if size == 0 and (total_probes != 0 or max_probe != 0 or rehashes != 0): + raise ValueError("empty edge-set diagnostics are inconsistent") + + +def allocate_edge_set(expected_size: int) -> tuple[U64, U8, U64]: + if ( + isinstance(expected_size, bool) + or not isinstance(expected_size, int) + or expected_size < 0 + ): + raise ValueError("expected_size must be a nonnegative integer") + + capacity = 2 + while 10 * expected_size > 7 * capacity: + if capacity >= _MAX_CAPACITY: + raise ValueError("edge-set capacity exceeds the addressable range") + capacity *= 2 + + keys = np.zeros(capacity, dtype=np.uint64) + occupied = np.zeros(capacity, dtype=np.uint8) + diagnostics = np.asarray( + (capacity, 0, 0, 0, 0), dtype=np.uint64 + ) + validate_edge_set_state(keys, occupied, diagnostics) + return keys, occupied, diagnostics + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _add_u64(left: np.uint64, right: np.uint64) -> np.uint64: + low_sum = (left & _LOW32) + (right & _LOW32) + low = low_sum & _LOW32 + carry = low_sum >> np.uint64(32) + high = ( + (left >> np.uint64(32)) + + (right >> np.uint64(32)) + + carry + ) & _LOW32 + return np.uint64((high << np.uint64(32)) | low) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _multiply_u64(left: np.uint64, right: np.uint64) -> np.uint64: + left_low = left & _LOW32 + right_low = right & _LOW32 + low_product = left_low * right_low + cross = ( + ((left >> np.uint64(32)) * right_low & _LOW32) + + (left_low * (right >> np.uint64(32)) & _LOW32) + ) & _LOW32 + low = low_product & _LOW32 + high = ((low_product >> np.uint64(32)) + cross) & _LOW32 + return np.uint64((high << np.uint64(32)) | low) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _splitmix64(value: np.uint64) -> np.uint64: + value = _add_u64(value, np.uint64(0x9E3779B97F4A7C15)) + value = _multiply_u64( + value ^ (value >> np.uint64(30)), + np.uint64(0xBF58476D1CE4E5B9), + ) + value = _multiply_u64( + value ^ (value >> np.uint64(27)), + np.uint64(0x94D049BB133111EB), + ) + return np.uint64(value ^ (value >> np.uint64(31))) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _checked_probe( + total_probes: np.uint64, max_probe: np.uint64, probe: int +) -> tuple[np.uint64, np.uint64]: + if total_probes == np.uint64(0xFFFFFFFFFFFFFFFF): + raise OverflowError("probe diagnostics exceed uint64") + total_probes += np.uint64(1) + if np.uint64(probe) > max_probe: + max_probe = np.uint64(probe) + return total_probes, max_probe + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _rehash_local( + old_keys: U64, + old_occupied: U8, + total_probes: np.uint64, + max_probe: np.uint64, +) -> tuple[U64, U8, np.uint64, np.uint64]: + old_capacity = len(old_keys) + if old_capacity >= _MAX_CAPACITY: + raise ValueError("edge-set capacity exceeds the addressable range") + capacity = old_capacity * 2 + keys = np.zeros(capacity, dtype=np.uint64) + occupied = np.zeros(capacity, dtype=np.uint8) + mask = capacity - 1 + + for old_slot in range(old_capacity): + if old_occupied[old_slot] == np.uint8(0): + continue + value = old_keys[old_slot] + slot = _splitmix64(value) & np.uint64(mask) + probe = 1 + while occupied[slot] != np.uint8(0): + total_probes, max_probe = _checked_probe( + total_probes, max_probe, probe + ) + slot = (slot + np.uint64(1)) & np.uint64(mask) + probe += 1 + total_probes, max_probe = _checked_probe( + total_probes, max_probe, probe + ) + keys[slot] = value + occupied[slot] = np.uint8(1) + + return keys, occupied, total_probes, max_probe + + +_U64_C = numba.types.Array(numba.uint64, 1, "C") +_U8_C = numba.types.Array(numba.uint8, 1, "C") +_INSERT_RESULT = numba.types.Tuple((_U64_C, _U8_C, numba.boolean)) +_INSERT_SIGNATURE = _INSERT_RESULT( + _U64_C, _U8_C, _U64_C, numba.uint64 +) + + +@numba.njit( + _INSERT_SIGNATURE, cache=True, boundscheck=True, fastmath=False +) +def edge_set_insert_kernel( + keys: U64, + occupied: U8, + diagnostics: U64, + value: np.uint64, +) -> tuple[U64, U8, bool]: + size = diagnostics[1] + total_probes = diagnostics[2] + max_probe = diagnostics[3] + rehashes = diagnostics[4] + + while True: + capacity = len(keys) + mask = capacity - 1 + slot = _splitmix64(value) & np.uint64(mask) + probe = 1 + while occupied[slot] != np.uint8(0): + total_probes, max_probe = _checked_probe( + total_probes, max_probe, probe + ) + if keys[slot] == value: + diagnostics[2] = total_probes + diagnostics[3] = max_probe + return keys, occupied, False + slot = (slot + np.uint64(1)) & np.uint64(mask) + probe += 1 + + total_probes, max_probe = _checked_probe( + total_probes, max_probe, probe + ) + if np.uint64(10) * (size + np.uint64(1)) <= np.uint64( + 7 * capacity + ): + keys[slot] = value + occupied[slot] = np.uint8(1) + diagnostics[0] = np.uint64(capacity) + diagnostics[1] = size + np.uint64(1) + diagnostics[2] = total_probes + diagnostics[3] = max_probe + diagnostics[4] = rehashes + return keys, occupied, True + + if rehashes == np.uint64(0xFFFFFFFFFFFFFFFF): + raise OverflowError("rehash diagnostics exceed uint64") + keys, occupied, total_probes, max_probe = _rehash_local( + keys, occupied, total_probes, max_probe + ) + rehashes += np.uint64(1) + + +def edge_set_insert( + keys: U64, + occupied: U8, + diagnostics: U64, + value: np.uint64, +) -> tuple[U64, U8, bool]: + validate_edge_set_state(keys, occupied, diagnostics) + if ( + isinstance(value, (bool, np.bool_)) + or not isinstance(value, (int, np.integer)) + or not 0 <= int(value) <= _MAX_UINT64 + ): + raise ValueError("value must be an integer in the uint64 range") + return edge_set_insert_kernel( + keys, occupied, diagnostics, np.uint64(value) + ) + + +def build_class_start(multiplicity: U64) -> U64: + if ( + not isinstance(multiplicity, np.ndarray) + or multiplicity.dtype != np.dtype(np.uint64) + or multiplicity.ndim != 1 + or not multiplicity.flags.c_contiguous + or multiplicity.size < 1 + ): + raise ValueError( + "multiplicity must be a nonempty contiguous uint64 array" + ) + + class_start = np.empty(multiplicity.size + 1, dtype=np.uint64) + class_start[0] = np.uint64(0) + running = 0 + for index, raw_count in enumerate(multiplicity): + count = int(raw_count) + if count < 1: + raise ValueError("class multiplicities must be positive") + if running > _MAX_UINT64 - count: + raise ValueError("class-start prefix sum exceeds uint64") + running += count + class_start[index + 1] = np.uint64(running) + return class_start + + +def encode_edge_id( + class_start: U64, distance_index: int, offset: int +) -> np.uint64: + if ( + not isinstance(class_start, np.ndarray) + or class_start.dtype != np.dtype(np.uint64) + or class_start.ndim != 1 + or not class_start.flags.c_contiguous + or class_start.size < 2 + or int(class_start[0]) != 0 + ): + raise ValueError( + "class_start must be a nonempty canonical contiguous uint64 array" + ) + previous = -1 + for raw_start in class_start: + start = int(raw_start) + if start <= previous: + raise ValueError("class_start must be strictly increasing") + previous = start + if ( + isinstance(distance_index, (bool, np.bool_)) + or not isinstance(distance_index, (int, np.integer)) + or not 0 <= int(distance_index) < class_start.size - 1 + ): + raise ValueError("distance_index is outside class_start") + if ( + isinstance(offset, (bool, np.bool_)) + or not isinstance(offset, (int, np.integer)) + or int(offset) < 0 + ): + raise ValueError("offset must be a nonnegative integer") + + start = int(class_start[int(distance_index)]) + stop = int(class_start[int(distance_index) + 1]) + offset_value = int(offset) + if offset_value >= stop - start: + raise ValueError("offset is outside the selected distance class") + if start > _MAX_UINT64 - offset_value: + raise ValueError("encoded edge ID exceeds uint64") + return np.uint64(start + offset_value) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/enumeration.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/enumeration.py new file mode 100644 index 000000000..2a41539c4 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/enumeration.py @@ -0,0 +1,154 @@ +from __future__ import annotations + +from dataclasses import dataclass +import math +import sys +from typing import Iterator + +import numpy as np + +from .kernel import periodic_kernel +from .model import ModelSpec, iter_unordered_edges +from .union_find import UnionFind + +# log_rate = log(kappa) + log(J) branch thresholds (float64). +# +# exp(log_rate) overflows when log_rate exceeds LOG_RATE_EXP_OVERFLOW. +LOG_RATE_EXP_OVERFLOW = math.log(sys.float_info.max) + + +def _compute_open_saturation_log_rate() -> float: + lo = math.log(-math.log(sys.float_info.min)) + hi = 7.0 + for _ in range(100): + mid = (lo + hi) / 2.0 + if math.exp(-math.exp(mid)) == 0.0: + hi = mid + else: + lo = mid + return hi + + +def _compute_exp_underflow_log_rate() -> float: + lo = -750.0 + hi = -700.0 + for _ in range(100): + mid = (lo + hi) / 2.0 + if math.exp(mid) == 0.0: + lo = mid + else: + hi = mid + return lo + + +# exp(-exp(log_rate)) underflows to 0.0 once log_rate reaches this value. +LOG_RATE_OPEN_SATURATION = _compute_open_saturation_log_rate() + +# exp(log_rate) underflows to 0.0 at or below this value. +LOG_RATE_EXP_UNDERFLOW = _compute_exp_underflow_log_rate() + + +@dataclass(frozen=True) +class GraphOutcome: + mask: int + probability: float + open_edges: int + component_sizes: tuple[int, ...] + + +def _log_open_edge_weight(log_rate: float) -> float: + if log_rate <= LOG_RATE_EXP_UNDERFLOW: + return log_rate + if log_rate >= LOG_RATE_OPEN_SATURATION: + return 0.0 + rate = math.exp(log_rate) + if rate == 0.0: + return log_rate + neg_rate_exp = math.exp(-rate) + if neg_rate_exp == 0.0: + return 0.0 + if neg_rate_exp == 1.0: + complement = -math.expm1(-rate) + if complement == 0.0: + return log_rate + return math.log(complement) + return math.log1p(-neg_rate_exp) + + +def _log_closed_edge_weight(log_rate: float) -> float: + if log_rate > LOG_RATE_EXP_OVERFLOW: + return -math.inf + rate = math.exp(log_rate) + if rate == 0.0: + return -0.0 + return -rate + + +def _probability_from_log(log_probability: float) -> float: + if log_probability == -math.inf: + return 0.0 + return math.exp(log_probability) + + +def _component_sizes_for_mask( + length: int, + edges: list[tuple[int, int]], + mask: int, +) -> tuple[int, ...]: + union_find = UnionFind(length) + for index, (left, right) in enumerate(edges): + if mask & (1 << index): + union_find.union(left, right) + return tuple(union_find.component_sizes().tolist()) + + +def enumerate_graphs(spec: ModelSpec) -> Iterator[GraphOutcome]: + if spec.length > 6: + raise ValueError("exact enumeration supports length at most six") + edges = list(iter_unordered_edges(spec.length)) + if spec.kappa == 0.0: + for mask in range(1 << len(edges)): + yield GraphOutcome( + mask=mask, + probability=1.0 if mask == 0 else 0.0, + open_edges=0 if mask == 0 else mask.bit_count(), + component_sizes=( + tuple([1] * spec.length) + if mask == 0 + else _component_sizes_for_mask(spec.length, edges, mask) + ), + ) + return + kernel = periodic_kernel(spec.length, spec.sigma) + log_rates = math.log(spec.kappa) + np.log(kernel) + for mask in range(1 << len(edges)): + union_find = UnionFind(spec.length) + log_probability = 0.0 + open_count = 0 + for index, (left, right) in enumerate(edges): + separation = right - left + distance = min(separation, spec.length - separation) + log_rate = float(log_rates[distance - 1]) + if mask & (1 << index): + log_probability += _log_open_edge_weight(log_rate) + open_count += 1 + union_find.union(left, right) + else: + log_probability += _log_closed_edge_weight(log_rate) + yield GraphOutcome( + mask=mask, + probability=_probability_from_log(log_probability), + open_edges=open_count, + component_sizes=tuple(union_find.component_sizes().tolist()), + ) + + +def exact_partition_distribution( + spec: ModelSpec, +) -> dict[tuple[int, ...], float]: + result: dict[tuple[int, ...], float] = {} + for outcome in enumerate_graphs(spec): + result[outcome.component_sizes] = ( + result.get(outcome.component_sizes, 0.0) + outcome.probability + ) + return result diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/geometric.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/geometric.py new file mode 100644 index 000000000..ca91e8271 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/geometric.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import math +from typing import Iterator + +import numpy as np + +from .kernel import periodic_kernel +from .model import ModelSpec, canonical_edge, distance_classes +from .sample import GraphSample +from .union_find import UnionFind + + +def _iter_open_offsets( + multiplicity: int, + rate: float, + rng: np.random.Generator, +) -> Iterator[int]: + if multiplicity < 1 or rate == 0.0: + return + if not math.isfinite(rate) or math.exp(-rate) == 0.0: + yield from range(multiplicity) + return + + offset = 0 + while offset < multiplicity: + remaining = multiplicity - offset + exponential = -math.log1p(-float(rng.random())) + if exponential >= rate * remaining: + break + skipped = int(exponential / rate) + offset += skipped + yield offset + offset += 1 + + +def sample_geometric( + spec: ModelSpec, + rng: np.random.Generator, +) -> GraphSample: + if not isinstance(rng, np.random.Generator): + raise ValueError("rng must be numpy.random.Generator") + if spec.kappa == 0.0: + return GraphSample( + spec.length, + np.empty((0, 2), dtype=np.int64), + np.arange(spec.length, dtype=np.int64), + ) + + with np.errstate(over="ignore", under="ignore"): + rates = spec.kappa * periodic_kernel(spec.length, spec.sigma) + + union_find = UnionFind(spec.length) + edges: list[tuple[int, int]] = [] + for item in distance_classes(spec.length): + rate = float(rates[item.distance - 1]) + for offset in _iter_open_offsets(item.multiplicity, rate, rng): + edge = canonical_edge(spec.length, item.distance, offset) + edges.append(edge) + union_find.union(*edge) + + edge_array = np.asarray(sorted(edges), dtype=np.int64).reshape(-1, 2) + return GraphSample(spec.length, edge_array, union_find.labels()) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/kernel.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/kernel.py new file mode 100644 index 000000000..f02f6e367 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/kernel.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +import math + +import numpy as np +from scipy.special import zeta + +from .model import ModelSpec + + +def _negative_power(values: np.ndarray, exponent: float) -> np.ndarray: + logarithms = -exponent * np.log(values) + representable = logarithms >= math.log( + np.nextafter(0.0, 1.0) + ) + result = np.zeros_like(values) + with np.errstate(under="ignore"): + result[representable] = np.power( + values[representable], -exponent + ) + return result + + +def _negative_power_scalar(value: float, exponent: float) -> float: + logarithm = -exponent * math.log(value) + if logarithm < math.log(np.nextafter(0.0, 1.0)): + return 0.0 + return math.pow(value, -exponent) + + +def periodic_kernel(length: int, sigma: float) -> np.ndarray: + ModelSpec(length=length, sigma=sigma, kappa=0.0) + distances = np.arange(1, length // 2 + 1, dtype=np.float64) + if float(sigma) == 1.0: + angles = np.pi * distances / length + values = (np.pi / length) ** 2 / np.sin(angles) ** 2 + else: + exponent = 1.0 + float(sigma) + fraction = distances / length + nearest = _negative_power(distances, exponent) + mirrored = _negative_power(length - distances, exponent) + scale = _negative_power_scalar(float(length), exponent) + tail = scale * ( + zeta(exponent, 1.0 + fraction) + + zeta(exponent, 2.0 - fraction) + ) + values = nearest + mirrored + tail + if not np.all(np.isfinite(values)): + raise ValueError("periodic kernel produced a nonfinite entry") + if np.any(values <= 0.0): + raise ValueError( + "periodic kernel has a positive entry below float64 " + "representability" + ) + return values + + +def periodic_kernel_reference( + length: int, + sigma: float, + images: int, +) -> tuple[np.ndarray, np.ndarray]: + ModelSpec(length=length, sigma=sigma, kappa=0.0) + if isinstance(images, bool) or not isinstance(images, int) or images < 1: + raise ValueError("images must be a positive integer") + exponent = 1.0 + float(sigma) + distances = np.arange(1, length // 2 + 1, dtype=np.float64) + partial = np.zeros_like(distances) + for image in range(-images, images + 1): + displacement = np.abs(distances + image * length) + partial += displacement ** (-exponent) + half_index = images + 0.5 + tail = np.full_like( + distances, + 2.0 * length ** (-exponent) * ( + half_index ** (-exponent) + + half_index ** (1.0 - exponent) / (exponent - 1.0) + ), + ) + return partial, tail + + +def kernel_weight_sum(length: int, sigma: float) -> float: + ModelSpec(length=length, sigma=sigma, kappa=0.0) + exponent = 1.0 + float(sigma) + finite_size_correction = _negative_power_scalar( + float(length), exponent + ) + total = float( + length + * zeta(exponent, 1.0) + * (1.0 - finite_size_correction) + ) + if not math.isfinite(total) or total <= 0.0: + raise ValueError("kernel weight sum must be finite and positive") + return total + + +def edge_probabilities(spec: ModelSpec, kernel: np.ndarray) -> np.ndarray: + values = np.asarray(kernel, dtype=np.float64) + if values.shape != (spec.length // 2,): + raise ValueError("kernel shape does not match model length") + if not np.all(np.isfinite(values)) or np.any(values <= 0.0): + raise ValueError("kernel must contain finite positive values") + return -np.expm1(-spec.kappa * values) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/model.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/model.py new file mode 100644 index 000000000..a126c53db --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/model.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from dataclasses import dataclass +import math +from typing import Iterator + + +def _strict_int(value: object, name: str) -> int: + if isinstance(value, bool) or not isinstance(value, int): + raise ValueError(f"{name} must be an integer") + return value + + +@dataclass(frozen=True) +class ModelSpec: + length: int + sigma: float + kappa: float + + def __post_init__(self) -> None: + length = _strict_int(self.length, "length") + if length < 2 or length % 2: + raise ValueError("length must be even and at least two") + sigma = float(self.sigma) + exponent = 1.0 + sigma + if ( + isinstance(self.sigma, bool) + or not isinstance(self.sigma, (int, float)) + or not math.isfinite(sigma) + or sigma <= 0.0 + or not math.isfinite(exponent) + or exponent <= 1.0 + ): + raise ValueError( + "sigma must be finite, positive, and satisfy 1.0 + sigma > 1.0" + ) + if ( + isinstance(self.kappa, bool) + or not isinstance(self.kappa, (int, float)) + or not math.isfinite(float(self.kappa)) + or float(self.kappa) < 0.0 + ): + raise ValueError("kappa must be finite and nonnegative") + + +@dataclass(frozen=True) +class DistanceClass: + distance: int + multiplicity: int + + +def distance_classes(length: int) -> tuple[DistanceClass, ...]: + length = _strict_int(length, "length") + if length < 2 or length % 2: + raise ValueError("length must be even and at least two") + return tuple( + DistanceClass( + distance=distance, + multiplicity=length if distance < length // 2 else length // 2, + ) + for distance in range(1, length // 2 + 1) + ) + + +def canonical_edge(length: int, distance: int, offset: int) -> tuple[int, int]: + matching = {item.distance: item for item in distance_classes(length)} + if distance not in matching: + raise ValueError("distance is outside the canonical range") + if isinstance(offset, bool) or not isinstance(offset, int): + raise ValueError("offset must be an integer") + if not 0 <= offset < matching[distance].multiplicity: + raise ValueError("offset is outside the distance class") + left = offset + right = (offset + distance) % length + return (left, right) if left < right else (right, left) + + +def iter_unordered_edges(length: int) -> Iterator[tuple[int, int]]: + distance_classes(length) + for left in range(length): + for right in range(left + 1, length): + yield left, right diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/observables.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/observables.py new file mode 100644 index 000000000..3065bf531 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/observables.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass(frozen=True) +class BasicObservables: + open_edges: int + component_count: int + largest_size: int + second_largest_size: int + s1_fraction: float + s2_fraction: float + sum_size_sq: float + sum_size_fourth: float + q_g: float + four_sector_crossing: bool diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/oracle.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/oracle.py new file mode 100644 index 000000000..65a3cc92a --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/oracle.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import math + +import numpy as np + +from .kernel import edge_probabilities, kernel_weight_sum, periodic_kernel +from .model import ModelSpec +from .sample import GraphSample +from .union_find import UnionFind + + +def _distance(left: int, right: int, length: int) -> int: + separation = right - left + return min(separation, length - separation) + + +def sample_quadratic( + spec: ModelSpec, + rng: np.random.Generator, +) -> GraphSample: + if not isinstance(rng, np.random.Generator): + raise ValueError("rng must be numpy.random.Generator") + probabilities = edge_probabilities( + spec, + periodic_kernel(spec.length, spec.sigma), + ) + union_find = UnionFind(spec.length) + edges = [] + for left in range(spec.length): + for right in range(left + 1, spec.length): + probability = probabilities[_distance(left, right, spec.length) - 1] + if rng.random() < probability: + edges.append((left, right)) + union_find.union(left, right) + edge_array = np.asarray(edges, dtype=np.int64).reshape(-1, 2) + return GraphSample(spec.length, edge_array, union_find.labels()) + + +def _class_probabilities(spec: ModelSpec) -> tuple[np.ndarray, np.ndarray]: + from .model import distance_classes + + multiplicity = np.array( + [item.multiplicity for item in distance_classes(spec.length)], + dtype=np.float64, + ) + probability = edge_probabilities( + spec, + periodic_kernel(spec.length, spec.sigma), + ) + return multiplicity, probability + + +def expected_open_edges(spec: ModelSpec) -> float: + multiplicity, probability = _class_probabilities(spec) + return float(multiplicity @ probability) + + +def variance_open_edges(spec: ModelSpec) -> float: + multiplicity, probability = _class_probabilities(spec) + return float(multiplicity @ (probability * (1.0 - probability))) + + +def no_edge_probability(spec: ModelSpec) -> float: + return math.exp(-spec.kappa * kernel_weight_sum(spec.length, spec.sigma)) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py new file mode 100644 index 000000000..6fd3156db --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot.py @@ -0,0 +1,3511 @@ +from __future__ import annotations + +import fcntl +import hashlib +import json +import os +import re +import signal +import stat +import subprocess +import tempfile +import threading +import uuid +from collections import Counter +from collections.abc import Callable, Iterator, Mapping, Sequence, Set +from contextlib import contextmanager +from dataclasses import dataclass +from datetime import datetime, timezone +from pathlib import Path +from typing import Never + +import numpy as np + +from . import artifacts as _artifacts +from .alias import build_distance_alias +from .artifacts import ( + CONVERSION_VERSION, + load_verified_trajectory, + publish_batch_manifest, + publish_trajectory, + reconstruct_progress, +) +from .counter_rng import ( + RNG_VERSION, + STREAM_COUNT, + StreamIdentity, + derive_stream_material, +) +from .kernel import periodic_kernel +from .poisson_sweep import run_poisson_numba +from .runtime import runtime_capability +from .trajectory import TrajectoryRequest, TrajectoryResult, request_digest +from .validation import ( + ValidationProtocol, + _protocol_document, + _repository_state, + validate_report_payload, +) +from .validation_shards import validate_run_spec as validate_validation_run_spec + +RUN_SPEC_SCHEMA = "challenge-194-pilot-run-spec-v1" +TEST_RUN_SPEC_SCHEMA = "challenge-194-pilot-test-run-spec-v1" +TEST_EXTENSION_RUN_SPEC_SCHEMA = "challenge-194-p0-extension-test-run-spec-v1" +CELL_MANIFEST_SCHEMA = "challenge-194-pilot-cell-manifest-v1" +MERGED_SCHEMA = "challenge-194-pilot-progress-v1" +APPROVAL_SCHEMA = "challenge-194-pilot-correctness-approval-v1" +CORRECTNESS_APPROVAL_REVISION = "877ab9393f320bfe31ff74a26c3db1fb205d7ef3" +APPROVAL_REGISTRY_SHA256 = ( + "29dc5d04fd18728ee46fffe90c70d98caa61032005974f354e2b4e0e6018a7ab" +) +PILOT_SIGMAS = (0.8, 0.9, 1.0, 1.1) +PILOT_LENGTHS = (2**10, 2**14, 2**18) +PILOT_REPLICAS = tuple(range(8)) +PILOT_KAPPAS = (0.0,) + tuple(0.25 * 1.25**j for j in range(15)) +PILOT_MASTER_SEED = 19_420_260_729 +PILOT_PHASE = "pilot" +RUN_SPEC_NAME = "run_spec.json" +MERGED_NAME = "progress.json" +_HEX40 = re.compile(r"[0-9a-f]{40}") +_HEX64 = re.compile(r"[0-9a-f]{64}") +APPROVAL_MAX_BYTES = 16 * 1024 +PILOT_RUN_SPEC_MAX_BYTES = 1024 * 1024 +PILOT_MARKER_MAX_BYTES = 16 * 1024 +PILOT_PROGRESS_MAX_BYTES = 256 * 1024 +CORRECTNESS_RUN_SPEC_MAX_BYTES = 128 * 1024 * 1024 +CORRECTNESS_REPORT_MAX_BYTES = 256 * 1024 * 1024 +PILOT_JSON_MAX_DEPTH = 32 +PILOT_JSON_MAX_STRING = 4096 +PILOT_JSON_MAX_CONTAINER = 100_000 +PILOT_JSON_MAX_NODES = 20_000_000 +CORRECTNESS_JSON_MAX_NODES = 64_000_000 +PILOT_CELL_MAX_ENTRIES = 64 +PILOT_SNAPSHOT_MAX_BYTES = 128 * 1024 * 1024 +PILOT_SNAPSHOT_SAFETY_RESERVE_BYTES = 64 * 1024 * 1024 +PILOT_SNAPSHOT_MAX_ENTRIES = ( + 2 + + 1 + + len(PILOT_SIGMAS) + * len(PILOT_LENGTHS) + * len(PILOT_REPLICAS) + * (1 + PILOT_CELL_MAX_ENTRIES) +) +PILOT_SNAPSHOT_STALE_SCAN_MAX = 256 +PILOT_SNAPSHOT_PREFIX = "challenge-194-p0-snapshot-" +PILOT_SNAPSHOT_MARKER = ".challenge-194-owner.json" + +_read_descriptor_bounded = _artifacts._read_descriptor_bounded +_generation_tuple = _artifacts._generation_tuple +_hash_descriptor = _artifacts._hash_descriptor + +# This is intentionally narrower than validation's implementation inventory. +# Drift in any module that defines the model, RNG, trajectory, or production +# engine invalidates the correctness evidence; orchestration files may differ. +SCIENTIFIC_ENGINE_MODULES = ( + "src/long_range_percolation/model.py", + "src/long_range_percolation/kernel.py", + "src/long_range_percolation/counter_rng.py", + "src/long_range_percolation/alias.py", + "src/long_range_percolation/edge_set.py", + "src/long_range_percolation/observables.py", + "src/long_range_percolation/production_union_find.py", + "src/long_range_percolation/trajectory.py", + "src/long_range_percolation/poisson_reference.py", + "src/long_range_percolation/poisson_sweep.py", +) + + +@dataclass(frozen=True) +class PilotRunContract: + run_spec_schema: str + progress_schema: str + master_seed: int + phase: str + production_kind: str + + +P0_CONTRACT = PilotRunContract( + RUN_SPEC_SCHEMA, + MERGED_SCHEMA, + PILOT_MASTER_SEED, + PILOT_PHASE, + "p0", +) +TEST_P0_CONTRACT = PilotRunContract( + TEST_RUN_SPEC_SCHEMA, + MERGED_SCHEMA, + PILOT_MASTER_SEED, + PILOT_PHASE, + "test-p0", +) +EXTENSION_CONTRACT = PilotRunContract( + "challenge-194-p0-extension-run-spec-v1", + "challenge-194-p0-extension-progress-v1", + 19_420_262_729, + "pilot", + "p0-extension-v1", +) +TEST_EXTENSION_CONTRACT = PilotRunContract( + TEST_EXTENSION_RUN_SPEC_SCHEMA, + "challenge-194-p0-extension-progress-v1", + 19_420_262_729, + "pilot", + "test-p0-extension-v1", +) + + +def _contract_for_schema(schema: object) -> PilotRunContract: + if schema == RUN_SPEC_SCHEMA: + return P0_CONTRACT + if schema == TEST_RUN_SPEC_SCHEMA: + return TEST_P0_CONTRACT + from .pilot_extension import ( + EXTENSION_MASTER_SEED, + EXTENSION_PHASE, + EXTENSION_PROGRESS_SCHEMA, + EXTENSION_RUN_SPEC_SCHEMA, + ) + + extension_identity = ( + EXTENSION_RUN_SPEC_SCHEMA, + EXTENSION_PROGRESS_SCHEMA, + EXTENSION_MASTER_SEED, + EXTENSION_PHASE, + ) + if extension_identity != ( + EXTENSION_CONTRACT.run_spec_schema, + EXTENSION_CONTRACT.progress_schema, + EXTENSION_CONTRACT.master_seed, + EXTENSION_CONTRACT.phase, + ): + raise RuntimeError("registered P0 extension contract constants drifted") + if schema == EXTENSION_RUN_SPEC_SCHEMA: + return EXTENSION_CONTRACT + if schema == TEST_EXTENSION_RUN_SPEC_SCHEMA: + return TEST_EXTENSION_CONTRACT + raise RuntimeError("registered Pilot run-spec schema is not supported") + + +def _is_production_contract(contract: PilotRunContract) -> bool: + return contract.production_kind in {"p0", "p0-extension-v1"} + + +def _canonical_bytes(document: object) -> bytes: + try: + return ( + json.dumps( + document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + + b"\n" + ) + except (TypeError, ValueError) as error: + raise RuntimeError("document is not canonical finite JSON") from error + + +def _sha256(payload: bytes) -> str: + return hashlib.sha256(payload).hexdigest() + + +def _document_hash(document: Mapping[str, object], field: str) -> str: + unsigned = dict(document) + unsigned.pop(field, None) + return _sha256(_canonical_bytes(unsigned)) + + +def _solution_root() -> Path: + return Path(__file__).resolve().parents[2] + + +def _repo_root() -> Path: + return _solution_root().parents[4] + + +def _validate_json_bounds( + value: object, *, maximum_nodes: int = PILOT_JSON_MAX_NODES +) -> None: + stack: list[tuple[object, int]] = [(value, 1)] + nodes = 0 + while stack: + item, depth = stack.pop() + nodes += 1 + if nodes > maximum_nodes: + raise RuntimeError("JSON node count exceeds the frozen limit") + if depth > PILOT_JSON_MAX_DEPTH: + raise RuntimeError("JSON depth exceeds the frozen limit") + if isinstance(item, str): + if len(item) > PILOT_JSON_MAX_STRING: + raise RuntimeError("JSON string exceeds the frozen limit") + elif isinstance(item, dict): + if len(item) > PILOT_JSON_MAX_CONTAINER: + raise RuntimeError("JSON mapping exceeds the frozen limit") + for key, child in item.items(): + if not isinstance(key, str) or len(key) > PILOT_JSON_MAX_STRING: + raise RuntimeError("JSON key exceeds the frozen limit") + stack.append((child, depth + 1)) + elif isinstance(item, list): + if len(item) > PILOT_JSON_MAX_CONTAINER: + raise RuntimeError("JSON sequence exceeds the frozen limit") + stack.extend((child, depth + 1) for child in item) + + +DirectoryEntry = tuple[Path, int, os.stat_result] + + +def _directory_identity(metadata: object) -> tuple[int, int, int, int, int]: + return ( + int(metadata.st_dev), + int(metadata.st_ino), + int(metadata.st_mode), + int(metadata.st_uid), + int(metadata.st_gid), + ) + + +def _directory_flags() -> int: + return os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0) + + +def _open_directory_at(name: str, parent_fd: int) -> int: + return os.open(name, _directory_flags(), dir_fd=parent_fd) + + +def _snapshot_existing_directories(path: Path) -> dict[Path, os.stat_result]: + absolute = path.absolute() + current = Path(absolute.anchor) + snapshots: dict[Path, os.stat_result] = {} + for component in absolute.parts: + if component == absolute.anchor: + candidate = current + else: + current = current / component + candidate = current + try: + metadata = candidate.lstat() + except FileNotFoundError: + break + except OSError as error: + raise RuntimeError("unable to inspect directory ancestry") from error + if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode): + raise RuntimeError("directory ancestry must contain only directories") + snapshots[candidate] = metadata + return snapshots + + +def _open_directory_chain( + path: Path, *, create: bool, allow_final_mutation: bool = False +) -> list[DirectoryEntry]: + absolute = path.absolute() + snapshots = _snapshot_existing_directories(absolute) + entries: list[DirectoryEntry] = [] + current_path = Path(absolute.anchor) + try: + descriptor = os.open(absolute.anchor, _directory_flags()) + root_status = os.fstat(descriptor) + entries.append((current_path, descriptor, root_status)) + for component in absolute.parts[1:]: + parent_fd = entries[-1][1] + current_path = current_path / component + try: + descriptor = _open_directory_at(component, parent_fd) + except FileNotFoundError: + if not create: + raise RuntimeError("directory ancestry is missing") + try: + os.mkdir(component, 0o755, dir_fd=parent_fd) + except FileExistsError: + pass + descriptor = _open_directory_at(component, parent_fd) + status = os.fstat(descriptor) + if not stat.S_ISDIR(status.st_mode): + os.close(descriptor) + raise RuntimeError("directory ancestry contains a non-directory") + original = snapshots.get(current_path) + if original is not None: + if ( + _directory_identity(status) != _directory_identity(original) + or status.st_nlink < 2 + ): + os.close(descriptor) + raise RuntimeError( + "directory ancestor identity changed before descriptor open" + ) + entries.append((current_path, descriptor, status)) + if create: + entries = [ + (entry_path, entry_fd, os.fstat(entry_fd)) + for entry_path, entry_fd, _ in entries + ] + _require_directory_chain(entries, allow_final_mutation=allow_final_mutation) + return entries + except BaseException: + _close_directory_chain(entries) + raise + + +def _close_directory_chain(entries: Sequence[DirectoryEntry]) -> None: + for _, descriptor, _ in reversed(entries): + os.close(descriptor) + + +def _open_cell_directory_chain(root: Path, cell_id: str) -> list[DirectoryEntry]: + entries = _open_directory_chain(root, create=False, allow_final_mutation=True) + root_fd = entries[-1][1] + cell_locked = False + try: + fcntl.flock(root_fd, fcntl.LOCK_EX) + try: + _require_directory_chain(entries, allow_final_mutation=True) + parent_path = root + parent_fd = root_fd + for name in ("cells", cell_id): + child_path = parent_path / name + try: + child_fd = _open_directory_at(name, parent_fd) + except FileNotFoundError: + try: + os.mkdir(name, 0o755, dir_fd=parent_fd) + except FileExistsError: + pass + child_fd = _open_directory_at(name, parent_fd) + child_status = os.fstat(child_fd) + entries.append((child_path, child_fd, child_status)) + parent_path = child_path + parent_fd = child_fd + fcntl.flock(parent_fd, fcntl.LOCK_EX) + cell_locked = True + entries = [ + (entry_path, entry_fd, os.fstat(entry_fd)) + for entry_path, entry_fd, _ in entries + ] + _require_directory_chain(entries, allow_final_mutation=False) + return entries + finally: + fcntl.flock(root_fd, fcntl.LOCK_UN) + except BaseException: + if cell_locked: + fcntl.flock(entries[-1][1], fcntl.LOCK_UN) + _close_directory_chain(entries) + raise + + +def _require_directory_chain( + entries: Sequence[DirectoryEntry], + *, + allow_final_mutation: bool, + mutable_indexes: Set[int] = frozenset(), +) -> None: + del allow_final_mutation, mutable_indexes + for path, descriptor, original in entries: + try: + path_status = path.lstat() + descriptor_status = os.fstat(descriptor) + except OSError as error: + raise RuntimeError("directory ancestor identity changed") from error + if ( + stat.S_ISLNK(path_status.st_mode) + or not stat.S_ISDIR(path_status.st_mode) + or _directory_identity(path_status) + != _directory_identity(descriptor_status) + or _directory_identity(descriptor_status) != _directory_identity(original) + or descriptor_status.st_nlink < 2 + ): + raise RuntimeError("directory identity changed") + + +def _directory_generation(path: Path, descriptor: int) -> tuple[int, ...]: + try: + path_status = path.lstat() + descriptor_status = os.fstat(descriptor) + except OSError as error: + raise RuntimeError("pilot cell directory identity changed") from error + if _generation_tuple(path_status) != _generation_tuple(descriptor_status): + raise RuntimeError("pilot cell directory identity changed") + return _generation_tuple(descriptor_status) + + +def _require_directory_generation( + path: Path, descriptor: int, expected: tuple[int, ...] +) -> None: + if _directory_generation(path, descriptor) != expected: + raise RuntimeError("pilot cell directory generation changed during work") + + +def _open_regular_at( + name: str, + parent_fd: int, + description: str, + *, + maximum_size: int | None = None, +) -> tuple[int, os.stat_result]: + flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_NONBLOCK", 0) + try: + before = os.stat(name, dir_fd=parent_fd, follow_symlinks=False) + if stat.S_ISLNK(before.st_mode): + raise RuntimeError(f"{description} must not be a symlink") + descriptor = os.open(name, flags, dir_fd=parent_fd) + opened = os.fstat(descriptor) + except RuntimeError: + raise + except OSError as error: + raise RuntimeError(f"unable to open {description}") from error + if ( + stat.S_ISLNK(before.st_mode) + or not stat.S_ISREG(opened.st_mode) + or _generation_tuple(before) != _generation_tuple(opened) + ): + os.close(descriptor) + raise RuntimeError(f"{description} identity changed before descriptor open") + if maximum_size is not None and opened.st_size > maximum_size: + os.close(descriptor) + raise RuntimeError(f"{description} exceeds the byte-size limit") + return descriptor, opened + + +def _require_regular_at_identity( + name: str, + parent_fd: int, + descriptor: int, + original: os.stat_result, + description: str, +) -> None: + try: + path_status = os.stat(name, dir_fd=parent_fd, follow_symlinks=False) + descriptor_status = os.fstat(descriptor) + except OSError as error: + raise RuntimeError(f"{description} pathname identity changed") from error + if ( + not stat.S_ISREG(path_status.st_mode) + or _generation_tuple(path_status) != _generation_tuple(descriptor_status) + or _generation_tuple(descriptor_status) != _generation_tuple(original) + ): + raise RuntimeError(f"{description} generation or identity changed") + + +def _read_canonical( + path: Path, + description: str, + *, + maximum_size: int, + maximum_nodes: int = PILOT_JSON_MAX_NODES, + allow_parent_mutation: bool = False, +) -> tuple[dict[str, object], bytes]: + parent_chain = _open_directory_chain( + path.parent, + create=False, + allow_final_mutation=allow_parent_mutation, + ) + parent_fd = parent_chain[-1][1] + descriptor, original = _open_regular_at( + path.name, parent_fd, description, maximum_size=maximum_size + ) + try: + payload = _read_descriptor_bounded(descriptor, maximum_size, description) + _require_regular_at_identity( + path.name, parent_fd, descriptor, original, description + ) + try: + document = json.loads(payload) + except ( + UnicodeError, + json.JSONDecodeError, + RecursionError, + MemoryError, + OverflowError, + ValueError, + ) as error: + raise RuntimeError(f"{description} is not valid JSON") from error + _validate_json_bounds(document, maximum_nodes=maximum_nodes) + if not isinstance(document, dict) or payload != _canonical_bytes(document): + raise RuntimeError(f"{description} is not canonical JSON") + _require_regular_at_identity( + path.name, parent_fd, descriptor, original, description + ) + _require_directory_chain( + parent_chain, + allow_final_mutation=allow_parent_mutation, + ) + return document, payload + finally: + os.close(descriptor) + _close_directory_chain(parent_chain) + + +def _link_at( + source: str, destination: str, source_fd: int, destination_fd: int +) -> None: + os.link( + source, + destination, + src_dir_fd=source_fd, + dst_dir_fd=destination_fd, + follow_symlinks=False, + ) + + +def _publish_once(path: Path, document: Mapping[str, object]) -> None: + payload = _canonical_bytes(document) + parent_chain = _open_directory_chain(path.parent, create=True) + parent_fd = parent_chain[-1][1] + temporary = f".{path.name}.{os.getpid()}.{uuid.uuid4().hex}.tmp" + descriptor = os.open( + temporary, + os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_NOFOLLOW", 0), + 0o644, + dir_fd=parent_fd, + ) + try: + with os.fdopen(descriptor, "wb") as stream: + stream.write(payload) + stream.flush() + os.fsync(stream.fileno()) + try: + _link_at(temporary, path.name, parent_fd, parent_fd) + except FileExistsError: + existing, existing_payload = _read_canonical( + path, + "immutable output", + maximum_size=max(len(payload), PILOT_MARKER_MAX_BYTES), + ) + if existing_payload != payload or existing != dict(document): + raise RuntimeError("immutable output already exists with other bytes") + os.unlink(temporary, dir_fd=parent_fd) + temporary = "" + os.fsync(parent_fd) + _require_directory_chain(parent_chain, allow_final_mutation=True) + finally: + if temporary: + try: + os.unlink(temporary, dir_fd=parent_fd) + except FileNotFoundError: + pass + _close_directory_chain(parent_chain) + + +def _file_hash( + path: Path, + *, + maximum_size: int | None = None, + description: str | None = None, +) -> str: + source_description = ( + f"required source file {path}" if description is None else description + ) + parent_chain = _open_directory_chain(path.parent, create=False) + parent_fd = parent_chain[-1][1] + descriptor, original = _open_regular_at( + path.name, + parent_fd, + source_description, + maximum_size=maximum_size, + ) + try: + if maximum_size is None: + digest, size = _hash_descriptor(descriptor, source_description) + else: + payload = _read_descriptor_bounded( + descriptor, + maximum_size, + source_description, + ) + digest, size = _sha256(payload), len(payload) + if size != original.st_size: + raise RuntimeError(f"required source file size changed: {path}") + _require_regular_at_identity( + path.name, + parent_fd, + descriptor, + original, + source_description, + ) + _require_directory_chain(parent_chain, allow_final_mutation=False) + return digest + finally: + os.close(descriptor) + _close_directory_chain(parent_chain) + + +def _lock_hash() -> str: + return _file_hash(_solution_root() / "uv.lock") + + +def _scientific_hashes() -> dict[str, str]: + root = _solution_root() + return { + relative: _file_hash(root / relative) for relative in SCIENTIFIC_ENGINE_MODULES + } + + +def _aggregate_hash(values: Mapping[str, str]) -> str: + return _sha256(_canonical_bytes(dict(values))) + + +def _current_source(*, require_clean: bool) -> dict[str, object]: + source = _repository_state() + revision = source.get("source_revision") + if not isinstance(revision, str) or _HEX40.fullmatch(revision) is None: + raise RuntimeError("current orchestration revision is unavailable") + if require_clean and ( + source.get("clean_tree") is not True + or source.get("provenance_error") is not None + ): + raise RuntimeError("current repository must be clean") + return source + + +def _runtime_document() -> tuple[dict[str, object], str]: + document = runtime_capability() + return document, _sha256(_canonical_bytes(document)) + + +def _analysis_plan_hash() -> str: + path = _solution_root() / "PILOT_PLAN.md" + return _file_hash(path) + + +def _approval_registry_path() -> Path: + return _solution_root() / "pilot_correctness_approval.json" + + +def _check_registry_document( + validation_spec: Mapping[str, object], +) -> dict[str, object]: + cells = validation_spec.get("cells") + global_checks = validation_spec.get("global_expected_checks") + if ( + not isinstance(cells, list) + or len(cells) != 120 + or not isinstance(global_checks, list) + ): + raise RuntimeError("correctness check registry has invalid cardinality") + registry_cells: list[dict[str, object]] = [] + for index, cell in enumerate(cells): + if not isinstance(cell, Mapping): + raise RuntimeError("correctness cell registry is malformed") + checks = cell.get("expected_checks") + if not isinstance(checks, list) or len(checks) > 100_000: + raise RuntimeError("correctness cell check registry is unbounded") + registry_cells.append( + { + "case_index": cell.get("case_index", index), + "case_id": cell.get("case_id"), + "expected_checks": checks, + } + ) + return {"global": global_checks, "cells": registry_cells} + + +def _load_approval_registry() -> dict[str, object]: + document, payload = _read_canonical( + _approval_registry_path(), + "Pilot correctness approval registry", + maximum_size=APPROVAL_MAX_BYTES, + ) + if _sha256(payload) != APPROVAL_REGISTRY_SHA256: + raise RuntimeError( + "Pilot correctness approval registry does not match pinned SHA256" + ) + expected = { + "schema_version", + "approval_revision", + "validation_source_revision", + "report_sha256", + "run_spec_sha256", + "protocol_sha256", + "check_registry_sha256", + "check_count", + "cell_count", + "scientific_engine_sha256", + } + if set(document) != expected or document.get("schema_version") != APPROVAL_SCHEMA: + raise RuntimeError("Pilot correctness approval registry is not exact") + for field in ( + "report_sha256", + "run_spec_sha256", + "protocol_sha256", + "check_registry_sha256", + "scientific_engine_sha256", + ): + if ( + not isinstance(document.get(field), str) + or _HEX64.fullmatch(str(document[field])) is None + ): + raise RuntimeError(f"approval registry {field} is malformed") + if ( + document.get("approval_revision") != CORRECTNESS_APPROVAL_REVISION + or not isinstance(document.get("validation_source_revision"), str) + or _HEX40.fullmatch(str(document["validation_source_revision"])) is None + or document.get("cell_count") != 120 + or document.get("check_count") != 22_755 + ): + raise RuntimeError("Pilot correctness approval identity is invalid") + return document + + +def _approval_registry_digest() -> str: + _load_approval_registry() + return APPROVAL_REGISTRY_SHA256 + + +def _validation_spec_path(report: Path) -> Path: + candidate = report.parent.parent / RUN_SPEC_NAME + if candidate.is_file() and not candidate.is_symlink(): + return candidate + raise RuntimeError("approved correctness report lacks adjacent immutable run spec") + + +def _verified_correctness(report_path: Path) -> dict[str, object]: + approval = _load_approval_registry() + report, report_payload = _read_canonical( + report_path, + "correctness report", + maximum_size=CORRECTNESS_REPORT_MAX_BYTES, + maximum_nodes=CORRECTNESS_JSON_MAX_NODES, + ) + if _sha256(report_payload) != approval["report_sha256"]: + raise RuntimeError("approved correctness report SHA256 mismatch") + protocol = ValidationProtocol.production_v1() + validate_report_payload(report, protocol) + if report.get("passed") is not True: + raise RuntimeError("correctness report did not pass") + source = report.get("source") + validation_source_revision = ( + source.get("source_revision") if isinstance(source, Mapping) else None + ) + if ( + not isinstance(source, Mapping) + or not isinstance(validation_source_revision, str) + or _HEX40.fullmatch(validation_source_revision) is None + or source.get("clean_tree") is not True + or source.get("provenance_error") is not None + ): + raise RuntimeError("correctness report source evidence is not approved") + + validation_spec_path = _validation_spec_path(report_path) + validation_spec, validation_spec_payload = _read_canonical( + validation_spec_path, + "correctness run spec", + maximum_size=CORRECTNESS_RUN_SPEC_MAX_BYTES, + maximum_nodes=CORRECTNESS_JSON_MAX_NODES, + ) + if _sha256(validation_spec_payload) != approval["run_spec_sha256"]: + raise RuntimeError("approved correctness run spec SHA256 mismatch") + validate_validation_run_spec(validation_spec, enforce_production=True) + cells = validation_spec.get("cells") + if not isinstance(cells, list) or len(cells) != 120: + raise RuntimeError("correctness run spec must contain exactly 120 cells") + if ( + validation_source_revision != approval["validation_source_revision"] + or validation_spec.get("source_revision") != validation_source_revision + or validation_spec.get("uv_lock_sha256") != _lock_hash() + or validation_spec.get("runtime_capability") != report.get("runtime_capability") + ): + raise RuntimeError("correctness source/runtime/lock evidence is inconsistent") + if ( + validation_spec.get("protocol", {}).get("sha256") != approval["protocol_sha256"] + or _sha256(_canonical_bytes(_check_registry_document(validation_spec))) + != approval["check_registry_sha256"] + or len(report.get("checks", [])) != approval["check_count"] + ): + raise RuntimeError("approved correctness protocol/check registry mismatch") + + expected_identities = Counter( + (str(check["family"]), str(check["check_case_id"])) + for check in validation_spec["global_expected_checks"] + ) + for cell in cells: + expected_identities.update( + (str(check["family"]), str(check["check_case_id"])) + for check in cell["expected_checks"] + ) + actual_identities = Counter( + (str(check.get("family")), str(check.get("case_id"))) + for check in report["checks"] + ) + if actual_identities != expected_identities: + raise RuntimeError( + "correctness report check registry is incomplete or reordered" + ) + + recorded_modules = validation_spec.get("implementation_modules") + if not isinstance(recorded_modules, Mapping): + raise RuntimeError("correctness run spec lacks implementation hashes") + current = _scientific_hashes() + approved = {path: recorded_modules.get(path) for path in SCIENTIFIC_ENGINE_MODULES} + if ( + approved != current + or _aggregate_hash(current) != approval["scientific_engine_sha256"] + ): + raise RuntimeError("scientific engine module drift from correctness report") + return { + "correctness_report_sha256": _sha256(report_payload), + "correctness_run_spec_sha256": _sha256(validation_spec_payload), + "correctness_approval_registry_sha256": _sha256(_canonical_bytes(approval)), + "validation_source_revision": validation_source_revision, + "validated_engine_modules": current, + "validated_engine_sha256": _aggregate_hash(current), + "validation_runtime_capability_sha256": validation_spec[ + "runtime_capability_sha256" + ], + } + + +@dataclass(frozen=True) +class PilotCell: + cell_index: int + cell_id: str + sigma: float + length: int + replica: int + sigma_grid_id: str + kappas: tuple[float, ...] + kernel_sha256: str + request_sha256: str + cell_path: str + run_path: str + manifest_path: str + rng_material_sha256: tuple[str, ...] + + @classmethod + def from_document(cls, document: Mapping[str, object]) -> PilotCell: + try: + return cls( + cell_index=int(document["cell_index"]), + cell_id=str(document["cell_id"]), + sigma=float.fromhex(str(document["sigma"])), + length=int(document["length"]), + replica=int(document["replica"]), + sigma_grid_id=str(document["sigma_grid_id"]), + kappas=tuple(float.fromhex(str(value)) for value in document["kappas"]), + kernel_sha256=str(document["kernel_sha256"]), + request_sha256=str(document["request_sha256"]), + cell_path=str(document["cell_path"]), + run_path=str(document["run_path"]), + manifest_path=str(document["manifest_path"]), + rng_material_sha256=tuple( + str(value) for value in document["rng_material_sha256"] + ), + ) + except (KeyError, TypeError, ValueError) as error: + raise RuntimeError("pilot cell is malformed") from error + + def request(self, *, master_seed: int, phase: str) -> TrajectoryRequest: + return TrajectoryRequest( + length=self.length, + sigma=self.sigma, + sigma_grid_id=self.sigma_grid_id, + kappas=np.asarray(self.kappas, dtype=np.float64), + master_seed=master_seed, + phase=phase, + replica=self.replica, + kernel_sha256=self.kernel_sha256, + ) + + +def _stream_hashes( + length: int, + sigma_grid_id: str, + replica: int, + *, + master_seed: int, + phase: str, +) -> tuple[str, ...]: + return tuple( + derive_stream_material( + StreamIdentity( + master_seed=master_seed, + phase=phase, + length=length, + sigma_grid_id=sigma_grid_id, + replica=replica, + stream_id=stream, + ) + ).material_sha256 + for stream in range(STREAM_COUNT) + ) + + +def _build_document( + *, + lengths: Sequence[int], + sigmas: Sequence[float], + replicas: Sequence[int], + kappas: Sequence[float], + source: Mapping[str, object], + runtime: Mapping[str, object], + runtime_sha256: str, + correctness: Mapping[str, object], + waiver_timestamp: str, + analysis_plan_sha256: str, + schema_version: str, + master_seed: int = PILOT_MASTER_SEED, + phase: str = PILOT_PHASE, + grid_namespace: str = "pilot-p0-v1", + purpose: str = "exploratory-window-selection-only", +) -> dict[str, object]: + protocol = { + "lengths": list(lengths), + "sigmas": [float(value).hex() for value in sigmas], + "replicas": list(replicas), + "kappas": [float(value).hex() for value in kappas], + "master_seed": master_seed, + "phase": phase, + "loop_order": ["sigma", "length", "replica"], + "purpose": purpose, + } + protocol["sha256"] = _sha256(_canonical_bytes(protocol)) + cells: list[dict[str, object]] = [] + all_assignments: list[dict[str, object]] = [] + for sigma in sigmas: + sigma_value = float(sigma) + grid_id = f"{grid_namespace}|sigma-f64={sigma_value.hex()}" + for length in lengths: + kernel = periodic_kernel(int(length), sigma_value) + kernel_sha256 = _sha256(kernel.astype(" dict[str, object]: + if not isinstance(output_root, Path) or not output_root.is_absolute(): + raise RuntimeError("output_root must be an absolute path") + source = _current_source(require_clean=True) + correctness = _verified_correctness(validation_report) + runtime, runtime_hash = _runtime_document() + timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") + document = _build_document( + lengths=PILOT_LENGTHS, + sigmas=PILOT_SIGMAS, + replicas=PILOT_REPLICAS, + kappas=PILOT_KAPPAS, + source=source, + runtime=runtime, + runtime_sha256=runtime_hash, + correctness=correctness, + waiver_timestamp=timestamp, + analysis_plan_sha256=_analysis_plan_hash(), + schema_version=RUN_SPEC_SCHEMA, + ) + _publish_once(output_root / RUN_SPEC_NAME, document) + return document + + +def build_p0_extension_run_spec( + output_root: Path, + validation_report: Path, + protocol: Mapping[str, object], + p0_analysis: Mapping[str, object], + p0_evidence_root: Path, +) -> dict[str, object]: + from .pilot_extension import ( + EXTENSION_RUN_SPEC_SCHEMA, + validate_p0_extension_protocol, + ) + + if ( + not isinstance(output_root, Path) + or not output_root.is_absolute() + or not isinstance(validation_report, Path) + or not validation_report.is_absolute() + or not isinstance(p0_evidence_root, Path) + or not p0_evidence_root.is_absolute() + ): + raise RuntimeError( + "extension output, validation, and evidence paths must be absolute" + ) + validate_p0_extension_protocol(p0_analysis, protocol, p0_evidence_root) + source = _current_source(require_clean=True) + correctness = _verified_correctness(validation_report) + runtime, runtime_sha256 = _runtime_document() + timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") + copied = json.loads(_canonical_bytes(protocol)) + cells = copied.pop("cells") + capability_waiver = { + "reason": "user-waived-after-correctness-gate", + "benchmark_status": "cancelled-without-capability-report", + "utc_timestamp": timestamp, + } + document: dict[str, object] = { + "schema_version": EXTENSION_RUN_SPEC_SCHEMA, + "artifact_root": ".", + "protocol": copied, + "cells": cells, + "cell_count": 96, + "source_extension_protocol_sha256": protocol["protocol_sha256"], + "source_p0_analysis_document_sha256": protocol[ + "source_p0_analysis_document_sha256" + ], + "design_sha256": protocol["design_sha256"], + "correctness_report_sha256": correctness["correctness_report_sha256"], + "correctness_run_spec_sha256": correctness["correctness_run_spec_sha256"], + "correctness_approval_registry_sha256": correctness[ + "correctness_approval_registry_sha256" + ], + "correctness_approval_revision": CORRECTNESS_APPROVAL_REVISION, + "validation_source_revision": correctness["validation_source_revision"], + "validated_engine_modules": dict(correctness["validated_engine_modules"]), + "validated_engine_sha256": correctness["validated_engine_sha256"], + "validation_runtime_capability_sha256": correctness[ + "validation_runtime_capability_sha256" + ], + "orchestration_revision": source["source_revision"], + "clean_tree": True, + "uv_lock_sha256": _lock_hash(), + "runtime_capability": runtime, + "runtime_capability_sha256": runtime_sha256, + "analysis_plan_sha256": _analysis_plan_hash(), + "rng_assignment_sha256": protocol["rng_assignment_sha256"], + "capability_waiver": capability_waiver, + "merged_progress_path": MERGED_NAME, + } + document["run_spec_sha256"] = _document_hash(document, "run_spec_sha256") + _validate_pilot_spec( + document, + contract=_contract_for_schema(EXTENSION_RUN_SPEC_SCHEMA), + ) + _publish_once(output_root / RUN_SPEC_NAME, document) + return document + + +def _relative_path(root: Path, value: object, prefix: str) -> Path: + if not isinstance(value, str): + raise RuntimeError("artifact path must be a string") + relative = Path(value) + if ( + relative.is_absolute() + or ".." in relative.parts + or not relative.parts + or relative.parts[0] != prefix + ): + raise RuntimeError("artifact path escapes its portable namespace") + candidate = root / relative + if candidate.resolve(strict=False) != candidate: + raise RuntimeError("artifact path contains a symlink or alias") + return candidate + + +def _validate_pilot_spec( + document: Mapping[str, object], + *, + contract: PilotRunContract, +) -> None: + p0_fields = { + "schema_version", + "artifact_root", + "protocol", + "cells", + "cell_count", + "correctness_report_sha256", + "correctness_run_spec_sha256", + "correctness_approval_registry_sha256", + "correctness_approval_revision", + "validation_source_revision", + "validated_engine_modules", + "validated_engine_sha256", + "validation_runtime_capability_sha256", + "orchestration_revision", + "clean_tree", + "uv_lock_sha256", + "runtime_capability", + "runtime_capability_sha256", + "analysis_plan_sha256", + "rng_assignment_sha256", + "capability_waiver", + "merged_progress_path", + "run_spec_sha256", + } + extension_fields = p0_fields | { + "source_extension_protocol_sha256", + "source_p0_analysis_document_sha256", + "design_sha256", + } + is_extension = contract.production_kind == "p0-extension-v1" + expected_fields = extension_fields if is_extension else p0_fields + if ( + set(document) != expected_fields + or document.get("schema_version") != contract.run_spec_schema + ): + raise RuntimeError("pilot run spec fields or schema are invalid") + if document.get("run_spec_sha256") != _document_hash(document, "run_spec_sha256"): + raise RuntimeError("pilot run spec hash mismatch") + if ( + document.get("artifact_root") != "." + or document.get("merged_progress_path") != MERGED_NAME + ): + raise RuntimeError("pilot portable paths are not frozen") + for field in ( + "correctness_report_sha256", + "correctness_run_spec_sha256", + "correctness_approval_registry_sha256", + "validated_engine_sha256", + "validation_runtime_capability_sha256", + "uv_lock_sha256", + "runtime_capability_sha256", + "analysis_plan_sha256", + "rng_assignment_sha256", + ): + if ( + not isinstance(document.get(field), str) + or _HEX64.fullmatch(str(document[field])) is None + ): + raise RuntimeError(f"pilot {field} is malformed") + if ( + document.get("clean_tree") is not True + or not isinstance(document.get("orchestration_revision"), str) + or _HEX40.fullmatch(str(document["orchestration_revision"])) is None + or document.get("correctness_approval_revision") + != CORRECTNESS_APPROVAL_REVISION + or not isinstance(document.get("validation_source_revision"), str) + or _HEX40.fullmatch(str(document["validation_source_revision"])) is None + ): + raise RuntimeError("pilot orchestration source evidence is invalid") + runtime = document.get("runtime_capability") + if not isinstance(runtime, Mapping) or _sha256( + _canonical_bytes(runtime) + ) != document.get("runtime_capability_sha256"): + raise RuntimeError("pilot runtime capability hash mismatch") + modules = document.get("validated_engine_modules") + if ( + not isinstance(modules, Mapping) + or set(modules) != set(SCIENTIFIC_ENGINE_MODULES) + or _aggregate_hash({str(k): str(v) for k, v in modules.items()}) + != document.get("validated_engine_sha256") + ): + raise RuntimeError("pilot scientific engine binding is invalid") + waiver = document.get("capability_waiver") + if ( + not isinstance(waiver, Mapping) + or set(waiver) != {"reason", "benchmark_status", "utc_timestamp"} + or waiver.get("reason") != "user-waived-after-correctness-gate" + or waiver.get("benchmark_status") != "cancelled-without-capability-report" + or not isinstance(waiver.get("utc_timestamp"), str) + or not str(waiver["utc_timestamp"]).endswith("Z") + ): + raise RuntimeError("pilot capability waiver is invalid") + if _is_production_contract(contract): + approval = _load_approval_registry() + if ( + document.get("correctness_report_sha256") != approval["report_sha256"] + or document.get("correctness_run_spec_sha256") + != approval["run_spec_sha256"] + or document.get("validation_source_revision") + != approval["validation_source_revision"] + or document.get("validated_engine_sha256") + != approval["scientific_engine_sha256"] + or document.get("correctness_approval_registry_sha256") + != _sha256(_canonical_bytes(approval)) + ): + raise RuntimeError("pilot run spec is not bound to approved correctness") + + protocol = document.get("protocol") + if not isinstance(protocol, Mapping): + raise RuntimeError("pilot protocol is malformed") + if is_extension: + from .pilot_extension import ( + P0_ANALYSIS_DOCUMENT_SHA256, + _validate_bound_p0_extension_protocol_for_revision, + ) + + combined_protocol = dict(protocol) + combined_protocol["cells"] = document.get("cells") + if ( + document.get("source_extension_protocol_sha256") + != protocol.get("protocol_sha256") + or document.get("source_p0_analysis_document_sha256") + != P0_ANALYSIS_DOCUMENT_SHA256 + or document.get("design_sha256") != protocol.get("design_sha256") + or document.get("rng_assignment_sha256") + != protocol.get("rng_assignment_sha256") + or document.get("cell_count") != protocol.get("cell_count") + ): + raise RuntimeError("extension run spec source binding is invalid") + _validate_bound_p0_extension_protocol_for_revision( + combined_protocol, + expected_source_revision=str(document["orchestration_revision"]), + ) + else: + unsigned_protocol = dict(protocol) + protocol_hash = unsigned_protocol.pop("sha256", None) + if protocol_hash != _sha256(_canonical_bytes(unsigned_protocol)): + raise RuntimeError("pilot protocol hash mismatch") + if contract.production_kind == "p0" and unsigned_protocol != { + "lengths": list(PILOT_LENGTHS), + "sigmas": [value.hex() for value in PILOT_SIGMAS], + "replicas": list(PILOT_REPLICAS), + "kappas": [value.hex() for value in PILOT_KAPPAS], + "master_seed": PILOT_MASTER_SEED, + "phase": PILOT_PHASE, + "loop_order": ["sigma", "length", "replica"], + "purpose": "exploratory-window-selection-only", + }: + raise RuntimeError("pilot P0 protocol is not frozen") + cells = document.get("cells") + if ( + not isinstance(cells, list) + or document.get("cell_count") != len(cells) + or (_is_production_contract(contract) and len(cells) != 96) + ): + raise RuntimeError("pilot cell count is invalid") + seen_ids: set[str] = set() + seen_requests: set[str] = set() + assignments: list[dict[str, object]] = [] + expected_positions = ( + [ + (sigma, length, replica) + for sigma in PILOT_SIGMAS + for length in PILOT_LENGTHS + for replica in PILOT_REPLICAS + ] + if contract.production_kind == "p0" + else None + ) + for index, raw in enumerate(cells): + if not isinstance(raw, Mapping): + raise RuntimeError("pilot cell is malformed") + expected_keys = { + "cell_index", + "cell_id", + "sigma", + "length", + "replica", + "sigma_grid_id", + "kappas", + "kernel_sha256", + "request_sha256", + "rng_material_sha256", + "cell_path", + "run_path", + "manifest_path", + } + raw_kappas = raw.get("kappas") + raw_rng = raw.get("rng_material_sha256") + expected_kappa_count = ( + len(PILOT_KAPPAS) + if contract.production_kind == "p0" + else 17 + if is_extension + else None + ) + if ( + set(raw) != expected_keys + or not isinstance(raw_kappas, list) + or not 1 <= len(raw_kappas) <= 17 + or ( + expected_kappa_count is not None + and len(raw_kappas) != expected_kappa_count + ) + or not isinstance(raw_rng, list) + or len(raw_rng) != STREAM_COUNT + ): + raise RuntimeError("pilot cell bounded fields are invalid") + cell = PilotCell.from_document(raw) + if cell.cell_index != index: + raise RuntimeError("pilot cell registry is noncanonical") + if ( + expected_positions is not None + and ( + cell.sigma, + cell.length, + cell.replica, + ) + != expected_positions[index] + ): + raise RuntimeError("pilot positional cell registry is not frozen") + if contract.production_kind == "p0" and cell.kappas != PILOT_KAPPAS: + raise RuntimeError("pilot cell kappas are not frozen") + expected_grid_namespace = { + "p0": "pilot-p0-v1", + "test-p0": "pilot-p0-v1", + "test-p0-extension-v1": "pilot-p0-extension-test-v1", + }.get(contract.production_kind) + if ( + ( + expected_grid_namespace is not None + and cell.sigma_grid_id + != f"{expected_grid_namespace}|sigma-f64={cell.sigma.hex()}" + ) + or request_digest( + cell.request( + master_seed=contract.master_seed, + phase=contract.phase, + ) + ) + != cell.request_sha256 + or _HEX64.fullmatch(cell.kernel_sha256) is None + or len(cell.rng_material_sha256) != STREAM_COUNT + or tuple(cell.rng_material_sha256) + != _stream_hashes( + cell.length, + cell.sigma_grid_id, + cell.replica, + master_seed=contract.master_seed, + phase=contract.phase, + ) + ): + raise RuntimeError("pilot cell request or RNG identity is stale") + identity = { + "cell_index": index, + "sigma": cell.sigma.hex(), + "length": cell.length, + "replica": cell.replica, + "request_sha256": cell.request_sha256, + } + expected_id = f"{index:03d}-{_sha256(_canonical_bytes(identity))[:16]}" + expected_cell_path = f"cells/{expected_id}" + if ( + cell.cell_id != expected_id + or cell.cell_path != expected_cell_path + or cell.run_path != f"{expected_cell_path}/run" + or cell.manifest_path != f"{expected_cell_path}/manifest.json" + ): + raise RuntimeError("pilot cell paths are noncanonical") + if cell.cell_id in seen_ids or cell.request_sha256 in seen_requests: + raise RuntimeError("pilot cells contain duplicate identities") + seen_ids.add(cell.cell_id) + seen_requests.add(cell.request_sha256) + assignments.append( + { + "cell_index": index, + "request_sha256": cell.request_sha256, + "streams": list(cell.rng_material_sha256), + } + ) + if _sha256(_canonical_bytes({"assignments": assignments})) != document.get( + "rng_assignment_sha256" + ): + raise RuntimeError("pilot complete RNG assignment hash mismatch") + + +def _validate_loaded_pilot_spec( + document: dict[str, object], + *, + verify_current_environment: bool, + expected_schema: str, +) -> dict[str, object]: + if document.get("schema_version") != expected_schema: + description = ( + "P0 run spec" + if expected_schema == RUN_SPEC_SCHEMA + else "P0 extension run spec" + if expected_schema == EXTENSION_CONTRACT.run_spec_schema + else "registered Pilot run spec" + ) + raise RuntimeError(f"{description} schema is required") + contract = _contract_for_schema(document.get("schema_version")) + _validate_pilot_spec( + document, + contract=contract, + ) + if _lock_hash() != document["uv_lock_sha256"]: + raise RuntimeError("uv.lock drift from pilot run spec") + modules = _scientific_hashes() + if ( + modules != document["validated_engine_modules"] + or _aggregate_hash(modules) != document["validated_engine_sha256"] + ): + raise RuntimeError("scientific engine module drift from pilot run spec") + if _analysis_plan_hash() != document["analysis_plan_sha256"]: + raise RuntimeError("analysis plan drift from pilot run spec") + if _approval_registry_digest() != document["correctness_approval_registry_sha256"]: + raise RuntimeError("correctness approval registry drift from pilot run spec") + if verify_current_environment: + source = _current_source(require_clean=True) + if source["source_revision"] != document["orchestration_revision"]: + raise RuntimeError("orchestration revision drift from pilot run spec") + runtime, runtime_hash = _runtime_document() + if ( + runtime != document["runtime_capability"] + or runtime_hash != document["runtime_capability_sha256"] + ): + raise RuntimeError("compute-node runtime capability drift") + return document + + +def _load_pilot_spec( + path: Path, + *, + verify_current_environment: bool, + expected_schema: str, +) -> dict[str, object]: + if ( + not isinstance(path, Path) + or not path.is_absolute() + or path.name != RUN_SPEC_NAME + ): + raise RuntimeError("pilot run spec path must be absolute and canonical") + document, _ = _read_canonical( + path, + "pilot run spec", + maximum_size=PILOT_RUN_SPEC_MAX_BYTES, + allow_parent_mutation=True, + ) + return _validate_loaded_pilot_spec( + document, + verify_current_environment=verify_current_environment, + expected_schema=expected_schema, + ) + + +def load_pilot_run_spec( + path: Path, verify_current_environment: bool +) -> dict[str, object]: + return _load_pilot_spec( + path, + verify_current_environment=verify_current_environment, + expected_schema=RUN_SPEC_SCHEMA, + ) + + +def load_p0_extension_run_spec( + path: Path, verify_current_environment: bool = True +) -> dict[str, object]: + from .pilot_extension import EXTENSION_RUN_SPEC_SCHEMA + + return _load_pilot_spec( + path, + verify_current_environment=verify_current_environment, + expected_schema=EXTENSION_RUN_SPEC_SCHEMA, + ) + + +def _expected(spec: Mapping[str, object], cell: PilotCell) -> dict[str, str]: + return { + "request_sha256": cell.request_sha256, + "kernel_sha256": cell.kernel_sha256, + "source_revision": str(spec["orchestration_revision"]), + "uv_lock_sha256": str(spec["uv_lock_sha256"]), + "runtime_capability_sha256": str(spec["runtime_capability_sha256"]), + "analysis_plan_sha256": str(spec["analysis_plan_sha256"]), + "rng_sha256": str(spec["rng_assignment_sha256"]), + "conversion_version": CONVERSION_VERSION, + "rng_version": RNG_VERSION, + } + + +def _provenance(spec: Mapping[str, object]) -> dict[str, object]: + return { + "source_revision": spec["orchestration_revision"], + "clean_tree": True, + "uv_lock_sha256": spec["uv_lock_sha256"], + "runtime_capability_sha256": spec["runtime_capability_sha256"], + "analysis_plan_sha256": spec["analysis_plan_sha256"], + "rng_sha256": spec["rng_assignment_sha256"], + "conversion_version": CONVERSION_VERSION, + "rng_version": RNG_VERSION, + } + + +def _reject_markers(cell_root: Path) -> None: + if not cell_root.exists(): + return + chain = _open_directory_chain(cell_root, create=False) + try: + for count, path in enumerate(cell_root.rglob("*"), start=1): + if count > PILOT_CELL_MAX_ENTRIES: + raise RuntimeError("pilot cell artifact count exceeds frozen bound") + if path.is_symlink(): + raise RuntimeError("pilot cell contains a symlink") + if path.name.endswith((".partial", ".intent")): + raise RuntimeError(f"surviving publication marker: {path.name}") + _require_directory_chain(chain, allow_final_mutation=False) + finally: + _close_directory_chain(chain) + + +def _initialize_run( + run: Path, + spec: Mapping[str, object], + cell: PilotCell, + kernel: np.ndarray, + contract: PilotRunContract, +) -> None: + if run.exists(): + return + run.mkdir() + kernel_dir = run / "kernel" + kernel_dir.mkdir() + (kernel_dir / "kernel-f64le.bin").write_bytes( + kernel.astype(" Path: + return run / "trajectories" / f"trajectory-{cell.request_sha256}.h5" + + +def _cell_manifest_document( + spec: Mapping[str, object], cell: PilotCell, run: Path +) -> dict[str, object]: + progress, progress_payload = _read_canonical( + run / "progress.json", + "cell progress", + maximum_size=PILOT_PROGRESS_MAX_BYTES, + ) + trajectory = _trajectory_path(run, cell) + sidecar, _ = _read_canonical( + trajectory.with_suffix(".sha256.json"), + "trajectory digest", + maximum_size=PILOT_MARKER_MAX_BYTES, + ) + if progress.get("trajectory_count") != 1 or progress.get("batch_count") != 1: + raise RuntimeError("cell progress does not contain one complete trajectory") + return { + "schema_version": CELL_MANIFEST_SCHEMA, + "status": "success", + "run_spec_sha256": spec["run_spec_sha256"], + "cell_index": cell.cell_index, + "cell_id": cell.cell_id, + "request_sha256": cell.request_sha256, + "kernel_sha256": cell.kernel_sha256, + "trajectory_path": ( + f"{cell.run_path}/trajectories/trajectory-{cell.request_sha256}.h5" + ), + "trajectory_sha256": sidecar["trajectory_sha256"], + "progress_sha256": _sha256(progress_payload), + } + + +def _verify_success_cell( + root: Path, spec: Mapping[str, object], cell: PilotCell +) -> dict[str, object]: + cell_root = _relative_path(root, cell.cell_path, "cells") + _reject_markers(cell_root) + run = _relative_path(root, cell.run_path, "cells") + marker = _relative_path(root, cell.manifest_path, "cells") + if not marker.is_file(): + raise RuntimeError("cell success manifest is missing") + expected = _expected(spec, cell) + progress = reconstruct_progress(run, expected) + trajectory = _trajectory_path(run, cell) + load_verified_trajectory(trajectory, expected) + manifest, _ = _read_canonical( + marker, + "cell success manifest", + maximum_size=PILOT_MARKER_MAX_BYTES, + ) + required = _cell_manifest_document(spec, cell, run) + if manifest != required: + raise RuntimeError("cell success manifest is stale or corrupt") + if progress.get("trajectory_count") != 1: + raise RuntimeError("cell has duplicate trajectories") + return manifest + + +def _run_cell( + run_spec_path: Path, + cell_index: int, + *, + verify_current_environment: bool, + expected_schema: str, + crash_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + spec = _load_pilot_spec( + run_spec_path, + verify_current_environment=verify_current_environment, + expected_schema=expected_schema, + ) + contract = _contract_for_schema(spec["schema_version"]) + cells = spec["cells"] + if ( + isinstance(cell_index, bool) + or not isinstance(cell_index, int) + or not 0 <= cell_index < len(cells) + ): + raise ValueError("cell_index is outside the pilot run spec") + cell = PilotCell.from_document(cells[cell_index]) + root = run_spec_path.parent + cell_root = _relative_path(root, cell.cell_path, "cells") + cell_chain = _open_cell_directory_chain(root, cell.cell_id) + descriptor = cell_chain[-1][1] + shared_cells_index = len(cell_chain) - 2 + locked = True + try: + _require_directory_chain( + cell_chain, + allow_final_mutation=True, + mutable_indexes={shared_cells_index}, + ) + generation = _directory_generation(cell_root, descriptor) + _reject_markers(cell_root) + _require_directory_generation(cell_root, descriptor, generation) + marker = _relative_path(root, cell.manifest_path, "cells") + if marker.exists(): + manifest = _verify_success_cell(root, spec, cell) + _require_directory_generation(cell_root, descriptor, generation) + _require_directory_chain( + cell_chain, + allow_final_mutation=True, + mutable_indexes={shared_cells_index}, + ) + return { + "cell_index": cell_index, + "cell_id": cell.cell_id, + "manifest_path": cell.manifest_path, + "trajectory_sha256": manifest["trajectory_sha256"], + } + _reject_markers(cell_root) + kernel = periodic_kernel(cell.length, cell.sigma) + actual_kernel_hash = _sha256( + kernel.astype(" dict[str, object]: + return _run_cell( + run_spec_path, + cell_index, + verify_current_environment=True, + expected_schema=RUN_SPEC_SCHEMA, + ) + + +def pending_pilot_cells( + run_spec_path: Path, *, verify_current_environment: bool = True +) -> list[int]: + return _pending_registered_cells( + run_spec_path, + verify_current_environment=verify_current_environment, + expected_schema=RUN_SPEC_SCHEMA, + ) + + +def _pending_registered_cells( + run_spec_path: Path, + *, + verify_current_environment: bool, + expected_schema: str, +) -> list[int]: + spec = _load_pilot_spec( + run_spec_path, + verify_current_environment=verify_current_environment, + expected_schema=expected_schema, + ) + root = run_spec_path.parent + pending: list[int] = [] + for raw in spec["cells"]: + cell = PilotCell.from_document(raw) + marker = _relative_path(root, cell.manifest_path, "cells") + if marker.exists(): + _verify_success_cell(root, spec, cell) + else: + _reject_markers(_relative_path(root, cell.cell_path, "cells")) + pending.append(cell.cell_index) + return pending + + +def run_p0_extension_cell(run_spec_path: Path, cell_index: int) -> dict[str, object]: + from .pilot_extension import EXTENSION_RUN_SPEC_SCHEMA + + return _run_cell( + run_spec_path, + cell_index, + verify_current_environment=True, + expected_schema=EXTENSION_RUN_SPEC_SCHEMA, + ) + + +def pending_p0_extension_cells( + run_spec_path: Path, *, verify_current_environment: bool = True +) -> list[int]: + from .pilot_extension import EXTENSION_RUN_SPEC_SCHEMA + + return _pending_registered_cells( + run_spec_path, + verify_current_environment=verify_current_environment, + expected_schema=EXTENSION_RUN_SPEC_SCHEMA, + ) + + +def _merged_document( + run_spec_path: Path, + *, + verify_current_environment: bool, + expected_schema: str, +) -> dict[str, object]: + spec = _load_pilot_spec( + run_spec_path, + verify_current_environment=verify_current_environment, + expected_schema=expected_schema, + ) + return _merged_document_from_spec(run_spec_path, spec) + + +def _merged_document_from_spec( + run_spec_path: Path, + spec: Mapping[str, object], +) -> dict[str, object]: + contract = _contract_for_schema(spec["schema_version"]) + root = run_spec_path.parent + cells_root = root / "cells" + expected_names = {PilotCell.from_document(raw).cell_id for raw in spec["cells"]} + cells_chain = _open_directory_chain(cells_root, create=False) + try: + actual_names: set[str] = set() + with os.scandir(cells_chain[-1][1]) as stream: + for count, entry in enumerate(stream, start=1): + if count > len(expected_names) + 1: + raise RuntimeError( + "pilot cell directory count exceeds frozen bound" + ) + metadata = entry.stat(follow_symlinks=False) + if not stat.S_ISDIR(metadata.st_mode): + raise RuntimeError("pilot cells root contains a non-directory") + actual_names.add(entry.name) + missing = sorted(expected_names - actual_names) + extra = sorted(actual_names - expected_names) + if missing or extra: + raise RuntimeError( + f"pilot cell set mismatch; missing={missing}, extra={extra}" + ) + records = [] + requests: set[str] = set() + for raw in spec["cells"]: + cell = PilotCell.from_document(raw) + manifest = _verify_success_cell(root, spec, cell) + if cell.request_sha256 in requests: + raise RuntimeError("merged pilot contains duplicate request identity") + requests.add(cell.request_sha256) + records.append( + { + "cell_index": cell.cell_index, + "cell_id": cell.cell_id, + "manifest_path": cell.manifest_path, + "request_sha256": cell.request_sha256, + "trajectory_sha256": manifest["trajectory_sha256"], + } + ) + _require_directory_chain(cells_chain, allow_final_mutation=False) + finally: + _close_directory_chain(cells_chain) + return { + "schema_version": contract.progress_schema, + "run_spec_sha256": spec["run_spec_sha256"], + "cell_count": len(records), + "trajectory_count": len(records), + "cells": records, + "purpose": ( + "exploratory-p0-extension-only" + if "extension" in contract.production_kind + else "exploratory-window-selection-only" + ), + "physics_claims_authorized": False, + } + + +def verify_frozen_challenge_194_p0_download(run_spec_path: Path) -> dict[str, object]: + expected_run_spec_sha256 = ( + "d17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840" + ) + expected_progress_sha256 = ( + "ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f" + ) + spec, spec_payload = _read_canonical( + run_spec_path, + "historical frozen P0 run spec", + maximum_size=PILOT_RUN_SPEC_MAX_BYTES, + allow_parent_mutation=True, + ) + if _sha256(spec_payload) != expected_run_spec_sha256: + raise RuntimeError("historical frozen P0 run spec hash mismatch") + _validate_pilot_spec(spec, contract=P0_CONTRACT) + progress_path = run_spec_path.parent / MERGED_NAME + existing, progress_payload = _read_canonical( + progress_path, + "historical frozen P0 progress", + maximum_size=PILOT_PROGRESS_MAX_BYTES, + ) + if _sha256(progress_payload) != expected_progress_sha256: + raise RuntimeError("historical frozen P0 progress hash mismatch") + reconstructed = _merged_document_from_spec(run_spec_path, spec) + if existing != reconstructed: + raise RuntimeError("historical frozen P0 progress is stale or corrupt") + return reconstructed + + +def merge_pilot_progress( + run_spec_path: Path, output: Path | None = None +) -> dict[str, object]: + document = _merged_document( + run_spec_path, + verify_current_environment=True, + expected_schema=RUN_SPEC_SCHEMA, + ) + fixed = run_spec_path.parent / MERGED_NAME + if output is not None and output != fixed: + raise RuntimeError("merge output must be the portable run-spec progress path") + _publish_once(fixed, document) + return document + + +def verify_pilot_download(run_spec_path: Path) -> dict[str, object]: + document = _merged_document( + run_spec_path, + verify_current_environment=False, + expected_schema=RUN_SPEC_SCHEMA, + ) + progress = run_spec_path.parent / MERGED_NAME + if not progress.exists(): + raise RuntimeError("merged pilot progress is missing") + existing, _ = _read_canonical( + progress, + "merged pilot progress", + maximum_size=PILOT_PROGRESS_MAX_BYTES, + ) + if existing != document: + raise RuntimeError("merged pilot progress is stale or corrupt") + return document + + +def merge_p0_extension_progress( + run_spec_path: Path, output: Path | None = None +) -> dict[str, object]: + from .pilot_extension import EXTENSION_RUN_SPEC_SCHEMA + + document = _merged_document( + run_spec_path, + verify_current_environment=True, + expected_schema=EXTENSION_RUN_SPEC_SCHEMA, + ) + fixed = run_spec_path.parent / MERGED_NAME + if output is not None and output != fixed: + raise RuntimeError("merge output must be the portable run-spec progress path") + _publish_once(fixed, document) + return document + + +def verify_p0_extension_download(run_spec_path: Path) -> dict[str, object]: + from .pilot_extension import EXTENSION_RUN_SPEC_SCHEMA + + document = _merged_document( + run_spec_path, + verify_current_environment=False, + expected_schema=EXTENSION_RUN_SPEC_SCHEMA, + ) + progress = run_spec_path.parent / MERGED_NAME + if not progress.exists(): + raise RuntimeError("merged pilot progress is missing") + existing, _ = _read_canonical( + progress, + "merged P0 extension progress", + maximum_size=PILOT_PROGRESS_MAX_BYTES, + ) + if existing != document: + raise RuntimeError("merged P0 extension progress is stale or corrupt") + return document + + +_PILOT_ANALYSIS_SNAPSHOT_TOKEN = object() + + +@dataclass +class _PilotSnapshotPreflight: + byte_budget: int + total_bytes: int = 0 + entry_count: int = 0 + + def add_entry(self, size: int | None = None) -> None: + self.entry_count += 1 + if self.entry_count > PILOT_SNAPSHOT_MAX_ENTRIES: + raise RuntimeError("pilot snapshot aggregate entry budget exceeded") + if size is None: + return + if isinstance(size, bool) or not isinstance(size, int) or size < 0: + raise RuntimeError("pilot snapshot file size is invalid") + remaining = self.byte_budget - self.total_bytes + if size > remaining: + raise RuntimeError("pilot snapshot aggregate byte budget exceeded") + self.total_bytes += size + + +def _snapshot_regular_size_at( + name: str, + parent_fd: int, + *, + maximum_size: int, + description: str, + preflight: _PilotSnapshotPreflight, +) -> int: + descriptor, metadata = _open_regular_at( + name, + parent_fd, + description, + maximum_size=maximum_size, + ) + try: + current = os.fstat(descriptor) + if _generation_tuple(current) != _generation_tuple(metadata): + raise RuntimeError(f"{description} changed during preflight") + preflight.add_entry(metadata.st_size) + return metadata.st_size + finally: + os.close(descriptor) + + +def _snapshot_names_at( + descriptor: int, + *, + maximum_count: int, + description: str, +) -> tuple[str, ...]: + names: list[str] = [] + with os.scandir(descriptor) as stream: + for entry in stream: + names.append(entry.name) + if len(names) > maximum_count: + raise RuntimeError(f"{description} entry cardinality exceeds bound") + if len(names) != len(set(names)): + raise RuntimeError(f"{description} contains duplicate names") + return tuple(sorted(names)) + + +def _require_snapshot_names( + actual: Sequence[str], + expected: Set[str], + description: str, +) -> None: + if set(actual) != set(expected): + raise RuntimeError(f"unknown snapshot layout entry in {description}") + + +def _preflight_cell_snapshot( + cells_fd: int, + cell: PilotCell, + preflight: _PilotSnapshotPreflight, +) -> None: + cell_fd = _open_directory_at(cell.cell_id, cells_fd) + preflight.add_entry() + try: + _require_snapshot_names( + _snapshot_names_at( + cell_fd, + maximum_count=3, + description="pilot cell root", + ), + {"manifest.json", "run"}, + "pilot cell root", + ) + _snapshot_regular_size_at( + "manifest.json", + cell_fd, + maximum_size=PILOT_MARKER_MAX_BYTES, + description="pilot outer success manifest", + preflight=preflight, + ) + run_fd = _open_directory_at("run", cell_fd) + preflight.add_entry() + try: + _require_snapshot_names( + _snapshot_names_at( + run_fd, + maximum_count=len(_artifacts._ROOT_ENTRIES) + 1, + description="pilot cell run root", + ), + _artifacts._ROOT_ENTRIES, + "pilot cell run root", + ) + for name in sorted(_artifacts._UPSTREAM_FILES | {"progress.json"}): + _snapshot_regular_size_at( + name, + run_fd, + maximum_size=_artifacts.MAX_JSON_BYTES, + description=f"pilot run metadata {name}", + preflight=preflight, + ) + + kernel_fd = _open_directory_at("kernel", run_fd) + preflight.add_entry() + try: + kernel_names = _snapshot_names_at( + kernel_fd, + maximum_count=_artifacts.MAX_KERNEL_FILES, + description="pilot kernel snapshot", + ) + if not kernel_names: + raise RuntimeError("pilot kernel snapshot is empty") + for name in kernel_names: + _snapshot_regular_size_at( + name, + kernel_fd, + maximum_size=_artifacts.MAX_KERNEL_FILE_BYTES, + description="pilot kernel snapshot file", + preflight=preflight, + ) + finally: + os.close(kernel_fd) + + trajectories_fd = _open_directory_at("trajectories", run_fd) + preflight.add_entry() + try: + expected_trajectory_names = { + f"trajectory-{cell.request_sha256}.h5", + f"trajectory-{cell.request_sha256}.sha256.json", + } + _require_snapshot_names( + _snapshot_names_at( + trajectories_fd, + maximum_count=3, + description="pilot trajectory snapshot", + ), + expected_trajectory_names, + "pilot trajectory snapshot", + ) + _snapshot_regular_size_at( + f"trajectory-{cell.request_sha256}.h5", + trajectories_fd, + maximum_size=_artifacts.MAX_HDF5_BYTES, + description="pilot trajectory snapshot file", + preflight=preflight, + ) + _snapshot_regular_size_at( + f"trajectory-{cell.request_sha256}.sha256.json", + trajectories_fd, + maximum_size=_artifacts.MAX_JSON_BYTES, + description="pilot trajectory sidecar snapshot", + preflight=preflight, + ) + finally: + os.close(trajectories_fd) + + batches_fd = _open_directory_at("batches", run_fd) + preflight.add_entry() + try: + batch_name = f"batch-cell-{cell.cell_index:03d}.json" + _require_snapshot_names( + _snapshot_names_at( + batches_fd, + maximum_count=2, + description="pilot batch snapshot", + ), + {batch_name}, + "pilot batch snapshot", + ) + _snapshot_regular_size_at( + batch_name, + batches_fd, + maximum_size=_artifacts.MAX_JSON_BYTES, + description="pilot batch snapshot file", + preflight=preflight, + ) + finally: + os.close(batches_fd) + finally: + os.close(run_fd) + finally: + os.close(cell_fd) + + +def _preflight_pilot_snapshot( + source_root_fd: int, + spec: Mapping[str, object], + *, + run_spec_size: int, + progress_size: int, + byte_budget: int, +) -> _PilotSnapshotPreflight: + if ( + isinstance(byte_budget, bool) + or not isinstance(byte_budget, int) + or not 1 <= byte_budget <= PILOT_SNAPSHOT_MAX_BYTES + ): + raise RuntimeError("pilot snapshot byte budget is invalid") + preflight = _PilotSnapshotPreflight(byte_budget=byte_budget) + preflight.add_entry(run_spec_size) + preflight.add_entry(progress_size) + cells_fd = _open_directory_at("cells", source_root_fd) + preflight.add_entry() + try: + raw_cells = spec["cells"] + if not isinstance(raw_cells, Sequence): + raise RuntimeError("pilot snapshot cells are malformed") + cells = tuple(PilotCell.from_document(raw) for raw in raw_cells) + expected_names = {cell.cell_id for cell in cells} + _require_snapshot_names( + _snapshot_names_at( + cells_fd, + maximum_count=len(cells) + 1, + description="pilot cells snapshot", + ), + expected_names, + "pilot cells snapshot", + ) + for cell in cells: + _preflight_cell_snapshot(cells_fd, cell, preflight) + finally: + os.close(cells_fd) + return preflight + + +def _copy_regular_snapshot_at( + name: str, + parent_fd: int, + destination: Path, + *, + maximum_size: int, + description: str, + global_counter: list[int], + global_limit: int, + retained_source: tuple[int, os.stat_result] | None = None, +) -> bytes | None: + if retained_source is None: + descriptor, original = _open_regular_at( + name, + parent_fd, + description, + maximum_size=maximum_size, + ) + close_descriptor = True + else: + descriptor, original = retained_source + close_descriptor = False + os.lseek(descriptor, 0, os.SEEK_SET) + captured: list[bytes] | None = ( + [] if maximum_size <= PILOT_RUN_SPEC_MAX_BYTES else None + ) + copied = 0 + try: + with destination.open("xb") as output: + while block := os.read(descriptor, 1024 * 1024): + copied += len(block) + if copied > maximum_size: + raise RuntimeError(f"{description} exceeds the byte-size limit") + if len(block) > global_limit - global_counter[0]: + raise RuntimeError("snapshot byte budget changed during copy") + global_counter[0] += len(block) + output.write(block) + if captured is not None: + captured.append(block) + current = os.fstat(descriptor) + if copied != original.st_size or _generation_tuple( + current + ) != _generation_tuple(original): + raise RuntimeError(f"{description} changed during snapshot") + finally: + if close_descriptor: + os.close(descriptor) + return b"".join(captured) if captured is not None else None + + +def _copy_cell_tree_snapshot( + source_fd: int, + destination: Path, + *, + remaining_entries: list[int], + global_counter: list[int], + global_limit: int, + depth: int = 0, +) -> None: + if depth > 6: + raise RuntimeError("pilot cell snapshot depth exceeds frozen bound") + destination.mkdir() + with os.scandir(source_fd) as stream: + names = sorted(entry.name for entry in stream) + for name in names: + remaining_entries[0] -= 1 + if remaining_entries[0] < 0: + raise RuntimeError("pilot cell artifact count exceeds frozen bound") + metadata = os.stat(name, dir_fd=source_fd, follow_symlinks=False) + target = destination / name + if stat.S_ISLNK(metadata.st_mode): + raise RuntimeError("pilot cell snapshot contains a symlink") + if stat.S_ISDIR(metadata.st_mode): + child = _open_directory_at(name, source_fd) + try: + _copy_cell_tree_snapshot( + child, + target, + remaining_entries=remaining_entries, + global_counter=global_counter, + global_limit=global_limit, + depth=depth + 1, + ) + finally: + os.close(child) + elif stat.S_ISREG(metadata.st_mode): + maximum_size = ( + _artifacts.MAX_HDF5_BYTES + if name.endswith(".h5") + else max(_artifacts.MAX_JSON_BYTES, _artifacts.MAX_KERNEL_FILE_BYTES) + ) + _copy_regular_snapshot_at( + name, + source_fd, + target, + maximum_size=maximum_size, + description="pilot cell snapshot file", + global_counter=global_counter, + global_limit=global_limit, + ) + else: + raise RuntimeError("pilot cell snapshot contains a special file") + + +def _default_pilot_snapshot_parent() -> Path: + parent = ( + Path(tempfile.gettempdir()).resolve() + / f".challenge-194-p0-snapshots-{os.geteuid()}" + ) + try: + parent.mkdir(mode=0o700) + except FileExistsError: + pass + return parent + + +def _open_validated_snapshot_parent(path: Path) -> int: + if not isinstance(path, Path) or not path.is_absolute(): + raise RuntimeError("snapshot parent must be an absolute path") + try: + before = path.lstat() + except OSError as error: + raise RuntimeError("snapshot parent must already exist") from error + if ( + stat.S_ISLNK(before.st_mode) + or not stat.S_ISDIR(before.st_mode) + or before.st_uid != os.geteuid() + or before.st_mode & 0o022 + ): + raise RuntimeError( + "snapshot parent must be an owned non-symlink private directory" + ) + descriptor = os.open(path, _directory_flags()) + opened = os.fstat(descriptor) + if ( + _directory_identity(before) != _directory_identity(opened) + or opened.st_uid != os.geteuid() + ): + os.close(descriptor) + raise RuntimeError("snapshot parent identity changed before descriptor open") + return descriptor + + +SnapshotProcessIdentity = tuple[int, str | None] + + +def _read_linux_process_birth_token(pid: int) -> str | None: + if pid <= 0: + return None + try: + boot_id = ( + Path("/proc/sys/kernel/random/boot_id").read_text(encoding="ascii").strip() + ) + stat_payload = Path(f"/proc/{pid}/stat").read_bytes() + except (OSError, UnicodeError): + return None + compact_boot_id = boot_id.replace("-", "") + closing_parenthesis = stat_payload.rfind(b")") + if ( + re.fullmatch(r"[0-9a-f]{32}", compact_boot_id) is None + or closing_parenthesis < 0 + or len(stat_payload) > 4096 + ): + return None + fields = stat_payload[closing_parenthesis + 1 :].split() + if len(fields) <= 19: + return None + try: + start_ticks = int(fields[19]) + except ValueError: + return None + if start_ticks <= 0: + return None + return f"linux-{compact_boot_id}-{start_ticks}" + + +def _snapshot_process_identity(pid: int | None = None) -> SnapshotProcessIdentity: + process_id = os.getpid() if pid is None else pid + if ( + isinstance(process_id, bool) + or not isinstance(process_id, int) + or process_id <= 0 + ): + raise RuntimeError("pilot snapshot process PID is invalid") + return process_id, _read_linux_process_birth_token(process_id) + + +def _snapshot_birth_name(token: str | None) -> str: + if token is None: + return "unverifiable" + if re.fullmatch(r"linux-[0-9a-f]{32}-[1-9][0-9]*", token) is None: + raise RuntimeError("pilot snapshot process birth token is malformed") + return token + + +def _snapshot_directory_name( + process_identity: SnapshotProcessIdentity, + uniqueness: str, + run_kind: str | None = None, +) -> str: + pid, birth_token = process_identity + if ( + isinstance(pid, bool) + or not isinstance(pid, int) + or pid <= 0 + or re.fullmatch(r"[0-9a-f]{32}", uniqueness) is None + or ( + run_kind is not None + and re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", run_kind) is None + ) + ): + raise RuntimeError("pilot snapshot directory identity is malformed") + kind_component = "" if run_kind is None else f"{run_kind}-" + return ( + f"{PILOT_SNAPSHOT_PREFIX}{kind_component}{pid}-" + f"{_snapshot_birth_name(birth_token)}-{uniqueness}" + ) + + +def _parse_snapshot_directory_name( + name: str, +) -> tuple[SnapshotProcessIdentity, str] | None: + match = re.fullmatch( + re.escape(PILOT_SNAPSHOT_PREFIX) + + r"(?:[a-z0-9]+(?:-[a-z0-9]+)*-)?" + + r"([1-9][0-9]*)-" + + r"(linux-[0-9a-f]{32}-[1-9][0-9]*|unverifiable)-" + + r"([0-9a-f]{32})", + name, + ) + if match is None: + return None + birth_name = match.group(2) + return ( + ( + int(match.group(1)), + None if birth_name == "unverifiable" else birth_name, + ), + match.group(3), + ) + + +def _snapshot_pid_exists(pid: int) -> bool | None: + try: + os.kill(pid, 0) + except ProcessLookupError: + return False + except PermissionError: + return True + except OSError: + return None + return True + + +def _snapshot_identity_liveness( + process_identity: SnapshotProcessIdentity, +) -> bool | None: + pid, expected_birth = process_identity + observed_birth = _read_linux_process_birth_token(pid) + if expected_birth is not None and observed_birth is not None: + return observed_birth == expected_birth + exists = _snapshot_pid_exists(pid) + if exists is False: + return False + return None + + +def _snapshot_marker_document( + name: str, + token: str, + process_identity: SnapshotProcessIdentity, +) -> dict[str, object]: + parsed = _parse_snapshot_directory_name(name) + if parsed != (process_identity, token): + raise RuntimeError("pilot snapshot name and process identity differ") + pid, birth_token = process_identity + return { + "schema_version": "challenge-194-p0-snapshot-owner-v2", + "directory_name": name, + "owner_uid": os.geteuid(), + "owner_pid": pid, + "owner_birth_token": birth_token, + "token": token, + } + + +def _read_snapshot_marker(directory_fd: int) -> dict[str, object]: + descriptor, original = _open_regular_at( + PILOT_SNAPSHOT_MARKER, + directory_fd, + "pilot snapshot ownership marker", + maximum_size=1024, + ) + try: + payload = _read_descriptor_bounded( + descriptor, + 1024, + "pilot snapshot ownership marker", + ) + current = os.fstat(descriptor) + if ( + current.st_uid != os.geteuid() + or current.st_nlink != 1 + or _generation_tuple(current) != _generation_tuple(original) + ): + raise RuntimeError("pilot snapshot ownership marker is unsafe") + document = json.loads(payload) + if ( + not isinstance(document, dict) + or payload != _canonical_bytes(document) + or set(document) + != { + "schema_version", + "directory_name", + "owner_uid", + "owner_pid", + "owner_birth_token", + "token", + } + ): + raise RuntimeError("pilot snapshot ownership marker is malformed") + return document + finally: + os.close(descriptor) + + +def _remove_snapshot_tree( + directory_fd: int, + *, + remaining_entries: list[int], + preserved_names: Set[str] = frozenset(), +) -> None: + names = _snapshot_names_at( + directory_fd, + maximum_count=PILOT_SNAPSHOT_MAX_ENTRIES + 1, + description="owned pilot snapshot cleanup", + ) + for name in names: + if name in preserved_names: + continue + remaining_entries[0] -= 1 + if remaining_entries[0] < 0: + raise RuntimeError("owned pilot snapshot cleanup exceeds entry bound") + metadata = os.stat(name, dir_fd=directory_fd, follow_symlinks=False) + if metadata.st_uid != os.geteuid(): + raise RuntimeError("owned pilot snapshot contains a foreign entry") + if stat.S_ISDIR(metadata.st_mode): + child_fd = _open_directory_at(name, directory_fd) + child_original = os.fstat(child_fd) + try: + _remove_snapshot_tree( + child_fd, + remaining_entries=remaining_entries, + ) + current = os.stat(name, dir_fd=directory_fd, follow_symlinks=False) + if _directory_identity(current) != _directory_identity(child_original): + raise RuntimeError( + "owned pilot snapshot directory identity changed" + ) + finally: + os.close(child_fd) + os.rmdir(name, dir_fd=directory_fd) + elif stat.S_ISREG(metadata.st_mode) or stat.S_ISLNK(metadata.st_mode): + os.unlink(name, dir_fd=directory_fd) + else: + raise RuntimeError("owned pilot snapshot contains a special file") + + +def _remove_owned_snapshot( + parent_fd: int, + name: str, + directory_fd: int, + expected_marker: Mapping[str, object], +) -> None: + marker = _read_snapshot_marker(directory_fd) + if marker != expected_marker: + raise RuntimeError("pilot snapshot ownership marker mismatch") + original = os.fstat(directory_fd) + if ( + original.st_uid != os.geteuid() + or not stat.S_ISDIR(original.st_mode) + or stat.S_IMODE(original.st_mode) != 0o700 + ): + raise RuntimeError("pilot snapshot directory ownership is unsafe") + _remove_snapshot_tree( + directory_fd, + remaining_entries=[PILOT_SNAPSHOT_MAX_ENTRIES + 1], + preserved_names={PILOT_SNAPSHOT_MARKER}, + ) + os.unlink(PILOT_SNAPSHOT_MARKER, dir_fd=directory_fd) + current = os.stat(name, dir_fd=parent_fd, follow_symlinks=False) + if _directory_identity(current) != _directory_identity(original): + raise RuntimeError("pilot snapshot pathname identity changed during cleanup") + os.rmdir(name, dir_fd=parent_fd) + + +def _remove_new_snapshot( + parent_fd: int, + name: str, + directory_fd: int, +) -> None: + original = os.fstat(directory_fd) + if ( + original.st_uid != os.geteuid() + or not stat.S_ISDIR(original.st_mode) + or stat.S_IMODE(original.st_mode) != 0o700 + ): + raise RuntimeError("new pilot snapshot directory ownership is unsafe") + _remove_snapshot_tree( + directory_fd, + remaining_entries=[PILOT_SNAPSHOT_MAX_ENTRIES + 1], + ) + current = os.stat(name, dir_fd=parent_fd, follow_symlinks=False) + if _directory_identity(current) != _directory_identity(original): + raise RuntimeError("new pilot snapshot pathname identity changed") + os.rmdir(name, dir_fd=parent_fd) + + +def _remove_empty_owned_snapshot( + parent_fd: int, + name: str, + directory_fd: int, +) -> bool: + original = os.fstat(directory_fd) + if ( + original.st_uid != os.geteuid() + or not stat.S_ISDIR(original.st_mode) + or stat.S_IMODE(original.st_mode) != 0o700 + or _snapshot_names_at( + directory_fd, + maximum_count=1, + description="empty stale pilot snapshot", + ) + ): + return False + current = os.stat(name, dir_fd=parent_fd, follow_symlinks=False) + if _directory_identity(current) != _directory_identity(original): + return False + os.rmdir(name, dir_fd=parent_fd) + return True + + +def _cleanup_stale_owned_snapshots(parent_fd: int) -> None: + names = _snapshot_names_at( + parent_fd, + maximum_count=PILOT_SNAPSHOT_STALE_SCAN_MAX, + description="pilot snapshot parent", + ) + for name in names: + parsed_name = _parse_snapshot_directory_name(name) + if parsed_name is None: + continue + process_identity, token = parsed_name + if _snapshot_identity_liveness(process_identity) is not False: + continue + try: + directory_fd = _open_directory_at(name, parent_fd) + except RuntimeError: + continue + try: + marker = _read_snapshot_marker(directory_fd) + expected_marker = _snapshot_marker_document( + name, + token, + process_identity, + ) + if marker != expected_marker: + continue + _remove_owned_snapshot(parent_fd, name, directory_fd, expected_marker) + except (OSError, RuntimeError, ValueError): + try: + _remove_empty_owned_snapshot(parent_fd, name, directory_fd) + except (OSError, RuntimeError, ValueError): + pass + finally: + os.close(directory_fd) + + +class _PilotSnapshotInterrupted(RuntimeError): + pass + + +def _install_snapshot_signal_handlers() -> dict[int, object]: + if threading.current_thread() is not threading.main_thread(): + return {} + previous: dict[int, object] = {} + + def interrupt(signum: int, _frame: object) -> None: + raise _PilotSnapshotInterrupted( + f"pilot snapshot interrupted by signal {signum}" + ) + + for signal_name in ("SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"): + signum = getattr(signal, signal_name, None) + if isinstance(signum, int): + previous[signum] = signal.getsignal(signum) + signal.signal(signum, interrupt) + return previous + + +def _restore_snapshot_signal_handlers(previous: Mapping[int, object]) -> None: + for signum, handler in previous.items(): + signal.signal(signum, handler) + + +@contextmanager +def _owned_pilot_snapshot_directory( + parent: Path, + *, + run_kind: str, +) -> Iterator[Path]: + parent_fd = _open_validated_snapshot_parent(parent) + try: + _cleanup_stale_owned_snapshots(parent_fd) + token = uuid.uuid4().hex + process_identity = _snapshot_process_identity() + name = _snapshot_directory_name(process_identity, token, run_kind) + marker = _snapshot_marker_document(name, token, process_identity) + directory_fd: int | None = None + directory_created = False + marker_complete = False + previous_handlers = _install_snapshot_signal_handlers() + try: + os.mkdir(name, 0o700, dir_fd=parent_fd) + directory_created = True + directory_fd = _open_directory_at(name, parent_fd) + marker_descriptor = os.open( + PILOT_SNAPSHOT_MARKER, + os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_NOFOLLOW", 0), + 0o600, + dir_fd=directory_fd, + ) + try: + payload = _canonical_bytes(marker) + written = os.write(marker_descriptor, payload) + if written != len(payload): + raise RuntimeError("short pilot snapshot marker write") + marker_complete = True + finally: + os.close(marker_descriptor) + yield parent / name + finally: + _restore_snapshot_signal_handlers(previous_handlers) + if directory_fd is not None: + try: + if marker_complete: + _remove_owned_snapshot(parent_fd, name, directory_fd, marker) + else: + _remove_new_snapshot(parent_fd, name, directory_fd) + finally: + os.close(directory_fd) + elif directory_created: + os.rmdir(name, dir_fd=parent_fd) + finally: + os.close(parent_fd) + + +def _preflight_snapshot_capacity(parent: Path, total_bytes: int) -> None: + descriptor = _open_validated_snapshot_parent(parent) + try: + capacity = os.statvfs(descriptor) + available = int(capacity.f_bavail) * int(capacity.f_frsize) + required = total_bytes + PILOT_SNAPSHOT_SAFETY_RESERVE_BYTES + if required > available: + raise RuntimeError("snapshot filesystem capacity is insufficient") + finally: + os.close(descriptor) + + +def _load_analysis_trajectory( + path: Path, + expected: dict[str, str], + required_digest: str, +) -> TrajectoryResult: + result, _, actual_digest, _ = _artifacts._verify_trajectory( + path, + expected["request_sha256"], + expected, + ) + if actual_digest != required_digest: + raise RuntimeError("trajectory digest differs from verified pilot progress") + return result + + +def _snapshot_malformed(message: str) -> Never: + raise RuntimeError(message) + + +@dataclass(frozen=True) +class _PilotAnalysisSnapshot: + run_spec_path: Path + run_spec_payload: bytes + progress_payload: bytes + spec: Mapping[str, object] + progress: Mapping[str, object] + _token: object + + def load_trajectory(self, cell_index: int) -> TrajectoryResult: + if self._token is not _PILOT_ANALYSIS_SNAPSHOT_TOKEN: + raise RuntimeError("invalid pilot analysis snapshot capability") + raw_cells = self.spec["cells"] + raw_progress = self.progress["cells"] + if not isinstance(raw_cells, Sequence) or not isinstance( + raw_progress, Sequence + ): + _snapshot_malformed("verified pilot snapshot is malformed") + raw_cell = raw_cells[cell_index] + progress_cell = raw_progress[cell_index] + if not isinstance(raw_cell, Mapping) or not isinstance(progress_cell, Mapping): + _snapshot_malformed("verified pilot snapshot cell is malformed") + cell = PilotCell.from_document(raw_cell) + trajectory = _trajectory_path( + self.run_spec_path.parent / cell.run_path, + cell, + ) + return _load_analysis_trajectory( + trajectory, + _expected(self.spec, cell), + str(progress_cell["trajectory_sha256"]), + ) + + +@contextmanager +def _open_verified_pilot_analysis_snapshot( + run_spec_path: Path, + *, + production: bool, + snapshot_parent: Path | None = None, + _snapshot_hook: Callable[[str], None] | None = None, + _snapshot_byte_budget: int = PILOT_SNAPSHOT_MAX_BYTES, + _expected_schema: str | None = None, +) -> Iterator[_PilotAnalysisSnapshot]: + expected_schema = ( + (RUN_SPEC_SCHEMA if production else TEST_RUN_SPEC_SCHEMA) + if _expected_schema is None + else _expected_schema + ) + contract = _contract_for_schema(expected_schema) + if ( + not isinstance(run_spec_path, Path) + or not run_spec_path.is_absolute() + or run_spec_path.name != RUN_SPEC_NAME + ): + raise RuntimeError("pilot run spec path must be absolute and canonical") + source_chain = _open_directory_chain( + run_spec_path.parent, + create=False, + allow_final_mutation=True, + ) + source_root_fd = source_chain[-1][1] + run_spec_descriptor: int | None = None + progress_descriptor: int | None = None + try: + run_spec_descriptor, run_spec_original = _open_regular_at( + RUN_SPEC_NAME, + source_root_fd, + "pilot run spec snapshot source", + maximum_size=PILOT_RUN_SPEC_MAX_BYTES, + ) + progress_descriptor, progress_original = _open_regular_at( + MERGED_NAME, + source_root_fd, + "merged pilot progress snapshot source", + maximum_size=PILOT_PROGRESS_MAX_BYTES, + ) + run_spec_payload = _read_descriptor_bounded( + run_spec_descriptor, + PILOT_RUN_SPEC_MAX_BYTES, + "pilot run spec snapshot source", + ) + progress_payload = _read_descriptor_bounded( + progress_descriptor, + PILOT_PROGRESS_MAX_BYTES, + "merged pilot progress snapshot source", + ) + try: + spec_document = json.loads(run_spec_payload) + progress_document = json.loads(progress_payload) + except (UnicodeError, json.JSONDecodeError, ValueError) as error: + raise RuntimeError("pilot snapshot source is not valid JSON") from error + _validate_json_bounds(spec_document) + _validate_json_bounds(progress_document) + if ( + not isinstance(spec_document, dict) + or run_spec_payload != _canonical_bytes(spec_document) + or not isinstance(progress_document, dict) + or progress_payload != _canonical_bytes(progress_document) + ): + raise RuntimeError("pilot snapshot source is not canonical JSON") + spec = _validate_loaded_pilot_spec( + spec_document, + verify_current_environment=False, + expected_schema=expected_schema, + ) + preflight = _preflight_pilot_snapshot( + source_root_fd, + spec, + run_spec_size=run_spec_original.st_size, + progress_size=progress_original.st_size, + byte_budget=_snapshot_byte_budget, + ) + parent = ( + _default_pilot_snapshot_parent() + if snapshot_parent is None + else snapshot_parent + ) + _preflight_snapshot_capacity(parent, preflight.total_bytes) + if _snapshot_hook is not None: + _snapshot_hook("snapshot-preflighted") + + with _owned_pilot_snapshot_directory( + parent, + run_kind=contract.production_kind, + ) as snapshot_root: + if _snapshot_hook is not None: + _snapshot_hook("snapshot-copy-start") + global_counter = [0] + snapshot_run_spec = snapshot_root / RUN_SPEC_NAME + copied_run_spec_payload = _copy_regular_snapshot_at( + RUN_SPEC_NAME, + source_root_fd, + snapshot_run_spec, + maximum_size=PILOT_RUN_SPEC_MAX_BYTES, + description="pilot run spec snapshot", + global_counter=global_counter, + global_limit=preflight.total_bytes, + retained_source=(run_spec_descriptor, run_spec_original), + ) + if copied_run_spec_payload != run_spec_payload: + raise RuntimeError("pilot run spec changed after snapshot preflight") + if _snapshot_hook is not None: + _snapshot_hook("run-spec-copied") + copied_progress_payload = _copy_regular_snapshot_at( + MERGED_NAME, + source_root_fd, + snapshot_root / MERGED_NAME, + maximum_size=PILOT_PROGRESS_MAX_BYTES, + description="merged pilot progress snapshot", + global_counter=global_counter, + global_limit=preflight.total_bytes, + retained_source=(progress_descriptor, progress_original), + ) + if copied_progress_payload != progress_payload: + raise RuntimeError("pilot progress changed after snapshot preflight") + if _snapshot_hook is not None: + _snapshot_hook("progress-copied") + + cells_descriptor = _open_directory_at("cells", source_root_fd) + try: + with os.scandir(cells_descriptor) as stream: + cell_names = sorted(entry.name for entry in stream) + if len(cell_names) > len(spec["cells"]) + 1: + raise RuntimeError( + "pilot cell directory count exceeds frozen bound" + ) + cells_destination = snapshot_root / "cells" + cells_destination.mkdir() + for name in cell_names: + cell_descriptor = _open_directory_at(name, cells_descriptor) + try: + _copy_cell_tree_snapshot( + cell_descriptor, + cells_destination / name, + remaining_entries=[PILOT_CELL_MAX_ENTRIES], + global_counter=global_counter, + global_limit=preflight.total_bytes, + ) + finally: + os.close(cell_descriptor) + finally: + os.close(cells_descriptor) + if global_counter[0] != preflight.total_bytes: + raise RuntimeError("snapshot byte budget changed during copy") + if _snapshot_hook is not None: + _snapshot_hook("source-copied") + + reconstructed = _merged_document( + snapshot_run_spec, + verify_current_environment=False, + expected_schema=expected_schema, + ) + existing, verified_progress_payload = _read_canonical( + snapshot_root / MERGED_NAME, + "merged pilot progress snapshot", + maximum_size=PILOT_PROGRESS_MAX_BYTES, + ) + if ( + existing != reconstructed + or verified_progress_payload != progress_payload + ): + raise RuntimeError("merged pilot progress snapshot is stale or corrupt") + snapshot = _PilotAnalysisSnapshot( + run_spec_path=snapshot_run_spec, + run_spec_payload=run_spec_payload, + progress_payload=progress_payload, + spec=spec, + progress=reconstructed, + _token=_PILOT_ANALYSIS_SNAPSHOT_TOKEN, + ) + if _snapshot_hook is not None: + _snapshot_hook("snapshot-verified") + try: + yield snapshot + finally: + if _snapshot_hook is not None: + _snapshot_hook("snapshot-closed") + finally: + if progress_descriptor is not None: + os.close(progress_descriptor) + if run_spec_descriptor is not None: + os.close(run_spec_descriptor) + _close_directory_chain(source_chain) + + +@contextmanager +def _open_verified_registered_pilot_analysis_snapshot( + run_spec_path: Path, + *, + snapshot_parent: Path | None = None, + _snapshot_hook: Callable[[str], None] | None = None, + _snapshot_byte_budget: int = PILOT_SNAPSHOT_MAX_BYTES, +) -> Iterator[_PilotAnalysisSnapshot]: + expected_schema = _registered_schema(run_spec_path) + with _open_verified_pilot_analysis_snapshot( + run_spec_path, + production=False, + snapshot_parent=snapshot_parent, + _snapshot_hook=_snapshot_hook, + _snapshot_byte_budget=_snapshot_byte_budget, + _expected_schema=expected_schema, + ) as snapshot: + yield snapshot + + +def _test_source() -> dict[str, object]: + completed = subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=_repo_root(), + check=True, + capture_output=True, + text=True, + ) + return { + "source_revision": completed.stdout.strip(), + "clean_tree": True, + "provenance_error": None, + } + + +def _build_test_pilot_run_spec( + output_root: Path, + *, + lengths: Sequence[int] = PILOT_LENGTHS, + sigmas: Sequence[float] = PILOT_SIGMAS, + replicas: Sequence[int] = PILOT_REPLICAS, + kappas: Sequence[float] = PILOT_KAPPAS, + production: bool = False, +) -> dict[str, object]: + runtime, runtime_hash = _runtime_document() + modules = _scientific_hashes() + correctness = { + "correctness_report_sha256": "1" * 64, + "correctness_run_spec_sha256": "2" * 64, + "correctness_approval_registry_sha256": _approval_registry_digest(), + "validation_source_revision": "b" * 40, + "validated_engine_modules": modules, + "validated_engine_sha256": _aggregate_hash(modules), + "validation_runtime_capability_sha256": "3" * 64, + } + if production: + approval = _load_approval_registry() + correctness.update( + { + "correctness_report_sha256": approval["report_sha256"], + "correctness_run_spec_sha256": approval["run_spec_sha256"], + "validation_source_revision": approval["validation_source_revision"], + "validated_engine_sha256": approval["scientific_engine_sha256"], + } + ) + plan = _solution_root() / "PILOT_PLAN.md" + analysis_hash = ( + _file_hash(plan) + if plan.exists() + else _sha256(_canonical_bytes({"protocol": "P0/P1-test"})) + ) + return _build_document( + lengths=lengths, + sigmas=sigmas, + replicas=replicas, + kappas=kappas, + source=_test_source(), + runtime=runtime, + runtime_sha256=runtime_hash, + correctness=correctness, + waiver_timestamp="2026-07-29T00:00:00Z", + analysis_plan_sha256=analysis_hash, + schema_version=RUN_SPEC_SCHEMA if production else TEST_RUN_SPEC_SCHEMA, + ) + + +def _write_test_pilot_run_spec(output_root: Path, **kwargs: object) -> Path: + document = _build_test_pilot_run_spec(output_root, **kwargs) + path = output_root / RUN_SPEC_NAME + _publish_once(path, document) + return path + + +def _write_test_frozen_pilot_run_spec(output_root: Path) -> Path: + return _write_test_pilot_run_spec(output_root, production=True) + + +def _build_test_extension_run_spec( + output_root: Path, + *, + protocol: Mapping[str, object] | None = None, + tiny: bool = False, +) -> dict[str, object]: + from .pilot_extension import ( + EXTENSION_MASTER_SEED, + EXTENSION_PHASE, + EXTENSION_RUN_SPEC_SCHEMA, + ) + + if tiny: + runtime, runtime_hash = _runtime_document() + modules = _scientific_hashes() + return _build_document( + lengths=(8,), + sigmas=(1.0,), + replicas=(24,), + kappas=(0.0, 0.25), + source=_test_source(), + runtime=runtime, + runtime_sha256=runtime_hash, + correctness={ + "correctness_report_sha256": "1" * 64, + "correctness_run_spec_sha256": "2" * 64, + "correctness_approval_registry_sha256": _approval_registry_digest(), + "validation_source_revision": "b" * 40, + "validated_engine_modules": modules, + "validated_engine_sha256": _aggregate_hash(modules), + "validation_runtime_capability_sha256": "3" * 64, + }, + waiver_timestamp="2026-07-30T00:00:00Z", + analysis_plan_sha256=_analysis_plan_hash(), + schema_version=TEST_EXTENSION_RUN_SPEC_SCHEMA, + master_seed=EXTENSION_MASTER_SEED, + phase=EXTENSION_PHASE, + grid_namespace="pilot-p0-extension-test-v1", + purpose="exploratory-p0-extension-only", + ) + + if protocol is None: + raise RuntimeError("non-tiny extension tests require an explicit protocol") + extension_protocol = protocol + runtime, runtime_hash = _runtime_document() + modules = _scientific_hashes() + approval = _load_approval_registry() + copied = json.loads(_canonical_bytes(extension_protocol)) + cells = copied.pop("cells") + document: dict[str, object] = { + "schema_version": EXTENSION_RUN_SPEC_SCHEMA, + "artifact_root": ".", + "protocol": copied, + "cells": cells, + "cell_count": 96, + "source_extension_protocol_sha256": extension_protocol["protocol_sha256"], + "source_p0_analysis_document_sha256": extension_protocol[ + "source_p0_analysis_document_sha256" + ], + "design_sha256": extension_protocol["design_sha256"], + "correctness_report_sha256": approval["report_sha256"], + "correctness_run_spec_sha256": approval["run_spec_sha256"], + "correctness_approval_registry_sha256": _approval_registry_digest(), + "correctness_approval_revision": CORRECTNESS_APPROVAL_REVISION, + "validation_source_revision": approval["validation_source_revision"], + "validated_engine_modules": modules, + "validated_engine_sha256": approval["scientific_engine_sha256"], + "validation_runtime_capability_sha256": "3" * 64, + "orchestration_revision": _test_source()["source_revision"], + "clean_tree": True, + "uv_lock_sha256": _lock_hash(), + "runtime_capability": runtime, + "runtime_capability_sha256": runtime_hash, + "analysis_plan_sha256": _analysis_plan_hash(), + "rng_assignment_sha256": extension_protocol["rng_assignment_sha256"], + "capability_waiver": { + "reason": "user-waived-after-correctness-gate", + "benchmark_status": "cancelled-without-capability-report", + "utc_timestamp": "2026-07-30T00:00:00Z", + }, + "merged_progress_path": MERGED_NAME, + } + document["run_spec_sha256"] = _document_hash(document, "run_spec_sha256") + _validate_pilot_spec( + document, + contract=_contract_for_schema(EXTENSION_RUN_SPEC_SCHEMA), + ) + return document + + +def _write_test_extension_run_spec( + output_root: Path, + *, + protocol: Mapping[str, object] | None = None, + tiny: bool = False, +) -> Path: + document = _build_test_extension_run_spec( + output_root, + protocol=protocol, + tiny=tiny, + ) + path = output_root / RUN_SPEC_NAME + _publish_once(path, document) + return path + + +def _registered_schema(run_spec_path: Path) -> str: + document, _ = _read_canonical( + run_spec_path, + "registered Pilot run spec", + maximum_size=PILOT_RUN_SPEC_MAX_BYTES, + allow_parent_mutation=True, + ) + return _contract_for_schema(document.get("schema_version")).run_spec_schema + + +def _run_test_registered_pilot_cell( + run_spec_path: Path, + cell_index: int, + *, + crash_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + return _run_cell( + run_spec_path, + cell_index, + verify_current_environment=False, + expected_schema=_registered_schema(run_spec_path), + crash_hook=crash_hook, + ) + + +def _merge_test_registered_pilot_progress( + run_spec_path: Path, output: Path | None = None +) -> dict[str, object]: + document = _merged_document( + run_spec_path, + verify_current_environment=False, + expected_schema=_registered_schema(run_spec_path), + ) + fixed = run_spec_path.parent / MERGED_NAME + if output is not None and output != fixed: + raise RuntimeError("merge output must be the portable run-spec progress path") + _publish_once(fixed, document) + return document + + +def _verify_test_registered_pilot_download( + run_spec_path: Path, +) -> dict[str, object]: + document = _merged_document( + run_spec_path, + verify_current_environment=False, + expected_schema=_registered_schema(run_spec_path), + ) + progress = run_spec_path.parent / MERGED_NAME + if not progress.exists(): + raise RuntimeError("merged pilot progress is missing") + existing, _ = _read_canonical( + progress, + "test merged registered Pilot progress", + maximum_size=PILOT_PROGRESS_MAX_BYTES, + ) + if existing != document: + raise RuntimeError("merged test registered Pilot progress is stale or corrupt") + return document + + +def _pending_test_registered_pilot_cells(run_spec_path: Path) -> list[int]: + return _pending_registered_cells( + run_spec_path, + verify_current_environment=False, + expected_schema=_registered_schema(run_spec_path), + ) + + +def _run_test_pilot_cell( + run_spec_path: Path, + cell_index: int, + *, + crash_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + return _run_cell( + run_spec_path, + cell_index, + verify_current_environment=False, + expected_schema=TEST_RUN_SPEC_SCHEMA, + crash_hook=crash_hook, + ) + + +def _merge_test_pilot_progress( + run_spec_path: Path, output: Path | None = None +) -> dict[str, object]: + document = _merged_document( + run_spec_path, + verify_current_environment=False, + expected_schema=TEST_RUN_SPEC_SCHEMA, + ) + fixed = run_spec_path.parent / MERGED_NAME + if output is not None and output != fixed: + raise RuntimeError("merge output must be the portable run-spec progress path") + _publish_once(fixed, document) + return document + + +def _verify_test_pilot_download(run_spec_path: Path) -> dict[str, object]: + document = _merged_document( + run_spec_path, + verify_current_environment=False, + expected_schema=TEST_RUN_SPEC_SCHEMA, + ) + progress = run_spec_path.parent / MERGED_NAME + if not progress.exists(): + raise RuntimeError("merged pilot progress is missing") + existing, _ = _read_canonical( + progress, + "test merged pilot progress", + maximum_size=PILOT_PROGRESS_MAX_BYTES, + ) + if existing != document: + raise RuntimeError("merged test pilot progress is stale or corrupt") + return document + + +def _pending_test_pilot_cells(run_spec_path: Path) -> list[int]: + spec = _load_pilot_spec( + run_spec_path, + verify_current_environment=False, + expected_schema=TEST_RUN_SPEC_SCHEMA, + ) + root = run_spec_path.parent + pending: list[int] = [] + for raw in spec["cells"]: + cell = PilotCell.from_document(raw) + marker = _relative_path(root, cell.manifest_path, "cells") + if marker.exists(): + _verify_success_cell(root, spec, cell) + else: + _reject_markers(_relative_path(root, cell.cell_path, "cells")) + pending.append(cell.cell_index) + return pending + + +def _test_approval_document( + report_path: Path, + validation_spec_path: Path, + modules: Mapping[str, str], +) -> dict[str, object]: + report, report_payload = _read_canonical( + report_path, "test report", maximum_size=CORRECTNESS_REPORT_MAX_BYTES + ) + spec, spec_payload = _read_canonical( + validation_spec_path, + "test validation spec", + maximum_size=CORRECTNESS_RUN_SPEC_MAX_BYTES, + ) + return { + "schema_version": APPROVAL_SCHEMA, + "approval_revision": CORRECTNESS_APPROVAL_REVISION, + "validation_source_revision": spec["source_revision"], + "report_sha256": _sha256(report_payload), + "run_spec_sha256": _sha256(spec_payload), + "protocol_sha256": str(spec.get("protocol_sha256", "0" * 64)), + "check_registry_sha256": _sha256( + _canonical_bytes(_check_registry_document(spec)) + ), + "check_count": len(report.get("checks", [])), + "cell_count": len(spec.get("cells", [])), + "scientific_engine_sha256": _aggregate_hash(modules), + } diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py new file mode 100644 index 000000000..20e53ab0c --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_analysis.py @@ -0,0 +1,1327 @@ +from __future__ import annotations + +import hashlib +import json +import math +from collections.abc import Callable, Mapping, Sequence +from dataclasses import dataclass +from itertools import pairwise +from pathlib import Path +from types import MappingProxyType +from typing import Never + +import numpy as np + +from . import pilot as _pilot +from .counter_rng import STREAM_COUNT, StreamIdentity, derive_stream_material +from .kernel import periodic_kernel +from .pilot import PilotCell +from .trajectory import TrajectoryRequest, request_digest + +ANALYSIS_SCHEMA = "challenge-194-p0-analysis-v1" +EXTENSION_ANALYSIS_SCHEMA = "challenge-194-p0-extension-analysis-v1" +COMBINED_ANALYSIS_SCHEMA = "challenge-194-p0-combined-analysis-v2" +BRACKET_SCHEMA = "challenge-194-p1-brackets-v1" +COMBINED_BRACKET_SCHEMA = "challenge-194-p1-brackets-v2" +P1_PROTOCOL_SCHEMA = "challenge-194-p1-protocol-v1" +P1_MASTER_SEED = 19_420_261_729 +P1_REPLICAS = tuple(range(8, 24)) +_P0_PRESERVED_WINDOWS: Mapping[str, tuple[str, str]] = MappingProxyType( + { + (0.8).hex(): ( + "0x1.f400000000000p-2", + "0x1.3880000000000p-1", + ), + (1.1).hex(): ( + "0x1.312d000000000p+0", + "0x1.7d78400000000p+0", + ), + } +) +_MISSING_TRUSTED_INPUT = object() +OBSERVABLE_COLUMNS: Mapping[str, int] = MappingProxyType( + { + "s1_fraction": 4, + "s2_fraction": 5, + "q_g": 8, + "four_sector_crossing": 9, + } +) +_OBSERVABLE_INDICES = tuple(OBSERVABLE_COLUMNS.values()) + + +def _canonical_bytes(document: object) -> bytes: + try: + return ( + json.dumps( + document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + + b"\n" + ) + except (TypeError, ValueError) as error: + raise RuntimeError("analysis document is not canonical finite JSON") from error + + +def _sha256(payload: bytes) -> str: + return hashlib.sha256(payload).hexdigest() + + +def _malformed(message: str) -> Never: + raise RuntimeError(message) + + +@dataclass(frozen=True) +class PilotEstimate: + sigma: float + length: int + kappa: float + replica_count: int + means: Mapping[str, float] + standard_errors: Mapping[str, float] + request_sha256: tuple[str, ...] + + def __post_init__(self) -> None: + object.__setattr__(self, "means", MappingProxyType(dict(self.means))) + object.__setattr__( + self, + "standard_errors", + MappingProxyType(dict(self.standard_errors)), + ) + object.__setattr__(self, "request_sha256", tuple(self.request_sha256)) + + def to_document(self) -> dict[str, object]: + return { + "sigma_hex": float(self.sigma).hex(), + "length": self.length, + "kappa_hex": float(self.kappa).hex(), + "replica_count": self.replica_count, + "means": dict(self.means), + "standard_errors": dict(self.standard_errors), + "request_sha256": list(self.request_sha256), + } + + +@dataclass(frozen=True) +class SelectorSigmaEvidence: + sigma: float + lengths: tuple[int, ...] + kappas: tuple[float, ...] + values: Mapping[tuple[float, int, float], tuple[float, float]] + + def __post_init__(self) -> None: + object.__setattr__(self, "lengths", tuple(self.lengths)) + object.__setattr__(self, "kappas", tuple(self.kappas)) + object.__setattr__(self, "values", MappingProxyType(dict(self.values))) + + +def _protocol_axis(protocol: Mapping[str, object], name: str) -> Sequence[object]: + value = protocol.get(name) + if isinstance(value, (str, bytes)) or not isinstance(value, Sequence): + _malformed(f"pilot protocol {name} axis is malformed") + return value + + +def _validated_axes( + spec: Mapping[str, object], +) -> tuple[tuple[float, ...], tuple[int, ...], tuple[int, ...], tuple[float, ...]]: + protocol = spec.get("protocol") + if not isinstance(protocol, Mapping): + _malformed("pilot protocol is missing") + try: + sigmas = tuple( + float.fromhex(str(value)) for value in _protocol_axis(protocol, "sigmas") + ) + lengths = tuple(int(value) for value in _protocol_axis(protocol, "lengths")) + replicas = tuple(int(value) for value in _protocol_axis(protocol, "replicas")) + kappas = tuple( + float.fromhex(str(value)) for value in _protocol_axis(protocol, "kappas") + ) + except (TypeError, ValueError) as error: + raise RuntimeError("pilot protocol axes are malformed") from error + if protocol.get("loop_order") != ["sigma", "length", "replica"]: + raise RuntimeError("pilot protocol loop order is not canonical") + if len(replicas) < 2: + raise RuntimeError( + "missing replicas: sample standard errors require two replicas" + ) + if len(set(replicas)) != len(replicas): + raise RuntimeError("duplicate replicas in pilot protocol") + if ( + not sigmas + or not lengths + or not kappas + or len(set(sigmas)) != len(sigmas) + or len(set(lengths)) != len(lengths) + or len(set(kappas)) != len(kappas) + ): + raise RuntimeError("pilot protocol axes are empty or duplicate") + return sigmas, lengths, replicas, kappas + + +def _validate_cells( + spec: Mapping[str, object], + sigmas: tuple[float, ...], + lengths: tuple[int, ...], + replicas: tuple[int, ...], + kappas: tuple[float, ...], +) -> Sequence[object]: + raw_cells = spec.get("cells") + if isinstance(raw_cells, (str, bytes)) or not isinstance(raw_cells, Sequence): + _malformed("pilot cells are malformed") + expected_count = len(sigmas) * len(lengths) * len(replicas) + if len(raw_cells) != expected_count: + raise RuntimeError("missing replicas: pilot cell cardinality is incomplete") + seen: set[tuple[float, int, int]] = set() + expected_identities = ( + (sigma, length, replica) + for sigma in sigmas + for length in lengths + for replica in replicas + ) + for index, (raw, expected_identity) in enumerate( + zip(raw_cells, expected_identities, strict=True) + ): + if not isinstance(raw, Mapping): + _malformed("pilot cell is malformed") + cell = PilotCell.from_document(raw) + identity = (cell.sigma, cell.length, cell.replica) + if identity in seen: + raise RuntimeError("duplicate replica in pilot cells") + seen.add(identity) + if ( + identity != expected_identity + or cell.cell_index != index + or cell.kappas != kappas + ): + raise RuntimeError("pilot cells are not in canonical protocol order") + return raw_cells + + +def _group_estimates( + sigma: float, + length: int, + kappas: tuple[float, ...], + values: np.ndarray, + request_sha256: tuple[str, ...], +) -> list[PilotEstimate]: + replica_count = values.shape[0] + means = np.mean(values, axis=0) + standard_errors = np.std(values, axis=0, ddof=1) / math.sqrt(replica_count) + estimates: list[PilotEstimate] = [] + names = tuple(OBSERVABLE_COLUMNS) + for kappa_index, kappa in enumerate(kappas): + estimates.append( + PilotEstimate( + sigma=sigma, + length=length, + kappa=kappa, + replica_count=replica_count, + means={ + name: float(means[kappa_index, observable_index]) + for observable_index, name in enumerate(names) + }, + standard_errors={ + name: float(standard_errors[kappa_index, observable_index]) + for observable_index, name in enumerate(names) + }, + request_sha256=request_sha256, + ) + ) + return estimates + + +def _pool_estimates( + left_n: int, + left_mean: float, + left_se: float, + right_n: int, + right_mean: float, + right_se: float, +) -> tuple[int, float, float]: + total = left_n + right_n + delta = right_mean - left_mean + mean = left_mean + delta * right_n / total + left_m2 = (left_n - 1) * left_n * left_se * left_se + right_m2 = (right_n - 1) * right_n * right_se * right_se + pooled_m2 = left_m2 + right_m2 + delta * delta * left_n * right_n / total + sample_variance = pooled_m2 / (total - 1) + standard_error = math.sqrt(sample_variance / total) + if not all(math.isfinite(value) for value in (mean, standard_error)): + raise RuntimeError("combined estimate is nonfinite") + return total, mean, standard_error + + +def _aggregate_p0( + run_spec: Path, + *, + production: bool, + snapshot_parent: Path | None = None, + _snapshot_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + if not isinstance(run_spec, Path) or not run_spec.is_absolute(): + raise RuntimeError("P0 run spec path must be absolute") + with _pilot._open_verified_pilot_analysis_snapshot( + run_spec, + production=production, + snapshot_parent=snapshot_parent, + _snapshot_hook=_snapshot_hook, + ) as snapshot: + spec = snapshot.spec + sigmas, lengths, replicas, kappas = _validated_axes(spec) + raw_cells = _validate_cells(spec, sigmas, lengths, replicas, kappas) + + estimates: list[dict[str, object]] = [] + cell_index = 0 + for sigma in sigmas: + for length in lengths: + values = np.empty( + (len(replicas), len(kappas), len(OBSERVABLE_COLUMNS)), + dtype=np.float64, + ) + request_hashes: list[str] = [] + for _replica in replicas: + raw_cell = raw_cells[cell_index] + if not isinstance(raw_cell, Mapping): # validated above + _malformed("pilot cell is malformed") + cell = PilotCell.from_document(raw_cell) + result = snapshot.load_trajectory(cell_index) + if ( + result.observables.shape != (len(kappas), 10) + or not np.isfinite(result.observables).all() + ): + raise RuntimeError( + "verified trajectory observables are malformed" + ) + values[len(request_hashes), :, :] = result.observables[ + :, _OBSERVABLE_INDICES + ] + request_hashes.append(cell.request_sha256) + cell_index += 1 + del result + estimates.extend( + estimate.to_document() + for estimate in _group_estimates( + sigma, + length, + kappas, + values, + tuple(request_hashes), + ) + ) + + document: dict[str, object] = { + "schema_version": ANALYSIS_SCHEMA, + "p0_run_spec_sha256": _sha256(snapshot.run_spec_payload), + "p0_progress_sha256": _sha256(snapshot.progress_payload), + "source_revision": spec["orchestration_revision"], + "analysis_plan_sha256": spec["analysis_plan_sha256"], + "observable_columns": dict(OBSERVABLE_COLUMNS), + "estimates": estimates, + } + document["analysis_document_sha256"] = _sha256(_canonical_bytes(document)) + return document + + +def aggregate_p0( + run_spec: Path, + *, + snapshot_parent: Path | None = None, +) -> dict[str, object]: + return _aggregate_p0( + run_spec, + production=True, + snapshot_parent=snapshot_parent, + ) + + +def _aggregate_test_p0( + run_spec: Path, + *, + snapshot_parent: Path | None = None, + _snapshot_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + return _aggregate_p0( + run_spec, + production=False, + snapshot_parent=snapshot_parent, + _snapshot_hook=_snapshot_hook, + ) + + +def _validated_extension_axes( + protocol: Mapping[str, object], + *, + production: bool, +) -> tuple[ + tuple[float, ...], tuple[int, ...], tuple[int, ...], tuple[tuple[float, ...], ...] +]: + digest = protocol.get("protocol_sha256") + if not isinstance(digest, str): + _malformed("extension protocol digest is malformed") + unsigned = dict(protocol) + unsigned.pop("protocol_sha256", None) + if _sha256(_canonical_bytes(unsigned)) != digest: + raise RuntimeError("extension protocol hash mismatch") + if protocol.get("loop_order") != ["sigma", "length", "replica"]: + raise RuntimeError("extension protocol loop order is not canonical") + try: + lengths = tuple(int(value) for value in _protocol_axis(protocol, "lengths")) + replicas = tuple(int(value) for value in _protocol_axis(protocol, "replicas")) + except (TypeError, ValueError) as error: + raise RuntimeError("extension protocol axes are malformed") from error + raw_entries = protocol.get("sigma_entries") + if isinstance(raw_entries, (str, bytes)) or not isinstance(raw_entries, Sequence): + _malformed("extension sigma entries are malformed") + sigmas: list[float] = [] + grids: list[tuple[float, ...]] = [] + for raw in raw_entries: + if not isinstance(raw, Mapping): + _malformed("extension sigma entry is malformed") + try: + sigma = float.fromhex(str(raw.get("sigma_hex"))) + kappas = tuple( + float.fromhex(str(value)) for value in _protocol_axis(raw, "kappas") + ) + except (TypeError, ValueError) as error: + raise RuntimeError("extension sigma grid is malformed") from error + if ( + not math.isfinite(sigma) + or sigma.hex() != raw.get("sigma_hex") + or not kappas + or any(not math.isfinite(value) for value in kappas) + or [value.hex() for value in kappas] != raw.get("kappas") + or len(set(kappas)) != len(kappas) + ): + raise RuntimeError("extension sigma grid is not canonical finite binary64") + sigmas.append(sigma) + grids.append(kappas) + if ( + not lengths + or len(set(lengths)) != len(lengths) + or len(replicas) < 2 + or len(set(replicas)) != len(replicas) + or not sigmas + or len(set(sigmas)) != len(sigmas) + ): + raise RuntimeError("extension protocol axes are empty or duplicate") + if production and ( + len(sigmas) != 2 + or len(lengths) != 3 + or len(replicas) != 16 + or any(len(grid) != 17 for grid in grids) + ): + raise RuntimeError("extension production cardinality is not exactly 2x3x16x17") + return tuple(sigmas), lengths, replicas, tuple(grids) + + +def _aggregate_p0_extension( + run_spec: Path, + protocol: Mapping[str, object], + *, + production: bool, + snapshot_parent: Path | None = None, + _snapshot_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + if not isinstance(run_spec, Path) or not run_spec.is_absolute(): + raise RuntimeError("P0 extension run spec path must be absolute") + if not isinstance(protocol, Mapping): + _malformed("extension protocol is malformed") + sigmas, lengths, replicas, grids = _validated_extension_axes( + protocol, + production=production, + ) + expected_schema = ( + _pilot.EXTENSION_CONTRACT.run_spec_schema + if production + else _pilot.TEST_EXTENSION_RUN_SPEC_SCHEMA + ) + with _pilot._open_verified_pilot_analysis_snapshot( + run_spec, + production=production, + snapshot_parent=snapshot_parent, + _snapshot_hook=_snapshot_hook, + _expected_schema=expected_schema, + ) as snapshot: + spec = snapshot.spec + source_protocol_sha256 = protocol["protocol_sha256"] + if ( + production + and spec.get("source_extension_protocol_sha256") != source_protocol_sha256 + ): + raise RuntimeError("extension run spec is not bound to the protocol") + raw_cells = spec.get("cells") + expected_cell_count = len(sigmas) * len(lengths) * len(replicas) + if ( + not isinstance(raw_cells, Sequence) + or isinstance(raw_cells, (str, bytes)) + or len(raw_cells) != expected_cell_count + ): + raise RuntimeError("extension cell cardinality is incomplete") + + estimates: list[dict[str, object]] = [] + cell_index = 0 + for sigma, kappas in zip(sigmas, grids, strict=True): + for length in lengths: + values = np.empty( + (len(replicas), len(kappas), len(OBSERVABLE_COLUMNS)), + dtype=np.float64, + ) + request_hashes: list[str] = [] + for replica_index, replica in enumerate(replicas): + raw_cell = raw_cells[cell_index] + if not isinstance(raw_cell, Mapping): + _malformed("extension cell is malformed") + cell = PilotCell.from_document(raw_cell) + if ( + cell.cell_index != cell_index + or (cell.sigma, cell.length, cell.replica) + != (sigma, length, replica) + or cell.kappas != kappas + ): + raise RuntimeError( + "extension cells are not in canonical protocol order" + ) + result = snapshot.load_trajectory(cell_index) + if ( + result.observables.shape != (len(kappas), 10) + or not np.isfinite(result.observables).all() + ): + raise RuntimeError( + "verified extension trajectory observables are malformed" + ) + values[replica_index, :, :] = result.observables[ + :, _OBSERVABLE_INDICES + ] + request_hashes.append(cell.request_sha256) + cell_index += 1 + del result + grouped = _group_estimates( + sigma, + length, + kappas, + values, + tuple(request_hashes), + ) + estimates.extend(estimate.to_document() for estimate in grouped) + del grouped + del values + + expected_estimates = sum(len(grid) for grid in grids) * len(lengths) + if len(estimates) != expected_estimates or ( + production and len(estimates) != 102 + ): + raise RuntimeError("extension estimate cardinality is invalid") + document: dict[str, object] = { + "schema_version": EXTENSION_ANALYSIS_SCHEMA, + "source_extension_protocol_sha256": source_protocol_sha256, + "extension_run_spec_sha256": _sha256(snapshot.run_spec_payload), + "extension_progress_sha256": _sha256(snapshot.progress_payload), + "source_revision": spec["orchestration_revision"], + "analysis_plan_sha256": spec["analysis_plan_sha256"], + "observable_columns": dict(OBSERVABLE_COLUMNS), + "estimates": estimates, + } + document["analysis_document_sha256"] = _sha256(_canonical_bytes(document)) + return document + + +def aggregate_p0_extension( + run_spec: Path, + protocol: Mapping[str, object], +) -> dict[str, object]: + return _aggregate_p0_extension( + run_spec, + protocol, + production=True, + ) + + +def _aggregate_test_p0_extension( + run_spec: Path, + protocol: Mapping[str, object], + *, + snapshot_parent: Path | None = None, + _snapshot_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + return _aggregate_p0_extension( + run_spec, + protocol, + production=False, + snapshot_parent=snapshot_parent, + _snapshot_hook=_snapshot_hook, + ) + + +def _exact_hex_value(raw: object, name: str) -> float: + try: + value = float.fromhex(str(raw)) + except (TypeError, ValueError) as error: + raise RuntimeError(f"analysis {name} is malformed") from error + if not math.isfinite(value) or value.hex() != raw: + raise RuntimeError(f"analysis {name} is not canonical finite binary64") + return value + + +def _selector_estimates( + analysis: Mapping[str, object], +) -> tuple[ + tuple[float, ...], + tuple[int, ...], + tuple[float, ...], + dict[tuple[float, int, float], tuple[float, float]], +]: + if analysis.get("schema_version") != ANALYSIS_SCHEMA: + raise RuntimeError("analysis schema version is not supported") + raw_estimates = analysis.get("estimates") + if isinstance(raw_estimates, (str, bytes)) or not isinstance( + raw_estimates, Sequence + ): + _malformed("analysis estimates are malformed") + + identities: list[tuple[float, int, float]] = [] + values: dict[tuple[float, int, float], tuple[float, float]] = {} + sigmas: list[float] = [] + lengths: list[int] = [] + kappas: list[float] = [] + for raw in raw_estimates: + if not isinstance(raw, Mapping): + _malformed("analysis estimate is malformed") + sigma = _exact_hex_value(raw.get("sigma_hex"), "sigma") + kappa = _exact_hex_value(raw.get("kappa_hex"), "coupling") + length = raw.get("length") + if not isinstance(length, int) or isinstance(length, bool) or length <= 0: + _malformed("analysis length is malformed") + means = raw.get("means") + if not isinstance(means, Mapping): + _malformed("analysis estimate means are malformed") + observables: list[float] = [] + for name in ("q_g", "four_sector_crossing"): + value = means.get(name) + if not isinstance(value, (int, float)) or isinstance(value, bool): + _malformed(f"analysis {name} mean is malformed") + value = float(value) + if not math.isfinite(value): + raise RuntimeError("analysis estimator means must be finite") + observables.append(value) + identity = (sigma, length, kappa) + if identity in values: + raise RuntimeError("analysis contains duplicate estimates") + identities.append(identity) + values[identity] = (observables[0], observables[1]) + if sigma not in sigmas: + sigmas.append(sigma) + if length not in lengths: + lengths.append(length) + if kappa not in kappas: + kappas.append(kappa) + + if len(lengths) < 2: + raise RuntimeError("analysis lacks two largest sizes") + if ( + not kappas + or kappas[0] != 0.0 + or any(right <= left for left, right in pairwise(kappas)) + ): + raise RuntimeError("analysis estimates are not in canonical coupling order") + expected = [ + (sigma, length, kappa) + for sigma in sigmas + for length in lengths + for kappa in kappas + ] + if identities != expected: + if len(identities) != len(expected): + raise RuntimeError("analysis is missing largest-size estimates") + raise RuntimeError("analysis estimates are not in canonical coupling order") + + digest = analysis.get("analysis_document_sha256") + if not isinstance(digest, str): + _malformed("analysis document digest is malformed") + unsigned = dict(analysis) + unsigned.pop("analysis_document_sha256", None) + if _sha256(_canonical_bytes(unsigned)) != digest: + raise RuntimeError("analysis document digest mismatch") + return tuple(sigmas), tuple(lengths), tuple(kappas), values + + +def _selector_v1_evidence( + analysis: Mapping[str, object], +) -> tuple[SelectorSigmaEvidence, ...]: + sigmas, lengths, kappas, values = _selector_estimates(analysis) + return tuple( + SelectorSigmaEvidence( + sigma=sigma, + lengths=lengths, + kappas=kappas, + values=values, + ) + for sigma in sigmas + ) + + +def _selector_v2_evidence( + analysis: Mapping[str, object], +) -> tuple[SelectorSigmaEvidence, ...]: + raw_entries = analysis.get("sigma_entries") + if isinstance(raw_entries, (str, bytes)) or not isinstance(raw_entries, Sequence): + _malformed("combined analysis sigma entries are malformed") + evidence: list[SelectorSigmaEvidence] = [] + estimate_count = 0 + for raw_entry in raw_entries: + if not isinstance(raw_entry, Mapping): + _malformed("combined analysis sigma entry is malformed") + sigma = _exact_hex_value(raw_entry.get("sigma_hex"), "sigma") + raw_lengths = raw_entry.get("lengths") + raw_kappas = raw_entry.get("kappas") + raw_estimates = raw_entry.get("estimates") + if ( + isinstance(raw_lengths, (str, bytes)) + or not isinstance(raw_lengths, Sequence) + or isinstance(raw_kappas, (str, bytes)) + or not isinstance(raw_kappas, Sequence) + or isinstance(raw_estimates, (str, bytes)) + or not isinstance(raw_estimates, Sequence) + ): + _malformed("combined analysis per-sigma evidence is malformed") + if any( + not isinstance(value, int) or isinstance(value, bool) or value <= 0 + for value in raw_lengths + ): + _malformed("combined analysis length axis is malformed") + lengths = tuple(raw_lengths) + kappas = tuple(_exact_hex_value(value, "coupling") for value in raw_kappas) + if ( + len(lengths) < 2 + or len(set(lengths)) != len(lengths) + or any(right <= left for left, right in pairwise(lengths)) + or not kappas + or kappas[0] != 0.0 + or len(set(kappas)) != len(kappas) + or any(right <= left for left, right in pairwise(kappas)) + ): + raise RuntimeError("combined analysis axes are not canonical") + + identities: list[tuple[float, int, float]] = [] + values: dict[tuple[float, int, float], tuple[float, float]] = {} + for raw in raw_estimates: + if not isinstance(raw, Mapping): + _malformed("combined analysis estimate is malformed") + row_sigma = _exact_hex_value(raw.get("sigma_hex"), "sigma") + kappa = _exact_hex_value(raw.get("kappa_hex"), "coupling") + length = raw.get("length") + if ( + row_sigma != sigma + or not isinstance(length, int) + or isinstance(length, bool) + or length <= 0 + ): + _malformed("combined analysis estimate identity is malformed") + means = raw.get("means") + if not isinstance(means, Mapping): + _malformed("combined analysis estimate means are malformed") + observables: list[float] = [] + for name in ("q_g", "four_sector_crossing"): + value = means.get(name) + if not isinstance(value, (int, float)) or isinstance(value, bool): + _malformed(f"combined analysis {name} mean is malformed") + finite_value = float(value) + if not math.isfinite(finite_value): + raise RuntimeError( + "combined analysis estimator means must be finite" + ) + observables.append(finite_value) + identity = (sigma, length, kappa) + if identity in values: + raise RuntimeError("combined analysis contains duplicate estimates") + identities.append(identity) + values[identity] = (observables[0], observables[1]) + expected = [(sigma, length, kappa) for length in lengths for kappa in kappas] + if identities != expected: + if len(identities) != len(expected): + raise RuntimeError( + "combined analysis is missing largest-size estimates" + ) + raise RuntimeError( + "combined analysis estimates are not in canonical coupling order" + ) + evidence.append( + SelectorSigmaEvidence( + sigma=sigma, + lengths=lengths, + kappas=kappas, + values=values, + ) + ) + estimate_count += len(raw_estimates) + + sigmas = tuple(item.sigma for item in evidence) + if ( + not sigmas + or len(set(sigmas)) != len(sigmas) + or any(right <= left for left, right in pairwise(sigmas)) + ): + raise RuntimeError("combined analysis sigma entries are not canonical") + raw_count = analysis.get("estimate_count") + if ( + not isinstance(raw_count, int) + or isinstance(raw_count, bool) + or raw_count != estimate_count + ): + raise RuntimeError("combined analysis estimate cardinality is invalid") + digest = analysis.get("analysis_document_sha256") + if not isinstance(digest, str): + _malformed("combined analysis document digest is malformed") + unsigned = dict(analysis) + unsigned.pop("analysis_document_sha256", None) + if _sha256(_canonical_bytes(unsigned)) != digest: + raise RuntimeError("combined analysis document digest mismatch") + return tuple(evidence) + + +def _selector_sigma_evidence( + analysis: Mapping[str, object], +) -> tuple[SelectorSigmaEvidence, ...]: + if analysis.get("schema_version") == ANALYSIS_SCHEMA: + return _selector_v1_evidence(analysis) + if analysis.get("schema_version") == COMBINED_ANALYSIS_SCHEMA: + return _selector_v2_evidence(analysis) + raise RuntimeError("analysis schema version is not supported") + + +def _validated_selector_sigma_evidence( + analysis: Mapping[str, object], + *, + p0_analysis: Mapping[str, object] | None, + extension_analysis: Mapping[str, object] | None, + p0_evidence_root: Path | object, + extension_run_spec: Path | object, + extension_protocol: Mapping[str, object] | object, +) -> tuple[SelectorSigmaEvidence, ...]: + if analysis.get("schema_version") == COMBINED_ANALYSIS_SCHEMA: + if not isinstance(p0_analysis, Mapping) or not isinstance( + extension_analysis, Mapping + ): + raise RuntimeError( + "combined analysis source validation requires exact P0 and " + "extension analyses" + ) + if ( + not isinstance(p0_evidence_root, Path) + or not isinstance(extension_run_spec, Path) + or not isinstance(extension_protocol, Mapping) + ): + raise TypeError( + "combined analysis requires p0_evidence_root, " + "extension_run_spec, and extension_protocol" + ) + from . import pilot_extension + + pilot_extension.validate_combined_p0_evidence( + p0_analysis, + extension_analysis, + analysis, + p0_evidence_root=p0_evidence_root, + extension_run_spec=extension_run_spec, + extension_protocol=extension_protocol, + ) + return _selector_sigma_evidence(analysis) + + +def _sign_change(left: float, right: float) -> bool: + return (left <= 0.0 <= right) or (right <= 0.0 <= left) + + +def _transition_evidence( + sigma: float, + lengths: tuple[int, int], + kappas: tuple[float, ...], + values: Mapping[tuple[float, int, float], tuple[float, float]], + interval_index: int, +) -> tuple[bool, bool, dict[str, object]]: + lower = kappas[interval_index] + upper = kappas[interval_index + 1] + q_endpoints: list[list[str]] = [] + crossing_endpoints: list[list[str]] = [] + q_differences: list[float] = [] + crossing_marked = False + for kappa in (lower, upper): + small = values[(sigma, lengths[0], kappa)] + large = values[(sigma, lengths[1], kappa)] + q_differences.append(small[0] - large[0]) + q_endpoints.append([small[0].hex(), large[0].hex()]) + crossing_endpoints.append([small[1].hex(), large[1].hex()]) + for length_index in range(2): + endpoints = ( + float.fromhex(crossing_endpoints[0][length_index]), + float.fromhex(crossing_endpoints[1][length_index]), + ) + crossing_marked |= min(endpoints) <= 0.25 and max(endpoints) >= 0.75 + q_marked = _sign_change(q_differences[0], q_differences[1]) + return ( + q_marked, + crossing_marked, + { + "q_g": { + "marked": q_marked, + "largest_size_difference_hex": [value.hex() for value in q_differences], + "endpoint_means_hex": q_endpoints, + }, + "four_sector_crossing": { + "marked": crossing_marked, + "closed_target_range_hex": [(0.25).hex(), (0.75).hex()], + "endpoint_means_hex": crossing_endpoints, + }, + }, + ) + + +def _select_transition_bracket( + sigma: float, + lengths: tuple[int, int], + kappas: tuple[float, ...], + values: Mapping[tuple[float, int, float], tuple[float, float]], +) -> dict[str, object]: + candidates: list[tuple[float, float, int, dict[str, object]]] = [] + zero_interval_is_common = False + for interval_index in range(len(kappas) - 1): + q_marked, crossing_marked, evidence = _transition_evidence( + sigma, lengths, kappas, values, interval_index + ) + if not (q_marked and crossing_marked): + continue + if kappas[interval_index] == 0.0: + zero_interval_is_common = True + continue + candidates.append( + ( + kappas[interval_index + 1] - kappas[interval_index], + kappas[interval_index], + interval_index, + evidence, + ) + ) + if not candidates: + if zero_interval_is_common: + raise RuntimeError("zero-coupling interval cannot be selected") + return { + "sigma_hex": sigma.hex(), + "status": "requires_p0_extension", + "reason": "no_nonzero_interval_marked_by_both_estimators", + "lengths": list(lengths), + } + width, lower, interval_index, evidence = min( + candidates, key=lambda candidate: (candidate[0], candidate[1]) + ) + return { + "sigma_hex": sigma.hex(), + "status": "selected", + "purpose": "transition_refinement", + "lower_kappa_hex": lower.hex(), + "upper_kappa_hex": kappas[interval_index + 1].hex(), + "lengths": list(lengths), + "estimator_evidence": evidence, + "tie_break": { + "rule": "narrowest_interval_then_lower_coupling", + "candidate_count": len(candidates), + "selected_width_hex": width.hex(), + }, + } + + +def _select_crossover_bracket( + sigma: float, + lengths: tuple[int, int], + kappas: tuple[float, ...], + values: Mapping[tuple[float, int, float], tuple[float, float]], +) -> dict[str, object]: + largest = lengths[1] + candidates: list[tuple[float, float, int, float, float]] = [] + for interval_index in range(1, len(kappas) - 1): + lower = kappas[interval_index] + upper = kappas[interval_index + 1] + left = values[(sigma, largest, lower)][1] + right = values[(sigma, largest, upper)][1] + slope = abs(right - left) / (upper - lower) + candidates.append((-slope, lower, interval_index, left, right)) + if not candidates: + raise RuntimeError("no nonzero crossover interval is available") + negative_slope, lower, interval_index, left, right = min(candidates) + slope = -negative_slope + return { + "sigma_hex": sigma.hex(), + "status": "selected", + "purpose": "crossover_refinement", + "lower_kappa_hex": lower.hex(), + "upper_kappa_hex": kappas[interval_index + 1].hex(), + "lengths": list(lengths), + "estimator_evidence": { + "estimator": "largest_size_four_sector_crossing", + "largest_length": largest, + "endpoint_means_hex": [left.hex(), right.hex()], + "absolute_slope_hex": slope.hex(), + }, + "tie_break": { + "rule": "maximum_absolute_slope_then_lower_coupling", + "candidate_count": len(candidates), + }, + } + + +def _select_p1_brackets_from_evidence( + analysis: Mapping[str, object], + sigma_evidence: tuple[SelectorSigmaEvidence, ...], +) -> dict[str, object]: + brackets: list[dict[str, object]] = [] + for evidence in sigma_evidence: + lengths = (evidence.lengths[-2], evidence.lengths[-1]) + brackets.append( + _select_transition_bracket( + evidence.sigma, + lengths, + evidence.kappas, + evidence.values, + ) + if evidence.sigma <= 1.0 + else _select_crossover_bracket( + evidence.sigma, + lengths, + evidence.kappas, + evidence.values, + ) + ) + document: dict[str, object] = { + "schema_version": ( + BRACKET_SCHEMA + if analysis.get("schema_version") == ANALYSIS_SCHEMA + else COMBINED_BRACKET_SCHEMA + ), + "source_analysis_document_sha256": analysis["analysis_document_sha256"], + "requires_p0_extension": any( + bracket["status"] == "requires_p0_extension" for bracket in brackets + ), + "brackets": brackets, + } + document["bracket_document_sha256"] = _sha256(_canonical_bytes(document)) + return document + + +def select_p1_brackets( + analysis: Mapping[str, object], + *, + p0_analysis: Mapping[str, object] | None = None, + extension_analysis: Mapping[str, object] | None = None, + p0_evidence_root: Path | object = _MISSING_TRUSTED_INPUT, + extension_run_spec: Path | object = _MISSING_TRUSTED_INPUT, + extension_protocol: Mapping[str, object] | object = _MISSING_TRUSTED_INPUT, +) -> dict[str, object]: + if not isinstance(analysis, Mapping): + _malformed("analysis document is malformed") + sigma_evidence = _validated_selector_sigma_evidence( + analysis, + p0_analysis=p0_analysis, + extension_analysis=extension_analysis, + p0_evidence_root=p0_evidence_root, + extension_run_spec=extension_run_spec, + extension_protocol=extension_protocol, + ) + return _select_p1_brackets_from_evidence(analysis, sigma_evidence) + + +def _validate_bracket_document( + analysis: Mapping[str, object], + brackets: Mapping[str, object], +) -> Sequence[object]: + combined = analysis.get("schema_version") == COMBINED_ANALYSIS_SCHEMA + expected_schema = COMBINED_BRACKET_SCHEMA if combined else BRACKET_SCHEMA + if brackets.get("schema_version") != expected_schema: + raise RuntimeError("bracket schema version is not supported") + if brackets.get("source_analysis_document_sha256") != analysis.get( + "analysis_document_sha256" + ): + raise RuntimeError("bracket document is not bound to the analysis") + digest = brackets.get("bracket_document_sha256") + if not isinstance(digest, str): + _malformed("bracket document digest is malformed") + unsigned = dict(brackets) + unsigned.pop("bracket_document_sha256", None) + if _sha256(_canonical_bytes(unsigned)) != digest: + raise RuntimeError("bracket document digest mismatch") + raw = brackets.get("brackets") + if isinstance(raw, (str, bytes)) or not isinstance(raw, Sequence): + _malformed("bracket entries are malformed") + extension_sigmas = [ + str(entry.get("sigma_hex")) + for entry in raw + if isinstance(entry, Mapping) and entry.get("status") == "requires_p0_extension" + ] + if brackets.get("requires_p0_extension") is True or extension_sigmas: + labels = ", ".join(str(float.fromhex(value)) for value in extension_sigmas) + raise RuntimeError(f"P0 extension required before P1 publication: {labels}") + if combined and ( + brackets.get("requires_p0_extension") is not False + or any( + not isinstance(entry, Mapping) or entry.get("status") != "selected" + for entry in raw + ) + ): + raise RuntimeError("P1 requires all four combined statuses selected") + return raw + + +def _recursive_binary64_grid(lower: float, upper: float) -> tuple[float, ...]: + if ( + not math.isfinite(lower) + or not math.isfinite(upper) + or lower <= 0.0 + or upper <= lower + ): + raise RuntimeError("P1 bracket endpoints are invalid") + points = [lower, upper] + for _level in range(3): + previous = sorted(points) + points.extend(left + (right - left) / 2.0 for left, right in pairwise(previous)) + ordered = tuple(sorted({value.hex(): value for value in points}.values())) + if ( + len(ordered) != 9 + or ordered[0] != lower + or ordered[-1] != upper + or any(right <= left for left, right in pairwise(ordered)) + ): + raise RuntimeError("P1 bracket cannot produce nine unique binary64 points") + return ordered + + +def _p1_stream_hashes( + length: int, + sigma_grid_id: str, + replica: int, +) -> tuple[str, ...]: + return tuple( + derive_stream_material( + StreamIdentity( + master_seed=P1_MASTER_SEED, + phase="pilot", + length=length, + sigma_grid_id=sigma_grid_id, + replica=replica, + stream_id=stream, + ) + ).material_sha256 + for stream in range(STREAM_COUNT) + ) + + +def build_p1_protocol( + analysis: Mapping[str, object], + brackets: Mapping[str, object] | None = None, + *, + p0_analysis: Mapping[str, object] | None = None, + extension_analysis: Mapping[str, object] | None = None, + p0_evidence_root: Path | object = _MISSING_TRUSTED_INPUT, + extension_run_spec: Path | object = _MISSING_TRUSTED_INPUT, + extension_protocol: Mapping[str, object] | object = _MISSING_TRUSTED_INPUT, +) -> dict[str, object]: + sigma_evidence = _validated_selector_sigma_evidence( + analysis, + p0_analysis=p0_analysis, + extension_analysis=extension_analysis, + p0_evidence_root=p0_evidence_root, + extension_run_spec=extension_run_spec, + extension_protocol=extension_protocol, + ) + sigmas = tuple(evidence.sigma for evidence in sigma_evidence) + lengths = sigma_evidence[0].lengths + if ( + len(sigmas) != 4 + or len(lengths) != 3 + or any(evidence.lengths != lengths for evidence in sigma_evidence) + or ( + analysis.get("schema_version") == COMBINED_ANALYSIS_SCHEMA + and sigmas != (0.8, 0.9, 1.0, 1.1) + ) + ): + raise RuntimeError("P1 requires exactly four sigmas and three lengths") + selected_brackets = _select_p1_brackets_from_evidence(analysis, sigma_evidence) + bracket_document = selected_brackets if brackets is None else brackets + raw_brackets = _validate_bracket_document(analysis, bracket_document) + if len(raw_brackets) != len(sigmas): + raise RuntimeError("P1 requires one bracket per sigma") + if analysis.get("schema_version") == COMBINED_ANALYSIS_SCHEMA: + if _canonical_bytes(bracket_document) != _canonical_bytes(selected_brackets): + raise RuntimeError( + "combined bracket document does not match frozen selector output" + ) + for index, sigma in ((0, 0.8), (3, 1.1)): + raw = raw_brackets[index] + if not isinstance(raw, Mapping): + _malformed("combined control bracket is malformed") + lower, upper = _P0_PRESERVED_WINDOWS[sigma.hex()] + if ( + raw.get("sigma_hex") != sigma.hex() + or raw.get("lower_kappa_hex") != lower + or raw.get("upper_kappa_hex") != upper + ): + raise RuntimeError( + "combined sigma 0.8 and 1.1 windows are not preserved" + ) + + sigma_entries: list[dict[str, object]] = [] + grids: dict[float, tuple[float, ...]] = {} + grid_ids: dict[float, str] = {} + for sigma, raw in zip(sigmas, raw_brackets, strict=True): + if not isinstance(raw, Mapping): + _malformed("bracket entry is malformed") + raw_sigma = _exact_hex_value(raw.get("sigma_hex"), "bracket sigma") + if raw_sigma != sigma or raw.get("status") != "selected": + raise RuntimeError("bracket entries are not in canonical sigma order") + lower = _exact_hex_value(raw.get("lower_kappa_hex"), "lower bracket") + upper = _exact_hex_value(raw.get("upper_kappa_hex"), "upper bracket") + grid = _recursive_binary64_grid(lower, upper) + grid_id = ( + f"pilot-p1-v1|sigma-f64={sigma.hex()}|" + f"analysis={analysis['analysis_document_sha256']}" + ) + grids[sigma] = grid + grid_ids[sigma] = grid_id + sigma_entries.append( + { + "sigma_hex": sigma.hex(), + "purpose": raw.get("purpose"), + "lower_kappa_hex": lower.hex(), + "upper_kappa_hex": upper.hex(), + "kappas": [value.hex() for value in grid], + "sigma_grid_id": grid_id, + } + ) + + cells: list[dict[str, object]] = [] + assignments: list[dict[str, object]] = [] + request_ids: set[str] = set() + stream_ids: set[str] = set() + for sigma in sigmas: + grid = grids[sigma] + grid_id = grid_ids[sigma] + for length in lengths: + kernel = periodic_kernel(length, sigma) + kernel_sha256 = _sha256(kernel.astype(" None: + _selector_sigma_evidence(analysis) + if protocol.get("schema_version") != P1_PROTOCOL_SCHEMA: + raise RuntimeError("P1 protocol schema version is not supported") + if protocol.get("source_analysis_document_sha256") != analysis.get( + "analysis_document_sha256" + ): + raise RuntimeError("P1 protocol is not bound to the analysis") + digest = protocol.get("protocol_sha256") + if not isinstance(digest, str): + _malformed("P1 protocol digest is malformed") + unsigned = dict(protocol) + unsigned.pop("protocol_sha256", None) + if _sha256(_canonical_bytes(unsigned)) != digest: + raise RuntimeError("P1 protocol digest mismatch") + cells = protocol.get("cells") + if ( + not isinstance(cells, Sequence) + or isinstance(cells, (str, bytes)) + or len(cells) != 4 * 3 * len(P1_REPLICAS) + or protocol.get("cell_count") != len(cells) + ): + raise RuntimeError("P1 protocol cell cardinality is invalid") + request_ids: set[str] = set() + stream_ids: set[str] = set() + for index, raw in enumerate(cells): + if not isinstance(raw, Mapping) or raw.get("cell_index") != index: + raise RuntimeError("P1 cells are not in canonical order") + request_id = raw.get("request_sha256") + streams = raw.get("rng_material_sha256") + if ( + not isinstance(request_id, str) + or request_id in request_ids + or not isinstance(streams, Sequence) + or isinstance(streams, (str, bytes)) + or len(streams) != STREAM_COUNT + or any(not isinstance(value, str) for value in streams) + or any(value in stream_ids for value in streams) + ): + raise RuntimeError("P1 request or RNG identities are invalid") + request_ids.add(request_id) + stream_ids.update(streams) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py new file mode 100644 index 000000000..de0c71542 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/pilot_extension.py @@ -0,0 +1,1410 @@ +from __future__ import annotations + +import hashlib +import json +import math +import os +import subprocess +from collections.abc import Mapping, Sequence +from itertools import pairwise +from pathlib import Path +from types import MappingProxyType +from typing import Never + +import numpy as np + +from .counter_rng import STREAM_COUNT, StreamIdentity, derive_stream_material +from .kernel import periodic_kernel +from .pilot import ( + PILOT_KAPPAS, + PILOT_LENGTHS, + PILOT_MASTER_SEED, + PILOT_PROGRESS_MAX_BYTES, + PILOT_REPLICAS, + PILOT_RUN_SPEC_MAX_BYTES, + PILOT_SIGMAS, + _close_directory_chain, + _file_hash, + _open_directory_chain, + _open_regular_at, + _read_canonical, + _read_descriptor_bounded, + _require_directory_chain, + _require_regular_at_identity, +) +from .pilot_analysis import ( + ANALYSIS_SCHEMA, + OBSERVABLE_COLUMNS, + P1_MASTER_SEED, + P1_REPLICAS, + _pool_estimates, + _selector_estimates, + _transition_evidence, + select_p1_brackets, +) +from .trajectory import TrajectoryRequest, request_digest + +EXTENSION_PROTOCOL_SCHEMA = "challenge-194-p0-extension-protocol-v1" +EXTENSION_RUN_SPEC_SCHEMA = "challenge-194-p0-extension-run-spec-v1" +EXTENSION_PROGRESS_SCHEMA = "challenge-194-p0-extension-progress-v1" +EXTENSION_ANALYSIS_SCHEMA = "challenge-194-p0-extension-analysis-v1" +COMBINED_ANALYSIS_SCHEMA = "challenge-194-p0-combined-analysis-v2" +COMBINED_BRACKET_SCHEMA = "challenge-194-p1-brackets-v2" +EXTENSION_SIGMAS = (0.9, 1.0) +EXTENSION_LENGTHS = (2**10, 2**14, 2**18) +EXTENSION_REPLICAS = tuple(range(24, 40)) +EXTENSION_MASTER_SEED = 19_420_262_729 +EXTENSION_PHASE = "pilot" +EXTENSION_GRID_NAMESPACE = "pilot-p0-extension-v1" +EXTENSION_GRID_HASHES = MappingProxyType( + { + (0.9).hex(): "76dc7e07639ed085873a8f291cc2aaee0e8942ddac8efce3982743dd67491071", + (1.0).hex(): "d40b4a2afac533d74965513513fff1870918831000b2e040063ca2a0e29ad091", + } +) +P0_ANALYSIS_MAX_BYTES = 16 * 1024 * 1024 +DESIGN_MAX_BYTES = 1024 * 1024 +P0_PROGRESS_MAX_BYTES = PILOT_PROGRESS_MAX_BYTES +P0_RUN_SPEC_MAX_BYTES = PILOT_RUN_SPEC_MAX_BYTES + +P0_RUN_SPEC_SHA256 = "d17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840" +P0_PROGRESS_SHA256 = "ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f" +P0_ANALYSIS_DOCUMENT_SHA256 = ( + "e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8" +) +P0_ANALYSIS_FILE_SHA256 = ( + "44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b" +) +EXTENSION_SOURCE_REVISION = "9308087c5c609519234da48136b88cdd60f79667" +EXTENSION_PROTOCOL_SHA256 = ( + "a37ab41f3224594e61f4eebbe292975aeec449b9ecb7893e3e54f18d82d53321" +) +EXTENSION_PROTOCOL_FILE_SHA256 = ( + "e363a60f842b11b32972c7a68ec1c5f237741bc45bc79ab8bf93f51f6760d84d" +) +EXTENSION_RUN_SPEC_SHA256 = ( + "c1ca9b6c8ba751919c6d9337fe1cd4c09a57ed9b99abbb9d3ebfed7f89c3d32e" +) +EXTENSION_PROGRESS_SHA256 = ( + "c78d1fb03daf19297ef9e0617410c68a6a364bffc2f2888dfa9067e7e8d6b65f" +) +P0_BRACKET_DOCUMENT_SHA256 = ( + "fb3df666044bf9531443fc00c5c2c2d489512b4162864b3a92ffc2e756832403" +) +P0_SOURCE_REVISION = "739880d9ccdcffbfc8a15310250349bd11d63bbb" +DESIGN_SHA256 = "5426e3007e9d83039f371ca6a9372f1868ef9d5447b66a12b1643ecf72907aba" + +_PROTOCOL_FIELDS = { + "schema_version", + "source_p0_run_spec_sha256", + "source_p0_progress_sha256", + "source_p0_analysis_document_sha256", + "source_p0_bracket_document_sha256", + "design_sha256", + "source_revision", + "grid_namespace", + "master_seed", + "phase", + "purpose", + "lengths", + "replicas", + "loop_order", + "sigma_entries", + "cells", + "cell_count", + "rng_assignment_sha256", + "protocol_sha256", +} +_SIGMA_ENTRY_FIELDS = { + "sigma_hex", + "lengths", + "q_g_components", + "four_sector_components", + "selected_q_g_component", + "selected_four_sector_component", + "guard_interval_indices", + "lower_kappa_hex", + "upper_kappa_hex", + "kappas", + "grid_sha256", + "sigma_grid_id", +} +_CELL_FIELDS = { + "cell_index", + "cell_id", + "sigma", + "length", + "replica", + "sigma_grid_id", + "kappas", + "kernel_sha256", + "request_sha256", + "cell_path", + "run_path", + "manifest_path", + "rng_material_sha256", +} + + +def _canonical_bytes(document: object) -> bytes: + try: + return ( + json.dumps( + document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + + b"\n" + ) + except (TypeError, ValueError) as error: + raise RuntimeError("document is not canonical finite JSON") from error + + +def _sha256(payload: bytes) -> str: + return hashlib.sha256(payload).hexdigest() + + +def _malformed(message: str) -> Never: + raise RuntimeError(message) + + +def _solution_root() -> Path: + return Path(__file__).resolve().parents[2] + + +def _repo_root() -> Path: + return _solution_root().parents[4] + + +def _design_path() -> Path: + return ( + _repo_root() + / "docs/superpowers/specs/2026-07-30-challenge-194-p0-extension-design.md" + ) + + +def _file_sha256(path: Path) -> str: + return _file_hash( + path, + maximum_size=DESIGN_MAX_BYTES, + description="P0 extension design", + ) + + +def load_frozen_p0_analysis(path: Path) -> dict[str, object]: + document, _ = _read_canonical( + path, + "frozen P0 analysis artifact", + maximum_size=P0_ANALYSIS_MAX_BYTES, + ) + _validate_source(document) + return document + + +def _current_revision() -> str: + try: + result = subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=_repo_root(), + check=True, + capture_output=True, + text=True, + ) + except (OSError, subprocess.CalledProcessError) as error: + raise RuntimeError("unable to resolve extension source revision") from error + revision = result.stdout.strip() + if len(revision) != 40 or any( + character not in "0123456789abcdef" for character in revision + ): + raise RuntimeError("extension source revision is malformed") + return revision + + +def _marked_components(indices: Sequence[int]) -> tuple[tuple[int, int], ...]: + ordered = tuple(sorted(set(indices))) + if tuple(indices) != ordered: + raise RuntimeError("marked interval indices are not canonical") + components: list[tuple[int, int]] = [] + for index in ordered: + if components and index == components[-1][1] + 1: + components[-1] = (components[-1][0], index) + else: + components.append((index, index)) + return tuple(components) + + +def _component_gap(left: tuple[int, int], right: tuple[int, int]) -> int: + if left[1] < right[0]: + return right[0] - left[1] - 1 + if right[1] < left[0]: + return left[0] - right[1] - 1 + return 0 + + +def _recursive_binary64_grid_17(lower: float, upper: float) -> tuple[float, ...]: + if ( + not math.isfinite(lower) + or not math.isfinite(upper) + or lower <= 0.0 + or upper <= lower + ): + raise RuntimeError("extension grid endpoints are invalid") + points = [lower, upper] + for _level in range(4): + previous = sorted(points) + points.extend(left + (right - left) / 2.0 for left, right in pairwise(previous)) + ordered = tuple(sorted({value.hex(): value for value in points}.values())) + if len(ordered) != 17 or ordered[0] != lower or ordered[-1] != upper: + raise RuntimeError("extension span cannot produce 17 binary64 points") + return ordered + + +def derive_p0_extension_ranges( + p0_analysis: Mapping[str, object], +) -> dict[str, dict[str, object]]: + sigmas, lengths, kappas, values = _selector_estimates(p0_analysis) + selected_lengths = (lengths[-2], lengths[-1]) + result: dict[str, dict[str, object]] = {} + for sigma in EXTENSION_SIGMAS: + if sigma not in sigmas: + raise RuntimeError("blocked sigma is missing from P0 analysis") + q_indices: list[int] = [] + crossing_indices: list[int] = [] + for interval_index in range(1, len(kappas) - 1): + q_marked, crossing_marked, _evidence = _transition_evidence( + sigma, selected_lengths, kappas, values, interval_index + ) + q_indices.extend([interval_index] if q_marked else []) + crossing_indices.extend([interval_index] if crossing_marked else []) + q_components = _marked_components(q_indices) + crossing_components = _marked_components(crossing_indices) + if not q_components or not crossing_components: + raise RuntimeError("extension estimator component is missing") + crossing = crossing_components[0] + q_component = min( + q_components, + key=lambda component: (_component_gap(component, crossing), component[0]), + ) + union_lower = min(crossing[0], q_component[0]) + union_upper = max(crossing[1], q_component[1]) + guard_lower = union_lower - 1 + guard_upper = union_upper + 1 + if guard_lower < 1 or guard_upper + 1 >= len(kappas): + raise RuntimeError("extension range lacks adjacent P0 guards") + lower = kappas[guard_lower] + upper = kappas[guard_upper + 1] + grid = _recursive_binary64_grid_17(lower, upper) + result[sigma.hex()] = { + "sigma_hex": sigma.hex(), + "lengths": list(selected_lengths), + "q_g_components": [list(component) for component in q_components], + "four_sector_components": [ + list(component) for component in crossing_components + ], + "selected_q_g_component": list(q_component), + "selected_four_sector_component": list(crossing), + "guard_interval_indices": [guard_lower, guard_upper], + "lower_kappa_hex": lower.hex(), + "upper_kappa_hex": upper.hex(), + "kappas": [value.hex() for value in grid], + } + return result + + +def _validate_source(p0_analysis: Mapping[str, object]) -> None: + if ( + p0_analysis.get("p0_run_spec_sha256") != P0_RUN_SPEC_SHA256 + or p0_analysis.get("p0_progress_sha256") != P0_PROGRESS_SHA256 + or p0_analysis.get("analysis_document_sha256") != P0_ANALYSIS_DOCUMENT_SHA256 + or p0_analysis.get("source_revision") != P0_SOURCE_REVISION + ): + raise RuntimeError("P0 source hashes or revision are not frozen") + if _sha256(_canonical_bytes(p0_analysis)) != P0_ANALYSIS_FILE_SHA256: + raise RuntimeError("P0 source canonical file hash mismatch") + _selector_estimates(p0_analysis) + _validate_recomputed_brackets(p0_analysis) + + +def _read_evidence_document_at( + root_fd: int, + name: str, + description: str, + maximum_size: int, +) -> tuple[dict[str, object], bytes]: + descriptor, original = _open_regular_at( + name, + root_fd, + description, + maximum_size=maximum_size, + ) + try: + payload = _read_descriptor_bounded(descriptor, maximum_size, description) + _require_regular_at_identity( + name, + root_fd, + descriptor, + original, + description, + ) + try: + document = json.loads(payload) + except (UnicodeDecodeError, json.JSONDecodeError) as error: + raise RuntimeError(f"{description} is not canonical JSON") from error + if not isinstance(document, dict) or payload != _canonical_bytes(document): + raise RuntimeError(f"{description} is not canonical JSON") + _require_regular_at_identity( + name, + root_fd, + descriptor, + original, + description, + ) + return document, payload + finally: + os.close(descriptor) + + +def _load_p0_evidence( + p0_evidence_root: Path, +) -> tuple[dict[str, object], dict[str, object]]: + if not isinstance(p0_evidence_root, Path) or not p0_evidence_root.is_absolute(): + raise RuntimeError("p0_evidence_root must be an absolute canonical directory") + try: + if p0_evidence_root.resolve(strict=True) != p0_evidence_root: + raise RuntimeError( + "p0_evidence_root must be canonical and contain no symlink components" + ) + chain = _open_directory_chain(p0_evidence_root, create=False) + except RuntimeError: + raise + except OSError as error: + raise RuntimeError("p0_evidence_root is missing or unsafe") from error + try: + root_fd = chain[-1][1] + run_spec, run_payload = _read_evidence_document_at( + root_fd, + "run_spec.json", + "frozen P0 run spec evidence", + P0_RUN_SPEC_MAX_BYTES, + ) + progress, progress_payload = _read_evidence_document_at( + root_fd, + "progress.json", + "frozen P0 progress evidence", + P0_PROGRESS_MAX_BYTES, + ) + _require_directory_chain(chain, allow_final_mutation=False) + if _sha256(run_payload) != P0_RUN_SPEC_SHA256: + raise RuntimeError("frozen P0 run spec evidence hash mismatch") + if _sha256(progress_payload) != P0_PROGRESS_SHA256: + raise RuntimeError("frozen P0 progress evidence hash mismatch") + return run_spec, progress + finally: + _close_directory_chain(chain) + + +def _validate_recomputed_brackets(p0_analysis: Mapping[str, object]) -> None: + bracket = select_p1_brackets(p0_analysis) + if not isinstance(bracket, Mapping): + _malformed("recomputed P0 bracket document is malformed") + unsigned = dict(bracket) + digest = unsigned.pop("bracket_document_sha256", None) + if ( + digest != P0_BRACKET_DOCUMENT_SHA256 + or _sha256(_canonical_bytes(unsigned)) != digest + ): + raise RuntimeError("recomputed P0 bracket document hash mismatch") + + +def _grid_id(entry: Mapping[str, object]) -> str: + return ( + f"{EXTENSION_GRID_NAMESPACE}|sigma-f64={entry['sigma_hex']}" + f"|source-analysis={P0_ANALYSIS_DOCUMENT_SHA256}" + f"|range={entry['lower_kappa_hex']}:{entry['upper_kappa_hex']}" + ) + + +def _kernel_hash(length: int, sigma: float) -> str: + kernel = periodic_kernel(length, sigma) + return _sha256(kernel.astype(" tuple[str, ...]: + return tuple( + derive_stream_material( + StreamIdentity( + master_seed=master_seed, + phase=phase, + length=length, + sigma_grid_id=sigma_grid_id, + replica=replica, + stream_id=stream, + ) + ).material_sha256 + for stream in range(STREAM_COUNT) + ) + + +def _p0_identity_hashes( + p0_evidence_root: Path, +) -> tuple[tuple[str, ...], tuple[str, ...]]: + document, _ = _load_p0_evidence(p0_evidence_root) + try: + cells = document["cells"] + requests = tuple(str(cell["request_sha256"]) for cell in cells) + streams = tuple( + str(digest) for cell in cells for digest in cell["rng_material_sha256"] + ) + except (KeyError, TypeError, json.JSONDecodeError) as error: + raise RuntimeError("verified P0 identity registry is malformed") from error + if len(requests) != 96 or len(streams) != 96 * STREAM_COUNT: + raise RuntimeError("verified P0 identity registry is incomplete") + return requests, streams + + +def _validate_identity_axes() -> None: + replicas = tuple(EXTENSION_REPLICAS) + if ( + EXTENSION_MASTER_SEED in {PILOT_MASTER_SEED, P1_MASTER_SEED} + or len(replicas) != 16 + or len(set(replicas)) != len(replicas) + or set(replicas) & set(PILOT_REPLICAS) + or set(replicas) & set(P1_REPLICAS) + ): + raise RuntimeError("extension identities overlap P0 or reserved P1") + + +def _protocol_hash(protocol: Mapping[str, object]) -> str: + unsigned = dict(protocol) + unsigned.pop("protocol_sha256", None) + return _sha256(_canonical_bytes(unsigned)) + + +def _validate_bound_p0_extension_protocol_for_revision( + protocol: Mapping[str, object], + *, + expected_source_revision: str, +) -> None: + if set(protocol) != _PROTOCOL_FIELDS: + raise RuntimeError("extension protocol fields are invalid") + if protocol.get("protocol_sha256") != _protocol_hash(protocol): + raise RuntimeError("extension protocol hash mismatch") + if ( + protocol.get("schema_version") != EXTENSION_PROTOCOL_SCHEMA + or protocol.get("source_p0_run_spec_sha256") != P0_RUN_SPEC_SHA256 + or protocol.get("source_p0_progress_sha256") != P0_PROGRESS_SHA256 + or protocol.get("source_p0_analysis_document_sha256") + != P0_ANALYSIS_DOCUMENT_SHA256 + or protocol.get("source_p0_bracket_document_sha256") + != P0_BRACKET_DOCUMENT_SHA256 + or protocol.get("design_sha256") != DESIGN_SHA256 + or _file_sha256(_design_path()) != DESIGN_SHA256 + or protocol.get("source_revision") != expected_source_revision + or protocol.get("grid_namespace") != EXTENSION_GRID_NAMESPACE + or protocol.get("master_seed") != EXTENSION_MASTER_SEED + or protocol.get("phase") != EXTENSION_PHASE + or protocol.get("purpose") != "exploratory-p0-extension-only" + or protocol.get("lengths") != list(EXTENSION_LENGTHS) + or protocol.get("replicas") != list(EXTENSION_REPLICAS) + or protocol.get("loop_order") != ["sigma", "length", "replica"] + or protocol.get("cell_count") != 96 + ): + raise RuntimeError("extension bound protocol contract is invalid") + + +def build_p0_extension_protocol( + p0_analysis: Mapping[str, object], + p0_evidence_root: Path, +) -> dict[str, object]: + _validate_source(p0_analysis) + _validate_identity_axes() + design_sha256 = _file_sha256(_design_path()) + if design_sha256 != DESIGN_SHA256: + raise RuntimeError("extension design hash mismatch") + + ranges = derive_p0_extension_ranges(p0_analysis) + sigma_entries: list[dict[str, object]] = [] + for sigma in EXTENSION_SIGMAS: + entry = dict(ranges[sigma.hex()]) + grid_sha256 = _sha256(_canonical_bytes({"kappas": entry["kappas"]})) + if grid_sha256 != EXTENSION_GRID_HASHES[sigma.hex()]: + raise RuntimeError("derived extension grid hash mismatch") + entry["grid_sha256"] = grid_sha256 + entry["sigma_grid_id"] = _grid_id(entry) + sigma_entries.append(entry) + + p0_requests, p0_streams = _p0_identity_hashes(p0_evidence_root) + p0_request_set = set(p0_requests) + p0_stream_set = set(p0_streams) + cells: list[dict[str, object]] = [] + assignments: list[dict[str, object]] = [] + seen_requests: set[str] = set() + seen_streams: set[str] = set() + for sigma, entry in zip(EXTENSION_SIGMAS, sigma_entries, strict=True): + kappas_hex = list(entry["kappas"]) + kappas = np.asarray( + [float.fromhex(value) for value in kappas_hex], dtype=np.float64 + ) + grid_id = str(entry["sigma_grid_id"]) + for length in EXTENSION_LENGTHS: + kernel_sha256 = _kernel_hash(length, sigma) + for replica in EXTENSION_REPLICAS: + request = TrajectoryRequest( + length=length, + sigma=sigma, + sigma_grid_id=grid_id, + kappas=kappas, + master_seed=EXTENSION_MASTER_SEED, + phase=EXTENSION_PHASE, + replica=replica, + kernel_sha256=kernel_sha256, + ) + request_sha256 = request_digest(request) + streams = _stream_hashes( + length=length, + sigma_grid_id=grid_id, + replica=replica, + master_seed=EXTENSION_MASTER_SEED, + phase=EXTENSION_PHASE, + ) + if ( + request_sha256 in seen_requests + or request_sha256 in p0_request_set + or any( + digest in seen_streams or digest in p0_stream_set + for digest in streams + ) + ): + raise RuntimeError("extension request or RNG identity collision") + seen_requests.add(request_sha256) + seen_streams.update(streams) + index = len(cells) + identity = { + "cell_index": index, + "sigma": sigma.hex(), + "length": length, + "replica": replica, + "request_sha256": request_sha256, + } + cell_id = f"{index:03d}-{_sha256(_canonical_bytes(identity))[:16]}" + cell_path = f"cells/{cell_id}" + cells.append( + { + **identity, + "cell_id": cell_id, + "sigma_grid_id": grid_id, + "kappas": kappas_hex, + "kernel_sha256": kernel_sha256, + "rng_material_sha256": list(streams), + "cell_path": cell_path, + "run_path": f"{cell_path}/run", + "manifest_path": f"{cell_path}/manifest.json", + } + ) + assignments.append( + { + "cell_index": index, + "request_sha256": request_sha256, + "streams": list(streams), + } + ) + + protocol: dict[str, object] = { + "schema_version": EXTENSION_PROTOCOL_SCHEMA, + "source_p0_run_spec_sha256": P0_RUN_SPEC_SHA256, + "source_p0_progress_sha256": P0_PROGRESS_SHA256, + "source_p0_analysis_document_sha256": P0_ANALYSIS_DOCUMENT_SHA256, + "source_p0_bracket_document_sha256": P0_BRACKET_DOCUMENT_SHA256, + "design_sha256": design_sha256, + "source_revision": _current_revision(), + "grid_namespace": EXTENSION_GRID_NAMESPACE, + "master_seed": EXTENSION_MASTER_SEED, + "phase": EXTENSION_PHASE, + "purpose": "exploratory-p0-extension-only", + "lengths": list(EXTENSION_LENGTHS), + "replicas": list(EXTENSION_REPLICAS), + "loop_order": ["sigma", "length", "replica"], + "sigma_entries": sigma_entries, + "cells": cells, + "cell_count": 96, + "rng_assignment_sha256": _sha256( + _canonical_bytes({"assignments": assignments}) + ), + } + protocol["protocol_sha256"] = _protocol_hash(protocol) + validate_p0_extension_protocol(p0_analysis, protocol, p0_evidence_root) + return protocol + + +def _exact_hex(raw: object) -> float: + if not isinstance(raw, str): + _malformed("extension binary64 value is malformed") + try: + value = float.fromhex(raw) + except ValueError as error: + raise RuntimeError("extension binary64 value is malformed") from error + if not math.isfinite(value) or value.hex() != raw: + raise RuntimeError("extension binary64 value is not canonical") + return value + + +def _exact_digest(raw: object, name: str) -> str: + if ( + not isinstance(raw, str) + or len(raw) != 64 + or any(character not in "0123456789abcdef" for character in raw) + ): + _malformed(f"extension {name} digest is malformed") + return raw + + +def _validate_p0_extension_protocol_for_revision( + p0_analysis: Mapping[str, object], + protocol: Mapping[str, object], + p0_evidence_root: Path, + *, + expected_source_revision: str, +) -> None: + _validate_source(p0_analysis) + _validate_identity_axes() + if set(protocol) != _PROTOCOL_FIELDS: + raise RuntimeError("extension protocol fields are invalid") + if protocol.get("protocol_sha256") != _protocol_hash(protocol): + raise RuntimeError("extension protocol hash mismatch") + if ( + protocol.get("schema_version") != EXTENSION_PROTOCOL_SCHEMA + or protocol.get("source_p0_run_spec_sha256") != P0_RUN_SPEC_SHA256 + or protocol.get("source_p0_progress_sha256") != P0_PROGRESS_SHA256 + or protocol.get("source_p0_analysis_document_sha256") + != P0_ANALYSIS_DOCUMENT_SHA256 + or protocol.get("source_p0_bracket_document_sha256") + != P0_BRACKET_DOCUMENT_SHA256 + ): + raise RuntimeError("extension source bindings are invalid") + if ( + protocol.get("design_sha256") != DESIGN_SHA256 + or _file_sha256(_design_path()) != DESIGN_SHA256 + ): + raise RuntimeError("extension design hash mismatch") + if ( + protocol.get("grid_namespace") != EXTENSION_GRID_NAMESPACE + or protocol.get("master_seed") != EXTENSION_MASTER_SEED + or protocol.get("phase") != EXTENSION_PHASE + or protocol.get("purpose") != "exploratory-p0-extension-only" + or protocol.get("lengths") != list(EXTENSION_LENGTHS) + or protocol.get("loop_order") != ["sigma", "length", "replica"] + ): + raise RuntimeError("extension protocol axes are invalid") + replicas = protocol.get("replicas") + if replicas != list(EXTENSION_REPLICAS): + raise RuntimeError("extension replica axis is missing, duplicate, or reordered") + if ( + not isinstance(expected_source_revision, str) + or len(expected_source_revision) != 40 + or any( + character not in "0123456789abcdef" + for character in expected_source_revision + ) + or protocol.get("source_revision") != expected_source_revision + ): + raise RuntimeError("extension source revision mismatch") + + expected_ranges = derive_p0_extension_ranges(p0_analysis) + raw_entries = protocol.get("sigma_entries") + if not isinstance(raw_entries, list) or len(raw_entries) != 2: + raise RuntimeError("extension sigma entries are malformed") + entries: list[Mapping[str, object]] = [] + for sigma, raw in zip(EXTENSION_SIGMAS, raw_entries, strict=True): + if not isinstance(raw, Mapping) or set(raw) != _SIGMA_ENTRY_FIELDS: + raise RuntimeError("extension sigma entry fields are invalid") + expected_range = expected_ranges[sigma.hex()] + for field in ( + "sigma_hex", + "lengths", + "q_g_components", + "four_sector_components", + "selected_q_g_component", + "selected_four_sector_component", + "guard_interval_indices", + "lower_kappa_hex", + "upper_kappa_hex", + ): + if raw.get(field) != expected_range[field]: + raise RuntimeError("extension component range is invalid") + kappas = raw.get("kappas") + if not isinstance(kappas, list): + _malformed("extension grid is malformed") + parsed = tuple(_exact_hex(value) for value in kappas) + expected_grid = _recursive_binary64_grid_17( + _exact_hex(raw["lower_kappa_hex"]), + _exact_hex(raw["upper_kappa_hex"]), + ) + if parsed != expected_grid: + raise RuntimeError("extension grid order or values are invalid") + grid_sha256 = _sha256(_canonical_bytes({"kappas": kappas})) + if ( + raw.get("grid_sha256") != grid_sha256 + or grid_sha256 != EXTENSION_GRID_HASHES[sigma.hex()] + or raw.get("sigma_grid_id") != _grid_id(raw) + ): + raise RuntimeError("extension grid hash or identity is invalid") + entries.append(raw) + + cells = protocol.get("cells") + if ( + not isinstance(cells, list) + or protocol.get("cell_count") != 96 + or len(cells) != 96 + ): + raise RuntimeError("extension cell count is invalid") + p0_requests, p0_streams = _p0_identity_hashes(p0_evidence_root) + p0_request_set = set(p0_requests) + p0_stream_set = set(p0_streams) + seen_requests: set[str] = set() + seen_streams: set[str] = set() + assignments: list[dict[str, object]] = [] + expected_positions = ( + (sigma, length, replica, entry) + for sigma, entry in zip(EXTENSION_SIGMAS, entries, strict=True) + for length in EXTENSION_LENGTHS + for replica in EXTENSION_REPLICAS + ) + for index, (raw, expected) in enumerate( + zip(cells, expected_positions, strict=True) + ): + if not isinstance(raw, Mapping) or set(raw) != _CELL_FIELDS: + raise RuntimeError("extension cell fields are invalid") + sigma, length, replica, entry = expected + if ( + raw.get("cell_index") != index + or raw.get("sigma") != sigma.hex() + or raw.get("length") != length + or raw.get("replica") != replica + ): + raise RuntimeError("extension cells are not in canonical order") + request_sha256 = _exact_digest(raw.get("request_sha256"), "request") + raw_streams = raw.get("rng_material_sha256") + if not isinstance(raw_streams, list) or len(raw_streams) != STREAM_COUNT: + _malformed("extension RNG material digest list is malformed") + streams = tuple(_exact_digest(digest, "RNG material") for digest in raw_streams) + if request_sha256 in p0_request_set or any( + digest in p0_stream_set for digest in streams + ): + raise RuntimeError("extension identity collision with P0") + kernel_sha256 = _kernel_hash(length, sigma) + request = TrajectoryRequest( + length=length, + sigma=sigma, + sigma_grid_id=str(entry["sigma_grid_id"]), + kappas=np.asarray( + [float.fromhex(value) for value in entry["kappas"]], + dtype=np.float64, + ), + master_seed=EXTENSION_MASTER_SEED, + phase=EXTENSION_PHASE, + replica=replica, + kernel_sha256=kernel_sha256, + ) + expected_request = request_digest(request) + expected_streams = _stream_hashes( + length=length, + sigma_grid_id=str(entry["sigma_grid_id"]), + replica=replica, + master_seed=EXTENSION_MASTER_SEED, + phase=EXTENSION_PHASE, + ) + if request_sha256 != expected_request: + raise RuntimeError("extension request digest mismatch") + if streams != expected_streams: + raise RuntimeError("extension RNG material digest mismatch") + if request_sha256 in seen_requests or any( + digest in seen_streams for digest in expected_streams + ): + raise RuntimeError("extension request or RNG identity collision") + seen_requests.add(str(request_sha256)) + seen_streams.update(expected_streams) + identity = { + "cell_index": index, + "sigma": sigma.hex(), + "length": length, + "replica": replica, + "request_sha256": expected_request, + } + cell_id = f"{index:03d}-{_sha256(_canonical_bytes(identity))[:16]}" + cell_path = f"cells/{cell_id}" + if ( + raw.get("cell_id") != cell_id + or raw.get("sigma_grid_id") != entry["sigma_grid_id"] + or raw.get("kappas") != entry["kappas"] + or raw.get("kernel_sha256") != kernel_sha256 + or raw.get("cell_path") != cell_path + or raw.get("run_path") != f"{cell_path}/run" + or raw.get("manifest_path") != f"{cell_path}/manifest.json" + ): + raise RuntimeError("extension cell identity or path mismatch") + assignments.append( + { + "cell_index": index, + "request_sha256": expected_request, + "streams": list(expected_streams), + } + ) + expected_assignment_hash = _sha256(_canonical_bytes({"assignments": assignments})) + if protocol.get("rng_assignment_sha256") != expected_assignment_hash: + raise RuntimeError("extension aggregate RNG assignment hash mismatch") + + +def validate_p0_extension_protocol( + p0_analysis: Mapping[str, object], + protocol: Mapping[str, object], + p0_evidence_root: Path, +) -> None: + _validate_p0_extension_protocol_for_revision( + p0_analysis, + protocol, + p0_evidence_root, + expected_source_revision=_current_revision(), + ) + + +_ESTIMATE_FIELDS = { + "sigma_hex", + "length", + "kappa_hex", + "replica_count", + "means", + "standard_errors", + "request_sha256", +} +_P0_ANALYSIS_FIELDS = { + "schema_version", + "p0_run_spec_sha256", + "p0_progress_sha256", + "source_revision", + "analysis_plan_sha256", + "observable_columns", + "estimates", + "analysis_document_sha256", +} +_EXTENSION_ANALYSIS_FIELDS = { + "schema_version", + "source_extension_protocol_sha256", + "extension_run_spec_sha256", + "extension_progress_sha256", + "source_revision", + "analysis_plan_sha256", + "observable_columns", + "estimates", + "analysis_document_sha256", +} +_COMBINED_FIELDS = { + "schema_version", + "source_p0_analysis_document_sha256", + "source_extension_analysis_document_sha256", + "p0_run_spec_sha256", + "p0_progress_sha256", + "extension_run_spec_sha256", + "extension_progress_sha256", + "p0_source_revision", + "extension_source_revision", + "observable_columns", + "sigma_entries", + "estimate_count", + "analysis_document_sha256", +} +_COMBINED_SIGMA_FIELDS = {"sigma_hex", "kappas", "lengths", "estimates"} + + +def _exact_revision(raw: object, name: str) -> str: + if ( + not isinstance(raw, str) + or len(raw) != 40 + or any(character not in "0123456789abcdef" for character in raw) + ): + _malformed(f"{name} source revision is malformed") + return raw + + +def _require_builtin_int(raw: object, name: str) -> int: + if type(raw) is not int: + _malformed(f"{name} must be a built-in integer") + return raw + + +def _analysis_hash(analysis: Mapping[str, object]) -> str: + digest = _exact_digest(analysis.get("analysis_document_sha256"), "analysis") + unsigned = dict(analysis) + unsigned.pop("analysis_document_sha256", None) + if _sha256(_canonical_bytes(unsigned)) != digest: + raise RuntimeError("source analysis document digest mismatch") + return digest + + +def _validated_combination_rows( + source: Mapping[str, object], + *, + schema: str, + sigmas: tuple[float, ...], + grids: Mapping[float, tuple[float, ...]], + replica_count: int, + source_name: str, + expected_fields: set[str], +) -> tuple[ + dict[tuple[float, int, float], Mapping[str, object]], + dict[tuple[float, int], tuple[str, ...]], + set[str], +]: + if ( + not isinstance(source, Mapping) + or set(source) != expected_fields + or source.get("schema_version") != schema + ): + raise RuntimeError(f"{source_name} analysis schema is invalid") + raw_columns = source.get("observable_columns") + if not isinstance(raw_columns, Mapping) or set(raw_columns) != set( + OBSERVABLE_COLUMNS + ): + raise RuntimeError(f"{source_name} observable columns are invalid") + for name, expected_index in OBSERVABLE_COLUMNS.items(): + index = _require_builtin_int( + raw_columns[name], f"{source_name} observable column index" + ) + if index != expected_index: + raise RuntimeError(f"{source_name} observable columns are invalid") + raw_rows = source.get("estimates") + if not isinstance(raw_rows, Sequence) or isinstance(raw_rows, (str, bytes)): + _malformed(f"{source_name} estimates are malformed") + expected_identities = [ + (sigma, length, kappa) + for sigma in sigmas + for length in PILOT_LENGTHS + for kappa in grids[sigma] + ] + if len(raw_rows) != len(expected_identities): + raise RuntimeError(f"{source_name} estimate cardinality is invalid") + + rows: dict[tuple[float, int, float], Mapping[str, object]] = {} + group_requests: dict[tuple[float, int], tuple[str, ...]] = {} + request_owners: dict[str, tuple[float, int]] = {} + for raw, expected in zip(raw_rows, expected_identities, strict=True): + if not isinstance(raw, Mapping) or set(raw) != _ESTIMATE_FIELDS: + raise RuntimeError(f"{source_name} estimate shape is invalid") + sigma = _exact_hex(raw.get("sigma_hex")) + kappa = _exact_hex(raw.get("kappa_hex")) + length = _require_builtin_int( + raw.get("length"), f"{source_name} estimate length" + ) + identity = (sigma, length, kappa) + if identity != expected: + raise RuntimeError( + f"{source_name} grid estimates are not in canonical order" + ) + raw_replica_count = _require_builtin_int( + raw.get("replica_count"), f"{source_name} replica count" + ) + if raw_replica_count != replica_count: + raise RuntimeError(f"{source_name} replica count is invalid") + means = raw.get("means") + errors = raw.get("standard_errors") + if ( + not isinstance(means, Mapping) + or not isinstance(errors, Mapping) + or set(means) != set(OBSERVABLE_COLUMNS) + or set(errors) != set(OBSERVABLE_COLUMNS) + ): + raise RuntimeError(f"{source_name} observable moments are invalid") + for name in OBSERVABLE_COLUMNS: + mean = means[name] + error = errors[name] + if ( + not isinstance(mean, (int, float)) + or isinstance(mean, bool) + or not isinstance(error, (int, float)) + or isinstance(error, bool) + or not math.isfinite(float(mean)) + or not math.isfinite(float(error)) + or float(error) < 0.0 + ): + raise RuntimeError(f"{source_name} observable moments must be finite") + raw_requests = raw.get("request_sha256") + if not isinstance(raw_requests, list) or len(raw_requests) != replica_count: + raise RuntimeError(f"{source_name} request replica list is invalid") + requests = tuple(_exact_digest(value, "request") for value in raw_requests) + if len(set(requests)) != replica_count: + raise RuntimeError(f"{source_name} request identities are duplicate") + group = (sigma, length) + previous = group_requests.setdefault(group, requests) + if previous != requests: + raise RuntimeError( + f"{source_name} ordered request bindings are inconsistent" + ) + for request in requests: + owner = request_owners.setdefault(request, group) + if owner != group: + raise RuntimeError( + f"{source_name} request identity is bound to multiple groups" + ) + rows[(sigma, length, kappa)] = raw + _analysis_hash(source) + return rows, group_requests, set(request_owners) + + +def _validated_combination_sources( + p0_analysis: Mapping[str, object], + extension_analysis: Mapping[str, object], +) -> tuple[ + dict[tuple[float, int, float], Mapping[str, object]], + dict[tuple[float, int, float], Mapping[str, object]], + dict[float, tuple[float, ...]], +]: + if not isinstance(p0_analysis, Mapping) or not isinstance( + extension_analysis, Mapping + ): + _malformed("combination source analysis is malformed") + for field in ( + "p0_run_spec_sha256", + "p0_progress_sha256", + "analysis_plan_sha256", + ): + _exact_digest(p0_analysis.get(field), f"P0 {field}") + _exact_revision(p0_analysis.get("source_revision"), "P0") + for field in ( + "source_extension_protocol_sha256", + "extension_run_spec_sha256", + "extension_progress_sha256", + "analysis_plan_sha256", + ): + _exact_digest(extension_analysis.get(field), f"extension {field}") + _exact_revision(extension_analysis.get("source_revision"), "extension") + p0_grids = {sigma: tuple(PILOT_KAPPAS) for sigma in PILOT_SIGMAS} + p0_rows, _p0_groups, p0_requests = _validated_combination_rows( + p0_analysis, + schema=ANALYSIS_SCHEMA, + sigmas=tuple(PILOT_SIGMAS), + grids=p0_grids, + replica_count=len(PILOT_REPLICAS), + source_name="P0", + expected_fields=_P0_ANALYSIS_FIELDS, + ) + extension_grids = { + 0.9: _recursive_binary64_grid_17(PILOT_KAPPAS[4], PILOT_KAPPAS[8]), + 1.0: _recursive_binary64_grid_17(PILOT_KAPPAS[5], PILOT_KAPPAS[10]), + } + for sigma, grid in extension_grids.items(): + digest = _sha256(_canonical_bytes({"kappas": [value.hex() for value in grid]})) + if digest != EXTENSION_GRID_HASHES[sigma.hex()]: + raise RuntimeError("extension grid binding is invalid") + extension_rows, _extension_groups, extension_requests = _validated_combination_rows( + extension_analysis, + schema=EXTENSION_ANALYSIS_SCHEMA, + sigmas=EXTENSION_SIGMAS, + grids=extension_grids, + replica_count=len(EXTENSION_REPLICAS), + source_name="extension", + expected_fields=_EXTENSION_ANALYSIS_FIELDS, + ) + if p0_requests & extension_requests: + raise RuntimeError("P0 and extension request identities overlap") + return p0_rows, extension_rows, extension_grids + + +def _pooled_row( + p0_row: Mapping[str, object], + extension_row: Mapping[str, object], +) -> dict[str, object]: + left_n = _require_builtin_int(p0_row["replica_count"], "P0 replica count") + right_n = _require_builtin_int( + extension_row["replica_count"], "extension replica count" + ) + left_means = p0_row["means"] + right_means = extension_row["means"] + left_errors = p0_row["standard_errors"] + right_errors = extension_row["standard_errors"] + if not all( + isinstance(value, Mapping) + for value in (left_means, right_means, left_errors, right_errors) + ): + _malformed("source observable moments are malformed") + means: dict[str, float] = {} + standard_errors: dict[str, float] = {} + total = 0 + for name in OBSERVABLE_COLUMNS: + total, mean, standard_error = _pool_estimates( + left_n, + float(left_means[name]), + float(left_errors[name]), + right_n, + float(right_means[name]), + float(right_errors[name]), + ) + means[name] = mean + standard_errors[name] = standard_error + requests = list(p0_row["request_sha256"]) + list(extension_row["request_sha256"]) + if len(requests) != total or len(set(requests)) != total: + raise RuntimeError("combined request identities are invalid") + return { + "sigma_hex": p0_row["sigma_hex"], + "length": p0_row["length"], + "kappa_hex": p0_row["kappa_hex"], + "replica_count": total, + "means": means, + "standard_errors": standard_errors, + "request_sha256": requests, + } + + +def _build_combined_p0_evidence( + p0_analysis: Mapping[str, object], + extension_analysis: Mapping[str, object], +) -> dict[str, object]: + p0_rows, extension_rows, extension_grids = _validated_combination_sources( + p0_analysis, extension_analysis + ) + sigma_entries: list[dict[str, object]] = [] + estimate_count = 0 + for sigma in PILOT_SIGMAS: + kappas = ( + tuple(PILOT_KAPPAS) + if sigma not in extension_grids + else tuple(sorted(set(PILOT_KAPPAS) | set(extension_grids[sigma]))) + ) + expected_count = 16 if sigma in (0.8, 1.1) else 31 + if len(kappas) != expected_count: + raise RuntimeError("combined grid overlap or cardinality is invalid") + estimates: list[dict[str, object]] = [] + for length in PILOT_LENGTHS: + for kappa in kappas: + identity = (sigma, length, kappa) + p0_row = p0_rows.get(identity) + extension_row = extension_rows.get(identity) + if p0_row is not None and extension_row is not None: + row = _pooled_row(p0_row, extension_row) + elif p0_row is not None: + row = dict(p0_row) + elif extension_row is not None: + row = dict(extension_row) + else: + raise RuntimeError("combined estimate is missing") + estimates.append(row) + estimate_count += len(estimates) + sigma_entries.append( + { + "sigma_hex": sigma.hex(), + "kappas": [value.hex() for value in kappas], + "lengths": list(PILOT_LENGTHS), + "estimates": estimates, + } + ) + if estimate_count != 282: + raise RuntimeError("combined estimate cardinality is invalid") + document: dict[str, object] = { + "schema_version": COMBINED_ANALYSIS_SCHEMA, + "source_p0_analysis_document_sha256": _analysis_hash(p0_analysis), + "source_extension_analysis_document_sha256": _analysis_hash(extension_analysis), + "p0_run_spec_sha256": _exact_digest( + p0_analysis.get("p0_run_spec_sha256"), "P0 run spec" + ), + "p0_progress_sha256": _exact_digest( + p0_analysis.get("p0_progress_sha256"), "P0 progress" + ), + "extension_run_spec_sha256": _exact_digest( + extension_analysis.get("extension_run_spec_sha256"), + "extension run spec", + ), + "extension_progress_sha256": _exact_digest( + extension_analysis.get("extension_progress_sha256"), + "extension progress", + ), + "p0_source_revision": _exact_revision(p0_analysis.get("source_revision"), "P0"), + "extension_source_revision": _exact_revision( + extension_analysis.get("source_revision"), "extension" + ), + "observable_columns": dict(OBSERVABLE_COLUMNS), + "sigma_entries": sigma_entries, + "estimate_count": estimate_count, + } + document["analysis_document_sha256"] = _sha256(_canonical_bytes(document)) + return document + + +def _validate_combined_p0_evidence( + p0_analysis: Mapping[str, object], + extension_analysis: Mapping[str, object], + combined_analysis: Mapping[str, object], +) -> None: + if ( + not isinstance(combined_analysis, Mapping) + or set(combined_analysis) != _COMBINED_FIELDS + ): + raise RuntimeError("combined analysis fields are invalid") + estimate_count = _require_builtin_int( + combined_analysis.get("estimate_count"), "combined estimate count" + ) + if estimate_count != 282: + raise RuntimeError("combined estimate cardinality is invalid") + raw_columns = combined_analysis.get("observable_columns") + if not isinstance(raw_columns, Mapping) or set(raw_columns) != set( + OBSERVABLE_COLUMNS + ): + raise RuntimeError("combined observable columns are invalid") + for name, expected_index in OBSERVABLE_COLUMNS.items(): + index = _require_builtin_int( + raw_columns[name], "combined observable column index" + ) + if index != expected_index: + raise RuntimeError("combined observable columns are invalid") + raw_entries = combined_analysis.get("sigma_entries") + if ( + not isinstance(raw_entries, list) + or len(raw_entries) != len(PILOT_SIGMAS) + or any( + not isinstance(entry, Mapping) or set(entry) != _COMBINED_SIGMA_FIELDS + for entry in raw_entries + ) + ): + raise RuntimeError("combined analysis sigma entries are malformed") + for entry in raw_entries: + raw_lengths = entry.get("lengths") + if not isinstance(raw_lengths, list) or len(raw_lengths) != len(PILOT_LENGTHS): + raise RuntimeError("combined length axis is invalid") + lengths = tuple( + _require_builtin_int(value, "combined length axis value") + for value in raw_lengths + ) + if lengths != tuple(PILOT_LENGTHS): + raise RuntimeError("combined length axis is invalid") + raw_estimates = entry.get("estimates") + if not isinstance(raw_estimates, list): + raise RuntimeError("combined estimates are malformed") + for raw in raw_estimates: + if not isinstance(raw, Mapping) or set(raw) != _ESTIMATE_FIELDS: + raise RuntimeError("combined estimate shape is invalid") + _require_builtin_int(raw.get("length"), "combined estimate length") + _require_builtin_int(raw.get("replica_count"), "combined replica count") + expected = _build_combined_p0_evidence(p0_analysis, extension_analysis) + if _canonical_bytes(combined_analysis) != _canonical_bytes(expected): + raise RuntimeError("combined analysis semantic recomputation mismatch") + + +def _authenticate_combined_sources( + p0_analysis: Mapping[str, object], + extension_analysis: Mapping[str, object], + *, + p0_evidence_root: Path, + extension_run_spec: Path, + extension_protocol: Mapping[str, object], +) -> tuple[Mapping[str, object], Mapping[str, object]]: + from . import pilot + from .pilot_analysis import aggregate_p0_extension + + _validate_source(p0_analysis) + _load_p0_evidence(p0_evidence_root) + pilot.verify_frozen_challenge_194_p0_download( + p0_evidence_root / pilot.RUN_SPEC_NAME + ) + + if ( + not isinstance(extension_protocol, Mapping) + or extension_protocol.get("protocol_sha256") != EXTENSION_PROTOCOL_SHA256 + or _sha256(_canonical_bytes(extension_protocol)) + != EXTENSION_PROTOCOL_FILE_SHA256 + ): + raise RuntimeError("immutable extension protocol identity mismatch") + _validate_p0_extension_protocol_for_revision( + p0_analysis, + extension_protocol, + p0_evidence_root, + expected_source_revision=EXTENSION_SOURCE_REVISION, + ) + + if not isinstance(extension_run_spec, Path) or not extension_run_spec.is_absolute(): + raise RuntimeError("extension_run_spec must be an absolute canonical path") + if extension_run_spec.resolve(strict=True) != extension_run_spec: + raise RuntimeError("extension_run_spec must be an absolute canonical path") + _run_spec, run_spec_payload = pilot._read_canonical( + extension_run_spec, + "immutable extension run spec", + maximum_size=pilot.PILOT_RUN_SPEC_MAX_BYTES, + ) + _progress, progress_payload = pilot._read_canonical( + extension_run_spec.parent / pilot.MERGED_NAME, + "immutable extension progress", + maximum_size=pilot.PILOT_PROGRESS_MAX_BYTES, + ) + if _sha256(run_spec_payload) != EXTENSION_RUN_SPEC_SHA256: + raise RuntimeError("immutable extension run spec hash mismatch") + if _sha256(progress_payload) != EXTENSION_PROGRESS_SHA256: + raise RuntimeError("immutable extension progress hash mismatch") + pilot.verify_p0_extension_download(extension_run_spec) + recomputed_extension = aggregate_p0_extension( + extension_run_spec, + extension_protocol, + ) + if ( + recomputed_extension.get("extension_run_spec_sha256") + != EXTENSION_RUN_SPEC_SHA256 + or recomputed_extension.get("extension_progress_sha256") + != EXTENSION_PROGRESS_SHA256 + ): + raise RuntimeError("post-aggregation extension source hash mismatch") + if _canonical_bytes(extension_analysis) != _canonical_bytes(recomputed_extension): + raise RuntimeError( + "supplied extension analysis does not match authenticated recomputation" + ) + return p0_analysis, recomputed_extension + + +def validate_combined_p0_evidence( + p0_analysis: Mapping[str, object], + extension_analysis: Mapping[str, object], + combined_analysis: Mapping[str, object], + *, + p0_evidence_root: Path, + extension_run_spec: Path, + extension_protocol: Mapping[str, object], +) -> None: + authenticated_p0, authenticated_extension = _authenticate_combined_sources( + p0_analysis, + extension_analysis, + p0_evidence_root=p0_evidence_root, + extension_run_spec=extension_run_spec, + extension_protocol=extension_protocol, + ) + _validate_combined_p0_evidence( + authenticated_p0, + authenticated_extension, + combined_analysis, + ) + + +def combine_p0_evidence( + p0_analysis: Mapping[str, object], + extension_analysis: Mapping[str, object], + *, + p0_evidence_root: Path, + extension_run_spec: Path, + extension_protocol: Mapping[str, object], +) -> dict[str, object]: + authenticated_p0, authenticated_extension = _authenticate_combined_sources( + p0_analysis, + extension_analysis, + p0_evidence_root=p0_evidence_root, + extension_run_spec=extension_run_spec, + extension_protocol=extension_protocol, + ) + document = _build_combined_p0_evidence(authenticated_p0, authenticated_extension) + _validate_combined_p0_evidence( + authenticated_p0, + authenticated_extension, + document, + ) + return document diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/poisson_reference.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/poisson_reference.py new file mode 100644 index 000000000..b486c3082 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/poisson_reference.py @@ -0,0 +1,466 @@ +from __future__ import annotations + +from bisect import bisect_left, bisect_right +import math +from typing import Protocol, Sequence + +import numpy as np + +from .counter_rng import ( + StreamIdentity, + derive_stream_material, + philox4x32_10_reference, + u32_to_open, +) +from .trajectory import ( + F64, + U32, + U64, + TrajectoryDiagnostics, + TrajectoryRequest, + TrajectoryResult, + _request_digest, + _validate_event_time_resolution, + _validate_kernel, + validate_trajectory_request, +) +from .union_find import UnionFind + + +_CLASS_COLUMN_STREAM = 0 +_CLASS_THRESHOLD_STREAM = 1 +_OFFSET_STREAM = 2 +_EXPONENTIAL_STREAM = 3 +_STREAM_COUNT = 4 +_UINT64_LIMIT = 1 << 64 +_MASK32 = (1 << 32) - 1 +_PREFIX_REL_TOL = 8.0 * np.finfo(np.float64).eps +_MINIMUM_OPEN_HAZARD = -math.log( + u32_to_open(np.uint32(_MASK32)) +) + + +class _Streams(Protocol): + terminal_counters: U32 + draw_counts: U64 + + @property + def minimum_exponential_hazard(self) -> float: ... + + def uniform(self, stream_id: int) -> float: ... + + def bounded(self, stream_id: int, bound: int) -> int: ... + + +def _compensated_prefix( + weights: Sequence[float], +) -> tuple[tuple[float, ...], float, int]: + running = 0.0 + correction = 0.0 + previous = 0.0 + cumulative: list[float] = [] + operations = 0 + for value in weights: + weight = float(value) + if not math.isfinite(weight) or weight <= 0.0: + raise ValueError("class weights must be finite and positive") + combined = running + weight + if abs(running) >= abs(weight): + correction += (running - combined) + weight + else: + correction += (weight - combined) + running + running = combined + prefix = running + correction + operations += 1 + if not math.isfinite(prefix) or prefix < previous: + raise ValueError( + "compensated class prefix must be finite and monotone" + ) + cumulative.append(prefix) + previous = prefix + if not cumulative: + raise ValueError("at least one class weight is required") + + exact_total = math.fsum(float(value) for value in weights) + approximate_total = cumulative[-1] + tolerance = _PREFIX_REL_TOL * abs(exact_total) + if ( + not math.isfinite(exact_total) + or exact_total <= 0.0 + or abs(approximate_total - exact_total) > tolerance + ): + raise ValueError( + "compensated class total disagrees with the reference sum" + ) + if len(cumulative) > 1 and exact_total < cumulative[-2]: + raise ValueError("reference class total violates prefix monotonicity") + cumulative[-1] = exact_total + return tuple(cumulative), exact_total, operations + + +def _class_data(length: int, kernel: F64) -> tuple[ + tuple[int, ...], tuple[int, ...], tuple[float, ...], float +]: + multiplicities = tuple( + length if distance < length // 2 else length // 2 + for distance in range(1, length // 2 + 1) + ) + weights = tuple( + float(multiplicity) * float(kernel[index]) + for index, multiplicity in enumerate(multiplicities) + ) + cumulative, total_rate, _ = _compensated_prefix(weights) + starts = [0] + for multiplicity in multiplicities: + starts.append(starts[-1] + multiplicity) + if starts[-1] >= _UINT64_LIMIT: + raise ValueError("the canonical edge count must fit uint64") + return multiplicities, tuple(starts), cumulative, total_rate + + +class _ReferenceWordStream: + def __init__(self, identity: StreamIdentity): + material = derive_stream_material(identity) + self.key = np.array(material.key, dtype=np.uint32, copy=True) + self.counter = np.array( + material.initial_counter, dtype=np.uint32, copy=True + ) + self.block = np.zeros(4, dtype=np.uint32) + self.lane = 4 + self.accounting = np.zeros(3, dtype=np.uint64) + + def _increment_counter(self) -> None: + carry = 1 + for index in range(4): + total = int(self.counter[index]) + carry + self.counter[index] = np.uint32(total & _MASK32) + carry = total >> 32 + + def next_word(self) -> np.uint32: + if self.lane == 4: + self.block[:] = philox4x32_10_reference(self.counter, self.key) + self._increment_counter() + self.lane = 0 + self.accounting[1] += np.uint64(1) + word = self.block[self.lane] + self.lane += 1 + self.accounting[0] += np.uint64(1) + return word + + def uniform(self) -> float: + return u32_to_open(self.next_word()) + + def bounded(self, bound: int) -> int: + if isinstance(bound, bool) or not isinstance(bound, int): + raise ValueError("bound must be a Python integer") + if not 1 <= bound <= _MASK32: + raise ValueError("bound must be in [1, 2**32 - 1]") + threshold = ((1 << 32) - bound) % bound + while True: + word = int(self.next_word()) + if word < threshold: + self.accounting[2] += np.uint64(1) + continue + return word % bound + + +class _ReferenceStreams: + def __init__(self, request: TrajectoryRequest): + self._streams = tuple( + _ReferenceWordStream( + StreamIdentity( + master_seed=request.master_seed, + phase=request.phase, + length=request.length, + sigma_grid_id=request.sigma_grid_id, + replica=request.replica, + stream_id=stream_id, + ) + ) + for stream_id in range(_STREAM_COUNT) + ) + + def uniform(self, stream_id: int) -> float: + if not 0 <= stream_id < _STREAM_COUNT: + raise ValueError("stream_id is outside the frozen namespace") + return self._streams[stream_id].uniform() + + def bounded(self, stream_id: int, bound: int) -> int: + if stream_id != _OFFSET_STREAM: + raise ValueError("bounded draws are restricted to the offset stream") + return self._streams[stream_id].bounded(bound) + + @property + def minimum_exponential_hazard(self) -> float: + return _MINIMUM_OPEN_HAZARD + + @property + def terminal_counters(self) -> U32: + return np.stack([stream.counter for stream in self._streams]) + + @property + def draw_counts(self) -> U64: + return np.stack([stream.accounting for stream in self._streams]) + + +def _build_reference_streams(request: TrajectoryRequest) -> _ReferenceStreams: + validate_trajectory_request(request) + return _ReferenceStreams(request) + + +def _decode_edge( + edge_id: int, + length: int, + starts: tuple[int, ...], +) -> tuple[int, int]: + class_index = bisect_right(starts, edge_id) - 1 + if not 0 <= class_index < len(starts) - 1: + raise RuntimeError("stored edge identifier is outside all classes") + offset = edge_id - starts[class_index] + distance = class_index + 1 + multiplicity = starts[class_index + 1] - starts[class_index] + if not 0 <= offset < multiplicity: + raise RuntimeError("stored edge offset is outside its class") + if distance == length // 2 and offset >= length // 2: + raise RuntimeError("antipodal offset is outside its half-ring") + left = offset + right = (offset + distance) % length + return (left, right) if left < right else (right, left) + + +def _checkpoint( + length: int, + open_edge_ids: set[int], + starts: tuple[int, ...], +) -> tuple[float, ...]: + connectivity = UnionFind(length) + for edge_id in sorted(open_edge_ids): + left, right = _decode_edge(edge_id, length, starts) + connectivity.union(left, right) + labels = connectivity.labels() + sizes_by_label: dict[int, int] = {} + masks_by_label: dict[int, int] = {} + for vertex, label_value in enumerate(labels.tolist()): + label = int(label_value) + sizes_by_label[label] = sizes_by_label.get(label, 0) + 1 + sector = min(3, (4 * vertex) // length) + masks_by_label[label] = masks_by_label.get(label, 0) | (1 << sector) + sizes = sorted(sizes_by_label.values(), reverse=True) + largest = sizes[0] + second_largest = sizes[1] if len(sizes) > 1 else 0 + sum_size_sq = math.fsum(float(size) ** 2 for size in sizes) + sum_size_fourth = math.fsum(float(size) ** 4 for size in sizes) + q_g = sum_size_fourth / (sum_size_sq * sum_size_sq) + return ( + float(len(open_edge_ids)), + float(len(sizes)), + float(largest), + float(second_largest), + float(largest) / float(length), + float(second_largest) / float(length), + sum_size_sq, + sum_size_fourth, + q_g, + float(any(mask == 0b1111 for mask in masks_by_label.values())), + ) + + +def _checked_stream_arrays(streams: _Streams) -> tuple[U32, U64]: + terminal = streams.terminal_counters + counts = streams.draw_counts + if ( + not isinstance(terminal, np.ndarray) + or terminal.dtype != np.dtype(np.uint32) + or terminal.shape != (_STREAM_COUNT, 4) + or not terminal.flags.c_contiguous + ): + raise ValueError("stream terminal counters violate the fixed contract") + if ( + not isinstance(counts, np.ndarray) + or counts.dtype != np.dtype(np.uint64) + or counts.shape != (_STREAM_COUNT, 3) + or not counts.flags.c_contiguous + ): + raise ValueError("stream draw counts violate the fixed contract") + return terminal, counts + + +def _compensated_hazard_add( + high: float, low: float, increment: float +) -> tuple[float, float]: + summed = high + increment + virtual_increment = summed - high + error = (high - (summed - virtual_increment)) + ( + increment - virtual_increment + ) + residual = low + error + next_high = summed + residual + next_low = residual - (next_high - summed) + return next_high, next_low + + +def _hazard_pair_greater_than_scalar( + high: float, low: float, scalar: float +) -> bool: + return high > scalar or (high == scalar and low > 0.0) + + +def _hazard_pair_at_least_scalar( + high: float, low: float, scalar: float +) -> bool: + return high > scalar or (high == scalar and low >= 0.0) + + +def _run_poisson_with_streams( + request: TrajectoryRequest, + kernel: F64, + streams: _Streams, +) -> TrajectoryDiagnostics: + validate_trajectory_request(request) + _validate_kernel(request, kernel) + multiplicities, starts, cumulative, total_rate = _class_data( + request.length, kernel + ) + kappa_max = float(request.kappas[-1]) + minimum_hazard = getattr( + streams, + "minimum_exponential_hazard", + _MINIMUM_OPEN_HAZARD, + ) + _validate_event_time_resolution( + kappa_max, + total_rate, + float(minimum_hazard), + ) + + rows: list[tuple[float, ...]] = [] + snapshots: list[frozenset[int]] = [] + open_edge_ids: set[int] = set() + event_times: list[float] = [] + event_count = 0 + duplicate_count = 0 + checkpoint_index = 0 + current_hazard_high = 0.0 + current_hazard_low = 0.0 + terminal_hazard = kappa_max * total_rate + + while ( + checkpoint_index < request.kappas.size + and request.kappas[checkpoint_index] == 0.0 + ): + rows.append(_checkpoint(request.length, open_edge_ids, starts)) + snapshots.append(frozenset(open_edge_ids)) + checkpoint_index += 1 + + if kappa_max > 0.0: + while True: + exponential_uniform = streams.uniform(_EXPONENTIAL_STREAM) + if not 0.0 < exponential_uniform < 1.0: + raise ValueError("exponential stream must produce open uniforms") + hazard = -math.log(exponential_uniform) + next_hazard_high, next_hazard_low = _compensated_hazard_add( + current_hazard_high, current_hazard_low, hazard + ) + if ( + not math.isfinite(hazard) + or hazard <= 0.0 + or not math.isfinite(next_hazard_high) + ): + raise ValueError("event hazard failed finite strict advancement") + if _hazard_pair_greater_than_scalar( + next_hazard_high, next_hazard_low, terminal_hazard + ): + break + + while ( + checkpoint_index < request.kappas.size + and _hazard_pair_greater_than_scalar( + next_hazard_high, + next_hazard_low, + float(request.kappas[checkpoint_index]) * total_rate, + ) + ): + rows.append(_checkpoint(request.length, open_edge_ids, starts)) + snapshots.append(frozenset(open_edge_ids)) + checkpoint_index += 1 + + column_uniform = streams.uniform(_CLASS_COLUMN_STREAM) + class_uniform = streams.uniform(_CLASS_THRESHOLD_STREAM) + if not 0.0 < column_uniform < 1.0: + raise ValueError("class-column stream must produce open uniforms") + if not 0.0 < class_uniform < 1.0: + raise ValueError("class stream must produce open uniforms") + target = class_uniform * total_rate + class_index = bisect_left(cumulative, target) + if class_index == len(cumulative): + class_index = len(cumulative) - 1 + offset = streams.bounded( + _OFFSET_STREAM, multiplicities[class_index] + ) + if not 0 <= offset < multiplicities[class_index]: + raise ValueError("offset stream returned an out-of-range value") + edge_id = starts[class_index] + offset + event_count += 1 + event_times.append( + next_hazard_high / total_rate + + next_hazard_low / total_rate + ) + if edge_id in open_edge_ids: + duplicate_count += 1 + else: + open_edge_ids.add(edge_id) + current_hazard_high = next_hazard_high + current_hazard_low = next_hazard_low + + while ( + checkpoint_index < request.kappas.size + and _hazard_pair_at_least_scalar( + current_hazard_high, + current_hazard_low, + float(request.kappas[checkpoint_index]) * total_rate, + ) + ): + rows.append(_checkpoint(request.length, open_edge_ids, starts)) + snapshots.append(frozenset(open_edge_ids)) + checkpoint_index += 1 + + while checkpoint_index < request.kappas.size: + rows.append(_checkpoint(request.length, open_edge_ids, starts)) + snapshots.append(frozenset(open_edge_ids)) + checkpoint_index += 1 + + terminal_counters, draw_counts = _checked_stream_arrays(streams) + result = TrajectoryResult( + request_sha256=_request_digest(request), + observables=np.asarray(rows, dtype=np.float64).reshape(-1, 10), + terminal_counters=terminal_counters, + draw_counts=draw_counts, + event_count=event_count, + duplicate_count=duplicate_count, + hash_diagnostics=np.zeros(5, dtype=np.uint64), + ) + return TrajectoryDiagnostics( + result=result, + event_times=tuple(event_times), + edge_ids_by_checkpoint=tuple(snapshots), + ) + + +def run_poisson_reference( + request: TrajectoryRequest, + kernel: F64, +) -> TrajectoryResult: + validate_trajectory_request(request) + _validate_kernel(request, kernel) + streams = _build_reference_streams(request) + return _run_poisson_with_streams(request, kernel, streams).result + + +def run_poisson_reference_with_diagnostics( + request: TrajectoryRequest, + kernel: F64, +) -> TrajectoryDiagnostics: + validate_trajectory_request(request) + _validate_kernel(request, kernel) + streams = _build_reference_streams(request) + return _run_poisson_with_streams(request, kernel, streams) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/poisson_sweep.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/poisson_sweep.py new file mode 100644 index 000000000..ab873e955 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/poisson_sweep.py @@ -0,0 +1,675 @@ +from __future__ import annotations + +import hashlib +import math + +import numba +import numpy as np +import numpy.typing as npt + +from .alias import AliasTable, draw_alias +from .counter_rng import ( + STREAM_ALIAS_COLUMN, + STREAM_ALIAS_THRESHOLD, + STREAM_COUNT, + STREAM_EDGE_OFFSET, + STREAM_EXPONENTIAL, + StreamIdentity, + bounded_u32, + derive_stream_material, + next_u32, + uniform_open, +) +from .edge_set import ( + allocate_edge_set, + build_class_start, + edge_set_insert_kernel, +) +from .trajectory import ( + TrajectoryRequest, + TrajectoryResult, + _request_digest, + _validate_event_time_resolution, + _validate_kernel, + validate_trajectory_request, +) +from .production_union_find import ( + _scan_basic_observables_kernel, + allocate_union_find, + union_incremental, +) + + +F64 = npt.NDArray[np.float64] +I64 = npt.NDArray[np.int64] +U8 = npt.NDArray[np.uint8] +U32 = npt.NDArray[np.uint32] +U64 = npt.NDArray[np.uint64] + +_MINIMUM_OPEN_HAZARD = -math.log( + (float(np.iinfo(np.uint32).max) + 0.5) * (2.0**-32) +) +_MAX_INT64 = np.iinfo(np.int64).max +_MAX_UINT64 = np.iinfo(np.uint64).max +_LOW32 = np.uint64(0xFFFFFFFF) + +_F64_1D = numba.types.Array(numba.float64, 1, "C") +_F64_1D_RO = numba.types.Array( + numba.float64, 1, "C", readonly=True +) +_F64_2D = numba.types.Array(numba.float64, 2, "C") +_I64_1D = numba.types.Array(numba.int64, 1, "C") +_I64_1D_RO = numba.types.Array(numba.int64, 1, "C", readonly=True) +_U8_1D = numba.types.Array(numba.uint8, 1, "C") +_U8_2D = numba.types.Array(numba.uint8, 2, "C") +_U32_2D = numba.types.Array(numba.uint32, 2, "C") +_U64_1D = numba.types.Array(numba.uint64, 1, "C") +_U64_1D_RO = numba.types.Array(numba.uint64, 1, "C", readonly=True) +_U64_2D = numba.types.Array(numba.uint64, 2, "C") + +_RUN_RESULT = numba.types.UniTuple(numba.int64, 3) +_RUN_SIGNATURE = _RUN_RESULT( + numba.int64, + _F64_1D_RO, + numba.float64, + _F64_1D_RO, + _I64_1D_RO, + _U64_1D_RO, + _U64_1D, + _U64_1D, + _U8_1D, + _U64_1D, + _I64_1D, + _I64_1D, + _U8_1D, + _F64_1D, + _I64_1D, + _U32_2D, + _U32_2D, + _U32_2D, + _U8_2D, + _U64_2D, + _F64_2D, +) +_RECORD_SIGNATURE = numba.int64( + numba.int64, + numba.int64, + _I64_1D, + _I64_1D, + _U8_1D, + _F64_1D, + _I64_1D, + _F64_2D, +) + + +@numba.njit( + numba.types.UniTuple(numba.float64, 2)( + numba.float64, numba.float64, numba.float64 + ), + cache=True, + fastmath=False, +) +def _compensated_hazard_add( + high: float, low: float, increment: float +) -> tuple[float, float]: + summed = high + increment + virtual_increment = summed - high + error = (high - (summed - virtual_increment)) + ( + increment - virtual_increment + ) + residual = low + error + next_high = summed + residual + next_low = residual - (next_high - summed) + return next_high, next_low + + +@numba.njit( + numba.boolean(numba.float64, numba.float64, numba.float64), + cache=True, + fastmath=False, +) +def _hazard_pair_greater_than_scalar( + high: float, low: float, scalar: float +) -> bool: + return high > scalar or (high == scalar and low > 0.0) + + +@numba.njit( + numba.boolean(numba.float64, numba.float64, numba.float64), + cache=True, + fastmath=False, +) +def _hazard_pair_at_least_scalar( + high: float, low: float, scalar: float +) -> bool: + return high > scalar or (high == scalar and low >= 0.0) + + +@numba.njit( + _RECORD_SIGNATURE, cache=True, boundscheck=True, fastmath=False +) +def _record_checkpoint( + length: int, + row: int, + parent: I64, + size: I64, + sector_mask: U8, + moments: F64, + counts: I64, + output: F64, +) -> int: + ( + status, + open_edges, + component_count, + largest_size, + second_largest_size, + sum_size_sq, + sum_size_fourth, + q_g, + four_sector_crossing, + ) = _scan_basic_observables_kernel( + parent, size, sector_mask, moments, counts + ) + if status != 0: + return 100 + status + if ( + not math.isfinite(sum_size_sq) + or not math.isfinite(sum_size_fourth) + or not math.isfinite(q_g) + ): + return 200 + output[row, 0] = float(open_edges) + output[row, 1] = float(component_count) + output[row, 2] = float(largest_size) + output[row, 3] = float(second_largest_size) + output[row, 4] = float(largest_size) / float(length) + output[row, 5] = float(second_largest_size) / float(length) + output[row, 6] = sum_size_sq + output[row, 7] = sum_size_fourth + output[row, 8] = q_g + output[row, 9] = 1.0 if four_sector_crossing else 0.0 + return 0 + + +@numba.njit( + _RUN_SIGNATURE, + cache=True, + boundscheck=True, + fastmath=False, +) +def _run_poisson_kernel( + length: int, + kappas: F64, + total_rate: float, + alias_probability: F64, + alias_index: I64, + multiplicity: U64, + class_start: U64, + keys: U64, + occupied: U8, + hash_diagnostics: U64, + parent: I64, + size: I64, + sector_mask: U8, + moments: F64, + counts: I64, + counters: U32, + keys_by_stream: U32, + blocks: U32, + lane_valid: U8, + draw_counts: U64, + output: F64, +) -> tuple[int, int, int]: + checkpoint = 0 + current_hazard_high = 0.0 + current_hazard_low = 0.0 + event_count = 0 + duplicate_count = 0 + kappa_max = kappas[len(kappas) - 1] + maximum_hazard = kappa_max * total_rate + class_count = len(alias_probability) + + while checkpoint < len(kappas) and kappas[checkpoint] == 0.0: + status = _record_checkpoint( + length, + checkpoint, + parent, + size, + sector_mask, + moments, + counts, + output, + ) + if status != 0: + return status, event_count, duplicate_count + checkpoint += 1 + + if kappa_max > 0.0: + while True: + exponential_uniform = uniform_open( + counters[STREAM_EXPONENTIAL], + keys_by_stream[STREAM_EXPONENTIAL], + blocks[STREAM_EXPONENTIAL], + lane_valid[STREAM_EXPONENTIAL], + draw_counts[STREAM_EXPONENTIAL], + ) + hazard = -math.log(exponential_uniform) + next_hazard_high, next_hazard_low = _compensated_hazard_add( + current_hazard_high, current_hazard_low, hazard + ) + if ( + not math.isfinite(hazard) + or hazard <= 0.0 + or not math.isfinite(next_hazard_high) + ): + return 1, event_count, duplicate_count + if _hazard_pair_greater_than_scalar( + next_hazard_high, next_hazard_low, maximum_hazard + ): + break + + while ( + checkpoint < len(kappas) + and _hazard_pair_greater_than_scalar( + next_hazard_high, + next_hazard_low, + kappas[checkpoint] * total_rate, + ) + ): + status = _record_checkpoint( + length, + checkpoint, + parent, + size, + sector_mask, + moments, + counts, + output, + ) + if status != 0: + return status, event_count, duplicate_count + checkpoint += 1 + + rejection_threshold = ( + np.uint64(1 << 32) - np.uint64(class_count) + ) % np.uint64(class_count) + while True: + column_word = next_u32( + counters[STREAM_ALIAS_COLUMN], + keys_by_stream[STREAM_ALIAS_COLUMN], + blocks[STREAM_ALIAS_COLUMN], + lane_valid[STREAM_ALIAS_COLUMN], + draw_counts[STREAM_ALIAS_COLUMN], + ) + product = np.uint64(column_word) * np.uint64(class_count) + if (product & _LOW32) < rejection_threshold: + if ( + draw_counts[STREAM_ALIAS_COLUMN, 2] + == np.uint64(0xFFFFFFFFFFFFFFFF) + ): + return 3, event_count, duplicate_count + draw_counts[STREAM_ALIAS_COLUMN, 2] += np.uint64(1) + continue + break + threshold_word = next_u32( + counters[STREAM_ALIAS_THRESHOLD], + keys_by_stream[STREAM_ALIAS_THRESHOLD], + blocks[STREAM_ALIAS_THRESHOLD], + lane_valid[STREAM_ALIAS_THRESHOLD], + draw_counts[STREAM_ALIAS_THRESHOLD], + ) + selected = draw_alias( + alias_probability, + alias_index, + column_word, + threshold_word, + ) + offset = int( + bounded_u32( + int(multiplicity[selected]), + counters[STREAM_EDGE_OFFSET], + keys_by_stream[STREAM_EDGE_OFFSET], + blocks[STREAM_EDGE_OFFSET], + lane_valid[STREAM_EDGE_OFFSET], + draw_counts[STREAM_EDGE_OFFSET], + ) + ) + + if event_count == _MAX_INT64: + return 4, event_count, duplicate_count + event_count += 1 + edge_id = class_start[selected] + np.uint64(offset) + keys, occupied, inserted = edge_set_insert_kernel( + keys, occupied, hash_diagnostics, edge_id + ) + if not inserted: + if duplicate_count == _MAX_INT64: + return 5, event_count, duplicate_count + duplicate_count += 1 + else: + if counts[0] == _MAX_INT64: + return 6, event_count, duplicate_count + counts[0] += 1 + distance = selected + 1 + left = offset + right = (offset + distance) % length + union_incremental( + parent, + size, + sector_mask, + moments, + counts, + left, + right, + ) + current_hazard_high = next_hazard_high + current_hazard_low = next_hazard_low + + while ( + checkpoint < len(kappas) + and _hazard_pair_at_least_scalar( + current_hazard_high, + current_hazard_low, + kappas[checkpoint] * total_rate, + ) + ): + status = _record_checkpoint( + length, + checkpoint, + parent, + size, + sector_mask, + moments, + counts, + output, + ) + if status != 0: + return status, event_count, duplicate_count + checkpoint += 1 + + while checkpoint < len(kappas): + status = _record_checkpoint( + length, + checkpoint, + parent, + size, + sector_mask, + moments, + counts, + output, + ) + if status != 0: + return status, event_count, duplicate_count + checkpoint += 1 + return 0, event_count, duplicate_count + + +def _validate_alias( + request: TrajectoryRequest, + kernel: F64, + alias: AliasTable, +) -> None: + if not isinstance(alias, AliasTable): + raise ValueError("alias must be an AliasTable") + class_count = request.length // 2 + arrays = ( + (alias.probability, np.dtype(np.float64), (class_count,), "probability"), + (alias.alias, np.dtype(np.int64), (class_count,), "index"), + ( + alias.multiplicity, + np.dtype(np.uint64), + (class_count,), + "multiplicity", + ), + ( + alias.class_weight, + np.dtype(np.float64), + (class_count,), + "class weight", + ), + ) + for value, dtype, shape, name in arrays: + if ( + not isinstance(value, np.ndarray) + or value.dtype != dtype + or value.shape != shape + or not value.flags.c_contiguous + ): + raise ValueError( + f"alias {name} must be a C-contiguous {dtype.name} array " + "with exact shape" + ) + if ( + np.any(~np.isfinite(alias.probability)) + or np.any(alias.probability < 0.0) + or np.any(alias.probability > 1.0) + ): + raise ValueError("alias probabilities must be finite and in [0, 1]") + if np.any(alias.alias < 0) or np.any(alias.alias >= class_count): + raise ValueError("alias index is outside the distance classes") + + expected_multiplicity = np.full( + class_count, request.length, dtype=np.uint64 + ) + expected_multiplicity[-1] = np.uint64(request.length // 2) + if not np.array_equal(alias.multiplicity, expected_multiplicity): + raise ValueError("alias multiplicity does not match the ring classes") + if int(alias.multiplicity.max()) > np.iinfo(np.uint32).max: + raise ValueError("alias multiplicity exceeds the bounded-draw range") + expected_weight = alias.multiplicity * kernel + if ( + np.any(~np.isfinite(alias.class_weight)) + or np.any(alias.class_weight <= 0.0) + or not np.array_equal(alias.class_weight, expected_weight) + ): + raise ValueError("alias class weights do not match kernel rates") + expected_total = math.fsum(float(value) for value in expected_weight) + if ( + not isinstance(alias.total_rate, (int, float)) + or not math.isfinite(float(alias.total_rate)) + or float(alias.total_rate) <= 0.0 + or float(alias.total_rate) != expected_total + ): + raise ValueError("alias total rate is not the canonical finite sum") + if ( + alias.kernel_sha256 != request.kernel_sha256 + or alias.kernel_sha256 + != hashlib.sha256(kernel.tobytes(order="C")).hexdigest() + ): + raise ValueError("alias kernel digest does not match the request") + expected_residual = ( + math.fsum(float(value / expected_total) for value in expected_weight) + - 1.0 + ) + if ( + not isinstance(alias.normalized_residual, (int, float)) + or not math.isfinite(float(alias.normalized_residual)) + or float(alias.normalized_residual) != expected_residual + ): + raise ValueError("alias normalized residual is not canonical") + + represented = np.zeros(class_count, dtype=np.float64) + correction = np.zeros(class_count, dtype=np.float64) + inverse_count = 1.0 / float(class_count) + for column in range(class_count): + direct = float(alias.probability[column]) * inverse_count + alternate = ( + 1.0 - float(alias.probability[column]) + ) * inverse_count + for target, contribution in ( + (column, direct), + (int(alias.alias[column]), alternate), + ): + combined = represented[target] + contribution + if abs(represented[target]) >= abs(contribution): + correction[target] += ( + represented[target] - combined + ) + contribution + else: + correction[target] += ( + contribution - combined + ) + represented[target] + represented[target] = combined + represented += correction + expected_probability = expected_weight / expected_total + unit_roundoff = 0.5 * np.finfo(np.float64).eps + fan_in = np.bincount(alias.alias, minlength=class_count).astype( + np.float64, copy=False + ) + scaled_error = (2.0 * fan_in + 8.0) * unit_roundoff + if np.any(scaled_error >= 1.0): + raise ValueError("alias semantic error bound exceeds float64 capacity") + gamma = scaled_error / (1.0 - scaled_error) + tolerance = gamma * ( + np.abs(expected_probability) + np.abs(represented) + ) + 8.0 * unit_roundoff * np.maximum( + expected_probability, inverse_count + ) + if np.any(~np.isfinite(represented)) or np.any( + np.abs(represented - expected_probability) > tolerance + ): + raise ValueError( + "alias represented class probabilities do not match class weights" + ) + + +def _build_stream_state( + request: TrajectoryRequest, +) -> tuple[U32, U32, U32, U8, U64]: + counters = np.empty((STREAM_COUNT, 4), dtype=np.uint32) + keys = np.empty((STREAM_COUNT, 2), dtype=np.uint32) + fingerprints: set[str] = set() + for stream_id in range(STREAM_COUNT): + material = derive_stream_material( + StreamIdentity( + master_seed=request.master_seed, + phase=request.phase, + length=request.length, + sigma_grid_id=request.sigma_grid_id, + replica=request.replica, + stream_id=stream_id, + ) + ) + if material.material_sha256 in fingerprints: + raise ValueError("derived RNG stream material collides") + fingerprints.add(material.material_sha256) + counters[stream_id] = material.initial_counter + keys[stream_id] = material.key + return ( + counters, + keys, + np.zeros((STREAM_COUNT, 4), dtype=np.uint32), + np.zeros((STREAM_COUNT, 2), dtype=np.uint8), + np.zeros((STREAM_COUNT, 3), dtype=np.uint64), + ) + + +def run_poisson_numba( + request: TrajectoryRequest, + kernel: F64, + alias: AliasTable, +) -> TrajectoryResult: + validate_trajectory_request(request) + _validate_kernel(request, kernel) + _validate_alias(request, kernel, alias) + total_rate = float(alias.total_rate) + kappa_max = float(request.kappas[-1]) + _validate_event_time_resolution( + kappa_max, total_rate, _MINIMUM_OPEN_HAZARD + ) + terminal_hazard = kappa_max * total_rate + if ( + math.isfinite(terminal_hazard) + and terminal_hazard > float(_MAX_INT64) + ): + raise ValueError("expected event count exceeds the int64 engine range") + canonical_edges = request.length * (request.length - 1) // 2 + if canonical_edges > _MAX_INT64: + raise ValueError("canonical edge count exceeds the int64 engine range") + + class_start = build_class_start(alias.multiplicity) + expected_class_start = np.empty( + alias.multiplicity.size + 1, dtype=np.uint64 + ) + expected_class_start[0] = np.uint64(0) + running = 0 + for index, raw_count in enumerate(alias.multiplicity): + running += int(raw_count) + expected_class_start[index + 1] = np.uint64(running) + if not np.array_equal(class_start, expected_class_start): + raise ValueError("class_start does not match alias multiplicities") + counters, stream_keys, blocks, lane_valid, draw_counts = ( + _build_stream_state(request) + ) + keys, occupied, hash_diagnostics = allocate_edge_set(0) + parent, size, sector_mask, moments, counts = allocate_union_find( + request.length + ) + output = np.empty((request.kappas.size, 10), dtype=np.float64) + + status, event_count, duplicate_count = _run_poisson_kernel( + request.length, + request.kappas, + total_rate, + alias.probability, + alias.alias, + alias.multiplicity, + class_start, + keys, + occupied, + hash_diagnostics, + parent, + size, + sector_mask, + moments, + counts, + counters, + stream_keys, + blocks, + lane_valid, + draw_counts, + output, + ) + if status != 0: + details = { + 1: "nonfinite exponential terminal comparison", + 2: "event hazard failed finite strict advancement", + 3: "alias rejection accounting overflow", + 4: "event counter overflow", + 5: "duplicate counter overflow", + 6: "open-edge counter overflow", + 200: "nonfinite checkpoint observable", + } + detail = details.get( + int(status), "incremental union-find checkpoint mismatch" + ) + raise RuntimeError( + f"Poisson kernel failed with status {status}: {detail}" + ) + + return TrajectoryResult( + request_sha256=_request_digest(request), + observables=output, + terminal_counters=counters, + draw_counts=draw_counts, + event_count=int(event_count), + duplicate_count=int(duplicate_count), + hash_diagnostics=hash_diagnostics, + ) + + +def assert_nopython_signatures() -> None: + if numba.config.DISABLE_JIT: + raise RuntimeError("nopython signatures are unavailable with JIT disabled") + dispatchers = (_run_poisson_kernel, _record_checkpoint) + missing = [ + dispatcher.py_func.__name__ + for dispatcher in dispatchers + if not dispatcher.nopython_signatures + ] + if missing: + raise RuntimeError( + "production kernels lack nopython signatures: " + ", ".join(missing) + ) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/production_union_find.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/production_union_find.py new file mode 100644 index 000000000..7a629635b --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/production_union_find.py @@ -0,0 +1,376 @@ +from __future__ import annotations + +import math +import sys + +import numba +import numpy as np +import numpy.typing as npt + +from .observables import BasicObservables + + +I64 = npt.NDArray[np.int64] +U8 = npt.NDArray[np.uint8] +F64 = npt.NDArray[np.float64] + +_I64_C = numba.types.Array(numba.int64, 1, "C") +_U8_C = numba.types.Array(numba.uint8, 1, "C") +_F64_C = numba.types.Array(numba.float64, 1, "C") +_UNION_SIGNATURE = numba.boolean( + _I64_C, + _I64_C, + _U8_C, + _F64_C, + _I64_C, + numba.int64, + numba.int64, +) +_SCAN_RESULT = numba.types.Tuple( + ( + numba.int64, + numba.int64, + numba.int64, + numba.int64, + numba.int64, + numba.float64, + numba.float64, + numba.float64, + numba.boolean, + ) +) +_SCAN_SIGNATURE = _SCAN_RESULT(_I64_C, _I64_C, _U8_C, _F64_C, _I64_C) +_EPSILON = np.finfo(np.float64).eps +_MAX_INT64 = np.iinfo(np.int64).max + + +def allocate_union_find(length: int) -> tuple[I64, I64, U8, F64, I64]: + if ( + isinstance(length, (bool, np.bool_)) + or not isinstance(length, (int, np.integer)) + or int(length) < 1 + or int(length) > min(sys.maxsize, _MAX_INT64) + ): + raise ValueError("length must be a positive addressable integer") + length_value = int(length) + + parent = np.arange(length_value, dtype=np.int64) + size = np.ones(length_value, dtype=np.int64) + vertices = np.arange(length_value, dtype=np.int64) + sector = np.minimum(3, (4 * vertices) // length_value) + sector_mask = np.left_shift( + np.uint8(1), sector.astype(np.uint8) + ).astype(np.uint8) + moments = np.asarray( + (float(length_value), float(length_value)), dtype=np.float64 + ) + counts = np.asarray((0, length_value, 1), dtype=np.int64) + validate_union_find_state(parent, size, sector_mask, moments, counts) + return parent, size, sector_mask, moments, counts + + +def validate_union_find_state( + parent: I64, + size: I64, + sector_mask: U8, + moments: F64, + counts: I64, +) -> None: + arrays = ( + (parent, np.dtype(np.int64), "parent"), + (size, np.dtype(np.int64), "size"), + (sector_mask, np.dtype(np.uint8), "sector_mask"), + (moments, np.dtype(np.float64), "moments"), + (counts, np.dtype(np.int64), "counts"), + ) + for array, dtype, name in arrays: + if not isinstance(array, np.ndarray) or array.dtype != dtype: + raise ValueError(f"{name} must be a {dtype.name} NumPy array") + if array.ndim != 1: + raise ValueError(f"{name} must be one-dimensional") + if not array.flags.c_contiguous: + raise ValueError(f"{name} must be C-contiguous") + if not array.flags.writeable: + raise ValueError(f"{name} must be writable") + + length = parent.size + if length < 1: + raise ValueError("parent must be nonempty") + if size.shape != (length,): + raise ValueError("size must have the same shape as parent") + if sector_mask.shape != (length,): + raise ValueError("sector_mask must have the same shape as parent") + if moments.shape != (2,): + raise ValueError("moments must have shape (2,)") + if counts.shape != (3,): + raise ValueError("counts must have shape (3,)") + + state_arrays = (parent, size, sector_mask, moments, counts) + for left in range(len(state_arrays)): + for right in range(left + 1, len(state_arrays)): + if np.shares_memory(state_arrays[left], state_arrays[right]): + raise ValueError("union-find arrays must not overlap or share memory") + + if np.any(parent < 0) or np.any(parent >= length): + raise ValueError("parent contains an out-of-range index") + if np.any(size < 1) or np.any(size > length): + raise ValueError("size contains an out-of-range component size") + if np.any(sector_mask == 0) or np.any(sector_mask > np.uint8(0b1111)): + raise ValueError("sector_mask must contain nonzero four-bit masks") + if not np.all(np.isfinite(moments)) or np.any(moments < 1.0): + raise ValueError("moments must contain finite positive values") + + open_edges = int(counts[0]) + component_count = int(counts[1]) + largest_size = int(counts[2]) + maximum_edges = length * (length - 1) // 2 + if not 0 <= open_edges <= maximum_edges: + raise ValueError("open_edges is outside the simple-graph range") + if not 1 <= component_count <= length: + raise ValueError("component_count is outside the valid range") + if not 1 <= largest_size <= length: + raise ValueError("largest_size is outside the valid range") + + +@numba.njit( + _UNION_SIGNATURE, cache=True, boundscheck=True, fastmath=False +) +def union_incremental( + parent: I64, + size: I64, + sector_mask: U8, + moments: F64, + counts: I64, + left: int, + right: int, +) -> bool: + length = len(parent) + if ( + len(size) != length + or len(sector_mask) != length + or len(moments) != 2 + or len(counts) != 3 + ): + raise ValueError("union-find state arrays have inconsistent shapes") + if ( + counts[0] < 0 + or counts[1] < 1 + or counts[1] > length + or counts[2] < 1 + or counts[2] > length + or not math.isfinite(moments[0]) + or not math.isfinite(moments[1]) + ): + raise ValueError("union-find counters or moments are invalid") + if left < 0 or left >= length or right < 0 or right >= length: + raise ValueError("union endpoint is out of range") + + root_left = left + steps = 0 + while parent[root_left] != root_left: + ancestor = parent[root_left] + if ancestor < 0 or ancestor >= length: + raise ValueError("parent contains an out-of-range index") + grandparent = parent[ancestor] + if grandparent < 0 or grandparent >= length: + raise ValueError("parent contains an out-of-range index") + parent[root_left] = grandparent + root_left = grandparent + steps += 1 + if steps > length: + raise ValueError("parent does not describe a forest") + + root_right = right + steps = 0 + while parent[root_right] != root_right: + ancestor = parent[root_right] + if ancestor < 0 or ancestor >= length: + raise ValueError("parent contains an out-of-range index") + grandparent = parent[ancestor] + if grandparent < 0 or grandparent >= length: + raise ValueError("parent contains an out-of-range index") + parent[root_right] = grandparent + root_right = grandparent + steps += 1 + if steps > length: + raise ValueError("parent does not describe a forest") + + if root_left == root_right: + return False + + size_left = size[root_left] + size_right = size[root_right] + if ( + size_left < 1 + or size_right < 1 + or size_left > length + or size_right > length + or size_left > length - size_right + ): + raise OverflowError("component sizes exceed the union-find length") + if size_left < size_right or ( + size_left == size_right and root_left > root_right + ): + root_left, root_right = root_right, root_left + size_left, size_right = size_right, size_left + + merged_size = size_left + size_right + left_float = float(size_left) + right_float = float(size_right) + product = left_float * right_float + delta_sum_sq = 2.0 * product + delta_sum_fourth = 2.0 * product * ( + 2.0 * left_float * left_float + + 3.0 * product + + 2.0 * right_float * right_float + ) + new_sum_sq = moments[0] + delta_sum_sq + new_sum_fourth = moments[1] + delta_sum_fourth + if not math.isfinite(new_sum_sq) or not math.isfinite(new_sum_fourth): + raise OverflowError("component moments exceed float64") + if counts[1] <= 1: + raise ValueError("component_count cannot be decremented") + + parent[root_right] = root_left + size[root_left] = merged_size + sector_mask[root_left] = sector_mask[root_left] | sector_mask[root_right] + moments[0] = new_sum_sq + moments[1] = new_sum_fourth + counts[1] -= 1 + if merged_size > counts[2]: + counts[2] = merged_size + return True + + +@numba.njit( + _SCAN_SIGNATURE, cache=True, boundscheck=True, fastmath=False +) +def _scan_basic_observables_kernel( + parent: I64, + size: I64, + sector_mask: U8, + moments: F64, + counts: I64, +) -> tuple[int, int, int, int, int, float, float, float, bool]: + length = len(parent) + component_count = 0 + largest_size = 0 + second_largest_size = 0 + sum_size = 0 + sum_size_sq = 0.0 + sum_size_fourth = 0.0 + four_sector_crossing = False + + for index in range(length): + if parent[index] < 0 or parent[index] >= length: + return (1, 0, 0, 0, 0, 0.0, 0.0, 0.0, False) + if parent[index] != index: + continue + component_size = size[index] + if component_size < 1 or component_size > length: + return (2, 0, 0, 0, 0, 0.0, 0.0, 0.0, False) + component_count += 1 + sum_size += component_size + component_float = float(component_size) + sum_size_sq += component_float * component_float + sum_size_fourth += component_float**4 + + if component_size > largest_size: + second_largest_size = largest_size + largest_size = component_size + elif component_size > second_largest_size: + second_largest_size = component_size + if sector_mask[index] == np.uint8(0b1111): + four_sector_crossing = True + + if component_count != counts[1] or sum_size != length: + return (3, 0, 0, 0, 0, 0.0, 0.0, 0.0, False) + if largest_size != counts[2]: + return (4, 0, 0, 0, 0, 0.0, 0.0, 0.0, False) + successful_joins = length - component_count + unit_roundoff = 0.5 * _EPSILON + sq_steps = 4 * successful_joins + component_count + 32 + fourth_steps = 10 * successful_joins + 2 * component_count + 32 + sq_scaled = float(sq_steps) * unit_roundoff + fourth_scaled = float(fourth_steps) * unit_roundoff + if sq_scaled >= 1.0 or fourth_scaled >= 1.0: + return (5, 0, 0, 0, 0, 0.0, 0.0, 0.0, False) + sq_gamma = sq_scaled / (1.0 - sq_scaled) + fourth_gamma = fourth_scaled / (1.0 - fourth_scaled) + tolerance_sq = sq_gamma * max(1.0, abs(moments[0]), sum_size_sq) + tolerance_fourth = fourth_gamma * max( + 1.0, abs(moments[1]), sum_size_fourth + ) + if ( + not math.isfinite(moments[0]) + or abs(moments[0] - sum_size_sq) > tolerance_sq + ): + return (5, 0, 0, 0, 0, 0.0, 0.0, 0.0, False) + if ( + not math.isfinite(moments[1]) + or abs(moments[1] - sum_size_fourth) > tolerance_fourth + ): + return (6, 0, 0, 0, 0, 0.0, 0.0, 0.0, False) + + q_g = sum_size_fourth / (sum_size_sq * sum_size_sq) + return ( + 0, + counts[0], + component_count, + largest_size, + second_largest_size, + sum_size_sq, + sum_size_fourth, + q_g, + four_sector_crossing, + ) + + +def scan_basic_observables( + parent: I64, + size: I64, + sector_mask: U8, + moments: F64, + counts: I64, +) -> BasicObservables: + validate_union_find_state(parent, size, sector_mask, moments, counts) + ( + status, + open_edges, + component_count, + largest_size, + second_largest_size, + sum_size_sq, + sum_size_fourth, + q_g, + four_sector_crossing, + ) = _scan_basic_observables_kernel( + parent, size, sector_mask, moments, counts + ) + if status != 0: + detail = { + 1: "parent index", + 2: "root size", + 3: "component count", + 4: "largest size", + 5: "sum-size-squared moment", + 6: "sum-size-fourth moment", + }.get(int(status), "unknown state") + raise RuntimeError( + f"union-find checkpoint scan failed with status {status} " + f"({detail}); " + "incremental state does not match its exact root scan" + ) + length = parent.size + return BasicObservables( + open_edges=int(open_edges), + component_count=int(component_count), + largest_size=int(largest_size), + second_largest_size=int(second_largest_size), + s1_fraction=float(largest_size) / float(length), + s2_fraction=float(second_largest_size) / float(length), + sum_size_sq=float(sum_size_sq), + sum_size_fourth=float(sum_size_fourth), + q_g=float(q_g), + four_sector_crossing=bool(four_sector_crossing), + ) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/runtime.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/runtime.py new file mode 100644 index 000000000..403b0cbf4 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/runtime.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +import hashlib +import importlib.metadata +import json +import os +from pathlib import Path +import platform +import re +import subprocess +import sys + +import numba + + +_CAPABILITY_KEYS = { + "schema_version", + "python", + "implementation", + "platform", + "machine", + "numpy", + "scipy", + "h5py", + "numba", + "llvmlite", + "cpu_name", + "cpu_features", + "threading_layer", + "numba_disable_jit", + "fastmath", + "boundscheck", +} + + +def runtime_capability() -> dict[str, object]: + return { + "schema_version": "challenge-194-runtime-v1", + "python": platform.python_version(), + "implementation": sys.implementation.name, + "platform": platform.platform(), + "machine": platform.machine(), + "numpy": importlib.metadata.version("numpy"), + "scipy": importlib.metadata.version("scipy"), + "h5py": importlib.metadata.version("h5py"), + "numba": importlib.metadata.version("numba"), + "llvmlite": importlib.metadata.version("llvmlite"), + "cpu_name": numba.config.CPU_NAME or "", + "cpu_features": numba.config.CPU_FEATURES or "", + "threading_layer": os.environ.get("NUMBA_THREADING_LAYER", ""), + "numba_disable_jit": bool(numba.config.DISABLE_JIT), + "fastmath": False, + "boundscheck": True, + } + + +def _git_output(repository_root: Path, *arguments: str) -> str: + command = ["git", *arguments] + operation = "git " + " ".join(arguments) + try: + completed = subprocess.run( + command, + cwd=repository_root, + check=True, + capture_output=True, + text=True, + ) + except subprocess.CalledProcessError as error: + detail = (error.stderr or error.stdout or "").strip() + message = f"{operation} failed" + if detail: + message += f": {detail}" + raise RuntimeError(message) from error + except OSError as error: + raise RuntimeError(f"unable to execute {operation}: {error}") from error + return completed.stdout.strip() + + +def _canonical_capability_bytes() -> bytes: + capability = runtime_capability() + if not isinstance(capability, dict) or set(capability) != _CAPABILITY_KEYS: + raise RuntimeError("runtime capability is not canonical JSON") + try: + encoded = json.dumps( + capability, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + decoded = json.loads(encoded) + except (TypeError, ValueError, json.JSONDecodeError) as error: + raise RuntimeError("runtime capability is not canonical JSON") from error + if decoded != capability: + raise RuntimeError("runtime capability is not canonical JSON") + return encoded + + +def runtime_provenance(repository_root: Path) -> dict[str, str]: + lockfile = repository_root / "uv.lock" + if lockfile.is_symlink() or not lockfile.is_file(): + raise RuntimeError("uv.lock must be a regular non-symlink file") + try: + lock_bytes = lockfile.read_bytes() + except OSError as error: + raise RuntimeError(f"unable to read uv.lock: {error}") from error + + if _git_output(repository_root, "status", "--porcelain"): + raise RuntimeError("repository is dirty") + revision = _git_output(repository_root, "rev-parse", "HEAD") + if re.fullmatch(r"[0-9a-f]{40}", revision) is None: + raise RuntimeError("malformed Git revision") + + capability_bytes = _canonical_capability_bytes() + return { + "schema_version": "challenge-194-runtime-provenance-v1", + "source_revision": revision, + "uv_lock_sha256": hashlib.sha256(lock_bytes).hexdigest(), + "runtime_capability_sha256": hashlib.sha256( + capability_bytes + ).hexdigest(), + } diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/sample.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/sample.py new file mode 100644 index 000000000..0099b01c2 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/sample.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from dataclasses import dataclass + +import numpy as np + +from .union_find import UnionFind + + +def _validated_int64_array(array: object, name: str) -> np.ndarray: + values = np.asarray(array) + dtype = values.dtype + if dtype == np.bool_: + raise ValueError(f"{name} must use an integer dtype") + if np.issubdtype(dtype, np.floating): + raise ValueError(f"{name} must use an integer dtype") + if np.issubdtype(dtype, np.complexfloating): + raise ValueError(f"{name} must use an integer dtype") + if dtype == np.object_: + raise ValueError(f"{name} must use an integer dtype") + if not np.issubdtype(dtype, np.integer): + raise ValueError(f"{name} must use an integer dtype") + if np.issubdtype(dtype, np.unsignedinteger) and values.size: + if np.any(values > np.iinfo(np.int64).max): + raise ValueError( + f"{name} contains values that cannot be represented as int64" + ) + return np.array(values, dtype=np.int64, copy=True) + + +@dataclass(frozen=True) +class GraphSample: + length: int + edges: np.ndarray + labels: np.ndarray + + def __post_init__(self) -> None: + if ( + isinstance(self.length, bool) + or not isinstance(self.length, int) + or self.length < 1 + ): + raise ValueError("length must be a positive integer") + edges = _validated_int64_array(self.edges, "edges") + labels = _validated_int64_array(self.labels, "labels") + if edges.ndim != 2 or edges.shape[1:] != (2,): + raise ValueError("edges must have shape (n_edges, 2)") + if labels.shape != (self.length,): + raise ValueError("labels must have shape (length,)") + if edges.size and ( + np.any(edges < 0) or np.any(edges >= self.length) + ): + raise ValueError("edge endpoint is out of range") + if edges.size and np.any(edges[:, 0] >= edges[:, 1]): + raise ValueError("edges must have canonical increasing endpoints") + edge_tuples = [tuple(edge) for edge in edges.tolist()] + if edge_tuples != sorted(edge_tuples): + raise ValueError("edges must be sorted") + if len(edge_tuples) != len(set(edge_tuples)): + raise ValueError("duplicate edges are forbidden") + union_find = UnionFind(self.length) + for left, right in edge_tuples: + union_find.union(left, right) + if not np.array_equal(labels, union_find.labels()): + raise ValueError("labels do not match the edge-induced partition") + edges.setflags(write=False) + labels.setflags(write=False) + object.__setattr__(self, "edges", edges) + object.__setattr__(self, "labels", labels) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/trajectory.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/trajectory.py new file mode 100644 index 000000000..6149516be --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/trajectory.py @@ -0,0 +1,298 @@ +from __future__ import annotations + +from dataclasses import dataclass +import hashlib +import json +import math +import re +import sys +from typing import Literal + +import numpy as np +import numpy.typing as npt + + +Phase = Literal["validation", "benchmark", "pilot", "confirmatory"] +F64 = npt.NDArray[np.float64] +U32 = npt.NDArray[np.uint32] +U64 = npt.NDArray[np.uint64] + +_STREAM_COUNT = 4 +_UINT64_LIMIT = 1 << 64 +_PHASES = frozenset(("validation", "benchmark", "pilot", "confirmatory")) +_HEX256 = re.compile(r"[0-9a-f]{64}") +_REQUEST_DOMAIN = b"challenge-194-trajectory-request-v1\0" + + +def _frozen_copy(array: np.ndarray, dtype: np.dtype) -> np.ndarray: + copy = np.array(array, dtype=dtype, order="C", copy=True) + copy.setflags(write=False) + return copy + + +@dataclass(frozen=True) +class TrajectoryRequest: + length: int + sigma: float + sigma_grid_id: str + kappas: F64 + master_seed: int + phase: Phase + replica: int + kernel_sha256: str + + def __post_init__(self) -> None: + if ( + not isinstance(self.kappas, np.ndarray) + or self.kappas.dtype != np.dtype(np.float64) + or self.kappas.ndim != 1 + or not self.kappas.flags.c_contiguous + ): + raise ValueError( + "kappas must be a C-contiguous one-dimensional float64 array" + ) + object.__setattr__( + self, + "kappas", + _frozen_copy(self.kappas, np.dtype(np.float64)), + ) + + +@dataclass(frozen=True) +class TrajectoryResult: + request_sha256: str + observables: F64 + terminal_counters: U32 + draw_counts: U64 + event_count: int + duplicate_count: int + hash_diagnostics: U64 + + def __post_init__(self) -> None: + if not isinstance(self.request_sha256, str) or _HEX256.fullmatch( + self.request_sha256 + ) is None: + raise ValueError("request_sha256 must be a lowercase SHA-256 digest") + arrays = ( + (self.observables, np.dtype(np.float64), 2, "observables"), + ( + self.terminal_counters, + np.dtype(np.uint32), + 2, + "terminal_counters", + ), + (self.draw_counts, np.dtype(np.uint64), 2, "draw_counts"), + ( + self.hash_diagnostics, + np.dtype(np.uint64), + 1, + "hash_diagnostics", + ), + ) + for value, dtype, ndim, name in arrays: + if ( + not isinstance(value, np.ndarray) + or value.dtype != dtype + or value.ndim != ndim + or not value.flags.c_contiguous + ): + raise ValueError( + f"{name} must be a C-contiguous {ndim}-dimensional " + f"{dtype.name} array" + ) + if self.observables.shape[1:] != (10,): + raise ValueError("observables must have shape (n_kappa, 10)") + if self.terminal_counters.shape != (_STREAM_COUNT, 4): + raise ValueError("terminal_counters must have shape (4, 4)") + if self.draw_counts.shape != (_STREAM_COUNT, 3): + raise ValueError("draw_counts must have shape (4, 3)") + if self.hash_diagnostics.shape != (5,): + raise ValueError("hash_diagnostics must have shape (5,)") + for value, name in ( + (self.event_count, "event_count"), + (self.duplicate_count, "duplicate_count"), + ): + if ( + isinstance(value, bool) + or not isinstance(value, int) + or value < 0 + ): + raise ValueError(f"{name} must be a nonnegative Python integer") + if self.duplicate_count > self.event_count: + raise ValueError("duplicate_count cannot exceed event_count") + if not np.all(np.isfinite(self.observables)): + raise ValueError("observables must be finite") + for name, value, dtype in ( + ("observables", self.observables, np.dtype(np.float64)), + ("terminal_counters", self.terminal_counters, np.dtype(np.uint32)), + ("draw_counts", self.draw_counts, np.dtype(np.uint64)), + ("hash_diagnostics", self.hash_diagnostics, np.dtype(np.uint64)), + ): + object.__setattr__(self, name, _frozen_copy(value, dtype)) + + +@dataclass(frozen=True) +class TrajectoryDiagnostics: + result: TrajectoryResult + event_times: tuple[float, ...] + edge_ids_by_checkpoint: tuple[frozenset[int], ...] + + def __post_init__(self) -> None: + if not isinstance(self.result, TrajectoryResult): + raise ValueError("result must be a TrajectoryResult") + if len(self.edge_ids_by_checkpoint) != self.result.observables.shape[0]: + raise ValueError("edge diagnostics must cover every checkpoint") + if any( + not math.isfinite(value) or value < 0.0 + for value in self.event_times + ): + raise ValueError("event times must be finite and nonnegative") + if any( + not isinstance(snapshot, frozenset) + or any( + isinstance(edge_id, bool) + or not isinstance(edge_id, int) + or edge_id < 0 + for edge_id in snapshot + ) + for snapshot in self.edge_ids_by_checkpoint + ): + raise ValueError("edge diagnostics must contain nonnegative IDs") + + +def validate_trajectory_request(request: TrajectoryRequest) -> None: + if not isinstance(request, TrajectoryRequest): + raise ValueError("request must be a TrajectoryRequest") + if ( + isinstance(request.length, bool) + or not isinstance(request.length, int) + or request.length < 2 + or request.length % 2 + or request.length > sys.maxsize + ): + raise ValueError("length must be an even addressable Python integer") + if ( + isinstance(request.sigma, bool) + or not isinstance(request.sigma, (int, float)) + ): + raise ValueError("sigma must be a finite positive real number") + sigma = float(request.sigma) + exponent = 1.0 + sigma + if ( + not math.isfinite(sigma) + or sigma <= 0.0 + or not math.isfinite(exponent) + or exponent <= 1.0 + ): + raise ValueError( + "sigma must be finite, positive, and satisfy 1.0 + sigma > 1.0" + ) + if ( + not isinstance(request.kappas, np.ndarray) + or request.kappas.dtype != np.dtype(np.float64) + or request.kappas.ndim != 1 + or not request.kappas.flags.c_contiguous + or request.kappas.size < 1 + or np.any(~np.isfinite(request.kappas)) + or np.any(request.kappas < 0.0) + or ( + request.kappas.size > 1 + and np.any(request.kappas[1:] <= request.kappas[:-1]) + ) + ): + raise ValueError( + "kappas must be finite, nonnegative, sorted, unique float64 values" + ) + for value, name in ( + (request.master_seed, "master_seed"), + (request.replica, "replica"), + ): + if ( + isinstance(value, bool) + or not isinstance(value, int) + or not 0 <= value < _UINT64_LIMIT + ): + raise ValueError(f"{name} must fit uint64") + if not isinstance(request.phase, str) or request.phase not in _PHASES: + raise ValueError("phase is not in the frozen phase namespace") + if ( + not isinstance(request.sigma_grid_id, str) + or not request.sigma_grid_id + or request.sigma_grid_id != request.sigma_grid_id.strip() + or any( + ord(character) < 0x20 or ord(character) == 0x7F + for character in request.sigma_grid_id + ) + ): + raise ValueError( + "sigma_grid_id must be trimmed, nonempty, and contain no controls" + ) + try: + request.sigma_grid_id.encode("utf-8") + except UnicodeEncodeError as error: + raise ValueError("sigma_grid_id must be valid UTF-8") from error + if ( + not isinstance(request.kernel_sha256, str) + or _HEX256.fullmatch(request.kernel_sha256) is None + ): + raise ValueError("kernel_sha256 must be a lowercase SHA-256 digest") + + +def validate_kernel(request: TrajectoryRequest, kernel: F64) -> None: + if ( + not isinstance(kernel, np.ndarray) + or kernel.dtype != np.dtype(np.float64) + or kernel.shape != (request.length // 2,) + or not kernel.flags.c_contiguous + ): + raise ValueError( + "kernel must be a C-contiguous float64 array with exact shape" + ) + if np.any(~np.isfinite(kernel)) or np.any(kernel <= 0.0): + raise ValueError("kernel must contain finite positive values") + actual = hashlib.sha256(kernel.tobytes(order="C")).hexdigest() + if actual != request.kernel_sha256: + raise ValueError("kernel digest does not match kernel_sha256") + + +def validate_event_time_resolution( + kappa_max: float, + total_rate: float, + minimum_hazard: float, +) -> None: + if not math.isfinite(minimum_hazard) or minimum_hazard <= 0.0: + raise ValueError("minimum exponential hazard must be finite and positive") + terminal_hazard = kappa_max * total_rate + if not math.isfinite(terminal_hazard): + raise ValueError("largest coupling times total rate must be finite") + if terminal_hazard > float((1 << 63) - 1): + raise ValueError( + "expected event count exceeds the int64 engine range" + ) + + +def request_digest(request: TrajectoryRequest) -> str: + document = { + "kernel_sha256": request.kernel_sha256, + "kappas_le_f64": request.kappas.astype(" int: + if isinstance(node, bool) or not isinstance(node, int): + raise ValueError("node must be an integer") + if not 0 <= node < self.parent.size: + raise ValueError("node is out of range") + root = node + while self.parent[root] != root: + root = int(self.parent[root]) + while self.parent[node] != node: + parent = int(self.parent[node]) + self.parent[node] = root + node = parent + return root + + def union(self, left: int, right: int) -> bool: + root_left = self.find(left) + root_right = self.find(right) + if root_left == root_right: + return False + if ( + self.size[root_left] < self.size[root_right] + or ( + self.size[root_left] == self.size[root_right] + and root_left > root_right + ) + ): + root_left, root_right = root_right, root_left + self.parent[root_right] = root_left + self.size[root_left] += self.size[root_right] + return True + + def labels(self) -> np.ndarray: + roots = np.array([self.find(i) for i in range(self.parent.size)]) + minimum = {} + for node, root in enumerate(roots.tolist()): + minimum[root] = min(node, minimum.get(root, node)) + return np.array([minimum[int(root)] for root in roots], dtype=np.int64) + + def component_sizes(self) -> np.ndarray: + _, counts = np.unique(self.labels(), return_counts=True) + return np.sort(counts.astype(np.int64))[::-1] diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/validation.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/validation.py new file mode 100644 index 000000000..3765a7be1 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/validation.py @@ -0,0 +1,1739 @@ +from __future__ import annotations + +from concurrent.futures import ThreadPoolExecutor +from collections import Counter +from dataclasses import dataclass, replace +import ast +import hashlib +import json +import math +import os +from pathlib import Path +import subprocess +import time +from typing import Iterable, Mapping, Sequence +import uuid +from itertools import combinations + +import numpy as np +from scipy.stats import binomtest, poisson + +from .alias import build_distance_alias +from .counter_rng import ( + StreamIdentity, + derive_stream_material, + philox4x32_10, + philox4x32_10_reference, +) +from .edge_set import allocate_edge_set, edge_set_insert +from .geometric import sample_geometric +from .kernel import edge_probabilities, periodic_kernel +from .model import ModelSpec, canonical_edge, distance_classes, iter_unordered_edges +from .oracle import no_edge_probability, sample_quadratic +from .poisson_reference import ( + run_poisson_reference_with_diagnostics, +) +from .poisson_sweep import run_poisson_numba +from .production_union_find import ( + allocate_union_find, + scan_basic_observables, + union_incremental, +) +from .runtime import runtime_capability +from .trajectory import TrajectoryDiagnostics, TrajectoryRequest +from .union_find import UnionFind + + +VALIDATION_PROTOCOL_VERSION = "challenge-194-validation-v1" +FAMILYWISE_ALPHA = 0.001 +LENGTHS = (4, 6, 8, 16, 32, 64, 128, 256) +SIGMAS = (0.8, 1.0, 1.1) +KAPPAS = (0.0, 0.25, 0.7, 2.0, 6.0) +SAMPLES_BY_LENGTH = { + 4: 32768, + 6: 32768, + 8: 32768, + 16: 16384, + 32: 8192, + 64: 4096, + 128: 2048, + 256: 1024, +} +SAMPLERS = ("quadratic", "geometric", "poisson-reference", "poisson-numba") +MASTER_SEEDS = tuple(range(194_000_000, 194_032_768)) + +THREE_WAY_SAMPLERS = ("quadratic", "geometric", "poisson-numba") +PAIR_NAMES = tuple(combinations(SAMPLERS, 2)) +OBSERVABLE_SCHEMA = { + "normalized-second-moment": { + "formula": "sum_C(|C|^2)/L^2", + "source_column": 6, + "normalization_power": 2, + }, + "normalized-fourth-moment": { + "formula": "sum_C(|C|^4)/L^4", + "source_column": 7, + "normalization_power": 4, + }, +} +SCALAR_COLUMNS = { + "open-count": 0, + "S1": 4, + "S2": 5, + "normalized-second-moment": 6, + "normalized-fourth-moment": 7, + "QG": 8, + "four-sector": 9, +} +STATISTICAL_FAMILIES = ( + "all-graph-probability", + "edge-class-frequency", + "poisson-event-count", + "no-edge", + "bond-length", + "component-partition", + *SCALAR_COLUMNS, +) +EXACT_FAMILIES = ( + "philox-vectors", + "stream-separation", + "bounded-integer-accounting", + "alias-invariants", + "edge-id-uniqueness", + "hash-full-range-growth", + "all-graph-exact", + "kappa-zero", + "saturated-coupling", + "antipodal-counts", + "finite-parameter-extremes", + "duplicate-limits", + "incremental-root-scan", + "process-order-identity", + "sampler-structure", +) + + +def _strict_positive_int(value: object, name: str) -> int: + if isinstance(value, bool) or not isinstance(value, int) or value < 1: + raise ValueError(f"{name} must be a positive integer") + return value + + +def _f64(value: float) -> str: + return float(value).hex() + + +@dataclass(frozen=True) +class ValidationCase: + length: int + sigma: float + kappa: float + samples: int + seed_start: int + + @property + def case_id(self) -> str: + return ( + f"L{self.length}/sigma-{_f64(self.sigma)}/" + f"kappa-{_f64(self.kappa)}" + ) + + +def frozen_family_denominators( + lengths: Sequence[int], + sigmas: Sequence[float], + kappas: Sequence[float], +) -> dict[str, int]: + cases = len(lengths) * len(sigmas) * len(kappas) + edge_classes = sum(length // 2 for length in lengths) * len(sigmas) * len(kappas) + small_graphs = sum( + 1 << (length * (length - 1) // 2) + for length in lengths + if length <= 6 + ) + return { + "all-graph-probability": 4 * small_graphs * len(sigmas) * len(kappas), + "edge-class-frequency": 4 * edge_classes, + "poisson-event-count": 2 * cases, + "no-edge": 4 * cases, + "bond-length": 6 * cases, + "component-partition": 6 * cases, + **{name: 6 * cases for name in SCALAR_COLUMNS}, + } + + +@dataclass(frozen=True) +class ValidationProtocol: + lengths: tuple[int, ...] + sigmas: tuple[float, ...] + kappas: tuple[float, ...] + samples_by_length: Mapping[int, int] + master_seeds: tuple[int, ...] + familywise_alpha: float = FAMILYWISE_ALPHA + permutation_replicates: int = 49_999 + multinomial_replicates: int = 49_999 + jobs: int = 1 + name: str = "production-v1" + + def __post_init__(self) -> None: + if not self.lengths or any( + isinstance(length, bool) + or not isinstance(length, int) + or length < 2 + or length % 2 + for length in self.lengths + ): + raise ValueError("lengths must contain positive even integers") + if len(set(self.lengths)) != len(self.lengths): + raise ValueError("lengths must be unique") + for values, name, positive in ( + (self.sigmas, "sigmas", True), + (self.kappas, "kappas", False), + ): + if not values or any( + isinstance(value, bool) + or not isinstance(value, (int, float)) + or not math.isfinite(float(value)) + or (float(value) <= 0.0 if positive else float(value) < 0.0) + for value in values + ): + raise ValueError(f"{name} contain invalid values") + if len(set(float(value) for value in values)) != len(values): + raise ValueError(f"{name} must be unique") + if set(self.samples_by_length) != set(self.lengths): + raise ValueError("sample counts must cover every length exactly") + for count in self.samples_by_length.values(): + _strict_positive_int(count, "sample count") + required_seeds = max(self.samples_by_length.values()) + if ( + len(self.master_seeds) < required_seeds + or len(set(self.master_seeds)) != len(self.master_seeds) + or any( + isinstance(seed, bool) + or not isinstance(seed, int) + or not 0 <= seed < 1 << 64 + for seed in self.master_seeds + ) + ): + raise ValueError("master seeds are insufficient, repeated, or invalid") + if ( + not math.isfinite(float(self.familywise_alpha)) + or not 0.0 < float(self.familywise_alpha) < 1.0 + ): + raise ValueError("familywise alpha must be in (0, 1)") + _strict_positive_int(self.permutation_replicates, "permutation replicates") + _strict_positive_int(self.multinomial_replicates, "multinomial replicates") + _strict_positive_int(self.jobs, "jobs") + + @classmethod + def production_v1(cls) -> ValidationProtocol: + return cls( + lengths=LENGTHS, + sigmas=SIGMAS, + kappas=KAPPAS, + samples_by_length=dict(SAMPLES_BY_LENGTH), + master_seeds=MASTER_SEEDS, + ) + + @classmethod + def reduced( + cls, + *, + lengths: Sequence[int] = (4,), + sigmas: Sequence[float] = (1.0,), + kappas: Sequence[float] = (0.0, 0.25), + samples: int = 8, + replicates: int = 31, + jobs: int = 1, + ) -> ValidationProtocol: + count = _strict_positive_int(samples, "samples") + frozen_lengths = tuple(lengths) + return cls( + lengths=frozen_lengths, + sigmas=tuple(float(value) for value in sigmas), + kappas=tuple(float(value) for value in kappas), + samples_by_length={length: count for length in frozen_lengths}, + master_seeds=MASTER_SEEDS[:count], + permutation_replicates=replicates, + multinomial_replicates=replicates, + jobs=jobs, + name="reduced-test-v1", + ) + + @property + def is_production(self) -> bool: + return ( + self.name == "production-v1" + and self.lengths == LENGTHS + and self.sigmas == SIGMAS + and self.kappas == KAPPAS + and dict(self.samples_by_length) == SAMPLES_BY_LENGTH + and self.master_seeds == MASTER_SEEDS + and self.familywise_alpha == FAMILYWISE_ALPHA + and self.permutation_replicates == 49_999 + and self.multinomial_replicates == 49_999 + ) + + def require_production(self) -> None: + if not self.is_production: + raise ValueError("CLI requires the exact production-v1 protocol") + + @property + def case_registry(self) -> tuple[ValidationCase, ...]: + cases: list[ValidationCase] = [] + for length in self.lengths: + for sigma in self.sigmas: + for kappa in self.kappas: + cases.append( + ValidationCase( + length=length, + sigma=float(sigma), + kappa=float(kappa), + samples=self.samples_by_length[length], + seed_start=self.master_seeds[0], + ) + ) + return tuple(cases) + + @property + def family_denominators(self) -> dict[str, int]: + return frozen_family_denominators(self.lengths, self.sigmas, self.kappas) + + +def _protocol_document(protocol: ValidationProtocol) -> dict[str, object]: + document = { + "version": VALIDATION_PROTOCOL_VERSION, + "name": protocol.name, + "lengths": list(protocol.lengths), + "sigmas": [_f64(value) for value in protocol.sigmas], + "kappas": [_f64(value) for value in protocol.kappas], + "samples_by_length": { + str(length): protocol.samples_by_length[length] + for length in protocol.lengths + }, + "samplers": list(SAMPLERS), + "three_way_samplers": list(THREE_WAY_SAMPLERS), + "required_backends": list(SAMPLERS), + "observable_schema": OBSERVABLE_SCHEMA, + "master_seeds": list(protocol.master_seeds), + "familywise_alpha": _f64(protocol.familywise_alpha), + "family_denominators": protocol.family_denominators, + "permutation_replicates": protocol.permutation_replicates, + "multinomial_replicates": protocol.multinomial_replicates, + } + encoded = json.dumps( + document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + document["sha256"] = hashlib.sha256(encoded).hexdigest() + return document + + +def _check( + family: str, + case_id: str, + raw: object, + expected: object, + threshold: float, + margin: float, + passed: bool, +) -> dict[str, object]: + if not math.isfinite(float(threshold)) or not math.isfinite(float(margin)): + raise RuntimeError("check thresholds and margins must be finite") + return { + "family": family, + "case_id": case_id, + "raw": raw, + "expected": expected, + "threshold": float(threshold), + "margin": float(margin), + "passed": bool(passed), + } + + +def _exact( + family: str, + case_id: str, + raw: object, + expected: object, + equal: bool, + distance: float = 1.0, +) -> dict[str, object]: + margin = 0.0 if equal else -max(float(distance), np.finfo(float).tiny) + return _check(family, case_id, raw, expected, 0.0, margin, equal) + + +def _exact_mask_probabilities( + length: int, + sigma: float, + kappa: float, +) -> tuple[np.ndarray, np.ndarray]: + edges = tuple(iter_unordered_edges(length)) + class_probabilities = edge_probabilities( + ModelSpec(length, sigma, kappa), + periodic_kernel(length, sigma), + ) + edge_probabilities_array = np.asarray( + [ + class_probabilities[ + min(right - left, length - (right - left)) - 1 + ] + for left, right in edges + ], + dtype=np.float64, + ) + masks = np.arange(1 << len(edges), dtype=np.uint64) + probabilities = np.ones(masks.size, dtype=np.float64) + for edge_index, probability in enumerate(edge_probabilities_array): + is_open = (masks & np.uint64(1 << edge_index)) != 0 + probabilities *= np.where(is_open, probability, 1.0 - probability) + return probabilities, edge_probabilities_array + + +def assert_sampler_structure() -> None: + root = Path(__file__).parent + modules = ("oracle", "geometric", "poisson_reference", "poisson_sweep") + forbidden_symbols = { + "sample_quadratic", + "sample_geometric", + "run_poisson_reference", + "_run_poisson_with_streams", + "run_poisson_numba", + "_run_poisson_kernel", + } + for module in modules: + tree = ast.parse((root / f"{module}.py").read_text(encoding="utf-8")) + for node in ast.walk(tree): + if not isinstance(node, ast.ImportFrom) or not node.module: + continue + imported_module = node.module.rsplit(".", 1)[-1] + if imported_module not in modules: + continue + imported_names = {alias.name for alias in node.names} + overlap = imported_names & forbidden_symbols + if overlap: + raise RuntimeError( + f"{module} imports sampler selection logic " + f"from {imported_module}: {sorted(overlap)}" + ) + + +def _exact_checks(protocol: ValidationProtocol) -> list[dict[str, object]]: + checks: list[dict[str, object]] = [] + vectors = ( + ((0, 0, 0, 0), (0, 0), (0x6627E8D5, 0xE169C58D, 0xBC57AC4C, 0x9B00DBD8)), + ( + (0xFFFFFFFF,) * 4, + (0xFFFFFFFF,) * 2, + (0x408F276D, 0x41C83B0E, 0xA20BC7C6, 0x6D5451FD), + ), + ) + vector_pass = True + actual_vectors = [] + for counter_words, key_words, expected in vectors: + counter = np.asarray(counter_words, dtype=np.uint32) + key = np.asarray(key_words, dtype=np.uint32) + reference = philox4x32_10_reference(counter, key) + compiled = np.empty(4, dtype=np.uint32) + philox4x32_10(counter, key, compiled) + actual_vectors.append([int(value) for value in compiled]) + vector_pass &= tuple(int(value) for value in reference) == expected + vector_pass &= np.array_equal(compiled, reference) + checks.append( + _exact("philox-vectors", "published-random123", actual_vectors, "published", vector_pass) + ) + + identity = StreamIdentity(194, "validation", 4, "task-8", 0, 0) + materials = [ + derive_stream_material(replace(identity, stream_id=index)) + for index in range(4) + ] + fingerprints = [item.material_sha256 for item in materials] + checks.append( + _exact( + "stream-separation", + "four-streams", + fingerprints, + "four unique digests", + len(set(fingerprints)) == 4, + ) + ) + + bound = 2**31 + 1 + rejection_threshold = ((1 << 32) - bound) % bound + tape = (0, 1, rejection_threshold) + accepted = next(index for index, word in enumerate(tape) if word >= rejection_threshold) + checks.append( + _exact( + "bounded-integer-accounting", + "finite-tape", + {"words": accepted + 1, "rejections": accepted}, + {"words": 3, "rejections": 2}, + accepted == 2, + ) + ) + + kernel = periodic_kernel(8, 1.0) + digest = hashlib.sha256(kernel.tobytes()).hexdigest() + alias = build_distance_alias(8, 1.0, kernel, digest) + represented = np.zeros(4) + for column in range(4): + represented[column] += alias.probability[column] / 4.0 + represented[alias.alias[column]] += (1.0 - alias.probability[column]) / 4.0 + target = alias.class_weight / alias.total_rate + alias_error = float(np.max(np.abs(represented - target))) + checks.append( + _exact( + "alias-invariants", + "L8/sigma-1", + {"maximum_error": alias_error, "total_rate": alias.total_rate}, + {"maximum_error_lte": 32 * np.finfo(float).eps}, + alias_error <= 32 * np.finfo(float).eps, + alias_error, + ) + ) + + edge_ids = [ + canonical_edge(256, item.distance, offset) + for item in distance_classes(256) + for offset in range(item.multiplicity) + ] + expected_edges = 256 * 255 // 2 + unique_edges = len(set(edge_ids)) + checks.append( + _exact( + "edge-id-uniqueness", + "L256", + {"count": len(edge_ids), "unique": unique_edges}, + expected_edges, + len(edge_ids) == unique_edges == expected_edges, + ) + ) + keys, occupied, diagnostics = allocate_edge_set(0) + inserted = [] + for value in (0, 1, 2**63, 2**64 - 1): + keys, occupied, fresh = edge_set_insert(keys, occupied, diagnostics, value) + inserted.append(fresh) + hash_ok = all(inserted) and int(diagnostics[1]) == 4 and int(diagnostics[4]) > 0 + checks.append( + _exact( + "hash-full-range-growth", + "uint64-extremes", + diagnostics.tolist(), + {"size": 4, "grew": True}, + hash_ok, + ) + ) + + graph_residuals = {} + graph_coverage: dict[str, dict[str, object]] = {} + graph_ok = True + for length in (4, 6): + if length not in protocol.lengths and protocol.is_production is False: + continue + for sigma in protocol.sigmas: + for kappa in protocol.kappas: + probabilities, edge_probabilities_array = ( + _exact_mask_probabilities(length, sigma, kappa) + ) + masks = np.arange(probabilities.size, dtype=np.uint64) + residual = abs(math.fsum(probabilities.tolist()) - 1.0) + maximum_edge_error = 0.0 + for edge_index, expected_probability in enumerate( + edge_probabilities_array + ): + is_open = ( + masks & np.uint64(1 << edge_index) + ) != 0 + actual_probability = math.fsum( + probabilities[is_open].tolist() + ) + maximum_edge_error = max( + maximum_edge_error, + abs(actual_probability - expected_probability), + ) + graph_residuals[ + f"L{length}/{_f64(sigma)}/{_f64(kappa)}" + ] = residual + coverage = graph_coverage.setdefault( + f"L{length}", + { + "graph_count": probabilities.size, + "probabilities_compared": 0, + "maximum_product_error": 0.0, + "maximum_edge_event_error": 0.0, + }, + ) + coverage["probabilities_compared"] = int( + coverage["probabilities_compared"] + ) + probabilities.size + coverage["maximum_edge_event_error"] = max( + float(coverage["maximum_edge_event_error"]), + maximum_edge_error, + ) + graph_ok &= probabilities.size == 1 << ( + length * (length - 1) // 2 + ) + graph_ok &= residual <= 512 * np.finfo(float).eps + graph_ok &= maximum_edge_error <= 512 * np.finfo(float).eps + checks.append( + _exact( + "all-graph-exact", + "L<=6", + { + "normalization_residuals": graph_residuals, + "coverage": graph_coverage, + }, + {"maximum_residual_lte": 512 * np.finfo(float).eps}, + graph_ok, + max(graph_residuals.values(), default=0.0), + ) + ) + + zero_spec = ModelSpec(4, 1.0, 0.0) + q_zero = sample_quadratic(zero_spec, np.random.default_rng(1)) + g_zero = sample_geometric(zero_spec, np.random.default_rng(2)) + checks.append( + _exact( + "kappa-zero", + "L4", + [q_zero.edges.shape[0], g_zero.edges.shape[0]], + [0, 0], + q_zero.edges.size == 0 and g_zero.edges.size == 0, + ) + ) + saturated = ModelSpec(4, 1.0, np.finfo(float).max) + with np.errstate(over="ignore", under="ignore"): + q_full = sample_quadratic(saturated, np.random.default_rng(3)) + g_full = sample_geometric(saturated, np.random.default_rng(4)) + checks.append( + _exact( + "saturated-coupling", + "L4", + [len(q_full.edges), len(g_full.edges)], + [6, 6], + len(q_full.edges) == len(g_full.edges) == 6, + ) + ) + antipodes = distance_classes(256)[-1].multiplicity + checks.append( + _exact("antipodal-counts", "L256", antipodes, 128, antipodes == 128) + ) + extreme_ok = True + extreme_raw = [] + for sigma in (math.ulp(1.0), 128.0): + values = periodic_kernel(8, sigma) + extreme_raw.append([float(values.min()), float(values.max())]) + extreme_ok &= bool(np.all(np.isfinite(values)) and np.all(values > 0.0)) + checks.append( + _exact( + "finite-parameter-extremes", + "tiny-huge-sigma", + extreme_raw, + "finite positive kernels", + extreme_ok, + ) + ) + + duplicate_request, duplicate_kernel, duplicate_alias = _poisson_inputs( + 4, 1.0, 6.0, 194, 0, "numba" + ) + duplicate_result = run_poisson_numba( + duplicate_request, duplicate_kernel, duplicate_alias + ) + duplicate_ok = ( + duplicate_result.duplicate_count <= duplicate_result.event_count + and duplicate_result.observables[-1, 0] <= 6 + ) + checks.append( + _exact( + "duplicate-limits", + "L4/kappa-6", + { + "events": duplicate_result.event_count, + "duplicates": duplicate_result.duplicate_count, + "open": int(duplicate_result.observables[-1, 0]), + }, + {"duplicates_lte_events": True, "open_lte": 6}, + duplicate_ok, + ) + ) + + parent, size, masks, moments, counts = allocate_union_find(8) + for left, right in ((0, 1), (2, 3), (1, 2), (4, 7)): + counts[0] += 1 + union_incremental(parent, size, masks, moments, counts, left, right) + scanned = scan_basic_observables(parent, size, masks, moments, counts) + moments_ok = ( + scanned.sum_size_sq == moments[0] + and scanned.sum_size_fourth == moments[1] + ) + checks.append( + _exact( + "incremental-root-scan", + "scripted-unions", + [scanned.sum_size_sq, scanned.sum_size_fourth], + moments.tolist(), + moments_ok, + ) + ) + + requests = [ + _poisson_inputs(4, 1.0, 0.7, 194, replica, "numba") + for replica in (1, 2) + ] + forward = [ + run_poisson_numba(request, values, table).observables.tobytes() + for request, values, table in requests + ] + reverse = { + request.replica: run_poisson_numba(request, values, table).observables.tobytes() + for request, values, table in reversed(requests) + } + order_ok = all( + raw == reverse[request[0].replica] + for raw, request in zip(forward, requests, strict=True) + ) + checks.append( + _exact( + "process-order-identity", + "replicas-1-2", + [hashlib.sha256(raw).hexdigest() for raw in forward], + "same hashes in reverse order", + order_ok, + ) + ) + try: + assert_sampler_structure() + except Exception as error: + checks.append( + _exact( + "sampler-structure", + "four-sampler-modules", + {"error": f"{type(error).__name__}: {error}"}, + "no shared sampler selection logic", + False, + ) + ) + else: + checks.append( + _exact( + "sampler-structure", + "four-sampler-modules", + "independent", + "independent", + True, + ) + ) + return checks + + +def _poisson_inputs( + length: int, + sigma: float, + kappa: float, + master_seed: int, + replica: int, + implementation: str, +): + kernel = periodic_kernel(length, sigma) + digest = hashlib.sha256(kernel.tobytes()).hexdigest() + request = TrajectoryRequest( + length=length, + sigma=sigma, + sigma_grid_id=f"task-8-{implementation}-sigma-{_f64(sigma)}", + kappas=np.asarray((kappa,), dtype=np.float64), + master_seed=master_seed, + phase="validation", + replica=replica, + kernel_sha256=digest, + ) + return request, kernel, build_distance_alias(length, sigma, kernel, digest) + + +def _graph_observables(length: int, edges: np.ndarray, labels: np.ndarray) -> np.ndarray: + sizes = sorted( + ( + int(value) + for value in np.bincount(labels) + if int(value) > 0 + ), + reverse=True, + ) + largest = sizes[0] + second = sizes[1] if len(sizes) > 1 else 0 + sum_sq = math.fsum(float(value) ** 2 for value in sizes) + sum_fourth = math.fsum(float(value) ** 4 for value in sizes) + masks: dict[int, int] = {} + for vertex, label in enumerate(labels.tolist()): + masks[label] = masks.get(label, 0) | (1 << min(3, (4 * vertex) // length)) + return np.asarray( + ( + len(edges), + len(sizes), + largest, + second, + largest / length, + second / length, + sum_sq, + sum_fourth, + sum_fourth / (sum_sq * sum_sq), + float(any(mask == 0b1111 for mask in masks.values())), + ), + dtype=np.float64, + ) + + +def _component_partition(labels: np.ndarray) -> tuple[int, ...]: + return tuple( + sorted( + ( + int(value) + for value in np.bincount(labels) + if int(value) > 0 + ), + reverse=True, + ) + ) + + +def _scalar_values( + observables: np.ndarray, + family: str, + length: int, +) -> np.ndarray: + column = SCALAR_COLUMNS[family] + values = np.asarray(observables[:, column], dtype=np.float64) + specification = OBSERVABLE_SCHEMA.get(family) + if specification is None: + return values + power = int(specification["normalization_power"]) + return values / float(length**power) + + +class _AuditStream: + def __init__(self, identity: StreamIdentity): + material = derive_stream_material(identity) + self._key = material.key + self._counter = material.initial_counter.copy() + self._block = np.zeros(4, dtype=np.uint32) + self._lane = 4 + + def word(self) -> int: + if self._lane == 4: + self._block[:] = philox4x32_10_reference( + self._counter, self._key + ) + carry = 1 + for index in range(4): + total = int(self._counter[index]) + carry + self._counter[index] = np.uint32(total & 0xFFFFFFFF) + carry = total >> 32 + self._lane = 0 + value = int(self._block[self._lane]) + self._lane += 1 + return value + + def uniform(self) -> float: + return (float(self.word()) + 0.5) * (2.0**-32) + + def bounded(self, bound: int) -> int: + threshold = ((1 << 32) - bound) % bound + while True: + word = self.word() + if word >= threshold: + return word % bound + + +def _audit_numba_terminal( + request: TrajectoryRequest, + table, +) -> tuple[np.ndarray, np.ndarray, int, int]: + streams = [ + _AuditStream( + StreamIdentity( + request.master_seed, + request.phase, + request.length, + request.sigma_grid_id, + request.replica, + stream_id, + ) + ) + for stream_id in range(4) + ] + starts = np.cumsum( + [0, *(int(value) for value in table.multiplicity)], + dtype=np.int64, + ) + open_ids: set[int] = set() + current = 0.0 + terminal = float(request.kappas[-1]) + event_count = 0 + duplicate_count = 0 + class_count = len(table.probability) + column_rejection = ((1 << 32) - class_count) % class_count + while terminal > 0.0: + hazard = -math.log(streams[3].uniform()) + if hazard > (terminal - current) * float(table.total_rate): + break + current += hazard / float(table.total_rate) + while True: + column_word = streams[0].word() + product = column_word * class_count + if (product & 0xFFFFFFFF) >= column_rejection: + column = product >> 32 + break + threshold = streams[1].uniform() + selected = ( + column + if threshold <= float(table.probability[column]) + else int(table.alias[column]) + ) + offset = streams[2].bounded(int(table.multiplicity[selected])) + edge_id = int(starts[selected]) + offset + event_count += 1 + if edge_id in open_ids: + duplicate_count += 1 + else: + open_ids.add(edge_id) + + edges = [] + connectivity = UnionFind(request.length) + for edge_id in sorted(open_ids): + selected = int(np.searchsorted(starts, edge_id, side="right") - 1) + offset = edge_id - int(starts[selected]) + edge = canonical_edge(request.length, selected + 1, offset) + edges.append(edge) + connectivity.union(*edge) + edge_array = np.asarray(edges, dtype=np.int64).reshape(-1, 2) + return ( + _graph_observables( + request.length, edge_array, connectivity.labels() + ), + edge_array, + event_count, + duplicate_count, + ) + + +def _bond_counts(length: int, edges: Iterable[Sequence[int]]) -> np.ndarray: + counts = np.zeros(length // 2, dtype=np.int64) + for left, right in edges: + separation = int(right) - int(left) + counts[min(separation, length - separation) - 1] += 1 + return counts + + +@dataclass +class _Samples: + observables: dict[str, np.ndarray] + bonds: dict[str, np.ndarray] + partitions: dict[str, Counter[tuple[int, ...]]] + edge_classes: dict[str, np.ndarray] + events: dict[str, int] + graph_masks: dict[str, np.ndarray] + + +def _draw_case(case: ValidationCase, seeds: tuple[int, ...]) -> _Samples: + n = case.samples + length = case.length + observables = { + sampler: np.empty((n, 10), dtype=np.float64) for sampler in SAMPLERS + } + bonds = { + sampler: np.zeros(length // 2, dtype=np.int64) + for sampler in SAMPLERS + } + partitions: dict[str, Counter[tuple[int, ...]]] = { + sampler: Counter() for sampler in SAMPLERS + } + edge_classes = { + sampler: np.zeros(length // 2, dtype=np.int64) + for sampler in SAMPLERS + } + events = {"poisson-reference": 0, "poisson-numba": 0} + graph_masks = ( + { + sampler: np.zeros( + 1 << (length * (length - 1) // 2), dtype=np.int64 + ) + for sampler in SAMPLERS + } + if length <= 6 + else {} + ) + edge_positions = ( + {edge: index for index, edge in enumerate(iter_unordered_edges(length))} + if length <= 6 + else {} + ) + starts = np.cumsum( + [0, *(item.multiplicity for item in distance_classes(length))], + dtype=np.int64, + ) + + for replica, seed in enumerate(seeds[:n]): + spec = ModelSpec(length, case.sigma, case.kappa) + q_rng = np.random.Generator(np.random.PCG64(seed ^ 0x5155414452415449)) + g_rng = np.random.Generator(np.random.PCG64(seed ^ 0x47454F4D45545249)) + quadratic = sample_quadratic(spec, q_rng) + geometric = sample_geometric(spec, g_rng) + for name, sample in (("quadratic", quadratic), ("geometric", geometric)): + observables[name][replica] = _graph_observables( + length, sample.edges, sample.labels + ) + class_counts = _bond_counts(length, sample.edges) + bonds[name] += class_counts + edge_classes[name] += class_counts + partitions[name][_component_partition(sample.labels)] += 1 + if length <= 6: + mask = sum(1 << edge_positions[tuple(edge)] for edge in sample.edges) + graph_masks[name][mask] += 1 + + ref_request, kernel, _ = _poisson_inputs( + length, case.sigma, case.kappa, seed, replica, "reference" + ) + reference_run = run_poisson_reference_with_diagnostics( + ref_request, kernel + ) + if not isinstance(reference_run, TrajectoryDiagnostics): + raise RuntimeError( + "Python reference returned malformed diagnostics contract" + ) + reference = reference_run.result + if ( + reference.observables.shape != (1, 10) + or not np.all(np.isfinite(reference.observables)) + or len(reference_run.edge_ids_by_checkpoint) != 1 + ): + raise RuntimeError( + "Python reference returned malformed observable fields" + ) + observables["poisson-reference"][replica] = reference.observables[0] + events["poisson-reference"] += reference.event_count + ids = reference_run.edge_ids_by_checkpoint[0] + ref_classes = np.zeros(length // 2, dtype=np.int64) + for edge_id in ids: + class_index = int(np.searchsorted(starts, edge_id, side="right") - 1) + ref_classes[class_index] += 1 + if length <= 6: + reference_mask = 0 + for edge_id in ids: + class_index = int(np.searchsorted(starts, edge_id, side="right") - 1) + offset = int(edge_id - starts[class_index]) + edge = canonical_edge(length, class_index + 1, offset) + reference_mask |= 1 << edge_positions[edge] + graph_masks["poisson-reference"][reference_mask] += 1 + reference_edges = [] + reference_connectivity = UnionFind(length) + for edge_id in ids: + class_index = int( + np.searchsorted(starts, edge_id, side="right") - 1 + ) + offset = int(edge_id - starts[class_index]) + edge = canonical_edge(length, class_index + 1, offset) + reference_edges.append(edge) + reference_connectivity.union(*edge) + reference_edge_array = np.asarray( + sorted(reference_edges), dtype=np.int64 + ).reshape(-1, 2) + reconstructed = _graph_observables( + length, reference_edge_array, reference_connectivity.labels() + ) + if not np.array_equal(reconstructed, reference.observables[0]): + raise RuntimeError( + "Python reference edge diagnostics disagree with observables" + ) + bonds["poisson-reference"] += ref_classes + edge_classes["poisson-reference"] += ref_classes + partitions["poisson-reference"][ + _component_partition(reference_connectivity.labels()) + ] += 1 + + numba_request, kernel, table = _poisson_inputs( + length, case.sigma, case.kappa, seed, replica, "numba" + ) + numba_result = run_poisson_numba(numba_request, kernel, table) + observables["poisson-numba"][replica] = numba_result.observables[0] + events["poisson-numba"] += numba_result.event_count + ( + audit_observables, + audit_edges, + audit_events, + audit_duplicates, + ) = _audit_numba_terminal(numba_request, table) + if ( + not np.array_equal(audit_observables, numba_result.observables[0]) + or audit_events != numba_result.event_count + or audit_duplicates != numba_result.duplicate_count + ): + raise RuntimeError( + "independent Numba edge audit disagrees with production output" + ) + numba_classes = _bond_counts(length, audit_edges) + bonds["poisson-numba"] += numba_classes + edge_classes["poisson-numba"] += numba_classes + numba_connectivity = UnionFind(length) + for left, right in audit_edges: + numba_connectivity.union(int(left), int(right)) + partitions["poisson-numba"][ + _component_partition(numba_connectivity.labels()) + ] += 1 + if length <= 6: + mask = sum( + 1 << edge_positions[tuple(edge)] for edge in audit_edges + ) + graph_masks["poisson-numba"][mask] += 1 + + return _Samples( + observables, bonds, partitions, edge_classes, events, graph_masks + ) + + +def _poisson_two_sided(observed: int, expected: float) -> float: + if expected == 0.0: + return 1.0 if observed == 0 else 0.0 + lower = float(poisson.cdf(observed, expected)) + upper = float(poisson.sf(observed - 1, expected)) + return min(1.0, 2.0 * min(lower, upper)) + + +def _permutation_pvalue( + left: np.ndarray, + right: np.ndarray, + replicates: int, + seed: int, +) -> tuple[float, float]: + left = np.asarray(left, dtype=np.float64) + right = np.asarray(right, dtype=np.float64) + statistic = abs(float(np.mean(left) - np.mean(right))) + pooled = np.concatenate((left, right)) + values, counts = np.unique(pooled, return_counts=True) + total = float(np.sum(pooled)) + rng = np.random.Generator(np.random.Philox(seed)) + exceed = 0 + remaining = replicates + while remaining: + batch = min(1024, remaining) + selected_counts = rng.multivariate_hypergeometric( + counts, left.size, size=batch + ) + selected_sums = selected_counts @ values + permuted = np.abs( + selected_sums / left.size + - (total - selected_sums) / right.size + ) + exceed += int(np.count_nonzero(permuted >= statistic)) + remaining -= batch + return statistic, (exceed + 1.0) / (replicates + 1.0) + + +def _g_statistic(counts: np.ndarray, expected: np.ndarray) -> float: + mask = counts > 0 + if np.any(expected[mask] <= 0.0): + return math.inf + return float(2.0 * np.sum(counts[mask] * np.log(counts[mask] / expected[mask]))) + + +def _multinomial_pvalue( + left: np.ndarray, + right: np.ndarray, + replicates: int, + seed: int, +) -> tuple[float, float]: + left = np.asarray(left, dtype=np.int64) + right = np.asarray(right, dtype=np.int64) + pooled = left + right + total = int(pooled.sum()) + if total == 0: + return 0.0, 1.0 + active = pooled > 0 + left = left[active] + right = right[active] + pooled = pooled[active] + probability = pooled / total + expected_left = probability * int(left.sum()) + expected_right = probability * int(right.sum()) + statistic = _g_statistic(left, expected_left) + _g_statistic(right, expected_right) + rng = np.random.Generator(np.random.Philox(seed)) + exceed = 0 + remaining = replicates + while remaining: + batch = min(512, remaining) + simulated_left = rng.multinomial( + int(left.sum()), probability, size=batch + ) + simulated_right = rng.multinomial( + int(right.sum()), probability, size=batch + ) + left_terms = np.zeros_like(simulated_left, dtype=np.float64) + right_terms = np.zeros_like(simulated_right, dtype=np.float64) + left_mask = simulated_left > 0 + right_mask = simulated_right > 0 + np.log( + simulated_left, + out=left_terms, + where=left_mask, + ) + left_terms[left_mask] -= np.broadcast_to( + np.log(expected_left), simulated_left.shape + )[left_mask] + left_terms *= simulated_left + np.log( + simulated_right, + out=right_terms, + where=right_mask, + ) + right_terms[right_mask] -= np.broadcast_to( + np.log(expected_right), simulated_right.shape + )[right_mask] + right_terms *= simulated_right + simulated = 2.0 * ( + np.sum(left_terms, axis=1) + np.sum(right_terms, axis=1) + ) + exceed += int(np.count_nonzero(simulated >= statistic)) + remaining -= batch + return statistic, (exceed + 1.0) / (replicates + 1.0) + + +def _statistical_checks( + protocol: ValidationProtocol, + case: ValidationCase, + samples: _Samples, + case_index: int, +) -> list[dict[str, object]]: + checks: list[dict[str, object]] = [] + denominators = protocol.family_denominators + kernel = periodic_kernel(case.length, case.sigma) + probabilities = edge_probabilities( + ModelSpec(case.length, case.sigma, case.kappa), kernel + ) + multiplicities = np.asarray( + [item.multiplicity for item in distance_classes(case.length)], + dtype=np.int64, + ) + if case.length <= 6: + exact_probabilities, _ = _exact_mask_probabilities( + case.length, case.sigma, case.kappa + ) + threshold = ( + protocol.familywise_alpha + / denominators["all-graph-probability"] + ) + for sampler in SAMPLERS: + counts = samples.graph_masks[sampler] + pvalues = [] + for mask, expected_probability in enumerate( + exact_probabilities + ): + count = int(counts[mask]) + pvalue = float( + binomtest( + count, + case.samples, + float(expected_probability), + alternative="two-sided", + ).pvalue + ) + pvalues.append(pvalue) + minimum_pvalue = min(pvalues) + checks.append( + _check( + "all-graph-probability", + f"{case.case_id}/{sampler}", + { + "masks": list(range(exact_probabilities.size)), + "counts": counts.tolist(), + "trials": case.samples, + "pvalues": pvalues, + }, + { + "probabilities": [ + float(value) for value in exact_probabilities + ], + "comparison": "per-mask exact product measure", + }, + threshold, + minimum_pvalue - threshold, + minimum_pvalue >= threshold, + ) + ) + for sampler in SAMPLERS: + for class_index, (count, probability, multiplicity) in enumerate( + zip( + samples.edge_classes[sampler], + probabilities, + multiplicities, + strict=True, + ) + ): + trials = case.samples * int(multiplicity) + pvalue = float( + binomtest(int(count), trials, float(probability), alternative="two-sided").pvalue + ) + threshold = protocol.familywise_alpha / denominators["edge-class-frequency"] + checks.append( + _check( + "edge-class-frequency", + f"{case.case_id}/{sampler}/d{class_index + 1}", + {"successes": int(count), "trials": trials, "pvalue": pvalue}, + {"probability": float(probability)}, + threshold, + pvalue - threshold, + pvalue >= threshold, + ) + ) + + total_rate = math.fsum( + float(count) * float(rate) + for count, rate in zip(multiplicities, kernel, strict=True) + ) + for sampler in ("poisson-reference", "poisson-numba"): + expected_events = case.samples * case.kappa * total_rate + count = samples.events[sampler] + pvalue = _poisson_two_sided(count, expected_events) + threshold = protocol.familywise_alpha / denominators["poisson-event-count"] + checks.append( + _check( + "poisson-event-count", + f"{case.case_id}/{sampler}", + {"count": count, "pvalue": pvalue}, + {"mean": expected_events}, + threshold, + pvalue - threshold, + pvalue >= threshold, + ) + ) + + p_none = no_edge_probability(ModelSpec(case.length, case.sigma, case.kappa)) + for sampler in SAMPLERS: + no_edges = int(np.count_nonzero(samples.observables[sampler][:, 0] == 0.0)) + pvalue = float( + binomtest(no_edges, case.samples, p_none, alternative="two-sided").pvalue + ) + threshold = protocol.familywise_alpha / denominators["no-edge"] + checks.append( + _check( + "no-edge", + f"{case.case_id}/{sampler}", + {"successes": no_edges, "trials": case.samples, "pvalue": pvalue}, + {"probability": p_none}, + threshold, + pvalue - threshold, + pvalue >= threshold, + ) + ) + + for pair_index, (left, right) in enumerate(PAIR_NAMES): + for family, column in SCALAR_COLUMNS.items(): + left_values = _scalar_values( + samples.observables[left], family, case.length + ) + right_values = _scalar_values( + samples.observables[right], family, case.length + ) + permutation_seed = ( + protocol.master_seeds[0] + + case_index * 1000 + + pair_index * 100 + + column + ) + statistic, pvalue = _permutation_pvalue( + left_values, + right_values, + protocol.permutation_replicates, + permutation_seed, + ) + threshold = protocol.familywise_alpha / denominators[family] + checks.append( + _check( + family, + f"{case.case_id}/{left}-vs-{right}", + { + "left_sum": float(np.sum(left_values)), + "right_sum": float(np.sum(right_values)), + "left_raw_sum": float( + np.sum(samples.observables[left][:, column]) + ), + "right_raw_sum": float( + np.sum(samples.observables[right][:, column]) + ), + "left_count": case.samples, + "right_count": case.samples, + "statistic": statistic, + "replicates": protocol.permutation_replicates, + "pvalue": pvalue, + "seed": permutation_seed, + }, + {"equal_means": True}, + threshold, + pvalue - threshold, + pvalue >= threshold, + ) + ) + + left_bonds = samples.bonds[left] + right_bonds = samples.bonds[right] + statistic, pvalue = _multinomial_pvalue( + left_bonds, + right_bonds, + protocol.multinomial_replicates, + protocol.master_seeds[0] + case_index * 1000 + pair_index * 100 + 50, + ) + threshold = protocol.familywise_alpha / denominators["bond-length"] + checks.append( + _check( + "bond-length", + f"{case.case_id}/{left}-vs-{right}", + { + "left_bins": left_bonds.tolist(), + "right_bins": right_bonds.tolist(), + "statistic": statistic, + "replicates": protocol.multinomial_replicates, + "pvalue": pvalue, + "seed": protocol.master_seeds[0] + + case_index * 1000 + + pair_index * 100 + + 50, + }, + {"pooled_null": True}, + threshold, + pvalue - threshold, + pvalue >= threshold, + ) + ) + partition_bins = sorted( + set(samples.partitions[left]) | set(samples.partitions[right]), + reverse=True, + ) + left_partition = np.asarray( + [samples.partitions[left][item] for item in partition_bins], + dtype=np.int64, + ) + right_partition = np.asarray( + [samples.partitions[right][item] for item in partition_bins], + dtype=np.int64, + ) + statistic, pvalue = _multinomial_pvalue( + left_partition, + right_partition, + protocol.multinomial_replicates, + protocol.master_seeds[0] + case_index * 1000 + pair_index * 100 + 51, + ) + threshold = protocol.familywise_alpha / denominators["component-partition"] + checks.append( + _check( + "component-partition", + f"{case.case_id}/{left}-vs-{right}", + { + "bins": [list(item) for item in partition_bins], + "left_counts": left_partition.tolist(), + "right_counts": right_partition.tolist(), + "statistic": statistic, + "replicates": protocol.multinomial_replicates, + "pvalue": pvalue, + "seed": protocol.master_seeds[0] + + case_index * 1000 + + pair_index * 100 + + 51, + }, + {"pooled_null": True}, + threshold, + pvalue - threshold, + pvalue >= threshold, + ) + ) + return checks + + +def _repository_state() -> dict[str, object]: + root = Path(__file__).resolve() + while root != root.parent and not (root / ".git").exists(): + root = root.parent + try: + revision = subprocess.run( + ("git", "rev-parse", "HEAD"), + cwd=root, + check=True, + capture_output=True, + text=True, + ).stdout.strip() + status = subprocess.run( + ("git", "status", "--porcelain"), + cwd=root, + check=True, + capture_output=True, + text=True, + ).stdout + except (OSError, subprocess.CalledProcessError) as error: + return { + "source_revision": "unavailable", + "clean_tree": False, + "provenance_error": f"{type(error).__name__}: {error}", + } + return { + "source_revision": revision, + "clean_tree": not bool(status), + "provenance_error": None, + } + + +def canonical_report_bytes(report: Mapping[str, object]) -> bytes: + try: + return json.dumps( + report, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + b"\n" + except (TypeError, ValueError) as error: + raise RuntimeError(f"report is not canonical finite JSON: {error}") from error + + +def validate_report_payload( + report: Mapping[str, object], + protocol: ValidationProtocol, +) -> None: + if not isinstance(report, Mapping): + raise RuntimeError("validation report must be a mapping") + if report.get("schema_version") != VALIDATION_PROTOCOL_VERSION: + raise RuntimeError("validation report schema is corrupt") + if report.get("protocol") != _protocol_document(protocol): + raise RuntimeError("validation report protocol does not match the request") + checks = report.get("checks") + if not isinstance(checks, list) or not checks: + raise RuntimeError("validation report has no checks") + required_fields = { + "family", + "case_id", + "raw", + "expected", + "threshold", + "margin", + "passed", + } + for check in checks: + if not isinstance(check, Mapping) or not required_fields <= set(check): + raise RuntimeError("validation report contains a malformed check") + if ( + not isinstance(check["family"], str) + or not isinstance(check["case_id"], str) + or not isinstance(check["passed"], bool) + or not math.isfinite(float(check["threshold"])) + or not math.isfinite(float(check["margin"])) + ): + raise RuntimeError("validation report contains invalid check fields") + families = {str(check["family"]) for check in checks} + required = set(EXACT_FAMILIES) | set(STATISTICAL_FAMILIES) + if not required <= families: + raise RuntimeError("validation report is missing required families") + actual_passed = all(bool(check["passed"]) for check in checks) + if report.get("passed") is not actual_passed: + raise RuntimeError("validation report pass flag is inconsistent") + if report.get("family_count") != len(families): + raise RuntimeError("validation report family count is inconsistent") + margins = [float(check["margin"]) for check in checks] + if float(report.get("minimum_margin", math.nan)) != min(margins): + raise RuntimeError("validation report minimum margin is inconsistent") + canonical_report_bytes(report) + + +def _atomic_publish(output: Path, payload: bytes) -> None: + if not isinstance(output, Path): + raise ValueError("output must be a pathlib.Path") + output.parent.mkdir(parents=True, exist_ok=True) + if output.is_symlink(): + raise RuntimeError("refusing to publish through a symlink") + if output.exists() and not output.is_file(): + raise RuntimeError("output must be a regular file") + temporary = output.parent / f".{output.name}.{uuid.uuid4().hex}.tmp" + try: + descriptor = os.open( + temporary, + os.O_WRONLY | os.O_CREAT | os.O_EXCL, + 0o644, + ) + try: + with os.fdopen(descriptor, "wb") as stream: + stream.write(payload) + stream.flush() + os.fsync(stream.fileno()) + except BaseException: + try: + os.close(descriptor) + except OSError: + pass + raise + os.replace(temporary, output) + directory_fd = os.open(output.parent, os.O_RDONLY) + try: + os.fsync(directory_fd) + finally: + os.close(directory_fd) + finally: + try: + temporary.unlink() + except FileNotFoundError: + pass + + +def payload_without_elapsed(report: Mapping[str, object]) -> dict[str, object]: + payload = dict(report) + payload.pop("elapsed_seconds", None) + return payload + + +def _run_case_checks( + protocol: ValidationProtocol, + indexed_case: tuple[int, ValidationCase], +) -> list[dict[str, object]]: + case_index, case = indexed_case + samples = _draw_case(case, protocol.master_seeds[: case.samples]) + return _statistical_checks(protocol, case, samples, case_index) + + +def assemble_validation_report( + protocol: ValidationProtocol, + checks: Sequence[Mapping[str, object]], + *, + elapsed_seconds: float, + runtime_capability_value: Mapping[str, object] | None = None, + source: Mapping[str, object] | None = None, +) -> dict[str, object]: + assembled = [dict(item) for item in checks] + present = {item["family"] for item in assembled} + required = set(EXACT_FAMILIES) | set(STATISTICAL_FAMILIES) + for family in sorted(required - present): + assembled.append( + _exact( + family, + "missing-family", + {"missing": family}, + "at least one completed check", + False, + ) + ) + assembled.sort( + key=lambda item: (str(item["family"]), str(item["case_id"])) + ) + margins = [float(item["margin"]) for item in assembled] + report: dict[str, object] = { + "schema_version": VALIDATION_PROTOCOL_VERSION, + "protocol": _protocol_document(protocol), + "runtime_capability": dict( + runtime_capability() + if runtime_capability_value is None + else runtime_capability_value + ), + "source": dict(_repository_state() if source is None else source), + "coverage": { + "all_graph_probability": { + "backends": list(SAMPLERS), + "lengths": [ + length for length in protocol.lengths if length <= 6 + ], + "comparison": "per-mask exact product-measure binomial", + } + }, + "checks": assembled, + "family_count": len( + {str(item["family"]) for item in assembled} + ), + "minimum_margin": min(margins), + "passed": all(bool(item["passed"]) for item in assembled) + and required <= {str(item["family"]) for item in assembled}, + "elapsed_seconds": float(elapsed_seconds), + } + validate_report_payload(report, protocol) + return report + + +def run_production_validation( + protocol: ValidationProtocol, + output: Path, +) -> dict[str, object]: + if not isinstance(protocol, ValidationProtocol): + raise ValueError("protocol must be a ValidationProtocol") + started = time.perf_counter() + checks: list[dict[str, object]] = [] + if protocol.is_production: + print("validation exact checks started", flush=True) + try: + checks.extend(_exact_checks(protocol)) + except Exception as error: + checks.append( + _exact( + "backend-integrity", + "exact-checks", + {"error": f"{type(error).__name__}: {error}"}, + "all exact checks completed", + False, + ) + ) + + indexed_cases = tuple(enumerate(protocol.case_registry)) + total_cases = len(indexed_cases) + if protocol.jobs == 1: + outcomes: Iterable[list[dict[str, object]] | Exception] = [] + serial_outcomes: list[list[dict[str, object]] | Exception] = [] + for indexed_case in indexed_cases: + try: + serial_outcomes.append(_run_case_checks(protocol, indexed_case)) + except Exception as error: + serial_outcomes.append(error) + if protocol.is_production: + print( + f"validation case {indexed_case[0] + 1}/{total_cases} " + f"{indexed_case[1].case_id}", + flush=True, + ) + outcomes = serial_outcomes + else: + with ThreadPoolExecutor(max_workers=protocol.jobs) as executor: + futures = [ + executor.submit(_run_case_checks, protocol, indexed_case) + for indexed_case in indexed_cases + ] + parallel_outcomes: list[list[dict[str, object]] | Exception] = [] + for completed_index, future in enumerate(futures, start=1): + try: + parallel_outcomes.append(future.result()) + except Exception as error: + parallel_outcomes.append(error) + if protocol.is_production: + print( + f"validation case {completed_index}/{total_cases} " + f"{indexed_cases[completed_index - 1][1].case_id}", + flush=True, + ) + outcomes = parallel_outcomes + for (_, case), outcome in zip(indexed_cases, outcomes, strict=True): + if isinstance(outcome, Exception): + checks.append( + _exact( + "backend-integrity", + case.case_id, + { + "error": ( + f"{type(outcome).__name__}: {outcome}" + ) + }, + "all four backends returned valid data", + False, + ) + ) + else: + checks.extend(outcome) + + report = assemble_validation_report( + protocol, + checks, + elapsed_seconds=time.perf_counter() - started, + ) + _atomic_publish(output, canonical_report_bytes(report)) + return report diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/validation_shards.py b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/validation_shards.py new file mode 100644 index 000000000..5750edfa7 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/src/long_range_percolation/validation_shards.py @@ -0,0 +1,1351 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping +import ctypes +import errno +import hashlib +import json +import math +import os +from pathlib import Path +import re +import time +import uuid + +from .runtime import runtime_capability +from .validation import ( + VALIDATION_PROTOCOL_VERSION, + EXACT_FAMILIES, + PAIR_NAMES, + SAMPLERS, + SCALAR_COLUMNS, + ValidationProtocol, + _exact_checks, + _protocol_document, + _repository_state, + _run_case_checks, + assemble_validation_report, + canonical_report_bytes, +) + + +RUN_SPEC_SCHEMA = "challenge-194-validation-run-spec-v1" +CELL_SCHEMA = "challenge-194-validation-cell-v1" +GLOBAL_SCHEMA = "challenge-194-validation-global-v1" +MANIFEST_SCHEMA = "challenge-194-validation-shard-manifest-v1" +FINAL_REPORT_PATH = "report/report.json" +RUN_SPEC_NAME = "run_spec.json" +GLOBAL_CHECK_CASES = ( + "published-random123", + "four-streams", + "finite-tape", + "L8/sigma-1", + "L256", + "uint64-extremes", + "L<=6", + "L4", + "L4", + "L256", + "tiny-huge-sigma", + "L4/kappa-6", + "scripted-unions", + "replicas-1-2", + "four-sampler-modules", +) + + +def _canonical_bytes(document: Mapping[str, object]) -> bytes: + try: + return json.dumps( + document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + b"\n" + except (TypeError, ValueError) as error: + raise RuntimeError(f"document is not canonical finite JSON: {error}") from error + + +def _sha256(payload: bytes) -> str: + return hashlib.sha256(payload).hexdigest() + + +def _rename_no_replace(source: Path, destination: Path) -> None: + try: + renameat2 = ctypes.CDLL(None, use_errno=True).renameat2 + except AttributeError: + os.link(source, destination) + source.unlink() + return + renameat2.argtypes = ( + ctypes.c_int, + ctypes.c_char_p, + ctypes.c_int, + ctypes.c_char_p, + ctypes.c_uint, + ) + renameat2.restype = ctypes.c_int + result = renameat2( + -100, + os.fsencode(source), + -100, + os.fsencode(destination), + 1, + ) + if result == 0: + return + error_number = ctypes.get_errno() + if error_number == errno.EEXIST: + raise FileExistsError(error_number, os.strerror(error_number), destination) + if error_number in (errno.ENOSYS, errno.EINVAL): + os.link(source, destination) + source.unlink() + return + raise OSError(error_number, os.strerror(error_number), destination) + + +def _document_hash(document: Mapping[str, object], hash_field: str) -> str: + unsigned = dict(document) + unsigned.pop(hash_field, None) + return _sha256(_canonical_bytes(unsigned)) + + +def _repository_root() -> Path: + current = Path(__file__).resolve() + while current != current.parent: + if (current / ".git").exists(): + return current + current = current.parent + raise RuntimeError("unable to locate repository root") + + +def _solution_root() -> Path: + return Path(__file__).resolve().parents[2] + + +def _verified_source_state() -> dict[str, object]: + source = _repository_state() + revision = source.get("source_revision") + if ( + source.get("clean_tree") is not True + or source.get("provenance_error") is not None + or not isinstance(revision, str) + or re.fullmatch(r"[0-9a-f]{40}", revision) is None + ): + raise RuntimeError( + "repository must have an exact revision and clean source state" + ) + return source + + +def _runtime_document() -> tuple[dict[str, object], str]: + capability = runtime_capability() + return capability, _sha256(_canonical_bytes(capability)) + + +def _lock_hash() -> str: + lockfile = _solution_root() / "uv.lock" + if lockfile.is_symlink() or not lockfile.is_file(): + raise RuntimeError("challenge uv.lock must be a regular file") + return _sha256(lockfile.read_bytes()) + + +def _implementation_hashes() -> dict[str, str]: + solution = _solution_root() + paths = [ + *sorted((solution / "src" / "long_range_percolation").glob("*.py")), + *sorted((solution / "scripts").glob("*.py")), + ] + modules: dict[str, str] = {} + for path in paths: + if path.is_symlink() or not path.is_file(): + raise RuntimeError("implementation modules must be regular files") + relative = str(path.relative_to(solution)) + modules[relative] = _sha256(path.read_bytes()) + if not modules: + raise RuntimeError("no implementation modules were found") + return modules + + +def _implementation_digest(modules: Mapping[str, str]) -> str: + return _sha256(_canonical_bytes(dict(modules))) + + +def _dependency_fields(spec: Mapping[str, object]) -> dict[str, object]: + return { + "run_spec_sha256": spec["run_spec_sha256"], + "protocol_sha256": spec["protocol"]["sha256"], + "source_revision": spec["source_revision"], + "runtime_capability_sha256": spec["runtime_capability_sha256"], + "uv_lock_sha256": spec["uv_lock_sha256"], + "implementation_sha256": spec["implementation_sha256"], + } + + +def _assert_dependencies(spec: Mapping[str, object]) -> None: + source = _verified_source_state() + capability, capability_hash = _runtime_document() + if source["source_revision"] != spec.get("source_revision"): + raise RuntimeError("source revision does not match run spec") + if capability_hash != spec.get("runtime_capability_sha256"): + raise RuntimeError("runtime capability does not match run spec") + if capability != spec.get("runtime_capability"): + raise RuntimeError("runtime capability payload is stale") + if _lock_hash() != spec.get("uv_lock_sha256"): + raise RuntimeError("uv.lock does not match run spec") + modules = _implementation_hashes() + if modules != spec.get("implementation_modules"): + raise RuntimeError("implementation module hashes do not match run spec") + if _implementation_digest(modules) != spec.get("implementation_sha256"): + raise RuntimeError("implementation aggregate hash does not match run spec") + + +def _protocol_from_document( + document: Mapping[str, object], + *, + enforce_production: bool, +) -> ValidationProtocol: + try: + lengths = tuple(int(value) for value in document["lengths"]) + samples = { + int(length): int(count) + for length, count in document["samples_by_length"].items() + } + protocol = ValidationProtocol( + lengths=lengths, + sigmas=tuple(float.fromhex(value) for value in document["sigmas"]), + kappas=tuple(float.fromhex(value) for value in document["kappas"]), + samples_by_length=samples, + master_seeds=tuple(int(value) for value in document["master_seeds"]), + familywise_alpha=float.fromhex(document["familywise_alpha"]), + permutation_replicates=int(document["permutation_replicates"]), + multinomial_replicates=int(document["multinomial_replicates"]), + jobs=1, + name=str(document["name"]), + ) + except (KeyError, TypeError, ValueError) as error: + raise RuntimeError(f"run spec protocol is malformed: {error}") from error + if _protocol_document(protocol) != dict(document): + raise RuntimeError("run spec protocol is not canonical") + if enforce_production: + try: + protocol.require_production() + except ValueError as error: + raise RuntimeError("run spec is not exact production-v1") from error + return protocol + + +def _relative_artifact_path(value: object, prefix: str) -> Path: + if not isinstance(value, str): + raise RuntimeError("artifact path must be a string") + path = Path(value) + if ( + path.is_absolute() + or ".." in path.parts + or not path.parts + or path.parts[0] != prefix + ): + raise RuntimeError("artifact path escapes its immutable namespace") + return path + + +def _assert_no_symlink_components(path: Path) -> None: + current = Path(path.anchor) + for part in path.parts[1:]: + current /= part + if current.is_symlink(): + raise RuntimeError(f"path contains a symlink component: {current}") + if not current.exists(): + break + + +def _canonical_run_root(output_root: Path) -> Path: + if ( + not output_root.is_absolute() + or ".." in output_root.parts + or output_root != output_root.resolve(strict=False) + ): + raise RuntimeError("run root must be an absolute canonical path") + _assert_no_symlink_components(output_root) + return output_root + + +def _expected_path(root: Path, relative: object, prefix: str) -> Path: + path = _relative_artifact_path(relative, prefix) + candidate = root / path + _assert_no_symlink_components(candidate) + if candidate != candidate.resolve(strict=False): + raise RuntimeError("artifact path is aliased or noncanonical") + try: + candidate.relative_to(root) + except ValueError as error: + raise RuntimeError("artifact path escapes bound run root") from error + return candidate + + +def _check_registry_entry( + *, + ordinal: int, + scope: str, + case_id: str | None, + family: str, + check_case_id: str, + backends: tuple[str, ...] = (), +) -> dict[str, object]: + identity = { + "ordinal": ordinal, + "scope": scope, + "case_id": case_id, + "family": family, + "check_case_id": check_case_id, + "backends": list(backends), + } + return { + "check_id": _sha256(_canonical_bytes(identity)), + **identity, + } + + +def _global_check_registry() -> list[dict[str, object]]: + return [ + _check_registry_entry( + ordinal=index, + scope="global", + case_id=None, + family=family, + check_case_id=check_case_id, + ) + for index, (family, check_case_id) in enumerate( + zip(EXACT_FAMILIES, GLOBAL_CHECK_CASES, strict=True) + ) + ] + + +def _case_check_registry(case: object) -> list[dict[str, object]]: + case_id = str(case.case_id) + records: list[tuple[str, str, tuple[str, ...]]] = [] + if case.length <= 6: + records.extend( + ( + "all-graph-probability", + f"{case_id}/{backend}", + (backend,), + ) + for backend in SAMPLERS + ) + records.extend( + ( + "edge-class-frequency", + f"{case_id}/{backend}/d{distance}", + (backend,), + ) + for backend in SAMPLERS + for distance in range(1, case.length // 2 + 1) + ) + records.extend( + ( + "poisson-event-count", + f"{case_id}/{backend}", + (backend,), + ) + for backend in ("poisson-reference", "poisson-numba") + ) + records.extend( + ("no-edge", f"{case_id}/{backend}", (backend,)) + for backend in SAMPLERS + ) + for left, right in PAIR_NAMES: + pair_id = f"{case_id}/{left}-vs-{right}" + records.extend( + (family, pair_id, (left, right)) + for family in SCALAR_COLUMNS + ) + records.extend( + (family, pair_id, (left, right)) + for family in ("bond-length", "component-partition") + ) + return [ + _check_registry_entry( + ordinal=index, + scope="cell", + case_id=case_id, + family=family, + check_case_id=check_case_id, + backends=backends, + ) + for index, (family, check_case_id, backends) in enumerate(records) + ] + + +def _build_validation_run_spec( + protocol: ValidationProtocol, + output_root: Path, + *, + enforce_production: bool, +) -> dict[str, object]: + if not isinstance(protocol, ValidationProtocol): + raise ValueError("protocol must be a ValidationProtocol") + if not isinstance(output_root, Path): + raise ValueError("output_root must be a pathlib.Path") + if enforce_production: + protocol.require_production() + output_root = _canonical_run_root(output_root) + protocol_document = _protocol_document(protocol) + source = _verified_source_state() + capability, capability_hash = _runtime_document() + lock_hash = _lock_hash() + modules = _implementation_hashes() + implementation_hash = _implementation_digest(modules) + cells = [] + for case_index, case in enumerate(protocol.case_registry): + expected_checks = _case_check_registry(case) + registry_hash = _sha256(_canonical_bytes({"checks": expected_checks})) + identity = { + "case_index": case_index, + "case_id": case.case_id, + "protocol_sha256": protocol_document["sha256"], + "source_revision": source["source_revision"], + "runtime_capability_sha256": capability_hash, + "uv_lock_sha256": lock_hash, + "implementation_sha256": implementation_hash, + "expected_check_registry_sha256": registry_hash, + } + cell_hash = _sha256(_canonical_bytes(identity)) + cells.append( + { + **identity, + "cell_sha256": cell_hash, + "partial_path": f"cells/{case_index:03d}-{cell_hash[:16]}.json", + "manifest_path": ( + f"manifests/{case_index:03d}-{cell_hash[:16]}.json" + ), + "expected_checks": expected_checks, + } + ) + document: dict[str, object] = { + "schema_version": RUN_SPEC_SCHEMA, + "validation_schema_version": VALIDATION_PROTOCOL_VERSION, + "protocol": protocol_document, + "source": source, + "source_revision": source["source_revision"], + "runtime_capability": capability, + "runtime_capability_sha256": capability_hash, + "uv_lock_sha256": lock_hash, + "implementation_modules": modules, + "implementation_sha256": implementation_hash, + "run_root": str(output_root), + "artifact_root": ".", + "global_partial_path": "global/exact-checks.json", + "global_manifest_path": "global/exact-checks.manifest.json", + "global_expected_checks": _global_check_registry(), + "final_report_path": FINAL_REPORT_PATH, + "logs_path": "logs", + "cells": cells, + } + document["run_spec_sha256"] = _document_hash(document, "run_spec_sha256") + validate_run_spec(document, enforce_production=enforce_production) + return document + + +def build_validation_run_spec( + protocol: ValidationProtocol, + output_root: Path, +) -> dict[str, object]: + return _build_validation_run_spec( + protocol, output_root, enforce_production=True + ) + + +def validate_run_spec( + document: Mapping[str, object], + *, + enforce_production: bool = True, +) -> None: + expected_top_level = { + "schema_version", + "validation_schema_version", + "protocol", + "source", + "source_revision", + "runtime_capability", + "runtime_capability_sha256", + "uv_lock_sha256", + "implementation_modules", + "implementation_sha256", + "run_root", + "artifact_root", + "global_partial_path", + "global_manifest_path", + "global_expected_checks", + "final_report_path", + "logs_path", + "cells", + "run_spec_sha256", + } + if set(document) != expected_top_level: + raise RuntimeError("run spec fields are not exact") + if document.get("schema_version") != RUN_SPEC_SCHEMA: + raise RuntimeError("run spec schema is invalid") + if document.get("validation_schema_version") != VALIDATION_PROTOCOL_VERSION: + raise RuntimeError("run spec validation schema is stale") + actual_hash = _document_hash(document, "run_spec_sha256") + if document.get("run_spec_sha256") != actual_hash: + raise RuntimeError("run spec hash mismatch") + protocol = _protocol_from_document( + document["protocol"], enforce_production=enforce_production + ) + cells = document.get("cells") + if not isinstance(cells, list) or len(cells) != len(protocol.case_registry): + raise RuntimeError("run spec cell count does not match protocol") + if enforce_production and len(cells) != 120: + raise RuntimeError("production run spec must contain exactly 120 cells") + root = _canonical_run_root(Path(str(document.get("run_root", "")))) + source = document.get("source") + if ( + not isinstance(source, Mapping) + or set(source) + != {"source_revision", "clean_tree", "provenance_error"} + or source.get("clean_tree") is not True + or source.get("provenance_error") is not None + or source.get("source_revision") != document.get("source_revision") + ): + raise RuntimeError("run spec source state is not exactly clean") + capability = document.get("runtime_capability") + if ( + not isinstance(capability, Mapping) + or document.get("runtime_capability_sha256") + != _sha256(_canonical_bytes(capability)) + ): + raise RuntimeError("run spec runtime capability hash is malformed") + if document.get("artifact_root") != ".": + raise RuntimeError("artifact root is not frozen") + if ( + document.get("global_partial_path") != "global/exact-checks.json" + or document.get("global_manifest_path") + != "global/exact-checks.manifest.json" + ): + raise RuntimeError("global artifact paths are not frozen") + if document.get("final_report_path") != FINAL_REPORT_PATH: + raise RuntimeError("final report path is not frozen") + if document.get("logs_path") != "logs": + raise RuntimeError("logs path is not frozen") + seen_ids: set[str] = set() + seen_hashes: set[str] = set() + artifact_paths: set[str] = { + RUN_SPEC_NAME, + str(document.get("global_partial_path")), + str(document.get("global_manifest_path")), + str(document.get("final_report_path")), + } + expected_global = _global_check_registry() + if document.get("global_expected_checks") != expected_global: + raise RuntimeError("global expected check registry is not frozen") + for index, (cell, case) in enumerate( + zip(cells, protocol.case_registry, strict=True) + ): + if not isinstance(cell, Mapping): + raise RuntimeError("run spec cell is malformed") + if set(cell) != { + "case_index", + "case_id", + "protocol_sha256", + "source_revision", + "runtime_capability_sha256", + "uv_lock_sha256", + "implementation_sha256", + "expected_check_registry_sha256", + "cell_sha256", + "partial_path", + "manifest_path", + "expected_checks", + }: + raise RuntimeError("run spec cell fields are not exact") + if cell.get("case_index") != index or cell.get("case_id") != case.case_id: + raise RuntimeError("run spec cell registry is noncanonical") + expected_checks = _case_check_registry(case) + if cell.get("expected_checks") != expected_checks: + raise RuntimeError("cell expected check registry is not frozen") + registry_hash = _sha256(_canonical_bytes({"checks": expected_checks})) + identity = { + "case_index": index, + "case_id": case.case_id, + "protocol_sha256": document["protocol"]["sha256"], + "source_revision": document["source_revision"], + "runtime_capability_sha256": document[ + "runtime_capability_sha256" + ], + "uv_lock_sha256": document["uv_lock_sha256"], + "implementation_sha256": document["implementation_sha256"], + "expected_check_registry_sha256": registry_hash, + } + expected_hash = _sha256(_canonical_bytes(identity)) + if cell.get("cell_sha256") != expected_hash: + raise RuntimeError("run spec cell hash mismatch") + seen_ids.add(case.case_id) + seen_hashes.add(expected_hash) + expected_partial = f"cells/{index:03d}-{expected_hash[:16]}.json" + expected_manifest = f"manifests/{index:03d}-{expected_hash[:16]}.json" + if ( + cell.get("partial_path") != expected_partial + or cell.get("manifest_path") != expected_manifest + ): + raise RuntimeError("cell artifact paths are not canonical") + artifact_paths.add(expected_partial) + artifact_paths.add(expected_manifest) + if len(seen_ids) != len(cells) or len(seen_hashes) != len(cells): + raise RuntimeError("run spec contains duplicate cells") + if len(artifact_paths) != 4 + 2 * len(cells): + raise RuntimeError("run spec contains duplicate or overlapping paths") + for relative in artifact_paths: + prefix = relative.split("/", 1)[0] + if relative == RUN_SPEC_NAME: + candidate = root / relative + _assert_no_symlink_components(candidate) + elif prefix in {"global", "cells", "manifests", "report"}: + _expected_path(root, relative, prefix) + else: + raise RuntimeError("run spec artifact namespace is invalid") + modules = document.get("implementation_modules") + if ( + not isinstance(modules, Mapping) + or document.get("implementation_sha256") + != _implementation_digest(modules) + ): + raise RuntimeError("implementation module hashes are malformed") + _expected_path(root, "logs", "logs") + + +def _load_run_spec( + path: Path, + *, + enforce_production: bool, +) -> tuple[dict[str, object], ValidationProtocol]: + if not isinstance(path, Path): + raise RuntimeError("run spec path must be a pathlib.Path") + if ( + not path.is_absolute() + or ".." in path.parts + or path != path.resolve(strict=False) + ): + raise RuntimeError("run spec path must be absolute and canonical") + _assert_no_symlink_components(path) + if path.is_symlink() or not path.is_file(): + raise RuntimeError("run spec must be a regular non-symlink file") + try: + payload = path.read_bytes() + document = json.loads(payload) + except (OSError, UnicodeError, json.JSONDecodeError) as error: + raise RuntimeError(f"unable to load run spec: {error}") from error + if payload != _canonical_bytes(document): + raise RuntimeError("run spec is not canonical JSON") + validate_run_spec(document, enforce_production=enforce_production) + root = Path(document["run_root"]) + if path != root / RUN_SPEC_NAME: + raise RuntimeError("run spec path does not match its bound run root") + _assert_dependencies(document) + return document, _protocol_from_document( + document["protocol"], enforce_production=enforce_production + ) + + +def _write_once(path: Path, payload: bytes) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + _assert_no_symlink_components(path) + if path.is_symlink(): + raise RuntimeError("refusing to publish through a symlink") + temporary = path.parent / f".{path.name}.{uuid.uuid4().hex}.tmp" + try: + descriptor = os.open( + temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644 + ) + with os.fdopen(descriptor, "wb") as stream: + stream.write(payload) + stream.flush() + os.fsync(stream.fileno()) + try: + _rename_no_replace(temporary, path) + except FileExistsError: + if path.is_symlink() or not path.is_file(): + raise RuntimeError("immutable output is not a regular file") + if path.read_bytes() != payload: + raise RuntimeError("immutable output already exists with other bytes") + directory = os.open(path.parent, os.O_RDONLY) + try: + os.fsync(directory) + finally: + os.close(directory) + finally: + try: + temporary.unlink() + except FileNotFoundError: + pass + + +def _write_validation_run_spec( + protocol: ValidationProtocol, + output_root: Path, + run_spec_path: Path, + *, + enforce_production: bool, +) -> dict[str, object]: + output_root = _canonical_run_root(output_root) + expected_spec_path = output_root / RUN_SPEC_NAME + if ( + not run_spec_path.is_absolute() + or run_spec_path != run_spec_path.resolve(strict=False) + or run_spec_path != expected_spec_path + ): + raise RuntimeError("run spec path must be fixed under the bound run root") + if run_spec_path.exists(): + existing, _ = _load_run_spec( + run_spec_path, enforce_production=enforce_production + ) + expected = _build_validation_run_spec( + protocol, + output_root, + enforce_production=enforce_production, + ) + if existing != expected: + raise RuntimeError("existing run spec differs from requested spec") + return existing + document = _build_validation_run_spec( + protocol, + output_root, + enforce_production=enforce_production, + ) + _write_once(run_spec_path, _canonical_bytes(document)) + reloaded, _ = _load_run_spec( + run_spec_path, enforce_production=enforce_production + ) + return reloaded + + +def write_validation_run_spec( + protocol: ValidationProtocol, + output_root: Path, + run_spec_path: Path, +) -> dict[str, object]: + return _write_validation_run_spec( + protocol, + output_root, + run_spec_path, + enforce_production=True, + ) + + +def _write_test_run_spec( + protocol: ValidationProtocol, + output_root: Path, + run_spec_path: Path, +) -> dict[str, object]: + return _write_validation_run_spec( + protocol, + output_root, + run_spec_path, + enforce_production=False, + ) + + +def _manifest_document( + spec: Mapping[str, object], + artifact_path: str, + artifact_payload: bytes, +) -> dict[str, object]: + return { + "schema_version": MANIFEST_SCHEMA, + "status": "success", + **_dependency_fields(spec), + "artifact_path": artifact_path, + "artifact_sha256": _sha256(artifact_payload), + "artifact_size": len(artifact_payload), + } + + +def _validate_manifest( + manifest: Mapping[str, object], + spec: Mapping[str, object], + artifact_path: str, + artifact_payload: bytes, +) -> None: + expected = _manifest_document( + spec, artifact_path, artifact_payload + ) + if dict(manifest) != expected: + raise RuntimeError("artifact manifest is stale or corrupt") + + +def _load_json_payload(path: Path) -> tuple[dict[str, object], bytes]: + _assert_no_symlink_components(path) + if path.is_symlink() or not path.is_file(): + raise RuntimeError("artifact must be a regular non-symlink file") + payload = path.read_bytes() + try: + document = json.loads(payload) + except (UnicodeError, json.JSONDecodeError) as error: + raise RuntimeError(f"artifact is not valid JSON: {error}") from error + if payload != _canonical_bytes(document): + raise RuntimeError("artifact is not canonical JSON") + return document, payload + + +def _validate_shard_document( + document: Mapping[str, object], + spec: Mapping[str, object], + *, + schema: str, + case: Mapping[str, object] | None, + expected_checks: list[dict[str, object]], +) -> None: + if document.get("schema_version") != schema: + raise RuntimeError("shard artifact schema is invalid") + expected_document_fields = { + "schema_version", + *_dependency_fields(spec), + "check_records", + "elapsed_seconds", + } + if case is not None: + expected_document_fields.update( + {"case_index", "case_id", "cell_sha256"} + ) + if set(document) != expected_document_fields: + raise RuntimeError("shard artifact fields are not exact") + for key, value in _dependency_fields(spec).items(): + if document.get(key) != value: + raise RuntimeError(f"shard dependency mismatch: {key}") + records = document.get("check_records") + if not isinstance(records, list) or not records: + raise RuntimeError("shard artifact has no check registry") + if len(records) != len(expected_checks): + raise RuntimeError("shard check registry length mismatch") + required = { + "family", + "case_id", + "raw", + "expected", + "threshold", + "margin", + "passed", + } + seen: set[str] = set() + for record, expected in zip(records, expected_checks, strict=True): + if not isinstance(record, Mapping): + raise RuntimeError("shard check registry contains malformed records") + metadata = dict(record) + check = metadata.pop("check", None) + if metadata != expected: + raise RuntimeError("shard check registry identity or order mismatch") + check_id = str(record.get("check_id")) + if check_id in seen: + raise RuntimeError("shard check registry contains duplicate IDs") + seen.add(check_id) + if not isinstance(check, Mapping) or set(check) != required: + raise RuntimeError("shard check registry contains a malformed check") + if ( + not isinstance(check["family"], str) + or not isinstance(check["case_id"], str) + or not isinstance(check["passed"], bool) + or not math.isfinite(float(check["threshold"])) + or not math.isfinite(float(check["margin"])) + ): + raise RuntimeError("shard check registry contains invalid check fields") + if ( + check["family"] != expected["family"] + or check["case_id"] != expected["check_case_id"] + ): + raise RuntimeError("shard check registry check association mismatch") + elapsed = document.get("elapsed_seconds") + if ( + isinstance(elapsed, bool) + or not isinstance(elapsed, (int, float)) + or not math.isfinite(float(elapsed)) + or float(elapsed) < 0.0 + ): + raise RuntimeError("shard artifact elapsed time is invalid") + if case is not None and ( + document.get("case_index") != case["case_index"] + or document.get("case_id") != case["case_id"] + or document.get("cell_sha256") != case["cell_sha256"] + ): + raise RuntimeError("cell artifact identity mismatch") + + +def _bind_check_records( + checks: list[dict[str, object]], + expected_checks: list[dict[str, object]], +) -> list[dict[str, object]]: + if len(checks) != len(expected_checks): + raise RuntimeError("computed checks do not match frozen check registry") + records = [] + for check, expected in zip(checks, expected_checks, strict=True): + if ( + check.get("family") != expected["family"] + or check.get("case_id") != expected["check_case_id"] + ): + raise RuntimeError("computed check identity is not frozen") + records.append({**expected, "check": check}) + return records + + +def _reuse_shard_if_present( + spec_path: Path, + spec: Mapping[str, object], + *, + artifact_relative: str, + manifest_relative: str, + schema: str, + case: Mapping[str, object] | None, + expected_checks: list[dict[str, object]], +) -> dict[str, object] | None: + root = Path(spec["run_root"]) + artifact_path = _expected_path( + root, artifact_relative, artifact_relative.split("/", 1)[0] + ) + manifest_path = _expected_path( + root, manifest_relative, manifest_relative.split("/", 1)[0] + ) + if not artifact_path.exists(): + if manifest_path.exists(): + raise RuntimeError("success manifest exists without its artifact") + return None + artifact, payload = _load_json_payload(artifact_path) + _validate_shard_document( + artifact, + spec, + schema=schema, + case=case, + expected_checks=expected_checks, + ) + if manifest_path.exists(): + manifest, _ = _load_json_payload(manifest_path) + _validate_manifest(manifest, spec, artifact_relative, payload) + return manifest + manifest = _manifest_document(spec, artifact_relative, payload) + _write_once(manifest_path, _canonical_bytes(manifest)) + return manifest + + +def _publish_shard( + spec_path: Path, + spec: Mapping[str, object], + *, + artifact_relative: str, + manifest_relative: str, + document: Mapping[str, object], + schema: str, + case: Mapping[str, object] | None, + expected_checks: list[dict[str, object]], + crash_hook: Callable[[str], None] | None, +) -> dict[str, object]: + root = Path(spec["run_root"]) + artifact_path = _expected_path( + root, artifact_relative, artifact_relative.split("/", 1)[0] + ) + manifest_path = _expected_path( + root, manifest_relative, manifest_relative.split("/", 1)[0] + ) + payload = _canonical_bytes(document) + if artifact_path.exists(): + existing, existing_payload = _load_json_payload(artifact_path) + _validate_shard_document( + existing, + spec, + schema=schema, + case=case, + expected_checks=expected_checks, + ) + if manifest_path.exists(): + manifest, _ = _load_json_payload(manifest_path) + _validate_manifest( + manifest, spec, artifact_relative, existing_payload + ) + return manifest + manifest = _manifest_document( + spec, artifact_relative, existing_payload + ) + _write_once(manifest_path, _canonical_bytes(manifest)) + return manifest + if manifest_path.exists(): + raise RuntimeError("success manifest exists without its artifact") + + artifact_path.parent.mkdir(parents=True, exist_ok=True) + _assert_no_symlink_components(artifact_path) + temporary = artifact_path.parent / ( + f".{artifact_path.name}.{uuid.uuid4().hex}.tmp" + ) + try: + descriptor = os.open( + temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644 + ) + with os.fdopen(descriptor, "wb") as stream: + stream.write(payload) + stream.flush() + os.fsync(stream.fileno()) + reloaded = json.loads(temporary.read_text(encoding="utf-8")) + if temporary.read_bytes() != _canonical_bytes(reloaded): + raise RuntimeError("temporary shard failed canonical reload") + _validate_shard_document( + reloaded, + spec, + schema=schema, + case=case, + expected_checks=expected_checks, + ) + if crash_hook is not None: + crash_hook("before-artifact-rename") + try: + _rename_no_replace(temporary, artifact_path) + except FileExistsError: + # A concurrent duplicate task won publication. Never overwrite it; + # the semantic/hash checks below decide whether it is reusable. + pass + directory = os.open(artifact_path.parent, os.O_RDONLY) + try: + os.fsync(directory) + finally: + os.close(directory) + finally: + try: + temporary.unlink() + except FileNotFoundError: + pass + published, published_payload = _load_json_payload(artifact_path) + _validate_shard_document( + published, + spec, + schema=schema, + case=case, + expected_checks=expected_checks, + ) + manifest = _manifest_document( + spec, artifact_relative, published_payload + ) + if manifest_path.exists(): + existing_manifest, _ = _load_json_payload(manifest_path) + _validate_manifest( + existing_manifest, spec, artifact_relative, published_payload + ) + return existing_manifest + _write_once(manifest_path, _canonical_bytes(manifest)) + return manifest + + +def _run_validation_global_checks( + run_spec_path: Path, + *, + enforce_production: bool, + crash_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + spec, protocol = _load_run_spec( + run_spec_path, enforce_production=enforce_production + ) + expected_checks = spec["global_expected_checks"] + reused = _reuse_shard_if_present( + run_spec_path, + spec, + artifact_relative=str(spec["global_partial_path"]), + manifest_relative=str(spec["global_manifest_path"]), + schema=GLOBAL_SCHEMA, + case=None, + expected_checks=expected_checks, + ) + if reused is not None: + return reused + started = time.perf_counter() + checks = _exact_checks(protocol) + document = { + "schema_version": GLOBAL_SCHEMA, + **_dependency_fields(spec), + "check_records": _bind_check_records(checks, expected_checks), + "elapsed_seconds": time.perf_counter() - started, + } + return _publish_shard( + run_spec_path, + spec, + artifact_relative=str(spec["global_partial_path"]), + manifest_relative=str(spec["global_manifest_path"]), + document=document, + schema=GLOBAL_SCHEMA, + case=None, + expected_checks=expected_checks, + crash_hook=crash_hook, + ) + + +def run_validation_global_checks( + run_spec_path: Path, + *, + crash_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + return _run_validation_global_checks( + run_spec_path, + enforce_production=True, + crash_hook=crash_hook, + ) + + +def _run_test_global_checks( + run_spec_path: Path, + *, + crash_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + return _run_validation_global_checks( + run_spec_path, + enforce_production=False, + crash_hook=crash_hook, + ) + + +def _run_validation_cell( + run_spec_path: Path, + case_index: int, + *, + enforce_production: bool, + crash_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + spec, protocol = _load_run_spec( + run_spec_path, enforce_production=enforce_production + ) + if ( + isinstance(case_index, bool) + or not isinstance(case_index, int) + or not 0 <= case_index < len(spec["cells"]) + ): + raise ValueError("case_index is outside the run spec") + cell = spec["cells"][case_index] + expected_checks = cell["expected_checks"] + reused = _reuse_shard_if_present( + run_spec_path, + spec, + artifact_relative=str(cell["partial_path"]), + manifest_relative=str(cell["manifest_path"]), + schema=CELL_SCHEMA, + case=cell, + expected_checks=expected_checks, + ) + if reused is not None: + return { + "case_index": case_index, + "case_id": cell["case_id"], + "partial_path": cell["partial_path"], + "manifest_path": cell["manifest_path"], + "artifact_sha256": reused["artifact_sha256"], + } + started = time.perf_counter() + checks = _run_case_checks( + protocol, (case_index, protocol.case_registry[case_index]) + ) + document = { + "schema_version": CELL_SCHEMA, + **_dependency_fields(spec), + "case_index": case_index, + "case_id": cell["case_id"], + "cell_sha256": cell["cell_sha256"], + "check_records": _bind_check_records(checks, expected_checks), + "elapsed_seconds": time.perf_counter() - started, + } + manifest = _publish_shard( + run_spec_path, + spec, + artifact_relative=str(cell["partial_path"]), + manifest_relative=str(cell["manifest_path"]), + document=document, + schema=CELL_SCHEMA, + case=cell, + expected_checks=expected_checks, + crash_hook=crash_hook, + ) + return { + "case_index": case_index, + "case_id": cell["case_id"], + "partial_path": cell["partial_path"], + "manifest_path": cell["manifest_path"], + "artifact_sha256": manifest["artifact_sha256"], + } + + +def run_validation_cell( + run_spec_path: Path, + case_index: int, + *, + crash_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + return _run_validation_cell( + run_spec_path, + case_index, + enforce_production=True, + crash_hook=crash_hook, + ) + + +def _run_test_cell( + run_spec_path: Path, + case_index: int, + *, + crash_hook: Callable[[str], None] | None = None, +) -> dict[str, object]: + return _run_validation_cell( + run_spec_path, + case_index, + enforce_production=False, + crash_hook=crash_hook, + ) + + +def _verify_exact_directory( + root: Path, + directory: str, + expected: set[str], +) -> None: + path = root / directory + _assert_no_symlink_components(path) + actual = ( + { + str(item.relative_to(root)) + for item in path.iterdir() + } + if path.is_dir() + else set() + ) + if actual != expected: + missing = sorted(expected - actual) + extra = sorted(actual - expected) + raise RuntimeError( + f"{directory} artifact set mismatch; missing={missing}, extra={extra}" + ) + + +def _load_verified_shard( + spec_path: Path, + spec: Mapping[str, object], + artifact_relative: str, + manifest_relative: str, + *, + schema: str, + case: Mapping[str, object] | None, + expected_checks: list[dict[str, object]], +) -> dict[str, object]: + root = Path(spec["run_root"]) + artifact_path = _expected_path( + root, artifact_relative, artifact_relative.split("/", 1)[0] + ) + manifest_path = _expected_path( + root, manifest_relative, manifest_relative.split("/", 1)[0] + ) + artifact, payload = _load_json_payload(artifact_path) + _validate_shard_document( + artifact, + spec, + schema=schema, + case=case, + expected_checks=expected_checks, + ) + manifest, _ = _load_json_payload(manifest_path) + _validate_manifest(manifest, spec, artifact_relative, payload) + return artifact + + +def canonical_scientific_report_bytes( + report: Mapping[str, object], +) -> bytes: + scientific = dict(report) + scientific.pop("elapsed_seconds", None) + return canonical_report_bytes(scientific) + + +def _merge_validation_shards( + run_spec_path: Path, + output: Path | None, + *, + enforce_production: bool, +) -> dict[str, object]: + spec, protocol = _load_run_spec( + run_spec_path, enforce_production=enforce_production + ) + root = Path(spec["run_root"]) + fixed_output = _expected_path(root, spec["final_report_path"], "report") + if output is not None and ( + not output.is_absolute() + or output != output.resolve(strict=False) + or output != fixed_output + ): + raise RuntimeError("merge output must equal the fixed run-spec report path") + expected_cells = {str(cell["partial_path"]) for cell in spec["cells"]} + expected_manifests = { + str(cell["manifest_path"]) for cell in spec["cells"] + } + _verify_exact_directory( + root, "cells", expected_cells + ) + _verify_exact_directory( + root, "manifests", expected_manifests + ) + global_expected = { + str(spec["global_partial_path"]), + str(spec["global_manifest_path"]), + } + _verify_exact_directory( + root, "global", global_expected + ) + + global_artifact = _load_verified_shard( + run_spec_path, + spec, + str(spec["global_partial_path"]), + str(spec["global_manifest_path"]), + schema=GLOBAL_SCHEMA, + case=None, + expected_checks=spec["global_expected_checks"], + ) + checks = [ + record["check"] for record in global_artifact["check_records"] + ] + elapsed = float(global_artifact["elapsed_seconds"]) + for cell in spec["cells"]: + artifact = _load_verified_shard( + run_spec_path, + spec, + str(cell["partial_path"]), + str(cell["manifest_path"]), + schema=CELL_SCHEMA, + case=cell, + expected_checks=cell["expected_checks"], + ) + checks.extend( + record["check"] for record in artifact["check_records"] + ) + elapsed += float(artifact["elapsed_seconds"]) + report = assemble_validation_report( + protocol, + checks, + elapsed_seconds=elapsed, + runtime_capability_value=spec["runtime_capability"], + source=spec["source"], + ) + payload = canonical_report_bytes(report) + _assert_no_symlink_components(fixed_output) + _write_once(fixed_output, payload) + return report + + +def merge_validation_shards( + run_spec_path: Path, + output: Path | None = None, +) -> dict[str, object]: + return _merge_validation_shards( + run_spec_path, + output, + enforce_production=True, + ) + + +def _merge_test_shards( + run_spec_path: Path, + output: Path | None = None, +) -> dict[str, object]: + return _merge_validation_shards( + run_spec_path, + output, + enforce_production=False, + ) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/data/random123_philox4x32_10.json b/tracks/qmc/solutions/frustration-free/challenge-194/tests/data/random123_philox4x32_10.json new file mode 100644 index 000000000..30a2120f6 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/data/random123_philox4x32_10.json @@ -0,0 +1,16 @@ +{ + "algorithm": "Philox4x32-10", + "source": "https://github.com/DEShawResearch/random123/blob/main/tests/kat_vectors", + "vectors": [ + { + "counter": ["00000000", "00000000", "00000000", "00000000"], + "key": ["00000000", "00000000"], + "output": ["6627e8d5", "e169c58d", "bc57ac4c", "9b00dbd8"] + }, + { + "counter": ["ffffffff", "ffffffff", "ffffffff", "ffffffff"], + "key": ["ffffffff", "ffffffff"], + "output": ["408f276d", "41c83b0e", "a20bc7c6", "6d5451fd"] + } + ] +} diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_alias.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_alias.py new file mode 100644 index 000000000..e73e07ce9 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_alias.py @@ -0,0 +1,377 @@ +from __future__ import annotations + +from hashlib import sha256 +import math + +import numba +import numpy as np +import pytest + +from long_range_percolation.alias import build_distance_alias, draw_alias +from long_range_percolation.counter_rng import ( + STREAM_ALIAS_COLUMN, + STREAM_ALIAS_THRESHOLD, + StreamIdentity, + derive_stream_material, + next_u32, + u32_to_open, +) +from long_range_percolation.kernel import ( + kernel_weight_sum, + periodic_kernel, +) +from long_range_percolation.model import ModelSpec, distance_classes + + +def digest(array: np.ndarray) -> str: + return sha256(array.tobytes()).hexdigest() + + +def _draw_alias_reference( + probability: np.ndarray, + alias: np.ndarray, + column_word: np.uint32, + threshold_word: np.uint32, +) -> int: + column = (int(column_word) * len(probability)) >> 32 + if u32_to_open(threshold_word) <= probability[column]: + return column + return int(alias[column]) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _compiled_frequency_draws( + probability: np.ndarray, + alias: np.ndarray, + sample_count: int, + column_counter: np.ndarray, + column_key: np.ndarray, + threshold_counter: np.ndarray, + threshold_key: np.ndarray, +) -> tuple[np.ndarray, np.ndarray]: + class_count = len(probability) + counts = np.zeros(class_count, dtype=np.uint64) + column_block = np.zeros(4, dtype=np.uint32) + column_lane = np.zeros(2, dtype=np.uint8) + column_accounting = np.zeros(3, dtype=np.uint64) + threshold_block = np.zeros(4, dtype=np.uint32) + threshold_lane = np.zeros(2, dtype=np.uint8) + threshold_accounting = np.zeros(3, dtype=np.uint64) + rejection_threshold = ( + np.uint64(1 << 32) - np.uint64(class_count) + ) % np.uint64(class_count) + + for _ in range(sample_count): + while True: + column_word = next_u32( + column_counter, + column_key, + column_block, + column_lane, + column_accounting, + ) + product = np.uint64(column_word) * np.uint64(class_count) + low = product & np.uint64(0xFFFFFFFF) + if low < rejection_threshold: + column_accounting[2] += np.uint64(1) + continue + break + threshold_word = next_u32( + threshold_counter, + threshold_key, + threshold_block, + threshold_lane, + threshold_accounting, + ) + selected = draw_alias( + probability, alias, column_word, threshold_word + ) + counts[selected] += np.uint64(1) + + return counts, np.asarray( + ( + column_accounting[0], + column_accounting[2], + threshold_accounting[0], + ), + dtype=np.uint64, + ) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _scripted_rejection_aware_alias_draw( + probability: np.ndarray, + alias: np.ndarray, + column_block: np.ndarray, + threshold_block: np.ndarray, +) -> tuple[int, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + class_count = len(probability) + column_counter = np.zeros(4, dtype=np.uint32) + column_key = np.zeros(2, dtype=np.uint32) + column_lane = np.asarray((0, 1), dtype=np.uint8) + column_accounting = np.zeros(3, dtype=np.uint64) + threshold_counter = np.zeros(4, dtype=np.uint32) + threshold_key = np.zeros(2, dtype=np.uint32) + threshold_lane = np.asarray((0, 1), dtype=np.uint8) + threshold_accounting = np.zeros(3, dtype=np.uint64) + rejection_threshold = ( + np.uint64(1 << 32) - np.uint64(class_count) + ) % np.uint64(class_count) + + while True: + column_word = next_u32( + column_counter, + column_key, + column_block, + column_lane, + column_accounting, + ) + product = np.uint64(column_word) * np.uint64(class_count) + if ( + product & np.uint64(0xFFFFFFFF) + ) < rejection_threshold: + column_accounting[2] += np.uint64(1) + continue + break + threshold_word = next_u32( + threshold_counter, + threshold_key, + threshold_block, + threshold_lane, + threshold_accounting, + ) + selected = draw_alias( + probability, alias, column_word, threshold_word + ) + lanes = np.asarray( + ( + column_lane[0], + column_lane[1], + threshold_lane[0], + threshold_lane[1], + ), + dtype=np.uint8, + ) + counters = np.concatenate((column_counter, threshold_counter)) + return ( + selected, + column_accounting, + threshold_accounting, + lanes, + counters, + ) + + +def test_alias_table_is_deterministic_defensive_and_read_only(): + kernel = periodic_kernel(256, 0.9) + first = build_distance_alias(256, 0.9, kernel, digest(kernel)) + second = build_distance_alias( + 256, 0.9, kernel.copy(), first.kernel_sha256 + ) + + np.testing.assert_array_equal(first.probability, second.probability) + np.testing.assert_array_equal(first.alias, second.alias) + np.testing.assert_array_equal(first.multiplicity, second.multiplicity) + np.testing.assert_array_equal(first.class_weight, second.class_weight) + assert first.kernel_sha256 == digest(kernel) + for array in ( + first.probability, + first.alias, + first.multiplicity, + first.class_weight, + ): + assert array.flags.c_contiguous + assert not array.flags.writeable + + kernel[:] = 1.0 + assert first.kernel_sha256 != digest(kernel) + assert not np.all(first.class_weight == first.multiplicity) + + +def test_alias_invariants_cover_antipodal_class_and_finite_extremes(): + for length, sigma in [ + (2, 1.0), + (256, math.ulp(1.0)), + (256, 128.0), + ]: + spec = ModelSpec(length=length, sigma=sigma, kappa=0.0) + kernel = periodic_kernel(spec.length, spec.sigma) + table = build_distance_alias( + spec.length, spec.sigma, kernel, digest(kernel) + ) + expected_multiplicity = np.asarray( + [item.multiplicity for item in distance_classes(length)], + dtype=np.uint64, + ) + expected_weight = expected_multiplicity * kernel + + assert table.probability.dtype == np.dtype(np.float64) + assert table.alias.dtype == np.dtype(np.int64) + assert table.multiplicity.dtype == np.dtype(np.uint64) + assert table.class_weight.dtype == np.dtype(np.float64) + assert np.all( + (0.0 <= table.probability) & (table.probability <= 1.0) + ) + assert np.all( + (0 <= table.alias) & (table.alias < length // 2) + ) + np.testing.assert_array_equal( + table.multiplicity, expected_multiplicity + ) + np.testing.assert_array_equal(table.class_weight, expected_weight) + assert int(table.multiplicity[-1]) == length // 2 + assert int(table.multiplicity.sum()) == length * (length - 1) // 2 + assert table.total_rate == math.fsum( + float(value) for value in table.class_weight + ) + assert table.total_rate == pytest.approx( + kernel_weight_sum(length, sigma), rel=2e-13 + ) + assert abs(table.normalized_residual) <= 8 * np.finfo(float).eps + implied_probability = np.zeros(length // 2, dtype=np.float64) + for column in range(length // 2): + implied_probability[column] += ( + table.probability[column] / (length // 2) + ) + implied_probability[table.alias[column]] += ( + 1.0 - table.probability[column] + ) / (length // 2) + np.testing.assert_allclose( + implied_probability, + table.class_weight / table.total_rate, + rtol=2e-13, + atol=0.0, + ) + + +@pytest.mark.parametrize( + ("replacement", "message"), + ( + (np.ones(3, dtype=np.float64), "shape"), + (np.asarray((1.0, np.nan), dtype=np.float64), "finite positive"), + (np.asarray((1.0, 0.0), dtype=np.float64), "finite positive"), + (np.asarray((1.0, np.inf), dtype=np.float64), "finite positive"), + ), +) +def test_alias_builder_rejects_invalid_kernel_values( + replacement: np.ndarray, message: str +): + with pytest.raises(ValueError, match=message): + build_distance_alias(4, 1.0, replacement, digest(replacement)) + + +def test_alias_builder_rejects_kernel_digest_mismatch(): + kernel = periodic_kernel(8, 0.9) + with pytest.raises(ValueError, match="SHA-256"): + build_distance_alias(8, 0.9, kernel, "0" * 64) + + +def test_draw_alias_matches_python_at_boundaries_and_compiles_nopython(): + probability = np.asarray((0.0, 0.5, 1.0), dtype=np.float64) + alias = np.asarray((2, 0, 1), dtype=np.int64) + cases = ( + (np.uint32(0), np.uint32(0)), + (np.uint32(0x55555555), np.uint32(0x7FFFFFFF)), + (np.uint32(0xAAAAAAAA), np.uint32(0xFFFFFFFF)), + (np.uint32(0xFFFFFFFF), np.uint32(0xFFFFFFFF)), + ) + for column_word, threshold_word in cases: + assert draw_alias( + probability, alias, column_word, threshold_word + ) == _draw_alias_reference( + probability, alias, column_word, threshold_word + ) + if not numba.config.DISABLE_JIT: + assert draw_alias.nopython_signatures + + +def test_rejection_aware_alias_draw_uses_scripted_streams_independently(): + probability = np.ones(3, dtype=np.float64) + alias = np.arange(3, dtype=np.int64) + column_block = np.asarray( + (0, 0x55555556, 0xDEADBEEF, 0xFFFFFFFF), + dtype=np.uint32, + ) + threshold_block = np.asarray( + (0x80000000, 0xBAD5EED, 0, 0), + dtype=np.uint32, + ) + + selected, column_accounting, threshold_accounting, lanes, counters = ( + _scripted_rejection_aware_alias_draw( + probability, alias, column_block, threshold_block + ) + ) + + assert selected == 1 + np.testing.assert_array_equal( + column_accounting, np.asarray((2, 0, 1), dtype=np.uint64) + ) + np.testing.assert_array_equal( + threshold_accounting, np.asarray((1, 0, 0), dtype=np.uint64) + ) + np.testing.assert_array_equal( + lanes, np.asarray((2, 1, 1, 1), dtype=np.uint8) + ) + np.testing.assert_array_equal(counters, np.zeros(8, dtype=np.uint32)) + if not numba.config.DISABLE_JIT: + assert _scripted_rejection_aware_alias_draw.nopython_signatures + + +def test_fixed_philox_alias_frequencies_pass_one_simultaneous_threshold(): + length = 256 + sigma = 0.9 + sample_count = 2_000_000 + kernel = periodic_kernel(length, sigma) + table = build_distance_alias(length, sigma, kernel, digest(kernel)) + column_material = derive_stream_material( + StreamIdentity( + 194, + "validation", + length, + "alias-sigma-0.9", + 0, + STREAM_ALIAS_COLUMN, + ) + ) + threshold_material = derive_stream_material( + StreamIdentity( + 194, + "validation", + length, + "alias-sigma-0.9", + 0, + STREAM_ALIAS_THRESHOLD, + ) + ) + observed, accounting = _compiled_frequency_draws( + table.probability, + table.alias, + sample_count, + column_material.initial_counter.copy(), + column_material.key, + threshold_material.initial_counter.copy(), + threshold_material.key, + ) + expected = table.class_weight / table.total_rate + absolute_error = np.abs(observed / sample_count - expected) + epsilon = math.sqrt( + math.log(2 * len(expected) / 0.001) / (2 * sample_count) + ) + evidence = [ + { + "class": index + 1, + "observed_count": int(observed[index]), + "expected_probability": float(expected[index]), + "absolute_error": float(absolute_error[index]), + "threshold": epsilon, + "margin": float(epsilon - absolute_error[index]), + } + for index in range(len(expected)) + ] + + assert int(accounting[0]) == sample_count + int(accounting[1]) + assert int(accounting[2]) == sample_count + assert float(absolute_error.max()) <= epsilon, evidence + if not numba.config.DISABLE_JIT: + assert _compiled_frequency_draws.nopython_signatures diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py new file mode 100644 index 000000000..0bd1eb0f7 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_analyze_pilot_cli.py @@ -0,0 +1,706 @@ +from __future__ import annotations + +import hashlib +import importlib.util +import json +from pathlib import Path + +import pytest + +import long_range_percolation.pilot_analysis as analysis +import long_range_percolation.pilot_extension as extension + +ROOT = Path(__file__).resolve().parents[1] +SCRIPT = ROOT / "scripts" / "analyze_pilot.py" +CLI_SPEC = importlib.util.spec_from_file_location("analyze_pilot_cli", SCRIPT) +assert CLI_SPEC is not None and CLI_SPEC.loader is not None +CLI = importlib.util.module_from_spec(CLI_SPEC) +CLI_SPEC.loader.exec_module(CLI) + + +def _canonical_bytes(document: object) -> bytes: + return ( + json.dumps( + document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + + b"\n" + ) + + +def _analysis_document( + *, + marker: str = "a", + complete: bool = False, +) -> dict[str, object]: + estimates: list[dict[str, object]] = [] + if complete: + for sigma in (0.8, 0.9, 1.0, 1.1): + for length in (8, 16, 32): + for kappa in (0.0, 1.0): + estimates.append( + { + "sigma_hex": sigma.hex(), + "length": length, + "kappa_hex": kappa.hex(), + "means": { + "q_g": float(length), + "four_sector_crossing": kappa, + }, + } + ) + document: dict[str, object] = { + "schema_version": analysis.ANALYSIS_SCHEMA, + "p0_run_spec_sha256": marker * 64, + "p0_progress_sha256": "b" * 64, + "source_revision": "c" * 40, + "analysis_plan_sha256": "d" * 64, + "observable_columns": dict(analysis.OBSERVABLE_COLUMNS), + "estimates": estimates, + } + document["analysis_document_sha256"] = hashlib.sha256( + _canonical_bytes(document) + ).hexdigest() + return document + + +def _extension_brackets(source: dict[str, object]) -> dict[str, object]: + document: dict[str, object] = { + "schema_version": analysis.BRACKET_SCHEMA, + "source_analysis_document_sha256": source["analysis_document_sha256"], + "requires_p0_extension": True, + "brackets": [ + { + "sigma_hex": (1.0).hex(), + "status": "requires_p0_extension", + "reason": "no_nonzero_interval_marked_by_both_estimators", + "lengths": [16, 32], + } + ], + } + document["bracket_document_sha256"] = hashlib.sha256( + _canonical_bytes(document) + ).hexdigest() + return document + + +def test_analyze_command_publishes_once_and_verifies_identical_bytes( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +): + source = _analysis_document(complete=True) + output = tmp_path / "p0_analysis.json" + run_spec = tmp_path / "run_spec.json" + monkeypatch.setattr(CLI, "aggregate_p0", lambda _path: source) + + assert ( + CLI.main( + [ + "analyze", + "--run-spec", + str(run_spec), + "--output", + str(output), + ] + ) + == 0 + ) + first = output.read_bytes() + first_result = json.loads(capsys.readouterr().out) + assert first_result["publication"] == "published" + assert ( + first_result["analysis_document_sha256"] == source["analysis_document_sha256"] + ) + + assert ( + CLI.main( + [ + "analyze", + "--run-spec", + str(run_spec), + "--output", + str(output), + ] + ) + == 0 + ) + assert output.read_bytes() == first + assert json.loads(capsys.readouterr().out)["publication"] == "verified-existing" + + monkeypatch.setattr( + CLI, "aggregate_p0", lambda _path: _analysis_document(marker="e") + ) + assert ( + CLI.main( + [ + "analyze", + "--run-spec", + str(run_spec), + "--output", + str(output), + ] + ) + == 1 + ) + assert output.read_bytes() == first + assert "installed bytes mismatch" in capsys.readouterr().err + + +def test_build_p1_command_refuses_extension_without_publication( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +): + source = _analysis_document(complete=True) + analysis_path = tmp_path / "p0_analysis.json" + analysis_path.write_bytes(_canonical_bytes(source)) + output = tmp_path / "p1_protocol.json" + extension = _extension_brackets(source) + monkeypatch.setattr(CLI, "select_p1_brackets", lambda _source: extension) + monkeypatch.setattr( + CLI, + "build_p1_protocol", + lambda *_args, **_kwargs: (_ for _ in ()).throw( + RuntimeError("P0 extension required before P1 publication: 1.0") + ), + ) + + assert ( + CLI.main( + [ + "build-p1", + "--analysis", + str(analysis_path), + "--output", + str(output), + ] + ) + == 1 + ) + + assert not output.exists() + assert "P0 extension required" in capsys.readouterr().err + + +def test_build_p0_extension_publishes_once_and_rejects_different_bytes( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +): + source = _analysis_document(complete=True) + source_path = tmp_path / "p0_analysis.json" + output = tmp_path / "p0_extension_v1_protocol.json" + source_path.write_bytes(_canonical_bytes(source)) + protocol = { + "schema_version": extension.EXTENSION_PROTOCOL_SCHEMA, + "protocol_sha256": "a" * 64, + } + evidence_root = (tmp_path / "external-p0").resolve() + evidence_root.mkdir() + seen_roots: list[Path] = [] + monkeypatch.setattr( + CLI, + "build_p0_extension_protocol", + lambda _source, root: seen_roots.append(root) or protocol, + ) + + assert ( + CLI.main( + [ + "build-p0-extension", + "--analysis", + str(source_path), + "--p0-evidence-root", + str(evidence_root), + "--output", + str(output), + ] + ) + == 0 + ) + installed = output.read_bytes() + assert json.loads(capsys.readouterr().out) == { + "output": str(output.resolve()), + "protocol_sha256": protocol["protocol_sha256"], + "publication": "published", + "status": "ready", + } + + assert ( + CLI.main( + [ + "build-p0-extension", + "--analysis", + str(source_path), + "--p0-evidence-root", + str(evidence_root), + "--output", + str(output), + ] + ) + == 0 + ) + assert output.read_bytes() == installed + assert json.loads(capsys.readouterr().out) == { + "output": str(output.resolve()), + "protocol_sha256": protocol["protocol_sha256"], + "publication": "verified-existing", + "status": "ready", + } + + monkeypatch.setattr( + CLI, + "build_p0_extension_protocol", + lambda _source, root: ( + seen_roots.append(root) or {**protocol, "protocol_sha256": "b" * 64} + ), + ) + assert ( + CLI.main( + [ + "build-p0-extension", + "--analysis", + str(source_path), + "--p0-evidence-root", + str(evidence_root), + "--output", + str(output), + ] + ) + == 1 + ) + assert output.read_bytes() == installed + captured = capsys.readouterr() + assert captured.out == "" + assert "installed bytes mismatch" in captured.err + assert seen_roots == [evidence_root, evidence_root, evidence_root] + + +def test_build_p0_extension_requires_explicit_evidence_root(): + with pytest.raises(SystemExit): + CLI._parser().parse_args( + [ + "build-p0-extension", + "--analysis", + "/tmp/p0_analysis.json", + "--output", + "/tmp/p0_extension_v1_protocol.json", + ] + ) + + +def test_verify_command_accepts_bound_canonical_protocol( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +): + source = _analysis_document() + protocol: dict[str, object] = { + "schema_version": analysis.P1_PROTOCOL_SCHEMA, + "source_analysis_document_sha256": source["analysis_document_sha256"], + "cells": [], + } + protocol["protocol_sha256"] = hashlib.sha256(_canonical_bytes(protocol)).hexdigest() + analysis_path = tmp_path / "p0_analysis.json" + protocol_path = tmp_path / "p1_protocol.json" + analysis_path.write_bytes(_canonical_bytes(source)) + protocol_path.write_bytes(_canonical_bytes(protocol)) + monkeypatch.setattr(CLI, "validate_p1_protocol", lambda _source, _protocol: None) + + assert ( + CLI.main( + [ + "verify", + "--analysis", + str(analysis_path), + "--p1-protocol", + str(protocol_path), + ] + ) + == 0 + ) + assert json.loads(capsys.readouterr().out) == { + "protocol_sha256": protocol["protocol_sha256"], + "status": "verified", + } + + +def _command_sources(tmp_path: Path, command: str) -> tuple[list[str], Path]: + output = tmp_path / f"{command}.json" + documents = { + "protocol": {"schema_version": extension.EXTENSION_PROTOCOL_SCHEMA}, + "p0": {"schema_version": analysis.ANALYSIS_SCHEMA}, + "extension": {"schema_version": analysis.EXTENSION_ANALYSIS_SCHEMA}, + "combined": {"schema_version": analysis.COMBINED_ANALYSIS_SCHEMA}, + } + paths: dict[str, Path] = {} + for name, document in documents.items(): + path = tmp_path / f"{name}.json" + path.write_bytes(_canonical_bytes(document)) + paths[name] = path + if command == "analyze-extension": + return ( + [ + command, + "--run-spec", + str(tmp_path / "run_spec.json"), + "--protocol", + str(paths["protocol"]), + "--output", + str(output), + ], + output, + ) + if command == "combine": + evidence_root = tmp_path / "p0-root" + evidence_root.mkdir() + extension_run_spec = tmp_path / "extension-root/run_spec.json" + extension_run_spec.parent.mkdir() + extension_run_spec.write_text("{}\n", encoding="utf-8") + return ( + [ + command, + "--p0-analysis", + str(paths["p0"]), + "--extension-analysis", + str(paths["extension"]), + "--p0-evidence-root", + str(evidence_root), + "--extension-run-spec", + str(extension_run_spec), + "--extension-protocol", + str(paths["protocol"]), + "--output", + str(output), + ], + output, + ) + return ( + [ + command, + "--analysis", + str(paths["combined"]), + "--p0-analysis", + str(paths["p0"]), + "--extension-analysis", + str(paths["extension"]), + "--p0-evidence-root", + str(tmp_path / "p0-root"), + "--extension-run-spec", + str(tmp_path / "extension-root/run_spec.json"), + "--extension-protocol", + str(paths["protocol"]), + "--output", + str(output), + ], + output, + ) + + +def _stub_command( + monkeypatch: pytest.MonkeyPatch, + command: str, + marker: str, + *, + fail: bool = False, +) -> dict[str, object]: + schemas = { + "analyze-extension": analysis.EXTENSION_ANALYSIS_SCHEMA, + "combine": analysis.COMBINED_ANALYSIS_SCHEMA, + "select": analysis.COMBINED_BRACKET_SCHEMA, + "build-p1": analysis.P1_PROTOCOL_SCHEMA, + } + hash_fields = { + "analyze-extension": "analysis_document_sha256", + "combine": "analysis_document_sha256", + "select": "bracket_document_sha256", + "build-p1": "protocol_sha256", + } + document = { + "schema_version": schemas[command], + hash_fields[command]: marker * 64, + } + + def result(*_args, **_kwargs): + if fail: + raise RuntimeError("scientific refusal") + return document + + if command == "analyze-extension": + monkeypatch.setattr(CLI, "aggregate_p0_extension", result) + elif command == "combine": + monkeypatch.setattr(CLI, "combine_p0_evidence", result) + elif command == "select": + monkeypatch.setattr(CLI, "select_p1_brackets", result) + else: + monkeypatch.setattr( + CLI, + "select_p1_brackets", + lambda *_args, **_kwargs: { + "schema_version": analysis.COMBINED_BRACKET_SCHEMA, + "requires_p0_extension": False, + }, + ) + monkeypatch.setattr(CLI, "build_p1_protocol", result) + return document + + +@pytest.mark.parametrize( + "command", + ("analyze-extension", "combine", "select", "build-p1"), +) +def test_immutable_commands_publish_verify_and_reject_changed_bytes( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + command: str, +): + arguments, output = _command_sources(tmp_path, command) + _stub_command(monkeypatch, command, "a") + + assert CLI.main(arguments) == 0 + installed = output.read_bytes() + assert json.loads(capsys.readouterr().out)["publication"] == "published" + + assert CLI.main(arguments) == 0 + assert output.read_bytes() == installed + assert json.loads(capsys.readouterr().out)["publication"] == "verified-existing" + + _stub_command(monkeypatch, command, "b") + assert CLI.main(arguments) == 1 + captured = capsys.readouterr() + assert captured.out == "" + assert "installed bytes mismatch" in captured.err + assert output.read_bytes() == installed + + +@pytest.mark.parametrize( + ("command", "malformed_name"), + ( + ("analyze-extension", "protocol"), + ("combine", "p0"), + ("select", "combined"), + ("build-p1", "combined"), + ), +) +def test_immutable_commands_reject_noncanonical_inputs_without_output( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + command: str, + malformed_name: str, +): + arguments, output = _command_sources(tmp_path, command) + (tmp_path / f"{malformed_name}.json").write_text( + '{\n "schema_version": "noncanonical"\n}\n', + encoding="utf-8", + ) + _stub_command(monkeypatch, command, "a") + + assert CLI.main(arguments) == 1 + captured = capsys.readouterr() + assert captured.out == "" + assert "not canonical JSON" in captured.err + assert not output.exists() + + +@pytest.mark.parametrize( + "command", + ("analyze-extension", "combine", "select", "build-p1"), +) +def test_immutable_commands_leave_no_output_on_scientific_failure( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + command: str, +): + arguments, output = _command_sources(tmp_path, command) + _stub_command(monkeypatch, command, "a", fail=True) + + assert CLI.main(arguments) == 1 + captured = capsys.readouterr() + assert captured.out == "" + assert "scientific refusal" in captured.err + assert not output.exists() + + +def test_combined_build_leaves_protocol_absent_when_selection_is_unresolved( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +): + arguments, output = _command_sources(tmp_path, "build-p1") + monkeypatch.setattr( + CLI, + "select_p1_brackets", + lambda *_args, **_kwargs: { + "schema_version": analysis.COMBINED_BRACKET_SCHEMA, + "requires_p0_extension": True, + }, + ) + monkeypatch.setattr( + CLI, + "build_p1_protocol", + lambda *_args, **_kwargs: (_ for _ in ()).throw( + RuntimeError("P0 extension required before P1 publication: 1.0") + ), + ) + + assert CLI.main(arguments) == 1 + assert capsys.readouterr().out == "" + assert not output.exists() + + +@pytest.mark.parametrize("command", ("select", "build-p1")) +@pytest.mark.parametrize("sources", ((), ("p0",), ("extension",))) +def test_combined_commands_fail_closed_without_both_explicit_sources( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], + command: str, + sources: tuple[str, ...], +): + _full_arguments, output = _command_sources(tmp_path, command) + arguments = [ + command, + "--analysis", + str(tmp_path / "combined.json"), + "--output", + str(output), + ] + if "p0" in sources: + arguments.extend(["--p0-analysis", str(tmp_path / "p0.json")]) + if "extension" in sources: + arguments.extend(["--extension-analysis", str(tmp_path / "extension.json")]) + + assert CLI.main(arguments) == 1 + captured = capsys.readouterr() + assert captured.out == "" + assert ( + "requires explicit --p0-analysis, --extension-analysis, " + "--p0-evidence-root, --extension-run-spec, and --extension-protocol" + in captured.err + ) + assert not output.exists() + + +def test_v1_build_compatibility_is_allowed_only_without_source_arguments( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +): + source = _analysis_document(complete=True) + analysis_path = tmp_path / "p0.json" + analysis_path.write_bytes(_canonical_bytes(source)) + output = tmp_path / "p1.json" + brackets = {"schema_version": analysis.BRACKET_SCHEMA} + protocol = { + "schema_version": analysis.P1_PROTOCOL_SCHEMA, + "protocol_sha256": "a" * 64, + } + calls: list[tuple[object, object]] = [] + monkeypatch.setattr( + CLI, + "select_p1_brackets", + lambda _source: calls.append((None, None)) or brackets, + ) + monkeypatch.setattr(CLI, "build_p1_protocol", lambda *_args, **_kwargs: protocol) + base = [ + "build-p1", + "--analysis", + str(analysis_path), + "--output", + str(output), + ] + + assert CLI.main(base) == 0 + assert calls == [(None, None)] + capsys.readouterr() + + for extra in ( + ["--p0-analysis", str(analysis_path)], + [ + "--p0-analysis", + str(analysis_path), + "--extension-analysis", + str(analysis_path), + ], + ): + other_output = tmp_path / f"rejected-{len(extra)}.json" + arguments = [*base[:-1], str(other_output), *extra] + assert CLI.main(arguments) == 1 + captured = capsys.readouterr() + assert captured.out == "" + assert "v1 build-p1 does not accept combined trusted inputs" in captured.err + assert not other_output.exists() + + +@pytest.mark.parametrize("command", ("select", "build-p1")) +def test_cli_rejects_resigned_combined_provenance_bypass( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], + command: str, +): + from test_pilot_analysis import _combined_selector_document + + p0, extension_analysis, combined = _combined_selector_document() + for field in ( + "source_p0_analysis_document_sha256", + "source_extension_analysis_document_sha256", + "p0_run_spec_sha256", + "p0_progress_sha256", + "extension_run_spec_sha256", + "extension_progress_sha256", + "p0_source_revision", + "extension_source_revision", + "observable_columns", + ): + combined.pop(field) + unsigned = dict(combined) + unsigned.pop("analysis_document_sha256") + combined["analysis_document_sha256"] = hashlib.sha256( + _canonical_bytes(unsigned) + ).hexdigest() + for name, document in ( + ("p0", p0), + ("extension", extension_analysis), + ("combined", combined), + ("protocol", {"schema_version": extension.EXTENSION_PROTOCOL_SCHEMA}), + ): + (tmp_path / f"{name}.json").write_bytes(_canonical_bytes(document)) + p0_root = tmp_path / "p0-root" + p0_root.mkdir() + extension_run_spec = tmp_path / "extension-root/run_spec.json" + extension_run_spec.parent.mkdir() + extension_run_spec.write_text("{}\n", encoding="utf-8") + output = tmp_path / f"{command}.json" + + assert ( + CLI.main( + [ + command, + "--analysis", + str(tmp_path / "combined.json"), + "--p0-analysis", + str(tmp_path / "p0.json"), + "--extension-analysis", + str(tmp_path / "extension.json"), + "--p0-evidence-root", + str(p0_root), + "--extension-run-spec", + str(extension_run_spec), + "--extension-protocol", + str(tmp_path / "protocol.json"), + "--output", + str(output), + ] + ) + == 1 + ) + captured = capsys.readouterr() + assert captured.out == "" + assert "P0 source hashes or revision are not frozen" in captured.err + assert not output.exists() diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_artifacts.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_artifacts.py new file mode 100644 index 000000000..337363f17 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_artifacts.py @@ -0,0 +1,1957 @@ +from __future__ import annotations + +from collections.abc import Sequence +from concurrent.futures import ThreadPoolExecutor +import gc +import hashlib +import json +import os +from pathlib import Path +import shutil +import stat +import weakref + +import h5py +import numpy as np +import pytest + +import long_range_percolation.artifacts as artifacts +from long_range_percolation.artifacts import ( + ArtifactIntegrityError, + load_verified_trajectory, + publish_batch_manifest, + publish_trajectory as _publish_trajectory_api, + reconstruct_progress, +) +from long_range_percolation.counter_rng import ( + RNG_VERSION, + StreamIdentity, + derive_stream_material, +) +from long_range_percolation.trajectory import ( + TrajectoryRequest, + TrajectoryResult, + request_digest, +) + + +HEX = { + "source_revision": "1" * 40, + "uv_lock_sha256": "2" * 64, + "runtime_capability_sha256": "3" * 64, + "analysis_plan_sha256": "not-created-pre-pilot", + "rng_sha256": "4" * 64, +} +KERNEL_BYTES = b"challenge-194-kernel-fixture-v1" +KERNEL_SHA256 = hashlib.sha256(KERNEL_BYTES).hexdigest() + + +@pytest.fixture +def sample() -> tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]]: + request = TrajectoryRequest( + length=8, + sigma=1.25, + sigma_grid_id="sigma-grid-a", + kappas=np.asarray([0.0, 0.125, 0.5], dtype=np.float64), + master_seed=42, + phase="pilot", + replica=7, + kernel_sha256=KERNEL_SHA256, + ) + result = TrajectoryResult( + request_sha256=request_digest(request), + observables=np.arange(30, dtype=np.float64).reshape(3, 10) / 7.0, + terminal_counters=np.arange(16, dtype=np.uint32).reshape(4, 4), + draw_counts=np.arange(12, dtype=np.uint64).reshape(4, 3), + event_count=19, + duplicate_count=3, + hash_diagnostics=np.arange(5, dtype=np.uint64), + ) + provenance: dict[str, object] = { + **HEX, + "clean_tree": True, + "conversion_version": "challenge-194-artifact-conversion-v1", + "rng_version": RNG_VERSION, + } + return request, result, provenance + + +def expected(request: TrajectoryRequest) -> dict[str, str]: + return { + **HEX, + "request_sha256": request_digest(request), + "kernel_sha256": request.kernel_sha256, + "conversion_version": "challenge-194-artifact-conversion-v1", + "rng_version": RNG_VERSION, + } + + +def _write_upstream_metadata( + run_dir: Path, + request: TrajectoryRequest, + provenance: dict[str, object], +) -> None: + run_dir.mkdir(parents=True, exist_ok=True) + kernel_dir = run_dir / "kernel" + kernel_dir.mkdir() + (kernel_dir / "kernel.bin").write_bytes(KERNEL_BYTES) + documents = { + "request.json": { + "kernel_sha256": request.kernel_sha256, + "request_sha256": request_digest(request), + "schema_version": "test-request-v1", + }, + "environment.json": { + "clean_tree": provenance["clean_tree"], + "conversion_version": provenance["conversion_version"], + "rng_version": provenance["rng_version"], + "runtime_capability_sha256": provenance[ + "runtime_capability_sha256" + ], + "schema_version": "test-environment-v1", + "source_revision": provenance["source_revision"], + "uv_lock_sha256": provenance["uv_lock_sha256"], + }, + "seed-manifest.json": { + "rng_sha256": provenance["rng_sha256"], + "schema_version": "test-seed-manifest-v1", + }, + "capability.json": { + "runtime_capability_sha256": provenance[ + "runtime_capability_sha256" + ], + "schema_version": "test-capability-v1", + }, + "manifest.json": { + "analysis_plan_sha256": provenance["analysis_plan_sha256"], + "schema_version": "test-run-manifest-v1", + "source_revision": provenance["source_revision"], + }, + } + for name, document in documents.items(): + (run_dir / name).write_bytes(artifacts._canonical_json_bytes(document)) + + +def publish_trajectory( + run_dir: Path, + request: TrajectoryRequest, + result: TrajectoryResult, + provenance: dict[str, object], +) -> Path: + if not run_dir.is_symlink() and ( + not run_dir.exists() or not any(run_dir.iterdir()) + ): + _write_upstream_metadata(run_dir, request, provenance) + return _publish_trajectory_api(run_dir, request, result, provenance) + + +def test_trajectory_round_trip_preserves_complete_resampling_unit( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, result, provenance = sample + path = publish_trajectory(tmp_path, request, result, provenance) + loaded = load_verified_trajectory(path, expected(request)) + np.testing.assert_array_equal(loaded.observables, result.observables) + np.testing.assert_array_equal(loaded.terminal_counters, result.terminal_counters) + np.testing.assert_array_equal(loaded.draw_counts, result.draw_counts) + np.testing.assert_array_equal(loaded.hash_diagnostics, result.hash_diagnostics) + assert (loaded.event_count, loaded.duplicate_count) == (19, 3) + with pytest.raises(FileExistsError): + publish_trajectory(tmp_path, request, result, provenance) + + +def test_hdf5_schema_is_exact_and_little_endian( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + path = publish_trajectory(tmp_path, *sample) + with h5py.File(path, "r") as stream: + assert set(stream) == {"request", "result", "rng"} + assert set(stream["request"]) == {"kappas"} + assert set(stream["result"]) == { + "draw_counts", + "hash_diagnostics", + "observables", + "terminal_counters", + } + assert set(stream["rng"]) == { + "initial_counters", + "key_material_sha256", + "keys", + } + assert stream["request/kappas"].shape == (3,) + assert stream["result/observables"].shape == (3, 10) + assert stream["result/terminal_counters"].shape == (4, 4) + assert stream["result/draw_counts"].shape == (4, 3) + assert stream["result/hash_diagnostics"].shape == (5,) + assert stream["rng/initial_counters"].shape == (4, 4) + assert stream["rng/keys"].shape == (4, 2) + assert stream["rng/key_material_sha256"].shape == (4,) + assert stream["request/kappas"].dtype.str == "= 2 + and args[1] != "trajectory" + ): + return original(*args, **kwargs) + raise OSError(f"crash at {boundary}") + + monkeypatch.setattr(artifacts, boundary, crash) + with pytest.raises((OSError, ArtifactIntegrityError)): + publish_trajectory(tmp_path, *sample) + final = tmp_path / "trajectories" / f"trajectory-{request_digest(request)}.h5" + assert not final.exists() + assert list((tmp_path / "trajectories").glob("*.partial")) + assert list((tmp_path / "trajectories").glob("*.intent")) + monkeypatch.setattr(artifacts, boundary, original) + with pytest.raises(ArtifactIntegrityError, match="intent"): + reconstruct_progress(tmp_path, expected(request)) + + +@pytest.mark.parametrize("failing_call", (1, 2, 3)) +def test_directory_fsync_crashes_fail_closed_at_every_boundary( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + failing_call: int, +): + request, _, _ = sample + calls = 0 + original = artifacts._fsync_directory + + def crash_at_boundary(path: Path) -> None: + nonlocal calls + calls += 1 + if calls == failing_call: + raise OSError("directory fsync crash") + original(path) + + monkeypatch.setattr(artifacts, "_fsync_directory", crash_at_boundary) + with pytest.raises(OSError, match="directory fsync crash"): + publish_trajectory(tmp_path, *sample) + final = tmp_path / "trajectories" / f"trajectory-{request_digest(request)}.h5" + assert final.exists() is (failing_call >= 2) + assert list((tmp_path / "trajectories").glob("*.intent")) + with pytest.raises(ArtifactIntegrityError, match="intent"): + reconstruct_progress(tmp_path, expected(request)) + + +def test_existing_valid_file_is_not_removed_or_overwritten_on_failure( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + path = publish_trajectory(tmp_path, *sample) + before = path.read_bytes() + monkeypatch.setattr( + artifacts, "_replace", lambda *_: (_ for _ in ()).throw(OSError("forbidden")) + ) + with pytest.raises(FileExistsError): + publish_trajectory(tmp_path, *sample) + assert path.read_bytes() == before + + +def _publish_valid_run( + root: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +) -> tuple[Path, Path, dict[str, str]]: + request, _, _ = sample + trajectory = publish_trajectory(root, *sample) + batch = publish_batch_manifest(root, "batch-0001", [trajectory]) + return trajectory, batch, expected(request) + + +def test_batch_manifest_and_progress_are_canonical_and_reconstructible( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + trajectory, batch, hashes = _publish_valid_run(tmp_path, sample) + batch_document = json.loads(batch.read_text()) + assert batch.read_bytes() == ( + json.dumps( + batch_document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode() + + b"\n" + ) + assert batch_document["members"] == [ + { + "path": f"trajectories/{trajectory.name}", + "trajectory_id": sample[1].request_sha256, + "trajectory_sha256": hashlib.sha256(trajectory.read_bytes()).hexdigest(), + } + ] + first = reconstruct_progress(tmp_path, hashes) + progress_path = tmp_path / "progress.json" + original = progress_path.read_bytes() + progress_path.unlink() + second = reconstruct_progress(tmp_path, hashes) + assert second == first + assert progress_path.read_bytes() == original + assert first["trajectory_count"] == 1 + assert first["batch_count"] == 1 + + +@pytest.mark.parametrize( + "mutation", + ( + "truncate", + "dataset", + "request", + "source", + "dirty", + "lock", + "kernel", + "analysis", + "rng", + ), +) +def test_corrupt_or_stale_trajectory_is_rejected( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + mutation: str, +): + request, _, _ = sample + path = publish_trajectory(tmp_path, *sample) + hashes = expected(request) + if mutation == "truncate": + path.write_bytes(path.read_bytes()[:128]) + elif mutation == "dataset": + with h5py.File(path, "r+") as stream: + stream["result/observables"][0, 0] += 1.0 + else: + attribute = { + "request": "request_sha256", + "source": "source_revision", + "dirty": "clean_tree", + "lock": "uv_lock_sha256", + "kernel": "kernel_sha256", + "analysis": "analysis_plan_sha256", + "rng": "rng_sha256", + }[mutation] + with h5py.File(path, "r+") as stream: + stream.attrs[attribute] = np.uint8(0) if mutation == "dirty" else ( + "0" * (40 if mutation == "source" else 64) + ) + with pytest.raises(ArtifactIntegrityError): + load_verified_trajectory(path, hashes) + + +@pytest.mark.parametrize("kind", ("unknown", "partial", "intent", "symlink")) +def test_reconstruction_rejects_unknown_stale_or_symlink_entries( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + kind: str, +): + _, _, hashes = _publish_valid_run(tmp_path, sample) + trajectories = tmp_path / "trajectories" + if kind == "unknown": + (trajectories / "unknown.bin").write_bytes(b"x") + elif kind == "partial": + (trajectories / ".trajectory-x.1.a.partial").write_bytes(b"x") + elif kind == "intent": + (trajectories / ".trajectory-x.1.a.intent").write_bytes(b"{}\n") + else: + (trajectories / f"trajectory-{'0' * 64}.h5").symlink_to( + next(trajectories.glob("*.h5")) + ) + with pytest.raises(ArtifactIntegrityError): + reconstruct_progress(tmp_path, hashes) + + +def test_reconstruction_rejects_duplicate_id_missing_member_and_unbatched_member( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + trajectory, batch, hashes = _publish_valid_run(tmp_path, sample) + duplicate = trajectory.with_name(f"trajectory-{'f' * 64}.h5") + duplicate.write_bytes(trajectory.read_bytes()) + duplicate_sidecar = json.loads( + trajectory.with_suffix(".sha256.json").read_text() + ) + duplicate_sidecar["trajectory_id"] = "f" * 64 + duplicate.with_suffix(".sha256.json").write_bytes( + artifacts._canonical_json_bytes(duplicate_sidecar) + ) + with pytest.raises(ArtifactIntegrityError, match="duplicate"): + reconstruct_progress(tmp_path, hashes) + duplicate.unlink() + duplicate.with_suffix(".sha256.json").unlink() + + document = json.loads(batch.read_text()) + document["members"][0]["path"] = "trajectories/trajectory-" + "e" * 64 + ".h5" + batch.write_bytes(artifacts._canonical_json_bytes(document)) + with pytest.raises(ArtifactIntegrityError, match="missing|stale"): + reconstruct_progress(tmp_path, hashes) + + batch.unlink() + with pytest.raises(ArtifactIntegrityError, match="manifest"): + reconstruct_progress(tmp_path, hashes) + + +def test_batch_publication_rejects_missing_duplicate_foreign_and_unsafe_ids( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + path = publish_trajectory(tmp_path, *sample) + with pytest.raises(ArtifactIntegrityError): + publish_batch_manifest(tmp_path, "batch-1", [path, path]) + with pytest.raises(ArtifactIntegrityError): + publish_batch_manifest(tmp_path, "batch-1", [tmp_path / "missing.h5"]) + with pytest.raises(ValueError): + publish_batch_manifest(tmp_path, "../escape", [path]) + foreign = tmp_path.parent / path.name + foreign.write_bytes(path.read_bytes()) + with pytest.raises(ArtifactIntegrityError): + publish_batch_manifest(tmp_path, "batch-1", [foreign]) + + +def test_concurrent_trajectory_publication_never_clobbers( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + _write_upstream_metadata(tmp_path, sample[0], sample[2]) + + def publish() -> Path: + return _publish_trajectory_api(tmp_path, *sample) + + with ThreadPoolExecutor(max_workers=2) as pool: + futures = [pool.submit(publish) for _ in range(2)] + outcomes = [future.exception() for future in futures] + assert sum(error is None for error in outcomes) == 1 + assert sum(isinstance(error, FileExistsError) for error in outcomes) == 1 + path = next((tmp_path / "trajectories").glob("*.h5")) + load_verified_trajectory(path, expected(sample[0])) + + +def test_concurrent_batch_publication_never_clobbers( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + path = publish_trajectory(tmp_path, *sample) + + def publish() -> Path: + return publish_batch_manifest(tmp_path, "batch-1", [path]) + + with ThreadPoolExecutor(max_workers=2) as pool: + futures = [pool.submit(publish) for _ in range(2)] + outcomes = [future.exception() for future in futures] + assert sum(error is None for error in outcomes) == 1 + assert sum(isinstance(error, FileExistsError) for error in outcomes) == 1 + + +def test_run_and_managed_directories_must_not_be_symlinks( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + real = tmp_path / "real" + real.mkdir() + alias = tmp_path / "alias" + alias.symlink_to(real, target_is_directory=True) + with pytest.raises(ArtifactIntegrityError, match="symlink"): + publish_trajectory(alias, *sample) + root = tmp_path / "root" + _write_upstream_metadata(root, sample[0], sample[2]) + (root / "trajectories").symlink_to(real, target_is_directory=True) + with pytest.raises(ArtifactIntegrityError, match="symlink"): + publish_trajectory(root, *sample) + + +def _refresh_digest(path: Path) -> None: + payload = path.read_bytes() + path.with_suffix(".sha256.json").write_bytes( + artifacts._canonical_json_bytes( + { + "artifact_size": len(payload), + "schema_version": artifacts.TRAJECTORY_DIGEST_SCHEMA, + "trajectory_id": path.stem.removeprefix("trajectory-"), + "trajectory_sha256": hashlib.sha256(payload).hexdigest(), + } + ) + ) + + +def test_hash_and_hdf5_semantics_use_the_same_open_inode( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + path = publish_trajectory(tmp_path, *sample) + hostile = tmp_path / "hostile.h5" + shutil.copyfile(path, hostile) + with h5py.File(hostile, "r+") as stream: + stream["result/observables"][0, 0] = 999.0 + original_hash = artifacts._hash_descriptor + swapped = False + + def hash_then_swap(descriptor: int, description: str) -> tuple[str, int]: + nonlocal swapped + result = original_hash(descriptor, description) + if description == "trajectory" and not swapped: + swapped = True + os.replace(hostile, path) + return result + + monkeypatch.setattr(artifacts, "_hash_descriptor", hash_then_swap) + with pytest.raises(ArtifactIntegrityError, match="identity|mutat|digest"): + load_verified_trajectory(path, expected(request)) + assert swapped + + +@pytest.mark.parametrize("kind", ("sidecar", "manifest")) +@pytest.mark.parametrize("stage", ("after-read", "after-parse", "after-validation")) +@pytest.mark.parametrize("replacement_kind", ("identical", "different")) +def test_json_semantics_bind_each_boundary_to_path_identity( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + kind: str, + stage: str, + replacement_kind: str, +): + trajectory, batch, hashes = _publish_valid_run(tmp_path, sample) + target = trajectory.with_suffix(".sha256.json") if kind == "sidecar" else batch + replacement = tmp_path.parent / f"{tmp_path.name}-replacement-{kind}.json" + replacement.write_bytes( + target.read_bytes() + if replacement_kind == "identical" + else artifacts._canonical_json_bytes({"different_inode": True}) + ) + original_inode = target.stat().st_ino + original_read = artifacts._read_descriptor_bounded + original_loads = artifacts.json.loads + original_canonical = artifacts._canonical_json_bytes + original_stable = artifacts._require_stable_descriptor + expected_description = ( + "trajectory digest sidecar" if kind == "sidecar" else "batch manifest" + ) + armed = False + swapped = False + + def swap() -> None: + nonlocal swapped + assert not swapped + os.replace(replacement, target) + swapped = True + + def read_then_swap( + descriptor: int, + maximum_size: int, + description: str, + ) -> bytes: + nonlocal armed + payload = original_read(descriptor, maximum_size, description) + if description == expected_description: + armed = True + if stage == "after-read": + swap() + return payload + + def loads_then_swap(payload: bytes): + document = original_loads(payload) + if armed and stage == "after-parse" and not swapped: + swap() + return document + + def validate_then_swap(document: object) -> bytes: + payload = original_canonical(document) + if armed and stage == "after-validation" and not swapped: + swap() + return payload + + def ignore_descriptor_metadata_swap( + descriptor: int, + original: os.stat_result, + description: str, + ) -> os.stat_result: + if description == expected_description: + return os.fstat(descriptor) + return original_stable(descriptor, original, description) + + monkeypatch.setattr(artifacts, "_read_descriptor_bounded", read_then_swap) + monkeypatch.setattr(artifacts.json, "loads", loads_then_swap) + monkeypatch.setattr(artifacts, "_canonical_json_bytes", validate_then_swap) + monkeypatch.setattr( + artifacts, "_require_stable_descriptor", ignore_descriptor_metadata_swap + ) + with pytest.raises(ArtifactIntegrityError, match="identity"): + if kind == "sidecar": + load_verified_trajectory(trajectory, hashes) + else: + reconstruct_progress(tmp_path, hashes) + assert swapped + assert not replacement.exists() + assert target.stat().st_ino != original_inode + + +@pytest.mark.parametrize("indirection", ("external-link", "vds", "external-storage")) +def test_hdf5_storage_indirection_is_rejected_before_external_data_can_govern( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + indirection: str, +): + request, result, _ = sample + path = publish_trajectory(tmp_path, *sample) + external_h5 = tmp_path / "external.h5" + external_raw = tmp_path / "external.raw" + if indirection in {"external-link", "vds"}: + with h5py.File(external_h5, "w") as stream: + stream.create_dataset("observables", data=result.observables) + with h5py.File(path, "r+") as stream: + del stream["result/observables"] + if indirection == "external-link": + stream["result"]["observables"] = h5py.ExternalLink( + str(external_h5), "observables" + ) + elif indirection == "vds": + layout = h5py.VirtualLayout(shape=(3, 10), dtype=" None: + final.write_bytes(hostile) + original(partial, final) + + monkeypatch.setattr(artifacts, "_replace", install_hostile_then_continue) + with pytest.raises((FileExistsError, ArtifactIntegrityError)): + publish_trajectory(tmp_path, *sample) + final = ( + tmp_path + / "trajectories" + / f"trajectory-{request_digest(request)}.h5" + ) + assert final.read_bytes() == hostile + + +def test_installed_trajectory_inode_is_rehashed_before_staged_link_is_removed( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + original = artifacts._install_no_clobber + + def install_then_mutate(source: Path, destination: Path) -> None: + original(source, destination) + if destination.suffix == ".h5": + with destination.open("r+b") as stream: + stream.seek(0) + stream.write(b"hostile!") + + monkeypatch.setattr(artifacts, "_install_no_clobber", install_then_mutate) + with pytest.raises(ArtifactIntegrityError, match="digest|parse|HDF5"): + publish_trajectory(tmp_path, *sample) + assert list((tmp_path / "trajectories").glob("*.intent")) + + +def test_installed_batch_inode_bytes_are_verified_before_partial_removal( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + trajectory = publish_trajectory(tmp_path, *sample) + original = artifacts.os.link + + def link_then_mutate( + source: Path, + destination: Path, + *, + follow_symlinks: bool = True, + ) -> None: + original(source, destination, follow_symlinks=follow_symlinks) + if str(destination).endswith("batch-hostile.json"): + Path(destination).write_bytes(b'{"hostile":true}\n') + + monkeypatch.setattr(artifacts.os, "link", link_then_mutate) + with pytest.raises(ArtifactIntegrityError, match="installed|bytes|canonical"): + publish_batch_manifest(tmp_path, "hostile", [trajectory]) + assert (tmp_path / "batches" / "batch-hostile.json").read_bytes() == ( + b'{"hostile":true}\n' + ) + + +def test_publish_json_rejects_oversized_document_before_any_filesystem_output( + tmp_path: Path, +): + final = tmp_path / "batch-too-large.json" + document = { + "batch_id": "too-large", + "members": [], + "padding": "x" * artifacts.MAX_JSON_BYTES, + "schema_version": artifacts.BATCH_SCHEMA, + } + with pytest.raises(ArtifactIntegrityError, match="size|limit|large"): + artifacts._publish_json_once( + final, document, artifacts.BATCH_SCHEMA + ) + assert not final.exists() + assert list(tmp_path.iterdir()) == [] + + +def test_batch_member_limit_rejects_before_iteration_or_final_creation( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + trajectory = publish_trajectory(tmp_path, *sample) + + class OverLimitSequence(Sequence[Path]): + def __len__(self) -> int: + return 4097 + + def __getitem__(self, index: int) -> Path: + raise AssertionError("over-limit sequence was iterated") + + final = tmp_path / "batches" / "batch-too-many.json" + with pytest.raises(ArtifactIntegrityError, match="member|limit"): + publish_batch_manifest( + tmp_path, + "too-many", + OverLimitSequence(), # type: ignore[arg-type] + ) + assert not final.exists() + assert trajectory.exists() + + +@pytest.mark.parametrize( + "parser_error", + ( + RecursionError("nested"), + OverflowError("overflow"), + ValueError("malformed"), + ), +) +def test_json_parser_failures_are_normalized_without_publishing( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + parser_error: Exception, +): + request, _, _ = sample + path = publish_trajectory(tmp_path, *sample) + + def fail_parser(payload: bytes): + raise parser_error + + monkeypatch.setattr(artifacts.json, "loads", fail_parser) + with pytest.raises(ArtifactIntegrityError, match="JSON|parse|read"): + load_verified_trajectory(path, expected(request)) + + +def test_json_memory_error_is_not_swallowed( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + path = publish_trajectory(tmp_path, *sample) + + def fail_parser(payload: bytes): + raise MemoryError("allocation refused") + + monkeypatch.setattr(artifacts.json, "loads", fail_parser) + with pytest.raises(MemoryError, match="allocation refused"): + load_verified_trajectory(path, expected(request)) + + +def test_post_first_hdf5_verification_mutation_keeps_intent( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + final = ( + tmp_path + / "trajectories" + / f"trajectory-{request_digest(request)}.h5" + ) + original = artifacts._load_hdf5_verified + final_verifications = 0 + + def verify_then_mutate(path: Path, *args, **kwargs): + nonlocal final_verifications + if path == final: + final_verifications += 1 + result = original(path, *args, **kwargs) + if path == final and final_verifications == 1: + with path.open("r+b") as stream: + stream.seek(0) + stream.write(b"after-first-verify") + return result + + monkeypatch.setattr(artifacts, "_load_hdf5_verified", verify_then_mutate) + with pytest.raises(ArtifactIntegrityError, match="digest|HDF5|parse"): + publish_trajectory(tmp_path, *sample) + assert final_verifications >= 2 + assert final.read_bytes().startswith(b"after-first-verify") + assert list((tmp_path / "trajectories").glob("*.intent")) + + +def test_hdf5_mutation_during_sidecar_verification_keeps_intent( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + final = ( + tmp_path + / "trajectories" + / f"trajectory-{request_digest(request)}.h5" + ) + original = artifacts._verify_installed_bytes + mutated = False + sidecar_verifications = 0 + + def mutate_during_sidecar(path: Path, payload: bytes, description: str) -> None: + nonlocal mutated, sidecar_verifications + if description == "trajectory digest sidecar": + sidecar_verifications += 1 + if sidecar_verifications == 2: + with final.open("r+b") as stream: + stream.seek(0) + stream.write(b"during-sidecar") + mutated = True + original(path, payload, description) + + monkeypatch.setattr( + artifacts, "_verify_installed_bytes", mutate_during_sidecar + ) + with pytest.raises(ArtifactIntegrityError, match="digest|HDF5|parse"): + publish_trajectory(tmp_path, *sample) + assert mutated + assert sidecar_verifications == 2 + assert final.read_bytes().startswith(b"during-sidecar") + assert list((tmp_path / "trajectories").glob("*.intent")) + + +def test_sidecar_mutation_immediately_before_intent_removal_keeps_intent( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + sidecar = ( + tmp_path + / "trajectories" + / f"trajectory-{request_digest(request)}.sha256.json" + ) + original = artifacts._fsync_directory + calls = 0 + hostile = artifacts._canonical_json_bytes({"hostile": True}) + + def mutate_after_staged_unlink_fsync(path: Path) -> None: + nonlocal calls + original(path) + calls += 1 + if calls == 2: + sidecar.write_bytes(hostile) + + monkeypatch.setattr( + artifacts, "_fsync_directory", mutate_after_staged_unlink_fsync + ) + with pytest.raises(ArtifactIntegrityError, match="sidecar|bytes|digest"): + publish_trajectory(tmp_path, *sample) + assert sidecar.read_bytes() == hostile + assert list((tmp_path / "trajectories").glob("*.intent")) + + +def test_intent_cleanup_failure_performs_recovery_directory_fsync( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + calls: list[int] = [] + original = artifacts._fsync_directory + + def fail_cleanup_once(path: Path) -> None: + calls.append(len(calls) + 1) + if len(calls) == 3: + raise OSError("cleanup fsync") + original(path) + + monkeypatch.setattr(artifacts, "_fsync_directory", fail_cleanup_once) + with pytest.raises(OSError, match="cleanup fsync"): + publish_trajectory(tmp_path, *sample) + assert calls == [1, 2, 3, 4] + assert list((tmp_path / "trajectories").glob("*.intent")) + + +def test_intent_recovery_fsync_failure_raises_distinct_integrity_error( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + calls = 0 + original = artifacts._fsync_directory + + def fail_cleanup_and_recovery(path: Path) -> None: + nonlocal calls + calls += 1 + if calls in (3, 4): + raise OSError(f"fsync-{calls}") + original(path) + + monkeypatch.setattr(artifacts, "_fsync_directory", fail_cleanup_and_recovery) + with pytest.raises(ArtifactIntegrityError, match="recovery.*fsync"): + publish_trajectory(tmp_path, *sample) + assert calls == 4 + assert list((tmp_path / "trajectories").glob("*.intent")) + + +@pytest.mark.parametrize( + ("attribute", "stale"), + ( + ("rng_version", "philox-stale"), + ("conversion_version", "conversion-stale"), + ), +) +def test_caller_cannot_bless_stale_frozen_versions( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + attribute: str, + stale: str, +): + request, _, _ = sample + path = publish_trajectory(tmp_path, *sample) + with h5py.File(path, "r+") as stream: + stream.attrs[attribute] = stale + _refresh_digest(path) + stale_expected = expected(request) + stale_expected[attribute] = stale + with pytest.raises(ArtifactIntegrityError, match="version|stale"): + load_verified_trajectory(path, stale_expected) + + +def test_publication_preserves_upstream_metadata_and_initializes_only_owned_namespaces( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + upstream = { + path.relative_to(tmp_path): path.read_bytes() + for path in tmp_path.rglob("*") + if path.is_file() + } + _publish_trajectory_api(tmp_path, *sample) + assert {path.name for path in tmp_path.iterdir()} == { + "request.json", + "environment.json", + "kernel", + "seed-manifest.json", + "capability.json", + "trajectories", + "batches", + "manifest.json", + } + for name in ("kernel", "trajectories", "batches"): + assert (tmp_path / name).is_dir() + assert not (tmp_path / name).is_symlink() + assert { + path.relative_to(tmp_path): path.read_bytes() + for path in tmp_path.rglob("*") + if path.is_file() and path.relative_to(tmp_path) in upstream + } == upstream + assert all(b'"status":"reserved"' not in payload for payload in upstream.values()) + + +def test_publication_requires_real_upstream_metadata_before_owned_outputs( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + root = tmp_path / "missing-upstream" + root.mkdir() + with pytest.raises(ArtifactIntegrityError, match="metadata|missing|layout"): + _publish_trajectory_api(root, *sample) + assert set(root.iterdir()) == set() + + +@pytest.mark.parametrize( + "name", + ( + "request.json", + "environment.json", + "seed-manifest.json", + "capability.json", + "manifest.json", + "kernel/kernel.bin", + ), +) +def test_upstream_metadata_hash_is_bound_into_trajectory_and_rechecked( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + name: str, +): + request, _, _ = sample + path = publish_trajectory(tmp_path, *sample) + target = tmp_path / name + target.write_bytes(target.read_bytes() + b"\n") + with pytest.raises(ArtifactIntegrityError, match="metadata|kernel|digest"): + load_verified_trajectory(path, expected(request)) + + +def test_reconstruction_is_verify_only_and_rejects_empty_or_missing_layout( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + empty = tmp_path / "empty" + empty.mkdir() + with pytest.raises(ArtifactIntegrityError, match="layout|missing|empty"): + reconstruct_progress(empty, expected(request)) + assert list(empty.iterdir()) == [] + + root = tmp_path / "missing" + _, _, hashes = _publish_valid_run(root, sample) + (root / "capability.json").unlink() + with pytest.raises(ArtifactIntegrityError, match="layout|missing"): + reconstruct_progress(root, hashes) + assert not (root / "capability.json").exists() + + +@pytest.mark.skipif(not hasattr(os, "mkfifo"), reason="FIFO unavailable") +def test_reconstruction_rejects_wrong_kinds_fifo_and_hardlink_alias( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + root = tmp_path / "fifo" + _, _, hashes = _publish_valid_run(root, sample) + capability = root / "capability.json" + capability.unlink() + os.mkfifo(capability) + assert stat.S_ISFIFO(capability.lstat().st_mode) + with pytest.raises(ArtifactIntegrityError, match="regular|kind|layout"): + reconstruct_progress(root, hashes) + + alias_root = tmp_path / "alias" + _, _, alias_hashes = _publish_valid_run(alias_root, sample) + environment = alias_root / "environment.json" + environment.unlink() + os.link(alias_root / "request.json", environment) + with pytest.raises(ArtifactIntegrityError, match="alias|link|layout"): + reconstruct_progress(alias_root, alias_hashes) + + +def test_oversized_json_sidecar_and_manifest_are_rejected_before_parse( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + path = publish_trajectory(tmp_path / "sidecar", *sample) + path.with_suffix(".sha256.json").write_bytes( + b" " * (artifacts.MAX_JSON_BYTES + 1) + ) + with pytest.raises(ArtifactIntegrityError, match="size|large|limit"): + load_verified_trajectory(path, expected(request)) + + root = tmp_path / "manifest" + _, batch, hashes = _publish_valid_run(root, sample) + batch.write_bytes(b" " * (artifacts.MAX_JSON_BYTES + 1)) + with pytest.raises(ArtifactIntegrityError, match="size|large|limit"): + reconstruct_progress(root, hashes) + + +def test_deeply_nested_bounded_json_fails_closed( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + path = publish_trajectory(tmp_path, *sample) + path.with_suffix(".sha256.json").write_bytes(b"[" * 2000 + b"]" * 2000) + with pytest.raises(ArtifactIntegrityError, match="read|JSON|parse"): + load_verified_trajectory(path, expected(request)) + + +def test_sparse_over_limit_kappa_shape_is_rejected_before_dataset_read( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + path = publish_trajectory(tmp_path, *sample) + with h5py.File(path, "r+") as stream: + del stream["request/kappas"] + stream["request"].create_dataset( + "kappas", + shape=(4097,), + maxshape=(None,), + chunks=(1,), + dtype=" None: + path.write_bytes(artifacts._canonical_json_bytes(document)) + + +def test_request_json_hash_is_authoritative_for_changed_request_same_kernel( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + changed = TrajectoryRequest( + length=request.length, + sigma=request.sigma, + sigma_grid_id=request.sigma_grid_id, + kappas=request.kappas, + master_seed=request.master_seed, + phase=request.phase, + replica=request.replica + 1, + kernel_sha256=request.kernel_sha256, + ) + with pytest.raises(ArtifactIntegrityError, match="request_sha256|request"): + artifacts._verify_upstream_metadata(tmp_path, expected(changed)) + + +@pytest.mark.parametrize( + "variant", + ("missing", "nested-decoy", "ambiguous", "duplicate-key", "wrong-type"), +) +def test_request_json_requires_unambiguous_top_level_request_hash( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + variant: str, +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + path = tmp_path / "request.json" + document = json.loads(path.read_bytes()) + authoritative = document["request_sha256"] + if variant == "missing": + del document["request_sha256"] + elif variant == "nested-decoy": + document["request_sha256"] = "f" * 64 + document["decoy"] = {"request_sha256": authoritative} + elif variant == "ambiguous": + document["decoy"] = {"request_sha256": "f" * 64} + elif variant == "duplicate-key": + path.write_bytes( + ( + "{" + f'"kernel_sha256":"{request.kernel_sha256}",' + f'"request_sha256":"{"f" * 64}",' + f'"request_sha256":"{authoritative}",' + '"schema_version":"test-request-v1"' + "}\n" + ).encode() + ) + else: + document["request_sha256"] = 17 + if variant != "duplicate-key": + _rewrite_json(path, document) + with pytest.raises(ArtifactIntegrityError, match="request_sha256|request|ambiguous"): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + + +@pytest.mark.parametrize( + ("name", "field"), + ( + ("request.json", "kernel_sha256"), + ("environment.json", "clean_tree"), + ("environment.json", "conversion_version"), + ("environment.json", "rng_version"), + ("environment.json", "runtime_capability_sha256"), + ("environment.json", "source_revision"), + ("environment.json", "uv_lock_sha256"), + ("seed-manifest.json", "rng_sha256"), + ("capability.json", "runtime_capability_sha256"), + ("manifest.json", "analysis_plan_sha256"), + ("manifest.json", "source_revision"), + ), +) +def test_nested_decoy_never_satisfies_authoritative_metadata_path( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + name: str, + field: str, +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + path = tmp_path / name + document = json.loads(path.read_bytes()) + authoritative = document[field] + document[field] = False if field == "clean_tree" else "f" * 64 + document["decoy"] = {field: authoritative} + _rewrite_json(path, document) + with pytest.raises(ArtifactIntegrityError, match=field): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + + +@pytest.mark.parametrize("replacement_kind", ("identical-inode", "same-inode-mutation")) +def test_aggregate_metadata_rechecks_first_file_after_later_file( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + replacement_kind: str, +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + first = tmp_path / "capability.json" + replacement = tmp_path.parent / f"{tmp_path.name}-capability-replacement.json" + replacement.write_bytes(first.read_bytes()) + original = artifacts._read_descriptor_bounded + changed = False + + def mutate_first_during_later_read( + descriptor: int, + maximum_size: int, + description: str, + ) -> bytes: + nonlocal changed + payload = original(descriptor, maximum_size, description) + if description == "upstream metadata request.json" and not changed: + if replacement_kind == "identical-inode": + os.replace(replacement, first) + else: + document = json.loads(first.read_bytes()) + document["runtime_capability_sha256"] = "5" * 64 + _rewrite_json(first, document) + changed = True + return payload + + monkeypatch.setattr( + artifacts, "_read_descriptor_bounded", mutate_first_during_later_read + ) + with pytest.raises(ArtifactIntegrityError, match="identity|mutat|snapshot|metadata"): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + assert changed + + +def test_aggregate_metadata_rechecks_first_kernel_member_after_later_member( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + first = tmp_path / "kernel" / "kernel.bin" + (tmp_path / "kernel" / "zz.bin").write_bytes(b"later-kernel-member") + original = artifacts._hash_descriptor + kernel_hash_calls = 0 + changed = False + + def mutate_first_during_later_hash( + descriptor: int, + description: str, + ) -> tuple[str, int]: + nonlocal kernel_hash_calls, changed + result = original(descriptor, description) + if description == "kernel metadata file": + kernel_hash_calls += 1 + if kernel_hash_calls == 2: + with first.open("r+b") as stream: + stream.seek(0) + stream.write(b"mutated!") + changed = True + return result + + monkeypatch.setattr(artifacts, "_hash_descriptor", mutate_first_during_later_hash) + with pytest.raises(ArtifactIntegrityError, match="mutat|snapshot|metadata|digest"): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + assert changed + assert kernel_hash_calls >= 2 + + +@pytest.mark.parametrize("replacement_kind", ("identical-inode", "same-inode-mutation")) +def test_aggregate_final_sweep_catches_first_file_changed_during_second_pass( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + replacement_kind: str, +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + first = tmp_path / "capability.json" + replacement = tmp_path.parent / f"{tmp_path.name}-final-replacement.json" + replacement.write_bytes(first.read_bytes()) + original = artifacts._read_descriptor_bounded + request_reads = 0 + changed = False + + def mutate_after_first_was_second_pass_checked( + descriptor: int, + maximum_size: int, + description: str, + ) -> bytes: + nonlocal request_reads, changed + payload = original(descriptor, maximum_size, description) + if description == "upstream metadata request.json": + request_reads += 1 + if request_reads == 2: + if replacement_kind == "identical-inode": + os.replace(replacement, first) + else: + document = json.loads(first.read_bytes()) + document["runtime_capability_sha256"] = "5" * 64 + _rewrite_json(first, document) + changed = True + return payload + + monkeypatch.setattr( + artifacts, + "_read_descriptor_bounded", + mutate_after_first_was_second_pass_checked, + ) + with pytest.raises(ArtifactIntegrityError, match="identity|mutat|snapshot|metadata"): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + assert changed + assert request_reads >= 2 + + +def test_aggregate_final_sweep_catches_kernel_changed_during_second_pass( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + first = tmp_path / "kernel" / "kernel.bin" + (tmp_path / "kernel" / "zz.bin").write_bytes(b"later-kernel-member") + original = artifacts._hash_descriptor + kernel_hash_calls = 0 + changed = False + + def mutate_after_first_was_second_pass_checked( + descriptor: int, + description: str, + ) -> tuple[str, int]: + nonlocal kernel_hash_calls, changed + result = original(descriptor, description) + if description == "kernel metadata file": + kernel_hash_calls += 1 + if kernel_hash_calls == 4: + with first.open("r+b") as stream: + stream.seek(0) + stream.write(b"mutated!") + changed = True + return result + + monkeypatch.setattr( + artifacts, "_hash_descriptor", mutate_after_first_was_second_pass_checked + ) + with pytest.raises(ArtifactIntegrityError, match="mutat|snapshot|metadata|digest"): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + assert changed + assert kernel_hash_calls >= 4 + + +def test_frozen_json_limit_fits_worst_case_batch_and_progress(): + ids = [f"{index:064x}" for index in range(artifacts.MAX_BATCH_MEMBERS)] + members = [ + { + "path": f"trajectories/trajectory-{trajectory_id}.h5", + "trajectory_id": trajectory_id, + "trajectory_sha256": "f" * 64, + } + for trajectory_id in ids + ] + batch = { + "batch_id": "b" * 128, + "members": members, + "schema_version": artifacts.BATCH_SCHEMA, + } + trajectories = list(members) + batches = [ + { + "batch_id": f"{index:0128x}", + "path": f"batches/batch-{index:0128x}.json", + "trajectory_count": 1, + } + for index in range(artifacts.MAX_BATCH_MEMBERS) + ] + progress = { + "batch_count": len(batches), + "batches": batches, + "schema_version": artifacts.PROGRESS_SCHEMA, + "trajectory_count": len(trajectories), + "trajectories": trajectories, + } + worst_case = max( + len(artifacts._canonical_json_bytes(batch)), + len(artifacts._canonical_json_bytes(progress)), + ) + assert worst_case + artifacts.MAX_JSON_SAFETY_BYTES <= artifacts.MAX_JSON_BYTES + + +def test_reconstruction_rejects_global_trajectory_count_over_frozen_limit( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + trajectories = tmp_path / "trajectories" + trajectories.mkdir() + (tmp_path / "batches").mkdir() + for index in range(artifacts.MAX_BATCH_MEMBERS + 1): + (trajectories / f"trajectory-{index:064x}.h5").touch() + with pytest.raises(ArtifactIntegrityError, match="count|limit"): + reconstruct_progress(tmp_path, expected(request)) + + +def test_json_publication_accepts_exact_limit_and_rejects_limit_plus_one( + tmp_path: Path, +): + schema = "test-json-boundary-v1" + base = {"padding": "", "schema_version": schema} + overhead = len(artifacts._canonical_json_bytes(base)) + accepted_document = { + "padding": "x" * (artifacts.MAX_JSON_BYTES - overhead), + "schema_version": schema, + } + accepted = tmp_path / "accepted.json" + artifacts._publish_json_once(accepted, accepted_document, schema) + assert accepted.stat().st_size == artifacts.MAX_JSON_BYTES + + rejected_document = { + "padding": "x" * (artifacts.MAX_JSON_BYTES - overhead + 1), + "schema_version": schema, + } + rejected = tmp_path / "rejected.json" + with pytest.raises(ArtifactIntegrityError, match="size|limit"): + artifacts._publish_json_once(rejected, rejected_document, schema) + assert not rejected.exists() + + +def test_v2_schema_registry_and_v1_trajectory_rejection( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, _ = sample + assert artifacts.TRAJECTORY_SCHEMA.endswith("-v2") + assert artifacts.TRAJECTORY_DIGEST_SCHEMA.endswith("-v2") + assert artifacts.BATCH_SCHEMA.endswith("-v2") + assert artifacts.PROGRESS_SCHEMA.endswith("-v2") + path = publish_trajectory(tmp_path, *sample) + with h5py.File(path, "r+") as stream: + stream.attrs["schema_version"] = "challenge-194-trajectory-artifact-v1" + _refresh_digest(path) + with pytest.raises(ArtifactIntegrityError, match="schema|stale"): + load_verified_trajectory(path, expected(request)) + + +@pytest.mark.parametrize("target_name", ("request.json", "environment.json")) +def test_final_generation_boundary_catches_early_json_mutated_during_last_hash( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], + target_name: str, +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + target = tmp_path / target_name + original = artifacts._read_descriptor_bounded + seed_reads = 0 + mutated = False + + def mutate_during_last_later_read( + descriptor: int, maximum_size: int, description: str + ) -> bytes: + nonlocal seed_reads, mutated + payload = original(descriptor, maximum_size, description) + if description == "upstream metadata seed-manifest.json": + seed_reads += 1 + if seed_reads == 3: + with target.open("r+b") as stream: + stream.seek(0) + stream.write(b'{"same":') + generation = target.stat() + os.utime( + target, + ns=(generation.st_atime_ns, generation.st_mtime_ns + 1), + ) + mutated = True + return payload + + monkeypatch.setattr( + artifacts, "_read_descriptor_bounded", mutate_during_last_later_read + ) + with pytest.raises(ArtifactIntegrityError, match="generation|mutat|metadata"): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + assert mutated + assert seed_reads == 3 + + +def test_final_generation_boundary_catches_early_kernel_mutated_during_last_hash( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + first = tmp_path / "kernel" / "kernel.bin" + (tmp_path / "kernel" / "zz.bin").write_bytes(b"later") + original = artifacts._hash_descriptor + calls = 0 + mutated = False + + def mutate_during_last_hash( + descriptor: int, description: str + ) -> tuple[str, int]: + nonlocal calls, mutated + result = original(descriptor, description) + if description == "kernel metadata file": + calls += 1 + if calls == 6: + with first.open("r+b") as stream: + stream.seek(0) + stream.write(b"same-size") + generation = first.stat() + os.utime( + first, + ns=(generation.st_atime_ns, generation.st_mtime_ns + 1), + ) + mutated = True + return result + + monkeypatch.setattr(artifacts, "_hash_descriptor", mutate_during_last_hash) + with pytest.raises(ArtifactIntegrityError, match="generation|mutat|metadata"): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + assert mutated + assert calls == 6 + + +def test_bounded_scandir_stops_at_max_plus_one( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +): + consumed = 0 + + class Entry: + def __init__(self, name: str): + self.name = name + self.path = str(tmp_path / name) + + class Scan: + def __enter__(self): + return self + + def __exit__(self, *args): + return False + + def __iter__(self): + nonlocal consumed + for index in range(10_000): + consumed += 1 + yield Entry(str(index)) + + monkeypatch.setattr(artifacts.os, "scandir", lambda path: Scan()) + with pytest.raises(ArtifactIntegrityError, match="count|limit"): + artifacts._bounded_directory_entries(tmp_path, 3, "test directory") + assert consumed == 4 + + +def test_bounded_scandir_real_directory_accepts_max_and_rejects_max_plus_one( + tmp_path: Path, +): + for index in range(3): + (tmp_path / str(index)).touch() + assert len(artifacts._bounded_directory_entries(tmp_path, 3, "test")) == 3 + (tmp_path / "3").touch() + with pytest.raises(ArtifactIntegrityError, match="count|limit"): + artifacts._bounded_directory_entries(tmp_path, 3, "test") + + +def test_kernel_workload_and_fd_preflight_are_frozen( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + assert artifacts.MAX_KERNEL_FILES == 16 + assert artifacts.MAX_KERNEL_FILE_BYTES == 8 * 1024 * 1024 + assert artifacts.MAX_KERNEL_TOTAL_BYTES == 32 * 1024 * 1024 + assert artifacts.MAX_RETAINED_METADATA_DESCRIPTORS == 22 + opened = 0 + original = artifacts._open_regular + + def track_open(*args, **kwargs): + nonlocal opened + opened += 1 + return original(*args, **kwargs) + + monkeypatch.setattr(artifacts, "_open_regular", track_open) + monkeypatch.setattr(artifacts, "_soft_fd_limit", lambda: 10) + with pytest.raises(ArtifactIntegrityError, match="descriptor|RLIMIT|limit"): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + assert opened == 0 + + +def test_kernel_total_byte_limit_rejects_before_hashing( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + kernel = tmp_path / "kernel" + for index in range(4): + with (kernel / f"{index}.bin").open("wb") as stream: + stream.truncate(8 * 1024 * 1024) + hashed = 0 + original = artifacts._hash_descriptor + + def track_hash(descriptor: int, description: str) -> tuple[str, int]: + nonlocal hashed + hashed += 1 + return original(descriptor, description) + + monkeypatch.setattr(artifacts, "_hash_descriptor", track_hash) + with pytest.raises(ArtifactIntegrityError, match="total|byte|limit"): + artifacts._verify_upstream_metadata(tmp_path, expected(request)) + assert hashed == 0 + + +def test_maximum_valid_kernel_file_count_is_accepted( + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + request, _, provenance = sample + _write_upstream_metadata(tmp_path, request, provenance) + kernel = tmp_path / "kernel" + for index in range(artifacts.MAX_KERNEL_FILES - 1): + (kernel / f"{index:02d}.bin").write_bytes(f"kernel-{index}".encode()) + digest = artifacts._verify_upstream_metadata(tmp_path, expected(request)) + assert isinstance(digest, str) + assert len(digest) == 64 + + +def test_reconstruction_uses_one_metadata_snapshot_and_verifies_every_trajectory( + monkeypatch: pytest.MonkeyPatch, +): + paths = [ + Path(f"/run/trajectories/trajectory-{index:064x}.h5") + for index in range(artifacts.MAX_BATCH_MEMBERS) + ] + snapshot = object() + calls = 0 + + class Result: + def __init__(self, request_sha256: str): + self.request_sha256 = request_sha256 + + def verify(path: Path, trajectory_id: str, expected, metadata_snapshot=None): + nonlocal calls + assert metadata_snapshot is snapshot + calls += 1 + return Result(trajectory_id), {}, "f" * 64, 1 + + monkeypatch.setattr(artifacts, "_verify_trajectory", verify) + artifacts._verify_reconstruction_trajectories(paths, {}, snapshot) + assert calls == artifacts.MAX_BATCH_MEMBERS + + +def test_reconstruction_calls_metadata_verifier_once( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + _, _, hashes = _publish_valid_run(tmp_path, sample) + original = artifacts._verify_upstream_metadata + calls = 0 + + def count(*args, **kwargs): + nonlocal calls + calls += 1 + return original(*args, **kwargs) + + monkeypatch.setattr(artifacts, "_verify_upstream_metadata", count) + reconstruct_progress(tmp_path, hashes) + assert calls == 1 + + +def test_reconstruction_final_snapshot_boundary_rejects_metadata_mutation( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + sample: tuple[TrajectoryRequest, TrajectoryResult, dict[str, object]], +): + _, _, hashes = _publish_valid_run(tmp_path, sample) + target = tmp_path / "environment.json" + original = artifacts._verify_trajectory + mutated = False + + def verify_then_mutate(*args, **kwargs): + nonlocal mutated + result = original(*args, **kwargs) + if not mutated: + with target.open("r+b") as stream: + stream.seek(0) + stream.write(b'{"same":') + generation = target.stat() + os.utime( + target, + ns=(generation.st_atime_ns, generation.st_mtime_ns + 1), + ) + mutated = True + return result + + monkeypatch.setattr(artifacts, "_verify_trajectory", verify_then_mutate) + with pytest.raises(ArtifactIntegrityError, match="generation|reconstruction"): + reconstruct_progress(tmp_path, hashes) + assert mutated + + +def test_reconstruction_releases_result_before_verifying_next_trajectory( + monkeypatch: pytest.MonkeyPatch, +): + paths = [ + Path(f"/run/trajectories/trajectory-{index:064x}.h5") + for index in range(3) + ] + references: list[weakref.ReferenceType[object]] = [] + + class Result: + def __init__(self, request_sha256: str): + self.request_sha256 = request_sha256 + + def verify(path: Path, trajectory_id: str, expected, metadata_snapshot=None): + gc.collect() + if references: + assert references[-1]() is None + result = Result(trajectory_id) + references.append(weakref.ref(result)) + return result, {}, "f" * 64, 1 + + monkeypatch.setattr(artifacts, "_verify_trajectory", verify) + records = artifacts._verify_reconstruction_trajectories(paths, {}, object()) + gc.collect() + assert references[-1]() is None + assert len(records) == 3 + + +def test_reconstruction_peak_memory_bound_is_analytical_and_small(): + assert artifacts.MAX_TRAJECTORY_NUMERICAL_BYTES < 2 * 1024 * 1024 + assert artifacts.MAX_PROGRESS_RECORD_BYTES < artifacts.MAX_JSON_BYTES + assert artifacts.MAX_RECONSTRUCTION_PEAK_BYTES == ( + artifacts.MAX_TRAJECTORY_NUMERICAL_BYTES + + artifacts.MAX_PROGRESS_RECORD_BYTES + ) + assert artifacts.MAX_RECONSTRUCTION_PEAK_BYTES < 6 * 1024 * 1024 + + +def test_directly_constructed_metadata_snapshot_is_rejected( + tmp_path: Path, +): + forged = artifacts._VerifiedMetadataSnapshot( + run_dir=tmp_path, + digest="0" * 64, + file_generations=(), + kernel_generation=(0, 0, 0, 0, 0, 0, 0), + kernel_names=(), + _token=object(), + ) + with pytest.raises(ArtifactIntegrityError, match="snapshot|capability"): + artifacts._require_private_metadata_snapshot(forged, tmp_path) + + +def test_fd_preflight_accounts_for_current_open_descriptors( + monkeypatch: pytest.MonkeyPatch, +): + monkeypatch.setattr(artifacts, "_soft_fd_limit", lambda: 100) + monkeypatch.setattr(artifacts, "_current_open_fd_count", lambda: 90) + with pytest.raises(ArtifactIntegrityError, match="descriptor|RLIMIT|limit"): + artifacts._preflight_metadata_descriptors(5) + + +def test_fd_preflight_portable_fallback_uses_reserve( + monkeypatch: pytest.MonkeyPatch, +): + monkeypatch.setattr(artifacts, "_current_open_fd_count", lambda: None) + monkeypatch.setattr(artifacts, "_soft_fd_limit", lambda: 100) + artifacts._preflight_metadata_descriptors(5) + monkeypatch.setattr(artifacts, "_soft_fd_limit", lambda: 30) + with pytest.raises(ArtifactIntegrityError, match="descriptor|RLIMIT|limit"): + artifacts._preflight_metadata_descriptors(5) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_benchmark.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_benchmark.py new file mode 100644 index 000000000..68cf2a643 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_benchmark.py @@ -0,0 +1,597 @@ +from __future__ import annotations + +from dataclasses import replace +import json +import os +from pathlib import Path +import subprocess +import sys + +import pytest + +import long_range_percolation.artifacts # Preload before worker subprocess mocks. +import long_range_percolation.benchmark as benchmark +from long_range_percolation.benchmark import ( + BENCHMARK_KAPPAS, + BENCHMARK_LENGTHS, + BENCHMARK_SIGMAS, + GATE_LENGTH, + RSS_LIMIT_BYTES, + STEADY_RUNS, + WALL_LIMIT_SECONDS, + BenchmarkProtocol, + run_benchmark, +) + + +def _validation_report() -> dict[str, object]: + return { + "schema_version": "challenge-194-validation-v1", + "passed": True, + "checks": [{"passed": True}], + "source": {"clean_tree": True, "source_revision": "a" * 40}, + "runtime_capability": { + "schema_version": "challenge-194-runtime-v1", + "python": "3.12.0", + "implementation": "cpython", + "platform": "test-platform", + "machine": "x86_64", + "numpy": "2.2.6", + "scipy": "1.15.3", + "h5py": "3.14.0", + "numba": "0.66.0", + "llvmlite": "0.48.0", + "cpu_name": "", + "cpu_features": "", + "threading_layer": "", + "numba_disable_jit": False, + "fastmath": False, + "boundscheck": True, + }, + } + + +def _worker_payload( + *, + mode: str, + backend: str = "poisson-numba", + length: int = 8, + sigma: float = 1.0, + run_id: str = "run-id", +) -> dict[str, object]: + return { + "schema_version": "challenge-194-benchmark-worker-v1", + "run_id": run_id, + "mode": mode, + "backend": backend, + "length": length, + "sigma": sigma.hex(), + "kappas": [value.hex() for value in (0.25, 0.5)], + "status": "passed", + "failure": None, + "timings_ns": { + "startup": 1, + "cache_load_warmup": 2, + "compile": 3 if mode == "compile" else 0, + "sampling": 4, + "observable": 5, + "artifact_serialization": 6, + "wall": 15, + "cpu": 12, + }, + "metrics": { + "events": 10, + "unique_edges": 8, + "unions": 7, + "duplicates": 2, + "total_probes": 12, + "maximum_probe": 3, + "rehashes": 2, + "bytes": 4096, + }, + "peak_rss_bytes": 8192, + "selected_cpu": 0, + "affinity": [0], + "warmup": { + "length": 2, + "completed_before_timing": True, + }, + "runtime_capability": dict(_validation_report()["runtime_capability"]), + "process": { + "pid": 123, + "ppid": 1, + "python": sys.executable, + "platform": sys.platform, + }, + } + + +def _reduced_protocol(validation_report: Path) -> BenchmarkProtocol: + return BenchmarkProtocol.reduced( + lengths=(8,), + sigmas=(1.0,), + kappas=(0.25, 0.5), + steady_runs=2, + gate_length=8, + wall_limit_seconds=1.0, + rss_limit_bytes=1024, + backends=("poisson-numba",), + validation_report=validation_report, + ) + + +def test_production_protocol_is_exactly_frozen_and_hex_serialized(): + protocol = BenchmarkProtocol.production_v1() + assert BENCHMARK_LENGTHS == (2**10, 2**14, 2**18) + assert BENCHMARK_SIGMAS == (0.8, 0.9, 1.0, 1.1) + assert BENCHMARK_KAPPAS == tuple( + value for value in (0.25 * 1.25**j for j in range(32)) if value <= 6.0 + ) + assert STEADY_RUNS == 5 + assert WALL_LIMIT_SECONDS == 120.0 + assert RSS_LIMIT_BYTES == 4 * 1024**3 + assert GATE_LENGTH == 2**18 + assert protocol.is_production + document = protocol.to_document() + assert document["sigmas"] == [value.hex() for value in BENCHMARK_SIGMAS] + assert document["kappas"] == [value.hex() for value in BENCHMARK_KAPPAS] + assert document["wall_limit_seconds"] == WALL_LIMIT_SECONDS.hex() + assert document["backends"] == ["quadratic", "geometric", "poisson-numba"] + assert document["quadratic_max_length"] == 256 + + +def test_reduced_protocol_cannot_masquerade_as_production(tmp_path: Path): + protocol = _reduced_protocol(tmp_path / "validation.json") + assert not protocol.is_production + with pytest.raises(ValueError, match="production"): + protocol.require_production() + + +def test_cli_accepts_only_validation_report_and_output(): + parser = benchmark.cli_parser() + parsed = parser.parse_args( + ["--validation-report", "validation.json", "--output", "benchmark.json"] + ) + assert parsed.validation_report == Path("validation.json") + assert parsed.output == Path("benchmark.json") + for forbidden in ( + "--length", + "--sigma", + "--kappa", + "--repeat", + "--wall-limit", + "--rss-limit", + ): + with pytest.raises(SystemExit): + parser.parse_args( + [ + "--validation-report", + "validation.json", + "--output", + "benchmark.json", + forbidden, + "1", + ] + ) + + +@pytest.mark.parametrize( + ("report", "error", "expected"), + [ + ({"passed": True, "infrastructure_passed": True}, None, 0), + ({"passed": False, "infrastructure_passed": True}, None, 2), + (None, RuntimeError("broken worker"), 1), + ], +) +def test_cli_exit_codes_distinguish_gate_and_infrastructure( + monkeypatch: pytest.MonkeyPatch, + report: dict[str, object] | None, + error: Exception | None, + expected: int, +): + def fake_run(protocol, output): + if error is not None: + raise error + return report + + monkeypatch.setattr(benchmark, "run_benchmark", fake_run) + assert ( + benchmark.main( + [ + "--validation-report", + "validation.json", + "--output", + "benchmark.json", + ] + ) + == expected + ) + + +def test_parent_uses_fresh_compile_and_steady_processes_with_separate_caches( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + validation_path = tmp_path / "validation.json" + validation_path.write_text(json.dumps(_validation_report()), encoding="utf-8") + calls: list[tuple[list[str], dict[str, str]]] = [] + pids = iter((101, 102, 103, 104, 105, 106)) + + def fake_run(command, **kwargs): + env = kwargs["env"] + mode = command[command.index("--worker-mode") + 1] + run_id = command[command.index("--run-id") + 1] + payload = _worker_payload(mode=mode, run_id=run_id) + payload["process"]["pid"] = next(pids) + calls.append((command, env)) + cache = Path(env["NUMBA_CACHE_DIR"]) + if mode == "compile": + assert list(cache.iterdir()) == [] + (cache / "compiled.nbc").write_bytes(b"cache") + else: + assert (cache / "compiled.nbc").read_bytes() == b"cache" + assert not os.access(cache / "compiled.nbc", os.W_OK) + return subprocess.CompletedProcess( + command, 0, json.dumps(payload) + "\n", "" + ) + + monkeypatch.setattr(benchmark.subprocess, "run", fake_run) + protocol = replace(_reduced_protocol(validation_path), steady_runs=5) + report = run_benchmark(protocol, tmp_path / "report.json") + assert [command[command.index("--worker-mode") + 1] for command, _ in calls] == [ + "compile", + "steady", + "steady", + "steady", + "steady", + "steady", + ] + assert len({env["NUMBA_CACHE_DIR"] for _, env in calls}) == 6 + for _, env in calls: + for name in benchmark.ONE_THREAD_ENVIRONMENT: + assert env[name] == benchmark.ONE_THREAD_ENVIRONMENT[name] + assert report["runs"][0]["process"]["pid"] == 101 + assert [run["process"]["pid"] for run in report["runs"][1:]] == [ + 102, + 103, + 104, + 105, + 106, + ] + + +def test_nonzero_exit_and_timeout_are_visible_failed_records( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + validation_path = tmp_path / "validation.json" + validation_path.write_text(json.dumps(_validation_report()), encoding="utf-8") + call_index = 0 + + def fake_run(command, **kwargs): + nonlocal call_index + call_index += 1 + if call_index == 1: + run_id = command[command.index("--run-id") + 1] + (Path(kwargs["env"]["NUMBA_CACHE_DIR"]) / "compiled.nbc").write_bytes( + b"cache" + ) + return subprocess.CompletedProcess( + command, + 0, + json.dumps(_worker_payload(mode="compile", run_id=run_id)), + "", + ) + if call_index == 2: + return subprocess.CompletedProcess(command, 7, "partial", "crashed") + raise subprocess.TimeoutExpired( + command, timeout=1.0, output="slow", stderr="hung" + ) + + monkeypatch.setattr(benchmark.subprocess, "run", fake_run) + report = run_benchmark(_reduced_protocol(validation_path), tmp_path / "report.json") + failures = [run for run in report["runs"] if run["status"] == "failed"] + assert [run["failure"]["kind"] for run in failures] == [ + "nonzero-exit", + "timeout", + ] + assert failures[0]["stderr"] == "crashed" + assert failures[1]["stdout"] == "slow" + assert not report["passed"] + + +def test_worker_payload_requires_separated_timings_raw_metrics_and_identity(): + payload = _worker_payload(mode="steady") + benchmark.validate_worker_payload( + payload, + expected_mode="steady", + expected_backend="poisson-numba", + expected_length=8, + expected_sigma=1.0, + expected_kappas=(0.25, 0.5), + expected_run_id="run-id", + ) + for field in ( + "startup", + "cache_load_warmup", + "compile", + "sampling", + "observable", + "artifact_serialization", + "wall", + "cpu", + ): + broken = json.loads(json.dumps(payload)) + del broken["timings_ns"][field] + with pytest.raises(RuntimeError, match="timings"): + benchmark.validate_worker_payload( + broken, + expected_mode="steady", + expected_backend="poisson-numba", + expected_length=8, + expected_sigma=1.0, + expected_kappas=(0.25, 0.5), + expected_run_id="run-id", + ) + for field in ( + "events", + "unique_edges", + "unions", + "duplicates", + "total_probes", + "maximum_probe", + "rehashes", + "bytes", + ): + broken = json.loads(json.dumps(payload)) + del broken["metrics"][field] + with pytest.raises(RuntimeError, match="metrics"): + benchmark.validate_worker_payload( + broken, + expected_mode="steady", + expected_backend="poisson-numba", + expected_length=8, + expected_sigma=1.0, + expected_kappas=(0.25, 0.5), + expected_run_id="run-id", + ) + for key, value in ( + ("run_id", "stale"), + ("mode", "compile"), + ("length", 10), + ("sigma", float(0.9).hex()), + ): + broken = json.loads(json.dumps(payload)) + broken[key] = value + with pytest.raises(RuntimeError, match="mismatch"): + benchmark.validate_worker_payload( + broken, + expected_mode="steady", + expected_backend="poisson-numba", + expected_length=8, + expected_sigma=1.0, + expected_kappas=(0.25, 0.5), + expected_run_id="run-id", + ) + + +def test_gate_uses_maxima_without_dropping_outliers(): + runs = [] + for wall, rss in ((0.1, 100), (0.2, 200), (1.1, 900)): + payload = _worker_payload(mode="steady", length=8) + payload["timings_ns"]["wall"] = int(wall * 1e9) + payload["timings_ns"]["cpu"] = int(wall * 0.5e9) + payload["metrics"]["events"] = int(wall * 100) + payload["peak_rss_bytes"] = rss + runs.append(payload) + aggregate = benchmark.aggregate_steady_runs(runs) + assert aggregate["median_wall_seconds"] == pytest.approx(0.2) + assert aggregate["max_wall_seconds"] == pytest.approx(1.1) + assert aggregate["median_cpu_seconds"] == pytest.approx(0.1) + assert aggregate["max_cpu_seconds"] == pytest.approx(0.55) + assert aggregate["max_peak_rss_bytes"] == 900 + assert aggregate["metric_aggregates"]["events"] == { + "median": 20, + "maximum": 110, + } + gate = benchmark.evaluate_gate( + aggregates=[ + { + "backend": "poisson-numba", + "length": 8, + "sigma": float(1.0).hex(), + **aggregate, + } + ], + sigmas=(1.0,), + gate_length=8, + wall_limit_seconds=1.0, + rss_limit_bytes=1024, + correctness_passed=True, + ) + assert not gate["passed"] + assert gate["cells"][0]["wall_passed"] is False + assert gate["cells"][0]["rss_passed"] is True + + +@pytest.mark.parametrize( + "mutate", + [ + lambda report: report.update(schema_version="hostile"), + lambda report: report.update(passed=False), + lambda report: report["checks"].append({"passed": False}), + lambda report: report.update(source={"clean_tree": False}), + ], +) +def test_validation_report_fails_closed( + tmp_path: Path, mutate +): + report = _validation_report() + mutate(report) + path = tmp_path / "validation.json" + path.write_text(json.dumps(report), encoding="utf-8") + with pytest.raises(RuntimeError, match="validation"): + benchmark.load_correctness_report(path) + + +def test_worker_rejects_affinity_that_is_not_exactly_one_cpu(): + payload = _worker_payload(mode="steady") + payload["affinity"] = [0, 1] + with pytest.raises(RuntimeError, match="affinity"): + benchmark.validate_worker_payload( + payload, + expected_mode="steady", + expected_backend="poisson-numba", + expected_length=8, + expected_sigma=1.0, + expected_kappas=(0.25, 0.5), + expected_run_id="run-id", + ) + + +def test_worker_rejects_incomplete_runtime_provenance(): + payload = _worker_payload(mode="steady") + payload["runtime_capability"] = { + "schema_version": "challenge-194-runtime-v1" + } + with pytest.raises(RuntimeError, match="runtime provenance"): + benchmark.validate_worker_payload( + payload, + expected_mode="steady", + expected_backend="poisson-numba", + expected_length=8, + expected_sigma=1.0, + expected_kappas=(0.25, 0.5), + expected_run_id="run-id", + ) + + +def test_timeout_byte_streams_are_decoded_and_publishable( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + validation_path = tmp_path / "validation.json" + validation_path.write_text(json.dumps(_validation_report()), encoding="utf-8") + call_index = 0 + + def fake_run(command, **kwargs): + nonlocal call_index + call_index += 1 + if call_index == 1: + run_id = command[command.index("--run-id") + 1] + (Path(kwargs["env"]["NUMBA_CACHE_DIR"]) / "compiled.nbc").write_bytes( + b"cache" + ) + return subprocess.CompletedProcess( + command, + 0, + json.dumps(_worker_payload(mode="compile", run_id=run_id)), + "", + ) + raise subprocess.TimeoutExpired( + command, + timeout=1.0, + output=b"partial-\xff", + stderr=b"hung-\xfe", + ) + + monkeypatch.setattr(benchmark.subprocess, "run", fake_run) + protocol = replace(_reduced_protocol(validation_path), steady_runs=1) + output = tmp_path / "report.json" + report = run_benchmark(protocol, output) + assert report["runs"][1]["stdout"] == "partial-\\xff" + assert report["runs"][1]["stderr"] == "hung-\\xfe" + assert report["infrastructure_passed"] is True + assert report["passed"] is False + assert output.read_bytes() == benchmark.canonical_report_bytes(report) + + +def test_report_is_canonical_immutable_and_not_published_on_infrastructure_failure( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + validation_path = tmp_path / "validation.json" + validation_path.write_text(json.dumps(_validation_report()), encoding="utf-8") + output = tmp_path / "nested" / "benchmark.json" + worker_pid = 100 + + def successful(command, **kwargs): + nonlocal worker_pid + worker_pid += 1 + mode = command[command.index("--worker-mode") + 1] + run_id = command[command.index("--run-id") + 1] + if mode == "compile": + (Path(kwargs["env"]["NUMBA_CACHE_DIR"]) / "compiled.nbc").write_bytes( + b"cache" + ) + payload = _worker_payload(mode=mode, run_id=run_id) + payload["process"]["pid"] = worker_pid + return subprocess.CompletedProcess( + command, + 0, + json.dumps(payload) + "\n", + "", + ) + + monkeypatch.setattr(benchmark.subprocess, "run", successful) + report = run_benchmark(_reduced_protocol(validation_path), output) + assert output.read_bytes() == benchmark.canonical_report_bytes(report) + with pytest.raises(FileExistsError, match="immutable"): + run_benchmark(_reduced_protocol(validation_path), output) + + broken_output = tmp_path / "broken.json" + monkeypatch.setattr( + benchmark.subprocess, + "run", + lambda *args, **kwargs: subprocess.CompletedProcess( + ["worker"], 0, "{}\nextra\n", "" + ), + ) + with pytest.raises(RuntimeError, match="one JSON object"): + run_benchmark(_reduced_protocol(validation_path), broken_output) + assert not broken_output.exists() + + +def test_worker_smoke_reports_warmup_affinity_rss_and_timed_work(tmp_path: Path): + cache = tmp_path / "cache" + cache.mkdir() + command = [ + sys.executable, + "-m", + "long_range_percolation.benchmark", + "--worker-mode", + "steady", + "--backend", + "poisson-numba", + "--length", + "8", + "--sigma-hex", + float(1.0).hex(), + "--kappas-hex", + ",".join(value.hex() for value in (0.25, 0.5)), + "--run-id", + "smoke", + ] + env = os.environ.copy() | benchmark.ONE_THREAD_ENVIRONMENT + env["NUMBA_CACHE_DIR"] = str(cache) + completed = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + env=env, + timeout=60, + ) + payload = json.loads(completed.stdout) + assert payload["warmup"] == { + "length": 2, + "completed_before_timing": True, + } + assert payload["selected_cpu"] in payload["affinity"] + assert payload["peak_rss_bytes"] > 0 + assert payload["metrics"]["events"] >= payload["metrics"]["unique_edges"] + assert payload["metrics"]["unique_edges"] >= payload["metrics"]["unions"] + assert payload["metrics"]["duplicates"] == ( + payload["metrics"]["events"] - payload["metrics"]["unique_edges"] + ) + assert payload["timings_ns"]["wall"] >= ( + payload["timings_ns"]["sampling"] + + payload["timings_ns"]["observable"] + + payload["timings_ns"]["artifact_serialization"] + ) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_counter_rng.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_counter_rng.py new file mode 100644 index 000000000..5f255572b --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_counter_rng.py @@ -0,0 +1,541 @@ +from __future__ import annotations + +from dataclasses import dataclass, replace +import hashlib +import json +from pathlib import Path + +import numba +import numpy as np +import pytest + +from long_range_percolation.counter_rng import ( + RNG_VERSION, + STREAM_COUNT, + StreamIdentity, + bounded_u32, + derive_stream_material, + next_u32, + philox4x32_10, + philox4x32_10_reference, + u32_to_open, + uniform_open, +) + + +VECTOR_PATH = Path(__file__).parent / "data" / "random123_philox4x32_10.json" +PHASES = ("validation", "benchmark", "pilot", "confirmatory") + + +@dataclass(frozen=True) +class Accounting: + words: int + blocks: int + rejections: int + + +def bounded_u32_from_words_reference( + bound: int, words: list[int] +) -> tuple[int, Accounting]: + if not 1 <= bound <= 0xFFFFFFFF: + raise ValueError("bound must be in [1, 2**32 - 1]") + threshold = ((1 << 32) - bound) % bound + consumed = 0 + rejections = 0 + for word in words: + consumed += 1 + if word < threshold: + rejections += 1 + continue + return word % bound, Accounting( + words=consumed, + blocks=(consumed + 3) // 4, + rejections=rejections, + ) + raise AssertionError("finite word tape exhausted") + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _bounded_u32_from_words_numba( + bound: np.uint64, words: np.ndarray +) -> tuple[np.uint32, np.ndarray]: + if bound < np.uint64(1) or bound > np.uint64(0xFFFFFFFF): + raise ValueError("bound must be in [1, 2**32 - 1]") + threshold = (np.uint64(1 << 32) - bound) % bound + consumed = np.uint64(0) + rejections = np.uint64(0) + for word in words: + consumed += np.uint64(1) + if np.uint64(word) < threshold: + rejections += np.uint64(1) + continue + state = np.array( + ((consumed + np.uint64(3)) // np.uint64(4), rejections), + dtype=np.uint64, + ) + return np.uint32(np.uint64(word) % bound), state + raise AssertionError("finite word tape exhausted") + + +def bounded_u32_from_words_compiled( + bound: int, words: list[int] +) -> tuple[int, Accounting]: + value, packed = _bounded_u32_from_words_numba( + np.uint64(bound), np.asarray(words, dtype=np.uint32) + ) + consumed = next( + index + for index in range(1, len(words) + 1) + if words[index - 1] >= ((1 << 32) - bound) % bound + ) + return int(value), Accounting( + words=consumed, + blocks=int(packed[0]), + rejections=int(packed[1]), + ) + + +def _reference_draw( + counter: np.ndarray, + key: np.ndarray, + block: np.ndarray, + lane_and_valid: np.ndarray, + accounting: np.ndarray, +) -> int: + lane = int(lane_and_valid[0]) + valid = int(lane_and_valid[1]) + if not valid: + block[:] = philox4x32_10_reference(counter, key) + carry = 1 + for index in range(4): + if not carry: + break + incremented = (int(counter[index]) + carry) & 0xFFFFFFFF + carry = int(incremented == 0) + counter[index] = np.uint32(incremented) + lane = 0 + accounting[1] += np.uint64(1) + word = int(block[lane]) + lane += 1 + lane_and_valid[0] = np.uint8(0 if lane == 4 else lane) + lane_and_valid[1] = np.uint8(0 if lane == 4 else 1) + accounting[0] += np.uint64(1) + return word + + +def _reference_bounded( + bound: int, + counter: np.ndarray, + key: np.ndarray, + block: np.ndarray, + lane_and_valid: np.ndarray, + accounting: np.ndarray, +) -> int: + threshold = ((1 << 32) - bound) % bound + while True: + word = _reference_draw( + counter, key, block, lane_and_valid, accounting + ) + if word < threshold: + accounting[2] += np.uint64(1) + continue + return word % bound + + +def _fresh_state( + counter: tuple[int, int, int, int] = (0, 0, 0, 0), +) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + return ( + np.asarray(counter, dtype=np.uint32), + np.zeros(4, dtype=np.uint32), + np.zeros(2, dtype=np.uint8), + np.zeros(3, dtype=np.uint64), + ) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _compiled_draw_pipeline( + counter: np.ndarray, + key: np.ndarray, + block: np.ndarray, + lane_and_valid: np.ndarray, + accounting: np.ndarray, +) -> tuple[np.uint32, float, np.uint32]: + word = next_u32(counter, key, block, lane_and_valid, accounting) + uniform = uniform_open(counter, key, block, lane_and_valid, accounting) + bounded = bounded_u32( + 7, counter, key, block, lane_and_valid, accounting + ) + return word, uniform, bounded + + +def test_vector_fixture_is_pinned_to_the_published_random123_source(): + fixture = json.loads(VECTOR_PATH.read_text(encoding="utf-8")) + assert fixture["algorithm"] == "Philox4x32-10" + assert fixture["source"] == ( + "https://github.com/DEShawResearch/random123/blob/main/tests/kat_vectors" + ) + assert len(fixture["vectors"]) == 2 + + +def test_reference_and_numba_philox_match_published_vectors(): + vectors = json.loads(VECTOR_PATH.read_text(encoding="utf-8")) + for case in vectors["vectors"]: + counter = np.array( + [int(item, 16) for item in case["counter"]], dtype=np.uint32 + ) + key = np.array( + [int(item, 16) for item in case["key"]], dtype=np.uint32 + ) + expected = np.array( + [int(item, 16) for item in case["output"]], dtype=np.uint32 + ) + np.testing.assert_array_equal( + philox4x32_10_reference(counter, key), expected + ) + actual = np.empty(4, dtype=np.uint32) + philox4x32_10(counter, key, actual) + np.testing.assert_array_equal(actual, expected) + + +def test_reference_and_numba_philox_agree_bitwise_beyond_kat_vectors(): + cases = ( + ((1, 2, 3, 4), (5, 6)), + ((0xFFFFFFFF, 0, 0x80000000, 17), (0xDEADBEEF, 0x12345678)), + ((0x89ABCDEF, 0x76543210, 0x0F0F0F0F, 0xF0F0F0F0), (11, 13)), + ) + for counter_words, key_words in cases: + counter = np.asarray(counter_words, dtype=np.uint32) + key = np.asarray(key_words, dtype=np.uint32) + actual = np.empty(4, dtype=np.uint32) + philox4x32_10(counter, key, actual) + np.testing.assert_array_equal( + actual, philox4x32_10_reference(counter, key) + ) + if not numba.config.DISABLE_JIT: + assert philox4x32_10.nopython_signatures + + +def test_reference_philox_is_ordinary_python_and_independent(monkeypatch): + import long_range_percolation.counter_rng as counter_rng + + assert not isinstance( + philox4x32_10_reference, numba.core.registry.CPUDispatcher + ) + + def forbidden_compiled_call(*args, **kwargs): + raise AssertionError("reference called compiled Philox") + + monkeypatch.setattr( + counter_rng, "philox4x32_10", forbidden_compiled_call + ) + output = counter_rng.philox4x32_10_reference( + np.zeros(4, dtype=np.uint32), np.zeros(2, dtype=np.uint32) + ) + np.testing.assert_array_equal( + output, + np.asarray( + (0x6627E8D5, 0xE169C58D, 0xBC57AC4C, 0x9B00DBD8), + dtype=np.uint32, + ), + ) + + +def test_stream_identity_is_canonical_and_domain_separated(): + base = StreamIdentity(7, "validation", 256, "sigma-1-binary", 3, 0) + materials = [ + derive_stream_material(replace(base, stream_id=stream)) + for stream in range(STREAM_COUNT) + ] + assert len({item.material_sha256 for item in materials}) == STREAM_COUNT + assert ( + len( + { + item.key.tobytes() + item.initial_counter.tobytes() + for item in materials + } + ) + == STREAM_COUNT + ) + repeated = derive_stream_material(base) + np.testing.assert_array_equal(repeated.key, materials[0].key) + np.testing.assert_array_equal( + repeated.initial_counter, materials[0].initial_counter + ) + assert repeated.material_sha256 == materials[0].material_sha256 + changed = derive_stream_material(replace(base, phase="benchmark")) + assert changed.material_sha256 != materials[0].material_sha256 + + +def test_stream_material_matches_exact_canonical_json_and_digest_decoding(): + identity = StreamIdentity(7, "validation", 256, "sigma-1-binary", 3, 0) + canonical = ( + b'{"length":256,"master_seed":7,"phase":"validation","replica":3,' + b'"sigma_grid_id":"sigma-1-binary","stream_id":0}' + ) + digest = hashlib.sha256( + b"challenge-194-philox-stream-v1\0" + canonical + ).digest() + material = derive_stream_material(identity) + np.testing.assert_array_equal( + material.key, + np.frombuffer(digest[0:8], dtype="= threshold), + None, + ) + if accepted_at is not None and accepted_at >= 4: + starting_counter = (low_word, 0, 0, 0) + break + assert starting_counter is not None + + ref_counter, ref_block, ref_lane, ref_accounting = _fresh_state( + starting_counter + ) + actual_counter, actual_block, actual_lane, actual_accounting = _fresh_state( + starting_counter + ) + expected = _reference_bounded( + bound, ref_counter, key, ref_block, ref_lane, ref_accounting + ) + actual = int( + bounded_u32( + bound, + actual_counter, + key, + actual_block, + actual_lane, + actual_accounting, + ) + ) + assert actual == expected + np.testing.assert_array_equal(actual_counter, ref_counter) + np.testing.assert_array_equal(actual_block, ref_block) + np.testing.assert_array_equal(actual_lane, ref_lane) + np.testing.assert_array_equal(actual_accounting, ref_accounting) + assert int(actual_accounting[0]) >= 5 + assert int(actual_accounting[1]) >= 2 + assert int(actual_accounting[2]) == int(actual_accounting[0]) - 1 + + +@pytest.mark.parametrize("bound", [0, -1, 2**32, 1.5]) +def test_bounded_u32_rejects_invalid_bounds(bound: object): + key = np.zeros(2, dtype=np.uint32) + counter, block, lane_and_valid, accounting = _fresh_state() + with pytest.raises((TypeError, ValueError), match="bound"): + bounded_u32( + bound, counter, key, block, lane_and_valid, accounting + ) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_day0_acceptance.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_day0_acceptance.py new file mode 100644 index 000000000..3561f664f --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_day0_acceptance.py @@ -0,0 +1,200 @@ +from collections import Counter +import inspect + +import long_range_percolation.geometric as geometric_module +import long_range_percolation.oracle as oracle_module +import numpy as np +import pytest +from scipy.stats import binomtest + +from long_range_percolation.enumeration import exact_partition_distribution +from long_range_percolation.geometric import sample_geometric +from long_range_percolation.kernel import edge_probabilities, periodic_kernel +from long_range_percolation.model import ModelSpec, distance_classes +from long_range_percolation.oracle import sample_quadratic + +GEOMETRIC_DISTANCE_SAMPLE_COUNT = 20_000 +PARTITION_SAMPLE_COUNT = 40_000 + +FAMILYWISE_ALPHA = 0.001 + +GEOMETRIC_DISTANCE_LENGTHS = (4, 8, 32) +PARTITION_LENGTHS = (4, 6) +GEOMETRIC_DISTANCE_SEED_BASE = 100_000 +ORACLE_PARTITION_SEED_BASE = 200_000 +GEOMETRIC_PARTITION_SEED_BASE = 1_200_000 +PARTITION_SAMPLERS = ( + ("quadratic", sample_quadratic, ORACLE_PARTITION_SEED_BASE), + ("geometric", sample_geometric, GEOMETRIC_PARTITION_SEED_BASE), +) + + +def _geometric_distance_family_denominator() -> int: + return sum( + len(distance_classes(length)) + for length in GEOMETRIC_DISTANCE_LENGTHS + ) + + +def _partition_family_denominator() -> int: + return sum( + len(exact_partition_distribution(ModelSpec(length, 0.9, 0.6))) + * len(PARTITION_SAMPLERS) + for length in PARTITION_LENGTHS + ) + + +GEOMETRIC_DISTANCE_BONFERRONI_DENOMINATOR = ( + _geometric_distance_family_denominator() +) +PARTITION_BONFERRONI_DENOMINATOR = _partition_family_denominator() +GEOMETRIC_DISTANCE_GLOBAL_ALPHA = ( + FAMILYWISE_ALPHA / GEOMETRIC_DISTANCE_BONFERRONI_DENOMINATOR +) +PARTITION_GLOBAL_ALPHA = FAMILYWISE_ALPHA / PARTITION_BONFERRONI_DENOMINATOR + + +def _edge_distance(edge: tuple[int, int], length: int) -> int: + separation = edge[1] - edge[0] + return min(separation, length - separation) + + +def _distance_open_counts(samples: list, length: int) -> Counter[int]: + counts: Counter[int] = Counter() + for sample in samples: + counts.update( + _edge_distance(tuple(edge), length) + for edge in sample.edges.tolist() + ) + return counts + + +def _partition_counts(samples: list) -> Counter[tuple[int, ...]]: + counts: Counter[tuple[int, ...]] = Counter() + for sample in samples: + _, sizes = np.unique(sample.labels, return_counts=True) + counts[tuple(sorted(sizes.tolist(), reverse=True))] += 1 + return counts + + +def _acceptance_message( + *, + family: str, + pvalue: float, + threshold: float, + length: int, + sampler: str, + distance: int | None = None, + partition: tuple[int, ...] | None = None, +) -> str: + fields = [ + f"family={family}", + f"pvalue={pvalue:.6g}", + f"threshold={threshold:.6g}", + f"L={length}", + f"sampler={sampler}", + ] + if distance is not None: + fields.append(f"distance={distance}") + if partition is not None: + fields.append(f"partition={partition}") + return ", ".join(fields) + + +def _geometric_distance_acceptance_cases(length: int) -> list[dict[str, object]]: + spec = ModelSpec(length, 1.0, 0.7) + samples = [ + sample_geometric(spec, np.random.default_rng(GEOMETRIC_DISTANCE_SEED_BASE + index)) + for index in range(GEOMETRIC_DISTANCE_SAMPLE_COUNT) + ] + counts = _distance_open_counts(samples, length) + probabilities = edge_probabilities( + spec, + periodic_kernel(length, spec.sigma), + ) + cases: list[dict[str, object]] = [] + for item in distance_classes(length): + trials = GEOMETRIC_DISTANCE_SAMPLE_COUNT * item.multiplicity + result = binomtest( + counts[item.distance], + trials, + probabilities[item.distance - 1], + ) + cases.append( + { + "distance": item.distance, + "length": length, + "pvalue": float(result.pvalue), + "sampler": "geometric", + } + ) + return cases + + +def _partition_acceptance_cases(length: int) -> list[dict[str, object]]: + spec = ModelSpec(length, 0.9, 0.6) + exact_distribution = exact_partition_distribution(spec) + cases: list[dict[str, object]] = [] + + for sampler_name, sampler, seed_base in PARTITION_SAMPLERS: + samples = [ + sampler(spec, np.random.default_rng(seed_base + index)) + for index in range(PARTITION_SAMPLE_COUNT) + ] + observed = _partition_counts(samples) + for partition, probability in exact_distribution.items(): + result = binomtest(observed[partition], PARTITION_SAMPLE_COUNT, probability) + cases.append( + { + "length": length, + "partition": partition, + "pvalue": float(result.pvalue), + "sampler": sampler_name, + } + ) + return cases + + +@pytest.mark.parametrize("length", GEOMETRIC_DISTANCE_LENGTHS) +def test_geometric_distance_frequencies_match_exact_bernoulli_probabilities(length: int): + for case in _geometric_distance_acceptance_cases(length): + assert case["pvalue"] > GEOMETRIC_DISTANCE_GLOBAL_ALPHA, _acceptance_message( + family="geometric-distance", + pvalue=float(case["pvalue"]), + threshold=GEOMETRIC_DISTANCE_GLOBAL_ALPHA, + length=length, + sampler=str(case["sampler"]), + distance=int(case["distance"]), + ) + + +@pytest.mark.parametrize("length", PARTITION_LENGTHS) +def test_oracle_and_geometric_partition_histograms_match_exact_distribution(length: int): + for case in _partition_acceptance_cases(length): + assert case["pvalue"] > PARTITION_GLOBAL_ALPHA, _acceptance_message( + family="partition", + pvalue=float(case["pvalue"]), + threshold=PARTITION_GLOBAL_ALPHA, + length=length, + sampler=str(case["sampler"]), + partition=tuple(case["partition"]), + ) + + +def test_geometric_and_quadratic_samplers_remain_structurally_independent(): + geometric_module_source = inspect.getsource(geometric_module) + geometric_sampler_source = inspect.getsource(sample_geometric) + oracle_module_source = inspect.getsource(oracle_module) + quadratic_sampler_source = inspect.getsource(sample_quadratic) + + assert "distance_classes" in geometric_module_source + assert "_iter_open_offsets" in geometric_sampler_source + assert "sample_quadratic" not in geometric_module_source + assert "iter_unordered_edges" not in geometric_module_source + assert "for left in range(spec.length)" not in geometric_sampler_source + assert "for right in range(left + 1, spec.length)" not in geometric_sampler_source + + assert "for left in range(spec.length)" in quadratic_sampler_source + assert "for right in range(left + 1, spec.length)" in quadratic_sampler_source + assert "_iter_open_offsets" not in oracle_module_source + assert "sample_geometric" not in oracle_module_source diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_download_pilot.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_download_pilot.py new file mode 100644 index 000000000..30c89aa17 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_download_pilot.py @@ -0,0 +1,477 @@ +from __future__ import annotations + +import json +import os +from pathlib import Path +import subprocess +import time + +import pytest + + +SOLUTION = Path(__file__).resolve().parents[1] +SCRIPT = SOLUTION / "scripts" / "download_pilot.sh" +VERIFIED = '{"cells": 96, "status": "verified", "trajectories": 96}' + + +def _write_executable(path: Path, body: str) -> None: + path.write_text(body, encoding="utf-8") + path.chmod(0o755) + + +def _prepare( + tmp_path: Path, + *, + remote_root: str = "/remote/pilot-p0", + local_root: Path | None = None, + local_argument: str | None = None, + extra_env: dict[str, str] | None = None, +) -> tuple[list[str], dict[str, str], Path, Path]: + calls = tmp_path / "calls" + calls.mkdir(exist_ok=True) + fake_bin = tmp_path / "bin" + fake_bin.mkdir(exist_ok=True) + _write_executable( + fake_bin / "rsync", + "#!/bin/bash\n" + "printf '%s\\n' \"$@\" > \"${CALLS}/rsync.$$\"\n" + "if [[ -n \"${BLOCK_RSYNC:-}\" ]]; then\n" + " : > \"${CALLS}/ready\"\n" + " while [[ ! -e \"${CALLS}/release\" ]]; do sleep 0.01; done\n" + "fi\n" + "if [[ -n \"${MUTATE_FILE:-}\" ]]; then\n" + " printf 'rsync-ran\\n' >> \"${MUTATE_FILE}\"\n" + "fi\n", + ) + python = tmp_path / "python" + _write_executable( + python, + "#!/bin/bash\n" + "python3 - \"$@\" <<'PY'\n" + "import json, os, sys\n" + "from pathlib import Path\n" + "calls = Path(os.environ['CALLS'])\n" + "(calls / f'verify.{os.getpid()}').write_text(json.dumps({\n" + " 'argv': sys.argv[1:],\n" + " 'cwd': os.getcwd(),\n" + " 'pythonpath': os.environ.get('PYTHONPATH'),\n" + "}))\n" + "print(os.environ.get('VERIFY_OUTPUT', " + "'{\"cells\": 96, \"status\": \"verified\", \"trajectories\": 96}'))\n" + "raise SystemExit(int(os.environ.get('VERIFY_EXIT', '0')))\n" + "PY\n", + ) + local = local_root or tmp_path / "pilot-p0" + destination = local_argument or str(local) + command = [ + "bash", + str(SCRIPT), + "cluster", + remote_root, + destination, + str(python), + ] + environment = { + **os.environ, + "PATH": f"{fake_bin}:{os.environ['PATH']}", + "CALLS": str(calls), + **(extra_env or {}), + } + return command, environment, calls, local + + +def _run( + tmp_path: Path, + *, + remote_root: str = "/remote/pilot-p0", + local_root: Path | None = None, + local_argument: str | None = None, + extra_env: dict[str, str] | None = None, +) -> tuple[subprocess.CompletedProcess[str], Path, Path]: + command, environment, calls, local = _prepare( + tmp_path, + remote_root=remote_root, + local_root=local_root, + local_argument=local_argument, + extra_env=extra_env, + ) + result = subprocess.run( + command, + cwd=tmp_path, + env=environment, + capture_output=True, + text=True, + ) + return result, calls, local + + +def _rsync_calls(calls: Path) -> list[Path]: + return sorted(calls.glob("rsync.*")) + + +def _verify_calls(calls: Path) -> list[Path]: + return sorted(calls.glob("verify.*")) + + +def _state(local_root: Path) -> Path: + return Path(f"{local_root}.download-state") + + +@pytest.mark.parametrize( + ("remote_root", "local_root"), + (("relative/remote", None), ("/remote/pilot-p0", Path("relative/local"))), +) +def test_requires_absolute_source_and_destination( + tmp_path: Path, remote_root: str, local_root: Path | None +): + result, calls, _ = _run( + tmp_path, remote_root=remote_root, local_root=local_root + ) + assert result.returncode == 64 + assert not _rsync_calls(calls) + + +def test_requires_existing_parent_before_atomic_claim(tmp_path: Path): + local_root = tmp_path / "missing-parent" / "pilot-p0" + result, calls, _ = _run(tmp_path, local_root=local_root) + assert result.returncode == 73 + assert not local_root.parent.exists() + assert not _rsync_calls(calls) + + +def test_rejects_filesystem_root_destination(tmp_path: Path): + result, calls, _ = _run( + tmp_path, + local_root=Path("/"), + local_argument="/", + ) + assert result.returncode == 64 + assert not _rsync_calls(calls) + + +@pytest.mark.parametrize("spelling", ("trailing", "repeated", "dot")) +def test_normalizes_equivalent_destination_before_sibling_paths( + tmp_path: Path, spelling: str +): + first, calls, local_root = _run(tmp_path) + assert first.returncode == 0, first.stderr + root_before = local_root.stat() + completion = _state(local_root) / "verified" + completion_before = completion.stat() + if spelling == "trailing": + local_argument = f"{local_root}/" + elif spelling == "repeated": + local_argument = f"{local_root.parent}//{local_root.name}" + else: + local_argument = f"{local_root.parent}/./{local_root.name}" + + second, _, _ = _run( + tmp_path, + local_root=local_root, + local_argument=local_argument, + ) + + assert second.returncode == 0, second.stderr + assert len(_rsync_calls(calls)) == 1 + assert len(_verify_calls(calls)) == 2 + assert (local_root.stat().st_dev, local_root.stat().st_ino) == ( + root_before.st_dev, + root_before.st_ino, + ) + assert local_root.stat().st_mtime_ns == root_before.st_mtime_ns + assert completion.stat().st_ino == completion_before.st_ino + assert completion.stat().st_mtime_ns == completion_before.st_mtime_ns + assert not (local_root / ".download-state").exists() + assert not (local_root / ".download-claim").exists() + + +def test_transfers_with_checksum_and_partial_safe_archive_flags_then_verifies( + tmp_path: Path, +): + result, calls, local_root = _run(tmp_path) + assert result.returncode == 0, result.stderr + assert _rsync_calls(calls)[0].read_text(encoding="utf-8").splitlines() == [ + "--archive", + "--checksum", + "--partial", + "--itemize-changes", + "cluster:/remote/pilot-p0/", + f"{local_root}/", + ] + verify = json.loads(_verify_calls(calls)[0].read_text(encoding="utf-8")) + assert verify == { + "argv": [ + "scripts/run_pilot.py", + "verify", + "--run-spec", + f"{local_root}/run_spec.json", + ], + "cwd": str(SOLUTION), + "pythonpath": str(SOLUTION / "src"), + } + assert (_state(local_root) / "verified").read_text(encoding="utf-8") == ( + f"cluster:/remote/pilot-p0\n{VERIFIED}\n" + ) + + +def test_completed_root_rerun_verifies_without_rsync_or_root_mutation( + tmp_path: Path, +): + first, calls, local_root = _run(tmp_path) + assert first.returncode == 0, first.stderr + completion = _state(local_root) / "verified" + completion_before = completion.stat() + sentinel = local_root / "immutable" + sentinel.write_text("original\n", encoding="utf-8") + before = sentinel.stat() + + second, _, _ = _run( + tmp_path, + local_root=local_root, + extra_env={"MUTATE_FILE": str(sentinel)}, + ) + + assert second.returncode == 0, second.stderr + assert len(_rsync_calls(calls)) == 1 + assert len(_verify_calls(calls)) == 2 + assert sentinel.read_text(encoding="utf-8") == "original\n" + assert sentinel.stat().st_mtime_ns == before.st_mtime_ns + assert completion.stat().st_mtime_ns == completion_before.st_mtime_ns + assert completion.stat().st_mode & 0o777 == 0o444 + + +def test_allows_same_incomplete_root_to_resume(tmp_path: Path): + first, calls, local_root = _run(tmp_path, extra_env={"VERIFY_EXIT": "1"}) + assert first.returncode != 0 + assert not (_state(local_root) / "verified").exists() + second, _, _ = _run(tmp_path, local_root=local_root) + assert second.returncode == 0, second.stderr + assert len(_rsync_calls(calls)) == 2 + + +def test_refuses_unmarked_nonempty_destination(tmp_path: Path): + local_root = tmp_path / "pilot-p0" + local_root.mkdir() + (local_root / "unexpected").write_text("keep", encoding="utf-8") + result, calls, _ = _run(tmp_path, local_root=local_root) + assert result.returncode == 73 + assert not _rsync_calls(calls) + assert (local_root / "unexpected").read_text(encoding="utf-8") == "keep" + + +def test_refuses_destination_marked_for_different_remote(tmp_path: Path): + first, calls, local_root = _run(tmp_path) + assert first.returncode == 0, first.stderr + second, _, _ = _run( + tmp_path, + remote_root="/remote/other-pilot", + local_root=local_root, + ) + assert second.returncode == 73 + assert len(_rsync_calls(calls)) == 1 + + +@pytest.mark.parametrize("second_remote", ("/remote/pilot-p0", "/remote/other")) +def test_concurrent_same_or_different_source_fails_closed( + tmp_path: Path, second_remote: str +): + command, environment, calls, local_root = _prepare( + tmp_path, extra_env={"BLOCK_RSYNC": "1"} + ) + first = subprocess.Popen( + command, + cwd=tmp_path, + env=environment, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + deadline = time.monotonic() + 5 + while not (calls / "ready").exists() and time.monotonic() < deadline: + time.sleep(0.01) + assert (calls / "ready").exists() + + second, _, _ = _run( + tmp_path, + remote_root=second_remote, + local_root=local_root, + ) + + assert second.returncode == 75 + assert len(_rsync_calls(calls)) == 1 + assert (_state(local_root) / "source").read_text(encoding="utf-8") == ( + "cluster:/remote/pilot-p0\n" + ) + assert Path(f"{local_root}.download-claim").is_dir() + (calls / "release").touch() + stdout, stderr = first.communicate(timeout=5) + assert first.returncode == 0, (stdout, stderr) + assert not Path(f"{local_root}.download-claim").exists() + + +def test_preserves_unexpected_claim_for_diagnosis(tmp_path: Path): + local_root = tmp_path / "pilot-p0" + claim = Path(f"{local_root}.download-claim") + claim.mkdir() + (claim / "diagnostic").write_text("stale\n", encoding="utf-8") + result, calls, _ = _run(tmp_path, local_root=local_root) + assert result.returncode == 75 + assert (claim / "diagnostic").read_text(encoding="utf-8") == "stale\n" + assert not _rsync_calls(calls) + + +@pytest.mark.parametrize("target_exists", (False, True)) +def test_rejects_state_directory_symlink(tmp_path: Path, target_exists: bool): + local_root = tmp_path / "pilot-p0" + target = tmp_path / "state-target" + if target_exists: + target.mkdir() + _state(local_root).symlink_to(target, target_is_directory=True) + result, calls, _ = _run(tmp_path, local_root=local_root) + assert result.returncode == 73 + assert not _rsync_calls(calls) + + +@pytest.mark.parametrize("name", ("source", "verified")) +@pytest.mark.parametrize("target_exists", (False, True)) +def test_rejects_state_file_symlink( + tmp_path: Path, name: str, target_exists: bool +): + local_root = tmp_path / "pilot-p0" + state = _state(local_root) + state.mkdir() + if name != "source": + (state / "source").write_text( + "cluster:/remote/pilot-p0\n", encoding="utf-8" + ) + (state / "logs").mkdir() + target = tmp_path / f"{name}-target" + if target_exists: + target.write_text("target\n", encoding="utf-8") + (state / name).symlink_to(target) + result, calls, _ = _run(tmp_path, local_root=local_root) + assert result.returncode == 73 + assert not _rsync_calls(calls) + + +@pytest.mark.parametrize("target_exists", (False, True)) +def test_rejects_transfer_log_directory_symlink( + tmp_path: Path, target_exists: bool +): + local_root = tmp_path / "pilot-p0" + state = _state(local_root) + state.mkdir() + (state / "source").write_text( + "cluster:/remote/pilot-p0\n", encoding="utf-8" + ) + target = tmp_path / "logs-target" + if target_exists: + target.mkdir() + (state / "logs").symlink_to(target, target_is_directory=True) + result, calls, _ = _run(tmp_path, local_root=local_root) + assert result.returncode == 73 + assert not _rsync_calls(calls) + + +@pytest.mark.parametrize("target_exists", (False, True)) +def test_rejects_transfer_log_file_symlink(tmp_path: Path, target_exists: bool): + local_root = tmp_path / "pilot-p0" + state = _state(local_root) + logs = state / "logs" + logs.mkdir(parents=True) + (state / "source").write_text( + "cluster:/remote/pilot-p0\n", encoding="utf-8" + ) + target = tmp_path / "log-target" + if target_exists: + target.write_text("target\n", encoding="utf-8") + (logs / "transfer-hostile.log").symlink_to(target) + result, calls, _ = _run(tmp_path, local_root=local_root) + assert result.returncode == 73 + assert not _rsync_calls(calls) + + +def test_bootstraps_verified_existing_legacy_root_without_transfer(tmp_path: Path): + local_root = tmp_path / "pilot-p0" + local_root.mkdir() + (local_root / "run_spec.json").write_text("{}\n", encoding="utf-8") + Path(f"{local_root}.download-source").write_text( + "cluster:/remote/pilot-p0\n", encoding="utf-8" + ) + + result, calls, _ = _run(tmp_path, local_root=local_root) + + assert result.returncode == 0, result.stderr + assert not _rsync_calls(calls) + assert len(_verify_calls(calls)) == 1 + assert (_state(local_root) / "verified").is_file() + + +def test_failed_legacy_verification_retries_verification_without_transfer( + tmp_path: Path, +): + local_root = tmp_path / "pilot-p0" + local_root.mkdir() + run_spec = local_root / "run_spec.json" + run_spec.write_text("{}\n", encoding="utf-8") + legacy_source = Path(f"{local_root}.download-source") + legacy_source.write_text( + "cluster:/remote/pilot-p0\n", encoding="utf-8" + ) + root_before = local_root.stat() + spec_before = run_spec.stat() + + first, calls, _ = _run( + tmp_path, + local_root=local_root, + extra_env={"VERIFY_EXIT": "1"}, + ) + + assert first.returncode == 74 + assert not _rsync_calls(calls) + assert len(_verify_calls(calls)) == 1 + assert not (_state(local_root) / "source").exists() + assert not (_state(local_root) / "verified").exists() + assert _state(local_root).is_dir() + diagnostics = _state(local_root) / "diagnostics" + diagnostic_files = list(diagnostics.glob("legacy-verification-failed-*")) + assert len(diagnostic_files) == 1 + assert diagnostic_files[0].read_text(encoding="utf-8") == ( + "cluster:/remote/pilot-p0\nsemantic verification failed\n" + ) + assert diagnostic_files[0].stat().st_mode & 0o777 == 0o444 + assert diagnostics.parent == Path(f"{local_root}.download-state") + + second, _, _ = _run(tmp_path, local_root=local_root) + + assert second.returncode == 0, second.stderr + assert not _rsync_calls(calls) + assert len(_verify_calls(calls)) == 2 + assert (_state(local_root) / "source").is_file() + assert (_state(local_root) / "verified").is_file() + assert (local_root.stat().st_dev, local_root.stat().st_ino) == ( + root_before.st_dev, + root_before.st_ino, + ) + assert local_root.stat().st_mtime_ns == root_before.st_mtime_ns + assert (run_spec.stat().st_dev, run_spec.stat().st_ino) == ( + spec_before.st_dev, + spec_before.st_ino, + ) + assert run_spec.stat().st_mtime_ns == spec_before.st_mtime_ns + + +def test_keeps_claim_state_and_transfer_logs_outside_downloaded_root( + tmp_path: Path, +): + result, _, local_root = _run(tmp_path) + assert result.returncode == 0, result.stderr + state = _state(local_root) + assert state.is_dir() and not state.is_symlink() + assert (state / "source").is_file() + assert (state / "verified").is_file() + logs = state / "logs" + assert logs.is_dir() and not logs.is_symlink() + assert len(list(logs.glob("transfer-*.log"))) == 1 + assert not Path(f"{local_root}.download-claim").exists() + assert not any(entry.name.startswith(".download") for entry in local_root.iterdir()) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_edge_set.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_edge_set.py new file mode 100644 index 000000000..0511012fd --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_edge_set.py @@ -0,0 +1,412 @@ +from __future__ import annotations + +import copy + +import numba +import numpy as np +import pytest + +from long_range_percolation.edge_set import ( + allocate_edge_set, + build_class_start, + edge_set_insert, + edge_set_insert_kernel, + encode_edge_id, + validate_edge_set_state, +) + + +_MASK64 = (1 << 64) - 1 + + +def _splitmix64_reference(value: int) -> int: + value = (value + 0x9E3779B97F4A7C15) & _MASK64 + value = ((value ^ (value >> 30)) * 0xBF58476D1CE4E5B9) & _MASK64 + value = ((value ^ (value >> 27)) * 0x94D049BB133111EB) & _MASK64 + return (value ^ (value >> 31)) & _MASK64 + + +def _reference_insert_all( + values: np.ndarray, expected_size: int +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + capacity = 2 + while 10 * expected_size > 7 * capacity: + capacity *= 2 + keys = np.zeros(capacity, dtype=np.uint64) + occupied = np.zeros(capacity, dtype=np.uint8) + size = 0 + total_probes = 0 + max_probe = 0 + rehashes = 0 + + for raw_value in values: + value = int(raw_value) + while True: + slot = _splitmix64_reference(value) & (capacity - 1) + probe = 1 + while occupied[slot]: + total_probes += 1 + max_probe = max(max_probe, probe) + if int(keys[slot]) == value: + break + slot = (slot + 1) & (capacity - 1) + probe += 1 + else: + total_probes += 1 + max_probe = max(max_probe, probe) + if 10 * (size + 1) <= 7 * capacity: + keys[slot] = np.uint64(value) + occupied[slot] = np.uint8(1) + size += 1 + break + + old_keys = keys + old_occupied = occupied + capacity *= 2 + keys = np.zeros(capacity, dtype=np.uint64) + occupied = np.zeros(capacity, dtype=np.uint8) + rehashes += 1 + for old_slot in range(old_keys.size): + if not old_occupied[old_slot]: + continue + old_value = int(old_keys[old_slot]) + new_slot = _splitmix64_reference(old_value) & (capacity - 1) + rehash_probe = 1 + while occupied[new_slot]: + total_probes += 1 + max_probe = max(max_probe, rehash_probe) + new_slot = (new_slot + 1) & (capacity - 1) + rehash_probe += 1 + total_probes += 1 + max_probe = max(max_probe, rehash_probe) + keys[new_slot] = old_keys[old_slot] + occupied[new_slot] = np.uint8(1) + continue + break + + diagnostics = np.asarray( + (capacity, size, total_probes, max_probe, rehashes), + dtype=np.uint64, + ) + return keys, occupied, diagnostics + + +def _insert_all( + values: np.ndarray, expected_size: int = 1 +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + keys, occupied, diagnostics = allocate_edge_set(expected_size) + for value in values: + keys, occupied, inserted = edge_set_insert( + keys, occupied, diagnostics, value + ) + assert inserted + return keys, occupied, diagnostics + + +def test_edge_set_accepts_entire_uint64_domain_without_sentinel_collision(): + values = np.array([0, 1, 2**63, 2**64 - 2, 2**64 - 1], np.uint64) + keys, occupied, diagnostics = allocate_edge_set(1) + validate_edge_set_state(keys, occupied, diagnostics) + for value in values: + keys, occupied, inserted = edge_set_insert( + keys, occupied, diagnostics, value + ) + assert inserted + for value in values: + keys, occupied, inserted = edge_set_insert( + keys, occupied, diagnostics, value + ) + assert not inserted + assert int(diagnostics[1]) == len(values) + assert int(occupied.sum()) == len(values) + assert int(diagnostics[0]) & (int(diagnostics[0]) - 1) == 0 + assert diagnostics[1] / diagnostics[0] <= 0.70 + + +def test_growth_and_exact_probe_diagnostics_match_reference(): + collision_bucket = [ + value + for value in range(10_000) + if _splitmix64_reference(value) & 15 == 3 + ][:12] + values = np.asarray( + collision_bucket + collision_bucket[:3], dtype=np.uint64 + ) + actual_keys, actual_occupied, actual_diagnostics = _insert_all( + values[:12], expected_size=1 + ) + for value in values[12:]: + actual_keys, actual_occupied, inserted = edge_set_insert( + actual_keys, actual_occupied, actual_diagnostics, value + ) + assert not inserted + + expected_keys, expected_occupied, expected_diagnostics = ( + _reference_insert_all(values, expected_size=1) + ) + np.testing.assert_array_equal(actual_keys, expected_keys) + np.testing.assert_array_equal(actual_occupied, expected_occupied) + np.testing.assert_array_equal(actual_diagnostics, expected_diagnostics) + assert int(actual_diagnostics[1]) == 12 + assert int(actual_diagnostics[2]) >= int(actual_diagnostics[1]) + assert int(actual_diagnostics[3]) > 1 + assert int(actual_diagnostics[4]) > 0 + + +def test_growth_is_deterministic_and_does_not_consume_rng(): + values = np.arange(10_000, dtype=np.uint64) * np.uint64( + 0x9E3779B97F4A7C15 + ) + np.random.seed(194) + rng_state = copy.deepcopy(np.random.get_state()) + first = _insert_all(values) + after_first = copy.deepcopy(np.random.get_state()) + second = _insert_all(values) + after_second = copy.deepcopy(np.random.get_state()) + + np.testing.assert_array_equal(first[0], second[0]) + np.testing.assert_array_equal(first[1], second[1]) + np.testing.assert_array_equal(first[2], second[2]) + for actual in (after_first, after_second): + assert actual[0] == rng_state[0] + np.testing.assert_array_equal(actual[1], rng_state[1]) + assert actual[2:] == rng_state[2:] + + +@pytest.mark.parametrize("expected_size", (-1, True, 1.0, 2**63)) +def test_allocate_edge_set_rejects_invalid_or_unallocatable_sizes(expected_size): + with pytest.raises(ValueError): + allocate_edge_set(expected_size) + + +def test_insert_rejects_invalid_capacity_and_load_without_mutation(): + malformed_states = ( + ( + np.zeros(3, dtype=np.uint64), + np.zeros(3, dtype=np.uint8), + np.asarray((3, 0, 0, 0, 0), dtype=np.uint64), + ), + ( + np.zeros(4, dtype=np.uint64), + np.zeros(2, dtype=np.uint8), + np.asarray((4, 0, 0, 0, 0), dtype=np.uint64), + ), + ( + np.zeros(4, dtype=np.uint64), + np.ones(4, dtype=np.uint8), + np.asarray((4, 4, 0, 0, 0), dtype=np.uint64), + ), + ( + np.zeros(4, dtype=np.uint64), + np.asarray((0, 2, 0, 0), dtype=np.uint8), + np.asarray((4, 2, 0, 0, 0), dtype=np.uint64), + ), + ) + for keys, occupied, diagnostics in malformed_states: + old_keys = keys.copy() + old_occupied = occupied.copy() + old_diagnostics = diagnostics.copy() + with pytest.raises(ValueError): + edge_set_insert(keys, occupied, diagnostics, np.uint64(7)) + np.testing.assert_array_equal(keys, old_keys) + np.testing.assert_array_equal(occupied, old_occupied) + np.testing.assert_array_equal(diagnostics, old_diagnostics) + + +def test_host_state_validation_rejects_wrong_dtype_dimension_and_layout(): + keys, occupied, diagnostics = allocate_edge_set(2) + invalid_states = ( + (keys.astype(np.int64), occupied, diagnostics), + (keys, occupied.astype(np.int8), diagnostics), + (keys, occupied, diagnostics.astype(np.int64)), + (keys.reshape(2, 2), occupied, diagnostics), + (keys, occupied.reshape(2, 2), diagnostics), + (keys, occupied, diagnostics.reshape(1, 5)), + (np.zeros(8, dtype=np.uint64)[::2], occupied, diagnostics), + (keys, np.zeros(8, dtype=np.uint8)[::2], diagnostics), + (keys, occupied, np.zeros(10, dtype=np.uint64)[::2]), + ) + for invalid in invalid_states: + with pytest.raises(ValueError): + validate_edge_set_state(*invalid) + with pytest.raises(ValueError): + edge_set_insert(*invalid, np.uint64(1)) + + +def test_host_state_validation_rejects_readonly_and_aliased_arrays(): + keys, occupied, diagnostics = allocate_edge_set(2) + for index in range(3): + state = [keys.copy(), occupied.copy(), diagnostics.copy()] + state[index].setflags(write=False) + with pytest.raises(ValueError, match="writable"): + validate_edge_set_state(*state) + + shared = np.zeros(4 * np.dtype(np.uint64).itemsize, dtype=np.uint8) + aliased_keys = shared.view(np.uint64) + aliased_occupied = shared[:4] + aliased_diagnostics = np.asarray((4, 0, 0, 0, 0), dtype=np.uint64) + with pytest.raises(ValueError, match="overlap"): + validate_edge_set_state( + aliased_keys, aliased_occupied, aliased_diagnostics + ) + + +def test_host_state_validation_rejects_inconsistent_diagnostics_and_corruption(): + keys, occupied, diagnostics = allocate_edge_set(2) + invalid_diagnostics = ( + np.asarray((8, 0, 0, 0, 0), dtype=np.uint64), + np.asarray((4, 1, 0, 0, 0), dtype=np.uint64), + np.asarray((4, 0, 0, 1, 0), dtype=np.uint64), + np.asarray((4, 0, 0, 0, 1), dtype=np.uint64), + ) + for invalid in invalid_diagnostics: + with pytest.raises(ValueError): + validate_edge_set_state(keys, occupied, invalid) + + invalid_occupied = occupied.copy() + invalid_occupied[0] = np.uint8(2) + with pytest.raises(ValueError, match="zero or one"): + validate_edge_set_state(keys, invalid_occupied, diagnostics) + + +def test_host_checked_insert_rejects_full_or_corrupt_state_without_hanging(): + keys = np.arange(4, dtype=np.uint64) + occupied = np.ones(4, dtype=np.uint8) + diagnostics = np.asarray((4, 4, 4, 1, 0), dtype=np.uint64) + before = (keys.copy(), occupied.copy(), diagnostics.copy()) + with pytest.raises(ValueError, match="load"): + edge_set_insert(keys, occupied, diagnostics, np.uint64(99)) + for actual, expected in zip( + (keys, occupied, diagnostics), before, strict=True + ): + np.testing.assert_array_equal(actual, expected) + + +def test_each_probe_checked_add_fails_before_wrap_without_partial_mutation(): + capacity = 16 + collision_values = [ + value + for value in range(100_000) + if _splitmix64_reference(value) & (2 * capacity - 1) == 7 + ][:12] + required_before_final_insert = 12 + sum(range(1, 12)) + assert required_before_final_insert > 2 * capacity + 1 + assert len(collision_values) == 12 + keys, occupied, diagnostics = allocate_edge_set(11) + for value in collision_values[:11]: + keys, occupied, inserted = edge_set_insert( + keys, occupied, diagnostics, np.uint64(value) + ) + assert inserted + diagnostics[2] = np.uint64(_MASK64 - (2 * capacity + 1)) + before = (keys.copy(), occupied.copy(), diagnostics.copy()) + + with pytest.raises(OverflowError, match="probe"): + edge_set_insert_kernel( + keys, + occupied, + diagnostics, + np.uint64(collision_values[11]), + ) + + for actual, expected in zip( + (keys, occupied, diagnostics), before, strict=True + ): + np.testing.assert_array_equal(actual, expected) + + +def test_class_starts_and_edge_ids_cover_exact_disjoint_range(): + for length in (2, 8, 256): + multiplicity = np.full(length // 2, length, dtype=np.uint64) + multiplicity[-1] = np.uint64(length // 2) + class_start = build_class_start(multiplicity) + assert class_start.dtype == np.dtype(np.uint64) + assert class_start.flags.c_contiguous + assert class_start.shape == (length // 2 + 1,) + expected = 0 + encoded = [] + for distance_index, count in enumerate(multiplicity): + assert int(class_start[distance_index]) == expected + class_ids = [ + int(encode_edge_id(class_start, distance_index, offset)) + for offset in range(int(count)) + ] + assert class_ids == list(range(expected, expected + int(count))) + encoded.extend(class_ids) + expected += int(count) + assert int(class_start[-1]) == expected + assert encoded == list(range(length * (length - 1) // 2)) + + +def test_edge_id_rejects_oversized_offsets_for_middle_and_final_classes(): + class_start = build_class_start( + np.asarray((4, 4, 2), dtype=np.uint64) + ) + with pytest.raises(ValueError, match="offset"): + encode_edge_id(class_start, 1, 4) + with pytest.raises(ValueError, match="offset"): + encode_edge_id(class_start, 2, 2) + + +def test_class_start_and_edge_id_validation_fail_closed(): + with pytest.raises(ValueError): + build_class_start(np.asarray((1, 0), dtype=np.uint64)) + with pytest.raises(ValueError): + build_class_start( + np.asarray((2**64 - 1, 1), dtype=np.uint64) + ) + class_start = build_class_start(np.asarray((4, 2), dtype=np.uint64)) + for distance_index, offset in ( + (-1, 0), + (2, 0), + (0, -1), + (True, 0), + (0.0, 0), + (0, True), + (0, 1.0), + ): + with pytest.raises(ValueError): + encode_edge_id(class_start, distance_index, offset) + with pytest.raises(ValueError): + encode_edge_id( + np.asarray((0, 4, 3), dtype=np.uint64), 0, 1 + ) + + +def test_canonical_prefix_bounds_make_edge_id_addition_overflow_unreachable(): + class_start = build_class_start( + np.asarray((_MASK64,), dtype=np.uint64) + ) + assert int(encode_edge_id(class_start, 0, _MASK64 - 1)) == _MASK64 - 1 + with pytest.raises(ValueError, match="offset"): + encode_edge_id(class_start, 0, _MASK64) + with pytest.raises(ValueError): + encode_edge_id( + np.asarray((0, _MASK64, 0), dtype=np.uint64), 1, 0 + ) + + +def test_edge_set_insert_kernel_matches_python_and_compiles_nopython(): + if numba.config.DISABLE_JIT: + return + values = np.asarray((0, 7, 2**63, 7, 2**64 - 1), dtype=np.uint64) + compiled = allocate_edge_set(1) + python = allocate_edge_set(1) + compiled_results = [] + python_results = [] + for value in values: + compiled_keys, compiled_occupied, inserted = edge_set_insert_kernel( + compiled[0], compiled[1], compiled[2], value + ) + compiled = (compiled_keys, compiled_occupied, compiled[2]) + compiled_results.append(inserted) + python_keys, python_occupied, inserted = edge_set_insert_kernel.py_func( + python[0], python[1], python[2], value + ) + python = (python_keys, python_occupied, python[2]) + python_results.append(inserted) + + assert compiled_results == python_results + for actual, expected in zip(compiled, python, strict=True): + np.testing.assert_array_equal(actual, expected) + assert edge_set_insert_kernel.nopython_signatures diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_enumeration.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_enumeration.py new file mode 100644 index 000000000..9eae854d9 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_enumeration.py @@ -0,0 +1,182 @@ +import math + +import pytest + +import long_range_percolation as lrp +from long_range_percolation.enumeration import ( + LOG_RATE_EXP_OVERFLOW, + LOG_RATE_EXP_UNDERFLOW, + LOG_RATE_OPEN_SATURATION, + GraphOutcome, + _log_closed_edge_weight, + _log_open_edge_weight, + enumerate_graphs, + exact_partition_distribution, +) +from long_range_percolation.model import ModelSpec, iter_unordered_edges +from long_range_percolation.oracle import ( + expected_open_edges, + no_edge_probability, +) +from long_range_percolation.union_find import UnionFind + + +def _component_sizes_for_mask( + length: int, + edges: list[tuple[int, int]], + mask: int, +) -> tuple[int, ...]: + union_find = UnionFind(length) + for index, (left, right) in enumerate(edges): + if mask & (1 << index): + union_find.union(left, right) + return tuple(union_find.component_sizes().tolist()) + + +def test_all_graph_probabilities_normalize_and_reproduce_analytic_moments(): + for length in (2, 4, 6): + spec = ModelSpec(length, 0.9, 0.6) + outcomes = list(enumerate_graphs(spec)) + assert len(outcomes) == 2 ** (length * (length - 1) // 2) + assert math.fsum(item.probability for item in outcomes) == pytest.approx(1.0) + assert math.fsum( + item.probability * item.open_edges for item in outcomes + ) == pytest.approx(expected_open_edges(spec)) + assert outcomes[0].probability == pytest.approx(no_edge_probability(spec)) + + +def test_two_site_partition_probabilities_are_exact(): + spec = ModelSpec(2, 1.0, 0.3) + distribution = exact_partition_distribution(spec) + closed = no_edge_probability(spec) + assert distribution[(1, 1)] == pytest.approx(closed) + assert distribution[(2,)] == pytest.approx(1.0 - closed) + + +def test_enumeration_rejects_lengths_above_six(): + with pytest.raises(ValueError, match="at most six"): + list(enumerate_graphs(ModelSpec(8, 1.0, 1.0))) + + +def test_zero_coupling_assigns_unit_mass_to_empty_graph(): + spec = ModelSpec(4, 1.0, 0.0) + edges = list(iter_unordered_edges(spec.length)) + outcomes = list(enumerate_graphs(spec)) + assert outcomes[0].probability == 1.0 + assert all(item.probability == 0.0 for item in outcomes[1:]) + for outcome in outcomes: + assert outcome.open_edges == outcome.mask.bit_count() + expected_sizes = _component_sizes_for_mask( + spec.length, + edges, + outcome.mask, + ) + assert outcome.component_sizes == expected_sizes + + +def test_tiny_kappa_enumerates_without_underflow_cliff(): + spec = ModelSpec(6, 1.0, 5e-324) + edge_count = spec.length * (spec.length - 1) // 2 + outcomes = list(enumerate_graphs(spec)) + assert len(outcomes) == 2 ** edge_count + for outcome in outcomes: + assert math.isfinite(outcome.probability) + assert outcome.probability >= 0.0 + total = math.fsum(item.probability for item in outcomes) + assert total == pytest.approx(1.0) + assert outcomes[0].mask == 0 + assert outcomes[0].probability / total == pytest.approx(1.0, rel=1e-12) + + +def test_large_kappa_with_saturated_edge_probabilities(): + spec = ModelSpec(2, 1.0, 100.0) + edge_count = spec.length * (spec.length - 1) // 2 + outcomes = list(enumerate_graphs(spec)) + assert len(outcomes) == 2 ** edge_count + for outcome in outcomes: + assert math.isfinite(outcome.probability) + assert outcome.probability >= 0.0 + assert math.fsum(item.probability for item in outcomes) == pytest.approx(1.0) + fully_open_mask = (1 << edge_count) - 1 + open_outcome = next(item for item in outcomes if item.mask == fully_open_mask) + total_mass = math.fsum(item.probability for item in outcomes) + assert open_outcome.probability / total_mass == pytest.approx(1.0, rel=1e-12) + for item in outcomes: + if item.mask != fully_open_mask: + assert item.probability < 1e-50 + + +def test_huge_kappa_enumerates_without_overflow(): + spec = ModelSpec(2, 1.0, 1e308) + edge_count = spec.length * (spec.length - 1) // 2 + outcomes = list(enumerate_graphs(spec)) + assert len(outcomes) == 2 ** edge_count + for outcome in outcomes: + assert math.isfinite(outcome.probability) + assert outcome.probability >= 0.0 + assert math.fsum(item.probability for item in outcomes) == pytest.approx(1.0) + fully_open_mask = (1 << edge_count) - 1 + open_outcome = next(item for item in outcomes if item.mask == fully_open_mask) + assert open_outcome.probability == pytest.approx(1.0) + assert outcomes[0].probability == 0.0 + assert outcomes[0].mask == 0 + + +@pytest.mark.parametrize( + ("log_rate", "side"), + [ + (math.nextafter(LOG_RATE_EXP_UNDERFLOW, float("-inf")), "below"), + (math.nextafter(LOG_RATE_EXP_UNDERFLOW, float("inf")), "above"), + ], +) +def test_log_open_edge_weight_near_underflow_threshold(log_rate, side): + weight = _log_open_edge_weight(log_rate) + assert math.isfinite(weight) + assert weight <= 0.0 + if side == "below": + assert weight == log_rate + + +@pytest.mark.parametrize( + ("log_rate", "side"), + [ + (math.nextafter(LOG_RATE_OPEN_SATURATION, float("-inf")), "below"), + (math.nextafter(LOG_RATE_OPEN_SATURATION, float("inf")), "above"), + ], +) +def test_log_open_edge_weight_near_saturation_threshold(log_rate, side): + weight = _log_open_edge_weight(log_rate) + assert math.isfinite(weight) + assert weight <= 0.0 + if side == "above": + assert weight == 0.0 + + +@pytest.mark.parametrize( + ("log_rate", "side"), + [ + (math.nextafter(LOG_RATE_EXP_OVERFLOW, float("-inf")), "below"), + (math.nextafter(LOG_RATE_EXP_OVERFLOW, float("inf")), "above"), + ], +) +def test_log_closed_edge_weight_near_overflow_threshold(log_rate, side): + weight = _log_closed_edge_weight(log_rate) + if side == "above": + assert weight == -math.inf + else: + assert math.isfinite(weight) + assert weight <= 0.0 + + +def test_log_closed_edge_weight_underflow_is_negative_zero(): + log_rate = math.nextafter(LOG_RATE_EXP_UNDERFLOW, float("-inf")) + assert _log_closed_edge_weight(log_rate) == -0.0 + + +def test_package_root_exports_enumeration_symbols(): + assert lrp.GraphOutcome is GraphOutcome + assert lrp.enumerate_graphs is enumerate_graphs + assert lrp.exact_partition_distribution is exact_partition_distribution + assert "GraphOutcome" in lrp.__all__ + assert "enumerate_graphs" in lrp.__all__ + assert "exact_partition_distribution" in lrp.__all__ diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_generate_report_figures.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_generate_report_figures.py new file mode 100644 index 000000000..71b6f1dad --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_generate_report_figures.py @@ -0,0 +1,182 @@ +from __future__ import annotations + +import hashlib +import importlib.util +import math +from pathlib import Path + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[6] +RESULTS = REPO_ROOT / "results/challenge-194" +SOLUTION = Path(__file__).resolve().parents[1] +GENERATOR = SOLUTION / "scripts/generate_report_figures.py" + +EXPECTED_FILE_HASHES = { + "approval": "29dc5d04fd18728ee46fffe90c70d98caa61032005974f354e2b4e0e6018a7ab", + "p0_analysis": "44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b", + "extension_protocol": "e363a60f842b11b32972c7a68ec1c5f237741bc45bc79ab8bf93f51f6760d84d", + "extension_analysis": "d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5", + "combined_analysis": "6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929", + "brackets": "7a84d545b4526d94aa6f93ca4f0d264dcf01e518f2f9b04383921634786c9962", +} + + +def source_paths() -> dict[str, Path]: + if not RESULTS.is_dir(): + pytest.skip("gitignored results/challenge-194 evidence root is unavailable") + return { + "approval": SOLUTION / "pilot_correctness_approval.json", + "p0_analysis": RESULTS / "p0_analysis.json", + "extension_protocol": RESULTS / "p0_extension_v1_protocol.json", + "extension_analysis": RESULTS / "p0_extension_v1_analysis.json", + "combined_analysis": RESULTS / "p0_combined_analysis_v2.json", + "brackets": RESULTS / "p0_combined_brackets_v2.json", + } + + +def load_module(): + spec = importlib.util.spec_from_file_location("generate_report_figures", GENERATOR) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def independent_marks(rows, kappas, lengths): + q_marks = [] + crossing_marks = [] + for index in range(len(kappas) - 1): + q_diff = [ + rows[(lengths[0], kappas[endpoint])]["means"]["q_g"] + - rows[(lengths[1], kappas[endpoint])]["means"]["q_g"] + for endpoint in (index, index + 1) + ] + q_marks.append(min(q_diff) <= 0.0 <= max(q_diff)) + crossing_marks.append( + any( + min( + rows[(length, kappas[index])]["means"]["four_sector_crossing"], + rows[(length, kappas[index + 1])]["means"]["four_sector_crossing"], + ) + <= 0.25 + and max( + rows[(length, kappas[index])]["means"]["four_sector_crossing"], + rows[(length, kappas[index + 1])]["means"]["four_sector_crossing"], + ) + >= 0.75 + for length in lengths + ) + ) + return q_marks, crossing_marks + + +def test_authenticates_all_sources_and_embedded_identities(): + module = load_module() + paths = source_paths() + evidence = module.load_evidence(paths) + + assert evidence.file_hashes == EXPECTED_FILE_HASHES + assert evidence.embedded_hashes == module.EXPECTED_EMBEDDED_HASHES + for key, expected in EXPECTED_FILE_HASHES.items(): + assert hashlib.sha256(paths[key].read_bytes()).hexdigest() == expected + + +def test_rejects_changed_authenticated_source(tmp_path): + module = load_module() + paths = source_paths() + changed = tmp_path / "combined.json" + changed.write_bytes(paths["combined_analysis"].read_bytes() + b" ") + paths["combined_analysis"] = changed + + with pytest.raises(ValueError, match="SHA256"): + module.load_evidence(paths) + + +def test_extracts_required_rows_uncertainties_and_selector_marks(): + module = load_module() + evidence = module.load_evidence(source_paths()) + + assert tuple(evidence.panels) == ((0.9).hex(), (1.0).hex()) + for panel in evidence.panels.values(): + assert panel.lengths == (16384, 262144) + assert tuple(sorted(panel.kappas, key=float.fromhex)) == panel.kappas + for kappa in panel.kappas: + assert float.fromhex(kappa).hex() == kappa + for length in panel.lengths: + row = panel.rows[(length, kappa)] + for observable in ("q_g", "four_sector_crossing"): + assert observable in row["means"] + error = row["standard_errors"][observable] + assert math.isfinite(error) and error >= 0.0 + q_marks, crossing_marks = independent_marks( + panel.rows, panel.kappas, panel.lengths + ) + assert panel.q_marks == tuple(q_marks) + assert panel.crossing_marks == tuple(crossing_marks) + assert not any(q and c for q, c in zip(q_marks, crossing_marks)) + assert panel.status == "requires_p0_extension" + assert panel.reason == "no_nonzero_interval_marked_by_both_estimators" + + +def test_svg_contains_accessibility_labels_sources_and_boundaries(): + module = load_module() + evidence = module.load_evidence(source_paths()) + selector = module.render_selector_svg(evidence).decode() + workflow = module.render_workflow_svg( + evidence, + ( + "scripts/analyze_pilot.py", + "src/long_range_percolation/pilot_extension.py", + ), + ).decode() + + for svg in (selector, workflow): + assert "" in svg and "<desc>" in svg + assert "SHA256" in svg + for text in ( + "sigma = 0.9", + "sigma = 1.0", + "Q_G", + "four-sector", + "± 1 standard error", + "共同标记区间:无", + "p0_combined_analysis_v2.json", + "p0_combined_brackets_v2.json", + "exploratory selector evidence", + ): + assert text in selector + for text in ( + "P0", + "extension v1", + "96", + "P1", + "未发布", + "未运行", + "extension-v2", + "不属于本报告证据链", + ): + assert text in workflow + + +def test_rendering_is_byte_deterministic(): + module = load_module() + evidence = module.load_evidence(source_paths()) + dirty = ("scripts/analyze_pilot.py",) + + assert module.render_selector_svg(evidence) == module.render_selector_svg(evidence) + assert module.render_workflow_svg(evidence, dirty) == module.render_workflow_svg( + evidence, dirty + ) + + +def test_write_outputs_is_no_clobber(tmp_path): + module = load_module() + outputs = {"a.svg": b"same"} + module.write_outputs(tmp_path, outputs) + module.write_outputs(tmp_path, outputs) + assert (tmp_path / "a.svg").read_bytes() == b"same" + + with pytest.raises(FileExistsError, match="refusing to replace"): + module.write_outputs(tmp_path, {"a.svg": b"different"}) + assert (tmp_path / "a.svg").read_bytes() == b"same" diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_geometric.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_geometric.py new file mode 100644 index 000000000..f47983e28 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_geometric.py @@ -0,0 +1,165 @@ +import math +import inspect + +import numpy as np +import pytest +from scipy.stats import binomtest + +import long_range_percolation as lrp +import long_range_percolation.geometric as geometric_module +from long_range_percolation.geometric import _iter_open_offsets, sample_geometric +from long_range_percolation.model import ModelSpec, canonical_edge, distance_classes + + +class _FakeRandomStream: + def __init__(self, values: list[float]): + self._values = iter(values) + + def random(self) -> float: + return next(self._values) + + +def _distance(edge: tuple[int, int], length: int) -> int: + left, right = edge + return min((right - left) % length, (left - right) % length) + + +def _all_canonical_edges(length: int) -> list[tuple[int, int]]: + return sorted( + canonical_edge(length, item.distance, offset) + for item in distance_classes(length) + for offset in range(item.multiplicity) + ) + + +def _open_offset_matrix( + multiplicity: int, + rate: float, + stream_count: int, +) -> np.ndarray: + seeds = np.random.SeedSequence(20260729).spawn(stream_count) + result = np.zeros((stream_count, multiplicity), dtype=np.int64) + for row, seed in enumerate(seeds): + rng = np.random.default_rng(seed) + result[row, list(_iter_open_offsets(multiplicity, rate, rng))] = 1 + return result + + +def _binomial_fourth_central_moment(trials: int, probability: float) -> float: + variance = trials * probability * (1.0 - probability) + return variance * (1.0 - 6.0 * probability * (1.0 - probability)) + 3.0 * variance**2 + + +def test_iter_open_offsets_controlled_stream_hits_expected_offsets(): + rate = math.log(2.0) + offsets = list(_iter_open_offsets(6, rate, _FakeRandomStream([0.0, 0.75, 0.75]))) + assert offsets == [0, 3] + assert offsets == sorted(offsets) + assert len(offsets) == len(set(offsets)) + assert all(0 <= offset < 6 for offset in offsets) + + +def test_iter_open_offsets_boundary_branch_uses_ge_remaining_stop(): + rate = math.log(2.0) + boundary = 1.0 - math.exp(-2.0 * rate) + assert boundary == 0.75 + below = math.nextafter(boundary, 0.0) + above = math.nextafter(boundary, 1.0) + assert list(_iter_open_offsets(2, rate, _FakeRandomStream([below]))) == [1] + assert list(_iter_open_offsets(2, rate, _FakeRandomStream([boundary]))) == [] + assert list(_iter_open_offsets(2, rate, _FakeRandomStream([above]))) == [] + + +@pytest.mark.parametrize("rate", [math.log(2.0), 1.7]) +def test_iter_open_offsets_matches_binomial_marginals(rate: float): + multiplicity = 10 + stream_count = 20_000 + openings = _open_offset_matrix(multiplicity, rate, stream_count) + probability = -math.expm1(-rate) + per_offset_alpha = 0.001 / (2 * multiplicity) + for column in range(multiplicity): + observed = int(openings[:, column].sum()) + p_value = binomtest(observed, stream_count, probability).pvalue + assert p_value >= per_offset_alpha + counts = openings.sum(axis=1).astype(np.float64) + expected_mean = multiplicity * probability + expected_variance = multiplicity * probability * (1.0 - probability) + mean_standard_error = math.sqrt(expected_variance / stream_count) + assert counts.mean() == pytest.approx(expected_mean, abs=6.0 * mean_standard_error) + fourth_moment = _binomial_fourth_central_moment(multiplicity, probability) + variance_standard_error = math.sqrt( + ( + fourth_moment + - ((stream_count - 3.0) / (stream_count - 1.0)) * expected_variance**2 + ) + / stream_count + ) + assert counts.var(ddof=1) == pytest.approx( + expected_variance, + abs=6.0 * variance_standard_error, + ) + + +def test_geometric_sampler_does_not_call_quadratic_or_enumerate_pairs(): + source = inspect.getsource(geometric_module) + assert "sample_quadratic" not in source + assert "iter_unordered_edges" not in source + + +def test_geometric_sampler_exact_limits_and_antipodal_uniqueness(): + empty = sample_geometric( + ModelSpec(8, 1.0, 0.0), + np.random.default_rng(4), + ) + assert empty.edges.shape == (0, 2) + np.testing.assert_array_equal(empty.labels, np.arange(8, dtype=np.int64)) + + full = sample_geometric( + ModelSpec(8, 1.0, 1e6), + np.random.default_rng(4), + ) + expected_edges = _all_canonical_edges(8) + assert [tuple(edge) for edge in full.edges.tolist()] == expected_edges + antipodal = [edge for edge in expected_edges if _distance(edge, 8) == 4] + assert len(antipodal) == 4 + assert len([tuple(edge) for edge in full.edges.tolist() if _distance(tuple(edge), 8) == 4]) == 4 + np.testing.assert_array_equal(full.labels, np.zeros(8, dtype=np.int64)) + + +def test_geometric_sampler_is_seed_reproducible(): + spec = ModelSpec(32, 0.8, 0.7) + first = sample_geometric(spec, np.random.default_rng(20260729)) + second = sample_geometric(spec, np.random.default_rng(20260729)) + np.testing.assert_array_equal(first.edges, second.edges) + np.testing.assert_array_equal(first.labels, second.labels) + + +def test_geometric_sampler_rejects_unregistered_rng_objects(): + with pytest.raises(ValueError, match="numpy.random.Generator"): + sample_geometric(ModelSpec(8, 1.0, 0.7), object()) + + +def test_geometric_sampler_handles_rate_underflow_without_duplicate_edges(): + sample = sample_geometric( + ModelSpec(8, 128.0, np.nextafter(0.0, 1.0)), + np.random.default_rng(9), + ) + edge_tuples = [tuple(edge) for edge in sample.edges.tolist()] + assert edge_tuples == sorted(edge_tuples) + assert len(edge_tuples) == len(set(edge_tuples)) + assert all(_distance(edge, 8) == 1 for edge in edge_tuples) + + +def test_geometric_sampler_handles_rate_overflow_without_duplicate_edges(): + sample = sample_geometric( + ModelSpec(8, 1.0, 1.7e308), + np.random.default_rng(11), + ) + expected_edges = _all_canonical_edges(8) + assert [tuple(edge) for edge in sample.edges.tolist()] == expected_edges + np.testing.assert_array_equal(sample.labels, np.zeros(8, dtype=np.int64)) + + +def test_package_root_exports_geometric_sampler(): + assert lrp.sample_geometric is sample_geometric + assert "sample_geometric" in lrp.__all__ diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_kernel.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_kernel.py new file mode 100644 index 000000000..0cac96ddc --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_kernel.py @@ -0,0 +1,133 @@ +from decimal import Decimal, localcontext +import math + +import numpy as np +import pytest +from scipy.special import zeta + +from long_range_percolation.kernel import ( + edge_probabilities, + kernel_weight_sum, + periodic_kernel, + periodic_kernel_reference, +) +from long_range_percolation.model import ModelSpec, distance_classes + + +def test_sigma_one_kernel_matches_cosecant_identity(): + for length in (4, 6, 32, 256): + distances = np.arange(1, length // 2 + 1, dtype=np.float64) + expected = (np.pi / length) ** 2 / np.sin(np.pi * distances / length) ** 2 + np.testing.assert_allclose( + periodic_kernel(length, 1.0), + expected, + rtol=2e-14, + atol=2e-14, + ) + + +def test_hurwitz_kernel_is_enclosed_by_direct_image_sum(): + values = periodic_kernel(12, 0.8) + partial, bound = periodic_kernel_reference(12, 0.8, images=100_000) + assert np.all(np.abs(values - partial) <= bound) + + +def test_reference_error_and_bound_shrink_with_more_images(): + values = periodic_kernel(12, 0.8) + coarse, coarse_bound = periodic_kernel_reference(12, 0.8, images=100) + fine, fine_bound = periodic_kernel_reference(12, 0.8, images=200) + assert np.all(np.abs(values - fine) < np.abs(values - coarse)) + assert np.all(fine_bound < coarse_bound) + + +def test_global_kernel_sum_identity(): + for length, sigma in [(4, 0.8), (12, 1.0), (32, 1.1)]: + values = periodic_kernel(length, sigma) + measured = sum( + item.multiplicity * values[item.distance - 1] + for item in distance_classes(length) + ) + expected = length * zeta(1.0 + sigma, 1.0) * ( + 1.0 - length ** (-(1.0 + sigma)) + ) + assert measured == pytest.approx(expected, rel=2e-13) + + +def test_kernel_weight_sum_matches_periodic_kernel_table(): + for length, sigma in [(4, 0.8), (12, 1.0), (32, 1.1)]: + values = periodic_kernel(length, sigma) + measured = sum( + item.multiplicity * values[item.distance - 1] + for item in distance_classes(length) + ) + assert kernel_weight_sum(length, sigma) == pytest.approx(measured, rel=2e-13) + + +def test_periodic_kernel_uses_full_periodic_image_convention(): + length = 12 + sigma = 0.8 + distance = 3 + exponent = 1.0 + sigma + value = periodic_kernel(length, sigma)[distance - 1] + bare_minimum_image = distance ** (-exponent) + hurwitz_value = length ** (-exponent) * ( + zeta(exponent, distance / length) + zeta(exponent, 1.0 - distance / length) + ) + assert value == pytest.approx(hurwitz_value, rel=2e-13) + assert value > bare_minimum_image + + +def test_rearranged_kernel_matches_old_hurwitz_form_in_stable_regime(): + length = 64 + sigma = 0.9 + exponent = 1.0 + sigma + distances = np.arange(1, length // 2 + 1, dtype=np.float64) + fractions = distances / length + old_form = length ** (-exponent) * ( + zeta(exponent, fractions) + zeta(exponent, 1.0 - fractions) + ) + np.testing.assert_allclose( + periodic_kernel(length, sigma), + old_form, + rtol=3e-15, + atol=0.0, + ) + + +def test_high_sigma_kernel_matches_high_precision_direct_images(): + length = 256 + sigma = 128.0 + exponent = 129 + values = periodic_kernel(length, sigma) + assert np.all(np.isfinite(values)) + assert np.all(values > 0.0) + + with localcontext() as context: + context.prec = 120 + for distance in (1, 2, 64, 128): + expected = sum( + Decimal(abs(distance + image * length)) ** (-exponent) + for image in range(-16, 17) + ) + observed = float(values[distance - 1]) + assert math.isclose( + observed, + float(expected), + rel_tol=4e-15, + abs_tol=0.0, + ) + + +def test_kernel_rejects_positive_entries_below_float64_representability(): + with pytest.raises(ValueError, match="representability"): + periodic_kernel(256, 1024.0) + + +def test_edge_probabilities_use_stable_exponential_form(): + spec = ModelSpec(length=4, sigma=1.0, kappa=1e-16) + probability = edge_probabilities(spec, periodic_kernel(4, 1.0))[0] + assert probability > 0.0 + assert probability == pytest.approx( + spec.kappa * periodic_kernel(4, 1.0)[0], + rel=1e-15, + ) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_model.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_model.py new file mode 100644 index 000000000..296eeb8b7 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_model.py @@ -0,0 +1,91 @@ +import math + +import pytest + +from long_range_percolation.kernel import periodic_kernel + +from long_range_percolation.model import ( + ModelSpec, + canonical_edge, + distance_classes, + iter_unordered_edges, +) + + +def test_model_spec_rejects_non_even_or_nonphysical_parameters(): + for values in [ + {"length": 3, "sigma": 1.0, "kappa": 1.0}, + {"length": 2, "sigma": 0.0, "kappa": 1.0}, + {"length": 2, "sigma": 1.0, "kappa": -1.0}, + {"length": 2, "sigma": float("nan"), "kappa": 1.0}, + {"length": 2, "sigma": 1.0, "kappa": float("inf")}, + ]: + with pytest.raises(ValueError): + ModelSpec(**values) + + +def test_model_spec_rejects_positive_sigma_when_one_plus_sigma_rounds_to_one(): + sigma = math.nextafter(0.0, 1.0) + assert sigma > 0.0 + assert 1.0 + sigma == 1.0 + with pytest.raises(ValueError, match=r"1\.0 \+ sigma > 1\.0"): + ModelSpec(length=4, sigma=sigma, kappa=0.0) + + +def test_model_spec_accepts_boundary_sigma_values_and_kernel_stays_finite(): + for sigma in (math.ulp(1.0), 0.8, 1.0, 1.1): + spec = ModelSpec(length=12, sigma=sigma, kappa=0.5) + assert spec.sigma == sigma + assert math.isfinite(1.0 + sigma) + kernel = periodic_kernel(spec.length, spec.sigma) + assert kernel.shape == (spec.length // 2,) + assert all(math.isfinite(float(value)) for value in kernel) + + +def test_distance_classes_count_every_unordered_edge_once(): + for length in (2, 4, 6, 32): + classes = distance_classes(length) + assert sum(item.multiplicity for item in classes) == length * (length - 1) // 2 + assert classes[-1].distance == length // 2 + assert classes[-1].multiplicity == length // 2 + + +@pytest.mark.parametrize("length", [1, 3]) +def test_distance_classes_rejects_small_or_odd_lengths(length: int): + with pytest.raises(ValueError, match="length must be even and at least two"): + distance_classes(length) + + +def test_canonical_edges_match_direct_unordered_enumeration(): + length = 8 + from_classes = { + canonical_edge(length, item.distance, offset) + for item in distance_classes(length) + for offset in range(item.multiplicity) + } + assert from_classes == set(iter_unordered_edges(length)) + assert len(from_classes) == length * (length - 1) // 2 + + +@pytest.mark.parametrize("length", [1, 3]) +def test_canonical_edge_rejects_small_or_odd_lengths(length: int): + with pytest.raises(ValueError, match="length must be even and at least two"): + canonical_edge(length, 1, 0) + + +@pytest.mark.parametrize("distance", [0, 5]) +def test_canonical_edge_rejects_out_of_range_distance(distance: int): + with pytest.raises(ValueError, match="distance is outside the canonical range"): + canonical_edge(8, distance, 0) + + +@pytest.mark.parametrize("offset", [-1, 8]) +def test_canonical_edge_rejects_out_of_range_offset(offset: int): + with pytest.raises(ValueError, match="offset is outside the distance class"): + canonical_edge(8, 1, offset) + + +@pytest.mark.parametrize("offset", [False, 1.5]) +def test_canonical_edge_rejects_bool_and_non_integer_offset(offset: object): + with pytest.raises(ValueError, match="offset must be an integer"): + canonical_edge(8, 1, offset) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_oracle.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_oracle.py new file mode 100644 index 000000000..c1c5ed05c --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_oracle.py @@ -0,0 +1,70 @@ +import numpy as np +import pytest + +import long_range_percolation as lrp +from long_range_percolation.model import ModelSpec +from long_range_percolation.oracle import ( + expected_open_edges, + no_edge_probability, + sample_quadratic, + variance_open_edges, +) + + +def test_quadratic_oracle_exact_limits(): + empty = sample_quadratic( + ModelSpec(8, 1.0, 0.0), + np.random.default_rng(1), + ) + assert empty.edges.shape == (0, 2) + np.testing.assert_array_equal(empty.labels, np.arange(8)) + + full = sample_quadratic( + ModelSpec(8, 1.0, 1e6), + np.random.default_rng(1), + ) + assert full.edges.shape == (28, 2) + np.testing.assert_array_equal(full.labels, np.zeros(8, dtype=np.int64)) + + +def test_oracle_edge_count_matches_analytic_moments(): + spec = ModelSpec(8, 0.9, 0.7) + counts = np.array( + [ + sample_quadratic(spec, np.random.default_rng(seed)).edges.shape[0] + for seed in range(30_000) + ] + ) + assert counts.mean() == pytest.approx( + expected_open_edges(spec), + abs=5.0 * np.sqrt(variance_open_edges(spec) / counts.size), + ) + assert counts.var(ddof=1) == pytest.approx( + variance_open_edges(spec), + rel=0.05, + ) + + +def test_no_edge_probability_uses_total_kernel_weight(): + spec = ModelSpec(6, 1.0, 0.4) + observed = np.mean( + [ + sample_quadratic(spec, np.random.default_rng(seed)).edges.size == 0 + for seed in range(40_000) + ] + ) + assert observed == pytest.approx(no_edge_probability(spec), abs=0.01) + + +@pytest.mark.parametrize( + ("name", "symbol"), + [ + ("sample_quadratic", sample_quadratic), + ("expected_open_edges", expected_open_edges), + ("variance_open_edges", variance_open_edges), + ("no_edge_probability", no_edge_probability), + ], +) +def test_package_root_exports_oracle_public_symbols(name: str, symbol: object): + assert getattr(lrp, name) is symbol + assert name in lrp.__all__ diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot.py new file mode 100644 index 000000000..aeeab0d42 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot.py @@ -0,0 +1,722 @@ +from __future__ import annotations + +import json +import os +import shutil +from concurrent.futures import ThreadPoolExecutor +from pathlib import Path +from threading import Event + +import pytest + +from long_range_percolation import pilot +from long_range_percolation.pilot import PilotCell + + +def _tiny_spec(tmp_path: Path, *, replicas: tuple[int, ...] = (0,)) -> Path: + root = tmp_path / "pilot" + return pilot._write_test_pilot_run_spec( + root, + lengths=(8,), + sigmas=(1.0,), + replicas=replicas, + kappas=(0.0, 0.25), + ) + + +def _frozen_spec(tmp_path: Path) -> Path: + return pilot._write_test_frozen_pilot_run_spec(tmp_path / "frozen") + + +def test_frozen_registry_has_exact_order_grid_and_unique_identities(tmp_path: Path): + spec = pilot._build_test_pilot_run_spec(tmp_path / "pilot") + cells = [PilotCell.from_document(item) for item in spec["cells"]] + assert len(cells) == 96 + assert [(cell.sigma, cell.length, cell.replica) for cell in cells] == [ + (sigma, length, replica) + for sigma in (0.8, 0.9, 1.0, 1.1) + for length in (2**10, 2**14, 2**18) + for replica in range(8) + ] + assert spec["protocol"]["sigmas"] == [value.hex() for value in (0.8, 0.9, 1.0, 1.1)] + assert spec["protocol"]["kappas"] == [ + value.hex() for value in [0.0] + [0.25 * 1.25**j for j in range(15)] + ] + assert len({cell.request_sha256 for cell in cells}) == 96 + assert len({cell.cell_id for cell in cells}) == 96 + assert spec["rng_assignment_sha256"] + + +def test_run_spec_is_canonical_and_all_paths_are_relative(tmp_path: Path): + path = _tiny_spec(tmp_path) + payload = path.read_bytes() + document = json.loads(payload) + assert payload == pilot._canonical_bytes(document) + assert document["artifact_root"] == "." + assert "run_root" not in document + for cell in document["cells"]: + for key in ("cell_path", "run_path", "manifest_path"): + assert not Path(cell[key]).is_absolute() + assert ".." not in Path(cell[key]).parts + + +def test_small_cell_end_to_end_is_idempotent_and_portable(tmp_path: Path): + path = _tiny_spec(tmp_path) + first = pilot._run_test_pilot_cell(path, 0) + marker = path.parent / first["manifest_path"] + before = marker.stat().st_mtime_ns + assert pilot._run_test_pilot_cell(path, 0) == first + assert marker.stat().st_mtime_ns == before + merged = pilot._merge_test_pilot_progress(path) + assert merged["cell_count"] == 1 + assert merged["trajectory_count"] == 1 + assert pilot._verify_test_pilot_download(path)["cell_count"] == 1 + + copied = tmp_path / "downloaded" + shutil.copytree(path.parent, copied) + assert ( + pilot._verify_test_pilot_download(copied / "run_spec.json")["cell_count"] == 1 + ) + + +def test_duplicate_execution_has_one_equivalent_verified_winner(tmp_path: Path): + path = _tiny_spec(tmp_path) + with ThreadPoolExecutor(max_workers=2) as pool: + results = list( + pool.map(lambda _: pilot._run_test_pilot_cell(path, 0), range(2)) + ) + assert results[0] == results[1] + run = next((path.parent / "cells").iterdir()) / "run" + assert len(list((run / "trajectories").glob("trajectory-*.h5"))) == 1 + assert len(list((run / "batches").glob("batch-*.json"))) == 1 + + +def test_different_cells_can_initialize_while_first_worker_retains_chain( + tmp_path: Path, +): + path = _tiny_spec(tmp_path, replicas=(0, 1)) + first_ready = Event() + second_done = Event() + + def hold_first(stage: str) -> None: + if stage == "after-trajectory": + first_ready.set() + assert second_done.wait(timeout=10) + + with ThreadPoolExecutor(max_workers=2) as pool: + first = pool.submit( + pilot._run_test_pilot_cell, + path, + 0, + crash_hook=hold_first, + ) + assert first_ready.wait(timeout=10) + second = pilot._run_test_pilot_cell(path, 1) + second_done.set() + first_result = first.result(timeout=10) + + assert first_result["cell_index"] == 0 + assert second["cell_index"] == 1 + assert pilot._pending_test_pilot_cells(path) == [] + pilot._merge_test_pilot_progress(path) + assert pilot._verify_test_pilot_download(path)["cell_count"] == 2 + + +def test_run_spec_read_allows_same_parent_to_gain_cells_directory( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + path = _tiny_spec(tmp_path) + descriptor_read = Event() + parent_mutated = Event() + original = pilot._read_descriptor_bounded + held = False + + def hold_after_read(descriptor: int, maximum_size: int, description: str) -> bytes: + nonlocal held + payload = original(descriptor, maximum_size, description) + if description == "pilot run spec" and not held: + held = True + descriptor_read.set() + assert parent_mutated.wait(timeout=10) + return payload + + monkeypatch.setattr(pilot, "_read_descriptor_bounded", hold_after_read) + with ThreadPoolExecutor(max_workers=1) as pool: + loading = pool.submit( + pilot._load_pilot_spec, + path, + verify_current_environment=False, + expected_schema=pilot.TEST_RUN_SPEC_SCHEMA, + ) + assert descriptor_read.wait(timeout=10) + (path.parent / "cells").mkdir() + parent_mutated.set() + loaded = loading.result(timeout=10) + + assert loaded["run_spec_sha256"] + + +def test_replacing_shared_cells_inode_while_worker_retains_chain_fails( + tmp_path: Path, +): + path = _tiny_spec(tmp_path, replicas=(0, 1)) + cells_root = path.parent / "cells" + + def replace_cells(stage: str) -> None: + if stage == "after-trajectory": + cells_root.rename(path.parent / "detached-cells") + cells_root.mkdir() + + with pytest.raises(RuntimeError, match="identity|generation"): + pilot._run_test_pilot_cell(path, 0, crash_hook=replace_cells) + + +@pytest.mark.parametrize("stage", ("after-trajectory", "after-progress")) +def test_resume_after_publication_finishes_remaining_boundaries( + tmp_path: Path, stage: str +): + path = _tiny_spec(tmp_path) + + def stop(actual: str) -> None: + if actual == stage: + raise RuntimeError("injected stop") + + with pytest.raises(RuntimeError, match="injected stop"): + pilot._run_test_pilot_cell(path, 0, crash_hook=stop) + run = next((path.parent / "cells").iterdir()) / "run" + assert list((run / "trajectories").glob("trajectory-*.h5")) + if stage == "after-trajectory": + assert not list((run / "batches").glob("batch-*.json")) + else: + assert (run / "progress.json").is_file() + assert not (run.parent / "manifest.json").exists() + result = pilot._run_test_pilot_cell(path, 0) + assert (path.parent / result["manifest_path"]).is_file() + assert (run / "progress.json").is_file() + + +@pytest.mark.parametrize("suffix", (".partial", ".intent")) +def test_stale_publication_markers_fail_closed(tmp_path: Path, suffix: str): + path = _tiny_spec(tmp_path) + pilot._run_test_pilot_cell(path, 0) + cell = next((path.parent / "cells").iterdir()) + (cell / f"stale{suffix}").write_text("do not delete", encoding="utf-8") + with pytest.raises(RuntimeError, match="publication marker"): + pilot._run_test_pilot_cell(path, 0) + assert (cell / f"stale{suffix}").exists() + + +def test_pending_and_merge_reject_missing_extra_duplicate_and_corrupt(tmp_path: Path): + path = _tiny_spec(tmp_path, replicas=(0, 1)) + assert pilot._pending_test_pilot_cells(path) == [0, 1] + pilot._run_test_pilot_cell(path, 0) + assert pilot._pending_test_pilot_cells(path) == [1] + with pytest.raises(RuntimeError, match="missing"): + pilot._merge_test_pilot_progress(path) + pilot._run_test_pilot_cell(path, 1) + document = pilot._merge_test_pilot_progress(path) + assert document["cell_count"] == document["trajectory_count"] == 2 + + extra = path.parent / "cells" / "extra" + extra.mkdir() + with pytest.raises(RuntimeError, match="extra"): + pilot._merge_test_pilot_progress(path) + extra.rmdir() + + marker = path.parent / json.loads(path.read_text())["cells"][0]["manifest_path"] + marker.write_text("{}", encoding="utf-8") + with pytest.raises(RuntimeError, match="manifest"): + pilot._merge_test_pilot_progress(path) + + +def test_spec_loader_rejects_provenance_drift(tmp_path: Path): + path = _frozen_spec(tmp_path) + document = json.loads(path.read_text()) + document["uv_lock_sha256"] = "0" * 64 + document["run_spec_sha256"] = pilot._document_hash(document, "run_spec_sha256") + path.write_bytes(pilot._canonical_bytes(document)) + with pytest.raises(RuntimeError, match="uv.lock"): + pilot.load_pilot_run_spec(path, verify_current_environment=False) + + +def test_correctness_evidence_requires_checked_in_approval_digest( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + historical = pilot.CORRECTNESS_APPROVAL_REVISION + modules = pilot._scientific_hashes() + package = tmp_path / "package" + report_path = package / "report" / "report.json" + report_path.parent.mkdir(parents=True) + report = { + "passed": True, + "source": { + "source_revision": historical, + "clean_tree": True, + "provenance_error": None, + }, + "runtime_capability": {"node": "validated"}, + "checks": [], + } + report_path.write_bytes(pilot._canonical_bytes(report)) + validation_spec_path = package / "run_spec.json" + validation_spec = { + "source_revision": historical, + "uv_lock_sha256": pilot._lock_hash(), + "runtime_capability": report["runtime_capability"], + "runtime_capability_sha256": "3" * 64, + "implementation_modules": modules, + "global_expected_checks": [], + "cells": [{"expected_checks": []} for _ in range(120)], + } + validation_spec_path.write_bytes(pilot._canonical_bytes(validation_spec)) + monkeypatch.setattr(pilot, "validate_report_payload", lambda *_: None) + monkeypatch.setattr( + pilot, "validate_validation_run_spec", lambda *_args, **_kwargs: None + ) + report["checks"] = [ + { + "passed": True, + "internal_sha256": pilot._sha256(pilot._canonical_bytes({"changed": True})), + } + ] + report_path.write_bytes(pilot._canonical_bytes(report)) + with pytest.raises(RuntimeError, match="approved correctness report SHA256"): + pilot._verified_correctness(report_path) + + +@pytest.mark.parametrize( + "mutation", + ( + "cell-count", + "wrong-96-cells", + "reordered", + "kappa", + "sigma", + "length", + "replica", + "request", + "path", + ), +) +def test_public_loader_never_downgrades_frozen_p0(tmp_path: Path, mutation: str): + path = _frozen_spec(tmp_path) + document = json.loads(path.read_text()) + if mutation == "cell-count": + document["cell_count"] = 1 + elif mutation == "wrong-96-cells": + document["cells"] = [dict(document["cells"][0]) for _ in range(96)] + elif mutation == "reordered": + document["cells"][0], document["cells"][1] = ( + document["cells"][1], + document["cells"][0], + ) + elif mutation == "kappa": + document["cells"][0]["kappas"][1] = (0.3).hex() + elif mutation == "sigma": + document["cells"][0]["sigma"] = (0.7).hex() + elif mutation == "length": + document["cells"][0]["length"] = 2048 + elif mutation == "replica": + document["cells"][0]["replica"] = 9 + elif mutation == "request": + document["cells"][0]["request_sha256"] = "f" * 64 + else: + document["cells"][0]["run_path"] = "cells/other/run" + document["run_spec_sha256"] = pilot._document_hash(document, "run_spec_sha256") + path.write_bytes(pilot._canonical_bytes(document)) + with pytest.raises(RuntimeError): + pilot.load_pilot_run_spec(path, verify_current_environment=False) + + +def test_public_loader_rejects_private_tiny_schema(tmp_path: Path): + path = _tiny_spec(tmp_path) + with pytest.raises(RuntimeError, match="schema"): + pilot.load_pilot_run_spec(path, verify_current_environment=False) + + +def test_public_p0_loader_rejects_internally_rehashed_extension(tmp_path: Path): + path = pilot._write_test_extension_run_spec(tmp_path / "extension", tiny=True) + document = json.loads(path.read_text()) + document["run_spec_sha256"] = pilot._document_hash(document, "run_spec_sha256") + path.write_bytes(pilot._canonical_bytes(document)) + with pytest.raises(RuntimeError, match="P0 run spec"): + pilot.load_pilot_run_spec(path, verify_current_environment=False) + + +@pytest.mark.parametrize( + ("field", "value"), + ( + ("sigmas", (0.7, 0.9, 1.0, 1.1)), + ("lengths", (2048, 16384, 262144)), + ("replicas", (1, 0, 2, 3, 4, 5, 6, 7)), + ("kappas", (0.0, *tuple(0.3 * 1.25**j for j in range(15)))), + ), +) +def test_public_loader_rejects_internally_rehashed_non_p0_registry( + tmp_path: Path, field: str, value: tuple[object, ...] +): + root = tmp_path / "pilot" + kwargs = {field: value} + document = pilot._build_test_pilot_run_spec(root, **kwargs) + document["schema_version"] = pilot.RUN_SPEC_SCHEMA + document["run_spec_sha256"] = pilot._document_hash(document, "run_spec_sha256") + root.mkdir() + path = root / "run_spec.json" + path.write_bytes(pilot._canonical_bytes(document)) + with pytest.raises(RuntimeError): + pilot.load_pilot_run_spec(path, verify_current_environment=False) + + +def test_bounded_json_reader_rejects_limit_plus_one_malformed_and_deep(tmp_path: Path): + valid = tmp_path / "valid.json" + valid.write_bytes(pilot._canonical_bytes({"schema_version": "test"})) + pilot._read_canonical(valid, "test", maximum_size=valid.stat().st_size) + with pytest.raises(RuntimeError, match="byte-size"): + pilot._read_canonical(valid, "test", maximum_size=valid.stat().st_size - 1) + + oversized = tmp_path / "oversized.json" + with oversized.open("wb") as stream: + stream.truncate(pilot.PILOT_RUN_SPEC_MAX_BYTES + 1) + with pytest.raises(RuntimeError, match="byte-size"): + pilot._read_canonical( + oversized, + "oversized", + maximum_size=pilot.PILOT_RUN_SPEC_MAX_BYTES, + ) + + malformed = tmp_path / "malformed.json" + malformed.write_bytes(b"{") + with pytest.raises(RuntimeError, match="valid JSON"): + pilot._read_canonical(malformed, "malformed", maximum_size=16) + + deep: object = "leaf" + for _ in range(pilot.PILOT_JSON_MAX_DEPTH + 1): + deep = [deep] + deep_path = tmp_path / "deep.json" + deep_path.write_bytes(pilot._canonical_bytes({"value": deep})) + with pytest.raises(RuntimeError, match="depth"): + pilot._read_canonical(deep_path, "deep", maximum_size=4096) + + with pytest.raises(RuntimeError, match="string"): + pilot._validate_json_bounds("x" * (pilot.PILOT_JSON_MAX_STRING + 1)) + with pytest.raises(RuntimeError, match="sequence"): + pilot._validate_json_bounds([None] * (pilot.PILOT_JSON_MAX_CONTAINER + 1)) + pilot._validate_json_bounds([None, None], maximum_nodes=3) + with pytest.raises(RuntimeError, match="node"): + pilot._validate_json_bounds([None, None, None], maximum_nodes=3) + + +def test_correctness_documents_use_their_larger_frozen_node_budget( + tmp_path: Path, +): + document = {"values": [None, None, None]} + path = tmp_path / "correctness.json" + path.write_bytes(pilot._canonical_bytes(document)) + with pytest.raises(RuntimeError, match="node"): + pilot._read_canonical( + path, + "ordinary document", + maximum_size=path.stat().st_size, + maximum_nodes=3, + ) + loaded, _ = pilot._read_canonical( + path, + "correctness document", + maximum_size=path.stat().st_size, + maximum_nodes=pilot.CORRECTNESS_JSON_MAX_NODES, + ) + assert loaded == document + + +def test_descriptor_read_rejects_file_replacement_and_same_size_mutation( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + path = tmp_path / "document.json" + original = pilot._canonical_bytes({"schema_version": "test", "value": "aa"}) + replacement = pilot._canonical_bytes({"schema_version": "test", "value": "bb"}) + assert len(original) == len(replacement) + path.write_bytes(original) + real_read = pilot._read_descriptor_bounded + + def swapping_read(descriptor: int, maximum: int, description: str) -> bytes: + payload = real_read(descriptor, maximum, description) + other = tmp_path / "other.json" + other.write_bytes(replacement) + os.replace(other, path) + return payload + + monkeypatch.setattr(pilot, "_read_descriptor_bounded", swapping_read) + with pytest.raises(RuntimeError, match="identity|generation|changed"): + pilot._read_canonical(path, "document", maximum_size=4096) + + +def test_publication_rejects_parent_swap_to_symlink( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + parent = tmp_path / "parent" + parent.mkdir() + hostile = tmp_path / "hostile" + hostile.mkdir() + real_link = pilot._link_at + + def swapping_link( + source: str, destination: str, source_fd: int, destination_fd: int + ) -> None: + moved = tmp_path / "moved" + os.rename(parent, moved) + parent.symlink_to(hostile, target_is_directory=True) + real_link(source, destination, source_fd, destination_fd) + + monkeypatch.setattr(pilot, "_link_at", swapping_link) + with pytest.raises(RuntimeError, match="parent|identity|changed"): + pilot._publish_once(parent / "marker.json", {"schema_version": "test"}) + assert not (hostile / "marker.json").exists() + + +def test_publication_rejects_noncooperating_destination_winner( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + destination = tmp_path / "marker.json" + real_link = pilot._link_at + + def racing_link( + source: str, target: str, source_fd: int, destination_fd: int + ) -> None: + descriptor = os.open( + target, + os.O_WRONLY | os.O_CREAT | os.O_EXCL, + 0o600, + dir_fd=destination_fd, + ) + try: + os.write( + descriptor, + pilot._canonical_bytes({"schema_version": "hostile"}), + ) + finally: + os.close(descriptor) + real_link(source, target, source_fd, destination_fd) + + monkeypatch.setattr(pilot, "_link_at", racing_link) + with pytest.raises(RuntimeError, match="other bytes"): + pilot._publish_once(destination, {"schema_version": "expected"}) + + +def test_cell_root_replacement_after_descriptor_open_fails_closed( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + path = _tiny_spec(tmp_path) + cell = PilotCell.from_document(json.loads(path.read_text())["cells"][0]) + cell_root = path.parent / cell.cell_path + real_flock = pilot.fcntl.flock + swapped = False + + def swapping_flock(descriptor: int, operation: int) -> None: + nonlocal swapped + if operation == pilot.fcntl.LOCK_EX and not swapped and cell_root.exists(): + swapped = True + cell_root.rename(path.parent / "detached-cell") + cell_root.mkdir() + real_flock(descriptor, operation) + + monkeypatch.setattr(pilot.fcntl, "flock", swapping_flock) + with pytest.raises(RuntimeError, match="directory identity changed"): + pilot._run_test_pilot_cell(path, 0) + + +def test_approval_registry_rejects_symlink( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + real = pilot._approval_registry_path() + linked = tmp_path / "approval.json" + linked.symlink_to(real) + monkeypatch.setattr(pilot, "_approval_registry_path", lambda: linked) + with pytest.raises(RuntimeError, match="symlink"): + pilot._load_approval_registry() + + +def test_approval_registry_digest_is_independently_pinned_after_clean_check( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + fabricated = json.loads(pilot._approval_registry_path().read_text()) + fabricated["report_sha256"] = "a" * 64 + fabricated["run_spec_sha256"] = "b" * 64 + replacement = tmp_path / "approval.json" + replacement.write_bytes(pilot._canonical_bytes(fabricated)) + monkeypatch.setattr(pilot, "_approval_registry_path", lambda: replacement) + monkeypatch.setattr( + pilot, + "_repository_state", + lambda: { + "source_revision": "f" * 40, + "clean_tree": True, + "provenance_error": None, + }, + ) + + pilot._current_source(require_clean=True) + with pytest.raises(RuntimeError, match="pinned SHA256"): + pilot._load_approval_registry() + + +def test_descriptor_hash_rejects_same_size_source_replacement( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + source = tmp_path / "source.py" + source.write_bytes(b"original\n") + replacement = tmp_path / "replacement.py" + replacement.write_bytes(b"changed!\n") + assert source.stat().st_size == replacement.stat().st_size + real_hash = pilot._artifacts._hash_descriptor + + def swapping_hash(descriptor: int, description: str) -> tuple[str, int]: + result = real_hash(descriptor, description) + os.replace(replacement, source) + return result + + monkeypatch.setattr(pilot, "_hash_descriptor", swapping_hash, raising=False) + with pytest.raises(RuntimeError, match="identity|generation|changed"): + pilot._file_hash(source) + + +def test_publication_rejects_ancestor_replacement_before_descriptor_open( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + ancestor = tmp_path / "ancestor" + parent = ancestor / "parent" + parent.mkdir(parents=True) + flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0) + swapped = False + + def swapping_open(name: str, parent_fd: int) -> int: + nonlocal swapped + if name == "ancestor" and not swapped: + swapped = True + ancestor.rename(tmp_path / "detached-ancestor") + (ancestor / "parent").mkdir(parents=True) + return os.open(name, flags, dir_fd=parent_fd) + + monkeypatch.setattr(pilot, "_open_directory_at", swapping_open, raising=False) + with pytest.raises(RuntimeError, match="ancestor|generation|identity"): + pilot._publish_once(parent / "marker.json", {"schema_version": "test"}) + + +def test_directory_chain_accepts_generation_only_metadata_drift( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + root = tmp_path / "root" + root.mkdir() + descriptor = os.open( + root, + os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0), + ) + original = os.fstat(descriptor) + real_lstat = Path.lstat + + class Drifted: + st_dev = original.st_dev + st_ino = original.st_ino + st_mode = original.st_mode + st_nlink = original.st_nlink + st_uid = original.st_uid + st_gid = original.st_gid + st_size = original.st_size + 4096 + st_mtime_ns = original.st_mtime_ns + 1 + st_ctime_ns = original.st_ctime_ns + 1 + + def drifted_lstat(path: Path): + if path == root: + return Drifted() + return real_lstat(path) + + monkeypatch.setattr(Path, "lstat", drifted_lstat) + try: + pilot._require_directory_chain( + [(root, descriptor, original)], + allow_final_mutation=False, + ) + opened = pilot._open_directory_chain(root, create=False) + pilot._close_directory_chain(opened) + finally: + os.close(descriptor) + + +def test_cell_swap_and_restore_during_work_fails_closed( + tmp_path: Path, +): + path = _tiny_spec(tmp_path) + cell = PilotCell.from_document(json.loads(path.read_text())["cells"][0]) + cell_root = path.parent / cell.cell_path + + def swap_and_restore(stage: str) -> None: + if stage == "after-trajectory": + detached = path.parent / "detached-cell" + cell_root.rename(detached) + detached.rename(cell_root) + + with pytest.raises(RuntimeError, match="generation changed"): + pilot._run_test_pilot_cell(path, 0, crash_hook=swap_and_restore) + + +def test_download_verification_requires_merged_progress(tmp_path: Path): + path = _tiny_spec(tmp_path) + pilot._run_test_pilot_cell(path, 0) + assert not (path.parent / pilot.MERGED_NAME).exists() + with pytest.raises(RuntimeError, match="merged pilot progress is missing"): + pilot._verify_test_pilot_download(path) + + +def test_public_download_verifier_rejects_missing_merged_progress( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + run_spec = tmp_path / pilot.RUN_SPEC_NAME + monkeypatch.setattr( + pilot, + "_merged_document", + lambda *_args, **_kwargs: {"schema_version": pilot.MERGED_SCHEMA}, + ) + with pytest.raises(RuntimeError, match="merged pilot progress is missing"): + pilot.verify_pilot_download(run_spec) + + +@pytest.mark.parametrize("kind", ("source", "runtime", "engine", "analysis")) +def test_current_environment_rejects_every_bound_provenance_drift( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, kind: str +): + path = _frozen_spec(tmp_path) + revision = json.loads(path.read_text())["orchestration_revision"] + monkeypatch.setattr( + pilot, + "_current_source", + lambda **_: { + "source_revision": revision, + "clean_tree": True, + "provenance_error": None, + }, + ) + if kind == "source": + monkeypatch.setattr( + pilot, + "_current_source", + lambda **_: { + "source_revision": "f" * 40, + "clean_tree": True, + "provenance_error": None, + }, + ) + match = "orchestration revision" + elif kind == "runtime": + monkeypatch.setattr( + pilot, "_runtime_document", lambda: ({"changed": True}, "f" * 64) + ) + match = "runtime capability" + elif kind == "engine": + modules = pilot._scientific_hashes() + modules[next(iter(modules))] = "f" * 64 + monkeypatch.setattr(pilot, "_scientific_hashes", lambda: modules) + match = "scientific engine" + else: + monkeypatch.setattr(pilot, "_analysis_plan_hash", lambda: "f" * 64) + match = "analysis plan" + with pytest.raises(RuntimeError, match=match): + pilot.load_pilot_run_spec(path, verify_current_environment=True) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py new file mode 100644 index 000000000..90e88e92b --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_analysis.py @@ -0,0 +1,2383 @@ +from __future__ import annotations + +import hashlib +import json +import multiprocessing +import shutil +import weakref +from collections.abc import Mapping +from dataclasses import FrozenInstanceError +from itertools import pairwise +from pathlib import Path +from types import SimpleNamespace + +import numpy as np +import pytest + +import long_range_percolation.pilot_analysis as analysis +import long_range_percolation.pilot_extension as extension +from long_range_percolation import pilot +from long_range_percolation.pilot import PilotCell +from long_range_percolation.pilot_extension import EXTENSION_ANALYSIS_SCHEMA +from long_range_percolation.trajectory import TrajectoryResult + +OBSERVABLE_COLUMNS = { + "s1_fraction": 4, + "s2_fraction": 5, + "q_g": 8, + "four_sector_crossing": 9, +} + + +def _canonical_bytes(document: object) -> bytes: + return ( + json.dumps( + document, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + + b"\n" + ) + + +def _sign(document: dict[str, object]) -> None: + unsigned = dict(document) + unsigned.pop("analysis_document_sha256", None) + document["analysis_document_sha256"] = hashlib.sha256( + _canonical_bytes(unsigned) + ).hexdigest() + + +def _extension_grid(lower: float, upper: float) -> tuple[float, ...]: + points = [lower, upper] + for _ in range(4): + ordered = sorted(points) + points.extend(left + (right - left) / 2.0 for left, right in pairwise(ordered)) + return tuple(sorted(set(points))) + + +def _combined_source_documents() -> tuple[ + dict[str, object], + dict[str, object], + dict[tuple[str, float, int, float, str], np.ndarray], +]: + sigmas = (0.8, 0.9, 1.0, 1.1) + lengths = pilot.PILOT_LENGTHS + p0_kappas = pilot.PILOT_KAPPAS + extension_grids = { + 0.9: _extension_grid(p0_kappas[4], p0_kappas[8]), + 1.0: _extension_grid(p0_kappas[5], p0_kappas[10]), + } + samples: dict[tuple[str, float, int, float, str], np.ndarray] = {} + + def estimates( + source: str, + source_sigmas: tuple[float, ...], + grids: dict[float, tuple[float, ...]], + replicas: int, + ) -> list[dict[str, object]]: + rows: list[dict[str, object]] = [] + for sigma_index, sigma in enumerate(source_sigmas): + for length_index, length in enumerate(lengths): + requests = [ + hashlib.sha256( + f"{source}|{sigma.hex()}|{length}|{replica}".encode() + ).hexdigest() + for replica in range(replicas) + ] + for kappa_index, kappa in enumerate(grids[sigma]): + means: dict[str, float] = {} + standard_errors: dict[str, float] = {} + for observable_index, name in enumerate(OBSERVABLE_COLUMNS): + values = np.asarray( + [ + 1000.0 * sigma_index + + 100.0 * length_index + + 10.0 * kappa_index + + observable_index + + (50.0 if source == "extension" else 0.0) + + replica * (observable_index + 1) / 8.0 + for replica in range(replicas) + ], + dtype=np.float64, + ) + samples[(source, sigma, length, kappa, name)] = values + means[name] = float(np.mean(values)) + standard_errors[name] = float( + np.std(values, ddof=1) / np.sqrt(replicas) + ) + rows.append( + { + "sigma_hex": sigma.hex(), + "length": length, + "kappa_hex": kappa.hex(), + "replica_count": replicas, + "means": means, + "standard_errors": standard_errors, + "request_sha256": requests, + } + ) + return rows + + p0: dict[str, object] = { + "schema_version": analysis.ANALYSIS_SCHEMA, + "p0_run_spec_sha256": "1" * 64, + "p0_progress_sha256": "2" * 64, + "source_revision": "3" * 40, + "analysis_plan_sha256": "4" * 64, + "observable_columns": OBSERVABLE_COLUMNS, + "estimates": estimates( + "p0", + sigmas, + {sigma: p0_kappas for sigma in sigmas}, + 8, + ), + } + extension_analysis: dict[str, object] = { + "schema_version": EXTENSION_ANALYSIS_SCHEMA, + "source_extension_protocol_sha256": "5" * 64, + "extension_run_spec_sha256": "6" * 64, + "extension_progress_sha256": "7" * 64, + "source_revision": "8" * 40, + "analysis_plan_sha256": "9" * 64, + "observable_columns": OBSERVABLE_COLUMNS, + "estimates": estimates( + "extension", + (0.9, 1.0), + extension_grids, + 16, + ), + } + _sign(p0) + _sign(extension_analysis) + return p0, extension_analysis, samples + + +def test_combine_p0_evidence_unions_grids_and_pools_whole_replica_moments(): + p0, extension_analysis, samples = _combined_source_documents() + + combined = extension._build_combined_p0_evidence(p0, extension_analysis) + + entries = combined["sigma_entries"] + assert [entry["sigma_hex"] for entry in entries] == [ + sigma.hex() for sigma in (0.8, 0.9, 1.0, 1.1) + ] + assert [len(entry["kappas"]) for entry in entries] == [16, 31, 31, 16] + assert all(entry["lengths"] == list(pilot.PILOT_LENGTHS) for entry in entries) + assert [len(entry["estimates"]) for entry in entries] == [48, 93, 93, 48] + assert combined["estimate_count"] == 282 + + p0_rows = p0["estimates"] + assert entries[0]["estimates"] == p0_rows[:48] + assert entries[3]["estimates"] == p0_rows[-48:] + blocked = entries[1] + shared = set(pilot.PILOT_KAPPAS) & { + float.fromhex(value) for value in blocked["kappas"] + } + assert len(shared) == 16 + extension_grid = { + float.fromhex(row["kappa_hex"]) + for row in extension_analysis["estimates"] + if row["sigma_hex"] == (0.9).hex() + } + assert len(shared & extension_grid) == 2 + replica_counts = { + row["replica_count"] for entry in entries for row in entry["estimates"] + } + assert replica_counts == {8, 16, 24} + + endpoint = min(shared & extension_grid) + pooled = next( + row + for row in blocked["estimates"] + if row["length"] == pilot.PILOT_LENGTHS[0] + and row["kappa_hex"] == endpoint.hex() + ) + for name in OBSERVABLE_COLUMNS: + direct = np.concatenate( + ( + samples[("p0", 0.9, pilot.PILOT_LENGTHS[0], endpoint, name)], + samples[("extension", 0.9, pilot.PILOT_LENGTHS[0], endpoint, name)], + ) + ) + assert pooled["means"][name] == pytest.approx(float(np.mean(direct))) + assert pooled["standard_errors"][name] == pytest.approx( + float(np.std(direct, ddof=1) / np.sqrt(24)) + ) + assert pooled["request_sha256"] == ( + next( + row["request_sha256"] + for row in p0_rows + if row["sigma_hex"] == (0.9).hex() + and row["length"] == pilot.PILOT_LENGTHS[0] + ) + + next( + row["request_sha256"] + for row in extension_analysis["estimates"] + if row["sigma_hex"] == (0.9).hex() + and row["length"] == pilot.PILOT_LENGTHS[0] + ) + ) + assert len(set(pooled["request_sha256"])) == 24 + + +def test_combine_p0_evidence_binds_sources_and_hashes_unsigned_document(): + p0, extension_analysis, _ = _combined_source_documents() + + combined = extension._build_combined_p0_evidence(p0, extension_analysis) + unsigned = dict(combined) + digest = unsigned.pop("analysis_document_sha256") + + assert combined["schema_version"] == extension.COMBINED_ANALYSIS_SCHEMA + assert ( + combined["source_p0_analysis_document_sha256"] == p0["analysis_document_sha256"] + ) + assert ( + combined["source_extension_analysis_document_sha256"] + == (extension_analysis["analysis_document_sha256"]) + ) + assert combined["p0_run_spec_sha256"] == p0["p0_run_spec_sha256"] + assert combined["p0_progress_sha256"] == p0["p0_progress_sha256"] + assert ( + combined["extension_run_spec_sha256"] + == extension_analysis["extension_run_spec_sha256"] + ) + assert ( + combined["extension_progress_sha256"] + == extension_analysis["extension_progress_sha256"] + ) + assert combined["p0_source_revision"] == p0["source_revision"] + assert ( + combined["extension_source_revision"] == extension_analysis["source_revision"] + ) + assert combined["observable_columns"] == OBSERVABLE_COLUMNS + assert digest == hashlib.sha256(_canonical_bytes(unsigned)).hexdigest() + + +@pytest.mark.parametrize( + "operation", + ("combine", "select", "build-p1"), +) +def test_combined_v2_has_no_self_signed_source_only_path(operation: str): + p0, extension_analysis, _ = _combined_source_documents() + combined = extension._build_combined_p0_evidence(p0, extension_analysis) + + with pytest.raises(TypeError): + if operation == "combine": + extension.combine_p0_evidence(p0, extension_analysis) + elif operation == "select": + analysis.select_p1_brackets( + combined, + p0_analysis=p0, + extension_analysis=extension_analysis, + ) + else: + brackets = analysis._select_p1_brackets_from_evidence( + combined, + analysis._selector_v2_evidence(combined), + ) + analysis.build_p1_protocol( + combined, + brackets, + p0_analysis=p0, + extension_analysis=extension_analysis, + ) + + +def test_real_authenticated_artifacts_remain_unresolved_and_p1_absent(): + results = Path(__file__).resolve().parents[6] / "results/challenge-194" + p0 = json.loads((results / "p0_analysis.json").read_bytes()) + extension_analysis = json.loads( + (results / "p0_extension_v1_analysis.json").read_bytes() + ) + protocol = json.loads((results / "p0_extension_v1_protocol.json").read_bytes()) + p0_root = (results / "pilot-p0-739880d").resolve() + extension_run_spec = (results / "pilot-p0-extension-v1/run_spec.json").resolve() + + combined = extension.combine_p0_evidence( + p0, + extension_analysis, + p0_evidence_root=p0_root, + extension_run_spec=extension_run_spec, + extension_protocol=protocol, + ) + assert ( + _canonical_bytes(combined) + == (results / "p0_combined_analysis_v2.json").read_bytes() + ) + brackets = analysis.select_p1_brackets( + combined, + p0_analysis=p0, + extension_analysis=extension_analysis, + p0_evidence_root=p0_root, + extension_run_spec=extension_run_spec, + extension_protocol=protocol, + ) + assert ( + _canonical_bytes(brackets) + == (results / "p0_combined_brackets_v2.json").read_bytes() + ) + assert brackets["requires_p0_extension"] is True + assert not (results / "p1_protocol.json").exists() + with pytest.raises(RuntimeError, match="P0 extension required"): + analysis.build_p1_protocol( + combined, + brackets, + p0_analysis=p0, + extension_analysis=extension_analysis, + p0_evidence_root=p0_root, + extension_run_spec=extension_run_spec, + extension_protocol=protocol, + ) + + +@pytest.mark.parametrize("operation", ("combine", "select", "build-p1")) +def test_fully_synthetic_resigned_282_row_sources_fail_with_real_trust_inputs( + operation: str, +): + results = Path(__file__).resolve().parents[6] / "results/challenge-194" + p0, extension_analysis, _ = _combined_source_documents() + combined = extension._build_combined_p0_evidence(p0, extension_analysis) + protocol = json.loads((results / "p0_extension_v1_protocol.json").read_bytes()) + trusted = { + "p0_evidence_root": (results / "pilot-p0-739880d").resolve(), + "extension_run_spec": ( + results / "pilot-p0-extension-v1/run_spec.json" + ).resolve(), + "extension_protocol": protocol, + } + + with pytest.raises(RuntimeError, match="P0 source hashes or revision"): + if operation == "combine": + extension.combine_p0_evidence( + p0, + extension_analysis, + **trusted, + ) + elif operation == "select": + analysis.select_p1_brackets( + combined, + p0_analysis=p0, + extension_analysis=extension_analysis, + **trusted, + ) + else: + brackets = analysis._select_p1_brackets_from_evidence( + combined, + analysis._selector_v2_evidence(combined), + ) + analysis.build_p1_protocol( + combined, + brackets, + p0_analysis=p0, + extension_analysis=extension_analysis, + **trusted, + ) + + +@pytest.mark.parametrize("operation", ("combine", "select", "build-p1")) +def test_combined_paths_reject_valid_root_swapped_between_verify_and_aggregate( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + operation: str, +): + p0, alternate_analysis, combined = _combined_selector_document() + brackets = analysis._select_p1_brackets_from_evidence( + combined, + analysis._selector_v2_evidence(combined), + ) + initial_root = tmp_path / "extension" + alternate_root = tmp_path / "alternate" + saved_root = tmp_path / "verified-original" + initial_root.mkdir() + alternate_root.mkdir() + initial_run_payload = b'{"root":"initial-run"}\n' + initial_progress_payload = b'{"root":"initial-progress"}\n' + alternate_run_payload = b'{"root":"alternate-run"}\n' + alternate_progress_payload = b'{"root":"alternate-progress"}\n' + (initial_root / "run_spec.json").write_bytes(initial_run_payload) + (initial_root / "progress.json").write_bytes(initial_progress_payload) + (alternate_root / "run_spec.json").write_bytes(alternate_run_payload) + (alternate_root / "progress.json").write_bytes(alternate_progress_payload) + run_spec = (initial_root / "run_spec.json").resolve() + protocol = {"protocol_sha256": "a" * 64} + + monkeypatch.setattr(extension, "_validate_source", lambda _source: None) + monkeypatch.setattr(extension, "_load_p0_evidence", lambda _root: ({}, {})) + monkeypatch.setattr( + extension, + "_validate_p0_extension_protocol_for_revision", + lambda *_args, **_kwargs: None, + ) + monkeypatch.setattr( + extension, + "EXTENSION_PROTOCOL_SHA256", + protocol["protocol_sha256"], + ) + monkeypatch.setattr( + extension, + "EXTENSION_PROTOCOL_FILE_SHA256", + hashlib.sha256(_canonical_bytes(protocol)).hexdigest(), + ) + monkeypatch.setattr( + extension, + "EXTENSION_RUN_SPEC_SHA256", + hashlib.sha256(initial_run_payload).hexdigest(), + ) + monkeypatch.setattr( + extension, + "EXTENSION_PROGRESS_SHA256", + hashlib.sha256(initial_progress_payload).hexdigest(), + ) + monkeypatch.setattr( + pilot, + "verify_frozen_challenge_194_p0_download", + lambda _path: {}, + ) + + def initial_read( + _path: Path, + description: str, + *, + maximum_size: int, + **_kwargs, + ): + del maximum_size + if "run spec" in description: + return {}, initial_run_payload + return {}, initial_progress_payload + + monkeypatch.setattr(pilot, "_read_canonical", initial_read) + + def verify_then_swap(_path: Path) -> dict[str, object]: + initial_root.rename(saved_root) + alternate_root.rename(initial_root) + return {} + + monkeypatch.setattr(pilot, "verify_p0_extension_download", verify_then_swap) + + def aggregate_swapped_root( + supplied_run_spec: Path, + _protocol: Mapping[str, object], + ) -> dict[str, object]: + assert supplied_run_spec.read_bytes() == alternate_run_payload + assert (supplied_run_spec.parent / "progress.json").read_bytes() == ( + alternate_progress_payload + ) + return alternate_analysis + + monkeypatch.setattr( + analysis, + "aggregate_p0_extension", + aggregate_swapped_root, + ) + trusted = { + "p0_evidence_root": tmp_path.resolve(), + "extension_run_spec": run_spec, + "extension_protocol": protocol, + } + + with pytest.raises(RuntimeError, match="post-aggregation.*hash"): + if operation == "combine": + extension.combine_p0_evidence( + p0, + alternate_analysis, + **trusted, + ) + elif operation == "select": + analysis.select_p1_brackets( + combined, + p0_analysis=p0, + extension_analysis=alternate_analysis, + **trusted, + ) + else: + analysis.build_p1_protocol( + combined, + brackets, + p0_analysis=p0, + extension_analysis=alternate_analysis, + **trusted, + ) + assert not (tmp_path / "p1_protocol.json").exists() + + +def test_authenticated_combination_rejects_modified_extension_means(): + results = Path(__file__).resolve().parents[6] / "results/challenge-194" + p0 = json.loads((results / "p0_analysis.json").read_bytes()) + extension_analysis = json.loads( + (results / "p0_extension_v1_analysis.json").read_bytes() + ) + extension_analysis["estimates"][0]["means"]["q_g"] += 0.125 + _sign(extension_analysis) + protocol = json.loads((results / "p0_extension_v1_protocol.json").read_bytes()) + + with pytest.raises(RuntimeError, match="authenticated recomputation"): + extension.combine_p0_evidence( + p0, + extension_analysis, + p0_evidence_root=(results / "pilot-p0-739880d").resolve(), + extension_run_spec=( + results / "pilot-p0-extension-v1/run_spec.json" + ).resolve(), + extension_protocol=protocol, + ) + + +@pytest.mark.parametrize("swap", ("p0-root", "extension-run-spec", "protocol")) +def test_authenticated_combination_rejects_root_and_protocol_swaps( + tmp_path: Path, + swap: str, +): + results = Path(__file__).resolve().parents[6] / "results/challenge-194" + p0 = json.loads((results / "p0_analysis.json").read_bytes()) + extension_analysis = json.loads( + (results / "p0_extension_v1_analysis.json").read_bytes() + ) + protocol = json.loads((results / "p0_extension_v1_protocol.json").read_bytes()) + p0_root = (results / "pilot-p0-739880d").resolve() + extension_run_spec = (results / "pilot-p0-extension-v1/run_spec.json").resolve() + if swap == "p0-root": + replacement = tmp_path / "p0-root" + replacement.mkdir() + shutil.copyfile(p0_root / "run_spec.json", replacement / "run_spec.json") + shutil.copyfile(p0_root / "progress.json", replacement / "progress.json") + p0_root = replacement.resolve() + elif swap == "extension-run-spec": + replacement = tmp_path / "extension-root" + replacement.mkdir() + (replacement / "run_spec.json").write_bytes(extension_run_spec.read_bytes()) + (replacement / "progress.json").write_bytes( + (extension_run_spec.parent / "progress.json").read_bytes() + ) + extension_run_spec = (replacement / "run_spec.json").resolve() + else: + protocol["purpose"] = "forged" + unsigned = dict(protocol) + unsigned.pop("protocol_sha256") + protocol["protocol_sha256"] = hashlib.sha256( + _canonical_bytes(unsigned) + ).hexdigest() + + with pytest.raises(RuntimeError): + extension.combine_p0_evidence( + p0, + extension_analysis, + p0_evidence_root=p0_root, + extension_run_spec=extension_run_spec, + extension_protocol=protocol, + ) + + +@pytest.mark.parametrize( + ("defect", "match"), + ( + ("source-hash", "digest"), + ("wrong-grid", "grid"), + ("extra-overlap", "grid|overlap"), + ("duplicate-request", "request"), + ("missing-length", "cardinality|canonical"), + ("missing-replica", "replica"), + ("reordered", "canonical"), + ("noncanonical-hex", "canonical"), + ("nonfinite", "finite"), + ("observable-columns", "observable"), + ("different-shape", "canonical|cardinality|shape"), + ), +) +def test_combine_p0_evidence_rejects_adversarial_sources(defect: str, match: str): + p0, extension_analysis, _ = _combined_source_documents() + target = extension_analysis + estimates = target["estimates"] + assert isinstance(estimates, list) + if defect == "source-hash": + target["analysis_document_sha256"] = "0" * 64 + elif defect == "wrong-grid": + estimates[1]["kappa_hex"] = float.fromhex(estimates[1]["kappa_hex"]).hex() + estimates[1]["kappa_hex"] = ( + float.fromhex(estimates[1]["kappa_hex"]) + 1e-6 + ).hex() + _sign(target) + elif defect == "extra-overlap": + estimates[1]["kappa_hex"] = pilot.PILOT_KAPPAS[1].hex() + _sign(target) + elif defect == "duplicate-request": + estimates[0]["request_sha256"][1] = estimates[0]["request_sha256"][0] + _sign(target) + elif defect == "missing-length": + del estimates[17:34] + _sign(target) + elif defect == "missing-replica": + estimates[0]["replica_count"] = 15 + estimates[0]["request_sha256"].pop() + _sign(target) + elif defect == "reordered": + estimates[0], estimates[1] = estimates[1], estimates[0] + _sign(target) + elif defect == "noncanonical-hex": + estimates[0]["kappa_hex"] = "0X1.F400000000000P-2" + _sign(target) + elif defect == "nonfinite": + estimates[0]["means"]["q_g"] = float("inf") + elif defect == "observable-columns": + target["observable_columns"] = {**OBSERVABLE_COLUMNS, "q_g": 7} + _sign(target) + else: + estimates[-1] = dict(estimates[0]) + _sign(target) + + with pytest.raises(RuntimeError, match=match): + extension._build_combined_p0_evidence(p0, extension_analysis) + + +@pytest.mark.parametrize( + "defect", + ("source-binding", "reordered-sigma-entries", "rehashed-different-shape"), +) +def test_combined_p0_evidence_validation_rejects_internal_mutation(defect: str): + p0, extension_analysis, _ = _combined_source_documents() + combined = extension._build_combined_p0_evidence(p0, extension_analysis) + if defect == "source-binding": + combined["source_extension_analysis_document_sha256"] = "0" * 64 + elif defect == "reordered-sigma-entries": + combined["sigma_entries"][0], combined["sigma_entries"][1] = ( + combined["sigma_entries"][1], + combined["sigma_entries"][0], + ) + else: + combined["sigma_entries"][1]["estimates"].pop() + assert combined["estimate_count"] == 282 + _sign(combined) + + with pytest.raises(RuntimeError, match="recomputation"): + extension._validate_combined_p0_evidence( + p0, + extension_analysis, + combined, + ) + + +@pytest.mark.parametrize("source_name", ("p0", "extension")) +@pytest.mark.parametrize( + ("field", "malformed"), + ( + ("length", 1024.0), + ("length", True), + ("length", np.int64(1024)), + ("replica_count", 8.0), + ("replica_count", True), + ("replica_count", np.int64(8)), + ("observable_columns", 4.0), + ("observable_columns", True), + ), +) +def test_combine_p0_evidence_requires_builtin_integer_source_fields( + source_name: str, + field: str, + malformed: object, +): + p0, extension_analysis, _ = _combined_source_documents() + target = p0 if source_name == "p0" else extension_analysis + rows = target["estimates"] + assert isinstance(rows, list) + if field == "observable_columns": + target["observable_columns"] = { + **OBSERVABLE_COLUMNS, + "s1_fraction": malformed, + } + else: + rows[0][field] = ( + 16.0 + if source_name == "extension" + and field == "replica_count" + and malformed == 8.0 + else np.int64(16) + if source_name == "extension" + and field == "replica_count" + and isinstance(malformed, np.integer) + else malformed + ) + if not isinstance(malformed, np.integer): + _sign(target) + + with pytest.raises(RuntimeError, match="built-in integer"): + extension._build_combined_p0_evidence(p0, extension_analysis) + + +@pytest.mark.parametrize( + ("field", "malformed"), + ( + ("estimate_count", 282.0), + ("estimate_count", True), + ("estimate_count", np.int64(282)), + ("length_axis", 1024.0), + ("length_axis", True), + ("length_axis", np.int64(1024)), + ("row_length", 1024.0), + ("row_length", True), + ("row_length", np.int64(1024)), + ("replica_count", 8.0), + ("replica_count", True), + ("replica_count", np.int64(8)), + ("observable_columns", 4.0), + ("observable_columns", True), + ("observable_columns", np.int64(4)), + ), +) +def test_combined_p0_evidence_requires_builtin_integer_output_fields( + field: str, + malformed: object, +): + p0, extension_analysis, _ = _combined_source_documents() + combined = extension._build_combined_p0_evidence(p0, extension_analysis) + if field == "estimate_count": + combined["estimate_count"] = malformed + elif field == "length_axis": + combined["sigma_entries"][0]["lengths"][0] = malformed + elif field == "row_length": + combined["sigma_entries"][0]["estimates"][0]["length"] = malformed + elif field == "replica_count": + combined["sigma_entries"][0]["estimates"][0]["replica_count"] = malformed + else: + combined["observable_columns"]["s1_fraction"] = malformed + if not isinstance(malformed, np.integer): + _sign(combined) + + with pytest.raises(RuntimeError, match="built-in integer"): + extension._validate_combined_p0_evidence( + p0, + extension_analysis, + combined, + ) + + +def _selector_document( + *, + sigmas: tuple[float, ...] = (0.8, 1.1), + lengths: tuple[int, ...] = (8, 16, 32), + kappas: tuple[float, ...] = (0.0, 1.0, 2.0, 4.0), + values: dict[ + tuple[float, int, float], + tuple[float, float], + ] + | None = None, +) -> dict[str, object]: + values = values or {} + estimates: list[dict[str, object]] = [] + for sigma in sigmas: + for length in lengths: + for kappa in kappas: + q_g, crossing = values.get( + (sigma, length, kappa), + (float(length) + kappa, 0.1 * kappa), + ) + estimates.append( + { + "sigma_hex": sigma.hex(), + "length": length, + "kappa_hex": kappa.hex(), + "replica_count": 8, + "means": { + "s1_fraction": 0.1, + "s2_fraction": 0.05, + "q_g": q_g, + "four_sector_crossing": crossing, + }, + "standard_errors": {name: 0.01 for name in OBSERVABLE_COLUMNS}, + "request_sha256": [ + str(replica) * 64 for replica in range(1, 9) + ], + } + ) + document: dict[str, object] = { + "schema_version": analysis.ANALYSIS_SCHEMA, + "p0_run_spec_sha256": "a" * 64, + "p0_progress_sha256": "b" * 64, + "source_revision": "c" * 40, + "analysis_plan_sha256": "d" * 64, + "observable_columns": OBSERVABLE_COLUMNS, + "estimates": estimates, + } + document["analysis_document_sha256"] = hashlib.sha256( + _canonical_bytes(document) + ).hexdigest() + return document + + +def _set_selector_value( + values: dict[tuple[float, int, float], tuple[float, float]], + sigma: float, + length: int, + kappas: tuple[float, ...], + q_g: tuple[float, ...], + crossing: tuple[float, ...], +) -> None: + for kappa, q_value, crossing_value in zip(kappas, q_g, crossing, strict=True): + values[(sigma, length, kappa)] = (q_value, crossing_value) + + +def _configure_selector_rows( + rows: list[dict[str, object]], + sigma: float, + lengths: tuple[int, ...], + kappas: tuple[float, ...], + selected_interval: int, +) -> None: + for row in rows: + if row["sigma_hex"] != sigma.hex(): + continue + length = row["length"] + kappa_index = kappas.index(float.fromhex(row["kappa_hex"])) + means = row["means"] + if sigma <= 1.0: + means["q_g"] = ( + float(kappa_index <= selected_interval) + if length == lengths[-2] + else float(kappa_index > selected_interval) + if length == lengths[-1] + else 0.0 + ) + means["four_sector_crossing"] = ( + 0.1 if kappa_index <= selected_interval else 0.9 + ) + else: + means["q_g"] = 0.0 + means["four_sector_crossing"] = ( + 0.1 if kappa_index <= selected_interval else 0.9 + ) + + +def _combined_selector_document( + *, + unresolved_sigma: float | None = None, + blocked_interval_offset: int = 0, +) -> tuple[dict[str, object], dict[str, object], dict[str, object]]: + p0, extension_analysis, _ = _combined_source_documents() + p0_rows = p0["estimates"] + extension_rows = extension_analysis["estimates"] + assert isinstance(p0_rows, list) + assert isinstance(extension_rows, list) + for sigma, interval in ((0.8, 4), (1.1, 8)): + _configure_selector_rows( + p0_rows, + sigma, + tuple(pilot.PILOT_LENGTHS), + tuple(pilot.PILOT_KAPPAS), + interval, + ) + + for sigma, combined_interval in ( + (0.9, 9 + blocked_interval_offset), + (1.0, 19 + blocked_interval_offset), + ): + if sigma == unresolved_sigma: + continue + extension_kappas = tuple( + float.fromhex(row["kappa_hex"]) + for row in extension_rows + if row["sigma_hex"] == sigma.hex() + and row["length"] == pilot.PILOT_LENGTHS[0] + ) + combined_kappas = tuple(sorted(set(pilot.PILOT_KAPPAS) | set(extension_kappas))) + threshold = combined_kappas[combined_interval] + for rows, kappas in ( + (p0_rows, tuple(pilot.PILOT_KAPPAS)), + (extension_rows, extension_kappas), + ): + selected_interval = max( + index for index, kappa in enumerate(kappas) if kappa <= threshold + ) + _configure_selector_rows( + rows, + sigma, + tuple(pilot.PILOT_LENGTHS), + kappas, + selected_interval, + ) + _sign(p0) + _sign(extension_analysis) + combined = extension._build_combined_p0_evidence(p0, extension_analysis) + return p0, extension_analysis, combined + + +def _select_test_combined( + combined: dict[str, object], + p0: dict[str, object], + extension_analysis: dict[str, object], +) -> dict[str, object]: + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setattr( + extension, + "_authenticate_combined_sources", + lambda supplied_p0, supplied_extension, **_kwargs: ( + supplied_p0, + supplied_extension, + ), + ) + return analysis.select_p1_brackets( + combined, + p0_analysis=p0, + extension_analysis=extension_analysis, + p0_evidence_root=Path("/test/p0"), + extension_run_spec=Path("/test/extension/run_spec.json"), + extension_protocol={}, + ) + + +def _build_test_combined_p1( + combined: dict[str, object], + brackets: dict[str, object] | None, + p0: dict[str, object], + extension_analysis: dict[str, object], +) -> dict[str, object]: + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setattr( + extension, + "_authenticate_combined_sources", + lambda supplied_p0, supplied_extension, **_kwargs: ( + supplied_p0, + supplied_extension, + ), + ) + return analysis.build_p1_protocol( + combined, + brackets, + p0_analysis=p0, + extension_analysis=extension_analysis, + p0_evidence_root=Path("/test/p0"), + extension_run_spec=Path("/test/extension/run_spec.json"), + extension_protocol={}, + ) + + +def _tiny_complete_pilot( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + *, + name: str = "pilot", + value_offset: float = 0.0, +) -> tuple[Path, dict[str, object]]: + path = pilot._write_test_pilot_run_spec( + tmp_path / name, + lengths=(8, 16), + sigmas=(1.0,), + replicas=(0, 1), + kappas=(0.0, 0.25, 0.5), + ) + + def deterministic_trajectory( + request: object, _kernel: np.ndarray, _alias: object + ) -> TrajectoryResult: + rows = np.zeros((3, 10), dtype=np.float64) + for kappa_index in range(3): + base = request.length / 8 + 2 * request.replica + kappa_index + value_offset + rows[kappa_index, 4] = base + rows[kappa_index, 5] = base + 10 + rows[kappa_index, 8] = base + 20 + rows[kappa_index, 9] = (request.replica + kappa_index) % 2 + return TrajectoryResult( + request_sha256=pilot.request_digest(request), + observables=rows, + terminal_counters=np.zeros((4, 4), dtype=np.uint32), + draw_counts=np.zeros((4, 3), dtype=np.uint64), + event_count=0, + duplicate_count=0, + hash_diagnostics=np.zeros(5, dtype=np.uint64), + ) + + monkeypatch.setattr(pilot, "run_poisson_numba", deterministic_trajectory) + spec = pilot._load_pilot_spec( + path, + verify_current_environment=False, + expected_schema=pilot.TEST_RUN_SPEC_SCHEMA, + ) + for cell_index in range(len(spec["cells"])): + pilot._run_test_pilot_cell(path, cell_index) + pilot._merge_test_pilot_progress(path) + return path, spec + + +def _tiny_complete_p0_extension( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + *, + name: str = "extension", + value_offset: float = 0.0, +) -> tuple[Path, dict[str, object], dict[str, object]]: + sigmas = (0.9, 1.0) + lengths = (8, 16, 32) + replicas = (24, 25) + sigma_kappas = { + 0.9: (0.25, 0.5, 0.75), + 1.0: (1.0, 1.5, 2.0), + } + document = pilot._build_test_pilot_run_spec( + tmp_path / name, + lengths=lengths, + sigmas=sigmas, + replicas=replicas, + kappas=sigma_kappas[0.9], + ) + document["schema_version"] = pilot.TEST_EXTENSION_RUN_SPEC_SCHEMA + assignments: list[dict[str, object]] = [] + cells: list[dict[str, object]] = [] + for raw in document["cells"]: + cell = dict(raw) + sigma = float.fromhex(cell["sigma"]) + kappas = sigma_kappas[sigma] + cell["sigma_grid_id"] = f"pilot-p0-extension-test-v1|sigma-f64={sigma.hex()}" + cell["kappas"] = [value.hex() for value in kappas] + provisional = PilotCell.from_document(cell) + request = provisional.request( + master_seed=pilot.TEST_EXTENSION_CONTRACT.master_seed, + phase=pilot.TEST_EXTENSION_CONTRACT.phase, + ) + cell["request_sha256"] = pilot.request_digest(request) + cell["rng_material_sha256"] = list( + pilot._stream_hashes( + provisional.length, + provisional.sigma_grid_id, + provisional.replica, + master_seed=pilot.TEST_EXTENSION_CONTRACT.master_seed, + phase=pilot.TEST_EXTENSION_CONTRACT.phase, + ) + ) + identity = { + "cell_index": cell["cell_index"], + "sigma": cell["sigma"], + "length": cell["length"], + "replica": cell["replica"], + "request_sha256": cell["request_sha256"], + } + cell_id = ( + f"{cell['cell_index']:03d}-" + f"{hashlib.sha256(_canonical_bytes(identity)).hexdigest()[:16]}" + ) + cell_path = f"cells/{cell_id}" + cell.update( + { + "cell_id": cell_id, + "cell_path": cell_path, + "run_path": f"{cell_path}/run", + "manifest_path": f"{cell_path}/manifest.json", + } + ) + cells.append(cell) + assignments.append( + { + "cell_index": cell["cell_index"], + "request_sha256": cell["request_sha256"], + "streams": cell["rng_material_sha256"], + } + ) + document["cells"] = cells + document["rng_assignment_sha256"] = hashlib.sha256( + _canonical_bytes({"assignments": assignments}) + ).hexdigest() + document["run_spec_sha256"] = pilot._document_hash(document, "run_spec_sha256") + pilot._validate_pilot_spec( + document, + contract=pilot.TEST_EXTENSION_CONTRACT, + ) + path = tmp_path / name / pilot.RUN_SPEC_NAME + pilot._publish_once(path, document) + + protocol: dict[str, object] = { + "loop_order": ["sigma", "length", "replica"], + "lengths": list(lengths), + "replicas": list(replicas), + "sigma_entries": [ + { + "sigma_hex": sigma.hex(), + "kappas": [value.hex() for value in sigma_kappas[sigma]], + } + for sigma in sigmas + ], + } + protocol["protocol_sha256"] = hashlib.sha256(_canonical_bytes(protocol)).hexdigest() + + def deterministic_trajectory( + request: object, _kernel: np.ndarray, _alias: object + ) -> TrajectoryResult: + rows = np.zeros((3, 10), dtype=np.float64) + for kappa_index in range(3): + base = ( + request.length / 8 + + 2 * (request.replica - replicas[0]) + + kappa_index + + 10 * request.sigma + + value_offset + ) + rows[kappa_index, 4] = base + rows[kappa_index, 5] = base + 10 + rows[kappa_index, 8] = base + 20 + rows[kappa_index, 9] = (request.replica + kappa_index) % 2 + return TrajectoryResult( + request_sha256=pilot.request_digest(request), + observables=rows, + terminal_counters=np.zeros((4, 4), dtype=np.uint32), + draw_counts=np.zeros((4, 3), dtype=np.uint64), + event_count=0, + duplicate_count=0, + hash_diagnostics=np.zeros(5, dtype=np.uint64), + ) + + monkeypatch.setattr(pilot, "run_poisson_numba", deterministic_trajectory) + for cell_index in range(len(cells)): + pilot._run_test_registered_pilot_cell(path, cell_index) + pilot._merge_test_registered_pilot_progress(path) + return path, document, protocol + + +def test_aggregate_p0_extension_groups_sigma_grids_with_bounded_retention( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + path, spec, protocol = _tiny_complete_p0_extension(tmp_path, monkeypatch) + original_loader = pilot._load_analysis_trajectory + original_grouper = analysis._group_estimates + previous_trajectory: weakref.ReferenceType[TrajectoryResult] | None = None + previous_group: weakref.ReferenceType[np.ndarray] | None = None + observed_group_shapes: list[tuple[int, ...]] = [] + + def tracking_loader( + trajectory: Path, + expected: dict[str, str], + required_digest: str, + ) -> TrajectoryResult: + nonlocal previous_trajectory + if previous_trajectory is not None: + assert previous_trajectory() is None, ( + "more than one trajectory was retained" + ) + result = original_loader(trajectory, expected, required_digest) + previous_trajectory = weakref.ref(result) + return result + + def tracking_grouper( + sigma: float, + length: int, + kappas: tuple[float, ...], + values: np.ndarray, + request_sha256: tuple[str, ...], + ) -> list[analysis.PilotEstimate]: + nonlocal previous_group + if previous_group is not None: + assert previous_group() is None, "more than one group array was retained" + observed_group_shapes.append(values.shape) + previous_group = weakref.ref(values) + return original_grouper(sigma, length, kappas, values, request_sha256) + + monkeypatch.setattr(pilot, "_load_analysis_trajectory", tracking_loader) + monkeypatch.setattr(analysis, "_group_estimates", tracking_grouper) + document = analysis._aggregate_test_p0_extension(path, protocol) + + assert observed_group_shapes == [(2, 3, 4)] * 6 + assert len(document["estimates"]) == 2 * 3 * 3 + assert [ + ( + estimate["sigma_hex"], + estimate["length"], + estimate["kappa_hex"], + ) + for estimate in document["estimates"] + ] == [ + (entry["sigma_hex"], length, kappa) + for entry in protocol["sigma_entries"] + for length in (8, 16, 32) + for kappa in entry["kappas"] + ] + first = document["estimates"][0] + assert first["request_sha256"] == [ + spec["cells"][0]["request_sha256"], + spec["cells"][1]["request_sha256"], + ] + assert first["means"]["q_g"] == 31.0 + assert first["standard_errors"]["q_g"] == 1.0 + + +def test_aggregate_p0_extension_binds_exact_sources_and_document_hash( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + path, spec, protocol = _tiny_complete_p0_extension(tmp_path, monkeypatch) + + document = analysis._aggregate_test_p0_extension(path, protocol) + unsigned = dict(document) + digest = unsigned.pop("analysis_document_sha256") + + assert document["schema_version"] == EXTENSION_ANALYSIS_SCHEMA + assert document["source_extension_protocol_sha256"] == protocol["protocol_sha256"] + assert ( + document["extension_run_spec_sha256"] + == hashlib.sha256(path.read_bytes()).hexdigest() + ) + assert ( + document["extension_progress_sha256"] + == hashlib.sha256((path.parent / "progress.json").read_bytes()).hexdigest() + ) + assert document["source_revision"] == spec["orchestration_revision"] + assert document["analysis_plan_sha256"] == spec["analysis_plan_sha256"] + assert digest == hashlib.sha256(_canonical_bytes(unsigned)).hexdigest() + + +@pytest.mark.parametrize( + "defect", + ("merged-trajectory-digest", "outer-manifest", "inner-progress"), +) +def test_aggregate_p0_extension_rejects_forged_verified_evidence( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + defect: str, +): + path, spec, protocol = _tiny_complete_p0_extension(tmp_path, monkeypatch) + root = path.parent + first = spec["cells"][0] + if defect == "merged-trajectory-digest": + target = root / "progress.json" + document = json.loads(target.read_text(encoding="utf-8")) + document["cells"][0]["trajectory_sha256"] = "0" * 64 + elif defect == "outer-manifest": + target = root / first["manifest_path"] + document = json.loads(target.read_text(encoding="utf-8")) + document["trajectory_sha256"] = "0" * 64 + else: + target = root / first["run_path"] / "progress.json" + document = {"schema_version": "forged-progress"} + target.write_bytes(_canonical_bytes(document)) + + with pytest.raises(RuntimeError, match="stale|corrupt|mismatch|progress"): + analysis._aggregate_test_p0_extension(path, protocol) + + +@pytest.mark.parametrize("swap", ("root", "progress")) +def test_aggregate_p0_extension_uses_retained_descriptors_during_swap( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + swap: str, +): + original, _, protocol = _tiny_complete_p0_extension( + tmp_path, monkeypatch, name="original", value_offset=0.0 + ) + alternate, _, _ = _tiny_complete_p0_extension( + tmp_path, monkeypatch, name="alternate", value_offset=1000.0 + ) + baseline = analysis._aggregate_test_p0_extension(original, protocol) + original_root = original.parent + alternate_root = alternate.parent + saved = tmp_path / "saved" + events: list[str] = [] + + def swap_and_restore(stage: str) -> None: + if stage == "snapshot-verified": + events.append(stage) + if swap == "root": + original_root.rename(saved) + alternate_root.rename(original_root) + else: + progress = original_root / "progress.json" + progress.rename(saved) + shutil.copyfile(alternate_root / "progress.json", progress) + elif stage == "snapshot-closed": + events.append(stage) + if swap == "root": + original_root.rename(alternate_root) + saved.rename(original_root) + else: + progress = original_root / "progress.json" + progress.unlink() + saved.rename(progress) + + observed = analysis._aggregate_test_p0_extension( + original, + protocol, + _snapshot_hook=swap_and_restore, + ) + + assert observed == baseline + assert events == ["snapshot-verified", "snapshot-closed"] + + +def test_p0_extension_snapshot_is_bounded_named_and_cleaned( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + path, _, protocol = _tiny_complete_p0_extension(tmp_path, monkeypatch) + parent = _private_snapshot_parent(tmp_path) + observed_names: list[str] = [] + + def capture(stage: str) -> None: + if stage == "snapshot-copy-start": + observed_names.extend(entry.name for entry in parent.iterdir()) + + analysis._aggregate_test_p0_extension( + path, + protocol, + snapshot_parent=parent, + _snapshot_hook=capture, + ) + + assert len(observed_names) == 1 + assert "test-p0-extension-v1" in observed_names[0] + assert list(parent.iterdir()) == [] + + +def test_p0_extension_snapshot_preflight_fails_before_copy( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + path, spec, protocol = _tiny_complete_p0_extension(tmp_path, monkeypatch) + (path.parent / spec["cells"][0]["cell_path"] / "unknown.bin").write_bytes(b"x") + copy_calls: list[str] = [] + + def forbid_copy(*_args: object, **_kwargs: object) -> None: + copy_calls.append("called") + raise AssertionError("payload copy started before bounded preflight") + + monkeypatch.setattr(pilot, "_copy_regular_snapshot_at", forbid_copy) + with pytest.raises(RuntimeError, match="unknown snapshot layout entry"): + analysis._aggregate_test_p0_extension( + path, + protocol, + snapshot_parent=_private_snapshot_parent(tmp_path), + ) + assert copy_calls == [] + + +def test_aggregate_p0_extension_requires_exact_production_cardinality( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + sigmas = (0.9, 1.0) + lengths = (2**10, 2**14, 2**18) + replicas = tuple(range(24, 40)) + grids = { + sigma: tuple(sigma + 0.125 * index for index in range(17)) for sigma in sigmas + } + protocol: dict[str, object] = { + "loop_order": ["sigma", "length", "replica"], + "lengths": list(lengths), + "replicas": list(replicas), + "sigma_entries": [ + { + "sigma_hex": sigma.hex(), + "kappas": [value.hex() for value in grids[sigma]], + } + for sigma in sigmas + ], + } + protocol["protocol_sha256"] = hashlib.sha256(_canonical_bytes(protocol)).hexdigest() + cells: list[dict[str, object]] = [] + for sigma in sigmas: + for length in lengths: + for replica in replicas: + index = len(cells) + cell_path = f"cells/{index:03d}" + cells.append( + { + "cell_index": index, + "cell_id": f"{index:03d}", + "sigma": sigma.hex(), + "length": length, + "replica": replica, + "sigma_grid_id": f"production|{sigma.hex()}", + "kappas": [value.hex() for value in grids[sigma]], + "kernel_sha256": "1" * 64, + "request_sha256": f"{index:064x}", + "rng_material_sha256": ["2" * 64] * 4, + "cell_path": cell_path, + "run_path": f"{cell_path}/run", + "manifest_path": f"{cell_path}/manifest.json", + } + ) + + class FakeSnapshot: + run_spec_payload = b"production extension run spec\n" + progress_payload = b"production extension progress\n" + + def __init__(self) -> None: + self.spec = { + "source_extension_protocol_sha256": protocol["protocol_sha256"], + "orchestration_revision": "3" * 40, + "analysis_plan_sha256": "4" * 64, + "cells": cells, + } + + def load_trajectory(self, cell_index: int) -> TrajectoryResult: + rows = np.zeros((17, 10), dtype=np.float64) + rows[:, 4] = cell_index + rows[:, 5] = cell_index + 1 + rows[:, 8] = cell_index + 2 + rows[:, 9] = cell_index % 2 + return TrajectoryResult( + request_sha256=cells[cell_index]["request_sha256"], + observables=rows, + terminal_counters=np.zeros((4, 4), dtype=np.uint32), + draw_counts=np.zeros((4, 3), dtype=np.uint64), + event_count=0, + duplicate_count=0, + hash_diagnostics=np.zeros(5, dtype=np.uint64), + ) + + class FakeSnapshotContext: + def __enter__(self) -> FakeSnapshot: + return FakeSnapshot() + + def __exit__(self, *_args: object) -> None: + return None + + monkeypatch.setattr( + pilot, + "_open_verified_pilot_analysis_snapshot", + lambda *_args, **_kwargs: FakeSnapshotContext(), + ) + + document = analysis.aggregate_p0_extension( + (tmp_path / "run_spec.json").resolve(), + protocol, + ) + + assert len(cells) == 2 * 3 * 16 + assert len(document["estimates"]) == 102 + assert all(estimate["replica_count"] == 16 for estimate in document["estimates"]) + + malformed = json.loads(json.dumps(protocol)) + malformed["replicas"].pop() + unsigned = dict(malformed) + unsigned.pop("protocol_sha256") + malformed["protocol_sha256"] = hashlib.sha256( + _canonical_bytes(unsigned) + ).hexdigest() + with pytest.raises(RuntimeError, match="2x3x16x17"): + analysis.aggregate_p0_extension( + (tmp_path / "run_spec.json").resolve(), + malformed, + ) + + +def test_aggregate_p0_groups_whole_replicas_in_canonical_order( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + path, spec = _tiny_complete_pilot(tmp_path, monkeypatch) + original_loader = pilot._load_analysis_trajectory + previous: weakref.ReferenceType[TrajectoryResult] | None = None + + def tracking_loader( + trajectory: Path, + expected: dict[str, str], + required_digest: str, + ) -> TrajectoryResult: + nonlocal previous + if previous is not None: + assert previous() is None, "more than one trajectory was retained" + result = original_loader(trajectory, expected, required_digest) + previous = weakref.ref(result) + return result + + monkeypatch.setattr(pilot, "_load_analysis_trajectory", tracking_loader) + document = analysis._aggregate_test_p0(path) + + assert analysis.OBSERVABLE_COLUMNS == OBSERVABLE_COLUMNS + assert [ + ( + estimate["sigma_hex"], + estimate["length"], + estimate["kappa_hex"], + ) + for estimate in document["estimates"] + ] == [ + ((1.0).hex(), length, kappa.hex()) + for length in (8, 16) + for kappa in (0.0, 0.25, 0.5) + ] + first = document["estimates"][0] + assert first == { + "sigma_hex": (1.0).hex(), + "length": 8, + "kappa_hex": (0.0).hex(), + "replica_count": 2, + "means": { + "s1_fraction": 2.0, + "s2_fraction": 12.0, + "q_g": 22.0, + "four_sector_crossing": 0.5, + }, + "standard_errors": { + "s1_fraction": 1.0, + "s2_fraction": 1.0, + "q_g": 1.0, + "four_sector_crossing": 0.5, + }, + "request_sha256": [ + spec["cells"][0]["request_sha256"], + spec["cells"][1]["request_sha256"], + ], + } + + +def test_aggregate_p0_binds_exact_sources_and_hashes_unsigned_document( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + path, spec = _tiny_complete_pilot(tmp_path, monkeypatch) + document = analysis._aggregate_test_p0(path) + unsigned = dict(document) + digest = unsigned.pop("analysis_document_sha256") + + assert document["schema_version"] == "challenge-194-p0-analysis-v1" + assert ( + document["p0_run_spec_sha256"] == hashlib.sha256(path.read_bytes()).hexdigest() + ) + assert ( + document["p0_progress_sha256"] + == hashlib.sha256((path.parent / "progress.json").read_bytes()).hexdigest() + ) + assert document["source_revision"] == spec["orchestration_revision"] + assert document["analysis_plan_sha256"] == spec["analysis_plan_sha256"] + assert digest == hashlib.sha256(_canonical_bytes(unsigned)).hexdigest() + + +@pytest.mark.parametrize("defect", ("missing", "duplicate")) +def test_aggregate_p0_rejects_missing_or_duplicate_replicas( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, defect: str +): + _path, spec = _tiny_complete_pilot(tmp_path, monkeypatch) + malformed = dict(spec) + if defect == "missing": + malformed["cells"] = list(spec["cells"][:-1]) + else: + malformed["protocol"] = { + **spec["protocol"], + "replicas": [0, 0], + } + with pytest.raises(RuntimeError, match=defect): + axes = analysis._validated_axes(malformed) + analysis._validate_cells(malformed, *axes) + + +@pytest.mark.parametrize( + "defect", + ("merged-trajectory-digest", "outer-manifest", "inner-progress"), +) +def test_aggregate_p0_rejects_forged_or_stale_verified_evidence( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + defect: str, +): + path, spec = _tiny_complete_pilot(tmp_path, monkeypatch) + root = path.parent + first = spec["cells"][0] + if defect == "merged-trajectory-digest": + target = root / "progress.json" + document = json.loads(target.read_text(encoding="utf-8")) + document["cells"][0]["trajectory_sha256"] = "0" * 64 + elif defect == "outer-manifest": + target = root / first["manifest_path"] + document = json.loads(target.read_text(encoding="utf-8")) + document["trajectory_sha256"] = "0" * 64 + else: + target = root / first["run_path"] / "progress.json" + document = {"schema_version": "forged-progress"} + target.write_bytes(_canonical_bytes(document)) + + with pytest.raises(RuntimeError, match="stale|corrupt|mismatch|progress"): + analysis._aggregate_test_p0(path) + + +def test_aggregate_p0_uses_retained_root_during_swap_and_restore( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + original, _ = _tiny_complete_pilot( + tmp_path, monkeypatch, name="original", value_offset=0.0 + ) + alternate, _ = _tiny_complete_pilot( + tmp_path, monkeypatch, name="alternate", value_offset=1000.0 + ) + baseline = analysis._aggregate_test_p0(original) + original_root = original.parent + alternate_root = alternate.parent + detached = tmp_path / "detached-original" + events: list[str] = [] + + def swap_and_restore(stage: str) -> None: + if stage == "snapshot-verified": + events.append(stage) + original_root.rename(detached) + alternate_root.rename(original_root) + elif stage == "snapshot-closed": + events.append(stage) + original_root.rename(alternate_root) + detached.rename(original_root) + + observed = analysis._aggregate_test_p0(original, _snapshot_hook=swap_and_restore) + + assert observed == baseline + assert events == ["snapshot-verified", "snapshot-closed"] + assert original.is_file() + assert alternate.is_file() + + +def test_aggregate_p0_uses_descriptor_progress_during_swap_and_restore( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + original, _ = _tiny_complete_pilot( + tmp_path, monkeypatch, name="original", value_offset=0.0 + ) + alternate, _ = _tiny_complete_pilot( + tmp_path, monkeypatch, name="alternate", value_offset=1000.0 + ) + baseline = analysis._aggregate_test_p0(original) + progress = original.parent / "progress.json" + saved = original.parent / "progress.saved" + events: list[str] = [] + + def swap_and_restore(stage: str) -> None: + if stage == "snapshot-verified": + events.append(stage) + progress.rename(saved) + shutil.copyfile(alternate.parent / "progress.json", progress) + elif stage == "snapshot-closed": + events.append(stage) + progress.unlink() + saved.rename(progress) + + observed = analysis._aggregate_test_p0(original, _snapshot_hook=swap_and_restore) + + assert observed == baseline + assert events == ["snapshot-verified", "snapshot-closed"] + + +def _private_snapshot_parent(tmp_path: Path) -> Path: + parent = tmp_path / "snapshots" + parent.mkdir(mode=0o700) + return parent + + +def _snapshot_window_owner( + parent_value: str, + window: str, + ready: object, + finish: object, +) -> None: + parent = Path(parent_value) + process_identity = pilot._snapshot_process_identity() + token = ("a" if window == "mkdir-before-marker" else "b") * 32 + name = pilot._snapshot_directory_name(process_identity, token) + candidate = parent / name + candidate.mkdir(mode=0o700) + if window == "marker-last-before-rmdir": + marker = candidate / pilot.PILOT_SNAPSHOT_MARKER + marker.write_bytes( + pilot._canonical_bytes( + pilot._snapshot_marker_document( + name, + token, + process_identity, + ) + ) + ) + marker.unlink() + ready.send((name, candidate.stat().st_ino)) + finish.recv() + candidate.rmdir() + ready.send("completed") + + +def _assert_active_snapshot_window_survives( + parent: Path, + window: str, +) -> None: + context = multiprocessing.get_context("fork") + owner_ready, cleaner_ready = context.Pipe() + cleaner_finish, owner_finish = context.Pipe() + owner = context.Process( + target=_snapshot_window_owner, + args=(str(parent), window, cleaner_ready, owner_finish), + ) + owner.start() + try: + assert owner_ready.poll(10), "snapshot owner did not reach cleanup window" + name, inode = owner_ready.recv() + candidate = parent / name + parent_fd = pilot._open_validated_snapshot_parent(parent) + try: + pilot._cleanup_stale_owned_snapshots(parent_fd) + finally: + pilot.os.close(parent_fd) + assert candidate.stat().st_ino == inode + cleaner_finish.send("finish") + assert owner_ready.poll(10), "snapshot owner did not complete" + assert owner_ready.recv() == "completed" + owner.join(10) + assert owner.exitcode == 0 + assert not candidate.exists() + finally: + if owner.is_alive(): + cleaner_finish.send("finish") + owner.join(10) + if owner.is_alive(): + owner.kill() + owner.join(10) + + +def test_snapshot_preflight_rejects_aggregate_over_budget_before_payload_copy( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + path, _ = _tiny_complete_pilot(tmp_path, monkeypatch) + for trajectory in path.parent.glob("cells/*/run/trajectories/*.h5"): + with trajectory.open("r+b") as stream: + stream.truncate(40 * 1024 * 1024) + copy_calls: list[str] = [] + + def forbid_copy(*_args: object, **_kwargs: object) -> None: + copy_calls.append("called") + raise AssertionError("payload copy started before aggregate preflight") + + monkeypatch.setattr(pilot, "_copy_regular_snapshot_at", forbid_copy) + with pytest.raises(RuntimeError, match="aggregate byte budget"): + analysis._aggregate_test_p0( + path, + snapshot_parent=_private_snapshot_parent(tmp_path), + ) + assert copy_calls == [] + + +def test_snapshot_preflight_rejects_extra_entry_before_payload_copy( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + path, spec = _tiny_complete_pilot(tmp_path, monkeypatch) + (path.parent / spec["cells"][0]["cell_path"] / "unknown.bin").write_bytes(b"x") + copy_calls: list[str] = [] + + def forbid_copy(*_args: object, **_kwargs: object) -> None: + copy_calls.append("called") + raise AssertionError("payload copy started before layout preflight") + + monkeypatch.setattr(pilot, "_copy_regular_snapshot_at", forbid_copy) + with pytest.raises(RuntimeError, match="unknown snapshot layout entry"): + analysis._aggregate_test_p0( + path, + snapshot_parent=_private_snapshot_parent(tmp_path), + ) + assert copy_calls == [] + + +def test_snapshot_capacity_failure_occurs_before_payload_copy( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + path, _ = _tiny_complete_pilot(tmp_path, monkeypatch) + copy_calls: list[str] = [] + + def forbid_copy(*_args: object, **_kwargs: object) -> None: + copy_calls.append("called") + raise AssertionError("payload copy started before capacity preflight") + + monkeypatch.setattr(pilot, "_copy_regular_snapshot_at", forbid_copy) + monkeypatch.setattr( + pilot.os, + "statvfs", + lambda _path: SimpleNamespace(f_bavail=0, f_frsize=4096), + ) + with pytest.raises(RuntimeError, match="snapshot filesystem capacity"): + analysis._aggregate_test_p0( + path, + snapshot_parent=_private_snapshot_parent(tmp_path), + ) + assert copy_calls == [] + assert pilot.PILOT_SNAPSHOT_SAFETY_RESERVE_BYTES > 0 + + +def test_snapshot_copy_global_counter_rejects_file_growth_race( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + path, spec = _tiny_complete_pilot(tmp_path, monkeypatch) + request = path.parent / spec["cells"][0]["run_path"] / "request.json" + parent = _private_snapshot_parent(tmp_path) + stages: list[str] = [] + + def grow_after_preflight(stage: str) -> None: + if stage == "snapshot-preflighted": + stages.append(stage) + with request.open("ab") as stream: + stream.write(b" ") + + with pytest.raises(RuntimeError, match="snapshot byte budget changed during copy"): + analysis._aggregate_test_p0( + path, + snapshot_parent=parent, + _snapshot_hook=grow_after_preflight, + ) + assert stages == ["snapshot-preflighted"] + assert list(parent.iterdir()) == [] + + +def test_snapshot_exception_removes_uniquely_owned_directory( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + path, _ = _tiny_complete_pilot(tmp_path, monkeypatch) + parent = _private_snapshot_parent(tmp_path) + + def fail_during_copy(stage: str) -> None: + if stage == "snapshot-copy-start": + raise RuntimeError("injected snapshot failure") + + with pytest.raises(RuntimeError, match="injected snapshot failure"): + analysis._aggregate_test_p0( + path, + snapshot_parent=parent, + _snapshot_hook=fail_during_copy, + ) + assert list(parent.iterdir()) == [] + + +def test_snapshot_cleanup_removes_ownership_marker_last( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + path, _ = _tiny_complete_pilot(tmp_path, monkeypatch) + parent = _private_snapshot_parent(tmp_path) + original_unlink = pilot.os.unlink + removed_names: list[str] = [] + + def tracking_unlink( + name: str, + *, + dir_fd: int | None = None, + ) -> None: + removed_names.append(name) + original_unlink(name, dir_fd=dir_fd) + + monkeypatch.setattr(pilot.os, "unlink", tracking_unlink) + analysis._aggregate_test_p0(path, snapshot_parent=parent) + + assert removed_names[-1] == pilot.PILOT_SNAPSHOT_MARKER + assert list(parent.iterdir()) == [] + + +def test_stale_cleanup_preserves_active_mkdir_before_marker_window( + tmp_path: Path, +): + parent = _private_snapshot_parent(tmp_path) + _assert_active_snapshot_window_survives(parent, "mkdir-before-marker") + + +def test_stale_cleanup_preserves_active_marker_last_before_rmdir_window( + tmp_path: Path, +): + parent = _private_snapshot_parent(tmp_path) + _assert_active_snapshot_window_survives( + parent, + "marker-last-before-rmdir", + ) + + +def test_stale_cleanup_removes_proven_dead_markerless_snapshot( + tmp_path: Path, +): + parent = _private_snapshot_parent(tmp_path) + name = pilot._snapshot_directory_name( + (2_147_483_647, f"linux-{'0' * 32}-1"), + "c" * 32, + ) + candidate = parent / name + candidate.mkdir(mode=0o700) + + parent_fd = pilot._open_validated_snapshot_parent(parent) + try: + pilot._cleanup_stale_owned_snapshots(parent_fd) + finally: + pilot.os.close(parent_fd) + + assert not candidate.exists() + + +def test_stale_cleanup_leaves_unverifiable_markerless_snapshot( + tmp_path: Path, +): + parent = _private_snapshot_parent(tmp_path) + name = pilot._snapshot_directory_name( + (pilot.os.getpid(), None), + "d" * 32, + ) + candidate = parent / name + candidate.mkdir(mode=0o700) + inode = candidate.stat().st_ino + + parent_fd = pilot._open_validated_snapshot_parent(parent) + try: + pilot._cleanup_stale_owned_snapshots(parent_fd) + finally: + pilot.os.close(parent_fd) + + assert candidate.stat().st_ino == inode + candidate.rmdir() + + +def test_pilot_estimate_is_immutable(): + estimate = analysis.PilotEstimate( + sigma=1.0, + length=8, + kappa=0.25, + replica_count=2, + means={"q_g": 0.5}, + standard_errors={"q_g": 0.1}, + request_sha256=("1" * 64, "2" * 64), + ) + with pytest.raises(FrozenInstanceError): + estimate.length = 16 + with pytest.raises(TypeError): + estimate.means["q_g"] = 1.0 + + +def test_select_p1_brackets_selects_unique_common_interval(): + values: dict[tuple[float, int, float], tuple[float, float]] = {} + kappas = (0.0, 1.0, 2.0, 4.0) + _set_selector_value( + values, 0.8, 16, kappas, (4.0, 2.0, 1.0, 0.0), (0.0, 0.1, 0.2, 0.9) + ) + _set_selector_value( + values, 0.8, 32, kappas, (3.0, 1.0, 2.0, 3.0), (0.0, 0.2, 0.8, 1.0) + ) + + selected = analysis.select_p1_brackets( + _selector_document(sigmas=(0.8,), values=values) + ) + + assert selected["requires_p0_extension"] is False + bracket = selected["brackets"][0] + assert bracket["sigma_hex"] == (0.8).hex() + assert bracket["lower_kappa_hex"] == (1.0).hex() + assert bracket["upper_kappa_hex"] == (2.0).hex() + assert bracket["lengths"] == [16, 32] + assert bracket["estimator_evidence"]["q_g"]["marked"] is True + assert bracket["estimator_evidence"]["four_sector_crossing"]["marked"] is True + assert bracket["tie_break"] == { + "rule": "narrowest_interval_then_lower_coupling", + "candidate_count": 1, + "selected_width_hex": (1.0).hex(), + } + + +def test_select_p1_brackets_breaks_common_interval_ties_deterministically(): + values: dict[tuple[float, int, float], tuple[float, float]] = {} + kappas = (0.0, 1.0, 3.0, 5.0, 8.0) + for length, q_g in ( + (16, (9.0, 3.0, 1.0, 3.0, 3.0)), + (32, (8.0, 1.0, 2.0, 1.0, 2.0)), + ): + _set_selector_value( + values, + 0.8, + length, + kappas, + q_g, + (0.0, 0.2, 0.8, 0.2, 0.8), + ) + + selected = analysis.select_p1_brackets( + _selector_document(sigmas=(0.8,), kappas=kappas, values=values) + ) + + bracket = selected["brackets"][0] + assert bracket["lower_kappa_hex"] == (1.0).hex() + assert bracket["upper_kappa_hex"] == (3.0).hex() + assert bracket["tie_break"]["candidate_count"] == 2 + + +def test_select_p1_brackets_requests_extension_without_common_interval(): + selected = analysis.select_p1_brackets(_selector_document(sigmas=(0.8,), values={})) + + assert selected["requires_p0_extension"] is True + assert selected["brackets"] == [ + { + "sigma_hex": (0.8).hex(), + "status": "requires_p0_extension", + "reason": "no_nonzero_interval_marked_by_both_estimators", + "lengths": [16, 32], + } + ] + + +def test_select_p1_brackets_uses_maximum_control_slope(): + values: dict[tuple[float, int, float], tuple[float, float]] = {} + kappas = (0.0, 1.0, 3.0, 5.0) + _set_selector_value( + values, + 1.1, + 32, + kappas, + (1.0, 1.0, 1.0, 1.0), + (0.0, 0.1, 0.9, 1.0), + ) + + selected = analysis.select_p1_brackets( + _selector_document(sigmas=(1.1,), kappas=kappas, values=values) + ) + + bracket = selected["brackets"][0] + assert bracket["purpose"] == "crossover_refinement" + assert bracket["lower_kappa_hex"] == (1.0).hex() + assert bracket["upper_kappa_hex"] == (3.0).hex() + assert bracket["estimator_evidence"]["absolute_slope_hex"] == (0.4).hex() + assert bracket["tie_break"]["rule"] == "maximum_absolute_slope_then_lower_coupling" + + +def test_combined_selector_uses_per_sigma_axes_and_preserves_control_windows(): + p0, extension_analysis, combined = _combined_selector_document() + + original = analysis.select_p1_brackets(p0) + first = _select_test_combined(combined, p0, extension_analysis) + second = _select_test_combined(combined, p0, extension_analysis) + + assert first["schema_version"] == analysis.COMBINED_BRACKET_SCHEMA + assert ( + first["source_analysis_document_sha256"] == combined["analysis_document_sha256"] + ) + assert _canonical_bytes(first) == _canonical_bytes(second) + assert first["requires_p0_extension"] is False + assert [entry["status"] for entry in first["brackets"]] == ["selected"] * 4 + for index in (1, 2): + assert float.fromhex(first["brackets"][index]["lower_kappa_hex"]) > 0.0 + for index in (0, 3): + assert ( + first["brackets"][index]["lower_kappa_hex"], + first["brackets"][index]["upper_kappa_hex"], + ) == ( + original["brackets"][index]["lower_kappa_hex"], + original["brackets"][index]["upper_kappa_hex"], + ) + assert [ + ( + first["brackets"][index]["lower_kappa_hex"], + first["brackets"][index]["upper_kappa_hex"], + ) + for index in (0, 3) + ] == [ + ("0x1.f400000000000p-2", "0x1.3880000000000p-1"), + ("0x1.312d000000000p+0", "0x1.7d78400000000p+0"), + ] + assert [len(entry["kappas"]) for entry in combined["sigma_entries"]] == [ + 16, + 31, + 31, + 16, + ] + + +def test_combined_selector_fails_closed_when_one_sigma_remains_unresolved(): + p0, extension_analysis, combined = _combined_selector_document(unresolved_sigma=1.0) + + brackets = _select_test_combined(combined, p0, extension_analysis) + + assert brackets["schema_version"] == analysis.COMBINED_BRACKET_SCHEMA + assert brackets["requires_p0_extension"] is True + assert brackets["brackets"][2]["status"] == "requires_p0_extension" + with pytest.raises(RuntimeError, match="P0 extension required.*1\\.0"): + _build_test_combined_p1(combined, brackets, p0, extension_analysis) + + +def test_p1_accepts_combined_only_with_selected_v2_brackets(): + p0, extension_analysis, combined = _combined_selector_document() + brackets = _select_test_combined(combined, p0, extension_analysis) + + protocol = _build_test_combined_p1(combined, brackets, p0, extension_analysis) + + assert ( + protocol["source_analysis_document_sha256"] + == combined["analysis_document_sha256"] + ) + assert ( + protocol["source_bracket_document_sha256"] + == brackets["bracket_document_sha256"] + ) + assert protocol["grid_namespace"] == "pilot-p1-v1" + assert protocol["master_seed"] == 19_420_261_729 + assert protocol["replicas"] == list(range(8, 24)) + assert len(protocol["cells"]) == 4 * 3 * 16 + assert all(len(entry["kappas"]) == 9 for entry in protocol["sigma_entries"]) + + legacy = json.loads(json.dumps(brackets)) + legacy["schema_version"] = analysis.BRACKET_SCHEMA + unsigned = dict(legacy) + unsigned.pop("bracket_document_sha256") + legacy["bracket_document_sha256"] = hashlib.sha256( + _canonical_bytes(unsigned) + ).hexdigest() + with pytest.raises(RuntimeError, match="schema version"): + _build_test_combined_p1(combined, legacy, p0, extension_analysis) + + forged = json.loads(json.dumps(brackets)) + forged["brackets"][0]["lower_kappa_hex"] = (0.5).hex() + unsigned = dict(forged) + unsigned.pop("bracket_document_sha256") + forged["bracket_document_sha256"] = hashlib.sha256( + _canonical_bytes(unsigned) + ).hexdigest() + with pytest.raises(RuntimeError, match="selector output"): + _build_test_combined_p1(combined, forged, p0, extension_analysis) + + +def test_combined_v2_requires_sources_for_selection_and_direct_build(): + _p0, _extension_analysis, combined = _combined_selector_document() + + with pytest.raises(RuntimeError, match="source validation"): + analysis.select_p1_brackets(combined) + with pytest.raises(RuntimeError, match="source validation"): + analysis.build_p1_protocol(combined) + + +@pytest.mark.parametrize("defect", ("missing-nine-fields", "zeroed-source-hash")) +def test_combined_selector_rejects_resigned_provenance_bypass(defect: str): + p0, extension_analysis, combined = _combined_selector_document() + forged = json.loads(json.dumps(combined)) + if defect == "missing-nine-fields": + for field in ( + "source_p0_analysis_document_sha256", + "source_extension_analysis_document_sha256", + "p0_run_spec_sha256", + "p0_progress_sha256", + "extension_run_spec_sha256", + "extension_progress_sha256", + "p0_source_revision", + "extension_source_revision", + "observable_columns", + ): + forged.pop(field) + else: + forged["source_p0_analysis_document_sha256"] = "0" * 64 + _sign(forged) + + with pytest.raises(RuntimeError, match="fields|recomputation"): + _select_test_combined(forged, p0, extension_analysis) + + +@pytest.mark.parametrize("source_defect", ("swapped-types", "cross-generation")) +def test_combined_selector_rejects_wrong_source_documents(source_defect: str): + p0, extension_analysis, combined = _combined_selector_document() + alternate_p0, alternate_extension, _alternate = _combined_selector_document( + blocked_interval_offset=1 + ) + supplied_p0, supplied_extension = ( + (extension_analysis, p0) + if source_defect == "swapped-types" + else (alternate_p0, alternate_extension) + ) + + with pytest.raises(RuntimeError): + _select_test_combined(combined, supplied_p0, supplied_extension) + + +def test_combined_build_rejects_rebound_cross_generation_brackets(): + p0, extension_analysis, combined = _combined_selector_document() + alternate_p0, alternate_extension, alternate = _combined_selector_document( + blocked_interval_offset=1 + ) + alternate_brackets = _select_test_combined( + alternate, alternate_p0, alternate_extension + ) + rebound = json.loads(json.dumps(alternate_brackets)) + rebound["source_analysis_document_sha256"] = combined["analysis_document_sha256"] + unsigned = dict(rebound) + unsigned.pop("bracket_document_sha256") + rebound["bracket_document_sha256"] = hashlib.sha256( + _canonical_bytes(unsigned) + ).hexdigest() + + with pytest.raises(RuntimeError, match="selector output"): + _build_test_combined_p1(combined, rebound, p0, extension_analysis) + + +def test_combined_direct_build_rejects_resigned_forged_brackets(): + p0, extension_analysis, combined = _combined_selector_document() + brackets = _select_test_combined(combined, p0, extension_analysis) + forged = json.loads(json.dumps(brackets)) + target = forged["brackets"][1] + lower = float.fromhex(target["lower_kappa_hex"]) + upper = float.fromhex(target["upper_kappa_hex"]) + target["lower_kappa_hex"] = (lower + (upper - lower) / 4.0).hex() + unsigned = dict(forged) + unsigned.pop("bracket_document_sha256") + forged["bracket_document_sha256"] = hashlib.sha256( + _canonical_bytes(unsigned) + ).hexdigest() + + with pytest.raises(RuntimeError, match="selector output"): + _build_test_combined_p1(combined, forged, p0, extension_analysis) + + +@pytest.mark.parametrize( + ("defect", "match"), + ( + ("zero-only", "zero-coupling"), + ("reordered", "canonical coupling order"), + ("nan", "finite"), + ("missing-largest", "largest-size estimates"), + ), +) +def test_select_p1_brackets_rejects_malformed_evidence(defect: str, match: str): + values: dict[tuple[float, int, float], tuple[float, float]] = {} + kappas = (0.0, 1.0, 2.0, 4.0) + if defect == "zero-only": + _set_selector_value( + values, + 0.8, + 16, + kappas, + (2.0, 1.0, 1.0, 1.0), + (0.2, 0.8, 0.8, 0.8), + ) + _set_selector_value( + values, + 0.8, + 32, + kappas, + (1.0, 2.0, 2.0, 2.0), + (0.2, 0.8, 0.8, 0.8), + ) + document = _selector_document(sigmas=(0.8,), values=values) + estimates = document["estimates"] + assert isinstance(estimates, list) + if defect == "reordered": + estimates[5], estimates[6] = estimates[6], estimates[5] + elif defect == "nan": + estimates[-1]["means"]["q_g"] = float("nan") + elif defect == "missing-largest": + estimates.pop() + if defect not in ("zero-only", "nan"): + unsigned = dict(document) + unsigned.pop("analysis_document_sha256") + document["analysis_document_sha256"] = hashlib.sha256( + _canonical_bytes(unsigned) + ).hexdigest() + + with pytest.raises(RuntimeError, match=match): + analysis.select_p1_brackets(document) + + +def _selected_bracket_document( + source: dict[str, object], + *, + requires_extension: bool = False, +) -> dict[str, object]: + brackets: list[dict[str, object]] = [] + for index, sigma in enumerate((0.8, 0.9, 1.0, 1.1)): + if requires_extension and sigma == 1.0: + brackets.append( + { + "sigma_hex": sigma.hex(), + "status": "requires_p0_extension", + "reason": "no_nonzero_interval_marked_by_both_estimators", + "lengths": [16, 32], + } + ) + continue + brackets.append( + { + "sigma_hex": sigma.hex(), + "status": "selected", + "purpose": ( + "transition_refinement" if sigma <= 1.0 else "crossover_refinement" + ), + "lower_kappa_hex": float(index + 1).hex(), + "upper_kappa_hex": float(index + 2).hex(), + "lengths": [16, 32], + "estimator_evidence": {"synthetic": True}, + "tie_break": {"rule": "synthetic"}, + } + ) + document: dict[str, object] = { + "schema_version": analysis.BRACKET_SCHEMA, + "source_analysis_document_sha256": source["analysis_document_sha256"], + "requires_p0_extension": requires_extension, + "brackets": brackets, + } + document["bracket_document_sha256"] = hashlib.sha256( + _canonical_bytes(document) + ).hexdigest() + return document + + +def test_build_p1_protocol_freezes_grids_requests_and_rng_assignments(): + source = _selector_document( + sigmas=(0.8, 0.9, 1.0, 1.1), + lengths=(8, 16, 32), + ) + brackets = _selected_bracket_document(source) + + protocol = analysis.build_p1_protocol(source, brackets) + + assert protocol["schema_version"] == analysis.P1_PROTOCOL_SCHEMA + assert ( + protocol["source_analysis_document_sha256"] + == source["analysis_document_sha256"] + ) + assert ( + protocol["source_bracket_document_sha256"] + == brackets["bracket_document_sha256"] + ) + assert protocol["phase"] == "pilot" + assert protocol["lengths"] == [8, 16, 32] + assert protocol["replicas"] == list(range(8, 24)) + assert len(protocol["sigma_entries"]) == 4 + for entry in protocol["sigma_entries"]: + grid = [float.fromhex(value) for value in entry["kappas"]] + assert len(grid) == 9 + assert grid == sorted(grid) + assert len(set(entry["kappas"])) == 9 + assert entry["kappas"][0] == entry["lower_kappa_hex"] + assert entry["kappas"][-1] == entry["upper_kappa_hex"] + + cells = protocol["cells"] + assert len(cells) == 4 * 3 * 16 + assert [cell["cell_index"] for cell in cells] == list(range(len(cells))) + assert all(cell["replica"] not in range(8) for cell in cells) + assert len({cell["request_sha256"] for cell in cells}) == len(cells) + stream_hashes = [ + stream_hash for cell in cells for stream_hash in cell["rng_material_sha256"] + ] + assert len(set(stream_hashes)) == len(stream_hashes) + assert all( + cell["cell_path"] == f"cells/{cell['cell_id']}" + and cell["run_path"] == f"{cell['cell_path']}/run" + and cell["manifest_path"] == f"{cell['cell_path']}/manifest.json" + for cell in cells + ) + unsigned = dict(protocol) + digest = unsigned.pop("protocol_sha256") + assert digest == hashlib.sha256(_canonical_bytes(unsigned)).hexdigest() + analysis.validate_p1_protocol(source, protocol) + + +def test_build_p1_protocol_rejects_required_p0_extension(): + source = _selector_document( + sigmas=(0.8, 0.9, 1.0, 1.1), + lengths=(8, 16, 32), + ) + brackets = _selected_bracket_document(source, requires_extension=True) + + with pytest.raises(RuntimeError, match="P0 extension required.*1\\.0"): + analysis.build_p1_protocol(source, brackets) + + +def test_original_real_p0_bracket_bytes_hash_and_refusal_are_unchanged(): + source_path = ( + Path(__file__).resolve().parents[6] / "results/challenge-194/p0_analysis.json" + ) + source = extension.load_frozen_p0_analysis(source_path) + + first = analysis.select_p1_brackets(source) + second = analysis.select_p1_brackets(source) + + assert _canonical_bytes(first) == _canonical_bytes(second) + assert first["bracket_document_sha256"] == ( + "fb3df666044bf9531443fc00c5c2c2d489512b4162864b3a92ffc2e756832403" + ) + assert first["requires_p0_extension"] is True + assert [ + float.fromhex(entry["sigma_hex"]) + for entry in first["brackets"] + if entry["status"] == "requires_p0_extension" + ] == [0.9, 1.0] diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py new file mode 100644 index 000000000..bcb874cd7 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_extension.py @@ -0,0 +1,1093 @@ +from __future__ import annotations + +import copy +import hashlib +import importlib.util +import json +import os +import shutil +import subprocess +from pathlib import Path + +import pytest + +import long_range_percolation.pilot_extension as extension +from long_range_percolation import pilot + +ROOT = Path(__file__).resolve().parents[1] +RUN_PILOT = ROOT / "scripts" / "run_pilot.py" +RUN_PILOT_SPEC = importlib.util.spec_from_file_location("run_pilot_cli", RUN_PILOT) +assert RUN_PILOT_SPEC is not None and RUN_PILOT_SPEC.loader is not None +run_pilot_cli = importlib.util.module_from_spec(RUN_PILOT_SPEC) +RUN_PILOT_SPEC.loader.exec_module(run_pilot_cli) +P0_ANALYSIS = ( + Path(__file__).resolve().parents[6] / "results/challenge-194/p0_analysis.json" +) +P0_EVIDENCE_ROOT = P0_ANALYSIS.parent / "pilot-p0-739880d" +EXPECTED_SPANS = { + (0.9).hex(): ( + (4, 7), + (0.48828125).hex(), + float.fromhex("0x1.312d000000000p+0").hex(), + ), + (1.0).hex(): ( + (5, 9), + float.fromhex("0x1.3880000000000p-1").hex(), + float.fromhex("0x1.dcd6500000000p+0").hex(), + ), +} +EXPECTED_GRIDS = { + (0.9).hex(): [ + "0x1.f400000000000p-2", + "0x1.1085a00000000p-1", + "0x1.270b400000000p-1", + "0x1.3d90e00000000p-1", + "0x1.5416800000000p-1", + "0x1.6a9c200000000p-1", + "0x1.8121c00000000p-1", + "0x1.97a7600000000p-1", + "0x1.ae2d000000000p-1", + "0x1.c4b2a00000000p-1", + "0x1.db38400000000p-1", + "0x1.f1bde00000000p-1", + "0x1.0421c00000000p+0", + "0x1.0f64900000000p+0", + "0x1.1aa7600000000p+0", + "0x1.25ea300000000p+0", + "0x1.312d000000000p+0", + ], + (1.0).hex(): [ + "0x1.3880000000000p-1", + "0x1.6092ca0000000p-1", + "0x1.88a5940000000p-1", + "0x1.b0b85e0000000p-1", + "0x1.d8cb280000000p-1", + "0x1.006ef90000000p+0", + "0x1.14785e0000000p+0", + "0x1.2881c30000000p+0", + "0x1.3c8b280000000p+0", + "0x1.50948d0000000p+0", + "0x1.649df20000000p+0", + "0x1.78a7570000000p+0", + "0x1.8cb0bc0000000p+0", + "0x1.a0ba210000000p+0", + "0x1.b4c3860000000p+0", + "0x1.c8cceb0000000p+0", + "0x1.dcd6500000000p+0", + ], +} + + +def _source() -> dict[str, object]: + return json.loads(P0_ANALYSIS.read_text(encoding="utf-8")) + + +def _extension_protocol_fixture() -> dict[str, object]: + return extension.build_p0_extension_protocol(_source(), P0_EVIDENCE_ROOT) + + +def _rehash(protocol: dict[str, object]) -> None: + unsigned = dict(protocol) + unsigned.pop("protocol_sha256", None) + protocol["protocol_sha256"] = hashlib.sha256( + extension._canonical_bytes(unsigned) + ).hexdigest() + + +def test_extension_wrapper_has_exact_resources_and_task_map(): + text = (ROOT / "scripts/pilot_extension_array_slurm.sh").read_text() + assert "#SBATCH --cpus-per-task=1" in text + assert "#SBATCH --mem=1800M" in text + assert "#SBATCH --time=00:40:00" in text + assert "^([1-9]|[1-8][0-9]|9[0-6])$" in text + assert "CELL_INDEX=$((SLURM_ARRAY_TASK_ID - 1))" in text + assert "scripts/run_pilot.py run-cell" in text + + +def test_build_extension_spec_requires_protocol_and_exact_output_path(): + parser = run_pilot_cli._parser() + args = parser.parse_args( + [ + "build-extension-spec", + "--protocol", + "/tmp/p0_extension_v1_protocol.json", + "--validation-report", + "/tmp/report.json", + "--analysis", + "/tmp/p0_analysis.json", + "--p0-evidence-root", + "/tmp/pilot-p0-739880d", + "--output-root", + "/tmp/pilot-p0-extension-v1", + "--run-spec", + "/tmp/pilot-p0-extension-v1/run_spec.json", + ] + ) + assert args.command == "build-extension-spec" + + +def test_build_extension_spec_rejects_mismatched_run_spec_path( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], +): + output_root = tmp_path / "extension" + result = run_pilot_cli.main( + [ + "build-extension-spec", + "--protocol", + str(tmp_path / "protocol.json"), + "--validation-report", + str(tmp_path / "report.json"), + "--analysis", + str(tmp_path / "p0_analysis.json"), + "--p0-evidence-root", + str(tmp_path / "pilot-p0-739880d"), + "--output-root", + str(output_root), + "--run-spec", + str(tmp_path / "wrong-run_spec.json"), + ] + ) + assert result == 1 + assert ( + "--run-spec must equal <output-root>/run_spec.json" in capsys.readouterr().err + ) + assert not output_root.exists() + + +def test_build_extension_spec_requires_explicit_analysis_and_evidence_root(): + with pytest.raises(SystemExit): + run_pilot_cli._parser().parse_args( + [ + "build-extension-spec", + "--protocol", + "/tmp/protocol.json", + "--validation-report", + "/tmp/report.json", + "--output-root", + "/tmp/extension", + "--run-spec", + "/tmp/extension/run_spec.json", + ] + ) + + +def _run_extension_wrapper( + tmp_path: Path, + task_id: str, + *, + extra_env: dict[str, str] | None = None, + command: str | Path | None = None, +) -> tuple[subprocess.CompletedProcess[str], Path]: + repository = tmp_path / "repo" + runner = ( + repository + / "tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py" + ) + runner.parent.mkdir(parents=True) + runner.write_text("# controlled runner placeholder\n", encoding="utf-8") + run_spec = tmp_path / "run_spec.json" + run_spec.write_text("{}\n", encoding="utf-8") + fake_python = tmp_path / "fake-python" + fake_python.write_text( + "#!/bin/bash\n" + "printf 'ARGS='\n" + "printf '<%s>' \"$@\"\n" + "printf '\\nLAUNCHER=%s\\n' \"$0\"\n" + "printf '\\nCACHE=%s\\n' \"${NUMBA_CACHE_DIR}\"\n" + "printf 'NUMBA_DISABLE_JIT=%s\\n' \"${NUMBA_DISABLE_JIT}\"\n" + "printf 'NUMBA_NUM_THREADS=%s\\n' \"${NUMBA_NUM_THREADS}\"\n" + "printf 'OMP_NUM_THREADS=%s\\n' \"${OMP_NUM_THREADS}\"\n" + "printf 'PYTHONPATH=%s\\n' \"${PYTHONPATH}\"\n" + "printf 'PYTHONHOME_SET=%s\\n' \"${PYTHONHOME+x}\"\n" + "printf 'NUMBA_CPU_NAME_SET=%s\\n' \"${NUMBA_CPU_NAME+x}\"\n" + "printf 'LD_LIBRARY_PATH_SET=%s\\n' \"${LD_LIBRARY_PATH+x}\"\n", + encoding="utf-8", + ) + fake_python.chmod(0o755) + cache_root = tmp_path / "node" + cache_root.mkdir() + environment = { + **os.environ, + "HARNESS_RUN_SPEC": str(run_spec), + "HARNESS_ENTRYPOINT": str(repository), + "HARNESS_COMMAND": str(fake_python if command is None else command), + "SLURM_ARRAY_TASK_ID": task_id, + "SLURM_ARRAY_JOB_ID": "991", + "SLURM_CPUS_PER_TASK": "1", + "SLURM_TMPDIR": str(cache_root), + **(extra_env or {}), + } + wrapper = ROOT / "scripts/pilot_extension_array_slurm.sh" + result = subprocess.run( + ["bash", str(wrapper)], + cwd=tmp_path, + env=environment, + check=False, + capture_output=True, + text=True, + ) + return result, cache_root + + +def _run_extension_build_wrapper( + tmp_path: Path, + *, + command: str | Path | None = None, +) -> tuple[subprocess.CompletedProcess[str], Path]: + repository = tmp_path / "repo" + scripts = repository / "tracks/qmc/solutions/frustration-free/challenge-194/scripts" + scripts.mkdir(parents=True) + (scripts / "analyze_pilot.py").write_text("# controlled\n", encoding="utf-8") + (scripts / "run_pilot.py").write_text("# controlled\n", encoding="utf-8") + results = tmp_path / "results" + results.mkdir() + analysis = results / "p0_analysis.json" + analysis.write_bytes(P0_ANALYSIS.read_bytes()) + evidence_root = results / "pilot-p0-739880d" + evidence_root.mkdir() + for name in ("run_spec.json", "progress.json"): + shutil.copyfile(P0_EVIDENCE_ROOT / name, evidence_root / name) + validation_report = results / "validation-prod-877ab93/report/report.json" + validation_report.parent.mkdir(parents=True) + validation_report.write_text("{}\n", encoding="utf-8") + fake_python = tmp_path / "fake-python" + fake_python.write_text( + "#!/bin/bash\n" + "printf 'CALL='\n" + "printf '<%s>' \"$@\"\n" + "printf '\\nLAUNCHER=%s\\n' \"$0\"\n", + encoding="utf-8", + ) + fake_python.chmod(0o755) + cache_root = tmp_path / "node" + cache_root.mkdir() + result = subprocess.run( + ["bash", str(ROOT / "scripts/pilot_extension_build_slurm.sh")], + cwd=tmp_path, + env={ + **os.environ, + "HARNESS_RUN_SPEC": str(analysis), + "HARNESS_ENTRYPOINT": str(repository), + "HARNESS_COMMAND": str(fake_python if command is None else command), + "SLURM_JOB_ID": "992", + "SLURM_CPUS_PER_TASK": "1", + "SLURM_TMPDIR": str(cache_root), + }, + check=False, + capture_output=True, + text=True, + ) + return result, validation_report + + +@pytest.mark.parametrize(("task_id", "cell_index"), (("1", "0"), ("96", "95"))) +def test_extension_wrapper_executes_exact_cell_index( + tmp_path: Path, + task_id: str, + cell_index: str, +): + result, _ = _run_extension_wrapper(tmp_path, task_id) + assert result.returncode == 0, result.stderr + assert ( + f"ARGS=<scripts/run_pilot.py><run-cell><--run-spec>" + f"<{tmp_path / 'run_spec.json'}><--cell-index><{cell_index}>" + ) in result.stdout + + +@pytest.mark.parametrize( + "task_id", + ( + "0", + "97", + "18446744073709551617", + "01", + "+1", + "-1", + " 1", + "1 ", + "1x", + ), +) +def test_extension_wrapper_rejects_noncanonical_or_out_of_range_task_ids( + tmp_path: Path, + task_id: str, +): + result, _ = _run_extension_wrapper(tmp_path, task_id) + assert result.returncode == 64 + assert "ARGS=" not in result.stdout + + +def test_extension_wrapper_sanitizes_hostile_environment(tmp_path: Path): + result, _ = _run_extension_wrapper( + tmp_path, + "1", + extra_env={ + "NUMBA_DISABLE_JIT": "1", + "NUMBA_NUM_THREADS": "99", + "NUMBA_CPU_NAME": "hostile", + "OMP_NUM_THREADS": "99", + "PYTHONHOME": "/hostile/home", + "PYTHONPATH": "/hostile/path", + "LD_LIBRARY_PATH": "/hostile/lib", + }, + ) + assert result.returncode == 0, result.stderr + assert "NUMBA_DISABLE_JIT=0" in result.stdout + assert "NUMBA_NUM_THREADS=1" in result.stdout + assert "OMP_NUM_THREADS=1" in result.stdout + assert ( + f"PYTHONPATH={tmp_path / 'repo'}/tracks/qmc/solutions/frustration-free/challenge-194/src" + in result.stdout + ) + lines = result.stdout.splitlines() + assert "PYTHONHOME_SET=" in lines + assert "NUMBA_CPU_NAME_SET=" in lines + assert "LD_LIBRARY_PATH_SET=" in lines + + +@pytest.mark.parametrize( + ("name", "value"), + ( + ("HARNESS_RUN_SPEC", "run_spec.json"), + ("HARNESS_ENTRYPOINT", "{repo}/./"), + ("HARNESS_COMMAND", "{python}/./fake-python"), + ), +) +def test_extension_wrapper_rejects_noncanonical_harness_paths( + tmp_path: Path, + name: str, + value: str, +): + replacement = value.format( + repo=tmp_path / "repo", + python=tmp_path, + ) + result, _ = _run_extension_wrapper( + tmp_path, + "1", + extra_env={name: replacement}, + ) + assert result.returncode == 66 + assert "ARGS=" not in result.stdout + + +def test_extension_wrapper_creates_unique_private_empty_cache(tmp_path: Path): + first, cache_root = _run_extension_wrapper(tmp_path, "1") + assert first.returncode == 0, first.stderr + cache = cache_root / "challenge-194-p0-extension-991-1" + assert cache.is_dir() + assert cache.stat().st_mode & 0o777 == 0o700 + assert list(cache.iterdir()) == [] + second = subprocess.run( + ["bash", str(ROOT / "scripts/pilot_extension_array_slurm.sh")], + cwd=tmp_path, + env={ + **os.environ, + "HARNESS_RUN_SPEC": str(tmp_path / "run_spec.json"), + "HARNESS_ENTRYPOINT": str(tmp_path / "repo"), + "HARNESS_COMMAND": str(tmp_path / "fake-python"), + "SLURM_ARRAY_TASK_ID": "1", + "SLURM_ARRAY_JOB_ID": "991", + "SLURM_CPUS_PER_TASK": "1", + "SLURM_TMPDIR": str(cache_root), + }, + check=False, + capture_output=True, + text=True, + ) + assert second.returncode == 73 + assert "ARGS=" not in second.stdout + + +@pytest.mark.parametrize("wrapper_kind", ("array", "build")) +def test_extension_wrappers_preserve_venv_final_symlink_launcher( + tmp_path: Path, + wrapper_kind: str, +): + launcher = tmp_path / ".venv/bin/python" + launcher.parent.mkdir(parents=True) + launcher.symlink_to(Path("../../fake-python")) + if wrapper_kind == "array": + result, _ = _run_extension_wrapper(tmp_path, "1", command=launcher) + else: + result, _ = _run_extension_build_wrapper(tmp_path, command=launcher) + + assert launcher.is_symlink() + assert result.returncode == 0, result.stderr + assert f"LAUNCHER={launcher}" in result.stdout + + +def _invalid_python_candidate(tmp_path: Path, kind: str) -> str | Path: + candidate = tmp_path / "invalid-python" + if kind == "relative": + return "relative/python" + if kind == "missing": + return candidate + if kind == "directory": + candidate.mkdir() + return candidate + if kind == "non-executable": + candidate.write_text("#!/bin/bash\nexit 0\n", encoding="utf-8") + candidate.chmod(0o644) + return candidate + candidate.write_text("#!/bin/bash\nexit 0\n", encoding="utf-8") + candidate.chmod(0o755) + if kind == "lexically-noncanonical": + return f"{tmp_path}/./invalid-python" + parent = tmp_path / "parent" + parent.mkdir() + return f"{parent}/../invalid-python" + + +@pytest.mark.parametrize("wrapper_kind", ("array", "build")) +@pytest.mark.parametrize( + "kind", + ( + "relative", + "missing", + "non-executable", + "directory", + "lexically-noncanonical", + "unsafe-parent-component", + ), +) +def test_extension_wrappers_reject_invalid_python_candidates( + tmp_path: Path, + wrapper_kind: str, + kind: str, +): + command = _invalid_python_candidate(tmp_path, kind) + if wrapper_kind == "array": + result, _ = _run_extension_wrapper(tmp_path, "1", command=command) + else: + result, _ = _run_extension_build_wrapper(tmp_path, command=command) + + assert result.returncode == 66 + assert "ARGS=" not in result.stdout + assert "CALL=" not in result.stdout + + +def test_extension_build_wrapper_dispatches_approved_validation_package( + tmp_path: Path, +): + result, validation_report = _run_extension_build_wrapper(tmp_path) + assert result.returncode == 0, result.stderr + assert f"<--validation-report><{validation_report}>" in result.stdout + assert ( + result.stdout.count( + f"<--p0-evidence-root><{tmp_path / 'results/pilot-p0-739880d'}>" + ) + == 2 + ) + assert f"<--analysis><{tmp_path / 'results/p0_analysis.json'}>" in result.stdout + assert "validation-prod-fd0aa31-compute" not in result.stdout + + +def test_extension_run_spec_is_bound_and_p0_loader_stays_strict(tmp_path: Path): + protocol = _extension_protocol_fixture() + run_spec = pilot._write_test_extension_run_spec( + tmp_path / "extension", protocol=protocol + ) + loaded = pilot.load_p0_extension_run_spec( + run_spec, verify_current_environment=False + ) + assert loaded["schema_version"] == extension.EXTENSION_RUN_SPEC_SCHEMA + assert loaded["source_extension_protocol_sha256"] == protocol["protocol_sha256"] + assert loaded["cells"] == protocol["cells"] + with pytest.raises(RuntimeError, match="P0 run spec"): + pilot.load_pilot_run_spec(run_spec, verify_current_environment=False) + + +def test_extension_small_cell_restart_and_merge_use_extension_progress( + tmp_path: Path, +): + run_spec = pilot._write_test_extension_run_spec(tmp_path / "extension", tiny=True) + first = pilot._run_test_registered_pilot_cell(run_spec, 0) + second = pilot._run_test_registered_pilot_cell(run_spec, 0) + assert first == second + merged = pilot._merge_test_registered_pilot_progress(run_spec) + assert merged["schema_version"] == extension.EXTENSION_PROGRESS_SCHEMA + + +def test_extension_run_spec_has_only_bound_outer_fields(tmp_path: Path): + protocol = _extension_protocol_fixture() + path = pilot._write_test_extension_run_spec( + tmp_path / "extension", protocol=protocol + ) + document = json.loads(path.read_text()) + assert set(document) == { + "schema_version", + "artifact_root", + "protocol", + "cells", + "cell_count", + "source_extension_protocol_sha256", + "source_p0_analysis_document_sha256", + "design_sha256", + "correctness_report_sha256", + "correctness_run_spec_sha256", + "correctness_approval_registry_sha256", + "correctness_approval_revision", + "validation_source_revision", + "validated_engine_modules", + "validated_engine_sha256", + "validation_runtime_capability_sha256", + "orchestration_revision", + "clean_tree", + "uv_lock_sha256", + "runtime_capability", + "runtime_capability_sha256", + "analysis_plan_sha256", + "rng_assignment_sha256", + "capability_waiver", + "merged_progress_path", + "run_spec_sha256", + } + assert "cells" not in document["protocol"] + assert document["protocol"]["protocol_sha256"] == protocol["protocol_sha256"] + assert document["cell_count"] == len(document["cells"]) == 96 + assert all( + not Path(cell[field]).is_absolute() and ".." not in Path(cell[field]).parts + for cell in document["cells"] + for field in ("cell_path", "run_path", "manifest_path") + ) + + +def test_public_extension_builder_binds_approved_evidence( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + protocol = _extension_protocol_fixture() + approval = pilot._load_approval_registry() + modules = pilot._scientific_hashes() + monkeypatch.setattr( + pilot, + "_current_source", + lambda **_: { + "source_revision": protocol["source_revision"], + "clean_tree": True, + "provenance_error": None, + }, + ) + monkeypatch.setattr( + pilot, + "_verified_correctness", + lambda _path: { + "correctness_report_sha256": approval["report_sha256"], + "correctness_run_spec_sha256": approval["run_spec_sha256"], + "correctness_approval_registry_sha256": pilot._approval_registry_digest(), + "validation_source_revision": approval["validation_source_revision"], + "validated_engine_modules": modules, + "validated_engine_sha256": approval["scientific_engine_sha256"], + "validation_runtime_capability_sha256": "3" * 64, + }, + ) + output_root = (tmp_path / "extension").resolve() + clean_checkout = tmp_path / "clean-checkout" + clean_checkout.mkdir() + monkeypatch.setattr( + extension, + "_p0_run_spec_path", + lambda: clean_checkout / "results/missing/run_spec.json", + raising=False, + ) + monkeypatch.setattr( + extension, + "_p0_progress_path", + lambda: clean_checkout / "results/missing/progress.json", + raising=False, + ) + document = pilot.build_p0_extension_run_spec( + output_root, + (tmp_path / "correctness" / "report.json").resolve(), + protocol, + _source(), + P0_EVIDENCE_ROOT, + ) + assert not (clean_checkout / "results").exists() + assert document["cells"] == protocol["cells"] + assert document["design_sha256"] == protocol["design_sha256"] + assert document["correctness_report_sha256"] == approval["report_sha256"] + assert (output_root / pilot.RUN_SPEC_NAME).read_bytes() == pilot._canonical_bytes( + document + ) + with pytest.raises(RuntimeError, match="absolute"): + pilot.build_p0_extension_run_spec( + Path("relative"), + Path("relative-report.json"), + protocol, + _source(), + Path("relative-evidence"), + ) + + +@pytest.mark.parametrize( + "stage", + ("after-trajectory", "after-batch", "after-progress", "after-manifest"), +) +def test_extension_cell_resumes_every_publication_boundary(tmp_path: Path, stage: str): + path = pilot._write_test_extension_run_spec(tmp_path / "extension", tiny=True) + + def stop(actual: str) -> None: + if actual == stage: + raise RuntimeError("injected extension stop") + + with pytest.raises(RuntimeError, match="injected extension stop"): + pilot._run_test_registered_pilot_cell(path, 0, crash_hook=stop) + run = path.parent / json.loads(path.read_text())["cells"][0]["run_path"] + published_path: Path | None = None + published_payload: bytes | None = None + if stage == "after-batch": + published_path = next((run / "batches").glob("batch-*.json")) + published_payload = published_path.read_bytes() + assert not (run / "progress.json").exists() + if stage == "after-manifest": + manifest_path = json.loads(path.read_text())["cells"][0]["manifest_path"] + published_path = path.parent / manifest_path + published_payload = published_path.read_bytes() + result = pilot._run_test_registered_pilot_cell(path, 0) + assert (path.parent / result["manifest_path"]).is_file() + if published_path is not None: + assert published_path.read_bytes() == published_payload + assert pilot._run_test_registered_pilot_cell(path, 0) == result + + +@pytest.mark.parametrize("suffix", (".partial", ".intent")) +def test_extension_cell_preserves_stale_publication_markers( + tmp_path: Path, suffix: str +): + path = pilot._write_test_extension_run_spec(tmp_path / "extension", tiny=True) + pilot._run_test_registered_pilot_cell(path, 0) + cell = next((path.parent / "cells").iterdir()) + marker = cell / f"stale{suffix}" + marker.write_text("preserve", encoding="utf-8") + with pytest.raises(RuntimeError, match="publication marker"): + pilot._run_test_registered_pilot_cell(path, 0) + assert marker.read_text(encoding="utf-8") == "preserve" + + +def test_extension_pending_merge_and_snapshot_share_exact_schema(tmp_path: Path): + path = pilot._write_test_extension_run_spec(tmp_path / "extension", tiny=True) + assert pilot._pending_test_registered_pilot_cells(path) == [0] + result = pilot._run_test_registered_pilot_cell(path, 0) + assert pilot._pending_test_registered_pilot_cells(path) == [] + request = json.loads( + ( + path.parent + / json.loads(path.read_text())["cells"][0]["run_path"] + / "request.json" + ).read_text() + ) + assert request["master_seed"] == extension.EXTENSION_MASTER_SEED + assert request["phase"] == extension.EXTENSION_PHASE + merged = pilot._merge_test_registered_pilot_progress(path) + assert merged["cells"][0]["trajectory_sha256"] == result["trajectory_sha256"] + with pilot._open_verified_registered_pilot_analysis_snapshot(path) as snapshot: + assert snapshot.spec["schema_version"] == pilot.TEST_EXTENSION_RUN_SPEC_SCHEMA + assert ( + snapshot.progress["schema_version"] == extension.EXTENSION_PROGRESS_SCHEMA + ) + + +def test_extension_merge_rejects_extra_cell_directory(tmp_path: Path): + path = pilot._write_test_extension_run_spec(tmp_path / "extension", tiny=True) + pilot._run_test_registered_pilot_cell(path, 0) + (path.parent / "cells" / "extra").mkdir() + with pytest.raises(RuntimeError, match="extra"): + pilot._merge_test_registered_pilot_progress(path) + + +def test_extension_cell_root_swap_fails_closed(tmp_path: Path): + path = pilot._write_test_extension_run_spec(tmp_path / "extension", tiny=True) + cell_root = path.parent / json.loads(path.read_text())["cells"][0]["cell_path"] + + def replace(stage: str) -> None: + if stage == "after-trajectory": + cell_root.rename(path.parent / "detached-cell") + cell_root.mkdir() + + with pytest.raises(RuntimeError, match="identity|generation"): + pilot._run_test_registered_pilot_cell(path, 0, crash_hook=replace) + + +def test_production_extension_schema_cannot_be_downgraded_to_test( + tmp_path: Path, +): + path = pilot._write_test_extension_run_spec(tmp_path / "extension", tiny=True) + document = json.loads(path.read_text()) + document["schema_version"] = extension.EXTENSION_RUN_SPEC_SCHEMA + document["run_spec_sha256"] = pilot._document_hash(document, "run_spec_sha256") + path.write_bytes(pilot._canonical_bytes(document)) + with pytest.raises(RuntimeError): + pilot._run_test_registered_pilot_cell(path, 0) + + +@pytest.mark.parametrize( + ("source_class", "maximum_size"), + ( + ("design", extension.DESIGN_MAX_BYTES), + ("progress", extension.P0_PROGRESS_MAX_BYTES), + ("registry", extension.P0_RUN_SPEC_MAX_BYTES), + ("analysis", extension.P0_ANALYSIS_MAX_BYTES), + ), +) +def test_extension_sources_reject_oversize_before_read( + tmp_path: Path, + source_class: str, + maximum_size: int, +): + if source_class == "design": + source = tmp_path / "design.md" + with source.open("wb") as stream: + stream.truncate(maximum_size + 1) + invoke = lambda: extension._file_sha256(source) + elif source_class == "analysis": + source = tmp_path / "p0_analysis.json" + with source.open("wb") as stream: + stream.truncate(maximum_size + 1) + invoke = lambda: extension.load_frozen_p0_analysis(source) + else: + evidence = tmp_path / "evidence" + evidence.mkdir() + for name in ("run_spec.json", "progress.json"): + shutil.copyfile(P0_EVIDENCE_ROOT / name, evidence / name) + name = "progress.json" if source_class == "progress" else "run_spec.json" + with (evidence / name).open("wb") as stream: + stream.truncate(maximum_size + 1) + invoke = lambda: extension._load_p0_evidence(evidence) + with pytest.raises(RuntimeError, match="byte-size|bounded"): + invoke() + + +@pytest.mark.parametrize( + "source_class", + ("design", "progress", "registry", "analysis"), +) +def test_extension_sources_reject_pathname_swap_after_descriptor_read( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + source_class: str, +): + if source_class == "design": + source = tmp_path / "design.md" + source.write_bytes(b"original\n") + replacement = tmp_path / "replacement.md" + replacement.write_bytes(b"changed!\n") + invoke = lambda: extension._file_sha256(source) + read_owner = pilot + elif source_class == "analysis": + source = tmp_path / "p0_analysis.json" + source.write_bytes(P0_ANALYSIS.read_bytes()) + replacement = tmp_path / "replacement-p0_analysis.json" + payload = bytearray(source.read_bytes()) + payload[0] = ord("[") + replacement.write_bytes(payload) + invoke = lambda: extension.load_frozen_p0_analysis(source) + read_owner = pilot + else: + evidence = tmp_path / "evidence" + evidence.mkdir() + for name in ("run_spec.json", "progress.json"): + shutil.copyfile(P0_EVIDENCE_ROOT / name, evidence / name) + name = "progress.json" if source_class == "progress" else "run_spec.json" + source = evidence / name + replacement = tmp_path / f"replacement-{name}" + payload = bytearray(source.read_bytes()) + payload[0] = ord("[") if payload[0] != ord("[") else ord("{") + replacement.write_bytes(payload) + invoke = lambda: extension._load_p0_evidence(evidence) + read_owner = extension + target_description = { + "progress": "progress", + "registry": "run spec", + }.get(source_class) + real_read = read_owner._read_descriptor_bounded + swapped = False + + def swapping_read(descriptor: int, maximum_size: int, description: str) -> bytes: + nonlocal swapped + result = real_read(descriptor, maximum_size, description) + if not swapped and ( + target_description is None or target_description in description + ): + swapped = True + replacement.replace(source) + return result + + monkeypatch.setattr(read_owner, "_read_descriptor_bounded", swapping_read) + with pytest.raises(RuntimeError, match="identity|generation|changed"): + invoke() + + +def test_extension_ranges_are_derived_from_exact_real_p0(): + derived = extension.derive_p0_extension_ranges(_source()) + assert derived[(0.9).hex()]["four_sector_components"] == [[5, 5]] + assert derived[(0.9).hex()]["q_g_components"] == [[6, 6], [13, 14]] + assert derived[(1.0).hex()]["four_sector_components"] == [[6, 7]] + assert derived[(1.0).hex()]["q_g_components"] == [[8, 8], [12, 14]] + for sigma_hex, (guard_indices, lower, upper) in EXPECTED_SPANS.items(): + assert derived[sigma_hex]["guard_interval_indices"] == list(guard_indices) + assert derived[sigma_hex]["lower_kappa_hex"] == lower + assert derived[sigma_hex]["upper_kappa_hex"] == upper + + +def test_extension_grids_are_recursive_binary64_and_hash_bound(): + protocol = extension.build_p0_extension_protocol(_source(), P0_EVIDENCE_ROOT) + entries = {entry["sigma_hex"]: entry for entry in protocol["sigma_entries"]} + assert { + sigma: entry["kappas"] for sigma, entry in entries.items() + } == EXPECTED_GRIDS + assert ( + entries[(0.9).hex()]["grid_sha256"] + == extension.EXTENSION_GRID_HASHES[(0.9).hex()] + ) + assert ( + entries[(1.0).hex()]["grid_sha256"] + == extension.EXTENSION_GRID_HASHES[(1.0).hex()] + ) + + +def test_protocol_has_exact_axes_fresh_identities_and_canonical_cells(): + source = _source() + protocol = extension.build_p0_extension_protocol(source, P0_EVIDENCE_ROOT) + extension.validate_p0_extension_protocol(source, protocol, P0_EVIDENCE_ROOT) + assert protocol["schema_version"] == extension.EXTENSION_PROTOCOL_SCHEMA + assert protocol["master_seed"] == 19_420_262_729 + assert protocol["lengths"] == [2**10, 2**14, 2**18] + assert protocol["replicas"] == list(range(24, 40)) + assert protocol["cell_count"] == 96 + assert sum(len(cell["kappas"]) for cell in protocol["cells"]) == 1632 + assert [ + (cell["sigma"], cell["length"], cell["replica"]) for cell in protocol["cells"] + ] == [ + (sigma.hex(), length, replica) + for sigma in (0.9, 1.0) + for length in (2**10, 2**14, 2**18) + for replica in range(24, 40) + ] + assert len({cell["cell_id"] for cell in protocol["cells"]}) == 96 + assert len({cell["request_sha256"] for cell in protocol["cells"]}) == 96 + assert ( + len( + { + digest + for cell in protocol["cells"] + for digest in cell["rng_material_sha256"] + } + ) + == 96 * 4 + ) + + +def test_protocol_build_uses_explicit_external_evidence_in_clean_checkout( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + external = tmp_path / "external-p0" + external.mkdir() + for name in ("run_spec.json", "progress.json"): + shutil.copyfile(P0_EVIDENCE_ROOT / name, external / name) + clean_checkout = tmp_path / "clean-checkout" + clean_checkout.mkdir() + monkeypatch.setattr( + extension, + "_p0_run_spec_path", + lambda: clean_checkout / "results/missing/run_spec.json", + raising=False, + ) + monkeypatch.setattr( + extension, + "_p0_progress_path", + lambda: clean_checkout / "results/missing/progress.json", + raising=False, + ) + + protocol = extension.build_p0_extension_protocol(_source(), external) + extension.validate_p0_extension_protocol(_source(), protocol, external) + + assert not (clean_checkout / "results").exists() + assert protocol["source_p0_run_spec_sha256"] == extension.P0_RUN_SPEC_SHA256 + assert protocol["source_p0_progress_sha256"] == extension.P0_PROGRESS_SHA256 + + +@pytest.mark.parametrize( + "kind", + ("relative", "missing", "wrong-run-spec", "wrong-progress", "symlink"), +) +def test_protocol_evidence_root_fails_closed(tmp_path: Path, kind: str): + evidence = tmp_path / "evidence" + if kind == "relative": + candidate = Path("relative-evidence") + elif kind == "missing": + candidate = evidence + else: + target = tmp_path / "target" + target.mkdir() + for name in ("run_spec.json", "progress.json"): + shutil.copyfile(P0_EVIDENCE_ROOT / name, target / name) + if kind == "wrong-run-spec": + (target / "run_spec.json").write_text("{}\n", encoding="utf-8") + candidate = target + elif kind == "wrong-progress": + (target / "progress.json").write_text("{}\n", encoding="utf-8") + candidate = target + else: + evidence.symlink_to(target, target_is_directory=True) + candidate = evidence + + with pytest.raises(RuntimeError, match="evidence|absolute|canonical|hash"): + extension.build_p0_extension_protocol(_source(), candidate) + + +def test_protocol_evidence_root_rejects_directory_swap( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + evidence = tmp_path / "evidence" + evidence.mkdir() + for name in ("run_spec.json", "progress.json"): + shutil.copyfile(P0_EVIDENCE_ROOT / name, evidence / name) + original_reader = extension._read_evidence_document_at + + def swapping_reader(root_fd: int, name: str, description: str, maximum_size: int): + document = original_reader(root_fd, name, description, maximum_size) + if name == "run_spec.json": + moved = tmp_path / "moved-evidence" + evidence.rename(moved) + evidence.mkdir() + for filename in ("run_spec.json", "progress.json"): + shutil.copyfile(moved / filename, evidence / filename) + return document + + monkeypatch.setattr(extension, "_read_evidence_document_at", swapping_reader) + with pytest.raises(RuntimeError, match="identity changed"): + extension.build_p0_extension_protocol(_source(), evidence) + + +def test_protocol_rejects_actual_frozen_progress_drift(tmp_path: Path): + source = _source() + protocol = extension.build_p0_extension_protocol(source, P0_EVIDENCE_ROOT) + drifted_root = tmp_path / "drifted-p0" + drifted_root.mkdir() + shutil.copyfile(P0_EVIDENCE_ROOT / "run_spec.json", drifted_root / "run_spec.json") + (drifted_root / "progress.json").write_bytes(b"{}\n") + with pytest.raises(RuntimeError, match="progress"): + extension.build_p0_extension_protocol(source, drifted_root) + with pytest.raises(RuntimeError, match="progress"): + extension.validate_p0_extension_protocol(source, protocol, drifted_root) + + +def test_protocol_rejects_recomputed_bracket_mismatch( + monkeypatch: pytest.MonkeyPatch, +): + source = _source() + protocol = extension.build_p0_extension_protocol(source, P0_EVIDENCE_ROOT) + forged = copy.deepcopy(extension.select_p1_brackets(source)) + forged["requires_p0_extension"] = False + assert forged["bracket_document_sha256"] == extension.P0_BRACKET_DOCUMENT_SHA256 + monkeypatch.setattr(extension, "select_p1_brackets", lambda _source: forged) + with pytest.raises(RuntimeError, match="bracket"): + extension.build_p0_extension_protocol(source, P0_EVIDENCE_ROOT) + with pytest.raises(RuntimeError, match="bracket"): + extension.validate_p0_extension_protocol(source, protocol, P0_EVIDENCE_ROOT) + + +@pytest.mark.parametrize( + ("field", "value", "message"), + [ + ("request_sha256", [], "request"), + ("rng_material_sha256", [{}, "0" * 64, "1" * 64, "2" * 64], "RNG"), + ], +) +def test_validator_normalizes_malformed_digest_types( + field: str, value: object, message: str +): + source = _source() + protocol = copy.deepcopy( + extension.build_p0_extension_protocol(source, P0_EVIDENCE_ROOT) + ) + protocol["cells"][0][field] = value + _rehash(protocol) + with pytest.raises(RuntimeError, match=message): + extension.validate_p0_extension_protocol(source, protocol, P0_EVIDENCE_ROOT) + + +@pytest.mark.parametrize( + ("mutation", "message"), + [ + (lambda value: value.update(source_p0_run_spec_sha256="0" * 64), "source"), + ( + lambda value: value["sigma_entries"][0]["q_g_components"].reverse(), + "component", + ), + ( + lambda value: value["sigma_entries"][0]["kappas"].__setitem__( + 0, "0X1.F400000000000P-2" + ), + "binary64", + ), + (lambda value: value["sigma_entries"][0]["kappas"].reverse(), "grid"), + ( + lambda value: value["sigma_entries"][0].update(grid_sha256="0" * 64), + "grid", + ), + (lambda value: value.update(design_sha256="0" * 64), "design"), + (lambda value: value["cells"].reverse(), "canonical"), + (lambda value: value["replicas"].pop(), "replica"), + ( + lambda value: value["replicas"].__setitem__(1, value["replicas"][0]), + "replica", + ), + ( + lambda value: value["cells"][0].update(request_sha256="0" * 64), + "request", + ), + ( + lambda value: value["cells"][0]["rng_material_sha256"].__setitem__( + 0, "0" * 64 + ), + "RNG", + ), + ( + lambda value: value["cells"][0].update( + request_sha256=extension._p0_identity_hashes(P0_EVIDENCE_ROOT)[0][0] + ), + "collision", + ), + ], +) +def test_semantic_validator_rejects_superficially_rehashed_mutations( + mutation, message: str +): + source = _source() + protocol = copy.deepcopy( + extension.build_p0_extension_protocol(source, P0_EVIDENCE_ROOT) + ) + mutation(protocol) + _rehash(protocol) + with pytest.raises(RuntimeError, match=message): + extension.validate_p0_extension_protocol(source, protocol, P0_EVIDENCE_ROOT) + + +def test_protocol_rejects_unknown_fields_and_p1_identity_overlap( + monkeypatch: pytest.MonkeyPatch, +): + source = _source() + protocol = extension.build_p0_extension_protocol(source, P0_EVIDENCE_ROOT) + forged = copy.deepcopy(protocol) + forged["unknown"] = True + _rehash(forged) + with pytest.raises(RuntimeError, match="fields"): + extension.validate_p0_extension_protocol(source, forged, P0_EVIDENCE_ROOT) + + monkeypatch.setattr(extension, "EXTENSION_REPLICAS", tuple(range(8, 24))) + with pytest.raises(RuntimeError, match="P1|overlap"): + extension.build_p0_extension_protocol(source, P0_EVIDENCE_ROOT) + + +def test_component_and_grid_helpers_fail_closed(): + with pytest.raises(RuntimeError, match="canonical"): + extension._marked_components([2, 1]) + assert extension._component_gap((2, 3), (3, 5)) == 0 + with pytest.raises(RuntimeError, match="endpoints"): + extension._recursive_binary64_grid_17(1.0, 1.0) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_slurm.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_slurm.py new file mode 100644 index 000000000..33138df82 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_pilot_slurm.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +import os +import json +from pathlib import Path +import subprocess + +import pytest + + +SOLUTION = Path(__file__).resolve().parents[1] +WRAPPER = SOLUTION / "scripts" / "pilot_array_slurm.sh" + + +def _run( + tmp_path: Path, + task_id: str, + *, + cpus: str = "1", + extra_env: dict[str, str] | None = None, + cache_root: Path | None = None, +) -> subprocess.CompletedProcess[str]: + repo = tmp_path / "repo" + script = repo / "tracks/qmc/solutions/frustration-free/challenge-194/scripts/run_pilot.py" + script.parent.mkdir(parents=True) + script.write_text( + "import json,os,sys\n" + "print(sys.executable)\n" + "print(os.environ['NUMBA_CACHE_DIR'])\n" + "print('ENV=' + json.dumps(dict(os.environ), sort_keys=True))\n" + "print('ARGS=' + ' '.join(sys.argv[1:]))\n", + encoding="utf-8", + ) + run_spec = tmp_path / "run_spec.json" + run_spec.write_text("{}\n", encoding="utf-8") + local_tmp = tmp_path / "node" if cache_root is None else cache_root + if cache_root is None: + local_tmp.mkdir() + env = { + **os.environ, + "CHALLENGE_194_REPO_ROOT": str(repo), + "CHALLENGE_194_PYTHON": os.path.realpath(os.sys.executable), + "HARNESS_RUN_SPEC": str(run_spec), + "SLURM_ARRAY_TASK_ID": task_id, + "SLURM_ARRAY_JOB_ID": "991", + "SLURM_CPUS_PER_TASK": cpus, + "SLURM_TMPDIR": str(local_tmp), + "PYTHONPATH": "/hostile/path", + **(extra_env or {}), + } + return subprocess.run( + ["bash", str(WRAPPER)], + cwd=tmp_path, + env=env, + capture_output=True, + text=True, + ) + + +@pytest.mark.parametrize(("task_id", "index"), (("1", "0"), ("96", "95"))) +def test_array_boundaries_map_one_based_to_zero_based( + tmp_path: Path, task_id: str, index: str +): + result = _run(tmp_path, task_id) + assert result.returncode == 0, result.stderr + assert f"--cell-index {index}" in result.stdout + assert os.path.realpath(os.sys.executable) in result.stdout + assert f"challenge-194-pilot-991-{task_id}" in result.stdout + + +@pytest.mark.parametrize("task_id", ("0", "97", "-1", "x")) +def test_array_rejects_out_of_range_ids(tmp_path: Path, task_id: str): + result = _run(tmp_path, task_id) + assert result.returncode == 64 + + +def test_array_requires_exactly_one_cpu(tmp_path: Path): + assert _run(tmp_path, "1", cpus="2").returncode == 64 + + +def test_wrapper_preserves_venv_launcher_and_sanitizes_hostile_environment( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + hostile = { + "NUMBA_DISABLE_JIT": "1", + "NUMBA_CPU_NAME": "hostile", + "NUMBA_CPU_FEATURES": "+hostile", + "NUMBA_THREADING_LAYER": "hostile", + "NUMBA_CACHE_DIR": "/hostile/cache", + "PYTHONHOME": "/hostile/home", + "PYTHONUSERBASE": "/hostile/user", + "PYTHONPATH": "/hostile/path", + "LD_PRELOAD": "/hostile/preload.so", + "LD_LIBRARY_PATH": "/hostile/lib", + "LIBRARY_PATH": "/hostile/compiler", + "OMP_NUM_THREADS": "99", + "OPENBLAS_NUM_THREADS": "99", + "MKL_NUM_THREADS": "99", + "NUMEXPR_NUM_THREADS": "99", + "VECLIB_MAXIMUM_THREADS": "99", + "PYTHONHASHSEED": "random", + } + for key, value in hostile.items(): + monkeypatch.setenv(key, value) + result = _run(tmp_path, "1") + assert result.returncode == 0 + line = next(item for item in result.stdout.splitlines() if item.startswith("ENV=")) + environment = json.loads(line.removeprefix("ENV=")) + assert environment["NUMBA_DISABLE_JIT"] == "0" + assert environment["NUMBA_NUM_THREADS"] == "1" + assert environment["PYTHONNOUSERSITE"] == "1" + assert environment["PYTHONHASHSEED"] == "0" + assert environment["PYTHONPATH"].endswith("/challenge-194/src") + for key in ( + "NUMBA_CPU_NAME", + "NUMBA_CPU_FEATURES", + "NUMBA_THREADING_LAYER", + "PYTHONHOME", + "PYTHONUSERBASE", + "LD_PRELOAD", + "LD_LIBRARY_PATH", + "LIBRARY_PATH", + ): + assert key not in environment + for key in ( + "OMP_NUM_THREADS", + "OPENBLAS_NUM_THREADS", + "MKL_NUM_THREADS", + "NUMEXPR_NUM_THREADS", + "VECLIB_MAXIMUM_THREADS", + ): + assert environment[key] == "1" + + +@pytest.mark.parametrize( + ("name", "value"), + ( + ("SLURM_ARRAY_JOB_ID", "../bad"), + ("SLURM_JOB_ID", "x"), + ), +) +def test_wrapper_rejects_invalid_job_ids( + tmp_path: Path, name: str, value: str +): + result = _run(tmp_path, "1", extra_env={name: value}) + assert result.returncode == 64 + + +def test_wrapper_rejects_symlink_cache_root( + tmp_path: Path, +): + target = tmp_path / "target" + target.mkdir() + linked = tmp_path / "linked" + linked.symlink_to(target, target_is_directory=True) + result = _run(tmp_path, "1", cache_root=linked) + assert result.returncode == 73 + + +@pytest.mark.parametrize("kind", ("empty", "nonempty", "symlink")) +def test_wrapper_requires_uniquely_created_owned_cache_directory( + tmp_path: Path, kind: str +): + cache_root = tmp_path / "node" + cache_root.mkdir() + cache = cache_root / "challenge-194-pilot-991-1" + if kind == "symlink": + target = tmp_path / "hostile-cache" + target.mkdir() + cache.symlink_to(target, target_is_directory=True) + else: + cache.mkdir() + if kind == "nonempty": + (cache / "hostile").write_text("data", encoding="utf-8") + result = _run(tmp_path, "1", cache_root=cache_root) + assert result.returncode == 73 diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_poisson_reference.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_poisson_reference.py new file mode 100644 index 000000000..f1e4d0a87 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_poisson_reference.py @@ -0,0 +1,908 @@ +from __future__ import annotations + +from dataclasses import replace +import hashlib +import inspect +import math + +import numpy as np +import pytest +from scipy.stats import norm + +import long_range_percolation.poisson_reference as poisson_reference +from long_range_percolation.counter_rng import ( + STREAM_ALIAS_COLUMN, + STREAM_ALIAS_THRESHOLD, + STREAM_COUNT, + STREAM_EDGE_OFFSET, + STREAM_EXPONENTIAL, +) +from long_range_percolation.kernel import periodic_kernel +from long_range_percolation.enumeration import enumerate_graphs +from long_range_percolation.model import ( + ModelSpec, + distance_classes, + iter_unordered_edges, +) +from long_range_percolation.oracle import ( + expected_open_edges, + no_edge_probability, + variance_open_edges, +) +from long_range_percolation.poisson_reference import ( + TrajectoryRequest, + TrajectoryResult, + _PREFIX_REL_TOL, + _build_reference_streams, + _class_data, + _compensated_prefix, + _run_poisson_with_streams, + run_poisson_reference, + validate_trajectory_request, +) + + +UINT64_MAX = (1 << 64) - 1 +FAMILYWISE_ALPHA = 0.001 +EDGE_CASE_IDS = tuple(f"edge_marginal.{index}" for index in range(15)) +COMPONENT_CASE_IDS = ( + "component_count.mean", + "largest_component.mean", + "second_component.mean", + "s1_fraction.mean", + "s2_fraction.mean", + "sum_size_sq.mean", + "sum_size_fourth.mean", + "q_g.mean", + "four_sector_crossing.mean", +) +STATISTICAL_CASE_IDS = ( + "interarrival.mean", + "interarrival.variance", + "interarrival.cdf_at_1", + "event_count.mean", + "event_count.variance", + "event_count.p0", + *EDGE_CASE_IDS, + "no_edge.probability", + "open_edges.mean", + "open_edges.variance", + *COMPONENT_CASE_IDS, +) + + +def digest(kernel: np.ndarray) -> str: + return hashlib.sha256(kernel.tobytes(order="C")).hexdigest() + + +def make_request( + *, + length: int = 6, + sigma: float = 1.0, + kappas: object = (0.0, 0.2), + master_seed: int = 123, + phase: str = "validation", + replica: int = 4, + sigma_grid_id: str = "sigma-1-test", + kernel: np.ndarray | None = None, + kernel_sha256: str | None = None, +) -> TrajectoryRequest: + values = periodic_kernel(length, sigma) if kernel is None else kernel + return TrajectoryRequest( + length=length, + sigma=sigma, + sigma_grid_id=sigma_grid_id, + kappas=np.asarray(kappas, dtype=np.float64), + master_seed=master_seed, + phase=phase, + replica=replica, + kernel_sha256=digest(values) if kernel_sha256 is None else kernel_sha256, + ) + + +class ScriptedStreams: + def __init__( + self, + *, + exponential: list[float], + columns: list[float] | None = None, + thresholds: list[float] | None = None, + offsets: list[int] | None = None, + offset_words: list[int] | None = None, + ): + self._uniforms = { + STREAM_ALIAS_COLUMN: list(columns or []), + STREAM_ALIAS_THRESHOLD: list(thresholds or []), + STREAM_EXPONENTIAL: list(exponential), + } + self._offsets = list(offsets or []) + self._offset_words = list(offset_words or []) + self._positions = {stream: 0 for stream in self._uniforms} + self._offset_position = 0 + self._offset_word_position = 0 + self.terminal_counters = np.zeros((STREAM_COUNT, 4), dtype=np.uint32) + self.draw_counts = np.zeros((STREAM_COUNT, 3), dtype=np.uint64) + + @property + def minimum_exponential_hazard(self) -> float: + values = self._uniforms[STREAM_EXPONENTIAL] + if not values: + return -math.log(math.nextafter(1.0, 0.0)) + return min(-math.log(value) for value in values) + + def _record_word(self, stream_id: int) -> None: + words = int(self.draw_counts[stream_id, 0]) + if words % 4 == 0: + self.draw_counts[stream_id, 1] += np.uint64(1) + self.terminal_counters[stream_id, 0] += np.uint32(1) + self.draw_counts[stream_id, 0] += np.uint64(1) + + def uniform(self, stream_id: int) -> float: + position = self._positions[stream_id] + values = self._uniforms[stream_id] + if position >= len(values): + raise AssertionError(f"unexpected uniform draw from stream {stream_id}") + self._positions[stream_id] = position + 1 + self._record_word(stream_id) + return values[position] + + def bounded(self, stream_id: int, bound: int) -> int: + assert stream_id == STREAM_EDGE_OFFSET + if self._offset_words: + threshold = ((1 << 32) - bound) % bound + while True: + if self._offset_word_position >= len(self._offset_words): + raise AssertionError("unexpected offset word draw") + word = self._offset_words[self._offset_word_position] + self._offset_word_position += 1 + self._record_word(stream_id) + if word < threshold: + self.draw_counts[stream_id, 2] += np.uint64(1) + continue + return word % bound + if self._offset_position >= len(self._offsets): + raise AssertionError("unexpected offset draw") + value = self._offsets[self._offset_position] + self._offset_position += 1 + self._record_word(stream_id) + if not 0 <= value < bound: + raise AssertionError("scripted offset is outside its requested bound") + return value + + +def test_request_rejects_nonfinite_unsorted_or_duplicate_couplings(): + for values in ([0.2, 0.1], [0.1, 0.1], [math.nan], [math.inf], [-1.0]): + with pytest.raises(ValueError): + validate_trajectory_request(make_request(kappas=values)) + + +@pytest.mark.parametrize( + ("field", "value"), + [ + ("length", True), + ("length", 3), + ("length", 0), + ("sigma", True), + ("sigma", 0.0), + ("sigma", math.inf), + ("sigma", np.nextafter(0.0, 1.0)), + ("master_seed", True), + ("master_seed", -1), + ("master_seed", 1 << 64), + ("replica", True), + ("replica", -1), + ("replica", 1 << 64), + ("phase", "unknown"), + ("sigma_grid_id", ""), + ("sigma_grid_id", " padded "), + ("kernel_sha256", "not-a-digest"), + ], +) +def test_request_rejects_every_invalid_scalar(field: str, value: object): + request = make_request() + with pytest.raises(ValueError): + validate_trajectory_request(replace(request, **{field: value})) + + +def test_request_accepts_uint64_boundaries_and_freezes_a_defensive_kappa_copy(): + kappas = np.asarray([0.0, 0.25], dtype=np.float64) + request = make_request( + kappas=kappas, master_seed=UINT64_MAX, replica=UINT64_MAX + ) + kappas[1] = 9.0 + np.testing.assert_array_equal(request.kappas, [0.0, 0.25]) + assert not request.kappas.flags.writeable + validate_trajectory_request(request) + with pytest.raises(ValueError): + request.kappas[0] = 1.0 + + +def test_kernel_preflight_rejects_shape_dtype_layout_values_and_digest(): + request = make_request(kappas=[0.1]) + valid = periodic_kernel(request.length, request.sigma) + invalid = ( + valid[:-1], + valid.astype(np.float32), + np.asarray([[1.0, 2.0, 3.0]], dtype=np.float64), + np.asarray([1.0, math.nan, 3.0], dtype=np.float64), + np.asarray([1.0, 0.0, 3.0], dtype=np.float64), + np.arange(6.0, dtype=np.float64)[::2], + ) + for kernel in invalid: + with pytest.raises(ValueError): + run_poisson_reference(request, kernel) + with pytest.raises(ValueError, match="digest"): + run_poisson_reference( + replace(request, kernel_sha256="0" * 64), + valid, + ) + + +def test_reference_does_not_import_compiled_selection_or_connectivity(): + source = inspect.getsource(poisson_reference) + forbidden = ("alias", "edge_set", "production_union_find", "poisson_sweep") + for name in forbidden: + assert f"import {name}" not in source + assert f"from .{name}" not in source + + +def test_scripted_events_have_exact_checkpoint_duplicate_and_overshoot_semantics(): + kernel = np.asarray([1.0, 2.0, 3.0], dtype=np.float64) + total_rate = 6.0 + 12.0 + 9.0 + request = make_request( + kappas=[0.0, 0.1, 0.13, 0.18, 0.2, 0.3, 0.4], + kernel=kernel, + ) + streams = ScriptedStreams( + exponential=[ + math.exp(-0.05 * total_rate), + math.exp(-0.07 * total_rate), + math.exp(-0.13 * total_rate), + math.exp(-0.20 * total_rate), + ], + columns=[0.2, 0.4, 0.6], + thresholds=[0.1, 0.1, 0.9], + offsets=[0, 0, 2], + ) + + run = _run_poisson_with_streams(request, kernel, streams) + result = run.result + + assert result.event_count == 3 + assert result.duplicate_count == 1 + np.testing.assert_array_equal( + result.observables[:, 0], + [0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0], + ) + assert run.event_times == pytest.approx((0.05, 0.12, 0.25)) + assert run.edge_ids_by_checkpoint[0] == frozenset() + assert run.edge_ids_by_checkpoint[1] == frozenset({0}) + assert run.edge_ids_by_checkpoint[2:5] == (frozenset({0}),) * 3 + assert run.edge_ids_by_checkpoint[5:] == (frozenset({0, 14}),) * 2 + np.testing.assert_array_equal( + result.draw_counts[:, 0], + [3, 3, 3, 4], + ) + np.testing.assert_array_equal(result.hash_diagnostics, np.zeros(5)) + + +def test_zero_only_request_records_empty_graph_without_any_draw(): + kernel = periodic_kernel(8, 1.0) + request = make_request(length=8, kappas=[0.0], kernel=kernel) + streams = ScriptedStreams(exponential=[]) + result = _run_poisson_with_streams(request, kernel, streams).result + assert result.event_count == result.duplicate_count == 0 + assert result.observables[0, 0] == 0.0 + assert result.observables[0, 1] == 8.0 + assert result.observables[0, 2] == 1.0 + assert not np.any(result.draw_counts) + + +def test_positive_terminal_coupling_consumes_only_final_overshoot_exponential(): + kernel = np.asarray([1.0, 1.0], dtype=np.float64) + request = make_request(length=4, kappas=[0.1], kernel=kernel) + streams = ScriptedStreams(exponential=[math.exp(-1.0)]) + result = _run_poisson_with_streams(request, kernel, streams).result + assert result.event_count == 0 + np.testing.assert_array_equal(result.draw_counts[:, 0], [0, 0, 0, 1]) + + +def test_smallest_positive_rate_uses_hazard_terminal_comparison_without_overflow(): + kernel = np.asarray([np.nextafter(0.0, 1.0)], dtype=np.float64) + request = make_request( + length=2, + kappas=[np.finfo(np.float64).max], + kernel=kernel, + ) + streams = ScriptedStreams(exponential=[0.5]) + result = _run_poisson_with_streams(request, kernel, streams).result + assert result.event_count == result.duplicate_count == 0 + np.testing.assert_array_equal(result.observables[:, 0], [0.0]) + np.testing.assert_array_equal( + result.draw_counts, + [[0, 0, 0], [0, 0, 0], [0, 0, 0], [1, 1, 0]], + ) + np.testing.assert_array_equal( + result.terminal_counters[:, 0], [0, 0, 0, 1] + ) + + +def test_event_exactly_at_final_checkpoint_is_included_then_overshot(): + kernel = np.asarray([1.0], dtype=np.float64) + uniform = 0.5 + hazard = -math.log(uniform) + request = make_request(length=2, kappas=[hazard], kernel=kernel) + streams = ScriptedStreams( + exponential=[uniform, math.exp(-0.5)], + columns=[0.5], + thresholds=[0.5], + offsets=[0], + ) + run = _run_poisson_with_streams(request, kernel, streams) + assert run.event_times == (hazard,) + assert run.edge_ids_by_checkpoint == (frozenset({0}),) + assert run.result.event_count == 1 + assert run.result.observables[0, 0] == 1.0 + np.testing.assert_array_equal( + run.result.draw_counts, + [[1, 1, 0], [1, 1, 0], [1, 1, 0], [2, 1, 0]], + ) + + +def test_final_checkpoint_matches_extended_prefix_and_logical_draw_suffix(): + kernel = np.asarray([1.0], dtype=np.float64) + uniform = 0.5 + shared_kappa = -math.log(uniform) + final_request = make_request( + length=2, kappas=[shared_kappa], kernel=kernel + ) + extended_request = make_request( + length=2, kappas=[shared_kappa, shared_kappa + 1.0], kernel=kernel + ) + final_streams = ScriptedStreams( + exponential=[uniform, math.exp(-0.5)], + columns=[0.5], + thresholds=[0.5], + offsets=[0], + ) + extended_streams = ScriptedStreams( + exponential=[uniform, math.exp(-0.5), math.exp(-1.0)], + columns=[0.5, 0.5], + thresholds=[0.5, 0.5], + offsets=[0, 0], + ) + final = _run_poisson_with_streams( + final_request, kernel, final_streams + ).result + extended = _run_poisson_with_streams( + extended_request, kernel, extended_streams + ).result + np.testing.assert_array_equal(final.observables[0], extended.observables[0]) + np.testing.assert_array_equal( + extended.draw_counts - final.draw_counts, + [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]], + ) + np.testing.assert_array_equal( + extended.terminal_counters, + final.terminal_counters, + ) + + +def test_terminal_hazard_neighbors_choose_strictly_correct_side(): + kernel = np.asarray([1.0], dtype=np.float64) + boundary_uniform = 0.5 + boundary = -math.log(boundary_uniform) + above_uniform = math.nextafter(boundary_uniform, 0.0) + below_uniform = math.nextafter(boundary_uniform, 1.0) + assert -math.log(above_uniform) > boundary + assert -math.log(below_uniform) < boundary + + above_request = make_request(length=2, kappas=[boundary], kernel=kernel) + above_streams = ScriptedStreams(exponential=[above_uniform]) + above = _run_poisson_with_streams( + above_request, kernel, above_streams + ).result + assert above.event_count == 0 + np.testing.assert_array_equal( + above.draw_counts, + [[0, 0, 0], [0, 0, 0], [0, 0, 0], [1, 1, 0]], + ) + + below_request = make_request(length=2, kappas=[boundary], kernel=kernel) + below_streams = ScriptedStreams( + exponential=[below_uniform, math.exp(-1.0)], + columns=[0.5], + thresholds=[0.5], + offsets=[0], + ) + below = _run_poisson_with_streams( + below_request, kernel, below_streams + ).result + assert below.event_count == 1 + assert below.observables[0, 0] == 1.0 + np.testing.assert_array_equal( + below.draw_counts, + [[1, 1, 0], [1, 1, 0], [1, 1, 0], [2, 1, 0]], + ) + + +def test_huge_finite_rate_fails_event_count_preflight_before_consuming_streams(): + kernel = np.asarray([np.finfo(np.float64).max / 2.0], dtype=np.float64) + request = make_request(length=2, kappas=[1e-100], kernel=kernel) + streams = ScriptedStreams(exponential=[]) + with pytest.raises(ValueError, match="event count"): + _run_poisson_with_streams(request, kernel, streams) + assert not np.any(streams.draw_counts) + assert not np.any(streams.terminal_counters) + + +def test_reference_compensated_hazard_clock_retains_sub_ulp_increment(): + add = getattr(poisson_reference, "_compensated_hazard_add", None) + assert add is not None + high = float(2**21) + minimum_hazard = -math.log( + (float(np.iinfo(np.uint32).max) + 0.5) * (2.0**-32) + ) + next_high, next_low = add(high, 0.0, minimum_hazard) + assert next_high == high + assert 0.0 < next_low < math.ulp(high) + + +def test_huge_rate_tiny_terminal_hazard_overshoots_without_dividing(): + kernel = np.asarray([np.finfo(np.float64).max / 2.0], dtype=np.float64) + request = make_request( + length=2, + kappas=[np.nextafter(0.0, 1.0)], + kernel=kernel, + ) + streams = ScriptedStreams(exponential=[0.9999999999]) + result = _run_poisson_with_streams(request, kernel, streams).result + assert result.event_count == 0 + np.testing.assert_array_equal( + result.draw_counts, + [[0, 0, 0], [0, 0, 0], [0, 0, 0], [1, 1, 0]], + ) + + +def test_antipodal_offset_decoding_and_duplicate_suppression(): + kernel = np.asarray([1e-12, 1.0], dtype=np.float64) + total_rate = 4e-12 + 2.0 + request = make_request(length=4, kappas=[1.0], kernel=kernel) + streams = ScriptedStreams( + exponential=[ + math.exp(-0.1 * total_rate), + math.exp(-0.1 * total_rate), + math.exp(-2.0 * total_rate), + ], + columns=[0.1, 0.2], + thresholds=[0.9, 0.9], + offsets=[1, 1], + ) + run = _run_poisson_with_streams(request, kernel, streams) + assert run.result.event_count == 2 + assert run.result.duplicate_count == 1 + assert run.edge_ids_by_checkpoint == (frozenset({5}),) + assert run.result.observables[0, 0] == 1.0 + assert run.result.observables[0, 2] == 2.0 + + +def test_non_power_of_two_offset_rejection_is_stream_local_and_exact(): + kernel = np.asarray([1.0, 1e-12, 1e-12], dtype=np.float64) + total_rate = math.fsum((6.0, 6e-12, 3e-12)) + request = make_request(length=6, kappas=[0.1], kernel=kernel) + streams = ScriptedStreams( + exponential=[math.exp(-0.05 * total_rate), math.exp(-1.0)], + columns=[0.5], + thresholds=[0.1], + offset_words=[3, 10], + ) + result = _run_poisson_with_streams(request, kernel, streams).result + assert result.event_count == 1 + np.testing.assert_array_equal( + result.draw_counts, + [ + [1, 1, 0], + [1, 1, 0], + [2, 1, 1], + [2, 1, 0], + ], + ) + np.testing.assert_array_equal(result.terminal_counters[:, 0], [1, 1, 1, 1]) + assert result.draw_counts[STREAM_ALIAS_COLUMN, 0] == result.event_count + assert result.draw_counts[STREAM_ALIAS_THRESHOLD, 0] == result.event_count + assert result.draw_counts[STREAM_EXPONENTIAL, 0] == result.event_count + 1 + + +def test_results_are_reproducible_schedule_independent_and_immutable(): + kernel = periodic_kernel(8, 0.75) + requests = [ + make_request( + length=8, + sigma=0.75, + kappas=[0.0, 0.2, 0.7], + replica=replica, + kernel=kernel, + ) + for replica in (2, 9) + ] + forward = [run_poisson_reference(request, kernel) for request in requests] + reverse = { + request.replica: run_poisson_reference(request, kernel) + for request in reversed(requests) + } + repeated = run_poisson_reference(requests[0], kernel) + + for request, first in zip(requests, forward, strict=True): + second = reverse[request.replica] + assert first.request_sha256 == second.request_sha256 + for name in ( + "observables", + "terminal_counters", + "draw_counts", + "hash_diagnostics", + ): + np.testing.assert_array_equal( + getattr(first, name), getattr(second, name) + ) + np.testing.assert_array_equal(forward[0].observables, repeated.observables) + for array in ( + forward[0].observables, + forward[0].terminal_counters, + forward[0].draw_counts, + forward[0].hash_diagnostics, + ): + assert not array.flags.writeable + with pytest.raises(ValueError): + array.flat[0] = 0 + + +def test_compensated_prefix_is_monotone_accurate_and_linear_at_large_n(): + count = 1 << 17 + weights = tuple( + 1.0 if index % 2 == 0 else np.finfo(np.float64).eps + for index in range(count) + ) + cumulative, total, operations = _compensated_prefix(weights) + exact = math.fsum(weights) + assert len(cumulative) == count + assert operations == count + assert all( + cumulative[index] <= cumulative[index + 1] + for index in range(count - 1) + ) + assert cumulative[-1] == total + assert abs(total - exact) <= _PREFIX_REL_TOL * exact + + length = count * 2 + kernel = np.ones(count, dtype=np.float64) + _, _, class_cumulative, class_total = _class_data(length, kernel) + assert len(class_cumulative) == count + assert class_cumulative[-1] == class_total + + +def _statistical_result( + *, + case_id: str, + observed: float, + expected: float, + standard_error: float, + z_score: float, +) -> tuple[str, float, float]: + threshold = z_score * standard_error + deviation = abs(observed - expected) + signed_margin = threshold - deviation + z_observed = deviation / standard_error + p_value = float(2.0 * norm.sf(z_observed)) + assert signed_margin >= 0.0, ( + f"{case_id}: raw_observed={observed:.17g}, expected={expected:.17g}, " + f"threshold={threshold:.17g}, signed_margin={signed_margin:.17g}, " + f"p_value={p_value:.17g}" + ) + return case_id, p_value, signed_margin + + +def test_statistical_case_registry_is_unique_complete_and_familywise_bounded(): + assert len(STATISTICAL_CASE_IDS) == len(set(STATISTICAL_CASE_IDS)) + expected = { + "interarrival.mean", + "interarrival.variance", + "interarrival.cdf_at_1", + "event_count.mean", + "event_count.variance", + "event_count.p0", + *(f"edge_marginal.{index}" for index in range(15)), + "no_edge.probability", + "open_edges.mean", + "open_edges.variance", + "component_count.mean", + "largest_component.mean", + "second_component.mean", + "s1_fraction.mean", + "s2_fraction.mean", + "sum_size_sq.mean", + "sum_size_fourth.mean", + "q_g.mean", + "four_sector_crossing.mean", + } + assert set(STATISTICAL_CASE_IDS) == expected + allocated_alpha = math.fsum( + FAMILYWISE_ALPHA / len(STATISTICAL_CASE_IDS) + for _ in STATISTICAL_CASE_IDS + ) + assert allocated_alpha <= FAMILYWISE_ALPHA + + +def test_complete_reference_statistical_family(): + request = make_request(kappas=[0.1], master_seed=991) + streams = _build_reference_streams(request) + sample = np.asarray( + [ + -math.log(streams.uniform(STREAM_EXPONENTIAL)) + for _ in range(12_000) + ] + ) + observations: list[tuple[str, float, float, float]] = [ + ( + "interarrival.mean", + float(np.mean(sample)), + 1.0, + 1.0 / math.sqrt(sample.size), + ), + ( + "interarrival.variance", + float(np.var(sample, ddof=1)), + 1.0, + math.sqrt(8.0 / (sample.size - 1)), + ), + ( + "interarrival.cdf_at_1", + float(np.mean(sample <= 1.0)), + 1.0 - math.exp(-1.0), + math.sqrt( + (1.0 - math.exp(-1.0)) + * math.exp(-1.0) + / sample.size + ), + ), + ] + alpha_each = FAMILYWISE_ALPHA / len(STATISTICAL_CASE_IDS) + z_score = float(norm.isf(alpha_each / 2.0)) + + length = 6 + sigma = 1.0 + kappa = 0.18 + replicas = 6_000 + kernel = periodic_kernel(length, sigma) + classes = distance_classes(length) + class_starts = np.cumsum( + [0, *(item.multiplicity for item in classes)], dtype=np.int64 + ) + edge_rates = np.concatenate( + [ + np.full(item.multiplicity, kernel[index], dtype=np.float64) + for index, item in enumerate(classes) + ] + ) + total_rate = math.fsum(edge_rates.tolist()) + + event_counts = np.empty(replicas, dtype=np.float64) + open_counts = np.empty(replicas, dtype=np.float64) + edge_hits = np.zeros(edge_rates.size, dtype=np.float64) + no_edge = 0 + component_samples = np.empty((replicas, len(COMPONENT_CASE_IDS))) + for replica in range(replicas): + request = make_request( + length=length, + sigma=sigma, + kappas=[kappa], + master_seed=0x194, + replica=replica, + kernel=kernel, + ) + run = _run_poisson_with_streams( + request, kernel, _build_reference_streams(request) + ) + event_counts[replica] = run.result.event_count + open_counts[replica] = run.result.observables[0, 0] + component_samples[replica] = run.result.observables[0, 1:10] + ids = run.edge_ids_by_checkpoint[0] + if not ids: + no_edge += 1 + for edge_id in ids: + edge_hits[edge_id] += 1.0 + + poisson_mean = kappa * total_rate + edge_probability = -np.expm1(-kappa * edge_rates) + spec = ModelSpec(length, sigma, kappa) + expected_mean = expected_open_edges(spec) + expected_variance = variance_open_edges(spec) + + observations.append( + ( + "event_count.mean", + float(np.mean(event_counts)), + poisson_mean, + math.sqrt(poisson_mean / replicas), + ) + ) + poisson_variance_se = math.sqrt( + ( + poisson_mean + + 3.0 * poisson_mean**2 + - ((replicas - 3) / (replicas - 1)) * poisson_mean**2 + ) + / replicas + ) + observations.append( + ( + "event_count.variance", + float(np.var(event_counts, ddof=1)), + poisson_mean, + poisson_variance_se, + ) + ) + poisson_zero = math.exp(-poisson_mean) + observations.append( + ( + "event_count.p0", + float(np.mean(event_counts == 0.0)), + poisson_zero, + math.sqrt(poisson_zero * (1.0 - poisson_zero) / replicas), + ) + ) + for edge_id, probability in enumerate(edge_probability): + observations.append( + ( + f"edge_marginal.{edge_id}", + float(edge_hits[edge_id] / replicas), + float(probability), + math.sqrt( + float(probability * (1.0 - probability)) / replicas + ), + ) + ) + no_edge_expected = no_edge_probability(spec) + observations.append( + ( + "no_edge.probability", + no_edge / replicas, + no_edge_expected, + math.sqrt( + no_edge_expected * (1.0 - no_edge_expected) / replicas + ), + ) + ) + observations.append( + ( + "open_edges.mean", + float(np.mean(open_counts)), + expected_mean, + math.sqrt(expected_variance / replicas), + ) + ) + fourth_central = ( + 3.0 * expected_variance**2 + + math.fsum( + ( + float(probability) + * (1.0 - float(probability)) + * ( + 1.0 + - 6.0 + * float(probability) + * (1.0 - float(probability)) + ) + ) + for probability in edge_probability + ) + ) + variance_standard_error = math.sqrt( + ( + fourth_central + - ((replicas - 3) / (replicas - 1)) * expected_variance**2 + ) + / replicas + ) + observations.append( + ( + "open_edges.variance", + float(np.var(open_counts, ddof=1)), + expected_variance, + variance_standard_error, + ) + ) + + edges = list(iter_unordered_edges(length)) + component_values = [] + component_probabilities = [] + for outcome in enumerate_graphs(spec): + sizes = outcome.component_sizes + sum_sq = math.fsum(float(size) ** 2 for size in sizes) + sum_fourth = math.fsum(float(size) ** 4 for size in sizes) + connectivity = poisson_reference.UnionFind(length) + for edge_index, (left, right) in enumerate(edges): + if outcome.mask & (1 << edge_index): + connectivity.union(left, right) + labels = connectivity.labels().tolist() + sectors: dict[int, int] = {} + for vertex, label in enumerate(labels): + sectors[label] = sectors.get(label, 0) | ( + 1 << min(3, (4 * vertex) // length) + ) + component_values.append( + ( + float(len(sizes)), + float(sizes[0]), + float(sizes[1] if len(sizes) > 1 else 0), + float(sizes[0]) / length, + float(sizes[1] if len(sizes) > 1 else 0) / length, + sum_sq, + sum_fourth, + sum_fourth / (sum_sq * sum_sq), + float(any(mask == 0b1111 for mask in sectors.values())), + ) + ) + component_probabilities.append(outcome.probability) + values = np.asarray(component_values, dtype=np.float64) + probabilities = np.asarray(component_probabilities, dtype=np.float64) + for index, case_id in enumerate(COMPONENT_CASE_IDS): + expected = float(probabilities @ values[:, index]) + variance = float( + probabilities @ ((values[:, index] - expected) ** 2) + ) + observations.append( + ( + case_id, + float(np.mean(component_samples[:, index])), + expected, + math.sqrt(variance / replicas), + ) + ) + + assert {item[0] for item in observations} == set(STATISTICAL_CASE_IDS) + results = [ + _statistical_result( + case_id=case_id, + observed=observed, + expected=expected, + standard_error=standard_error, + z_score=z_score, + ) + for case_id, observed, expected, standard_error in observations + ] + minimum_p = min(item[1] for item in results) + minimum_margin = min(item[2] for item in results) + print( + "statistical_family " + f"laws={len(results)} alpha_each={alpha_each:.17g} " + f"minimum_p_value={minimum_p:.17g} " + f"minimum_signed_margin={minimum_margin:.17g}" + ) + + +def test_result_constructor_defensively_freezes_exact_array_contract(): + arrays = { + "observables": np.zeros((1, 10), dtype=np.float64), + "terminal_counters": np.zeros((STREAM_COUNT, 4), dtype=np.uint32), + "draw_counts": np.zeros((STREAM_COUNT, 3), dtype=np.uint64), + "hash_diagnostics": np.zeros(5, dtype=np.uint64), + } + result = TrajectoryResult( + request_sha256="1" * 64, + event_count=0, + duplicate_count=0, + **arrays, + ) + for array in arrays.values(): + array.flat[0] = 1 + assert not np.any(result.observables) + assert not np.any(result.terminal_counters) + assert not np.any(result.draw_counts) + assert not np.any(result.hash_diagnostics) + for array in ( + result.observables, + result.terminal_counters, + result.draw_counts, + result.hash_diagnostics, + ): + assert not array.flags.writeable diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_poisson_sweep.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_poisson_sweep.py new file mode 100644 index 000000000..bf49bf3a4 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_poisson_sweep.py @@ -0,0 +1,1005 @@ +from __future__ import annotations + +from dataclasses import dataclass, replace +import hashlib +import math +import multiprocessing +import os +import subprocess +import sys + +import numba +import numpy as np +import pytest +from scipy.stats import norm + +import long_range_percolation.poisson_sweep as poisson_sweep_module +from long_range_percolation.alias import AliasTable, build_distance_alias +from long_range_percolation.counter_rng import ( + STREAM_ALIAS_COLUMN, + STREAM_ALIAS_THRESHOLD, + STREAM_EDGE_OFFSET, + STREAM_EXPONENTIAL, + StreamIdentity, + derive_stream_material, +) +from long_range_percolation.kernel import periodic_kernel +from long_range_percolation.poisson_reference import ( + TrajectoryRequest, + run_poisson_reference, +) +from long_range_percolation.poisson_sweep import ( + _run_poisson_kernel, + assert_nopython_signatures, + run_poisson_numba, +) + + +def _digest(values: np.ndarray) -> str: + return hashlib.sha256(values.tobytes(order="C")).hexdigest() + + +def _case( + *, + length: int = 8, + sigma: float = 1.0, + kappas: tuple[float, ...] = (0.0, 0.1, 0.4), + seed: int = 194, + replica: int = 7, + kernel: np.ndarray | None = None, +) -> tuple[TrajectoryRequest, np.ndarray, AliasTable]: + values = periodic_kernel(length, sigma) if kernel is None else kernel + request = TrajectoryRequest( + length=length, + sigma=sigma, + sigma_grid_id=f"task-7-sigma-{sigma!r}", + kappas=np.asarray(kappas, dtype=np.float64), + master_seed=seed, + phase="validation", + replica=replica, + kernel_sha256=_digest(values), + ) + table = build_distance_alias(length, sigma, values, request.kernel_sha256) + return request, values, table + + +def _assert_result_equal(left, right, *, include_hash: bool = True) -> None: + assert left.request_sha256 == right.request_sha256 + assert left.event_count == right.event_count + assert left.duplicate_count == right.duplicate_count + fields = [ + "observables", + "terminal_counters", + "draw_counts", + ] + if include_hash: + fields.append("hash_diagnostics") + for field in fields: + np.testing.assert_array_equal(getattr(left, field), getattr(right, field)) + + +def _run_replica_in_spawned_process(replica: int): + return run_poisson_numba(*_case(replica=replica)) + + +class _AuditWordStream: + def __init__(self, identity: StreamIdentity, counter_delta: int = 0): + material = derive_stream_material(identity) + self.key = [int(value) for value in material.key] + self.counter = [int(value) for value in material.initial_counter] + carry = counter_delta + for index in range(4): + total = self.counter[index] + carry + self.counter[index] = total & 0xFFFFFFFF + carry = total >> 32 + self.block = [0, 0, 0, 0] + self.lane = 4 + self.accounting = [0, 0, 0] + + def _generate(self) -> None: + c0, c1, c2, c3 = self.counter + k0, k1 = self.key + for _ in range(10): + product0 = 0xD2511F53 * c0 + product1 = 0xCD9E8D57 * c2 + c0, c1, c2, c3 = ( + ((product1 >> 32) ^ c1 ^ k0) & 0xFFFFFFFF, + product1 & 0xFFFFFFFF, + ((product0 >> 32) ^ c3 ^ k1) & 0xFFFFFFFF, + product0 & 0xFFFFFFFF, + ) + k0 = (k0 + 0x9E3779B9) & 0xFFFFFFFF + k1 = (k1 + 0xBB67AE85) & 0xFFFFFFFF + self.block[:] = (c0, c1, c2, c3) + carry = 1 + for index in range(4): + total = self.counter[index] + carry + self.counter[index] = total & 0xFFFFFFFF + carry = total >> 32 + self.lane = 0 + self.accounting[1] += 1 + + def word(self) -> int: + if self.lane == 4: + self._generate() + value = self.block[self.lane] + self.lane += 1 + self.accounting[0] += 1 + return value + + def uniform(self) -> float: + return (float(self.word()) + 0.5) * (2.0**-32) + + def bounded(self, bound: int) -> int: + threshold = ((1 << 32) - bound) % bound + while True: + word = self.word() + if word < threshold: + self.accounting[2] += 1 + continue + return word % bound + + +class _AuditEdgeSet: + def __init__(self): + self.keys = [0, 0] + self.occupied = [False, False] + self.size = 0 + self.total_probes = 0 + self.max_probe = 0 + self.rehashes = 0 + + @staticmethod + def _mix(value: int) -> int: + mask = (1 << 64) - 1 + value = (value + 0x9E3779B97F4A7C15) & mask + value = ((value ^ (value >> 30)) * 0xBF58476D1CE4E5B9) & mask + value = ((value ^ (value >> 27)) * 0x94D049BB133111EB) & mask + return (value ^ (value >> 31)) & mask + + def _probe(self, probe: int) -> None: + self.total_probes += 1 + self.max_probe = max(self.max_probe, probe) + + def _grow(self) -> None: + old_keys = self.keys + old_occupied = self.occupied + capacity = 2 * len(old_keys) + self.keys = [0] * capacity + self.occupied = [False] * capacity + for old_slot, is_occupied in enumerate(old_occupied): + if not is_occupied: + continue + value = old_keys[old_slot] + slot = self._mix(value) & (capacity - 1) + probe = 1 + while self.occupied[slot]: + self._probe(probe) + slot = (slot + 1) & (capacity - 1) + probe += 1 + self._probe(probe) + self.keys[slot] = value + self.occupied[slot] = True + self.rehashes += 1 + + def insert(self, value: int) -> bool: + while True: + capacity = len(self.keys) + slot = self._mix(value) & (capacity - 1) + probe = 1 + while self.occupied[slot]: + self._probe(probe) + if self.keys[slot] == value: + return False + slot = (slot + 1) & (capacity - 1) + probe += 1 + self._probe(probe) + if 10 * (self.size + 1) <= 7 * capacity: + self.keys[slot] = value + self.occupied[slot] = True + self.size += 1 + return True + self._grow() + + @property + def diagnostics(self) -> np.ndarray: + return np.asarray( + ( + len(self.keys), + self.size, + self.total_probes, + self.max_probe, + self.rehashes, + ), + dtype=np.uint64, + ) + + +@dataclass(frozen=True) +class _AuditRun: + observables: np.ndarray + terminal_counters: np.ndarray + draw_counts: np.ndarray + event_times: tuple[float, ...] + event_ids: tuple[int, ...] + duplicate_flags: tuple[bool, ...] + edge_sets: tuple[frozenset[int], ...] + edge_set_sha256: str + hash_diagnostics: np.ndarray + + +def _audit_observables( + length: int, edge_ids: set[int], starts: np.ndarray +) -> tuple[float, ...]: + parent = list(range(length)) + sizes = [1] * length + masks = [1 << min(3, (4 * vertex) // length) for vertex in range(length)] + + def root(vertex: int) -> int: + while parent[vertex] != vertex: + vertex = parent[vertex] + return vertex + + for edge_id in sorted(edge_ids): + selected = 0 + while not int(starts[selected]) <= edge_id < int(starts[selected + 1]): + selected += 1 + offset = edge_id - int(starts[selected]) + left = offset + right = (offset + selected + 1) % length + left_root = root(left) + right_root = root(right) + if left_root == right_root: + continue + if sizes[left_root] < sizes[right_root] or ( + sizes[left_root] == sizes[right_root] and left_root > right_root + ): + left_root, right_root = right_root, left_root + parent[right_root] = left_root + sizes[left_root] += sizes[right_root] + masks[left_root] |= masks[right_root] + + root_sizes = [sizes[index] for index in range(length) if parent[index] == index] + largest = max(root_sizes) + second = sorted(root_sizes, reverse=True)[1] if len(root_sizes) > 1 else 0 + sum_sq = math.fsum(float(value) ** 2 for value in root_sizes) + sum_fourth = math.fsum(float(value) ** 4 for value in root_sizes) + return ( + float(len(edge_ids)), + float(len(root_sizes)), + float(largest), + float(second), + float(largest) / float(length), + float(second) / float(length), + sum_sq, + sum_fourth, + sum_fourth / (sum_sq * sum_sq), + float( + any( + parent[index] == index and masks[index] == 0b1111 + for index in range(length) + ) + ), + ) + + +def _run_independent_audit( + request: TrajectoryRequest, + table: AliasTable, + *, + counter_deltas: tuple[int, int, int, int] = (0, 0, 0, 0), +) -> _AuditRun: + streams = [ + _AuditWordStream( + StreamIdentity( + request.master_seed, + request.phase, + request.length, + request.sigma_grid_id, + request.replica, + stream_id, + ), + counter_deltas[stream_id], + ) + for stream_id in range(4) + ] + starts = np.empty(table.multiplicity.size + 1, dtype=np.uint64) + starts[0] = 0 + for index, value in enumerate(table.multiplicity): + starts[index + 1] = starts[index] + value + edge_table = _AuditEdgeSet() + open_ids: set[int] = set() + rows: list[tuple[float, ...]] = [] + snapshots: list[frozenset[int]] = [] + event_times: list[float] = [] + event_ids: list[int] = [] + duplicate_flags: list[bool] = [] + checkpoint = 0 + current = 0.0 + kappa_max = float(request.kappas[-1]) + + def checkpoint_row() -> None: + rows.append(_audit_observables(request.length, open_ids, starts)) + snapshots.append(frozenset(open_ids)) + + while checkpoint < request.kappas.size and request.kappas[checkpoint] == 0.0: + checkpoint_row() + checkpoint += 1 + if kappa_max > 0.0: + while True: + hazard = -math.log(streams[STREAM_EXPONENTIAL].uniform()) + terminal_hazard = (kappa_max - current) * float(table.total_rate) + if hazard > terminal_hazard: + break + next_kappa = current + hazard / float(table.total_rate) + while ( + checkpoint < request.kappas.size + and request.kappas[checkpoint] < next_kappa + ): + checkpoint_row() + checkpoint += 1 + + class_count = table.probability.size + rejection_threshold = ((1 << 32) - class_count) % class_count + while True: + column_word = streams[STREAM_ALIAS_COLUMN].word() + product = column_word * class_count + if (product & 0xFFFFFFFF) < rejection_threshold: + streams[STREAM_ALIAS_COLUMN].accounting[2] += 1 + continue + column = product >> 32 + break + threshold = ( + float(streams[STREAM_ALIAS_THRESHOLD].word()) + 0.5 + ) * (2.0**-32) + selected = ( + column + if threshold <= float(table.probability[column]) + else int(table.alias[column]) + ) + offset = streams[STREAM_EDGE_OFFSET].bounded( + int(table.multiplicity[selected]) + ) + edge_id = int(starts[selected]) + offset + inserted = edge_table.insert(edge_id) + if inserted: + open_ids.add(edge_id) + event_times.append(next_kappa) + event_ids.append(edge_id) + duplicate_flags.append(not inserted) + current = next_kappa + while ( + checkpoint < request.kappas.size + and request.kappas[checkpoint] <= current + ): + checkpoint_row() + checkpoint += 1 + while checkpoint < request.kappas.size: + checkpoint_row() + checkpoint += 1 + + encoded_edges = np.asarray(sorted(open_ids), dtype="<u8").tobytes() + return _AuditRun( + observables=np.asarray(rows, dtype=np.float64), + terminal_counters=np.asarray( + [stream.counter for stream in streams], dtype=np.uint32 + ), + draw_counts=np.asarray( + [stream.accounting for stream in streams], dtype=np.uint64 + ), + event_times=tuple(event_times), + event_ids=tuple(event_ids), + duplicate_flags=tuple(duplicate_flags), + edge_sets=tuple(snapshots), + edge_set_sha256=hashlib.sha256(encoded_edges).hexdigest(), + hash_diagnostics=edge_table.diagnostics, + ) + + +@numba.njit(cache=True, boundscheck=True, fastmath=False) +def _run_scripted_events( + length: int, + kappas: np.ndarray, + interarrival: np.ndarray, + class_index: np.ndarray, + offsets: np.ndarray, + multiplicity: np.ndarray, + class_start: np.ndarray, +) -> tuple[np.ndarray, int, int]: + """Test-only event semantics, independent of random class selection.""" + open_ids = np.zeros(int(class_start[-1]), dtype=np.uint8) + parent = np.arange(length, dtype=np.int64) + size = np.ones(length, dtype=np.int64) + output = np.zeros((len(kappas), 3), dtype=np.int64) + event_count = 0 + duplicate_count = 0 + open_count = 0 + checkpoint = 0 + current = 0.0 + + while checkpoint < len(kappas) and kappas[checkpoint] == 0.0: + output[checkpoint, 0] = open_count + output[checkpoint, 1] = length + output[checkpoint, 2] = 1 + checkpoint += 1 + + for event in range(len(interarrival)): + next_time = current + interarrival[event] + while checkpoint < len(kappas) and kappas[checkpoint] < next_time: + components = 0 + largest = 0 + for vertex in range(length): + if parent[vertex] == vertex: + components += 1 + largest = max(largest, size[vertex]) + output[checkpoint, 0] = open_count + output[checkpoint, 1] = components + output[checkpoint, 2] = largest + checkpoint += 1 + if next_time > kappas[-1]: + break + + selected = class_index[event] + offset = offsets[event] + edge_id = int(class_start[selected]) + offset + event_count += 1 + if open_ids[edge_id]: + duplicate_count += 1 + else: + open_ids[edge_id] = 1 + open_count += 1 + distance = selected + 1 + left = offset + right = (offset + distance) % length + while parent[left] != left: + left = parent[left] + while parent[right] != right: + right = parent[right] + if left != right: + if size[left] < size[right]: + left, right = right, left + parent[right] = left + size[left] += size[right] + current = next_time + while checkpoint < len(kappas) and kappas[checkpoint] <= current: + components = 0 + largest = 0 + for vertex in range(length): + if parent[vertex] == vertex: + components += 1 + largest = max(largest, size[vertex]) + output[checkpoint, 0] = open_count + output[checkpoint, 1] = components + output[checkpoint, 2] = largest + checkpoint += 1 + + while checkpoint < len(kappas): + components = 0 + largest = 0 + for vertex in range(length): + if parent[vertex] == vertex: + components += 1 + largest = max(largest, size[vertex]) + output[checkpoint, 0] = open_count + output[checkpoint, 1] = components + output[checkpoint, 2] = largest + checkpoint += 1 + return output, event_count, duplicate_count + + +def test_scripted_event_semantics_cover_duplicates_antipodes_and_crossings(): + length = 6 + kappas = np.asarray((0.0, 0.1, 0.12, 0.2, 0.4), dtype=np.float64) + interarrival = np.asarray((0.05, 0.07, 0.0, 0.19), dtype=np.float64) + classes = np.asarray((2, 2, 0, 1), dtype=np.int64) + offsets = np.asarray((1, 1, 0, 4), dtype=np.int64) + multiplicity = np.asarray((6, 6, 3), dtype=np.uint64) + starts = np.asarray((0, 6, 12, 15), dtype=np.uint64) + + actual, events, duplicates = _run_scripted_events( + length, kappas, interarrival, classes, offsets, multiplicity, starts + ) + expected = np.asarray( + ( + (0, 6, 1), + (1, 5, 2), + (1, 5, 2), + (2, 4, 3), + (3, 4, 3), + ), + dtype=np.int64, + ) + np.testing.assert_array_equal(actual, expected) + assert (events, duplicates) == (4, 1) + if not numba.config.DISABLE_JIT: + assert _run_scripted_events.nopython_signatures + + +def test_numba_matches_reference_event_for_event_when_there_is_one_class(): + request, kernel, table = _case( + length=2, kappas=(0.0, 0.1, 0.5, 2.0), seed=991 + ) + actual = run_poisson_numba(request, kernel, table) + expected = run_poisson_reference(request, kernel) + _assert_result_equal(actual, expected, include_hash=False) + assert actual.hash_diagnostics[1] == 1 + + +def test_independent_audit_matches_every_production_checkpoint_and_counter(): + request, kernel, table = _case( + length=6, + sigma=1.0, + kappas=(0.0, 0.1, 0.3, 1.0, 3.0), + seed=0x194, + replica=23, + ) + audit = _run_independent_audit(request, table) + actual = run_poisson_numba(request, kernel, table) + + np.testing.assert_array_equal(actual.observables, audit.observables) + np.testing.assert_array_equal( + actual.terminal_counters, audit.terminal_counters + ) + np.testing.assert_array_equal(actual.draw_counts, audit.draw_counts) + np.testing.assert_array_equal( + actual.hash_diagnostics, audit.hash_diagnostics + ) + assert actual.event_count == len(audit.event_ids) + assert actual.duplicate_count == sum(audit.duplicate_flags) + assert int(actual.observables[-1, 0]) == len(audit.edge_sets[-1]) + encoded = np.asarray(sorted(audit.edge_sets[-1]), dtype="<u8").tobytes() + assert hashlib.sha256(encoded).hexdigest() == audit.edge_set_sha256 + antipodal_start = int(np.sum(table.multiplicity[:-1])) + assert any(edge_id >= antipodal_start for edge_id in audit.event_ids) + assert any(audit.duplicate_flags) + assert int(actual.hash_diagnostics[4]) > 0 + assert len(audit.edge_sets) == request.kappas.size + assert actual.draw_counts[STREAM_EXPONENTIAL, 0] == len(audit.event_ids) + 1 + + +def test_exact_terminal_event_is_included_with_prefix_and_neighbor_semantics(): + unit_kernel = np.asarray((1.0,), dtype=np.float64) + base_request, _, table = _case( + length=2, + kappas=(1.0,), + seed=0x194, + replica=31, + kernel=unit_kernel, + ) + exponential = _AuditWordStream( + StreamIdentity( + base_request.master_seed, + base_request.phase, + base_request.length, + base_request.sigma_grid_id, + base_request.replica, + STREAM_EXPONENTIAL, + ) + ) + first_hazard = -math.log(exponential.uniform()) + assert first_hazard * table.total_rate == first_hazard + + exact = replace( + base_request, kappas=np.asarray((first_hazard,), dtype=np.float64) + ) + exact_result = run_poisson_numba(exact, unit_kernel, table) + exact_audit = _run_independent_audit(exact, table) + np.testing.assert_array_equal(exact_result.observables, exact_audit.observables) + assert exact_result.event_count == 1 + np.testing.assert_array_equal( + exact_result.draw_counts[:, 0], np.asarray((1, 1, 1, 2)) + ) + + extended = replace( + base_request, + kappas=np.asarray((first_hazard, first_hazard + 1.0), dtype=np.float64), + ) + extended_result = run_poisson_numba(extended, unit_kernel, table) + np.testing.assert_array_equal( + exact_result.observables[0], extended_result.observables[0] + ) + + below = replace( + base_request, + kappas=np.asarray( + (np.nextafter(first_hazard, -math.inf),), dtype=np.float64 + ), + ) + above = replace( + base_request, + kappas=np.asarray( + (np.nextafter(first_hazard, math.inf),), dtype=np.float64 + ), + ) + below_result = run_poisson_numba(below, unit_kernel, table) + above_result = run_poisson_numba(above, unit_kernel, table) + assert below_result.event_count == 0 + assert above_result.event_count == 1 + np.testing.assert_array_equal( + above_result.observables[0], exact_result.observables[0] + ) + assert below_result.draw_counts[STREAM_EXPONENTIAL, 0] == 1 + assert above_result.draw_counts[STREAM_EXPONENTIAL, 0] == 2 + + +def test_initial_counter_perturbations_are_stream_local(monkeypatch): + unit_kernel = np.asarray((1.0,), dtype=np.float64) + provisional, _, table = _case( + length=2, + kappas=(1.0,), + seed=774, + replica=19, + kernel=unit_kernel, + ) + + cumulative = [] + for delta in (0, 1): + stream = _AuditWordStream( + StreamIdentity( + provisional.master_seed, + provisional.phase, + provisional.length, + provisional.sigma_grid_id, + provisional.replica, + STREAM_EXPONENTIAL, + ), + delta, + ) + first = -math.log(stream.uniform()) + second = first - math.log(stream.uniform()) + cumulative.append((first, second)) + lower = max(item[0] for item in cumulative) + upper = min(item[1] for item in cumulative) + assert lower < upper + request = replace( + provisional, + kappas=np.asarray(((lower + upper) / 2.0,), dtype=np.float64), + ) + original_builder = poisson_sweep_module._build_stream_state + baseline = run_poisson_numba(request, unit_kernel, table) + baseline_audit = _run_independent_audit(request, table) + assert baseline.event_count == 1 + + outcomes = [] + for changed_stream in range(4): + def perturbed_builder(request_value, stream_id=changed_stream): + state = original_builder(request_value) + counters = state[0] + carry = 1 + for word in range(4): + total = int(counters[stream_id, word]) + carry + counters[stream_id, word] = np.uint32(total & 0xFFFFFFFF) + carry = total >> 32 + return state + + with monkeypatch.context() as context: + context.setattr( + poisson_sweep_module, "_build_stream_state", perturbed_builder + ) + changed = run_poisson_numba(request, unit_kernel, table) + audit_deltas = [0, 0, 0, 0] + audit_deltas[changed_stream] = 1 + changed_audit = _run_independent_audit( + request, table, counter_deltas=tuple(audit_deltas) + ) + assert changed.event_count == baseline.event_count == 1 + unrelated = [index for index in range(4) if index != changed_stream] + np.testing.assert_array_equal( + changed.draw_counts[unrelated], baseline.draw_counts[unrelated] + ) + np.testing.assert_array_equal( + changed.terminal_counters[unrelated], + baseline.terminal_counters[unrelated], + ) + np.testing.assert_array_equal( + changed.terminal_counters, changed_audit.terminal_counters + ) + outcomes.append( + ( + changed_stream, + changed_audit.event_times, + changed_audit.edge_set_sha256, + ) + ) + + assert outcomes[STREAM_EXPONENTIAL][1] != baseline_audit.event_times + assert all( + outcome[2] == baseline_audit.edge_set_sha256 for outcome in outcomes + ) + + +def test_draw_families_are_isolated_and_accounted(): + request, kernel, table = _case(length=10, kappas=(0.0, 0.3), seed=112) + result = run_poisson_numba(request, kernel, table) + assert result.draw_counts[STREAM_EXPONENTIAL, 0] == ( + result.event_count + int(request.kappas[-1] > 0.0) + ) + assert result.draw_counts[STREAM_ALIAS_COLUMN, 0] >= result.event_count + assert result.draw_counts[STREAM_ALIAS_THRESHOLD, 0] == result.event_count + assert result.draw_counts[STREAM_EDGE_OFFSET, 0] >= result.event_count + assert result.draw_counts[STREAM_EDGE_OFFSET, 2] == ( + result.draw_counts[STREAM_EDGE_OFFSET, 0] - result.event_count + ) + assert result.draw_counts[STREAM_ALIAS_COLUMN, 2] == ( + result.draw_counts[STREAM_ALIAS_COLUMN, 0] - result.event_count + ) + + +def test_schedule_retry_and_process_order_are_byte_invariant(): + cases = [_case(replica=index) for index in (2, 9, 17)] + forward = [run_poisson_numba(*case) for case in cases] + reverse = { + case[0].replica: run_poisson_numba(*case) for case in reversed(cases) + } + retry = run_poisson_numba(*cases[0]) + with multiprocessing.get_context("spawn").Pool(2) as pool: + spawned = pool.map(_run_replica_in_spawned_process, (2, 9, 17)) + for case, result in zip(cases, forward, strict=True): + _assert_result_equal(result, reverse[case[0].replica]) + for result, process_result in zip(forward, spawned, strict=True): + _assert_result_equal(result, process_result) + _assert_result_equal(forward[0], retry) + + +def test_zero_grid_and_extreme_model_parameters_are_finite(): + for length, sigma in ( + (2, 1.0), + (8, math.ulp(1.0)), + (8, 128.0), + ): + request, kernel, table = _case( + length=length, sigma=sigma, kappas=(0.0,), replica=length + ) + result = run_poisson_numba(request, kernel, table) + assert result.event_count == 0 + assert not np.any(result.draw_counts) + assert np.all(np.isfinite(result.observables)) + np.testing.assert_array_equal( + result.observables[0, :4], (0.0, length, 1.0, 1.0 if length > 1 else 0.0) + ) + + +def test_terminal_equality_includes_event_and_overshoot_only_draws_exponential(): + request, kernel, table = _case(length=2, kappas=(0.5,), seed=4) + result = run_poisson_numba(request, kernel, table) + assert result.draw_counts[STREAM_EXPONENTIAL, 0] == result.event_count + 1 + assert result.draw_counts[STREAM_ALIAS_THRESHOLD, 0] == result.event_count + + +def test_duplicate_saturation_antipodes_and_hash_growth(): + request, kernel, table = _case( + length=4, + kappas=(25.0,), + seed=12, + kernel=np.asarray((1e-12, 1.0), dtype=np.float64), + ) + result = run_poisson_numba(request, kernel, table) + assert result.duplicate_count > 0 + assert result.observables[0, 0] == 2.0 + assert result.hash_diagnostics[1] == 2 + assert result.hash_diagnostics[4] > 0 + + +def test_host_preflight_rejects_bad_alias_before_compiled_state_allocation(monkeypatch): + request, kernel, table = _case() + bad = replace(table, total_rate=math.inf) + + def forbidden(*args, **kwargs): + raise AssertionError("allocation happened before immutable preflight") + + monkeypatch.setattr( + "long_range_percolation.poisson_sweep.allocate_edge_set", forbidden + ) + with pytest.raises(ValueError, match="alias"): + run_poisson_numba(request, kernel, bad) + + +def test_alias_semantic_preflight_rejects_identity_bias_before_rng_state(monkeypatch): + request, kernel, table = _case(length=10, sigma=0.8) + malformed = replace( + table, + probability=np.ones_like(table.probability), + alias=np.arange(table.alias.size, dtype=np.int64), + ) + + def forbidden(*args, **kwargs): + raise AssertionError("RNG state was derived before alias rejection") + + monkeypatch.setattr( + "long_range_percolation.poisson_sweep._build_stream_state", forbidden + ) + with pytest.raises(ValueError, match="represented"): + run_poisson_numba(request, kernel, malformed) + + +def test_alias_semantic_preflight_rejects_subtle_in_range_bias_before_rng_state( + monkeypatch, +): + request, kernel, table = _case(length=16, sigma=0.7) + probability = table.probability.copy() + index = int(np.argmin(probability)) + assert 0.0 < probability[index] < 1.0 + probability[index] = np.nextafter( + probability[index] + 1e-10, 1.0 + ) + malformed = replace(table, probability=probability) + + def forbidden(*args, **kwargs): + raise AssertionError("RNG state was derived before alias rejection") + + monkeypatch.setattr( + "long_range_percolation.poisson_sweep._build_stream_state", forbidden + ) + with pytest.raises(ValueError, match="represented"): + run_poisson_numba(request, kernel, malformed) + + +def test_alias_semantic_preflight_accepts_production_size_roundoff(): + request, kernel, table = _case( + length=2**18, + sigma=1.0, + kappas=(0.0,), + ) + poisson_sweep_module._validate_alias(request, kernel, table) + + +@pytest.mark.parametrize( + "mutation", + ( + lambda table: replace(table, probability=table.probability.astype(np.float32)), + lambda table: replace(table, alias=table.alias[::-1]), + lambda table: replace(table, multiplicity=table.multiplicity.copy() * 2), + lambda table: replace(table, kernel_sha256="0" * 64), + lambda table: replace(table, normalized_residual=math.nan), + ), +) +def test_host_preflight_rejects_every_alias_contract_violation(mutation): + request, kernel, table = _case() + with pytest.raises(ValueError): + run_poisson_numba(request, kernel, mutation(table)) + + +def test_tiny_and_huge_rates_have_stable_preflight(): + tiny = np.asarray((np.nextafter(0.0, 1.0),), dtype=np.float64) + request, _, table = _case( + length=2, + kappas=(np.finfo(np.float64).max,), + kernel=tiny, + ) + result = run_poisson_numba(request, tiny, table) + assert result.event_count == 0 + assert np.all(np.isfinite(result.observables)) + + huge = np.asarray((np.finfo(np.float64).max / 2.0,), dtype=np.float64) + request, _, table = _case(length=2, kappas=(1e-100,), kernel=huge) + with pytest.raises(ValueError, match="event count"): + run_poisson_numba(request, huge, table) + + +def test_compensated_hazard_clock_retains_sub_ulp_increment(): + add = getattr(poisson_sweep_module, "_compensated_hazard_add", None) + assert add is not None + high = float(2**21) + minimum_hazard = -math.log( + (float(np.iinfo(np.uint32).max) + 0.5) * (2.0**-32) + ) + next_high, next_low = add(high, 0.0, minimum_hazard) + assert next_high == high + assert 0.0 < next_low < math.ulp(high) + + +def test_open_edge_means_pass_registered_simultaneous_analytic_bounds(): + length = 6 + sigma = 0.8 + kappas = (0.05, 0.2, 0.5) + replicas = 4096 + family_alpha = 0.001 + kernel = periodic_kernel(length, sigma) + multiplicity = np.asarray((length, length, length // 2), dtype=np.float64) + samples = np.empty((replicas, len(kappas)), dtype=np.float64) + for replica in range(replicas): + request, _, table = _case( + length=length, + sigma=sigma, + kappas=kappas, + seed=0x194, + replica=replica, + kernel=kernel, + ) + samples[replica] = run_poisson_numba( + request, kernel, table + ).observables[:, 0] + + probabilities = -np.expm1(-np.outer(np.asarray(kappas), kernel)) + expected = probabilities @ multiplicity + variances = (probabilities * (1.0 - probabilities)) @ multiplicity + observed = np.mean(samples, axis=0) + alpha_each = family_alpha / len(kappas) + critical = float(norm.isf(alpha_each / 2.0)) + thresholds = critical * np.sqrt(variances / replicas) + margins = thresholds - np.abs(observed - expected) + raw_sums = np.sum(samples, axis=0, dtype=np.float64).astype(np.int64) + print( + "poisson_sweep_analytic_family " + f"replicas={replicas} family_alpha={family_alpha:.17g} " + f"alpha_each={alpha_each:.17g} raw_open_edge_sums=" + f"{raw_sums.tolist()} minimum_margin={float(np.min(margins)):.17g}" + ) + assert family_alpha <= 0.001 + assert np.all(margins >= 0.0), { + "observed": observed.tolist(), + "expected": expected.tolist(), + "variance": variances.tolist(), + "threshold": thresholds.tolist(), + "margin": margins.tolist(), + "raw_open_edge_sums": raw_sums.tolist(), + } + + +def test_result_is_immutable_and_exported_from_package(): + request, kernel, table = _case() + result = run_poisson_numba(request, kernel, table) + for value in ( + result.observables, + result.terminal_counters, + result.draw_counts, + result.hash_diagnostics, + ): + assert not value.flags.writeable + with pytest.raises(ValueError): + value.flat[0] = 0 + from long_range_percolation import run_poisson_numba as exported + + assert exported is run_poisson_numba + + +def test_python_numba_and_disabled_jit_parity(): + unit_kernel = np.asarray((1.0,), dtype=np.float64) + request, kernel, table = _case( + length=2, + kappas=(0.0, 0.7), + seed=71, + kernel=unit_kernel, + ) + compiled = run_poisson_numba(request, kernel, table) + code = """ +import hashlib +import numpy as np +from long_range_percolation.alias import build_distance_alias +from long_range_percolation.poisson_reference import TrajectoryRequest +from long_range_percolation.poisson_sweep import run_poisson_numba +kernel=np.asarray([1.0], dtype=np.float64) +digest=hashlib.sha256(kernel.tobytes()).hexdigest() +request=TrajectoryRequest(2,1.0,"task-7-sigma-1.0",np.asarray([0.0,0.7]),71,"validation",7,digest) +table=build_distance_alias(2,1.0,kernel,digest) +result=run_poisson_numba(request,kernel,table) +print(result.observables.tobytes().hex()) +print(result.terminal_counters.tobytes().hex()) +print(result.draw_counts.tobytes().hex()) +print(result.event_count, result.duplicate_count) +""" + environment = dict(os.environ) + environment["NUMBA_DISABLE_JIT"] = "1" + completed = subprocess.run( + [sys.executable, "-c", code], + check=True, + capture_output=True, + text=True, + env=environment, + ) + lines = completed.stdout.splitlines() + assert lines == [ + compiled.observables.tobytes().hex(), + compiled.terminal_counters.tobytes().hex(), + compiled.draw_counts.tobytes().hex(), + f"{compiled.event_count} {compiled.duplicate_count}", + ] + + +def test_production_kernel_has_fixed_real_nopython_signature(): + request, kernel, table = _case() + run_poisson_numba(request, kernel, table) + if not numba.config.DISABLE_JIT: + assert _run_poisson_kernel.nopython_signatures + assert len(_run_poisson_kernel.signatures) == 1 + assert_nopython_signatures() diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_production_union_find.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_production_union_find.py new file mode 100644 index 000000000..d276629fe --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_production_union_find.py @@ -0,0 +1,365 @@ +from __future__ import annotations + +import json +import os +from pathlib import Path +import subprocess +import sys + +import numba +import numpy as np +import pytest + +from long_range_percolation.observables import BasicObservables +from long_range_percolation.production_union_find import ( + _scan_basic_observables_kernel, + allocate_union_find, + scan_basic_observables, + union_incremental, + validate_union_find_state, +) +from long_range_percolation.union_find import UnionFind + + +def _expected_masks(length: int) -> np.ndarray: + return np.asarray( + [1 << min(3, (4 * index) // length) for index in range(length)], + dtype=np.uint8, + ) + + +def _assert_matches_day0( + state: tuple[np.ndarray, ...], reference: UnionFind +) -> BasicObservables: + summary = scan_basic_observables(*state) + labels = reference.labels() + roots, sizes = np.unique(labels, return_counts=True) + order = sorted( + zip(sizes.tolist(), roots.tolist()), key=lambda item: (-item[0], item[1]) + ) + expected_masks = [] + vertex_masks = _expected_masks(labels.size) + for root in roots: + expected_masks.append( + int(np.bitwise_or.reduce(vertex_masks[labels == root])) + ) + + expected_sum2 = float(sum(int(value) ** 2 for value in sizes)) + expected_sum4 = float(sum(int(value) ** 4 for value in sizes)) + assert summary.component_count == sizes.size + assert summary.largest_size == order[0][0] + assert summary.second_largest_size == ( + order[1][0] if len(order) > 1 else 0 + ) + assert summary.s1_fraction == order[0][0] / labels.size + assert summary.s2_fraction == ( + order[1][0] / labels.size if len(order) > 1 else 0.0 + ) + assert summary.sum_size_sq == expected_sum2 + assert summary.sum_size_fourth == expected_sum4 + assert summary.q_g == expected_sum4 / expected_sum2**2 + assert summary.four_sector_crossing is (0b1111 in expected_masks) + return summary + + +def test_allocate_union_find_has_fixed_array_schema_and_quarter_masks(): + state = allocate_union_find(8) + parent, size, sector_mask, moments, counts = state + assert [array.dtype for array in state] == [ + np.dtype(np.int64), + np.dtype(np.int64), + np.dtype(np.uint8), + np.dtype(np.float64), + np.dtype(np.int64), + ] + assert [array.shape for array in state] == [(8,), (8,), (8,), (2,), (3,)] + assert all(array.flags.c_contiguous for array in state) + assert all(array.flags.writeable for array in state) + np.testing.assert_array_equal(parent, np.arange(8, dtype=np.int64)) + np.testing.assert_array_equal(size, np.ones(8, dtype=np.int64)) + np.testing.assert_array_equal(sector_mask, [1, 1, 2, 2, 4, 4, 8, 8]) + np.testing.assert_array_equal(moments, [8.0, 8.0]) + np.testing.assert_array_equal(counts, [0, 8, 1]) + + +@pytest.mark.parametrize("length", [True, 0, -1, 2.0]) +def test_allocate_union_find_rejects_invalid_lengths(length): + with pytest.raises(ValueError, match="length"): + allocate_union_find(length) + + +def test_incremental_state_matches_day0_after_every_unique_edge(): + state = allocate_union_find(8) + reference = UnionFind(8) + edges = [ + (0, 1), + (2, 3), + (1, 2), + (4, 5), + (6, 7), + (5, 6), + (0, 7), + (1, 6), + ] + expected_merges = [True, True, True, True, True, True, True, False] + for index, ((left, right), expected_merged) in enumerate( + zip(edges, expected_merges), start=1 + ): + state[4][0] += 1 + merged = union_incremental(*state, left, right) + assert merged is expected_merged + assert reference.union(left, right) is expected_merged + summary = _assert_matches_day0(state, reference) + assert summary.open_edges == index + + +def test_large_star_merge_keeps_incremental_moments_within_forward_error(): + length = 10_529 + state = allocate_union_find(length) + for vertex in range(1, length): + assert union_incremental(*state, 0, vertex) is True + + assert _scan_basic_observables_kernel(*state)[0] == 0 + + +def test_duplicate_accounting_is_owned_by_the_unique_insertion_caller(): + state = allocate_union_find(4) + state[4][0] += 1 + assert union_incremental(*state, 0, 1) is True + before = tuple(array.copy() for array in state) + assert union_incremental(*state, 0, 1) is False + assert state[4][0] == 1 + for actual, expected in zip(state, before): + np.testing.assert_array_equal(actual, expected) + + +def test_equal_size_union_uses_smaller_root_and_path_halving(): + state = allocate_union_find(4) + assert union_incremental(*state, 1, 0) is True + assert union_incremental(*state, 3, 2) is True + assert union_incremental(*state, 2, 0) is True + assert state[0].tolist() == [0, 0, 0, 2] + + parent = np.asarray([0, 0, 1, 2], dtype=np.int64) + size = np.asarray([4, 1, 1, 1], dtype=np.int64) + sector_mask = np.asarray([15, 2, 4, 8], dtype=np.uint8) + moments = np.asarray([16.0, 256.0], dtype=np.float64) + counts = np.asarray([3, 1, 4], dtype=np.int64) + assert ( + union_incremental( + parent, size, sector_mask, moments, counts, 3, 0 + ) + is False + ) + np.testing.assert_array_equal(parent, [0, 0, 1, 1]) + + +def test_scan_uses_size_then_smallest_root_ties_and_exact_basic_schema(): + state = allocate_union_find(8) + for left, right in [(1, 0), (3, 2), (4, 5)]: + state[4][0] += 1 + union_incremental(*state, left, right) + summary = scan_basic_observables(*state) + assert summary == BasicObservables( + open_edges=3, + component_count=5, + largest_size=2, + second_largest_size=2, + s1_fraction=0.25, + s2_fraction=0.25, + sum_size_sq=14.0, + sum_size_fourth=50.0, + q_g=50.0 / 14.0**2, + four_sector_crossing=False, + ) + + +def test_four_sector_indicator_requires_one_component_with_all_bits(): + disconnected = allocate_union_find(8) + disconnected[4][0] = 2 + union_incremental(*disconnected, 0, 2) + union_incremental(*disconnected, 4, 6) + assert scan_basic_observables(*disconnected).four_sector_crossing is False + + crossing = allocate_union_find(8) + for edge in [(0, 2), (2, 4), (4, 6)]: + crossing[4][0] += 1 + union_incremental(*crossing, *edge) + assert scan_basic_observables(*crossing).four_sector_crossing is True + + +def test_l2_and_power_of_two_boundary_moments_do_not_integer_overflow(): + state = allocate_union_find(2) + state[4][0] = 1 + union_incremental(*state, 0, 1) + summary = scan_basic_observables(*state) + assert summary.sum_size_sq == 4.0 + assert summary.sum_size_fourth == 16.0 + assert summary.second_largest_size == 0 + + length = 2**18 + large = allocate_union_find(length) + assert large[3].tolist() == [float(length), float(length)] + half = length // 2 + large[0][:half] = 0 + large[0][half:] = half + large[1][0] = half + large[1][half] = half + large[2][0] = np.uint8(0b0011) + large[2][half] = np.uint8(0b1100) + large[3][:] = [2.0 * float(half) ** 2, 2.0 * float(half) ** 4] + large[4][:] = [length - 2, 2, half] + assert union_incremental(*large, 0, half) is True + boundary = scan_basic_observables(*large) + assert boundary.sum_size_sq == float(length) ** 2 + assert boundary.sum_size_fourth == float(length) ** 4 + assert boundary.q_g == 1.0 + + +@pytest.mark.parametrize( + ("index", "replacement", "message"), + [ + (0, np.arange(4, dtype=np.int32), "parent"), + (1, np.ones((2, 2), dtype=np.int64), "size"), + (2, np.ones(8, dtype=np.uint8)[::2], "sector_mask"), + (3, np.ones(3, dtype=np.float64), "moments"), + (4, np.ones(4, dtype=np.int64), "counts"), + ], +) +def test_state_validation_rejects_wrong_dtype_shape_or_contiguity( + index, replacement, message +): + state = list(allocate_union_find(4)) + state[index] = replacement + with pytest.raises(ValueError, match=message): + validate_union_find_state(*state) + with pytest.raises(ValueError, match=message): + scan_basic_observables(*state) + + +def test_state_validation_rejects_overlap_invalid_values_and_moment_drift(): + state = list(allocate_union_find(4)) + state[1] = state[0] + with pytest.raises(ValueError, match="overlap|share"): + validate_union_find_state(*state) + + state = list(allocate_union_find(4)) + state[4][0] = -1 + with pytest.raises(ValueError, match="open_edges"): + validate_union_find_state(*state) + + state = list(allocate_union_find(4)) + state[3][0] += 1.0e-10 + with pytest.raises(RuntimeError, match="moment"): + scan_basic_observables(*state) + + +def test_incremental_rejects_out_of_range_endpoints_without_mutation(): + state = allocate_union_find(4) + before = tuple(array.copy() for array in state) + with pytest.raises(ValueError, match="range"): + union_incremental(*state, -1, 2) + with pytest.raises(ValueError, match="range"): + union_incremental(*state, 1, 4) + for actual, expected in zip(state, before): + np.testing.assert_array_equal(actual, expected) + + +def test_numba_dispatchers_have_nopython_signatures_and_compiled_parity(): + if numba.config.DISABLE_JIT: + pytest.skip("nopython signatures do not exist with JIT disabled") + compiled_state = allocate_union_find(8) + python_state = allocate_union_find(8) + for left, right in [ + (0, 1), + (2, 3), + (1, 2), + (4, 5), + (6, 7), + (5, 6), + (0, 7), + (1, 6), + ]: + compiled_state[4][0] += 1 + python_state[4][0] += 1 + compiled_merged = union_incremental( + *compiled_state, left, right + ) + python_merged = union_incremental.py_func( + *python_state, left, right + ) + assert compiled_merged is python_merged + for actual, expected in zip(compiled_state, python_state): + np.testing.assert_array_equal(actual, expected) + assert _scan_basic_observables_kernel( + *compiled_state + ) == _scan_basic_observables_kernel.py_func(*python_state) + + @numba.njit(cache=False, boundscheck=True, fastmath=False) + def compiled_step(parent, size, sector_mask, moments, counts): + counts[0] += 1 + merged = union_incremental( + parent, size, sector_mask, moments, counts, 0, 3 + ) + scan = _scan_basic_observables_kernel( + parent, size, sector_mask, moments, counts + ) + return merged, scan + + state = allocate_union_find(4) + merged, scan = compiled_step(*state) + assert merged is True + assert scan[0] == 0 + assert scan[1:5] == (1, 3, 2, 1) + assert union_incremental.nopython_signatures + assert _scan_basic_observables_kernel.nopython_signatures + assert compiled_step.nopython_signatures + + +def test_disabled_jit_matches_expected_python_semantics(): + project = Path(__file__).resolve().parents[1] + script = """ +import json +from long_range_percolation.production_union_find import ( + allocate_union_find, scan_basic_observables, union_incremental, +) +state = allocate_union_find(4) +for left, right in ((0, 1), (2, 3), (0, 2)): + state[4][0] += 1 + union_incremental(*state, left, right) +summary = scan_basic_observables(*state) +print(json.dumps([ + state[0].tolist(), state[1].tolist(), state[2].tolist(), + state[3].tolist(), state[4].tolist(), summary.__dict__, +], sort_keys=True)) +""" + environment = os.environ.copy() + environment["NUMBA_DISABLE_JIT"] = "1" + completed = subprocess.run( + [sys.executable, "-c", script], + cwd=project, + env=environment, + check=True, + capture_output=True, + text=True, + ) + payload = json.loads(completed.stdout) + assert payload[:5] == [ + [0, 0, 0, 2], + [4, 1, 2, 1], + [15, 2, 12, 8], + [16.0, 256.0], + [3, 1, 4], + ] + assert payload[5] == { + "component_count": 1, + "four_sector_crossing": True, + "largest_size": 4, + "open_edges": 3, + "q_g": 1.0, + "s1_fraction": 1.0, + "s2_fraction": 0.0, + "second_largest_size": 0, + "sum_size_fourth": 256.0, + "sum_size_sq": 16.0, + } diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py new file mode 100644 index 000000000..2f0a79b32 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_runtime.py @@ -0,0 +1,437 @@ +import hashlib +import importlib.metadata +import json +import subprocess +import sys +from pathlib import Path + +import pytest + +from long_range_percolation import runtime +from long_range_percolation.runtime import runtime_capability, runtime_provenance + +CAPABILITY_KEYS = { + "schema_version", + "python", + "implementation", + "platform", + "machine", + "numpy", + "scipy", + "h5py", + "numba", + "llvmlite", + "cpu_name", + "cpu_features", + "threading_layer", + "numba_disable_jit", + "fastmath", + "boundscheck", +} + + +def _git(repository: Path, *arguments: str) -> str: + completed = subprocess.run( + ["git", *arguments], + cwd=repository, + check=True, + capture_output=True, + text=True, + ) + return completed.stdout.strip() + + +def _commit(repository: Path, message: str) -> None: + _git(repository, "add", "uv.lock") + subprocess.run( + [ + "git", + "-c", + "user.name=Runtime Test", + "-c", + "user.email=runtime-test@local", + "commit", + "-m", + message, + ], + cwd=repository, + check=True, + capture_output=True, + text=True, + ) + + +def _repository(tmp_path: Path) -> Path: + repository = tmp_path / "repository" + repository.mkdir() + _git(repository, "init") + (repository / "uv.lock").write_bytes(b"version = 1\n") + _commit(repository, "initial") + return repository + + +def test_numba_is_exactly_pinned_and_imports_in_fresh_python(): + declared = Path("pyproject.toml").read_text(encoding="utf-8") + version = importlib.metadata.version("numba") + assert f'"numba=={version}"' in declared + smoke = """ +import numba + +@numba.njit +def add_one(value): + return value + 1 + +assert add_one(41) == 42 +assert add_one.nopython_signatures +print(numba.__version__) +""" + completed = subprocess.run( + [sys.executable, "-c", smoke], + check=True, + capture_output=True, + text=True, + ) + assert completed.stdout.strip() == version + + +def test_runtime_capability_is_complete_and_json_stable(): + first = runtime_capability() + second = runtime_capability() + assert first == second + assert set(first) == CAPABILITY_KEYS + assert first["schema_version"] == "challenge-194-runtime-v1" + assert first["fastmath"] is False + assert first["boundscheck"] is True + assert json.loads(json.dumps(first, sort_keys=True)) == first + + +def test_runtime_provenance_is_deterministic_and_tracks_each_input( + tmp_path, monkeypatch +): + repository = _repository(tmp_path) + first = runtime_provenance(repository) + assert first == runtime_provenance(repository) + assert set(first) == { + "schema_version", + "source_revision", + "uv_lock_sha256", + "runtime_capability_sha256", + } + assert first["schema_version"] == "challenge-194-runtime-provenance-v1" + assert first["source_revision"] == _git(repository, "rev-parse", "HEAD") + assert len(first["source_revision"]) == 40 + int(first["source_revision"], 16) + for key in ("uv_lock_sha256", "runtime_capability_sha256"): + assert len(first[key]) == 64 + int(first[key], 16) + assert ( + first["uv_lock_sha256"] + == hashlib.sha256((repository / "uv.lock").read_bytes()).hexdigest() + ) + capability_bytes = json.dumps( + runtime_capability(), + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + assert ( + first["runtime_capability_sha256"] + == hashlib.sha256(capability_bytes).hexdigest() + ) + + (repository / "revision-input").write_text("changed\n", encoding="utf-8") + _git(repository, "add", "revision-input") + subprocess.run( + [ + "git", + "-c", + "user.name=Runtime Test", + "-c", + "user.email=runtime-test@local", + "commit", + "-m", + "change revision", + ], + cwd=repository, + check=True, + capture_output=True, + text=True, + ) + revision_changed = runtime_provenance(repository) + assert revision_changed["source_revision"] != first["source_revision"] + assert revision_changed["uv_lock_sha256"] == first["uv_lock_sha256"] + assert ( + revision_changed["runtime_capability_sha256"] + == first["runtime_capability_sha256"] + ) + + (repository / "uv.lock").write_bytes(b"version = 2\n") + _commit(repository, "change lock") + lock_changed = runtime_provenance(repository) + assert lock_changed["uv_lock_sha256"] != revision_changed["uv_lock_sha256"] + + changed_capability = runtime_capability() | {"threading_layer": "workqueue"} + monkeypatch.setattr(runtime, "runtime_capability", lambda: changed_capability) + capability_changed = runtime_provenance(repository) + assert ( + capability_changed["runtime_capability_sha256"] + != lock_changed["runtime_capability_sha256"] + ) + + +def test_runtime_provenance_rejects_dirty_repository(tmp_path): + repository = _repository(tmp_path) + (repository / "untracked").write_text("dirty\n", encoding="utf-8") + with pytest.raises(RuntimeError, match="repository is dirty"): + runtime_provenance(repository) + + +@pytest.mark.parametrize("lock_kind", ["missing", "directory", "symlink"]) +def test_runtime_provenance_rejects_invalid_lockfile(tmp_path, lock_kind): + repository = _repository(tmp_path) + lockfile = repository / "uv.lock" + lockfile.unlink() + if lock_kind == "directory": + lockfile.mkdir() + elif lock_kind == "symlink": + target = repository / "target.lock" + target.write_bytes(b"replacement\n") + lockfile.symlink_to(target) + with pytest.raises(RuntimeError, match="uv.lock must be a regular non-symlink"): + runtime_provenance(repository) + + +def test_runtime_provenance_rejects_malformed_revision(tmp_path, monkeypatch): + repository = _repository(tmp_path) + real_run = runtime.subprocess.run + + def malformed_revision(command, **kwargs): + if command == ["git", "rev-parse", "HEAD"]: + return subprocess.CompletedProcess(command, 0, "not-a-revision\n", "") + return real_run(command, **kwargs) + + monkeypatch.setattr(runtime.subprocess, "run", malformed_revision) + with pytest.raises(RuntimeError, match="malformed Git revision"): + runtime_provenance(repository) + + +def test_runtime_provenance_reports_git_failures(tmp_path): + repository = tmp_path / "not-a-repository" + repository.mkdir() + (repository / "uv.lock").write_bytes(b"version = 1\n") + with pytest.raises(RuntimeError, match="git status --porcelain failed"): + runtime_provenance(repository) + + +def test_runtime_provenance_reports_git_execution_failures(tmp_path, monkeypatch): + repository = _repository(tmp_path) + + def unavailable_git(*args, **kwargs): + raise FileNotFoundError("git unavailable") + + monkeypatch.setattr(runtime.subprocess, "run", unavailable_git) + with pytest.raises(RuntimeError, match="unable to execute git status --porcelain"): + runtime_provenance(repository) + + +def test_readme_documents_exact_p0_p1_collaborator_boundary(): + readme = Path("README.md").read_text(encoding="utf-8") + required = ( + "scripts/download_pilot.sh", + "scripts/run_pilot.py verify --run-spec", + "scripts/analyze_pilot.py analyze --run-spec", + "--output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p0_analysis.json", + "scripts/analyze_pilot.py build-p1 --analysis", + "--output /home/footman/code/quantum.harness-challenge-194/results/challenge-194/p1_protocol.json", + "scripts/analyze_pilot.py verify --analysis", + "--p1-protocol", + "sha256sum", + "e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8", + "44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b", + "P0 extension required before P1 publication: 0.9, 1.0", + "scripts/analyze_pilot.py build-p0-extension", + "scripts/run_pilot.py build-extension-spec", + "--p0-evidence-root /absolute/results/challenge-194/pilot-p0-739880d", + "never inferred from a checkout-local", + "scripts/pilot_extension_build_slurm.sh", + "scripts/pilot_extension_array_slurm.sh", + "The versioned P0 extension is complete and deeply verified", + "p1_protocol.json does not exist", + "P1 has not been published or executed", + "verified-existing", + "scripts/analyze_pilot.py analyze-extension --run-spec", + "results/challenge-194/p0_extension_v1_protocol.json", + "results/challenge-194/pilot-p0-extension-v1/run_spec.json", + "results/challenge-194/p0_extension_v1_analysis.json", + "scripts/analyze_pilot.py combine --p0-analysis", + "results/challenge-194/p0_combined_analysis_v2.json", + "scripts/analyze_pilot.py select --analysis", + "--p0-analysis", + "--extension-analysis", + "--extension-run-spec", + "--extension-protocol", + "same\nfive trusted inputs", + "results/challenge-194/p0_combined_brackets_v2.json", + "143d35ac52923cff2d24c43d304a75c2d04d3c66", + "P0 analysis is authenticated by the exact dual root hashes", + "byte-identical supplied extension analysis", + ) + for text in required: + assert text in readme + assert "P1 was executed" not in readme + assert "P1 was published" not in readme + + +def test_pilot_plan_freezes_selector_and_exploratory_boundary(): + plan = Path("PILOT_PLAN.md").read_text(encoding="utf-8") + required = ( + "Use the two largest P0 sizes", + "sign change", + "[0.25, 0.75]", + "narrowest", + "lower coupling", + "maximum absolute", + "sigma `1.1`", + "P0 extension", + "P0 and P1 remain exploratory", + "confirmatory", + "challenge-194-p0-extension-protocol-v1", + "challenge-194-p0-extension-run-spec-v1", + "challenge-194-p0-extension-progress-v1", + "76dc7e07639ed085873a8f291cc2aaee0e8942ddac8efce3982743dd67491071", + "d40b4a2afac533d74965513513fff1870918831000b2e040063ca2a0e29ad091", + "40-minute", + "canonical decimal IDs", + "three submission batches", + "six acceptance checks", + "${RESULTS_ROOT}/pilot-p0-739880d", + "No construction or validation", + "path may infer these gitignored artifacts", + ) + for text in required: + assert text in plan + + +def test_extension_build_wrapper_freezes_resources_paths_and_environment(): + wrapper = Path("scripts/pilot_extension_build_slurm.sh").read_text(encoding="utf-8") + required = ( + "#SBATCH --cpus-per-task=1", + "#SBATCH --mem=1800M", + "#SBATCH --time=00:10:00", + 'P0_ANALYSIS_PATH="${HARNESS_RUN_SPEC}"', + 'RESULTS_ROOT="$(dirname "${P0_ANALYSIS_PATH}")"', + 'P0_EVIDENCE_ROOT="${RESULTS_ROOT}/pilot-p0-739880d"', + 'EXTENSION_PROTOCOL_PATH="${RESULTS_ROOT}/p0_extension_v1_protocol.json"', + 'VALIDATION_REPORT_PATH="${RESULTS_ROOT}/validation-prod-877ab93/report/report.json"', + 'EXTENSION_ROOT="${RESULTS_ROOT}/pilot-p0-extension-v1"', + "44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b", + "unset PYTHONHOME PYTHONUSERBASE PYTHONPATH", + "scripts/analyze_pilot.py build-p0-extension", + '--p0-evidence-root "${P0_EVIDENCE_ROOT}"', + "scripts/run_pilot.py build-extension-spec", + ) + for text in required: + assert text in wrapper + + +def test_extension_operational_contract_matches_approval_registry(): + approval = json.loads( + Path("pilot_correctness_approval.json").read_text(encoding="utf-8") + ) + package = f"validation-prod-{approval['approval_revision'][:7]}" + validation_path = f"{package}/report/report.json" + wrapper = Path("scripts/pilot_extension_build_slurm.sh").read_text(encoding="utf-8") + pilot_plan = Path("PILOT_PLAN.md").read_text(encoding="utf-8") + readme = Path("README.md").read_text(encoding="utf-8") + implementation_plan = ( + Path(__file__).resolve().parents[6] + / "docs/superpowers/plans/2026-07-30-challenge-194-p0-extension.md" + ).read_text(encoding="utf-8") + + for document in (wrapper, pilot_plan, readme, implementation_plan): + assert validation_path in document + assert "validation-prod-fd0aa31-compute" not in document + for field in ( + "approval_revision", + "report_sha256", + "run_spec_sha256", + "protocol_sha256", + "check_registry_sha256", + "scientific_engine_sha256", + ): + assert approval[field] in pilot_plan + assert approval[field] in readme + + remote_root_python = ( + "/work/share/giggleliu/jiangweiqi/" + "quantum.harness-challenge-194/.venv/bin/python" + ) + for document in (pilot_plan, readme, implementation_plan): + assert remote_root_python in document + assert approval["report_sha256"] in implementation_plan + assert ( + "quantum.harness-challenge-194/tracks/qmc/solutions/" + "frustration-free/challenge-194/.venv/bin/python" not in implementation_plan + ) + assert "quantum.harness-p0-extension-v3" in implementation_plan + assert "challenge-194-p0-extension-v3.bundle" in implementation_plan + assert "failed `v1` and `v2`" in implementation_plan + assert ( + 'REMOTE_ROOT="${REMOTE_RESULTS}/pilot-p0-extension-v1"' in implementation_plan + ) + assert ( + 'REMOTE_PROTOCOL="${REMOTE_RESULTS}/p0_extension_v1_protocol.json"' + in implementation_plan + ) + + +def test_extension_bundle_publication_is_remote_no_clobber(): + plan = ( + Path(__file__).resolve().parents[6] + / "docs/superpowers/plans/2026-07-30-challenge-194-p0-extension.md" + ).read_text(encoding="utf-8") + preflight = """ssh wuzh02-jiangweiqi " + set -euo pipefail + test ! -e '${REMOTE_BUNDLE}' + test ! -e '${REMOTE_REPO}' + test ! -e '${REMOTE_BUNDLE_STAGE}' +\"""" + staging_upload = 'scp "${LOCAL_BUNDLE}" "wuzh02-jiangweiqi:${REMOTE_BUNDLE_STAGE}"' + staged_hash = "sha256sum '${REMOTE_BUNDLE_STAGE}'" + install = "ln -- '${REMOTE_BUNDLE_STAGE}' '${REMOTE_BUNDLE}'" + file_sync = "sync -f -- '${REMOTE_BUNDLE}'" + final_hash = "sha256sum '${REMOTE_BUNDLE}'" + remove_stage = "rm -- '${REMOTE_BUNDLE_STAGE}'" + + assert 'BUNDLE_SHA256="$(sha256sum "${LOCAL_BUNDLE}"' in plan + assert 'REMOTE_BUNDLE_STAGE="${REMOTE_BUNDLE}.upload-' in plan + assert preflight in plan + assert staging_upload in plan + for command in (staged_hash, install, file_sync, final_hash, remove_stage): + assert command in plan + assert ( + plan.index(preflight) + < plan.index(staging_upload) + < plan.index(staged_hash) + < plan.index(install) + < plan.index(file_sync) + < plan.index(final_hash) + < plan.index(remove_stage) + ) + assert 'scp "${LOCAL_BUNDLE}" "wuzh02-jiangweiqi:${REMOTE_BUNDLE}"' not in plan + assert "Preserve the staging path on any failure" in plan + + +@pytest.mark.parametrize( + "invalid_value", + [{1, 2}, ("not", "canonical")], +) +def test_runtime_provenance_rejects_noncanonical_capability( + tmp_path, monkeypatch, invalid_value +): + repository = _repository(tmp_path) + capability = runtime_capability() | {"cpu_features": invalid_value} + monkeypatch.setattr(runtime, "runtime_capability", lambda: capability) + with pytest.raises(RuntimeError, match="canonical JSON"): + runtime_provenance(repository) diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_union_find.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_union_find.py new file mode 100644 index 000000000..d7e98e347 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_union_find.py @@ -0,0 +1,117 @@ +import numpy as np +import pytest + +import long_range_percolation as lrp +from long_range_percolation.sample import GraphSample +from long_range_percolation.union_find import UnionFind + + +def test_union_find_returns_deterministic_labels_and_sizes(): + union_find = UnionFind(6) + for left, right in [(4, 5), (1, 2), (0, 2), (3, 5)]: + union_find.union(left, right) + np.testing.assert_array_equal(union_find.labels(), [0, 0, 0, 3, 3, 3]) + np.testing.assert_array_equal(union_find.component_sizes(), [3, 3]) + + +def test_union_find_equal_size_tie_breaking_uses_smaller_root(): + union_find = UnionFind(4) + for left, right in [(0, 1), (2, 3), (0, 2)]: + union_find.union(left, right) + np.testing.assert_array_equal(union_find.labels(), [0, 0, 0, 0]) + + +def test_graph_sample_rejects_duplicate_or_noncanonical_edges(): + labels = np.arange(4) + with pytest.raises(ValueError, match="canonical"): + GraphSample(4, np.array([[2, 1]]), labels) + with pytest.raises(ValueError, match="duplicate"): + GraphSample(4, np.array([[0, 1], [0, 1]]), labels) + + +def test_graph_sample_rejects_non_integer_array_dtypes(): + labels = np.arange(4, dtype=np.int64) + with pytest.raises(ValueError, match="integer dtype"): + GraphSample(4, np.array([[0.5, 1.0]]), labels) + with pytest.raises(ValueError, match="integer dtype"): + GraphSample( + 4, + np.array([[0, 1]], dtype=np.int64), + np.array([True, False, True, False], dtype=np.bool_), + ) + with pytest.raises(ValueError, match="integer dtype"): + GraphSample( + 4, + np.array([[0, 1]], dtype=np.int64), + np.array([0, 1, 2, 3], dtype=object), + ) + with pytest.raises(ValueError, match="integer dtype"): + GraphSample(4, np.array([[0, 1]], dtype=np.complex128), labels) + unsafe = np.array([np.iinfo(np.int64).max + 1], dtype=np.uint64) + with pytest.raises(ValueError, match="int64"): + GraphSample(4, np.array([[0, 1]], dtype=np.int64), unsafe.repeat(4)) + + +def test_graph_sample_defensive_copy_isolates_caller_mutation(): + edges_in = np.array([[0, 1], [2, 3]], dtype=np.int64) + labels_in = np.array([0, 0, 2, 2], dtype=np.int64) + sample = GraphSample(4, edges_in, labels_in) + edges_in[0, 0] = 99 + labels_in[0] = 99 + np.testing.assert_array_equal(sample.edges, [[0, 1], [2, 3]]) + np.testing.assert_array_equal(sample.labels, [0, 0, 2, 2]) + + +def test_graph_sample_stored_arrays_are_read_only(): + sample = GraphSample( + 4, + np.array([[0, 1], [2, 3]], dtype=np.int64), + np.array([0, 0, 2, 2], dtype=np.int64), + ) + assert sample.edges.flags.writeable is False + assert sample.labels.flags.writeable is False + with pytest.raises(ValueError): + sample.edges[0, 0] = 1 + with pytest.raises(ValueError): + sample.labels[0] = 1 + + +def test_graph_sample_accepts_empty_edge_array(): + sample = GraphSample( + 3, + np.empty((0, 2), dtype=np.int64), + np.array([0, 1, 2], dtype=np.int64), + ) + assert sample.edges.shape == (0, 2) + assert sample.edges.dtype == np.int64 + np.testing.assert_array_equal(sample.labels, [0, 1, 2]) + + +def test_graph_sample_rejects_label_partition_mismatch(): + with pytest.raises(ValueError, match="edge-induced partition"): + GraphSample( + 4, + np.array([[0, 1], [2, 3]], dtype=np.int64), + np.array([0, 0, 0, 0], dtype=np.int64), + ) + + +def test_graph_sample_rejects_out_of_range_endpoints(): + labels = np.arange(4, dtype=np.int64) + with pytest.raises(ValueError, match="out of range"): + GraphSample(4, np.array([[-1, 1]], dtype=np.int64), labels) + with pytest.raises(ValueError, match="out of range"): + GraphSample(4, np.array([[0, 4]], dtype=np.int64), labels) + + +def test_graph_sample_rejects_unsorted_canonical_edges(): + labels = np.arange(4, dtype=np.int64) + with pytest.raises(ValueError, match="sorted"): + GraphSample(4, np.array([[2, 3], [0, 1]], dtype=np.int64), labels) + + +def test_package_root_exports_union_find_and_graph_sample(): + assert lrp.UnionFind is UnionFind + assert lrp.GraphSample is GraphSample + assert "UnionFind" in lrp.__all__ + assert "GraphSample" in lrp.__all__ diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_validation.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_validation.py new file mode 100644 index 000000000..8af3f2d1b --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_validation.py @@ -0,0 +1,522 @@ +from __future__ import annotations + +import ast +from dataclasses import replace +import json +import os +from pathlib import Path +import subprocess +import sys +from types import SimpleNamespace + +import pytest +import numpy as np + +import long_range_percolation.validation as validation +from long_range_percolation.trajectory import ( + TrajectoryDiagnostics, + TrajectoryRequest, + TrajectoryResult, +) +from long_range_percolation.validation import ( + FAMILYWISE_ALPHA, + KAPPAS, + LENGTHS, + MASTER_SEEDS, + SAMPLES_BY_LENGTH, + SAMPLERS, + SIGMAS, + VALIDATION_PROTOCOL_VERSION, + ValidationProtocol, + canonical_report_bytes, + run_production_validation, +) + + +def test_production_protocol_is_exactly_frozen(): + protocol = ValidationProtocol.production_v1() + assert VALIDATION_PROTOCOL_VERSION == "challenge-194-validation-v1" + assert FAMILYWISE_ALPHA == 0.001 + assert LENGTHS == (4, 6, 8, 16, 32, 64, 128, 256) + assert SIGMAS == (0.8, 1.0, 1.1) + assert KAPPAS == (0.0, 0.25, 0.7, 2.0, 6.0) + assert SAMPLES_BY_LENGTH == { + 4: 32768, + 6: 32768, + 8: 32768, + 16: 16384, + 32: 8192, + 64: 4096, + 128: 2048, + 256: 1024, + } + assert SAMPLERS == ( + "quadratic", + "geometric", + "poisson-reference", + "poisson-numba", + ) + assert MASTER_SEEDS == tuple(range(194_000_000, 194_032_768)) + assert protocol.is_production + assert protocol.permutation_replicates == 49_999 + assert protocol.multinomial_replicates == 49_999 + + +def test_registry_denominators_are_frozen_before_sampling(): + protocol = ValidationProtocol.production_v1() + registry = protocol.case_registry + assert len(registry) == len(LENGTHS) * len(SIGMAS) * len(KAPPAS) + assert registry[0].case_id == "L4/sigma-0x1.999999999999ap-1/kappa-0x0.0p+0" + assert registry[-1].case_id == "L256/sigma-0x1.199999999999ap+0/kappa-0x1.8000000000000p+2" + assert protocol.family_denominators == validation.frozen_family_denominators( + LENGTHS, SIGMAS, KAPPAS + ) + assert set(protocol.family_denominators) == set(validation.STATISTICAL_FAMILIES) + assert all(value > 0 for value in protocol.family_denominators.values()) + + +def test_reduced_constructor_cannot_masquerade_as_production(): + protocol = ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.0, 0.25), + samples=4, + replicates=7, + ) + assert not protocol.is_production + assert protocol.samples_by_length == {4: 4} + assert protocol.permutation_replicates == 7 + assert protocol.multinomial_replicates == 7 + with pytest.raises(ValueError, match="production"): + protocol.require_production() + + +def test_reduced_gate_writes_complete_canonical_report(tmp_path: Path): + protocol = ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.0, 0.25), + samples=8, + replicates=31, + ) + output = tmp_path / "nested" / "report.json" + report = run_production_validation(protocol, output) + assert output.read_bytes() == canonical_report_bytes(report) + assert json.loads(output.read_bytes()) == report + assert report["schema_version"] == VALIDATION_PROTOCOL_VERSION + assert report["protocol"]["familywise_alpha"] == FAMILYWISE_ALPHA.hex() + assert report["protocol"]["family_denominators"] == protocol.family_denominators + assert report["family_count"] == len({item["family"] for item in report["checks"]}) + assert report["minimum_margin"] == min( + float(item["margin"]) for item in report["checks"] + ) + required = { + "family", + "case_id", + "raw", + "expected", + "threshold", + "margin", + "passed", + } + assert report["checks"] + assert all(required <= set(item) for item in report["checks"]) + assert all(isinstance(item["passed"], bool) for item in report["checks"]) + assert report["passed"] == all(item["passed"] for item in report["checks"]) + + +def test_jobs_change_scheduling_only(tmp_path: Path): + base = ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.0,), + samples=2, + replicates=3, + ) + serial = run_production_validation(replace(base, jobs=1), tmp_path / "one.json") + parallel = run_production_validation(replace(base, jobs=2), tmp_path / "two.json") + assert validation.payload_without_elapsed(serial) == validation.payload_without_elapsed( + parallel + ) + + +def test_backend_exception_is_published_as_failed_check(monkeypatch, tmp_path: Path): + protocol = ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.25,), + samples=2, + replicates=3, + ) + + def broken(*args, **kwargs): + raise RuntimeError("malformed backend") + + monkeypatch.setattr(validation, "run_poisson_numba", broken) + report = run_production_validation(protocol, tmp_path / "failure.json") + failures = [item for item in report["checks"] if not item["passed"]] + assert not report["passed"] + assert any(item["family"] == "backend-integrity" for item in failures) + assert all(float(item["margin"]) < 0.0 for item in failures) + + +def test_report_publication_rejects_symlinks(tmp_path: Path): + target = tmp_path / "target.json" + target.write_text("unchanged", encoding="utf-8") + output = tmp_path / "report.json" + output.symlink_to(target) + protocol = ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.0,), + samples=1, + replicates=1, + ) + with pytest.raises(RuntimeError, match="symlink"): + run_production_validation(protocol, output) + assert target.read_text(encoding="utf-8") == "unchanged" + + +def test_sampler_modules_are_structurally_independent(): + root = Path(validation.__file__).parent + modules = ("oracle.py", "geometric.py", "poisson_reference.py", "poisson_sweep.py") + names = {Path(item).stem for item in modules} + imports: dict[str, set[str]] = {} + for filename in modules: + tree = ast.parse((root / filename).read_text(encoding="utf-8")) + imported = set() + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + imported.add(node.module.rsplit(".", 1)[-1]) + elif isinstance(node, ast.Import): + imported.update(alias.name.rsplit(".", 1)[-1] for alias in node.names) + imports[Path(filename).stem] = imported & names + assert imports["oracle"] == set() + assert imports["geometric"] == set() + assert imports["poisson_reference"] == set() + assert imports["poisson_sweep"] == set() + assert validation.assert_sampler_structure() is None + + +def test_sampler_import_graph_has_no_sampler_specific_paths(): + root = Path(validation.__file__).parent + samplers = {"oracle", "geometric", "poisson_reference", "poisson_sweep"} + module_names = {item.stem for item in root.glob("*.py")} + graph: dict[str, set[str]] = {} + for source in root.glob("*.py"): + imported: set[str] = set() + tree = ast.parse(source.read_text(encoding="utf-8")) + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + imported.add(node.module.rsplit(".", 1)[-1]) + graph[source.stem] = imported & module_names + for source in samplers: + pending = list(graph.get(source, ())) + visited: set[str] = set() + while pending: + target = pending.pop() + if target in visited: + continue + visited.add(target) + assert target not in samplers - {source} + pending.extend(graph.get(target, ())) + + +def test_neutral_trajectory_contracts_preserve_reference_reexports(): + from long_range_percolation.poisson_reference import ( + TrajectoryDiagnostics as ReferenceDiagnostics, + TrajectoryRequest as ReferenceRequest, + TrajectoryResult as ReferenceResult, + ) + + assert ReferenceRequest is TrajectoryRequest + assert ReferenceResult is TrajectoryResult + assert ReferenceDiagnostics is TrajectoryDiagnostics + + +def test_validation_observables_ignore_absent_union_find_labels(): + edges = np.asarray(((0, 1), (1, 2)), dtype=np.int64) + labels = np.asarray((0, 0, 0, 3), dtype=np.int64) + observed = validation._graph_observables(4, edges, labels) + assert observed[1] == 2.0 + assert observed[2] == 3.0 + assert observed[3] == 1.0 + + +def test_four_backends_are_frozen_into_every_applicable_family(tmp_path: Path): + protocol = ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.25,), + samples=8, + replicates=31, + ) + report = run_production_validation(protocol, tmp_path / "four-way.json") + assert report["passed"] + assert len(validation.PAIR_NAMES) == 6 + assert { + pair + for pair in validation.PAIR_NAMES + if "poisson-reference" in pair + } == { + ("quadratic", "poisson-reference"), + ("geometric", "poisson-reference"), + ("poisson-reference", "poisson-numba"), + } + for family in ( + "all-graph-probability", + "edge-class-frequency", + "no-edge", + ): + case_ids = { + check["case_id"] + for check in report["checks"] + if check["family"] == family + } + assert all(any(f"/{sampler}" in case_id for case_id in case_ids) for sampler in SAMPLERS) + for family in ( + "bond-length", + "component-partition", + "open-count", + "S1", + "S2", + "QG", + "four-sector", + "normalized-second-moment", + "normalized-fourth-moment", + ): + checks = [check for check in report["checks"] if check["family"] == family] + assert len(checks) == 6 + assert any("poisson-reference" in check["case_id"] for check in checks) + + +def test_component_partition_records_actual_descending_tuples(tmp_path: Path): + protocol = ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.25,), + samples=8, + replicates=31, + ) + report = run_production_validation(protocol, tmp_path / "partitions.json") + checks = [ + check + for check in report["checks"] + if check["family"] == "component-partition" + ] + assert checks + for check in checks: + raw = check["raw"] + assert raw["bins"] + assert all( + isinstance(item, list) + and item == sorted(item, reverse=True) + and sum(item) == 4 + for item in raw["bins"] + ) + assert sum(raw["left_counts"]) == 8 + assert sum(raw["right_counts"]) == 8 + + +def test_normalized_moment_schema_and_values_are_exact(tmp_path: Path): + schema = validation.OBSERVABLE_SCHEMA + assert schema["normalized-second-moment"] == { + "formula": "sum_C(|C|^2)/L^2", + "source_column": 6, + "normalization_power": 2, + } + assert schema["normalized-fourth-moment"] == { + "formula": "sum_C(|C|^4)/L^4", + "source_column": 7, + "normalization_power": 4, + } + raw = np.asarray((2.0, 0.0, 3.0, 1.0, 0.75, 0.25, 10.0, 82.0, 0.82, 0.0)) + assert validation._scalar_values( + raw.reshape(1, -1), "normalized-second-moment", 4 + ).tolist() == [10.0 / 16.0] + assert validation._scalar_values( + raw.reshape(1, -1), "normalized-fourth-moment", 4 + ).tolist() == [82.0 / 256.0] + report = run_production_validation( + ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.25,), + samples=4, + replicates=7, + ), + tmp_path / "moments.json", + ) + assert report["protocol"]["observable_schema"] == schema + moment_check = next( + check + for check in report["checks"] + if check["family"] == "normalized-second-moment" + ) + assert "left_raw_sum" in moment_check["raw"] + assert "right_raw_sum" in moment_check["raw"] + + +def test_malformed_python_reference_diagnostics_fail_closed(monkeypatch, tmp_path: Path): + protocol = ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.25,), + samples=2, + replicates=3, + ) + + def malformed(*args, **kwargs): + return SimpleNamespace( + result=SimpleNamespace( + observables=np.zeros((1, 9), dtype=np.float64), + event_count=0, + duplicate_count=0, + ), + edge_ids_by_checkpoint=(frozenset(),), + event_times=(), + ) + + monkeypatch.setattr( + validation, "run_poisson_reference_with_diagnostics", malformed + ) + report = run_production_validation(protocol, tmp_path / "malformed-reference.json") + assert not report["passed"] + assert any( + check["family"] == "backend-integrity" + and "Python reference" in check["raw"]["error"] + for check in report["checks"] + ) + + +def test_all_graph_exact_coverage_is_per_graph_and_four_backend(tmp_path: Path): + report = run_production_validation( + ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.25,), + samples=8, + replicates=31, + ), + tmp_path / "coverage.json", + ) + exact = next( + check + for check in report["checks"] + if check["family"] == "all-graph-exact" + ) + assert exact["raw"]["coverage"]["L4"]["graph_count"] == 64 + assert exact["raw"]["coverage"]["L4"]["probabilities_compared"] == 64 + assert exact["raw"]["coverage"]["L4"]["maximum_product_error"] >= 0.0 + coverage = report["coverage"]["all_graph_probability"] + assert coverage["backends"] == list(SAMPLERS) + assert coverage["lengths"] == [4] + assert coverage["comparison"] == "per-mask exact product-measure binomial" + + +def _minimal_cli_report(passed: bool = True) -> dict[str, object]: + protocol = ValidationProtocol.production_v1() + families = sorted( + set(validation.EXACT_FAMILIES) | set(validation.STATISTICAL_FAMILIES) + ) + checks = [ + { + "family": family, + "case_id": "cli-fixture", + "raw": {"count": 1}, + "expected": {"count": 1}, + "threshold": 0.0, + "margin": 0.0 if passed else -1.0, + "passed": passed, + } + for family in families + ] + return { + "schema_version": validation.VALIDATION_PROTOCOL_VERSION, + "protocol": validation._protocol_document(protocol), + "runtime_capability": {}, + "source": {}, + "coverage": { + "all_graph_probability": { + "backends": list(SAMPLERS), + "lengths": [4, 6], + "comparison": "per-mask exact product-measure binomial", + } + }, + "checks": checks, + "family_count": len(families), + "minimum_margin": 0.0 if passed else -1.0, + "passed": passed, + "elapsed_seconds": 0.0, + } + + +def _run_cli_fixture( + tmp_path: Path, + report: dict[str, object] | None, + *, + backend_exception: bool = False, +) -> tuple[subprocess.CompletedProcess[str], Path]: + fixture = tmp_path / "fixture.json" + if report is not None: + fixture.write_bytes(validation.canonical_report_bytes(report)) + output = tmp_path / "cli-report.json" + script = Path(__file__).parents[1] / "scripts" / "validate_production.py" + code = """ +import json +from pathlib import Path +import runpy +import sys +import long_range_percolation.validation as validation +fixture = Path(sys.argv[2]) +backend_exception = sys.argv[4] == "1" +def fake(protocol, output): + if backend_exception: + raise RuntimeError("backend exploded") + report = json.loads(fixture.read_text(encoding="utf-8")) + output.write_bytes(validation.canonical_report_bytes(report)) + return report +validation.run_production_validation = fake +sys.argv = [sys.argv[1], "--protocol", "production-v1", "--jobs", "1", "--output", sys.argv[3]] +runpy.run_path(sys.argv[0], run_name="__main__") +""" + completed = subprocess.run( + [ + sys.executable, + "-c", + code, + str(script), + str(fixture), + str(output), + "1" if backend_exception else "0", + ], + cwd=script.parents[1], + env=dict(os.environ), + capture_output=True, + text=True, + ) + return completed, output + + +def test_cli_subprocess_exit_zero_only_for_valid_passing_report(tmp_path: Path): + completed, output = _run_cli_fixture(tmp_path, _minimal_cli_report()) + assert completed.returncode == 0, completed.stderr + assert json.loads(output.read_text(encoding="utf-8"))["passed"] is True + + +@pytest.mark.parametrize("failure", ("failed", "missing", "schema", "backend")) +def test_cli_subprocess_fails_closed_for_invalid_evidence(tmp_path: Path, failure: str): + report = _minimal_cli_report(passed=failure != "failed") + if failure == "missing": + report["checks"] = report["checks"][1:] + report["family_count"] -= 1 + elif failure == "schema": + report["schema_version"] = "corrupt" + completed, output = _run_cli_fixture( + tmp_path, + report, + backend_exception=failure == "backend", + ) + assert completed.returncode != 0 + if output.exists(): + assert json.loads(output.read_text(encoding="utf-8")).get("passed") is not True diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_validation_shards.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_validation_shards.py new file mode 100644 index 000000000..92f96795d --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_validation_shards.py @@ -0,0 +1,664 @@ +from __future__ import annotations + +import importlib.util +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys + +import pytest + +import long_range_percolation.validation as validation_module +import long_range_percolation.validation_shards as shards +from long_range_percolation.validation import ( + ValidationProtocol, + canonical_report_bytes, + payload_without_elapsed, + run_production_validation, +) +from long_range_percolation.validation_shards import ( + RUN_SPEC_SCHEMA, + build_validation_run_spec, + canonical_scientific_report_bytes, +) + + +SOLUTION = Path(__file__).resolve().parents[1] +SCRIPT = SOLUTION / "scripts" / "validation_shard.py" +WRAPPER = SOLUTION / "scripts" / "validation_array_slurm.sh" +CLI_SPEC = importlib.util.spec_from_file_location("validation_shard_cli", SCRIPT) +assert CLI_SPEC is not None and CLI_SPEC.loader is not None +CLI = importlib.util.module_from_spec(CLI_SPEC) +CLI_SPEC.loader.exec_module(CLI) + + +@pytest.fixture(autouse=True) +def clean_source(monkeypatch: pytest.MonkeyPatch): + revision = validation_module._repository_state()["source_revision"] + source = { + "source_revision": revision, + "clean_tree": True, + "provenance_error": None, + } + monkeypatch.setattr(shards, "_repository_state", lambda: source) + monkeypatch.setattr(validation_module, "_repository_state", lambda: source) + + +def _protocol(jobs: int = 1) -> ValidationProtocol: + return ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.0, 0.25), + samples=4, + replicates=7, + jobs=jobs, + ) + + +def _prepare( + tmp_path: Path, + *, + order: tuple[int, ...] = (0, 1), +) -> tuple[Path, dict[str, object]]: + root = tmp_path / "shards" + run_spec_path = root / "run_spec.json" + shards._write_test_run_spec(_protocol(), root, run_spec_path) + shards._run_test_global_checks(run_spec_path) + for index in order: + shards._run_test_cell(run_spec_path, index) + return run_spec_path, json.loads(run_spec_path.read_text(encoding="utf-8")) + + +def test_production_run_spec_has_exact_120_opaque_cells(tmp_path: Path): + spec = build_validation_run_spec( + ValidationProtocol.production_v1(), tmp_path / "production" + ) + assert spec["schema_version"] == RUN_SPEC_SCHEMA + assert len(spec["cells"]) == 120 + assert [cell["case_index"] for cell in spec["cells"]] == list(range(120)) + assert len({cell["case_id"] for cell in spec["cells"]}) == 120 + assert len({cell["cell_sha256"] for cell in spec["cells"]}) == 120 + assert all(cell["partial_path"].startswith("cells/") for cell in spec["cells"]) + assert all( + cell["manifest_path"].startswith("manifests/") for cell in spec["cells"] + ) + assert spec["protocol"]["name"] == "production-v1" + assert spec["protocol"]["sha256"] + assert spec["source_revision"] + assert spec["runtime_capability_sha256"] + assert spec["uv_lock_sha256"] + assert len(spec["global_expected_checks"]) == 15 + assert all( + check["scope"] == "global" and check["case_id"] is None + for check in spec["global_expected_checks"] + ) + all_check_ids = [ + check["check_id"] for check in spec["global_expected_checks"] + ] + for cell in spec["cells"]: + assert cell["expected_checks"] + assert all( + check["scope"] == "cell" + and check["case_id"] == cell["case_id"] + for check in cell["expected_checks"] + ) + all_check_ids.extend( + check["check_id"] for check in cell["expected_checks"] + ) + assert len(all_check_ids) == len(set(all_check_ids)) + + +def test_serial_and_sharded_scientific_reports_are_canonical_equal(tmp_path: Path): + protocol = _protocol() + serial = run_production_validation(protocol, tmp_path / "serial.json") + run_spec_path, _ = _prepare(tmp_path) + sharded = shards._merge_test_shards(run_spec_path) + assert payload_without_elapsed(serial) == payload_without_elapsed(sharded) + assert canonical_scientific_report_bytes(serial) == ( + canonical_scientific_report_bytes(sharded) + ) + assert ( + run_spec_path.parent / "report" / "report.json" + ).read_bytes() == canonical_report_bytes(sharded) + + +def test_cell_order_does_not_change_merged_scientific_report(tmp_path: Path): + first_path, _ = _prepare(tmp_path / "first", order=(0, 1)) + second_path, _ = _prepare(tmp_path / "second", order=(1, 0)) + first = shards._merge_test_shards(first_path) + second = shards._merge_test_shards(second_path) + assert canonical_scientific_report_bytes(first) == ( + canonical_scientific_report_bytes(second) + ) + + +def test_process_scheduling_order_is_invariant(tmp_path: Path): + reports = [] + revision = validation_module._repository_state()["source_revision"] + for name, order in (("forward", (0, 1)), ("reverse", (1, 0))): + root = tmp_path / name + spec_path = root / "run_spec.json" + shards._write_test_run_spec(_protocol(), root, spec_path) + shards._run_test_global_checks(spec_path) + for index in order: + completed = subprocess.run( + [ + sys.executable, + "-c", + ( + "from pathlib import Path;" + "import long_range_percolation.validation_shards as s;" + f"s._repository_state=lambda:{{'source_revision':" + f"{revision!r},'clean_tree':True," + "'provenance_error':None};" + f"s._run_test_cell(Path({str(spec_path)!r}), {index})" + ), + ], + cwd=SOLUTION, + capture_output=True, + text=True, + ) + assert completed.returncode == 0, completed.stderr + reports.append( + shards._merge_test_shards(spec_path) + ) + assert canonical_scientific_report_bytes(reports[0]) == ( + canonical_scientific_report_bytes(reports[1]) + ) + + +def test_valid_cell_is_idempotent_and_never_overwritten(tmp_path: Path): + root = tmp_path / "idempotent" + spec_path = root / "run_spec.json" + shards._write_test_run_spec(_protocol(), root, spec_path) + first = shards._run_test_cell(spec_path, 0) + partial = root / first["partial_path"] + before = partial.stat().st_mtime_ns + second = shards._run_test_cell(spec_path, 0) + assert second == first + assert partial.stat().st_mtime_ns == before + + +@pytest.mark.parametrize( + "failure", ("missing", "corrupt", "stale", "extra", "extra-dir") +) +def test_merge_rejects_incomplete_or_noncanonical_cell_sets( + tmp_path: Path, failure: str +): + spec_path, spec = _prepare(tmp_path) + root = spec_path.parent + cell = spec["cells"][0] + partial = root / cell["partial_path"] + if failure == "missing": + partial.unlink() + elif failure == "corrupt": + partial.write_text("{broken", encoding="utf-8") + elif failure == "stale": + document = json.loads(partial.read_text(encoding="utf-8")) + document["protocol_sha256"] = "0" * 64 + partial.write_text(json.dumps(document), encoding="utf-8") + elif failure == "extra": + (root / "cells" / "extra.json").write_text("{}", encoding="utf-8") + else: + (root / "cells" / "unexpected").mkdir() + with pytest.raises(RuntimeError): + shards._merge_test_shards(spec_path) + assert not (tmp_path / "forbidden.json").exists() + + +def test_atomic_crash_before_cell_rename_leaves_no_valid_partial(tmp_path: Path): + root = tmp_path / "crash" + spec_path = root / "run_spec.json" + shards._write_test_run_spec(_protocol(), root, spec_path) + + def crash(stage: str) -> None: + if stage == "before-artifact-rename": + raise RuntimeError("injected crash") + + with pytest.raises(RuntimeError, match="injected crash"): + shards._run_test_cell(spec_path, 0, crash_hook=crash) + spec = json.loads(spec_path.read_text(encoding="utf-8")) + assert not (root / spec["cells"][0]["partial_path"]).exists() + assert not (root / spec["cells"][0]["manifest_path"]).exists() + shards._run_test_cell(spec_path, 0) + + +def test_missing_manifest_is_restartable_without_overwriting_partial(tmp_path: Path): + spec_path, spec = _prepare(tmp_path) + root = spec_path.parent + cell = spec["cells"][0] + partial = root / cell["partial_path"] + manifest = root / cell["manifest_path"] + before = partial.stat().st_mtime_ns + manifest.unlink() + shards._run_test_cell(spec_path, 0) + assert manifest.is_file() + assert partial.stat().st_mtime_ns == before + + +def test_cli_exit_codes_for_build_cell_global_and_merge(tmp_path: Path): + root = tmp_path / "cli" + spec_path = root / "run_spec.json" + build = CLI.main( + [ + "build-spec", + "--protocol", + "production-v1", + "--output-root", + str(root), + "--run-spec", + str(spec_path), + ] + ) + assert build == 0 + bad_cell = CLI.main( + [ + "run-cell", + "--run-spec", + str(spec_path), + "--case-index", + "120", + ] + ) + assert bad_cell != 0 + missing_merge = CLI.main( + [ + "merge", + "--run-spec", + str(spec_path), + "--output", + str(root / "report" / "report.json"), + ], + ) + assert missing_merge != 0 + assert not (root / "report" / "report.json").exists() + + +def test_spool_copied_slurm_wrapper_uses_explicit_solution_root(tmp_path: Path): + spool = tmp_path / "slurm-spool-copy.sh" + shutil.copy2(WRAPPER, spool) + bindir = tmp_path / "bin" + bindir.mkdir() + invocation = tmp_path / "invocation.json" + fake_uv = bindir / "uv" + fake_uv.write_text( + "#!/bin/bash\n" + "python3 - \"$@\" <<'PY'\n" + "import json, os, sys\n" + f"open({str(invocation)!r}, 'w').write(json.dumps({{'cwd': os.getcwd(), " + "'args': sys.argv[1:], 'omp': os.environ['OMP_NUM_THREADS'], " + "'openblas': os.environ['OPENBLAS_NUM_THREADS'], " + "'pythonpath': os.environ.get('PYTHONPATH')}))\n" + "PY\n", + encoding="utf-8", + ) + fake_uv.chmod(0o755) + (tmp_path / "run_spec.json").write_text("{}", encoding="utf-8") + env = { + **os.environ, + "PATH": f"{bindir}:{os.environ['PATH']}", + "HARNESS_RUN_SPEC": str(tmp_path / "run_spec.json"), + "HARNESS_ENTRYPOINT": str(Path(__file__).parents[6]), + "SLURM_ARRAY_TASK_ID": "17", + "PYTHONPATH": "/caller/uv/path", + } + completed = subprocess.run( + ["/bin/bash", str(spool)], + cwd=tmp_path, + env=env, + capture_output=True, + text=True, + ) + assert completed.returncode == 0, completed.stderr + recorded = json.loads(invocation.read_text(encoding="utf-8")) + assert recorded["cwd"] == str(SOLUTION) + assert recorded["omp"] == "1" + assert recorded["openblas"] == "1" + assert recorded["pythonpath"] == "/caller/uv/path" + assert recorded["args"][:3] == [ + "run", + "scripts/validation_shard.py", + "run-cell", + ] + assert recorded["args"][-2:] == ["--case-index", "17"] + assert "17" in completed.stdout + + +def _offline_wrapper_environment( + tmp_path: Path, + *, + python: Path | str, +) -> tuple[Path, Path, dict[str, str]]: + spool = tmp_path / "slurm-spool-copy.sh" + shutil.copy2(WRAPPER, spool) + invocation = tmp_path / "offline-invocation.json" + run_spec = tmp_path / "run_spec.json" + run_spec.write_text("{}", encoding="utf-8") + environment = { + **os.environ, + "PATH": "/usr/bin:/bin", + "HARNESS_RUN_SPEC": str(run_spec), + "HARNESS_ENTRYPOINT": str(Path(__file__).parents[6]), + "SLURM_ARRAY_TASK_ID": "23", + "CHALLENGE_194_PYTHON": str(python), + "PYTHONPATH": "/hostile/caller/path", + "OFFLINE_INVOCATION": str(invocation), + } + return spool, invocation, environment + + +def test_spool_wrapper_uses_direct_offline_interpreter_and_clean_pythonpath( + tmp_path: Path, +): + interpreter = tmp_path / "offline-python" + interpreter.write_text( + "#!/bin/bash\n" + "/usr/bin/python3 - \"$@\" <<'PY'\n" + "import json, os, sys\n" + "with open(os.environ['OFFLINE_INVOCATION'], 'w') as stream:\n" + " json.dump({'args': sys.argv[1:], " + "'pythonpath': os.environ.get('PYTHONPATH')}, stream)\n" + "PY\n", + encoding="utf-8", + ) + interpreter.chmod(0o755) + spool, invocation, environment = _offline_wrapper_environment( + tmp_path, python=interpreter + ) + completed = subprocess.run( + ["/bin/bash", str(spool)], + cwd=tmp_path, + env=environment, + capture_output=True, + text=True, + ) + assert completed.returncode == 0, completed.stderr + recorded = json.loads(invocation.read_text(encoding="utf-8")) + assert recorded["args"] == [ + "scripts/validation_shard.py", + "run-cell", + "--run-spec", + str(tmp_path / "run_spec.json"), + "--case-index", + "23", + ] + assert recorded["pythonpath"] == str(SOLUTION / "src") + assert "/hostile/caller/path" not in recorded["pythonpath"] + + +def test_spool_wrapper_isolates_numba_cache_per_array_cell(tmp_path: Path): + interpreter = tmp_path / "offline-python" + interpreter.write_text( + "#!/bin/bash\n" + "/usr/bin/python3 - \"$@\" <<'PY'\n" + "import json, os\n" + "with open(os.environ['OFFLINE_INVOCATION'], 'w') as stream:\n" + " json.dump({'numba_cache_dir': os.environ.get('NUMBA_CACHE_DIR')}, stream)\n" + "PY\n", + encoding="utf-8", + ) + interpreter.chmod(0o755) + spool, invocation, environment = _offline_wrapper_environment( + tmp_path, python=interpreter + ) + node_local = tmp_path / "node-local" + node_local.mkdir() + environment["SLURM_TMPDIR"] = str(node_local) + environment["SLURM_ARRAY_JOB_ID"] = "12345" + environment["SLURM_ARRAY_TASK_ID"] = "23" + + completed = subprocess.run( + ["/bin/bash", str(spool)], + cwd=tmp_path, + env=environment, + capture_output=True, + text=True, + ) + + assert completed.returncode == 0, completed.stderr + recorded = json.loads(invocation.read_text(encoding="utf-8")) + expected = node_local / "challenge-194-numba-12345-23" + assert recorded["numba_cache_dir"] == str(expected) + assert expected.is_dir() + + +def test_spool_wrapper_preserves_venv_launcher_identity(tmp_path: Path): + venv = tmp_path / "worker-venv" + subprocess.run( + [ + sys.executable, + "-m", + "venv", + "--without-pip", + "--system-site-packages", + str(venv), + ], + check=True, + ) + launcher = venv / "bin" / "python" + resolved = launcher.resolve() + assert launcher != resolved + + version = f"python{sys.version_info.major}.{sys.version_info.minor}" + site_packages = venv / "lib" / version / "site-packages" + site_packages.mkdir(parents=True, exist_ok=True) + (site_packages / "wrapper_venv_marker.py").write_text( + "VALUE = 'venv-site-packages'\n", + encoding="utf-8", + ) + + repository = tmp_path / "repository" + solution = ( + repository + / "tracks" + / "qmc" + / "solutions" + / "frustration-free" + / "challenge-194" + ) + scripts = solution / "scripts" + scripts.mkdir(parents=True) + invocation = tmp_path / "venv-invocation.json" + (scripts / "validation_shard.py").write_text( + "import json, os, sys\n" + "import wrapper_venv_marker\n" + "with open(os.environ['OFFLINE_INVOCATION'], 'w') as stream:\n" + " json.dump({\n" + " 'executable': sys.executable,\n" + " 'prefix': sys.prefix,\n" + " 'marker': wrapper_venv_marker.VALUE,\n" + " 'pythonpath': os.environ.get('PYTHONPATH'),\n" + " }, stream)\n", + encoding="utf-8", + ) + run_spec = tmp_path / "run_spec.json" + run_spec.write_text("{}", encoding="utf-8") + environment = { + **os.environ, + "PATH": "/usr/bin:/bin", + "HARNESS_RUN_SPEC": str(run_spec), + "CHALLENGE_194_REPO_ROOT": str(repository), + "SLURM_ARRAY_TASK_ID": "23", + "CHALLENGE_194_PYTHON": str(launcher), + "PYTHONPATH": "/hostile/caller/path", + "OFFLINE_INVOCATION": str(invocation), + } + + completed = subprocess.run( + ["/bin/bash", str(WRAPPER)], + cwd=tmp_path, + env=environment, + capture_output=True, + text=True, + ) + + assert completed.returncode == 0, completed.stderr + recorded = json.loads(invocation.read_text(encoding="utf-8")) + assert recorded == { + "executable": str(launcher), + "prefix": str(venv), + "marker": "venv-site-packages", + "pythonpath": str(solution / "src"), + } + + +def test_spool_wrapper_uses_valid_harness_command_as_interpreter(tmp_path: Path): + interpreter = tmp_path / "harness-python" + interpreter.write_text( + "#!/bin/bash\n" + "/usr/bin/python3 - \"$@\" <<'PY'\n" + "import json, os, sys\n" + "with open(os.environ['OFFLINE_INVOCATION'], 'w') as stream:\n" + " json.dump({'args': sys.argv[1:]}, stream)\n" + "PY\n", + encoding="utf-8", + ) + interpreter.chmod(0o755) + spool, invocation, environment = _offline_wrapper_environment( + tmp_path, python=interpreter + ) + environment.pop("CHALLENGE_194_PYTHON") + environment["HARNESS_COMMAND"] = str(interpreter) + completed = subprocess.run( + ["/bin/bash", str(spool)], + cwd=tmp_path, + env=environment, + capture_output=True, + text=True, + ) + assert completed.returncode == 0, completed.stderr + recorded = json.loads(invocation.read_text(encoding="utf-8")) + assert recorded["args"][0:2] == [ + "scripts/validation_shard.py", + "run-cell", + ] + + +def test_offline_interpreter_rejects_conflicting_explicit_candidates( + tmp_path: Path, +): + challenge_python = tmp_path / "challenge-python" + harness_python = tmp_path / "harness-python" + for interpreter in (challenge_python, harness_python): + interpreter.write_text("#!/bin/bash\nexit 0\n", encoding="utf-8") + interpreter.chmod(0o755) + spool, invocation, environment = _offline_wrapper_environment( + tmp_path, python=challenge_python + ) + environment["HARNESS_COMMAND"] = str(harness_python) + completed = subprocess.run( + ["/bin/bash", str(spool)], + cwd=tmp_path, + env=environment, + capture_output=True, + text=True, + ) + assert completed.returncode != 0 + assert "conflict" in completed.stderr + assert not invocation.exists() + + +def test_offline_interpreter_rejects_distinct_launchers_with_same_target( + tmp_path: Path, +): + interpreter = tmp_path / "offline-python" + interpreter.write_text("#!/bin/bash\nexit 0\n", encoding="utf-8") + interpreter.chmod(0o755) + alias = tmp_path / "python-alias" + alias.symlink_to(interpreter) + spool, _, environment = _offline_wrapper_environment( + tmp_path, python=alias + ) + environment["HARNESS_COMMAND"] = str(interpreter) + completed = subprocess.run( + ["/bin/bash", str(spool)], + cwd=tmp_path, + env=environment, + capture_output=True, + text=True, + ) + assert completed.returncode != 0 + assert "conflict" in completed.stderr + + +def test_offline_interpreter_may_be_valid_absolute_symlink(tmp_path: Path): + interpreter = tmp_path / "offline-python" + interpreter.write_text( + "#!/bin/bash\nexit 0\n", + encoding="utf-8", + ) + interpreter.chmod(0o755) + alias = tmp_path / "python-alias" + alias.symlink_to(interpreter) + spool, _, environment = _offline_wrapper_environment( + tmp_path, python=alias + ) + completed = subprocess.run( + ["/bin/bash", str(spool)], + cwd=tmp_path, + env=environment, + capture_output=True, + text=True, + ) + assert completed.returncode == 0, completed.stderr + + +@pytest.mark.parametrize( + "kind", + ("relative", "missing", "directory", "non-executable", "broken-symlink"), +) +def test_offline_interpreter_fails_closed_when_invalid( + tmp_path: Path, + kind: str, +): + candidate = tmp_path / "candidate" + if kind == "relative": + python: Path | str = "relative/python" + elif kind == "missing": + python = candidate + elif kind == "directory": + candidate.mkdir() + python = candidate + elif kind == "non-executable": + candidate.write_text("#!/bin/bash\nexit 0\n", encoding="utf-8") + candidate.chmod(0o644) + python = candidate + else: + candidate.symlink_to(tmp_path / "absent-target") + python = candidate + spool, invocation, environment = _offline_wrapper_environment( + tmp_path, python=python + ) + completed = subprocess.run( + ["/bin/bash", str(spool)], + cwd=tmp_path, + env=environment, + capture_output=True, + text=True, + ) + assert completed.returncode != 0 + assert "CHALLENGE_194_PYTHON" in completed.stderr + assert not invocation.exists() + + +def test_generated_shard_results_are_ignored(tmp_path: Path): + repository = Path(__file__).parents[6] + candidate = ( + repository + / "tracks" + / "qmc" + / "results" + / "frustration-free" + / "challenge-194" + / "validation-sharded" + / "run_spec.json" + ) + completed = subprocess.run( + ["git", "check-ignore", str(candidate)], + cwd=repository, + capture_output=True, + text=True, + ) + assert completed.returncode == 0 diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_validation_shards_adversarial.py b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_validation_shards_adversarial.py new file mode 100644 index 000000000..2b6b64c6b --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/tests/test_validation_shards_adversarial.py @@ -0,0 +1,344 @@ +from __future__ import annotations + +import hashlib +import json +from pathlib import Path +from threading import Barrier, Thread + +import pytest + +import long_range_percolation.validation_shards as shards +from long_range_percolation.validation import ValidationProtocol + + +def _reduced() -> ValidationProtocol: + return ValidationProtocol.reduced( + lengths=(4,), + sigmas=(1.0,), + kappas=(0.0, 0.25), + samples=3, + replicates=5, + ) + + +@pytest.fixture(autouse=True) +def clean_source(monkeypatch: pytest.MonkeyPatch): + revision = shards._repository_state()["source_revision"] + monkeypatch.setattr( + shards, + "_repository_state", + lambda: { + "source_revision": revision, + "clean_tree": True, + "provenance_error": None, + }, + ) + + +def _prepared(tmp_path: Path) -> tuple[Path, dict[str, object]]: + root = (tmp_path / "run").resolve() + spec_path = root / "run_spec.json" + shards._write_test_run_spec(_reduced(), root, spec_path) + shards._run_test_global_checks(spec_path) + for index in range(2): + shards._run_test_cell(spec_path, index) + return spec_path, json.loads(spec_path.read_text(encoding="utf-8")) + + +def _rewrite_spec(path: Path, mutate) -> dict[str, object]: + document = json.loads(path.read_text(encoding="utf-8")) + mutate(document) + document["run_spec_sha256"] = shards._document_hash( + document, "run_spec_sha256" + ) + path.write_bytes(shards._canonical_bytes(document)) + return document + + +def _rewrite_artifact( + spec_path: Path, + spec: dict[str, object], + *, + cell_index: int | None, + mutate, +) -> None: + if cell_index is None: + partial_relative = spec["global_partial_path"] + manifest_relative = spec["global_manifest_path"] + else: + partial_relative = spec["cells"][cell_index]["partial_path"] + manifest_relative = spec["cells"][cell_index]["manifest_path"] + partial = spec_path.parent / partial_relative + manifest = spec_path.parent / manifest_relative + document = json.loads(partial.read_text(encoding="utf-8")) + mutate(document) + payload = shards._canonical_bytes(document) + partial.write_bytes(payload) + manifest_document = json.loads(manifest.read_text(encoding="utf-8")) + manifest_document["artifact_sha256"] = hashlib.sha256(payload).hexdigest() + manifest_document["artifact_size"] = len(payload) + manifest.write_bytes(shards._canonical_bytes(manifest_document)) + + +def test_dirty_source_fails_before_build(monkeypatch: pytest.MonkeyPatch, tmp_path: Path): + monkeypatch.setattr( + shards, + "_repository_state", + lambda: { + "source_revision": "a" * 40, + "clean_tree": False, + "provenance_error": None, + }, + ) + root = (tmp_path / "dirty").resolve() + with pytest.raises(RuntimeError, match="clean"): + shards.build_validation_run_spec( + ValidationProtocol.production_v1(), root + ) + assert not root.exists() + + +@pytest.mark.parametrize("command", ("write", "global", "cell", "merge")) +def test_dirty_source_fails_every_invocation_before_publication( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + command: str, +): + root = (tmp_path / command).resolve() + spec_path = root / "run_spec.json" + protocol = ValidationProtocol.production_v1() + shards._write_test_run_spec(protocol, root, spec_path) + revision = json.loads(spec_path.read_text(encoding="utf-8"))[ + "source_revision" + ] + monkeypatch.setattr( + shards, + "_repository_state", + lambda: { + "source_revision": revision, + "clean_tree": False, + "provenance_error": None, + }, + ) + invocation = { + "write": lambda: shards.write_validation_run_spec( + protocol, root, spec_path + ), + "global": lambda: shards.run_validation_global_checks(spec_path), + "cell": lambda: shards.run_validation_cell(spec_path, 0), + "merge": lambda: shards.merge_validation_shards( + spec_path, root / "report" / "report.json" + ), + }[command] + with pytest.raises(RuntimeError, match="clean"): + invocation() + assert not (root / "global").exists() + assert not (root / "cells").exists() + assert not (root / "report").exists() + + +def test_reduced_spec_fails_every_public_execution_command(tmp_path: Path): + root = (tmp_path / "reduced").resolve() + spec_path = root / "run_spec.json" + shards._write_test_run_spec(_reduced(), root, spec_path) + with pytest.raises((RuntimeError, ValueError), match="production"): + shards.run_validation_global_checks(spec_path) + with pytest.raises((RuntimeError, ValueError), match="production"): + shards.run_validation_cell(spec_path, 0) + with pytest.raises((RuntimeError, ValueError), match="production"): + shards.merge_validation_shards( + spec_path, root / "report" / "report.json" + ) + with pytest.raises((RuntimeError, ValueError), match="production"): + shards.build_validation_run_spec(_reduced(), root) + + +def test_implementation_hash_change_fails_before_compute( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +): + root = (tmp_path / "implementation").resolve() + spec_path = root / "run_spec.json" + shards._write_test_run_spec(_reduced(), root, spec_path) + hashes = shards._implementation_hashes() + changed = dict(hashes) + changed[next(iter(changed))] = "0" * 64 + monkeypatch.setattr(shards, "_implementation_hashes", lambda: changed) + with pytest.raises(RuntimeError, match="implementation"): + shards._run_test_cell(spec_path, 0) + assert not (root / "cells").exists() + + +@pytest.mark.parametrize( + "mutation", + ( + lambda spec: spec.__setitem__("global_partial_path", "../outside.json"), + lambda spec: spec.__setitem__( + "global_manifest_path", spec["global_partial_path"] + ), + lambda spec: spec["cells"][1].__setitem__( + "partial_path", spec["cells"][0]["partial_path"] + ), + ), + ids=("outside-root", "overlap", "duplicate-artifact"), +) +def test_run_spec_rejects_unsafe_or_duplicate_paths( + tmp_path: Path, mutation +): + root = (tmp_path / "unsafe").resolve() + spec_path = root / "run_spec.json" + shards._write_test_run_spec(_reduced(), root, spec_path) + _rewrite_spec(spec_path, mutation) + with pytest.raises(RuntimeError): + shards._run_test_global_checks(spec_path) + + +def test_symlinked_report_directory_is_rejected(tmp_path: Path): + spec_path, spec = _prepared(tmp_path) + report_directory = spec_path.parent / "report" + outside = tmp_path / "outside" + outside.mkdir() + report_directory.symlink_to(outside, target_is_directory=True) + with pytest.raises(RuntimeError, match="symlink"): + shards._merge_test_shards(spec_path) + assert not (outside / "report.json").exists() + + +def test_arbitrary_merge_output_is_rejected(tmp_path: Path): + spec_path, _ = _prepared(tmp_path) + with pytest.raises(RuntimeError, match="fixed"): + shards._merge_test_shards(spec_path, tmp_path / "arbitrary.json") + + +def test_existing_valid_final_report_is_idempotent_but_different_is_rejected( + tmp_path: Path, +): + spec_path, spec = _prepared(tmp_path) + first = shards._merge_test_shards(spec_path) + report = spec_path.parent / spec["final_report_path"] + before = report.stat().st_mtime_ns + second = shards._merge_test_shards(spec_path) + assert first == second + assert report.stat().st_mtime_ns == before + report.write_text("{}\n", encoding="utf-8") + with pytest.raises(RuntimeError, match="immutable|existing"): + shards._merge_test_shards(spec_path) + assert report.read_text(encoding="utf-8") == "{}\n" + + +@pytest.mark.parametrize( + "mutation", + ( + lambda records: records.pop(), + lambda records: records.append(dict(records[0])), + lambda records: records.append( + { + **records[0], + "check_id": "extra", + } + ), + lambda records: records.reverse(), + lambda records: records[0].__setitem__("case_id", "other-case"), + lambda records: records[0].__setitem__("family", "other-family"), + lambda records: records[0]["check"].__setitem__( + "case_id", "cross-cell/check" + ), + ), + ids=( + "missing", + "duplicate", + "extra", + "reordered", + "cross-case", + "family-substituted", + "cross-cell-inner", + ), +) +def test_merge_rejects_noncanonical_cell_check_registry( + tmp_path: Path, mutation +): + spec_path, spec = _prepared(tmp_path) + _rewrite_artifact( + spec_path, + spec, + cell_index=0, + mutate=lambda document: mutation(document["check_records"]), + ) + with pytest.raises(RuntimeError, match="check registry"): + shards._merge_test_shards(spec_path) + + +def test_cell_rejects_global_check_and_global_rejects_case_check(tmp_path: Path): + spec_path, spec = _prepared(tmp_path) + global_artifact = json.loads( + (spec_path.parent / spec["global_partial_path"]).read_text(encoding="utf-8") + ) + cell_artifact = json.loads( + ( + spec_path.parent / spec["cells"][0]["partial_path"] + ).read_text(encoding="utf-8") + ) + _rewrite_artifact( + spec_path, + spec, + cell_index=0, + mutate=lambda document: document["check_records"].__setitem__( + 0, global_artifact["check_records"][0] + ), + ) + with pytest.raises(RuntimeError, match="check registry"): + shards._merge_test_shards(spec_path) + + # Restore the cell and independently substitute a case record globally. + (spec_path.parent / spec["cells"][0]["partial_path"]).unlink() + (spec_path.parent / spec["cells"][0]["manifest_path"]).unlink() + shards._run_test_cell(spec_path, 0) + _rewrite_artifact( + spec_path, + spec, + cell_index=None, + mutate=lambda document: document["check_records"].__setitem__( + 0, cell_artifact["check_records"][0] + ), + ) + with pytest.raises(RuntimeError, match="check registry"): + shards._merge_test_shards(spec_path) + + +def test_concurrent_no_clobber_accepts_identical_and_rejects_different( + tmp_path: Path, +): + identical = tmp_path / "identical.json" + barrier = Barrier(2) + errors: list[Exception] = [] + + def publish(path: Path, payload: bytes) -> None: + try: + barrier.wait() + shards._write_once(path, payload) + except Exception as error: + errors.append(error) + + threads = [ + Thread(target=publish, args=(identical, b'{"same":true}\n')) + for _ in range(2) + ] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + assert errors == [] + assert identical.read_bytes() == b'{"same":true}\n' + + different = tmp_path / "different.json" + barrier = Barrier(2) + errors.clear() + threads = [ + Thread(target=publish, args=(different, payload)) + for payload in (b'{"winner":1}\n', b'{"winner":2}\n') + ] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + assert len(errors) == 1 + assert different.read_bytes() in (b'{"winner":1}\n', b'{"winner":2}\n') diff --git a/tracks/qmc/solutions/frustration-free/challenge-194/uv.lock b/tracks/qmc/solutions/frustration-free/challenge-194/uv.lock new file mode 100644 index 000000000..9320e7595 --- /dev/null +++ b/tracks/qmc/solutions/frustration-free/challenge-194/uv.lock @@ -0,0 +1,173 @@ +version = 1 +revision = 3 +requires-python = "==3.12.*" + +[[package]] +name = "challenge-194-long-range-percolation" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "h5py" }, + { name = "numba" }, + { name = "numpy" }, + { name = "scipy" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, +] + +[package.metadata] +requires-dist = [ + { name = "h5py", specifier = "==3.14.0" }, + { name = "numba", specifier = "==0.66.0" }, + { name = "numpy", specifier = "==2.2.6" }, + { name = "scipy", specifier = "==1.15.3" }, +] + +[package.metadata.requires-dev] +dev = [{ name = "pytest", specifier = ">=8.3,<9" }] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "h5py" +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/57/dfb3c5c3f1bf5f5ef2e59a22dec4ff1f3d7408b55bfcefcfb0ea69ef21c6/h5py-3.14.0.tar.gz", hash = "sha256:2372116b2e0d5d3e5e705b7f663f7c8d96fa79a4052d250484ef91d24d6a08f4", size = 424323, upload-time = "2025-06-06T14:06:15.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/77/8f651053c1843391e38a189ccf50df7e261ef8cd8bfd8baba0cbe694f7c3/h5py-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e0045115d83272090b0717c555a31398c2c089b87d212ceba800d3dc5d952e23", size = 3312740, upload-time = "2025-06-06T14:05:01.193Z" }, + { url = "https://files.pythonhosted.org/packages/ff/10/20436a6cf419b31124e59fefc78d74cb061ccb22213226a583928a65d715/h5py-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6da62509b7e1d71a7d110478aa25d245dd32c8d9a1daee9d2a42dba8717b047a", size = 2829207, upload-time = "2025-06-06T14:05:05.061Z" }, + { url = "https://files.pythonhosted.org/packages/3f/19/c8bfe8543bfdd7ccfafd46d8cfd96fce53d6c33e9c7921f375530ee1d39a/h5py-3.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554ef0ced3571366d4d383427c00c966c360e178b5fb5ee5bb31a435c424db0c", size = 4708455, upload-time = "2025-06-06T14:05:11.528Z" }, + { url = "https://files.pythonhosted.org/packages/86/f9/f00de11c82c88bfc1ef22633557bfba9e271e0cb3189ad704183fc4a2644/h5py-3.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cbd41f4e3761f150aa5b662df991868ca533872c95467216f2bec5fcad84882", size = 4929422, upload-time = "2025-06-06T14:05:18.399Z" }, + { url = "https://files.pythonhosted.org/packages/7a/6d/6426d5d456f593c94b96fa942a9b3988ce4d65ebaf57d7273e452a7222e8/h5py-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:bf4897d67e613ecf5bdfbdab39a1158a64df105827da70ea1d90243d796d367f", size = 2862845, upload-time = "2025-06-06T14:05:23.699Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "llvmlite" +version = "0.48.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/a0/acc8ffcd5bdc63df0097e22c719bfcd61b604358343089313a8aebbb24ab/llvmlite-0.48.0.tar.gz", hash = "sha256:543b19f9ef8f3c7c60d1468191e4ee1b1537bf9f8a3d56f64c0ddd98de92edd2", size = 184016, upload-time = "2026-07-02T20:20:05.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/a2/28696a9e61e245d1a79816d29d106692a90a2b6e7d78c98b326db70827af/llvmlite-0.48.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:d66c3beb4209087ddd4cf4ed2a0856b6887e6a913bdcf1aacfec9851cf2cba4e", size = 40480651, upload-time = "2026-07-01T18:41:35.694Z" }, + { url = "https://files.pythonhosted.org/packages/80/f2/72409351db66d0a317ec5087e076f31fb7b773a640db8a90ce6b5cac9edd/llvmlite-0.48.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:416fa4c2c66c2c6dc6d0a402648c19206e548efa0aa1eff01ad5cdad0af8217d", size = 59890118, upload-time = "2026-07-01T18:41:44.886Z" }, + { url = "https://files.pythonhosted.org/packages/3a/27/5ae2f3722606360480707adb47f001ad89df8251d06b14ee80336e660b66/llvmlite-0.48.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5e5a5131045b72345c71062ea1a91910dde913792b6c9b28ebb2c1c0a712e98", size = 58343459, upload-time = "2026-07-01T18:41:40.306Z" }, + { url = "https://files.pythonhosted.org/packages/16/78/d824ffff7521cd140dc2006e44ce2bc82e64b48d1b32e90e956308c85a74/llvmlite-0.48.0-cp312-cp312-win_amd64.whl", hash = "sha256:d45c7541a80934ec6d8ab0defe67439494ecd2193cbf852a44ba827808976ac1", size = 41865022, upload-time = "2026-07-01T18:41:48.663Z" }, +] + +[[package]] +name = "numba" +version = "0.66.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llvmlite" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/a0/570e3dc53e5602b49108f62a13e529f1eec8bfc7ef37d49c825924dcf546/numba-0.66.0.tar.gz", hash = "sha256:b900e63a0e26c05ea9a6d5a3a5a0a177cb64c5011887bf43edb8c3ed2c38d363", size = 2806181, upload-time = "2026-07-01T23:12:46.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a3/70deb7f88461c1cd5d16aa990c2380604102661a427667b8950dcdccc27f/numba-0.66.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:53ca5900b7cab15109796030113a6b28576bae5ad7bb507ad6dd1360ddd81ba4", size = 2727264, upload-time = "2026-07-01T23:12:18.669Z" }, + { url = "https://files.pythonhosted.org/packages/2d/55/25c319845e9a4e08f16611ddbda56a192eb7b6ed13e1a2bff2da272ffb97/numba-0.66.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0999e3ee1b18c48e1fb51d11af35ef59852c7f4f50569c9550c25faef0616ad1", size = 3866252, upload-time = "2026-07-01T23:12:20.429Z" }, + { url = "https://files.pythonhosted.org/packages/71/ef/a82d6fd6bf1b0fe461651e924d3647eeec9ac17f8eee4896264bf7480930/numba-0.66.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:efe0d2d5099790df945e0cb6e1b3104bd965d7bbfac50d62f1d5d1d6ade0825d", size = 3566974, upload-time = "2026-07-01T23:12:22.116Z" }, + { url = "https://files.pythonhosted.org/packages/fc/eb/9e6171e378822ab191c7abcfd3d8cfc8644516f6c7834c22e210e4acc070/numba-0.66.0-cp312-cp312-win_amd64.whl", hash = "sha256:b075a4e7ebc43dc6294f223e2821659656209fd5e0ce53245877c23d66d6e1a9", size = 2797403, upload-time = "2026-07-01T23:12:23.724Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, +] diff --git "a/tracks/qmc/solutions/frustration-free/challenge-194/\346\214\221\346\210\230194\346\261\207\346\212\245.md" "b/tracks/qmc/solutions/frustration-free/challenge-194/\346\214\221\346\210\230194\346\261\207\346\212\245.md" new file mode 100644 index 000000000..cd15e8339 --- /dev/null +++ "b/tracks/qmc/solutions/frustration-free/challenge-194/\346\214\221\346\210\230194\346\261\207\346\212\245.md" @@ -0,0 +1,524 @@ +# 挑战194汇报 + +## 摘要 + +挑战 194 研究一维长程 `q=1` Fortuin–Kasteleyn(FK)随机团簇/长程渗流模型在衰减指数 `σ` 经过边缘点 `σ=1` 时的临界行为。本项目已经建立了一套经过认证、可复现并可继续扩展的数值研究基础:固定模型约定;实现并交叉验证独立二次 oracle、几何跳跃采样器和生产 Poisson/Newman–Ziff 扫描;完成真实 P0 与 P0 extension v1 集群采样,共获得 192 条认证轨迹;随后对不可变结果完成组合分析并原样重跑冻结 selector。现有数据成功给出 `σ=0.8` 的 transition-refinement 窗口和 `σ=1.1` 的 crossover-refinement 窗口;`σ=0.9` 与 `σ=1.0` 在当前严格双估计量判据下需要进一步细化。 + +本项目的核心贡献不仅是一次参数扫描,更是一条可审计的科学证据链。它系统排除了模型定义偏差、采样器实现错误、随机流不可复现、产物覆盖和事后挑选窗口等常见风险,并把“数据支持到哪一步”编码进自动判据。当前成果为后续临界标度计算提供了可信模型、生产引擎、集群流程、认证数据和窗口选择依据;关于 `2/3`、`η`、`ν`、标度形式与普适性的最终判断仍需在此基础上继续积累近临界数据。 + +## 题目与模型约定 + +[Issue #194](https://github.com/QuantumBFS/quantum.harness/issues/194) 指出,原始“`ν≈2/3, η≈0`”表述没有唯一指定随机过程、控制参数、相关函数和指数约定。项目因此固定研究偶数 `L` 环上的 `q=1` FK 模型。每个无序点对 `{i,j}` 独立开放: + +```text +p_ij(kappa, sigma) = 1 - exp[-kappa J_L,sigma(i-j)] +J_L,sigma(r) = sum_{n in Z} |r+nL|^[-(1+sigma)]. +``` + +约定是周期镜像和、每个无序点对只取一次、无距离截断、无 Kac 归一化、无独立最近邻参数;全部边采样后由并查集给出连通分量。`σ=1` 时有解析恒等式 + +```text +J_L,1(r) = (pi/L)^2 csc^2(pi r/L). +``` + +这不是 minimum-image `C/r^(1+σ)` 模型,不能混用阈值。Issue 给出的 Cardy `q→1` 延拓 `ν̃=2/3` 是待检验假设,不是本模型已经成立的结论;`σ>1` 的 `σ=1.1` 只作为 crossover 负对照,不能称为有限临界点。 + +物理上,生产计划原本要区分代数有限尺寸标度、essential singularity、巨团簇不连续出现或无有限耦合相变;并分别处理未减去巨团簇的连接度、有限团簇连接度、直接衰减指数和 Fisher 约定。但本轮真实数据只走到了窗口 selector。 + +## 本次完成范围 + +完成并由 Git 记录的实现包括: + +- `model.py`/`kernel.py`:固定模型与周期镜像核;`σ=1` 使用 `csc²` 身份,其他 `σ` 使用对称 Hurwitz-zeta 表达。 +- `counter_rng.py`:Philox4x32-10 计数型随机流;键由 master seed、phase、`L`、sigma grid ID、replica、stream ID 派生;有界整数用拒绝采样而非取模。 +- `alias.py`/`edge_set.py`:按 `M_d J_d` 的确定性 Walker alias 表和 `uint64` 开边集合。 +- `production_union_find.py`/`observables.py`:增量并查集、分量矩、最大/次大分量与四分区掩码。 +- `poisson_reference.py`/`poisson_sweep.py`/`trajectory.py`:独立参考过程及生产 Poisson/Newman–Ziff 单调耦合扫描。 +- `artifacts.py`、Pilot 与 Slurm 脚本:请求、环境、kernel、seed manifest、轨迹、batch、progress 和 manifest 的哈希绑定、原子发布、no-clobber、恢复和下载后深验证。 +- `pilot_analysis.py` 与 `analyze_pilot.py`:按完整轨迹为重采样单位计算均值/标准误,认证 P0 与 extension v1,组合证据并执行冻结 selector。 + +真实分析仅覆盖 `L=2^10, 2^14, 2^18`,且只聚合四个基本可观测量:`S1/L`、`S2/L`、`Q_G`、`four-sector crossing`。P0 是 `σ=0.8,0.9,1.0,1.1` 的 96 个 cell/96 条轨迹;extension v1 只覆盖 `σ=0.9,1.0`,也是 96 个 cell/96 条轨迹。后者每个 sigma 使用 17 个已冻结 coupling checkpoint、replica `24..39`,没有改变科学引擎或 selector。 + +下面两张 SVG 将既有认证 JSON 转化为可直接阅读的阶段性科学图。它们由本报告新增的确定性生成器重绘,不改变任何结果 JSON,也不引入插值或合成数据。 + +## 正确性与可复现性基础 + +正确性审批文件 `pilot_correctness_approval.json` 认证 revision `877ab9393f320bfe31ff74a26c3db1fb205d7ef3` 的 120-cell 验证包:共 22,755 个检查,报告 SHA256 `036b4b8a06164716aff5f40cc38ac4855a212026a556e1c5fe33ce32ce0babb8`,scientific-engine aggregate SHA256 `457fa669da897e59b03681039db6121fde4d7be9295bb46a743c8448875b3ee9`。验证范围包括核恒等式、总 rate、无边概率、开边数矩、`L≤6` 全图枚举、二次/几何/Poisson 三路一致性、极限值、antipodal class、随机流分离以及调度顺序不变性。 + +正确性 gate 完成后,项目将有限时间优先投入真实 P0/extension 采样,因此没有继续执行 Task 10 的 `L=2^18 ≤120 s`、峰值 RSS `≤4 GiB` capability gate 与 Task 11 优化;其登记状态为 `cancelled-without-capability-report`。这不影响已经完成的单核集群采样与科学正确性审批,但本文不额外声称冻结性能门已通过。 + +两次真实 run spec 记录的环境均为 CPython 3.12.13、NumPy 2.2.6、SciPy 1.15.3、Numba 0.66.0、llvmlite 0.48.0、h5py 3.14.0、Linux x86_64,`fastmath=false`、`boundscheck=true`、`NUMBA_DISABLE_JIT=false`。worker 固定单线程,清理有影响的 Python/Numba/动态链接环境变量,并使用新建的私有 node-local Numba cache。 + +## P0 与 extension v1 的探索性流程 + +P0 protocol 使用 master seed `19420260729`、replica `0..7`,sigma 顺序 `0.8,0.9,1.0,1.1`,长度顺序 `1024,16384,262144`,16 个 coupling(含零点),得到 96 条单调轨迹。认证本地结果 root 为 `pilot-p0-739880d`;canonical Slurm array job 为 `41506576`,构建日志 job `41506541`,合并日志 job `41506709`。运行时用 Wuzh02 `wzacnormal03`,每个 cell 一 CPU、1800 MiB;run spec 和每个 cell 的 request/RNG/kernel/environment 都有独立哈希。 + +初始 P0 selector 为 `σ=0.8` 选择了探索性 transition-refinement 窗口,却无法为 `σ=0.9,1.0` 找到两个 estimator 同时标记的非零相邻区间;`σ=1.1` 只选择 crossover-refinement 窗口。于是产生了版本化 extension v1,而不是手工放宽阈值。 + +Extension v1 使用 master seed `19420262729`、replica `24..39`,与 P0 `0..7` 及预留 P1 `8..23` 分离;每个 sigma 的 17 点轴来自认证 P0 组件、guard interval 与四层 binary64 midpoint。实际 Wuzh02 作业为 build `41535048`、smoke `41535233`、两组数组 `41535277` 和 `41535294`;每个 worker 一 CPU、1800 MiB、40 分钟调度上限,无 GPU,数组任务严格映射到 96 个 cell。40 分钟是资源上限,也不是性能 gate 通过证据。 + +下载使用 checksummed、partial-safe、no-delete 的 `rsync` 合约;transfer log `pilot-p0-extension-v1.download-state/logs/transfer-261942-15440.log` 显示 immutable cell 树、HDF5 trajectory 及 SHA256 sidecar 被传输。最终 progress 证明恰好 96 cells/96 trajectories,`physics_claims_authorized=false`。 + +![挑战194从模型约定到下一阶段细化的证据工作流](assets/challenge-194-workflow-status.svg) + +图 1 展示从 issue/model contract、正确性审批、P0、extension v1 到组合 selector 的完整证据链;虚线 extension-v2 框表示已经设计但尚未纳入当前科学证据的下一阶段。来源是 `pilot_correctness_approval.json`、`p0_analysis.json`、`p0_extension_v1_protocol.json`、`p0_extension_v1_analysis.json`、`p0_combined_analysis_v2.json`、`p0_combined_brackets_v2.json`,完整 SHA256 见附录 A。该图证明当前方法与数据流程已经走通,并准确标出由窗口定位进入临界标度计算前仍需补充的证据。 + +## 实际 selector 证据 + +冻结 selector 只使用两个最大尺寸 `L=16384,262144` 的均值: + +1. `Q_G`:对每个非零相邻 coupling 区间,检查 `mean(Q_G,L=16384)-mean(Q_G,L=262144)` 是否在端点间变号或触零。 +2. `four-sector crossing`:任一上述尺寸的两个端点均值必须覆盖闭区间 `[0.25,0.75]`。 +3. 对 `σ≤1`,同一个相邻区间必须同时被两条规则标记;误差条不参与 selector,也不允许插值、平滑、nearest fallback 或人工选窗。 + +![sigma 0.9 与 1.0 的 P0+extension-v1 selector 证据及标准误差](assets/challenge-194-selector-evidence.svg) + +图 2 从 `p0_combined_analysis_v2.json`(文件 SHA256 `6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929`)逐点读取 `Q_G` 与 four-sector crossing 均值及标准误,并从 `p0_combined_brackets_v2.json`(文件 SHA256 `7a84d545b4526d94aa6f93ca4f0d264dcf01e518f2f9b04383921634786c9962`)认证最终状态。误差条为均值 `±1 standard error`;它们只表达 8 或 16 条独立轨迹均值的不确定性,selector 仍只用均值。P0 source `p0_analysis.json` 文件 SHA256 为 `44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b`,extension-v1 source `p0_extension_v1_analysis.json` 文件 SHA256 为 `d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5`。图中没有插值、合成点或视觉估值,也不建立相变、标度或指数结论。 + +实际窗口/状态如下: + +- `σ=0.8`:`selected`,用途严格为 `transition_refinement`,窗口 `[0x1.f400000000000p-2, 0x1.3880000000000p-1]`。 +- `σ=0.9`:`requires_p0_extension`,原因 `no_nonzero_interval_marked_by_both_estimators`。 +- `σ=1.0`:`requires_p0_extension`,原因 `no_nonzero_interval_marked_by_both_estimators`。 +- `σ=1.1`:`selected`,用途严格为 `crossover_refinement`,窗口 `[0x1.312d000000000p+0, 0x1.7d78400000000p+0]`;它不是 transition。 + +Extension v1 的六项验收中,四项已经满足;第 4 项(`σ=0.9,1.0` 都有共同标记区间)与第 6 项(`requires_p0_extension=false` 且独立重算字节相同)尚未满足。因此当前结果应理解为“严格 selector 指示继续细化”,而不是对相变作出负面结论。 + +## 阶段性成果与下一步科学边界 + +P0 与 P0 extension v1 已经完成模型、引擎、集群执行、产物认证和探索性窗口选择的端到端闭环,为正式近临界采样建立了可复用基础。 + +在不事后改变 selector 的前提下,`σ=0.8` 与 `σ=1.1` 获得了用途明确的细化窗口;`σ=0.9` 与 `σ=1.0` 尚未在同一相邻区间同时满足两个估计量判据,现有逐点结果因此成为下一轮粗化/扩展网格的直接依据。 + +项目据此保留了 P1 的独立性:在共同窗口形成前不发布 `p1_protocol.json`,从而避免用同一批探索数据同时选窗和作最终推断。 + +名为 `challenge-194-p0-combined-analysis-v2` 的已认证 JSON 表示组合分析 schema v2;extension-v2 则是独立的后续采样设计。 + +仓库中已经形成 extension-v2 的预注册设计与实施计划;局部实现尚未纳入本报告的认证结果,因而当前图表继续只使用 P0 与 extension v1 数据。 + +下一阶段将在共同窗口基础上执行临界标度拟合,并估计 `η` 与 `ν`;当前报告主动保留这些结论,避免超出现有数据的分辨能力。 + +当前认证分析聚焦于 `L=2^10,2^14,2^18` 及 `S1/L`、`S2/L`、`Q_G`、`four-sector crossing` 四个基础可观测量。更丰富的 histogram、connectivity、有限团簇尺度、模型比较、confirmatory sampling、P1 与临界指数拟合属于后续扩展范围,完整清单见附录 D.3。本报告的两张 SVG 是从现有认证数据生成的首批 challenge-194 阶段性科学图。 + +## 完整复现步骤 + +以下命令从 repository root 或 solution directory 执行,只认证和重绘现有证据,不重跑科学 campaign: + +```bash +REPO=/home/footman/code/quantum.harness-challenge-194 +SOLUTION="$REPO/tracks/qmc/solutions/frustration-free/challenge-194" +RESULTS="$REPO/results/challenge-194" + +uv sync --project "$SOLUTION" --python 3.12 +uv run --project "$SOLUTION" pytest -q + +cd "$REPO" +sha256sum \ + "$SOLUTION/pilot_correctness_approval.json" \ + "$RESULTS/p0_analysis.json" \ + "$RESULTS/p0_extension_v1_analysis.json" \ + "$RESULTS/p0_combined_analysis_v2.json" \ + "$RESULTS/p0_combined_brackets_v2.json" \ + "$RESULTS/p0_extension_v1_protocol.json" \ + "$RESULTS/pilot-p0-739880d/run_spec.json" \ + "$RESULTS/pilot-p0-739880d/progress.json" \ + "$RESULTS/pilot-p0-extension-v1/run_spec.json" \ + "$RESULTS/pilot-p0-extension-v1/progress.json" + +cd "$SOLUTION" +uv run python scripts/generate_report_figures.py \ + --approval pilot_correctness_approval.json \ + --p0-analysis "$RESULTS/p0_analysis.json" \ + --extension-protocol "$RESULTS/p0_extension_v1_protocol.json" \ + --extension-analysis "$RESULTS/p0_extension_v1_analysis.json" \ + --combined-analysis "$RESULTS/p0_combined_analysis_v2.json" \ + --brackets "$RESULTS/p0_combined_brackets_v2.json" \ + --output-dir assets +``` + +生成器先校验六个源文件的固定 whole-file SHA256、五个 embedded document SHA256、schema、source cross-link、canonical `float.hex()` 轴、有限均值/非负标准误及两个 unresolved bracket;任何不一致均停止。SVG 使用固定 viewBox、颜色、元素顺序、十进制格式和 font fallback,不含时间、hostname、绝对路径、随机 ID 或当前 Git revision。内存中完成字节后以私有临时文件、`fsync` 和 no-clobber hard-link 发布;相同文件只验证,不同文件拒绝覆盖。 + +P0 与 extension v1 的分析/组合命令属于历史 provenance,不能在本报告构建中运行。尤其当前工作树含受保护 partial v2 修改,禁止运行 `combine`、`select`、`build-p1`、任何 v2 builder/worker、P1 worker、scaling fit、`η` fit 或 `ν` fit。只可在对应历史 revision 和不可变输入上按 README 所列命令复核;unresolved 时明确不要运行 `build-p1`。 + +## 参考资料 + +- Challenge issue #194:<https://github.com/QuantumBFS/quantum.harness/issues/194>。Issue 定义完整科学问题;本报告只记录更窄的探索性停止点。 +- 智御量子 2026 指南:<https://giggleliu.github.io/summer-school-2026/zh/guide>。指南要求可复现成果、最终中文汇报与如实说明未完成项。 +- 本地文献与哈希注册表:`references/README.md`。 +- J. L. Cardy, *One-dimensional models with 1/r² interactions*, <https://doi.org/10.1088/0305-4470/14/6/017>。 +- E. Luijten and H. Meßingfeld, *Criticality in one dimension with inverse square-law potentials*, <https://arxiv.org/abs/cond-mat/0104175>。 +- M. Aizenman and C. M. Newman, *Discontinuity of the percolation density in one dimensional 1/|x-y|² percolation models*, <https://doi.org/10.1007/BF01205489>。 +- H. Duminil-Copin, C. Garban, and V. Tassion, *Long-range models in 1D revisited*, <https://arxiv.org/abs/2011.04642>。 +- G. Gori et al., *One-dimensional long-range percolation: A numerical study*, <https://arxiv.org/abs/1610.00200>。其 minimum-image 模型只作比较,不是本项目 oracle。 + +## 附录 A:文件、schema 与 SHA256 证据账本 + +Whole-file SHA256: + +- `pilot_correctness_approval.json` — `29dc5d04fd18728ee46fffe90c70d98caa61032005974f354e2b4e0e6018a7ab` +- `p0_analysis.json` — `44083701db692304cd3aa054c8a9488b75674cead7cd6bf479c0a203cc1fa10b` +- `p0_extension_v1_analysis.json` — `d8fdd60a6de83cf3818349d4440f49f4a38bb5acd7fff1dab9b56ded4da913e5` +- `p0_combined_analysis_v2.json` — `6c38e3e18a4577da41bc70c5610b5449e0316b1588291cb178e437099fb78929` +- `p0_combined_brackets_v2.json` — `7a84d545b4526d94aa6f93ca4f0d264dcf01e518f2f9b04383921634786c9962` +- `p0_extension_v1_protocol.json` — `e363a60f842b11b32972c7a68ec1c5f237741bc45bc79ab8bf93f51f6760d84d` +- `pilot-p0-739880d/run_spec.json` — `d17d3df9528a09f0d834ebe9d5ce6f283e488d2326f6cb14873a90923c5d9840` +- `pilot-p0-739880d/progress.json` — `ea29a8163a5d3e85768842d64fac4c719f5aeadf965b3318b305fb7a2cc2d15f` +- `pilot-p0-extension-v1/run_spec.json` — `c1ca9b6c8ba751919c6d9337fe1cd4c09a57ed9b99abbb9d3ebfed7f89c3d32e` +- `pilot-p0-extension-v1/progress.json` — `c78d1fb03daf19297ef9e0617410c68a6a364bffc2f2888dfa9067e7e8d6b65f` +- `challenge-194-selector-evidence.svg` — `40fa91c741272cf9b1d15f5a0ff35f496d7d876ec6180741b84d744ad18ad7c2` +- `challenge-194-workflow-status.svg` — `252537c3084ee641432c78520de8579f7e41b49aa5d3e2aec1848884254a8a5b` + +Embedded identity: + +- `p0_analysis.json` / `analysis_document_sha256` — `e42ef6b9f82380305f80ceaba384bc29cb9fe2da0848d4c72a904f4cb4c8c7c8` +- `p0_extension_v1_protocol.json` / `protocol_sha256` — `a37ab41f3224594e61f4eebbe292975aeec449b9ecb7893e3e54f18d82d53321` +- `p0_extension_v1_analysis.json` / `analysis_document_sha256` — `79232574d314348c29a40cd2fbb7690e96f3cae5f26843bd4f1cf07cb6a1f45b` +- `p0_combined_analysis_v2.json` / `analysis_document_sha256` — `36f85c40e9159ef2e69742672c261769fb28d2f3c947780ba63e4ef5fe5975c3` +- `p0_combined_brackets_v2.json` / `bracket_document_sha256` — `098f19d8883097d5f1f274ce759416328c086958fa5301c034a0b46dcbd562df` + +Cross-link 为:combined analysis 同时绑定 P0 document `e42e…c7c8` 与 extension document `7923…f45b`;brackets 再绑定 combined document `36f8…75c3`。P0 analysis 有 192 estimate rows;extension v1 有 102 rows;combined 有 282 rows。两棵 progress 均严格记录 96 cells/96 trajectories。 + +## 附录 B:Selector 定义与逐项结果 + +算法只在每个 sigma 已排序且非零的相邻 `κ_i,κ_{i+1}` 上运行: + +```text +D_i(L_a,L_b) = mean Q_G(L_a,kappa_i) - mean Q_G(L_b,kappa_i) +Q mark(i) <=> min(D_i,D_{i+1}) <= 0 <= max(D_i,D_{i+1}) + +C mark(i) <=> exists L in {16384,262144}: + min(C_L(kappa_i),C_L(kappa_{i+1})) <= 0.25 + and max(C_L(kappa_i),C_L(kappa_{i+1})) >= 0.75 + +common(i) = Q mark(i) and C mark(i) +``` + +标准误只按 `mean ± SE` 绘制。没有 interpolation、smoothing、digitization、synthetic point、confidence-interval rescue 或人工窗口。`σ=0.9`、`1.0` 均无 `common(i)`;因此状态与原因完全相同。`σ=0.8` 的 transition-refinement 与 `σ=1.1` 的 crossover-refinement 只保留其认证标签。 + +## 附录 C:完整复现命令 + +### C.1 报告图测试、生成与字节确定性 + +```bash +cd "$SOLUTION" +uv run pytest tests/test_generate_report_figures.py -q +uv run python -m py_compile scripts/generate_report_figures.py +uv run python scripts/generate_report_figures.py --help + +SVG_A="$(mktemp -d /tmp/ch194-svg-a.XXXXXX)" +SVG_B="$(mktemp -d /tmp/ch194-svg-b.XXXXXX)" +for out in "$SVG_A" "$SVG_B"; do + uv run python scripts/generate_report_figures.py \ + --approval pilot_correctness_approval.json \ + --p0-analysis "$RESULTS/p0_analysis.json" \ + --extension-protocol "$RESULTS/p0_extension_v1_protocol.json" \ + --extension-analysis "$RESULTS/p0_extension_v1_analysis.json" \ + --combined-analysis "$RESULTS/p0_combined_analysis_v2.json" \ + --brackets "$RESULTS/p0_combined_brackets_v2.json" \ + --output-dir "$out" +done +cmp "$SVG_A/challenge-194-selector-evidence.svg" "$SVG_B/challenge-194-selector-evidence.svg" +cmp "$SVG_A/challenge-194-workflow-status.svg" "$SVG_B/challenge-194-workflow-status.svg" +cmp assets/challenge-194-selector-evidence.svg "$SVG_A/challenge-194-selector-evidence.svg" +cmp assets/challenge-194-workflow-status.svg "$SVG_A/challenge-194-workflow-status.svg" +sha256sum assets/*.svg +``` + +### C.2 历史 campaign provenance(不要为本报告重跑) + +原始 P0 使用 `scripts/pilot_array_slurm.sh` 的 array `1..96 → cell 0..95`;extension v1 使用 smoke `1-2%2`、light/medium `3-32,49-80%16`、heavy `33-48,81-96%8`。历史分析命令及全部 trust inputs 记录在 `README.md`。只有 exact historical revision `143d35ac52923cff2d24c43d304a75c2d04d3c66` 能对 P0 analysis 返回 `verified-existing`;当前 HEAD 不应改写它。 + +本报告不得运行下列命令: + +```text +analyze_pilot.py combine +analyze_pilot.py select +analyze_pilot.py build-p1 +任何 extension-v2 builder/worker +任何 P1 worker +任何 scaling / eta / nu fit +``` + +### C.3 数据布局与安全边界 + +```text +results/challenge-194/ +├── pilot-p0-739880d/ +│ ├── run_spec.json +│ ├── progress.json +│ └── cells/<cell-id>/run/{request,environment,kernel,seed-manifest, +│ capability,trajectories,batches,progress,manifest} +├── pilot-p0-extension-v1/ # 同构的 96-cell immutable tree +├── *.download-state/ # root 外的 source/verified/log +├── p0_analysis.json +├── p0_extension_v1_protocol.json +├── p0_extension_v1_analysis.json +├── p0_combined_analysis_v2.json +└── p0_combined_brackets_v2.json +``` + +Canonical JSON 为 sorted keys、compact separators、UTF-8、一个末尾换行、有限数值。publication 是 fsync 后 atomic no-clobber;`.partial`、`.intent`、unexpected path、hash mismatch、ABA replacement 和 runtime/source drift 均 fail closed 并保留诊断。下载状态与 transfer log 在 immutable root 外。报告生成器只读六个认证 JSON,既不 import 当前 dirty 的 `pilot_extension.py`/`analyze_pilot.py`,也不写 `results/challenge-194/`。 + +## 附录 D:实现与验证范围 + +### D.1 算法、复杂度与随机数 + +- Quadratic oracle:逐个 `i<j`,以 `p=-expm1(-κJ_d)` 独立采样,`O(L²)` 时间、`O(L)` 内存,主要用于 `L≤256` 交叉验证。 +- Geometric skipping:同一 distance class 内利用 closed-run 的几何分布,期望复杂度 `O(L+E_open α(L))`。 +- Poisson/Newman–Ziff:总 rate `Λ=Σ_e J_e=L ζ(1+σ)[1-L^-(1+σ)]`;按 `M_dJ_d` alias 抽 distance class,再抽 offset。重复 event 被忽略,新 edge 才 union;一次轨迹共享全部 `κ`,所以 bootstrap 单位必须是整条轨迹。 +- `Q_G=Σ_C|C|⁴/(Σ_C|C|²)²`;four-sector crossing 表示有分量同时碰到四个固定 quarter-ring arc。 +- Philox stream 与调度、数组顺序和 retry 解耦;P0、v1、预留 P1 的 replica/master seed 互不重用。 + +### D.2 Git 里程碑 + +- `e639b437…`(2026-07-29 03:52 +08)定义科学 protocol。 +- `55cf1f9f…`(04:08)固定模型。 +- `db29995c…`(05:43)规划 production engine。 +- `877ab939…`(2026-07-30 03:31)稳定 production-size Pilot numerics。 +- `06ce05c0…`(04:12)批准稳定 engine。 +- `739880d9…`(04:56)绑定 P0 ancestors。 +- `11b1e59f…`(11:30)发布 immutable extension protocol。 +- `9308087c…`(13:17)要求显式 P0 evidence root。 +- `aca4ea24…`(13:38)加入 bounded extension aggregation。 +- `b62d0d33…`(13:49)组合 verified evidence。 +- `95ac875e…`(14:03)原样重跑冻结 selector。 +- `37d90fb1…`(15:07)记录 extension boundary evidence。 +- `30c1be09…`(15:28)认证 combined evidence。 + +### D.3 验证成果与适用范围 + +已验证的是模型/核/采样器基本正确性、artifact/provenance、安全发布、P0 与 v1 root、四个基本 observable 聚合、source-bound selector 和本报告图的源哈希/确定性。性能 gate 是 waived/cancelled,不是 passed。 + +本报告新增 focused suite 最新一次为 `6 passed in 0.06s`;完整 solution suite 为 `835 passed in 206.19s`。此外已验证两次独立临时目录生成与已发布 SVG 三方逐字节相同、XML 可解析、强制标签/来源 caption 存在、Markdown 本地链接可解析、24 条 curated prompt 时间有序、claim-boundary regex 通过、账本哈希与当前文件一致、`git diff --check` 无输出。 + +下一阶段可在现有基础上扩展至:全 `L=2^10,2^12,2^14,2^16,2^18` production;`σ=1` 中间尺寸;histogram/connectivity/finite-cluster scale;12 点以上 near-critical retained window;density jump;essential/free-essential/algebraic/log-corrected model comparison;nested bootstrap/deletion stability;confirmatory phase;P1、v2 campaign,以及 `η`、`ν` 和 scaling 分析。 + +## 附录:Prompt 时间线 + +来源为本项目 canonical Cursor transcript `ca5a598d-f70e-482b-9024-c381ba92d7c7`,从首次明确指派 Challenge 194 开始。以下按原时间顺序保留 substantive user prompts 的关键原文;重复的“继续/状态/自动 follow-up”只在阶段边界合并说明。包含凭据文件路径、主机账号等敏感内容的 prompt 被排除;不包含 hidden reasoning、tool payload 或 credential。实现 prompt 的生成来源是批准的 `docs/superpowers/plans/2026-07-30-challenge-194-report.md`,在末尾单列关键原文。 + +### Prompt 1 — Wednesday, Jul 29, 2026, 3:00 AM (UTC+8) + +```text +你做194,你看到194的文件夹了吗,你进入194的分支,准备开始做 +``` + +### Prompt 2 — Wednesday, Jul 29, 2026, 3:13 AM (UTC+8) + +```text +https://giggleliu.github.io/summer-school-2026/zh/guide 话说按照要求最后分支合并以后要是一个能交的pr,我们应该在哪写代码,现在多个agent的做法合理吗? +``` + +### Prompt 3 — Wednesday, Jul 29, 2026, 3:22 AM (UTC+8) + +```text +现在我们的工作流是这样的: +可以采用“4 个开发 worktree + 1 个集成 worktree + 1 个最终 PR”的模式。技术上可并行,提交上仍保持一个入口。 +``` + +### Prompt 4 — Wednesday, Jul 29, 2026, 3:25 AM (UTC+8) + +```text +有需要下载的文章或者代码吗?下载一下 +``` + +### Prompt 5 — Wednesday, Jul 29, 2026, 3:34 AM (UTC+8) + +```text +你看懂题了吗,仔细读题,想想怎么做 +``` + +### Prompt 6 — Wednesday, Jul 29, 2026, 3:59 AM (UTC+8) + +```text +确认 +``` + +### Prompt 7 — Wednesday, Jul 29, 2026, 5:16 AM (UTC+8) + +```text +目前做了啥,算了啥,结果如何? +``` + +### Prompt 8 — Wednesday, Jul 29, 2026, 5:22 AM (UTC+8) + +```text +实现生产采样器 +Poisson/Newman–Ziff 扫描、计数型随机数、增量并查集和可重启数据产物。 + +性能与正确性标定 +与当前 O(L²) oracle、几何采样器交叉验证,测量内存和每样本耗时。 + +小规模 pilot +运行 σ=0.8、0.9、1.0、1.1,初步扫描 κ,定位相变区域。 + +大尺寸生产计算 +扩展至 L=2¹⁰、2¹⁴、2¹⁸;耗时超过本地阈值时迁移集群。 +``` + +### Prompt 9 — Wednesday, Jul 29, 2026, 12:20 PM (UTC+8) + +```text +算不动的话可以上超算 +``` + +### Prompt 10 — Wednesday, Jul 29, 2026, 7:01 PM (UTC+8) + +```text +10和11是必须的吗,如果代码性能也没有很差的话可以继续往后做 +``` + +### Prompt 11 — Wednesday, Jul 29, 2026, 8:43 PM (UTC+8) + +```text +刚刚意外中断了,你跑的这个太重了,停掉 +``` + +### Prompt 12 — Wednesday, Jul 29, 2026, 11:13 PM (UTC+8) + +```text +还在跑吗?刚刚断了,继续。本地别跑大的 +``` + +### Prompt 13 — Wednesday, Jul 29, 2026, 11:25 PM (UTC+8) + +```text +现在在算什么物理问题,最后要算哪些物理问题 +``` + +### Prompt 14 — Wednesday, Jul 29, 2026, 11:34 PM (UTC+8) + +```text +为什么要证明“单条轨迹 ≤120 秒、≤4 GiB”。是题目的要求吗? +``` + +### Prompt 15 — Wednesday, Jul 29, 2026, 11:36 PM (UTC+8) + +```text +我觉得没必要有这一步,直接开始跑题目吧,优化估计已经够好了 +``` + +### Prompt 16 — Thursday, Jul 30, 2026, 3:11 AM (UTC+8) + +```text +修复该并发检查并续跑失败的 77 个单元 +``` + +### Prompt 17 — Thursday, Jul 30, 2026, 5:04 AM (UTC+8) + +```text +下载 P0 产物并在本地再次验证。 +分析四个 σ 的可观测量,确定临界 κ 窗口。 +冻结 P1 参数、细化 κ 网格和新 RNG 副本。 +集群运行 P1。 +根据 P1 预注册正式生产参数,运行更大尺寸和更多独立轨迹。 +做有限尺寸标度、临界点/指数拟合、误差分析、绘图和最终报告。 +完成 Task 12 的公开 API 与 README。 +``` + +### Prompt 18 — Thursday, Jul 30, 2026, 10:56 AM (UTC+8) + +```text +P0你跑的是什么,也是真实数据吗,是题目里问的吗 +``` + +### Prompt 19 — Thursday, Jul 30, 2026, 1:32 PM (UTC+8) + +```text +所以这个问题的超算环节做完了吗 +``` + +### Prompt 20 — Thursday, Jul 30, 2026, 3:12 PM (UTC+8) + +```text +啥情况,跑了哪些,接下来还需要在超算上跑哪些? +``` + +### Prompt 21 — Thursday, Jul 30, 2026, 3:16 PM (UTC+8) + +```text +p1不是算完了吗,现在在干嘛 +``` + +### Prompt 22 — Thursday, Jul 30, 2026, 6:21 PM (UTC+8) + +```text +停掉吧,来不及做完了,做了多少就汇报多少 +``` + +### Prompt 23 — Thursday, Jul 30, 2026, 6:49 PM (UTC+8) + +```text +https://giggleliu.github.io/summer-school-2026/zh/guide https://github.com/QuantumBFS/quantum.harness/issues/194 +在这个包里写一个md,用中文,标题叫做挑战194汇报 +里面包括正文,支撑材料,prompt +代码目前没有全部运行完成。请基于仓库中真实存在的代码、配置、日志、数据、图片和 Git 记录进行汇报。不要虚构结果,不要把计划写成已完成工作。 +正文。需要介绍挑战物理背景还有我们做了什么,包括相关的结果(图表或者图片),讲一个完整的故事,做到哪说到哪。 +支撑材料也就是附录。需要包括代码实现的全部细节,可以额外做一张矢量图,包括如何复现正文的全部内容 +prompt。需要尽可能按顺序记录完成这个项目用的提示词 +注意: +1. 足够的信息去复现结果,2. 一个清晰的给人看的文件论证你的结果有用性和正确性。 +``` + +### Prompt 24 — Thursday, Jul 30, 2026, 6:59 PM (UTC+8) + +批准的 implementation plan 关键原文: + +```text +Goal: Produce one Chinese report, 挑战194汇报.md, with a detailed supporting appendix and chronological prompts, plus two deterministic SVGs derived only from authenticated P0 + extension-v1 evidence. + +Task Order +1. Freeze protected-work and evidence hashes. +2. Write failing generator tests. +3. Implement and test the authenticated generator. +4. Generate and deterministically validate the two SVGs. +5. Extract and curate the prompt chronology. +6. Write the single Chinese report and embedded appendices. +7. Validate links, hashes, claims, determinism, chronology, and untouched dirty work. +``` + +重复的自动 follow-up prompt 主要为 `Perform any necessary follow-up actions in response to the subagent completion above...`;重复状态/恢复 prompt 主要为 `刚刚断了,继续,包括后台的subagent`、`跑完了吗`、`接下来需要做哪些?`。它们作为过程控制被合并记录,没有被误作科学要求或完成证据。 + +## 附录 E:主张—证据—边界矩阵 + +### E.1 模型已固定 + +- 证据:`DESIGN.md`、Issue #194、commit `55cf1f9f…`、kernel/oracle tests。 +- 允许:报告周期镜像和 `q=1` 独立 Bernoulli 边模型。 +- 禁止:套用 minimum-image 模型阈值或说原始题意已被唯一识别。 + +### E.2 科学引擎经过正确性审批 + +- 证据:`pilot_correctness_approval.json` whole-file SHA256 `29dc…a7ab`,内部 report/check registry/engine hashes。 +- 允许:说审批的 engine 通过冻结正确性 gate。 +- 禁止:把未执行的性能 gate 说成 passed。 + +### E.3 P0 与 extension v1 真正运行并完成 + +- 证据:两份 authenticated run spec/progress;分别 96 cells/96 trajectories;scheduler logs 和 transfer log。 +- 允许:说真实探索性 campaign 完成并通过 artifact 深验证。 +- 禁止:说它们是 confirmatory、production scaling 或物理结论。 + +### E.4 `σ=0.9,1.0` unresolved + +- 证据:`p0_combined_analysis_v2.json` whole-file SHA256 `6c38…8929` 与 `p0_combined_brackets_v2.json` `7a84…9962`;图 2 为其确定性重绘。 +- 允许:`requires_p0_extension` / `no_nonzero_interval_marked_by_both_estimators`。 +- 禁止:插值制造窗口、用误差条救援 selector、声称不存在相变。 + +### E.5 `σ=0.8` 和 `1.1` 窗口 + +- 证据:认证 brackets。 +- 允许:`σ=0.8 transition_refinement`、`σ=1.1 crossover_refinement` 的探索性窗口。 +- 禁止:把 `σ=1.1` crossover 称为确认相变。 + +### E.6 P1、v2、scaling 与指数属于下一阶段 + +- 证据:`p1_protocol.json` 不存在;结果目录没有 v2 protocol/root/analysis;工作区只有五个受保护 dirty partial-v2 路径;v2 design/plan 明说设计与未来工作。 +- 允许:P1/v2 未运行,Task 1 只是 partial dirty work,`η`/`ν` 未估计。 +- 禁止:把 schema 名 `challenge-194-p0-combined-analysis-v2` 当作 extension-v2 运行,把计划 cardinality/job 命令写成完成事实。 + +### E.7 本报告图的身份 + +- 证据:`scripts/generate_report_figures.py`、focused tests、两次临时目录 byte comparison、SVG SHA256。 +- 允许:从认证 JSON 的 report-time deterministic redraw。 +- 禁止:声称此前已有 challenge-194 科学图,或把 redraw 当作新采样/拟合结果。