Skip to content

vector: candidate-anchored radius gate — result-side band, native cluster radii, two-tier Skip/Terminate - #185

Closed
RuchirRaj wants to merge 4 commits into
mainfrom
feat/candidate-anchored-radius-gate
Closed

vector: candidate-anchored radius gate — result-side band, native cluster radii, two-tier Skip/Terminate#185
RuchirRaj wants to merge 4 commits into
mainfrom
feat/candidate-anchored-radius-gate

Conversation

@RuchirRaj

@RuchirRaj RuchirRaj commented Jul 27, 2026

Copy link
Copy Markdown

TLDR

RuchirRaj and others added 4 commits July 27, 2026 20:56
…queries

The candidate-anchored IVF probe gate needs the exact current n-th best
at cluster boundaries. `threshold` can't serve that role: it is None
until the first truncation and lags between truncations (it holds the
(n+1)-th best at the last truncation point). kth_best() force-truncates
the buffer to top_n through the existing truncation machinery and
returns the buffer minimum — exact at every call, O(buffer), called only
at cluster boundaries so no per-push overhead is added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a second anchor for the IVF probe loop's distance-ratio gate: the
band follows the result heap's k-th best (via TopNComputer::kth_best)
instead of the first-ranked centroid's distance. The gate stays unarmed
until the segment heap holds top_n filter-passing candidates — the
saturation precondition subsumes the starvation problem the Centroid
arm's min_candidates/overfetch_margin survivor floor patches, so those
knobs are ignored in Candidate mode. Termination applies patience-2
against the ranking stream's documented non-monotone yield order.

Accounting contract: a pending Terminate-condition yield (patience
streak 1) is withheld but neither counted as a radius skip nor
budget-charged — the budget bounds pulls that do work, and a withheld
pending yield does none. ProbeStats gains heap_saturated,
gate_armed_at_probe, gate_armed_at_ceiling, and radius_skips (wired
here; the radius gate lands in a follow-up commit), all flowing through
the VectorSimilarityFruit stats path.

Candidate is the default. The Centroid arm is TEMPORARY A/B scaffolding
retained as the benchmark control, byte-exact with merged main
(centroid_mode_unchanged pins it); its gate computation is isolated in
centroid_gate_threshold so removal is one function + one enum variant.
Ranked-stream consumption order is deliberately unchanged — lower-bound
(d − r) reordering is a named follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One f32 per cluster: the max stored-representation displacement over the
cluster's NATIVE (rank-0) members only — replica spill is excluded so
replication cannot inflate radii into uselessness. True L2 for L2/Dot,
chord (sqrt(2·(1−cos))) for write-time-normalized Cosine; all three
reduce to L2 between stored representations, so one code path serves
every metric.

Computed in the merge's posting-write loop: the clusterer's assign()
returns only cluster ids (no distances), and the posting write already
touches every stored row's bytes post-normalization. The merge threads
provenance through AssignedVector (native: true on the primary
assignment push, false on replica_entries appends — the flag survives
the (cluster, doc) sort) and the fold skips non-native rows. One
l2_squared_bytes per native row (no decode allocation), one sqrt per
cluster. The IVF merge always re-trains and re-assigns, so merges
recompute radii fresh — no carry formula needed.

SOUNDNESS is closure through native homes, documented at the fold: any
point p with d(q,p) ≤ d_K has a native cluster c_p with d(p, μ_{c_p}) ≤
r_native(c_p) by membership, so d(q, μ_{c_p}) ≤ d_K + r_native(c_p) —
c_p can never Skip and sits inside the Terminate bound built from the
native r_max. A skipped cluster may hold qualifying REPLICA copies, but
each copy's native home clears the test; replicas are pure bonus. Same
in chord space for Cosine and via Cauchy–Schwarz for Dot.

Persisted as optional slot [3] of the .centroids composite; per-slot
presence in the composite footer is the format-compatibility mechanism
(slot [2] already works this way). A pre-radius segment loads with
all-zero radii, which reduces the radius-aware gate exactly to the
radius-less candidate gate. IvfIndex exposes cluster_radius(c) and a
load-time-cached max_radius().

Tests: serialize/open roundtrip, absent-slot zeros, length-mismatch
corruption; hand-computed radii per metric built with replicas = 2
pinning the NATIVE maxima (a replica-inclusive fold would read ~10.2);
replica_spill_does_not_inflate_radius (tight blobs + grid-gap spill →
every radius < 0.1); native invariant re-checked across a re-merge; and
an old-format end-to-end — slot [3] stripped from a real segment's
composite on disk, reloaded, queried in Candidate mode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
radius_gate() — pure in (sim, kth, ε, r_c, r_max, q_norm, metric), no
iterator or heap coupling — classifies each gated yield: Probe when the
cluster's radius-adjusted best case (d_c − r_c)⁺ can still beat the
sqrt(1+ε)-widened k-th-best band; Skip when only this cluster is ruled
out (r_c-bound) — a wider cluster at the same centroid distance could
still reach the band, so the terminate streak RESETS; TerminateCondition
when no cluster at this distance could qualify at the segment's widest
radius (r_max-bound), confirmed by patience-2. Per metric: true L2
distances (band sqrt(1+ε)·d_k keeps ε's squared-space meaning),
cosine both sides chord-converted (the space radii are stored in), dot
via Cauchy–Schwarz ⟨q,p⟩ ≤ sim + ‖q‖·r — a real bound, unlike the
Centroid arm's linear-band heuristic. ‖q‖ is computed once per query
and only for Dot.

Radii are NATIVE-only; soundness is closure through native homes (see
the merge's radius fold): a qualifying point's native home can never
Skip and sits inside the native-r_max Terminate bound, so skipped
clusters lose only replica copies whose native homes are still probed.

Accounting: a Skip charges SKIPPED_CLUSTER_COST to the filter-effective
budget (bounding pulls at ~1/SKIPPED_CLUSTER_COST × the ceiling),
increments radius_skips, resets patience, and never enters
probed_clusters; a pending Terminate yield charges and counts nothing
(the streak resolves within two yields, so unpaid pulls are bounded by
one per paid pull). The ceiling-tie check reuses radius_gate so
gate_armed_at_ceiling keeps its meaning under radii. With r_c = r_max =
0 the verdict reduces exactly to the radius-less candidate band and
Skip is unreachable — pre-radius segments behave identically to the
gate without this commit (zero_radii_equals_commit2_gate sweeps this).

At ε = 0 with exact radii, Skip is a per-cluster certificate against
the yield stream: a skipped cluster provably holds no NATIVE member
better than the current k-th best. The ε = 0 trap fixture is
resurrected sound (trap_recovered_by_radius_at_eps0); its radius-less
miss is preserved on a slot-stripped segment.

Tests: trap miss/recovery pair; zero-radii scans report exactly zero
radius_skips (pending yields are not skips); a nearer tight cluster
skipped while a farther wide one probes, with termination strictly
after; a Skip between two Terminate yields resets patience
(hand-ordered stream); a 30-cluster Skip run trips the CEILING on
budget, not the gate; Dot's bound in both directions (fires on small
radii, forces the probe for a high-norm member); cosine chord gating;
and the zero-radii reduction sweep across all three metrics.

Co-Authored-By: Claude Fable 5 <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.

1 participant