Skip to content

Establish a refinement function mapping sparse feature maps to the Verus sequence model #182

@coderabbitai

Description

@coderabbitai

Background

The Verus proof file verus/decomposition_vector_algebra.rs models vector operations on positional Seq<nat>, but the runtime implementation in common/src/decomposition_advice/vector.rs operates on sparse BTreeMap<String, u64> with key-based feature matching. There is no explicit mapping between feature keys and sequence indices, and no refinement lemma connecting the two representations.

Without establishing how the sparse, key-indexed map semantics correspond to the positional sequence semantics, the lemmas in the proof file do not justify the shipped implementation.

Raised by @leynos in PR #170: #170


Goal

Define and use an abstraction function to lift the BTreeMap<String, u64> state into a canonical Seq<nat> representation over a shared feature universe, then prove that dot_product and norm_squared preserve their meaning under that abstraction.


Task: Establish a refinement bridge between the runtime sparse map and the Verus sequence model

Context

  • Verus proof file: verus/decomposition_vector_algebra.rs
  • Runtime implementation: common/src/decomposition_advice/vector.rs
    • dot_product (line ~241) iterates &BTreeMap<String, u64>, multiplying weights for matching keys only.
    • norm_squared (line ~102) sums squared weights from the same map.
  • The Verus model uses positional Seq<nat> — there is no connection to the runtime's sparse, key-indexed map.

Steps

  1. Define a feature-universe type and an abstraction function.
    In verus/decomposition_vector_algebra.rs, inside the verus! { } block, add:

    // A sorted sequence of all feature keys present in either vector.
    // The abstraction function maps a BTreeMap to a Seq<nat> aligned to this universe.
    spec fn to_seq(map: Map<nat, nat>, universe: Seq<nat>) -> Seq<nat>
        recommends universe.no_duplicates()
        decreases universe.len()
    {
        if universe.len() == 0 {
            seq![]
        } else {
            let key = universe[0];
            let weight = if map.contains_key(key) { map[key] } else { 0nat };
            seq![weight] + to_seq(map, universe.subrange(1, universe.len() as int))
        }
    }

    Adjust the key type to match whatever Verus supports for the Map abstraction (use nat indices if string keys cannot be modelled directly, and document this choice).

  2. Prove that to_seq preserves length.
    Add:

    proof fn lemma_to_seq_len(map: Map<nat, nat>, universe: Seq<nat>)
        ensures to_seq(map, universe).len() == universe.len()
        decreases universe.len()
    { ... }
  3. Prove that the runtime dot-product refines the sequence dot-product.
    Add a refinement lemma:

    proof fn lemma_dot_product_refinement(
        left: Map<nat, nat>,
        right: Map<nat, nat>,
        universe: Seq<nat>,
    )
        requires universe.no_duplicates(),
                forall|k: nat| left.contains_key(k) ==> universe.contains(k),
                forall|k: nat| right.contains_key(k) ==> universe.contains(k),
        ensures
            dot_product(to_seq(left, universe), to_seq(right, universe))
                == sum_matching_keys(left, right),  // spec version of the runtime loop
        decreases universe.len()
    { ... }

    where sum_matching_keys is a spec fn mirroring the runtime's key-intersection loop.

  4. Update the module doc comment to explicitly state that to_seq is the abstraction function and that the proofs hold for any feature universe containing all keys from both maps.

  5. Verify by running:

    make verus 2>&1 | tee /tmp/verus-algebra.log
    grep -E 'error|verified|0 errors' /tmp/verus-algebra.log
    

    Expected: all lemmas verified, 0 errors.


Constraints

  • Do not add vstd or Verus as a dependency of any common/ crate.
  • Keep the proof file under 400 lines; decompose into a separate verus/decomposition_vector_algebra_refinement.rs if needed and add it to the deterministic list in scripts/run-verus.sh.
  • All comments and doc strings must use en-GB-oxendict spelling.

Note from the engineer:

I did not land the requested refinement bridge.

I tried several proof shapes in verus/decomposition_vector_algebra.rs: Map<nat, nat>, total spec_fn(nat) -> nat, and a sequence-of-pairs sparse-map model. In each case, the new refinement lemma either drove rust_verify into very long translation/verification runs or required lower-level sequence facts that would need a more substantial rework than the task described. I reverted those experiments so the repository stays in a healthy state.

Current status is unchanged and clean. I reran:

sh

make verus 2>&1 | tee /tmp/verus-algebra.log grep -E 'error|verified|0 errors' /tmp/verus-algebra.log

Result: existing Verus sidecars still verify with 0 errors.

If you want, I can take this on as a narrower follow-up focused just on making the refinement lemma verify, likely by first introducing small helper lemmas for sequence concatenation/subrange behaviour and then rebuilding the sparse-map abstraction on top of those.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions