Skip to content

lab(mpf): D-MPF-1 — the Stage 1 math polyfill already exists; one expression, oracle, refusals, cost split - #1752

Merged
AdaWorldAPI merged 14 commits into
mainfrom
lab/math-polyfill-probe-20261023
Oct 23, 2026
Merged

AdaWorldAPI merged 14 commits into
mainfrom
lab/math-polyfill-probe-20261023

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

⊘ Correction (2026-10-23, plan §10, commits e10523afb, 46ced03f4). In this description, "R2IL" means lance-graph-r2il::FoldDialect and its procedures. That crate is only the fold reading of R2IL. R2IL itself is an opcode table (ogar-r2il) with two readings:

  • machine (0xC400): r2sleigh's r2conc / r2sym;
  • fold (0xC401): FoldDialect over mask-risc.

The 94–99 ns and 171–172 ns figures are what the mask-risc and FoldDialect paths cost when chosen per call. They are not the cost of R2IL routing: a route resolved once per ClassId runs at the direct ndarray::simd cost (see D-MPF-4 below).

Lab crate plus board records. This adds no primitive, IR or executor, and it does not touch ONNX. It is independent of the three competing S0 plans (#1744, #1745, #1750) and does not choose between them.

Finding

The Stage 1 mathematical polyfill API already ships:

  • the R2IL procedure layer, lance-graph-r2il::procs: selection, comparisons, NULL presence, COUNT/SUM/MIN/MAX, set operations;
  • checked scalar arithmetic in FoldDialect: IntAdd, IntSub, IntMult, IntSDiv;
  • mask-risc as the engine underneath;
  • relalg as the frontend that rewrites before lowering.

Plan: .claude/plans/d-mpf-math-polyfill-v1.md.

Probe (crates/math-polyfill-probe)

  • The expression is E = SUM(v | sel) − k·COUNT(sel), with sel = valid(v) ∧ x ≥ lo ∧ y < hi. It is written only with the existing procedures and scalar calls.
  • The oracle is an i128 row loop that shares no code with the procedures, the dialect or mask-risc.

tests/polyfill.rs (9 tests):

  • E equals the oracle at 64, 1 000, 4 096 and 65 537 rows, for empty, partial and full selections and four multipliers.
  • An anti-vacuity check: the cases really cover an empty selection, a strict subset, and NULL rows inside the selected range.
  • A check that the NULL plane changes the answer.
  • relalg and the procedures count the same rows.
  • Typed refusals: lane + lane arithmetic, unsigned compare, overflow (with a fitting product accepted), an expression whose value does not fit i64, and division by zero.

Disable runs, each committed before the run with its edit anchor asserted:

run edit tests that turned red
D1 drop the NULL gate 3
D2 ≥ → > 2
D3 IntSub → IntAdd 1

Cost split (reported, not gated)

examples/cost_split.rs. Median ns per call, three runs on a 4-core container:

rows mask-risc exec dialect count whole expression scalar oracle
64 116–129 271–279 576–627 78–86
4 096 1 056–1 246 982–1 116 3 197–3 599 5 237–6 430
65 536 17 102–18 534 15 196–15 785 56 625–59 361 91 886–96 264
  • Per row, the target is met. At 64 K rows the expression costs 0.86–0.91 ns/row and is 1.6× faster than the scalar loop. (⊘ superseded by D-MPF-2: that loop was the i128 oracle; an equal-work i64 loop is ~3× faster than the fold.)
  • Per call on a single 64-bit word, it is not. One fold costs about 120 ns in mask-risc plus about 150 ns in the dialect, so the scalar loop is faster than the whole expression. Nanosecond-scale hot execution is therefore not met for one-word populations through these paths (see D-MPF-4).

Gaps, each shown by a failing case

  • G1: no element-wise value arithmetic. Needs an owner decision.
  • G2: no unsigned compare. (⊘ In the fold reading. ndarray has lt/le/gt/ge_u64_to_mask; the refusal is a mask-risc gap for U64 lanes.)
  • G3: no float lane.
  • G4: about 270 ns of fixed cost per fold on a single word. The next probe, D-MPF-2, attributes it before anything changes.

The ONNX fork AdaWorldAPI/optimum-onnx-rs is empty, and AdaWorldAPI/onnx is not accessible, so there is no ONNX integration surface to wire yet.

Checks run

  • cargo fmt --check
  • cargo clippy --release --all-targets -- -D warnings
  • cargo test --release: 9/9
  • entries_index.py --write; fruits_index.py --check passes
  • citation_decay.py: 0 new decays
  • append_only_gate.py: OK

New CI workflow math-polyfill-probe.yml.

D-MPF-2 — where the cost goes (commit 0a6f54c7c, measured, nothing changed)

Every arm is checked against the i128 oracle at every size before timing. Host: AVX-512, target-cpu=native.

  • Per call at 64 rows (~172 ns total):

    layer cost
    scalar i64 loop, for comparison 12 ns
    ndarray floor 39 ns
    mask-risc execution +57 ns
    Program::new +20 ns, 1 allocation
    dialect +50 ns
  • Per row at 64 K rows:

    • mask-risc costs about the same as the ndarray floor.
    • The floor's cost is ndarray::simd::masked_sum_i32, a scalar set-bit walk.
    • Its time tracks popcount: 1.7 / 25 / 44 µs at 0 / 50 / 100 % selected.
  • LLVM (codegen-witness.sh, modelled on ndarray's):

    • masked_sum_i32 is a tzcnt/blsr walk with no vector register.
    • gt_i32_to_mask is one vpcmpltd + kmov per 16 lanes.
    • A branch-free sum vectorizes.
  • Correction: the earlier "1.6× faster than the scalar loop" was measured against the i128 filter-chain oracle. An equal-work i64 loop is ~3× faster than the fold.

  • Proposed, not made: a density-chosen masked_sum_i32 body (owner: ndarray::simd). Sum branch-free when a word has more than ~16 set bits, keep the walk otherwise.

    • The branch-free form equals the oracle and is flat in selectivity.
    • The walk stays 7–10× faster on empty words.
  • Second proposal, not made: reuse the dialect's ops vector across folds to remove its one allocation per fold (owner: r2il). (⊘ An existing facility already removes the whole dialect pass for a fixed query: keep last_program() as a CompiledProgram; D-MPF-4.)

  • Still open: whether the mask form beats a scalar loop when the mask is reused or combined. (⊘ Measured in D-MPF-2b below.)

Plan §7 has the full tables.

D-MPF-2b — hybrid masked sum and mask reuse (commits 0834609fc, 6efc2743e; plan §9)

Those commits call this D-MPF-3. On the board that id is G1, so it is renamed D-MPF-2b.

  • A per-word density threshold (16 set bits) over masked_sum_i32: 2–3.7× faster on uniform masks, 4–5× on clustered ones, and never slower than the walk.
  • Reusing one mask across count/sum/min/max: ~2× when sparse. At 50 % selected a fused scalar pass is 6× faster, because min and max still walk the set bits.
  • Proposal for the ndarray::simd owner only; nothing in ndarray changed.

D-MPF-4 — R2IL as a ClassId-lazily-bound router (plan §10)

Attribution. Five different things are called R2IL:

  • r2sleigh's r2il: IL types only;
  • the machine reading: r2conc / r2sym;
  • ogar-r2il: the opcode table;
  • lance-graph-r2il::FoldDialect: the fold reading;
  • mask-risc / ndarray::simd / simd_runtime.

The earlier single-digit-ns figures are all direct kernels. The relevant one is ndarray::simd AND, AND, popcount over 4 words: 2.2–2.8 ns, against 54–70 ns for count_and(3) through r2il + mask-risc (entries/2026-10-21-d-rft-1-cost-breakdown.md).

Routing table (plan §10.3). Every wired fold-reading op is either DIRECT or COMPOSE onto existing ndarray::simd entrypoints, or refused. LOWER (R2Sleigh) is needed only for machine semantics: carry/borrow flags, address-space load/store, branches, width-exact integers. Floats, atomics and CallOther are refused by r2conc itself. Certification (egglog) is separate and runs before execution, never per call.

Probe examples/classid_route.rs. ClassId → LazyLock → callable copied into the binding → direct kernel. Frame, focus and scope are checked on every run by the DR lab's FrameStore::check. ns per call, two runs:

rows direct nd route route, no guard LazyLock + match per call mr prepared r2il prepared dialect onnx wrapper
64 36–39 61–67 36–39 37–39 85–94 85–91 167 204–219
512 185–203 199–223 185–203 186–192 229–249 229–250 358–381 344–395
  • The prebound route matches direct ndarray. The remaining 27–28 ns is the guard, which allocates nothing: objdump shows four atomic RMWs in FrameStore::check, from Arc::downgrade.
  • "r2il prepared" is the existing facility: run FoldDialect once and keep its last_program() as a CompiledProgram. It equals prepared mask-risc. The remaining ~50 ns over direct ndarray is mask-risc's per-call precheck / validate.
  • Asserted:
    • one resolution per ClassId;
    • 0 route evaluations and 0 allocations in steady state;
    • LOWER and REFUSE refused at bind;
    • focus, scope and source changes refused at run;
    • rebinding does not re-resolve the route.
  • Three disable runs went red.
  • LLVM: probe_route_run is the guard followed by a tail jmpq *80(%rax) into the stored callable. There is no LazyLock, Once or backend match on that path.
  • Proposal, not made (DR lab owner): compare Weak::as_ptr with Arc::as_ptr in FrameStore::check. Identity semantics are unchanged and the atomics go away.
  • Adds no ABI, interpreter or dispatcher to any production crate.

D-NFC cross-reference

This PR is layer [1] of the canonical dependency diagram in #1753. (⊘ Layer [1] is the R2IL router. The procedures and FoldDialect measured here are one strategy beneath it, not the layer itself; #1753's diagram is being corrected to match.) The PR stays in draft pending independent review. The 50 ns one-word target is not met through the mask-risc and FoldDialect paths. The fair i64 scalar baseline supersedes the earlier 1.6× claim (plan §7–§8).


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg

…oop oracle, typed refusals

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
- examples/cost_split.rs: bind / mask-risc exec / dialect count / whole
  expression / keep vs count / relalg / oracle, ns per call, reported not
  gated; asserts oracle equality before timing.
- .claude/plans/d-mpf-math-polyfill-v1.md: the Stage 1 polyfill is the
  shipped r2il procedure layer + checked scalar arithmetic over mask-risc;
  gaps G1-G4 by counterexample; next stages; open points.
- .github/workflows/math-polyfill-probe.yml: fmt, clippy, tests, cost split.
- Board: entry E-D-MPF-1, STATUS_BOARD D-MPF rows, INTEGRATION_PLANS entry,
  entries index regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 45fbfc1a-c7c4-426f-8104-dadd6e5b14b8
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…aph), oracle tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
… takes ndarray by git)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
…tives

