Skip to content

feat(spectrum): RC-downward truncated reseed interval; swap into FFI - #30

Merged
nh13 merged 1 commit into
mainfrom
feat/v0.3-trunc-iv-rc
Jun 12, 2026
Merged

feat(spectrum): RC-downward truncated reseed interval; swap into FFI#30
nh13 merged 1 commit into
mainfrom
feat/v0.3-trunc-iv-rc

Conversation

@nh13

@nh13 nh13 commented Jun 12, 2026

Copy link
Copy Markdown

v0.3 perf series (authored fresh on main; the fast twin of #29's binary-search mem_search_backward_truncated_interval).

What

Adds mem_search_backward_truncated_interval_rc and swaps the prmi_mem_search_backward_truncated_interval FFI to call it. Same contract as #29 — find the largest left span L with occ(L) >= min_intv (floored at anchor_len), return its exact forward interval — but it locates L* by walking DOWN in RC-strand space instead of re-locating each candidate length from the model.

Why it's faster

Forward intervals at different lengths do not nest, so the binary-search form pays a fresh model launch (the dominant reseed cost) per occ-query. RC-strand prefixes do nest: Q[..L] = RC(read[anchor_end-L .. anchor_end]), so Q[..L]'s interval is a superset of Q[..L+1]'s. Starting from the maximal extension's in-interval seed, each shorter length expands the carried [lo, hi) outward with a short shares_prefix scan capped at min_intv — no model launch. occ is strand-symmetric (occ(RC(P)) == occ(P)), so L* is identical; one forward mem_search at L* recovers the returned interval.

Byte-identity

Same L*, same mem_search(window) as the binary-search twin. The new proptest cross-checks _rc against both the independent truncated_interval_oracle (#29) and the binary-search _interval it replaces in the FFI, across min_intv ∈ {0,1,2,5,20,1e6}. The existing FFI test now exercises the _rc path.

Scope notes

  • The forward recovery uses a plain mem_search; the .isa-seeded fast path (which would skip the one cold forward locate's model launch) is a later, probe-count-only optimization that needs the seed-window core — byte-identity is unaffected.
  • est_hint != 0 delegates to the binary-search twin (which honors the no_search hint contract); the RC walk is the cold est_hint == 0 reseed's lever, the path the consumer's live reseed takes.

Self-review

Pre-PR /coderabbitai-review flagged that the occ_count == 0 boundary — reachable through the FFI but not from a found anchor — was asserted only by trace. I traced both forms to floor at the anchor (mem_search_backward returns {0,0,0}span_max=0 <= anchor_lenanchor; copying the perf source verbatim would have returned {0,0,0} and diverged) and added an explicit cross-check assertion to lock the FFI-swap invariant at that branch.

Summary by CodeRabbit

  • Documentation

    • Updated search interval computation documentation to reflect current implementation approach.
  • Refactor

    • Improved internal search interval computation algorithm for enhanced reliability and consistency.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@nh13, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 36 minutes and 45 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a7577569-198b-42d4-a391-b7dd7275f0cc

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa920a and f2e6bc6.

📒 Files selected for processing (2)
  • prmi-sys/src/lib.rs
  • prmi/src/index/spectrum.rs
📝 Walkthrough

Walkthrough

This PR introduces mem_search_backward_truncated_interval_rc, an RC-strand downward walk variant for computing maximal left-extension truncation within an occurrence threshold, adds comprehensive tests validating equivalence to oracle and binary-search form, and migrates the FFI wrapper to delegate to this new RC algorithm path.

Changes

RC Truncation Algorithm Migration

Layer / File(s) Summary
RC-downward truncation method and tests
prmi/src/index/spectrum.rs
New public method mem_search_backward_truncated_interval_rc walks RC-strand space downward with an outwardly-expanding interval capped by min_intv, delegates to the existing binary-search form when est_hint != 0, and recovers the exact SA interval via mem_search. Property test validates byte-identical output to oracle and binary-search twin, including the occ_count == 0 boundary case.
FFI wrapper migration to RC algorithm
prmi-sys/src/lib.rs
Documentation rewritten to specify RC-downward walk as the implementation and claim byte-identical output to binary-search form. FFI's internal call migrated from non-_rc to the new mem_search_backward_truncated_interval_rc variant.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • fg-labs/prmi#29: PR #30 builds directly on the truncated-backward-reseed functionality introduced in this PR.
  • fg-labs/prmi#26: The new RC path relies on the modified mem_search behavior that recovers intervals via insertion points returned by forward_maximal_len.

Poem

🐰 A rabbit's leap through RC strands so fine,
Left-extension walks in order—down the line!
Truncated intervals expand with care,
Oracle-blessed, byte-twins everywhere!
The FFI bounds this clever dance,
Equivalence proven—what a prance!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding an RC-downward truncated interval reseed and integrating it into FFI.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
prmi/src/index/spectrum.rs (1)

2150-2236: ⚡ Quick win

Extract the RC downward-walk core shared with mem_search_backward_truncated_span_rc.

This block duplicates the same q construction, in-interval seeding, capped expand, and downward walk already implemented in mem_search_backward_truncated_span_rc. Keeping two copies of the byte-identity-critical walk makes future fixes easy to land in one path but not the other. A small helper that returns L* would keep the span and interval APIs in lockstep.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prmi/src/index/spectrum.rs` around lines 2150 - 2236, This block duplicates
the RC downward-walk logic from mem_search_backward_truncated_span_rc (the q
construction, q_key/tokenize_32mer, forward_maximal_len call, seed selection via
shares_prefix, the expand logic and the downward l-loop that calls
mem_search/recover), so extract that core into a single helper (e.g.,
rc_downward_walk or compute_rc_star) that accepts the prepared q slice (or
q_start/anchor_end/read), anchor_len/anchor_end, sa_num, pac, enc, l_pac and
min_intv and returns the chosen MemMatch (or the L* span so callers can recover
as needed); replace the duplicated code here (the q construction + q_key, seed
derivation, expand closure, downward walk and recover call) with a call to the
new helper and reuse the same helper from mem_search_backward_truncated_span_rc
to keep both paths in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@prmi/src/index/spectrum.rs`:
- Around line 2150-2236: This block duplicates the RC downward-walk logic from
mem_search_backward_truncated_span_rc (the q construction, q_key/tokenize_32mer,
forward_maximal_len call, seed selection via shares_prefix, the expand logic and
the downward l-loop that calls mem_search/recover), so extract that core into a
single helper (e.g., rc_downward_walk or compute_rc_star) that accepts the
prepared q slice (or q_start/anchor_end/read), anchor_len/anchor_end, sa_num,
pac, enc, l_pac and min_intv and returns the chosen MemMatch (or the L* span so
callers can recover as needed); replace the duplicated code here (the q
construction + q_key, seed derivation, expand closure, downward walk and recover
call) with a call to the new helper and reuse the same helper from
mem_search_backward_truncated_span_rc to keep both paths in sync.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a239ad6b-709f-4ab6-a6a3-966e5eb3da2b

📥 Commits

Reviewing files that changed from the base of the PR and between adfc180 and 8aa920a.

📒 Files selected for processing (2)
  • prmi-sys/src/lib.rs
  • prmi/src/index/spectrum.rs

Add `mem_search_backward_truncated_interval_rc`, the fast twin of
`mem_search_backward_truncated_interval`: it locates L* by walking DOWN in
RC-strand space (whose prefixes nest) and expanding the carried [lo, hi)
interval outward with capped `shares_prefix` scans, instead of re-locating each
candidate length from the model as the binary-search form does. Forward
intervals do not nest, so the binary-search form pays a model launch per
occ-query; the RC walk pays a few probes per step plus one forward
`mem_search` at L*.

Byte-identical to the binary-search form (same L*, same forward interval),
verified by a proptest against both the independent `truncated_interval_oracle`
and the binary-search twin. The `est_hint != 0` path delegates to the
binary-search twin (which honors the `no_search` hint contract). The forward
recovery uses a plain `mem_search`; the .isa-seeded fast path is a later,
probe-count-only optimization.

Swap `prmi_mem_search_backward_truncated_interval` to call the RC form (same
signature, byte-identical output); the existing FFI test now exercises it.
@nh13
nh13 force-pushed the feat/v0.3-trunc-iv-rc branch from 8aa920a to f2e6bc6 Compare June 12, 2026 18:11
@nh13
nh13 merged commit c560c3c into main Jun 12, 2026
4 checks passed
@nh13
nh13 deleted the feat/v0.3-trunc-iv-rc branch June 12, 2026 18:25
nh13 added a commit that referenced this pull request Jun 12, 2026
Add `forward_truncate_below_maximal`, the FORWARD analogue of the TRUNC_IV
backward family (#27/#29/#30) for the reseed's right bound. Given a query and its
already-found maximal match whose `occ < min_intv`, it finds the largest length
L < match_len whose prefix occurs >= min_intv times and returns that interval (or
the zero MemMatch when none qualifies).

Forward PREFIX intervals nest directly (fixed start, shrinking end: query[..L] is
a superset of query[..L+1]), unlike the LEFT reseed where prepending bases breaks
forward nesting and forces the RC walk. So the walk carries [lo, hi) outward from
the maximal interval as L shrinks — a short shares_prefix scan capped at min_intv
per step, no model launch. `want_interval == false` returns only L* (the driver's
next_pivot use); `true` recovers the exact interval by galloping find_boundary out
from the capped walk's interior seed (seed-independent, so byte-identical to a
cold mem_search).

Adapted to main: 6-arg shares_prefix/ref_less (no keyed_compare_mask). Verified
byte-identical against an independent per-length mem_search oracle across
reference-lifted queries and a spread of min_intv. Pub primitive with no in-tree
caller until the collect reseed module lands (the proptest is its only consumer).
nh13 added a commit that referenced this pull request Jun 12, 2026
Add `forward_truncate_below_maximal`, the FORWARD analogue of the TRUNC_IV
backward family (#27/#29/#30) for the reseed's right bound. Given a query and its
already-found maximal match whose `occ < min_intv`, it finds the largest length
L < match_len whose prefix occurs >= min_intv times and returns that interval (or
the zero MemMatch when none qualifies).

Forward PREFIX intervals nest directly (fixed start, shrinking end: query[..L] is
a superset of query[..L+1]), unlike the LEFT reseed where prepending bases breaks
forward nesting and forces the RC walk. So the walk carries [lo, hi) outward from
the maximal interval as L shrinks — a short shares_prefix scan capped at min_intv
per step, no model launch. `want_interval == false` returns only L* (the driver's
next_pivot use); `true` recovers the exact interval by galloping find_boundary out
from the capped walk's interior seed (seed-independent, so byte-identical to a
cold mem_search).

Adapted to main: 6-arg shares_prefix/ref_less (no keyed_compare_mask). Verified
byte-identical against an independent per-length mem_search oracle across
reference-lifted queries and a spread of min_intv. Pub primitive with no in-tree
caller until the collect reseed module lands (the proptest is its only consumer).
nh13 added a commit that referenced this pull request Jun 12, 2026
Add `forward_truncate_below_maximal`, the FORWARD analogue of the TRUNC_IV
backward family (#27/#29/#30) for the reseed's right bound. Given a query and its
already-found maximal match whose `occ < min_intv`, it finds the largest length
L < match_len whose prefix occurs >= min_intv times and returns that interval (or
the zero MemMatch when none qualifies).

Forward PREFIX intervals nest directly (fixed start, shrinking end: query[..L] is
a superset of query[..L+1]), unlike the LEFT reseed where prepending bases breaks
forward nesting and forces the RC walk. So the walk carries [lo, hi) outward from
the maximal interval as L shrinks — a short shares_prefix scan capped at min_intv
per step, no model launch. `want_interval == false` returns only L* (the driver's
next_pivot use); `true` recovers the exact interval by galloping find_boundary out
from the capped walk's interior seed (seed-independent, so byte-identical to a
cold mem_search).

Adapted to main: 6-arg shares_prefix/ref_less (no keyed_compare_mask). Verified
byte-identical against an independent per-length mem_search oracle across
reference-lifted queries and a spread of min_intv. Pub primitive with no in-tree
caller until the collect reseed module lands (the proptest is its only consumer).
nh13 added a commit that referenced this pull request Jun 13, 2026
`mem_search_backward` allocated a fresh `Vec` for the reverse-complement query
on every call (the consumer fires ~50 backward calls/read). Build it into a
256-byte stack buffer for the common read length instead, falling back to the
heap only for reads longer than the buffer (e.g. long-read tech).

No probe/result change — byte-identical (the `mem_search_backward_equals_maximal_backward_step`
oracle proptest passes unchanged) — just removes the per-call heap allocation
from the hot backward path. This is the exact `rc_stack`/`rc_heap` pattern already
merged in `mem_search_backward_truncated_span_rc`/`_interval_rc` (#27/#30),
applied to `mem_search_backward`'s RC build.
nh13 added a commit that referenced this pull request Jun 13, 2026
`mem_search_backward` allocated a fresh `Vec` for the reverse-complement query
on every call (the consumer fires ~50 backward calls/read). Build it into a
256-byte stack buffer for the common read length instead, falling back to the
heap only for reads longer than the buffer (e.g. long-read tech).

No probe/result change — byte-identical (the `mem_search_backward_equals_maximal_backward_step`
oracle proptest passes unchanged) — just removes the per-call heap allocation
from the hot backward path. This is the exact `rc_stack`/`rc_heap` pattern already
merged in `mem_search_backward_truncated_span_rc`/`_interval_rc` (#27/#30),
applied to `mem_search_backward`'s RC build.
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.

1 participant