Skip to content
This repository was archived by the owner on Aug 20, 2026. It is now read-only.

perf(reg_flow): route bending==0 Lamé calls to matvec_lame/diag_lame - #87

Merged
balbasty merged 1 commit into
mainfrom
claude/51-flow-lame-dispatch
Aug 18, 2026
Merged

perf(reg_flow): route bending==0 Lamé calls to matvec_lame/diag_lame#87
balbasty merged 1 commit into
mainfrom
claude/51-flow-lame-dispatch

Conversation

@balbasty

Copy link
Copy Markdown
Contributor

Re: #51 (already closed — see "Issue status" below).

The defect

_flow_matvec, _flow_matvec_acc and _flow_diag in reg_flow.cpp sent any non-zero shears/div straight to the full combined matvec_all / diag_all stencil, with no bending != 0 guard:

if (shears != 0.0 || div != 0.0)
reg_flow::matvec_all<...>(..., absolute, membrane, bending, shears, div);
elseif (bending != 0.0)
...

That leaves the cheaper Lamé-only matvec_lame / diag_lame stencils unreachable from the public dispatch, even though they exist in the impl and are already exercised via relax_lame_. _flow_kernel and _flow_relaxin this same file already nest the branch correctly; this mirrors that exact nesting into the remaining three.

Sites

functionaction
_flow_matvecfixed
_flow_matvec_accfixed
_flow_diagfixed
_flow_kernel, _flow_relaxalready correct, untouched
_flow_matvec_rls, _flow_diag_rls, _flow_relax_rlscorrectly left alone — only *_lame_jrls / *_membrane_jrls exist at the impl layer (there is no bending-aware JRLS kernel), and bending is rejected by the public wrapper before dispatch (reg_flow.cpp:1254), so these already call the Lamé kernel unconditionally
is_matrix uses in the flow_kernel wrapperscorrectly left alone — those select the output tensor's rank, not a stencil, and are independent of bending

Why this cannot change results

Not just asserted from the test suite — checked in the kernel builders. With bending == 0, make_kernel_all's bending-only taps are all exactly zero:

w100 = (-4*bending*vxy - membrane)*vx -> -membrane*vx (== make_kernel_lame's w100)
w010 = (-4*bending*vxy - membrane)*vy -> -membrane*vy (== lame's w010)
w200 = bending*vx*vx -> 0
w020 = bending*vy*vy -> 0
w110 = 2*bending*vx*vy -> 0

and the surviving entries equal make_kernel_lame's one-for-one (kernel[0..2]/[6..8]/[12] of all map onto [0..2]/[3..5]/[6] of lame). So all at bending == 0 is lame plus zero-weighted taps — adding 0.0 * x to a running sum is exact for finite x, so results are bit-identical, and the only difference is work done. Roughly a 9-tap stencil per voxel per channel instead of ~25 for elastic-only (non-bending) registration, a common configuration.

Verification

make -j2 test CXX=clang++, before and after this change, with everything else held fixed (same kernels commit, same impl):

The full suite report is byte-identicaldiff of the two runs' running / checks: / PASSED lines is empty, 37 lines each, 13/13 suites, 0 failures:

test_distance 2352 test_distance_mesh 4622
test_distance_spline 704 test_posdef 4012
test_pushpull 308 test_pushpull_backward 6381
test_reg_field 18284 test_reg_flow 11415
test_reg_op 186 test_resize 630
test_restrict 65 test_splinc 4577
test_solve_field (pass)

Coverage of the newly-reachable path is real rather than incidental — test_reg_flow already runs bending == 0, shears/div != 0 cases through the Lamé symmetry and diag tests, and run_2d_kernel_impulse with bending = 0 cross-checks flow_matvec against the materialised flow_kernel stencil. Since _flow_kernel was already on kernel_lame for bending == 0, that test previously compared matvec_all against kernel_lame and now compares matvec_lame against kernel_lame — independent confirmation the two agree.

Issue status

#51 describes this defect but is already closed (2026-08-05), auto-closed by the unrelated merged PR #82 which referenced it. The defect was nevertheless still live on main — I re-verified the three unguarded dispatch sites directly before changing anything. So this PR deliberately does not say "Closes#51"; someone may want to confirm #51's closure was intended.

Provenance

The same change exists on the teeny branch in 8afcab4. fastfields-cpu-lib#52 was an attempt to port it to main, but it was filed from the claude-fastfields-to-teeny workstream, whose PRs must always target teeny — so it was mis-filed, not wrong. Its analysis is sound and was useful prior art; credit to it. This PR lands the equivalent change on main properly, re-derived and re-verified against current main (where _flow_diag is now templated on char op, and _flow_matvec_acc did not exist when the original was written).

🤖 Generated with Claude Code

https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z


Generated by Claude Code

`_flow_matvec`, `_flow_matvec_acc` and `_flow_diag` sent *any* non-zero
`shears`/`div` straight to the full combined `matvec_all`/`diag_all` stencil
with no `bending != 0` guard, leaving the cheaper Lame-only `matvec_lame` /
`diag_lame` stencils unreachable from the public dispatch even though they
exist in the impl and are already used by `relax_lame_`.
`_flow_kernel` and `_flow_relax` in this same file already nest the branch
correctly; this mirrors that exact nesting into the remaining three.
Numerically a no-op. With bending == 0, make_kernel_all's bending-only taps
(w200, w020, w110 in 2D) are all exactly zero and its remaining entries equal
make_kernel_lame's one-for-one, so `all` is `lame` plus zero-weighted taps --
identical results for finite inputs, just more work per voxel. Elastic-only
(non-bending) registration is a common configuration and now pays roughly a
9-tap stencil per voxel per channel instead of ~25.
The RLS/JRLS dispatches are deliberately untouched: only `*_lame_jrls` and
`*_membrane_jrls` exist at the impl layer (there is no bending-aware JRLS
kernel), and bending is rejected by the public wrapper before dispatch, so
they already call the Lame kernel unconditionally. The `is_matrix` uses in
the `flow_kernel` wrappers select the output tensor's rank, not a stencil,
and are likewise unaffected by bending.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
@github-actions

Copy link
Copy Markdown

Submodule staleness (non-blocking -- fastfields-lib#15)

submodulepinned committracksstatusbehind by
impl41d66b3fastfields/fastfields-cpu-impl@mainup to date0

@balbasty
balbasty merged commit db3c3b5 into mainAug 18, 2026
6 of 7 checks passed
@balbasty
balbasty deleted the claude/51-flow-lame-dispatch branch August 18, 2026 14:56
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: matvec_lame/diag_lame unreachable from _flow_matvec/_flow_diag, still present on main

2 participants

@balbasty@claude