Three probe examples and a codegen witness, lab crate only. No primitive,
contract or ABI changed.

- fixed_cost: one question (sum x > t) through six arms, each one layer
  more than the last, every arm checked against the oracle at every size.
  At 64 rows: scalar 12 ns, ndarray floor 39, mask-risc +57, Program::new
  +20 with 1 allocation, dialect +50 (~172 ns).
- floor_split: per row the floor is the bottleneck, and inside it
  ndarray::simd::masked_sum_i32, whose cost tracks popcount (1.7 / 25 /
  44 us at 0 / 50 / 100 % of 65 536 rows). A branch-free sum equals the
  oracle and is flat in selectivity; the walk stays 7-10x faster on empty
  words.
- codegen_probe + codegen-witness.sh (modelled on ndarray's): masked_sum
  is a scalar tzcnt/blsr walk with no vector register; gt_i32_to_mask is
  one vpcmpltd + kmov per 16 lanes; the branch-free sum vectorizes.

Corrects D-MPF-1: '1.6x faster than the scalar loop' compared against the
i128 filter-chain oracle; an equal-work i64 loop is ~3x faster than the
fold. Plan section 7, STATUS_BOARD D-MPF-2, board entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
examples/layers.rs runs count, exists, sum, min and max over x > t through
ndarray primitives, the mask-risc program and the R2IL polyfill on the
same resident lane, oracle-checked at every size, and reports each layer's
delta over the one below (before / after r2il) and allocations per call.

Measured: mask-risc adds ~50-100 ns per call and the R2IL polyfill another
~55-90 ns plus one allocation; both are fixed and fall inside noise by
65 536 rows. Count beats the scalar loop; sum is ~3x slower (masked_sum
bit walk); min/max match it; exists is not equal work (the scalar loop
stops at the first hit). Plan section 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
examples/hybrid.rs, probe crate only; ndarray and R2IL unchanged. Every
configuration is oracle-checked.

- Hybrid sum (skip empty words, walk sparse words, branch-free over dense
  ones, threshold 16 bits): matches or beats ndarray's masked_sum_i32 walk
  at every density measured, ~2x at 10 %, ~3x at 50 %, ~3.7x at 100 %
  (uniform), 4-5x on clustered masks. Branch-free alone loses 2-4x on
  sparse masks, so the walk stays for sparse words.
- Mask reuse for count/sum/min/max: ~2x over recomputing when sparse,
  20-30 % when dense; a fused scalar pass is still ~6x faster at 50 %
  selected because masked_min/max walk every set bit.

Proposal for the ndarray owner, approved separately. Plan section 9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
classid_route resolves each ClassId's route (DIRECT / COMPOSE / LOWER /
REFUSE) once in a LazyLock, copies the callable into the binding, and
times it against direct ndarray::simd, prepared mask-risc, the
FoldDialect's own last_program reused as a CompiledProgram, the dialect
per call and the ONNX-shaped wrapper (diagnostic). Frame, focus and
scope stay checked on every run with the DR lab's FrameStore::check.

Asserted: one resolution per ClassId, zero route evaluations during the
steady-state and multi-op loops, zero allocations on the route path,
LOWER/REFUSE refused at bind, focus/scope/source changes refused at
run, rebinding does not re-resolve the route. Adds no ABI, interpreter
or dispatcher to any production crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
…nd measurement

Plan §10 separates five things that are all called R2IL:
- r2sleigh's r2il IL types;
- the machine reading (r2conc / r2sym);
- the ogar-r2il opcode table, with readings 0xC400 machine and 0xC401 fold;
- lance-graph-r2il::FoldDialect, the fold reading only;
- mask-risc / ndarray::simd / simd_runtime.

The D-MPF-2 figures (94-99 ns, 171-172 ns) are recorded as the cost of
choosing those paths per call, not as a cost of R2IL routing.

Adds:
- the recovered single-digit-ns figures, each with its exact symbol (all
  direct kernels, none an R2IL call);
- a DIRECT / COMPOSE / LOWER / REFUSE table for the wired fold-reading ops
  and the machine-only cases that need R2Sleigh;
- the per-call layers that existing facilities already remove;
- the classid_route numbers.

The prebound route equals direct ndarray. The remaining 27-28 ns is the
frame/focus/scope guard: four atomic RMWs from Arc::downgrade.

