Date: 2025-11-17 Status: ✅ COMPLETE Scope: I-type and M-type standard operations + Skip-to-Match optimization
Phase 3 completed the formal verification of all standard Levenshtein operations (Match, Delete, Insert, Substitute) for both I-type (non-final) and M-type (final) positions, plus the skip-to-match optimization.
✅ All 8 Standard Operations Verified
✅ Skip-to-Match Optimization Verified
✅ Empirical Validation
Admitted or admit🔍 Critical Discovery (Finding F8)
Notation: I+offset#errors
Invariant:
-n ≤ offset ≤ n ∧ |offset| ≤ errors ∧ errors ≤ n
Semantics:
offset: Position relative to parameter Imatch_index = offset + n: Current position in wordNotation: M+offset#errors
Invariant:
-2n ≤ offset ≤ 0 ∧ errors ≥ (-offset - n) ∧ errors ≤ n
Semantics:
offset: Position relative to end of wordmatch_index = offset + 2n: Current position in word| Operation | Preconditions | Offset Change | Error Cost | Validated |
|---|---|---|---|---|
| Match | has_match(cv, offset + n) | 0 (diagonal) | 0 | ✅ |
| Delete | errors < n $\land$ offset > -n | -1 (left) | +1 | ✅ |
| Insert | errors < n | 0 (stay) | +1 | ✅ |
| Substitute | errors < n $\land$ $\lnot \text{has}_\text{match}(\text{cv}, \text{offset} + n)$ | 0 (diagonal) | +1 | ✅ |
| Operation | Preconditions | Offset Change | Error Cost | Validated |
|---|---|---|---|---|
| Match | has_match(cv, offset + 2n) $\land$ offset < 0 | +1 (toward 0) | 0 | ✅ |
| Delete | errors < n | 0 (no word left) | +1 | ✅ |
| Insert | errors < n $\land$ offset < 0 | +1 (toward 0) | +1 | ✅ |
| Substitute | errors < n $\land$ $\lnot \text{has}_\text{match}(\text{cv}, \text{offset} + 2n)$ $\land$ offset < 0 | +1 (toward 0) | +1 | ✅ |
Offset Semantics:
offset - 1)offset + 1)Preconditions:
offset > -n (boundary check)offset < 0 (can't exceed 0)Match Index Calculation:
match_index = offset + nmatch_index = offset + 2nInitial (Incorrect) Understanding:
offset → offset - 1 (backward)offset → offset - NCritical Discovery:
offset → offset - 1offset → offset + NResolution:
offset + skip_distance)Operation: When input character doesn't match current word position, scan forward to find next match.
Formula:
offset' = offset + distance (FORWARD scan)
errors' = errors + distance
Preconditions:
distance > 0\text{errors} + \text{distance} \le n$-n \le \text{offset} \le n$|\text{offset}| \le \text{errors}$Example:
Position: I+0#0, word="test", input='s'
Current: match_index = 0 + 1 = 1 → word[1] = 'e' (no match)
Next: word[2] = 's' ✓
Distance: 2 - 1 = 1
Result: offset' = 0 + 1 = 1, errors' = 0 + 1 = 1
Verify: match_index' = 1 + 1 = 2 → word[2] = 's' ✓
Operation: Same forward scan, but offset unchanged for M-type.
Formula:
offset' = offset (unchanged for M-type)
errors' = errors + distance
Preconditions:
distance > 0\text{errors} + \text{distance} \le n$-2n \le \text{offset} \le 0$|\text{offset}| \le \text{errors}$Rationale: M-type has already consumed the entire word, so skip-to-match only affects error budget, not position.
i_match_preserves_invariantTheorem i_match_preserves_invariant : forall p cv p',
i_invariant p ->
i_successor p OpMatch cv p' ->
i_invariant p'.
Status: ✅ Proven without admits
i_delete_preserves_invariantTheorem i_delete_preserves_invariant : forall p cv p',
i_invariant p ->
i_successor p OpDelete cv p' ->
i_invariant p'.
Status: ✅ Proven without admits
Key insight: Requires offset > -n precondition to ensure $\text{offset} - 1 \ge -n$
i_insert_preserves_invariantTheorem i_insert_preserves_invariant : forall p cv p',
i_invariant p ->
i_successor p OpInsert cv p' ->
i_invariant p'.
Status: ✅ Proven without admits
i_substitute_preserves_invariantTheorem i_substitute_preserves_invariant : forall p cv p',
i_invariant p ->
i_successor p OpSubstitute cv p' ->
i_invariant p'.
Status: ✅ Proven without admits
i_skip_to_match_preserves_invariantTheorem i_skip_to_match_preserves_invariant : forall p cv distance p',
i_invariant p ->
i_skip_to_match p distance cv p' ->
i_invariant p'.
Status: ✅ Proven without admits Note: Corrected after Finding F8 investigation
i_skip_to_match_formulaTheorem i_skip_to_match_formula : forall (offset : Z) (errors n distance : nat) cv p',
(distance > 0)%nat ->
(errors + distance <= n)%nat ->
i_skip_to_match (mkPosition VarINonFinal offset errors n None) distance cv p' ->
exists (offset' : Z) (errors' : nat),
p' = mkPosition VarINonFinal offset' errors' n None /\
offset' = offset + Z.of_nat distance /\
errors' = (errors + distance)%nat.
Status: ✅ Proven without admits
Key property: offset' = offset + distance (FORWARD scan)
m_match_preserves_invariantTheorem m_match_preserves_invariant : forall p cv p',
m_invariant p ->
m_successor p OpMatch cv p' ->
m_invariant p'.
Status: ✅ Proven without admits
m_delete_preserves_invariantTheorem m_delete_preserves_invariant : forall p cv p',
m_invariant p ->
m_successor p OpDelete cv p' ->
m_invariant p'.
Status: ✅ Proven without admits
m_insert_preserves_invariantTheorem m_insert_preserves_invariant : forall p cv p',
m_invariant p ->
m_successor p OpInsert cv p' ->
m_invariant p'.
Status: ✅ Proven without admits
m_substitute_preserves_invariantTheorem m_substitute_preserves_invariant : forall p cv p',
m_invariant p ->
m_successor p OpSubstitute cv p' ->
m_invariant p'.
Status: ✅ Proven without admits
m_skip_to_match_preserves_invariantTheorem m_skip_to_match_preserves_invariant : forall p cv distance p',
m_invariant p ->
m_skip_to_match p distance cv p' ->
m_invariant p'.
Status: ✅ Proven without admits Completed: 2025-11-17
m_skip_to_match_formulaTheorem m_skip_to_match_formula : forall (offset : Z) (errors n distance : nat) cv p',
(distance > 0)%nat ->
(errors + distance <= n)%nat ->
m_skip_to_match (mkPosition VarMFinal offset errors n None) distance cv p' ->
exists (errors' : nat),
p' = mkPosition VarMFinal offset errors' n None /\
errors' = (errors + distance)%nat.
Status: ✅ Proven without admits
Completed: 2025-11-17
Key property: offset' = offset (unchanged for M-type)
File: tests/proptest_skip_to_match.rs
i_skip_preserves_invariant
i_skip_formula
offset' = offset + distance, errors' = errors + distancem_skip_preserves_invariant
m_skip_formula
offset' = offset (unchanged), errors' = errors + distancei_skip_moves_forward
successor.offset() > offset after skip$ cargo test --test proptest_skip_to_match
running 5 tests
test i_skip_formula ... ok
test i_skip_moves_forward ... ok
test i_skip_preserves_invariant ... ok
test m_skip_formula ... ok
test m_skip_preserves_invariant ... ok
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured
See FINDINGS.md for detailed analysis. Summary:
offset_delta() methodsrc/transducer/generalized/state.rs:280-348src/transducer/generalized/state.rs:583-638src/transducer/generalized/state.rs:504-521src/transducer/generalized/position.rs:246-487rocq/liblevenshtein/Operations.vrocq/liblevenshtein/Core.v:117-122 (M-type), similar for I-typerocq/liblevenshtein/Transitions.v
rocq/liblevenshtein/proofs/tests/proptest_skip_to_match.rstests/test_*.rs (722 passing)| Coq Theorem | Rust Code | Match | Property Test | Status |
|---|---|---|---|---|
i_match_preserves_invariant | state.rs:280-295 | ✅ | ✅ tests/proptest_transitions.rs | Proven |
i_delete_preserves_invariant | state.rs:297-314 | ✅ | ✅ tests/proptest_transitions.rs | Proven |
i_insert_preserves_invariant | state.rs:315-329 | ✅ | ✅ tests/proptest_transitions.rs | Proven |
i_substitute_preserves_invariant | state.rs:330-348 | ✅ | ✅ tests/proptest_transitions.rs | Proven |
i_skip_to_match_preserves_invariant | state.rs:504-521 | ✅ | ✅ PASSING | Proven |
i_skip_to_match_formula | state.rs:504-521 | ✅ | ✅ PASSING | Proven |
m_match_preserves_invariant | state.rs:583-595 | ✅ | ✅ tests/proptest_transitions.rs | Proven |
m_delete_preserves_invariant | state.rs:596-610 | ✅ | ✅ tests/proptest_transitions.rs | Proven |
m_insert_preserves_invariant | state.rs:611-622 | ✅ | ✅ tests/proptest_transitions.rs | Proven |
m_substitute_preserves_invariant | state.rs:623-638 | ✅ | ✅ tests/proptest_transitions.rs | Proven |
m_skip_to_match_preserves_invariant | (M-type logic) | ✅ | ✅ PASSING | Proven |
m_skip_to_match_formula | (M-type logic) | ✅ | ✅ PASSING | Proven |
Finding F8 demonstrated that formal verification can find errors in the specification itself, not just the implementation. The Coq formalization incorrectly modeled skip-to-match, while the Rust code was correct.
When proofs fail, empirical testing is essential to determine ground truth. Testing with both versions (original and "fixed") definitively showed which was correct.
When tests fail after a "fix" to match the formal model, the spec might be wrong. Be willing to correct the formalization.
The relationship between offset, match_index, and word position is complex:
match_index = offset + nmatch_index = offset + 2noffset - 1offset + distanceThe 5 property-based tests provide continuous validation that the proven theorems hold empirically, catching any drift between formalization and implementation.
Investigate 3 failing tests:
test_phonetic_feature_X ... FAILED
test_phonetic_feature_Y ... FAILED
test_phonetic_feature_Z ... FAILED
Formalize phonetic operations in Coq
Prove correctness of phonetic operations
Fix bugs revealed by formal verification
Phase 3 successfully verified all 8 standard operations and the skip-to-match optimization for both I-type and M-type positions. The investigation into Finding F8 revealed a critical specification error in the Coq formalization, demonstrating the value of combining formal methods with empirical testing.
Key Result: All standard operations are mathematically correct. The Rust implementation is sound, and all proven theorems have been validated with property-based tests.
Total Theorems Proven: 12 (all without admits) Total Property Tests: 5 (all passing) Integration Tests: 722/725 passing (3 phonetic failures for Phase 4)
The formal verification effort has provided strong confidence in the correctness of the standard Levenshtein automaton implementation.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |