Skip to content

lance-graph-r2il: R1 successor, native prepared plans over CompiledProgram - #1897

Merged
AdaWorldAPI merged 13 commits into
mainfrom
ccr-85acaff0-oszq40-r1b
Oct 31, 2026
Merged

AdaWorldAPI merged 13 commits into
mainfrom
ccr-85acaff0-oszq40-r1b

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

This is the successor to #1892, which stays open as measured evidence until this one replaces it. The branch contains #1892's binding seam plus the new native path.

What changes. #1892's run re-interprets every step through FoldDialect on every call. It keeps per-run BTreeMap values, rebuilds the stack and allocates the receipt, which costs about 280 ns and 5 allocations per call.

  • Prepare once. binding_native::prepare_native runs the plan once through FoldDialect, which is the compilation D-MPF-4 proved. It keeps each fold's program as a mask-risc CompiledProgram, in step order, and records the output layout.
  • Run without allocating. run_native executes those programs with execute_compiled and fills a fixed-size receipt. It allocates nothing and adds no new interpreter.

Validation stays independent. run_native checks guards (frame, focus, scope), route and permit exactly as run does, in the same order and with the same refusals. ExecPermit remains a lab permission, and its doc now says so. It makes no production authorization claim, and #1891's ExecutionPolicy stays lab too.

Refusals instead of frozen values. Compiling once must never freeze a value that should be recomputed, so these cases are refused by name:

  • FoldResultFeedsFold: a fold that consumes an earlier fold's result;
  • ScalarAfterFold: a scalar computed from a fold result;
  • DataScalarNotPrepared: a fold-free scalar read from the planes;
  • TooManyOutputs;
  • PlanesChanged: the shape differs from preparation.

Multi-fold plans run their programs in step order.

Tests (tests/integration/binding_native.rs, 16):

  • run_native agrees with run on every runnable fixture: values, programs_run, plan_id, frame and intermediates;
  • a two-fold plan runs in step order;
  • each refusal has a can-stay-silent twin;
  • run's early refusals are reproduced, with their precedence;
  • changed planes and a scratch buffer from another plan are refused;
  • zero allocations over 1,000 run_native calls for four plans. The same counter is shown to see run allocate, so it can fire.

Four refusal guards were disabled one at a time, and each turned its test red. When only the pre-call FoldResultFeedsFold check is disabled, the suite stays green, because the post-fold check catches the same case.

Benchmark (r1_vs_prebound, 8 arms). Every timed call is checked against the oracle. Median ns per call, from the clean run of three; the other two were disturbed by VM load.

rows mask-risc prepared R2IL prepared R1 run R1 native
64 105.5 107.2 405.8 164.2
512 204.0 207.0 499.1 256.0
4096 1086.9 1089.1 1435.8 1139.7
65536 18670.8 18812.1 19039.0 18726.3

Allocations per call: R1 native 0, R1 run 5.

R1 native costs a constant ~50–60 ns above prepared mask-risc, independent of row count. The guard check alone costs ~30 ns (the prebound vs prebound+guard arms), and route lookup and the permit scan account for the rest. That split is a working model and was not timed separately.

Not done:

  • Plans that compute a scalar from a fold result are refused, not executed natively.
  • The permit check is still a linear scan.

cargo test -p lance-graph-r2il --release passes (9 + 28 + 22), and clippy -D warnings and fmt are clean.


Generated by Claude Code

…are/run)

binding::prepare resolves an admitted DT64 MethodBinding once, cold: frame,
focus, scope and route guards, class basis, admit, then per effect the
lens (OGAR's R2IL fold reading), the word's label resolved to exactly one
opcode by OGAR's vocabulary, the translatable subset, and OGAR's arity
against the lens's declared arity and the argument count. binding::run
executes the bound opcodes through FoldDialect, hot, rechecking only the
guards, the route and an explicit execution permit, and returns a receipt
bounded by the plan's step count.

This reading is optional: no consumer is required to execute through it.
Unsupported shapes (WHEN guards, three-valued truth, immediates, sinks,
out-of-range lane constants, unbound fields) are refused by name.

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

BindStore::register refuses an effect whose argument count differs from
the lens's declared arity, so comparing OGAR's arity with the declared one
could never fire on its own (a disable run left every test green). The
check is OGAR's arity against the argument count; the declared arity is
still reported in the refusal.

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

Seven arms on SUM { a : a < t }: direct ndarray, ClassId → LazyLock
prebound callable, prebound plus the dt64 guard R1 runs, mask-risc
prepared, R2IL prepared (FoldDialect once, CompiledProgram kept),
FoldDialect per call, and R1 binding::run.

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

Measured on 4 cores, median ns per call, answers asserted equal across arms:
64 rows: direct 29.4, prebound 30.0, +guard 60.9, mask-risc prepared 103.3,
r2il prepared 103.2, dialect 205.9, R1 run 398.4. R1 runs FoldDialect once
per call and allocates 5 times per call; prepared paths allocate 0.

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

The R1 arm discarded failures with if let Ok; every arm now counts calls that
returned the oracle's answer and the run fails unless that count equals the
call count. Board entry records the 4-row-size table (65,536 included), the
conclusion (prepared R2IL equals prepared mask-risc; R1's extra cost is its
interpreter and receipt) and the operator decision on #1892.

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

prepare_native runs a Prepared plan once through FoldDialect, keeps each
fold's program as a mask-risc CompiledProgram in step order, and records the
output layout. run_native checks guards, route and permit exactly as run does,
then executes the programs with no allocation and returns a fixed-size
receipt. Plans whose values compiling once would freeze are refused by name:
a fold fed by a fold result, a scalar computed after a fold, a fold-free
scalar read from the planes, too many outputs, and changed planes.
ExecPermit stays a lab permission.

Tests: agreement with run on every runnable fixture, step-ordered multi-fold,
each refusal with a can-stay-silent twin, run's early refusals reproduced, and
zero allocations per run_native call (the counter is shown to see run
allocate). Benchmark gains an 8th arm.

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

coderabbitai Bot commented Oct 31, 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: 67464e69-9cf9-4aaf-8357-2fd270b4b0ea
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 31, 2026 19:01
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 31, 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-31T19:02:46.089826Z 9e6e848 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: 9e6e848137

ℹ️ 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".

Comment thread crates/lance-graph-r2il/src/binding.rs Resolved
Comment thread crates/lance-graph-r2il/src/binding.rs Resolved
…alect state per run

Two review findings on the binding layer, both reproduced first:

- IntAnd's survivor gating rewrites the last predicate in place, so a field
  that is the right operand of an AND and is read again later changed meaning.
  m = a<5; t = a<10; u = m AND t; s = POP_COUNT(t) counted a<5. The dialect
  gains call_shared, which emits a separate And; binding::call_step uses it
  when an operand field is read by a later step, in run and prepare_native.
  Stack programs that consume their operands keep the gating.
- run left a plan's unconsumed predicates in the caller's dialect, so repeated
  predicate-only plans grew its ops until a later fold failed with
  ScratchBufferTooSmall. FoldDialect::discard_pending drops them and advances
  the epoch; run calls it before and after the steps, on success and error.

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMNZqKXSaeb9g2ypywF
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMNZqKXSaeb9g2ypywF
@AdaWorldAPI
AdaWorldAPI merged commit b38d88e into main Oct 31, 2026
16 checks passed
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