Skip to content

test(planner): property-test the Rust image of optimize_is_permutation - #209

Merged
hyperpolymath merged 2 commits into
mainfrom
test/planner-property-tests
Jul 29, 2026
Merged

test(planner): property-test the Rust image of optimize_is_permutation#209
hyperpolymath merged 2 commits into
mainfrom
test/planner-property-tests

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

test(planner): property-test the Rust image of optimize_is_permutation

formal/Planner.v proves optimize_is_permutation about a Coq function.
That says nothing about this crate until something checks the Rust against the
same statement, and Planner.v:23-28 has promised exactly that since the module
was written:

A property test added alongside Provenance.v's chain-link integrity check
would exercise this in Rust.

It was never added. This is that test, and it closes the refinement link #113
owes — the half that #206 could not supply, because discharging an axiom in Coq
tells you nothing about the Rust it models.

The gap it closes. optimizer.rs already has eight unit tests, but they
assert ordering (test_vector_before_graph, test_semantic_always_last) and
counts. None asserts the multiset — which is the actual content of the
permutation property, and the only thing that catches a node being silently
dropped, duplicated, or swapped for another.

Four properties, stated over the same (modality, list condition) projection
the Coq model uses so the two are comparable:

  • prop_optimize_is_permutation — multiset preserved.
  • prop_optimize_total_cost_invariant — permuting the input must not change the
    estimate, since that is what makes reordering a legitimate optimisation.
    Compared with a relative tolerance rather than ==: f64 +/* are
    commutative but not associative, so combine is permutation-invariant
    only up to rounding. Same caveat recorded in Planner.v's header.
  • prop_optimize_never_panics — guards the comparator. The tie-break is
    partial_cmp(...).unwrap_or(Equal) on f64, which a NaN makes non-transitive,
    violating sort_by's contract and potentially panicking on Rust >= 1.81. NaN
    looks unreachable today (the only division in CostModel::estimate is guarded
    and feeds selectivity, not time_ms) but nothing in the types enforces it,
    so the belief is tested rather than asserted.
  • prop_pushdown_fields_survive — generalises fix(planner): carry projections and early_limit into the physical plan #207's single hand-built case
    across the generated space.

Plus empty_plan_is_rejected, stated once because the Coq optimize is total
while the Rust one is partial (PlannerError::EmptyPlan). Recording that
difference beats letting the generators quietly avoid it.

Verified as real guards, not vacuous ones. Two negative controls:

  1. Dropping a node -> 2 properties fail with node count changed.
  2. A count-preserving corruption (duplicating node 0 over the last node) —
    invisible to any length assertion — is caught by the multiset with the exact
    diagnostic ("Graph", "Equality { field: \"a\", ... }"): 2. That is the
    specific case the existing unit tests cannot see, demonstrated rather than
    claimed.

Both restored to green afterwards.

Note on the committed .proptest-regressions: the repo already tracks these
(see verisim-document), and proptest recommends checking them in. But be clear
about provenance — both seeds come from the injected failures above, not from
any real historical bug. They shrink to a trivial one-node Graph plan and pass
now; they are cheap to replay and should not be read as evidence of a past
defect.

Verified: cargo test -p verisim-planner 118 + 5 + 4 + 1 passing, 0 failed;
clippy clean on --all-targets; cargo doc --no-deps clean (it is a required
check); reuse lint compliant.

`formal/Planner.v` proves `optimize_is_permutation` about a *Coq* function.
That says nothing about this crate until something checks the Rust against the
same statement, and `Planner.v:23-28` has promised exactly that since the module
was written:

  > A property test added alongside Provenance.v's chain-link integrity check
  > would exercise this in Rust.

It was never added. This is that test, and it closes the refinement link #113
owes — the half that #206 could not supply, because discharging an axiom in Coq
tells you nothing about the Rust it models.

**The gap it closes.** `optimizer.rs` already has eight unit tests, but they
assert *ordering* (`test_vector_before_graph`, `test_semantic_always_last`) and
*counts*. None asserts the multiset — which is the actual content of the
permutation property, and the only thing that catches a node being silently
dropped, duplicated, or swapped for another.

Four properties, stated over the same `(modality, list condition)` projection
the Coq model uses so the two are comparable:

* `prop_optimize_is_permutation` — multiset preserved.
* `prop_optimize_total_cost_invariant` — permuting the input must not change the
  estimate, since that is what makes reordering a legitimate optimisation.
  Compared with a relative tolerance rather than `==`: f64 `+`/`*` are
  commutative but **not associative**, so `combine` is permutation-invariant
  only up to rounding. Same caveat recorded in `Planner.v`'s header.
* `prop_optimize_never_panics` — guards the comparator. The tie-break is
  `partial_cmp(...).unwrap_or(Equal)` on f64, which a NaN makes non-transitive,
  violating `sort_by`'s contract and potentially panicking on Rust >= 1.81. NaN
  looks unreachable today (the only division in `CostModel::estimate` is guarded
  and feeds `selectivity`, not `time_ms`) but nothing in the types enforces it,
  so the belief is tested rather than asserted.
* `prop_pushdown_fields_survive` — generalises #207's single hand-built case
  across the generated space.

Plus `empty_plan_is_rejected`, stated once because the Coq `optimize` is total
while the Rust one is partial (`PlannerError::EmptyPlan`). Recording that
difference beats letting the generators quietly avoid it.

**Verified as real guards, not vacuous ones.** Two negative controls:

1. Dropping a node -> 2 properties fail with `node count changed`.
2. A **count-preserving** corruption (duplicating node 0 over the last node) —
   invisible to any length assertion — is caught by the multiset with the exact
   diagnostic `("Graph", "Equality { field: \"a\", ... }"): 2`. That is the
   specific case the existing unit tests cannot see, demonstrated rather than
   claimed.

Both restored to green afterwards.

Note on the committed `.proptest-regressions`: the repo already tracks these
(see `verisim-document`), and proptest recommends checking them in. But be clear
about provenance — both seeds come from the *injected* failures above, not from
any real historical bug. They shrink to a trivial one-node Graph plan and pass
now; they are cheap to replay and should not be read as evidence of a past
defect.

Verified: `cargo test -p verisim-planner` 118 + 5 + 4 + 1 passing, 0 failed;
clippy clean on `--all-targets`; `cargo doc --no-deps` clean (it is a required
check); `reuse lint` compliant.
@gitar-bot

gitar-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your trial's included automatic processing has been used for this period. Upgrade now, or comment "Gitar review" to run a review anytime.
Learn more

Code Review ✅ Approved

Adds property-based tests for the Rust implementation of optimize_is_permutation to verify multiset preservation, cost invariants, and safety guarantees against the Coq model. No issues found.

Auto-approved and auto-merge armed: No blocking issues found.
Please see Auto-approve Docs for details on setting custom approval criteria. — merges when pipeline and required approvals pass.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@gitar-bot

gitar-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

⚠️ Gitar auto-approved this PR but could not enable auto-merge: auto-merge is disabled for this repository — enable "Allow auto-merge" in the repository settings.

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR and enabled auto-merge (configure)

@gitar-bot gitar-bot Bot added the gitar-approved Added by Gitar label Jul 29, 2026
`rustfmt` is a required check and `physical_multiset`'s signature was wrapped
across three lines where it fits on one. My pre-commit verification ran test,
clippy, doc and reuse but not `cargo fmt --check` — adding it to the list.

No behaviour change; the 5 property tests still pass.
@hyperpolymath
hyperpolymath merged commit 0ea3ae3 into main Jul 29, 2026
41 checks passed
@hyperpolymath
hyperpolymath deleted the test/planner-property-tests branch July 29, 2026 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gitar-approved Added by Gitar

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant