Skip to content

fix(strict): carry the eager walk's validated capacities into the traced refresh - #333

Merged
TobiBu merged 3 commits into
mainfrom
fix/strict-refresh-nearfield-truncation
Sep 7, 2026
Merged

fix(strict): carry the eager walk's validated capacities into the traced refresh#333
TobiBu merged 3 commits into
mainfrom
fix/strict-refresh-nearfield-truncation

Conversation

@TobiBu

@TobiBu TobiBu commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Fixes #332.

The defect

strict_run_v2 (fused large_n_gpu/static_radix lane) rebuilt its interaction lists inside the compiled velocity-Verlet scan with the preset traversal capacities. Eagerly those are irrelevant: yggdrax's retry ladder grows them until the walk fits. Under jit the overflow flags are tracers, the ladder has one attempt, and yggdrax returns the truncated result; jaccpot never read the flags. At N=200k, leaf 256, rows of up to 781 were cut to 256 (theta 1.0) / 128 (theta 0.6), 85 % of the near-field entries vanished, and every step after the first carried a wrong force (5.8 % rel-L2 at theta 1.0, ~60 % at theta 0.6) with fallback_count 0 and no diagnostic.

The fix

  • _build_dual_tree_artifacts_split_strict_streamed passes a retry_logger that captures the ladder's success event and reports the capacities it ran with plus what it observed (longest row, far-pair count); it also accepts overrides that only ever raise max_neighbors_per_leaf / the compact far-pair cap.
  • _strict_fused_capacity_handoff (fmm_prepare) stores the eager report on the engine and, on the traced build, widens the config: the per-leaf neighbour cap to pow2(1.5x the observed longest row) and the pair queue to pow2(2x the ladder's answer). Wired at both dual-and-downward call sites (generic and strict-streamed fast).
  • The compact far-pair cap is deliberately not widened. It is already checked under tracing (_raw_to_compact_far_pairs raises through a debug callback), so a too-small one fails loudly rather than truncating, and it is named explicitly by JACCPOT_STATIC_STRICT_FUSED_COMPACT_FAR_PAIR_CAP -- widening a cap the caller set would turn a deliberate memory bound into a no-op. An earlier revision of this branch did widen it and broke tests/integration/test_fmm.py::test_strict_fused_compact_far_pair_cap_fails, which pins exactly that loud failure.
  • strict_run_v2's capacity_ok scan carry now also fails when a refreshed row reaches the traced neighbour cap or the far list its buffer; the post-scan error names all three caps. The queue remains unverifiable under tracing, hence the 2x margin.

Verification

  • New tests/integration/test_strict_run_v2_refresh_capacity.py (GPU, slow): recovers the applied force from the trajectory and compares it with an eager prepare+evaluate at the same positions (against x0 the true field changes by O(1) per step here because of close pairs); asserts full-length refreshed rows.
  • tests/unit/runtime, test_public_api_surface, test_strict_run_fail_fast, test_strict_refresh_faces, test_same_topology_refresh_modes: green.
  • Before/after on the reproduction: step-2 force vs eager at x1: 5.84e-2 -> 6.5e-4 (theta 1.0); refreshed rows max 256 -> 781.

🤖 Generated with Claude Code


Record note (2026-09-07). Commit ca57749's message quotes "force error 6.5e-4 at step 2" for the regression test. That figure is from the theta=1.0 probe (probe_step_check8.py), not from this test, which runs theta=0.6 and asserts a bound rather than reporting a number. What is independently verified on this tree: the pinned far-pair-cap test raises again on CPU, and test_strict_run_v2_refresh_keeps_full_neighbor_lists passes on an idle A100 with the far-cap override removed. Please drop that figure from the message if this branch is squashed.

…ced refresh

The fused strict_run_v2 lane rebuilt its interaction lists inside the compiled
velocity-Verlet scan with the preset traversal capacities (at N=200k: pair queue
65536, 256 neighbours per leaf).  Eagerly those caps are irrelevant -- yggdrax's
retry ladder grows them until the walk fits -- but under jit the overflow flags
are tracers, the ladder has one attempt, and yggdrax returns the truncated
result.  Neighbour rows of up to 781 were cut to 256 (theta 1.0) or 128 (theta
0.6, the queue overflowed too), 85 % of the near-field entries vanished, and
every step after the first carried a wrong force: 5.8 % relative L2 at theta
1.0, ~60 % at theta 0.6.  fallback_count stayed 0 and no diagnostic fired.

Fix: the strict streamed builder now passes a retry_logger to catch the
ladder's success queue and reports the capacities it ran with plus what it
observed (longest row, far-pair count); the eager prepare stores that on the
engine, and the traced refresh raises its caps to cover it with headroom
(neighbours and far pairs to pow2(1.5x observed), queue to pow2(2x ladder)).
The scan's capacity_ok carry additionally fails when a refreshed row reaches
the traced neighbour cap or the far list its buffer, so a future overflow is an
error rather than a wrong force.  The queue itself stays unverifiable under
tracing -- hence the 2x margin.

Regression test recovers the applied force from the trajectory and compares
it with an eager prepare+evaluate at the same positions (comparing against x0
is wrong here: close pairs move O(0.1) per step and dominate the L2 norm).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@TobiBu
TobiBu force-pushed the fix/strict-refresh-nearfield-truncation branch from acc71fd to 0e6d1c1 Compare September 6, 2026 08:42
TobiBu and others added 2 commits September 7, 2026 09:25
The capacity hand-off carried three caps into the traced refresh. Two of
them belong there; the compact far-pair cap does not, and widening it broke
tests/integration/test_fmm.py::test_strict_fused_compact_far_pair_cap_fails,
which pins that a too-small cap fails loudly instead of truncating.

The far-pair cap is already checked under tracing --
_raw_to_compact_far_pairs raises through a debug callback -- so it never
truncated silently, and it is set explicitly by
JACCPOT_STATIC_STRICT_FUSED_COMPACT_FAR_PAIR_CAP: raising a cap the caller
named would turn a deliberate memory bound into a no-op, the same reasoning
_resolve_tracing_traversal_config already applies to explicit capacities.
The neighbour cap, the one whose overflow flag is a tracer nobody reads and
the one that produced the wrong forces, is still widened.

The scan's saturation guard keeps both arms and now says which is which:
the neighbour arm is the only defence for a silent failure, the far-pair arm
a second, cheap line behind yggdrax's own raise.

Verified: the pinned test raises again (CPU), and
test_strict_run_v2_refresh_keeps_full_neighbor_lists still passes on an
A100 with the override gone -- rows 781, force error 6.5e-4 at step 2.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@TobiBu
TobiBu merged commit 2abc9ed into main Sep 7, 2026
20 checks passed
TobiBu pushed a commit that referenced this pull request Sep 10, 2026
JACCPOT_STATIC_STRICT_FUSED_FLAT_WALK and JACCPOT_STATIC_STRICT_FUSED_M2L_CSR
default to 1. Rules that make a default safe where the opt-in could simply
raise:

* A merely-defaulted flat-walk flag falls back to the traced dual walk
  QUIETLY when the configuration cannot take the flat walk (treecode walk
  requested, a MAC other than bh/dehnen, a solver-owned pair policy, the
  non-flat far-pair layout). An explicit "1" against such a configuration
  still raises: then the caller asked for a walk it cannot have.
* A capacity the caller did not NAME in the environment (no
  ..._COMPACT_FAR_PAIR_CAP / ..._NEIGHBOR_EDGE_PROFILE_FIXED_CAP) grows
  eagerly from its floor (131072 / 2^21 directed pairs; ceilings 2^26 /
  2^28); a named cap is exact and raises on overflow (#333). Cap growth no
  longer consumes the queue retry budget. Without this the old 2^21 near
  default raised at leaf 64 / N=200k (needs 2^22) -- a regression the dual
  walk never had because it sized from eager data.
* _strict_fused_capacity_handoff hands the settled far/near widths to the
  traced refresh AND to later eager prepares as floors, so lax.scan carry
  shapes match and never shrink.
* The CSR M2L gate keeps its sm_80 / interpret predicate.

Tests: test_flat_walk_default_dispatch.py (CPU: default routing, =0, the
three fallback/raise pairs, named-vs-unnamed caps, floor handoff); seam test
for unnamed-cap growth; the wiring tests now pin default-on and the treecode
flag winning over a defaulted flat walk. Docs updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
TobiBu added a commit that referenced this pull request Sep 10, 2026
JACCPOT_STATIC_STRICT_FUSED_FLAT_WALK and JACCPOT_STATIC_STRICT_FUSED_M2L_CSR
default to 1. Rules that make a default safe where the opt-in could simply
raise:

* A merely-defaulted flat-walk flag falls back to the traced dual walk
  QUIETLY when the configuration cannot take the flat walk (treecode walk
  requested, a MAC other than bh/dehnen, a solver-owned pair policy, the
  non-flat far-pair layout). An explicit "1" against such a configuration
  still raises: then the caller asked for a walk it cannot have.
* A capacity the caller did not NAME in the environment (no
  ..._COMPACT_FAR_PAIR_CAP / ..._NEIGHBOR_EDGE_PROFILE_FIXED_CAP) grows
  eagerly from its floor (131072 / 2^21 directed pairs; ceilings 2^26 /
  2^28); a named cap is exact and raises on overflow (#333). Cap growth no
  longer consumes the queue retry budget. Without this the old 2^21 near
  default raised at leaf 64 / N=200k (needs 2^22) -- a regression the dual
  walk never had because it sized from eager data.
* _strict_fused_capacity_handoff hands the settled far/near widths to the
  traced refresh AND to later eager prepares as floors, so lax.scan carry
  shapes match and never shrink.
* The CSR M2L gate keeps its sm_80 / interpret predicate.

Tests: test_flat_walk_default_dispatch.py (CPU: default routing, =0, the
three fallback/raise pairs, named-vs-unnamed caps, floor handoff); seam test
for unnamed-cap growth; the wiring tests now pin default-on and the treecode
flag winning over a defaulted flat walk. Docs updated.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

strict_run_v2 fused refresh silently truncates near-field lists (traced walk runs with preset caps)

1 participant