This document summarizes the complete analysis of subsumption optimizations across both Universal and Parameterized Levenshtein transducers, including implementation, validation, and comparison with the Java liblevenshtein implementation.
Status: ✅ Complete and Validated
Objective: Implement error-first sorting for Universal transducers to enable early termination during subsumption checks.
Implementation:
HashSet<UniversalPosition> → BTreeSet<UniversalPosition>Ord implementation sorting by (errors, offset)take_while() for error-based early termination\mathcal{O}(n)$ to $\mathcal{O}(k)$ where k << nFiles Modified:
src/transducer/universal/state.rs: State with BTreeSetsrc/transducer/universal/position.rs: Custom Ord implementationChallenge: 10 tests failed after BTreeSet implementation
Root Causes Identified:
Fixes Applied:
"abc" to "$abc" windowed bit vectorserrors + (distance-1) to errors + distanceResult: All 473 tests passing ✓
Documentation:
docs/research/universal-levenshtein/SUBSUMPTION_BTREESET_TEST_FIXES.mdObjective: Compare Rust BTreeSet approach with Java's linked list + merge sort approach.
Java Analysis:
\mathcal{O}(n \log n)$ merge sortRust Analysis:
\mathcal{O}(\log n)$ per insert)take_while() iterator combinatorConclusion: Functionally equivalent with different trade-offs
\mathcal{O}(k)$ subsumption checks with early termination (k << n)Documentation:
docs/research/universal-levenshtein/SUBSUMPTION_COMPARISON_JAVA_VS_RUST.mdQuestion: Should parameterized transducers adopt the same BTreeSet optimization?
Analysis:
\le 8$ positions (stack-allocated)Conclusion: No optimization needed
Documentation:
docs/research/universal-levenshtein/PARAMETERIZED_SUBSUMPTION_DECISION.mdAlgorithm: Mitankin (2005) Universal Levenshtein Automaton Positions: Relative offsets (I+k, M+k with errors) State Structure: BTreeSet with custom Ord
Optimization Benefits:
(errors, offset) automaticallytake_while() on error count\mathcal{O}(k)$ subsumption checks where k << nTrade-offs:
\mathcal{O}(\log n)$ insert vs $\mathcal{O}(1)$ for HashSetVerdict: Optimal for Universal transducers
Algorithm: Classic parameterized Levenshtein automaton Positions: Absolute indices (term_index, num_errors, is_special) State Structure: SmallVec<[Position; 8]>
Current Benefits:
\le 8$ positions)\mathcal{O}(\text{kn})$ average complexity with early exitAlternative (BTreeSet) Downsides:
Verdict: Keep SmallVec (already optimal)
Before optimization (HashSet):
\mathcal{O}(n)$ subsumption checks for all n positionsAfter optimization (BTreeSet):
\mathcal{O}(k)$ subsumption checks where k << nk \approx n/3$ for sparse error distributionsExpected improvement: ~3x fewer subsumption checks
Current implementation (SmallVec, from 2025-10-29 benchmarks):
| Position Count | Time | Throughput |
|---|---|---|
| n=10 | ~360ns | - |
| n=50 | ~1.7µs | 29.24 Melem/s |
| n=100 | ~2.6µs | - |
| n=200 | ~4.3µs | - |
vs Batch unsubsumption: 3.3x faster
vs BTreeSet: No improvement expected (same $\mathcal{O}(\text{kn})$, worse constants)
Universal Transducers:
Parameterized Transducers:
Java vs Rust (Universal):
\lvert j - i\rvert \le (f - e)$\mathcal{O}(k)$ subsumption checksUniversal vs Parameterized (Rust):
Universal transducers benefit from BTreeSet because:
take_while() on errors eliminates large swaths of comparisonsParameterized transducers benefit from SmallVec because:
\le 8$ positions)The right data structure depends on:
Universal BTreeSet Optimization
docs/research/universal-levenshtein/SUBSUMPTION_OPTIMIZATION.mddocs/research/universal-levenshtein/SUBSUMPTION_BTREESET_TEST_FIXES.mdJava Comparison
docs/research/universal-levenshtein/SUBSUMPTION_COMPARISON_JAVA_VS_RUST.mdParameterized Decision
docs/research/universal-levenshtein/PARAMETERIZED_SUBSUMPTION_DECISION.mdThis Document
docs/research/universal-levenshtein/SUBSUMPTION_OPTIMIZATION_COMPLETE.mdPhase 4 Bug Fix (substitution handling)
docs/research/universal-levenshtein/PHASE4_BUG_FIX_SUMMARY.mddocs/research/universal-levenshtein/PHASE4_TRACE_ANALYSIS.mdOriginal Subsumption Analysis (2025-10-29)
docs/optimization/SUBSUMPTION_OPTIMIZATION_REPORT.mddocs/optimization/SUBSUMPTION_BENCHMARK_RESULTS.mdState management:
src/transducer/universal/state.rs:83-119 - BTreeSet structuresrc/transducer/universal/state.rs:155-182 - add_position() with early terminationPosition ordering:
src/transducer/universal/position.rs:207-228 - Custom Ord implementationSubsumption logic:
src/transducer/universal/subsumption.rs:113-142 - subsumes() functionTests:
src/transducer/universal/position.rs:848-963 - Position successor testssrc/transducer/universal/state.rs:454-803 - State subsumption teststests/universal_vs_parameterized.rs - Cross-validation testsState management:
src/transducer/state.rs:18-23 - SmallVec structuresrc/transducer/state.rs:82-100 - insert() with online subsumptionPosition subsumption:
src/transducer/position.rs:82-116 - subsumes() by algorithmBenchmarks:
benches/subsumption_benchmarks.rs - Online vs batch comparisonBatch unsubsumption mode (Universal transducers)
Hybrid approach (Parameterized transducers)
n \le 8$ (current)The subsumption optimization work is complete and validated:
✅ Universal transducers use BTreeSet with error-first sorting
\mathcal{O}(k)$ subsumption checks (k << n)✅ Parameterized transducers use SmallVec with online subsumption
✅ Comprehensive documentation created
Both implementations are correct, optimal, and well-documented.
This work builds on:
Stoyan Mihov and Klaus U. Schulz (2004). "Fast approximate search in large dictionaries". Computational Linguistics 30(4):451-477.
Petar Nikolaev Mitankin (2005). "Universal Levenshtein Automata". Master's thesis, University of Sofia.
Klaus Schulz and Stoyan Mihov (2002). "Fast string correction with Levenshtein automata". International Journal on Document Analysis and Recognition 5(1):67-85.
liblevenshtein-java: https://github.com/vinary-tree/liblevenshtein-java
liblevenshtein-rust: https://github.com/vinary-tree/liblevenshtein-rust (current project)
Subsumption Report (2025-10-29)
docs/optimization/SUBSUMPTION_OPTIMIZATION_REPORT.mdCurrent Analysis (2025-11-11)
End of Document
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 |