Repository navigation
Conversation
|
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Comment |
Mutation testing (cargo-mutants,
|
| caught | missed | unviable | timeout |
|---|---|---|---|
| 89 | 0 | 5 | 0 |
Every mutant in the changed lines was caught by a test.
ADR-0009 decision 5: a caller who wants `want` real matches from a probabilistic query fetches `want + Poisson_quantile(λ = p·N, confidence)` rows, where `p` is the mean over stored filters of `f^b`, `f` a filter's fill and `b` the bits the query sets. - `Probabilistic::target_limit(&self, want, &Stats) -> Limit`. - `op::Stats::declared(rows)` uses a new `MatchConfig::expected_tokens()` prior, a default method returning `None`, so existing configurations compile unchanged; it is not part of the index's identity. - `op::Stats::measured(rows, histogram)` takes `(set bits, rows)` buckets from one sampled aggregate over the column, and takes the mean of the power, never the power of the mean fill. An empty histogram falls back to the prior. - `op::Limit` is `Rows(n)` or `Unbounded`, the latter when nothing is known of the stored filters or the confidence is not in (0, 1). `fetch_or(page)` reads either as a fetch size. - The quantile sums the Poisson mass exactly in log space from `λ − 10√λ`, so it costs about `12√λ` terms; past `λ = 1e7` it is the conservative `λ + 8√λ`. Unit tests reproduce the ADR's worked table and its mixed-length column, and pin the quantile to reference values computed with `lgamma`. Closes #1184 Claude-Session: https://claude.ai/code/session_01VYKAWbd361xDLmqBdLoUcB
- The Poisson quantile is a bounded, normalised sum over `[λ − 10√λ, λ + 12√λ + 50]`. Each term is a log relative to the window's first, so a large `λ` no longer loses the starting term to cancellation, and the sums run from the top, where the terms are smallest, so a confidence near 1 is compared against a tail rounding did not swallow. It matches an exact reference to within one up to 1 − 1e-12 (was off by thousands at λ ≈ 4.75e6), and no mutation of it can loop forever. `ln_factorial` is gone. - A `Limit` never exceeds `op::MAX_ROWS` (`i64::MAX`), the largest SQL `LIMIT`; `reltuples` of −1 read as a `u64` no longer overflows a bind. The ADR's statements use `greatest(reltuples, 0)`. - Histogram counts are summed as floats, so huge counts cannot overflow. - The independence assumption is stated in ADR-0009 decision 5 and on `target_limit`: a row sharing tokens with the needle passes more often, so the confidence is nominal on natural text; `verify` still decides. - Docs: the stats SQL filters NULL values; `target_limit` says a fetch that returned the limit is a page only when fewer than `want` verified; `expected_tokens` counts distinct tokens; decision 4's code shows `target_limit` on `Probabilistic`. - Tests: exact references at high confidence and at λ = 1e7 (which kills the threshold mutant), the `MAX_ROWS` cap, float-summed counts, and the measured-column test bounds the fetch against the histogram's own λ. Claude-Session: https://claude.ai/code/session_01VYKAWbd361xDLmqBdLoUcB
e008d76 to
b3dd1f7
Compare
Review (high effort): findings and how each was addressedAn independent review checked 5,220 (λ, confidence) pairs against an exact mpmath reference, plus a port of the mutants. The fixes are in
The reviewer also confirmed these were fine:
575 tests pass locally, with doctests, clippy and rustdoc clean. |
CI's mutants gate found one survivor and three timeouts in `poisson_quantile`: - Shifting each log term by the window's peak never mattered: relative to the window's first term no term exceeds about e^100, so `-` → `+` there was equivalent. The shift is gone. - A mutated threshold or window bound sent the `MAX_ROWS` test's λ of about 9e18 into a window of billions of terms. The window is now `22√λ + 50` wide from its start, capped at 2^20 terms whatever the input, so no mutation can hang a test. - The window's end is the answer when the confidence allows no mass (above 1), now pinned by a test. A focused `cargo mutants` run over `op/stats.rs` leaves no survivor and no timeout. Claude-Session: https://claude.ai/code/session_01VYKAWbd361xDLmqBdLoUcB
|
Follow-up: the CI mutants gate on
A focused |
CI's mutants gate found `start..=start + width` → `start * width` surviving: the loop's range sat outside the cap on the window's width, so the mutation ran for billions of terms instead of failing. The loop now runs over `0..=width`, the capped width itself, and derives each `k` from it. A focused `cargo mutants` run over `op/stats.rs`: 91 mutants, 87 caught, 4 unviable, none missed, no timeouts. Claude-Session: https://claude.ai/code/session_01VYKAWbd361xDLmqBdLoUcB
|
Another survivor came up from the CI mutants gate on Fixed in A focused |
Summary
Stacked on #1191. This PR implements ADR-0009 decision 5: a probabilistic query says how many rows to fetch to expect
wantreal matches.Probabilistic::target_limit(&self, want, &Stats) -> Limitreturnswant + Poisson_quantile(λ = p·N, confidence). Herepis the mean over stored filters off^b, andbis the number of bits the query sets.Stats::declared(rows)uses a newMatchConfig::expected_tokens()prior. It is a default method returningNone, so existing configurations compile unchanged. It is not part of the index's identity and changes no term.Stats::measured(rows, histogram)takes(set bits, rows)buckets, which one sampled aggregate over the column gives. It takes the mean of the power, never the power of the mean fill. That was the review finding on docs(adr): ADR-0009, operators select the index and a query term is not the stored term #1177, and a test pins it. An empty histogram falls back to the prior.Limit. It isRows(n), orUnboundedwhen nothing is known of the stored filters or the confidence is not in (0, 1).fetch_or(page)reads either as a fetch size.[λ − 10√λ, λ + 12√λ + 50], which holds all but about 1e-23 of the mass. Each term is kept as a log relative to the first, and the sums run from the top, so it matches an exact reference to within one up to a confidence of 1 − 1e-12. That costs about22√λterms. Pastλ = 1e7it returns the conservativeλ + 8√λ.Limitnever exceedsop::MAX_ROWS(i64::MAX).Tests
op::stats:math.lgamma, from λ = 0.034 to 1e6.want + 1,want + 7805andwant + 3.m, and overflow ofwant.tests/op.rs:target_limitisUnboundeduntil the column is measured from real stored filters.Test plan
cargo nextest run -p stack-encrypt --all-features: 575 passed. The review fixes are in a second commit; see the review comment.-D warningswith and without--all-features, and rustdoc with warnings as errors.The ADR examples now use
fetch_or(page). ADR-0009 staysproposed. #1178 says to mark it accepted once this pinsStatsandLimit, and that call is a human's.Closes #1184
https://claude.ai/code/session_01VYKAWbd361xDLmqBdLoUcB