Renames the hybrid section to D-MPF-2b, with a correction note: D-MPF-3 is
G1 on the board. Adds STATUS_BOARD rows D-MPF-2b / D-MPF-4 and a board
entry, and regenerates the entries index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T22:19:01.572295Z 46ced03 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 46ced03f44

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

count(d, &mut s)?;
s.push(Val::Scalar(e.k));
d.call(INT_MULT, [0; 3], &mut s)?;
d.call(INT_SUB, [0; 3], &mut s)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check overflow in the expression's final subtraction

When the product fits i64 but the final subtraction does not, this expression silently returns a wrapped answer in release builds instead of the promised typed refusal. For example, one selected, present row with v = -2 and k = i64::MAX produces oracle = (1, None) but eval = Ok(i64::MAX) (reproduced against the release build). The existing FoldDialect::INT_SUB implementation uses unchecked x - y, unlike its checked multiplication. Ensure this subtraction is checked and add a boundary case that overflows at subtraction rather than multiplication.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed and fixed in 4dc44a4c1. FoldDialect's IntSub was an unchecked x - y. It now uses checked_sub and refuses with FoldError::Overflow(INT_SUB), the same as IntAdd and IntMult.

Your case (one row, v = -2, k = i64::MAX) returned Ok(i64::MAX) before the fix. It is now the test an_expression_that_overflows_at_the_subtraction_is_refused, paired with a version that fits and still answers. The r2il refusal table also gains two IntSub rows.

The r2il, relalg, r2il-mask-abi-probe and math-polyfill-probe test suites pass.


Generated by Claude Code

FoldDialect computed IntSub as an unchecked x - y, so in release a
subtraction that leaves i64 wrapped (the polyfill expression returned
i64::MAX for one row v = -2, k = i64::MAX, where the oracle says the
answer does not fit). IntSub now uses checked_sub and refuses with
FoldError::Overflow, like IntAdd and IntMult.

Tests: the probe gains a boundary case that overflows at the
subtraction with a silent twin that fits; the r2il refusal table gains
two IntSub rows. Red before the fix (Ok(i64::MAX)), green after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
…be-20261023

# Conflicts:
#	.claude/board/STATUS_BOARD.md
#	.claude/board/entries/README.md
AdaWorldAPI pushed a commit that referenced this pull request Oct 23, 2026
…1753)

The diagram labelled the ClassId-prebound router SHIPPED. Only the
strategies beneath it ship; the router exists as the #1752 lab example
and no production ClassId table exists (S17a). Marked supersession.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
…be-20261023

# Conflicts:
#	.claude/board/STATUS_BOARD.md
#	.claude/board/entries/README.md
@AdaWorldAPI
AdaWorldAPI merged commit 5367fae into main Oct 23, 2026
18 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Oct 24, 2026
…marked shipped

- PR_ARC_INVENTORY: compact entries for #1742–#1758, recorded from each
  merge commit and its changed-file list.
- LATEST_STATE: "main after #1758" section.
- STATUS_BOARD: D-ONNX-NATIVE-0 (#1754), D-DRF-1 (#1756), D-NFR-6 (#1755),
  D-MPF-1 (#1752) and D-ONNX-0 (#1744) were still "In PR" / "this branch";
  now point at their merge commits.
- INTEGRATION_PLANS: index row for six plans merged without one
  (#1745–#1749, #1751).
- d-onnx-relalg-cap-v1 (#1744's S0): owner supersession note adopting
  D-NFC §1/§2 by reference (D-NFC §6 step 1); OP-ONNX-1 marked answered
  by D-NFC §4 + lance-graph-onnx. Which S0 draft carries the adapter
  layer stays open for the three owners.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VncKoyG6JkpsoqhqTGTv3f
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.

2 participants