Would it be better to use the parameterized transducer's unsubsumption method (SmallVec with online subsumption) for Universal transducers instead of the current BTreeSet with error-based early termination?
✅ Both approaches implemented and tested
master - BTreeSet with Error-Based Early TerminationData Structure:
pub struct UniversalState<V: PositionVariant> {
positions: BTreeSet<UniversalPosition<V>>,
max_distance: u8,
}
Key Features:
Ord implementation sorts by (errors, offset)take_while() based on error counterrors ≤ new.errors skip subsumption checkerrors < new.errors checked via take_while()add_position() Logic:
pub fn add_position(&mut self, pos: UniversalPosition<V>) {
let pos_errors = pos.errors();
// Step 1: Remove subsumed (with early termination)
self.positions.retain(|p| {
if p.errors() <= pos_errors {
true // Cannot be subsumed
} else {
!subsumes(&pos, p, self.max_distance)
}
});
// Step 2: Check if subsumed (with early termination)
let is_subsumed = self.positions.iter()
.take_while(|p| p.errors() < pos_errors) // Early exit!
.any(|p| subsumes(p, &pos, self.max_distance));
if !is_subsumed {
self.positions.insert(pos); // O(log n)
}
}
Test Results: All 473 tests passing ✓
experiment/universal-smallvec - SmallVec with Online SubsumptionData Structure:
pub struct UniversalState<V: PositionVariant> {
positions: SmallVec<[UniversalPosition<V>; 8]>,
max_distance: u8,
}
Key Features:
add_position() Logic:
pub fn add_position(&mut self, pos: UniversalPosition<V>) {
// Check if subsumed by existing
for existing in &self.positions {
if subsumes(existing, &pos, self.max_distance) {
return; // Early exit if subsumed
}
}
// Remove subsumed positions
self.positions
.retain(|p| !subsumes(&pos, p, self.max_distance));
// Insert in sorted position
let insert_pos = self.positions
.binary_search(&pos)
.unwrap_or_else(|pos| pos);
self.positions.insert(insert_pos, pos); // O(n)
}
Test Results: All 473 tests passing ✓
# Benchmark BTreeSet (master)
git checkout master
RUSTFLAGS="-C target-cpu=native" cargo bench --bench universal_state_comparison 2>&1 | tee /tmp/btreeset_results.txt
# Benchmark SmallVec (experiment)
git checkout experiment/universal-smallvec
RUSTFLAGS="-C target-cpu=native" cargo bench --bench universal_state_comparison 2>&1 | tee /tmp/smallvec_results.txt
# Return to master
git checkout master
# Compare results
diff /tmp/btreeset_results.txt /tmp/smallvec_results.txt
chmod +x scripts/benchmark_universal_approaches.sh
./scripts/benchmark_universal_approaches.sh
This will:
/tmp/Error-based early termination
take_while(|p| p.errors() < pos_errors) skips positionsAutomatic sorting maintenance
Better for varied state sizes
Stack allocation (≤8 positions)
Excellent cache locality
Simpler code
Ord implementation neededProven for parameterized
| Operation | BTreeSet | SmallVec |
|---|---|---|
| add_position (best) | O(log n) | O(1) if subsumed immediately |
| add_position (avg) | O(k log n) where k << n | O(k*n) where k << n |
| add_position (worst) | O(n log n) | O(n²) |
| Memory (≤8 pos) | Heap | Stack |
| Memory (>8 pos) | Heap + tree nodes | Heap + Vec |
| Cache locality | Good (tree) | Excellent (contiguous) |
When analyzing benchmark results, focus on:
take_while() skip positions?Based on the analysis:
If Universal states are typically small (≤8 positions):
If Universal states are larger or vary significantly:
The benchmark will empirically determine which assumption holds.
BTreeSet (master):
src/transducer/universal/state.rs: BTreeSet + error-based early terminationsrc/transducer/universal/position.rs: Custom Ord for (errors, offset) sortingSmallVec (experiment):
src/transducer/universal/state.rs: SmallVec + online subsumptionbenches/universal_state_comparison.rs (present in master, to be created if needed):
docs/optimization/SUBSUMPTION_OPTIMIZATION_REPORT.mddocs/research/universal-levenshtein/SUBSUMPTION_COMPARISON_JAVA_VS_RUST.mddocs/research/universal-levenshtein/SUBSUMPTION_OPTIMIZATION.mddocs/research/universal-levenshtein/PARAMETERIZED_SUBSUMPTION_DECISION.mdBoth implementations are:
The benchmark comparison will provide empirical data to make the final decision.
Current Status: Awaiting benchmark execution and analysis.
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 |