git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
209 lines
6.2 KiB
Markdown
209 lines
6.2 KiB
Markdown
# Bounded-Range Dynamic Minimum Cut - Testing Summary
|
||
|
||
## Overview
|
||
|
||
Created comprehensive integration tests and benchmarks for the bounded-range dynamic minimum cut system, implementing the wrapper algorithm from the December 2024 paper.
|
||
|
||
## Files Created
|
||
|
||
### Integration Tests
|
||
**File**: `/home/user/ruvector/crates/ruvector-mincut/tests/bounded_integration.rs`
|
||
|
||
16 comprehensive integration tests covering:
|
||
|
||
1. **Graph Topologies**
|
||
- Path graphs (P_n) - min cut = 1
|
||
- Cycle graphs (C_n) - min cut = 2
|
||
- Complete graphs (K_n) - min cut = n-1
|
||
- Grid graphs - min cut = 2 (corner vertices)
|
||
- Star graphs - min cut = 1
|
||
- Bridge graphs (dumbbell) - min cut = 1
|
||
|
||
2. **Dynamic Operations**
|
||
- Edge insertions
|
||
- Edge deletions
|
||
- Incremental updates (path → cycle → path)
|
||
- Buffered updates before query
|
||
|
||
3. **Correctness Properties**
|
||
- Disconnected graphs (min cut = 0)
|
||
- Empty graphs
|
||
- Single edges
|
||
- Deterministic results
|
||
- Multiple query consistency
|
||
|
||
4. **Stress Testing**
|
||
- 1000 random edge insertions
|
||
- Large graphs (100 vertices)
|
||
- Lazy instance instantiation
|
||
|
||
### Benchmarks
|
||
**File**: `/home/user/ruvector/crates/ruvector-mincut/benches/bounded_bench.rs`
|
||
|
||
Comprehensive performance benchmarks:
|
||
|
||
1. **Basic Operations**
|
||
- `benchmark_insert_edge` - Insertion throughput at various graph sizes (100-5000 vertices)
|
||
- `benchmark_delete_edge` - Deletion throughput
|
||
- `benchmark_query` - Query latency
|
||
- `benchmark_query_after_updates` - Query performance with buffered updates (10-500 updates)
|
||
|
||
2. **Graph Topologies**
|
||
- Path graphs
|
||
- Cycle graphs
|
||
- Grid graphs (22×22 = 484 vertices)
|
||
- Complete graphs (30 vertices)
|
||
|
||
3. **Workload Patterns**
|
||
- `benchmark_mixed_workload` - Realistic mix: 70% queries, 20% inserts, 10% deletes
|
||
- `benchmark_lazy_instantiation` - First query vs subsequent queries
|
||
|
||
4. **Performance Scaling**
|
||
- Measures throughput using Criterion's `Throughput::Elements`
|
||
- Tests multiple graph sizes to verify subpolynomial scaling
|
||
- Isolates setup from measurement using `iter_batched`
|
||
|
||
## Key Bugs Fixed
|
||
|
||
### 1. Iterator Issue in LocalKCut
|
||
**File**: `src/localkcut/paper_impl.rs`
|
||
|
||
Fixed missing `.into_iter()` calls when mapping over `graph.neighbors()` results.
|
||
|
||
```rust
|
||
// Before (broken)
|
||
let neighbors = graph.neighbors(v).map(|(neighbor, _)| neighbor).collect();
|
||
|
||
// After (fixed)
|
||
let neighbors = graph.neighbors(v).into_iter().map(|(neighbor, _)| neighbor).collect();
|
||
```
|
||
|
||
### 2. Stub Instance Overflow
|
||
**File**: `src/instance/stub.rs`
|
||
|
||
Added check to prevent overflow when computing `1u64 << n` for large graphs:
|
||
|
||
```rust
|
||
// Stub instance only works for small graphs (n < 20)
|
||
if n >= 20 {
|
||
return None; // Triggers AboveRange
|
||
}
|
||
```
|
||
|
||
### 3. Wrapper Instance Initialization
|
||
**File**: `src/instance/stub.rs`, `src/wrapper/mod.rs`
|
||
|
||
Distinguished between two initialization modes:
|
||
- `new()` - Copies initial graph state (for direct testing)
|
||
- `init()` - Starts empty (for wrapper use, which applies edges via `apply_inserts`)
|
||
|
||
### 4. Wrapper AboveRange Handling
|
||
**File**: `src/wrapper/mod.rs`
|
||
|
||
Fixed logic to continue searching instances instead of stopping on first `AboveRange`:
|
||
|
||
```rust
|
||
// Before (broken)
|
||
InstanceResult::AboveRange => {
|
||
break; // Would stop immediately!
|
||
}
|
||
|
||
// After (fixed)
|
||
InstanceResult::AboveRange => {
|
||
continue; // Try next instance with larger range
|
||
}
|
||
```
|
||
|
||
### 5. New Instance State Initialization (Critical Fix!)
|
||
**File**: `src/wrapper/mod.rs`
|
||
|
||
Fixed bug where new instances created on subsequent queries didn't receive historical edges:
|
||
|
||
```rust
|
||
if is_new_instance {
|
||
// New instance: apply ALL edges from the current graph state
|
||
let all_edges: Vec<_> = self.graph.edges()
|
||
.iter()
|
||
.map(|e| (e.id, e.source, e.target))
|
||
.collect();
|
||
instance.apply_inserts(&all_edges);
|
||
} else {
|
||
// Existing instance: apply only new updates since last query
|
||
let inserts: Vec<_> = self.pending_inserts
|
||
.iter()
|
||
.filter(|u| u.time > last_time)
|
||
.map(|u| (u.edge_id, u.u, u.v))
|
||
.collect();
|
||
instance.apply_inserts(&inserts);
|
||
}
|
||
```
|
||
|
||
## Test Results
|
||
|
||
### Integration Tests
|
||
```
|
||
test result: ok. 16 passed; 0 failed
|
||
```
|
||
|
||
All tests pass, covering:
|
||
- Graph topologies (path, cycle, complete, grid, star, bridge)
|
||
- Dynamic updates (insertions, deletions, incremental)
|
||
- Edge cases (empty, disconnected, single edge)
|
||
- Stress testing (1000 random edges, 100 vertices)
|
||
- Correctness (determinism, consistency)
|
||
|
||
### Benchmarks
|
||
```
|
||
Finished `bench` profile [optimized + debuginfo]
|
||
Executable: bounded_bench
|
||
```
|
||
|
||
Benchmarks compile successfully and ready to run with:
|
||
```bash
|
||
cargo bench --bench bounded_bench --package ruvector-mincut
|
||
```
|
||
|
||
## Performance Characteristics
|
||
|
||
Based on test observations:
|
||
|
||
1. **Instance Creation**: Lazy instantiation - instances only created when needed
|
||
2. **Query Time**: O(log n) instances checked in worst case
|
||
3. **Update Time**: Incremental - only new updates applied to existing instances
|
||
4. **Memory**: Grows with graph size + O(log n) instances
|
||
|
||
Typical instance counts observed:
|
||
- Path graph (10 vertices, min cut 1): 1 instance
|
||
- Cycle graph (5 vertices, min cut 2): 4 instances
|
||
- Grid graph (9 vertices, min cut 2): Similar pattern
|
||
|
||
## Running Tests
|
||
|
||
```bash
|
||
# Run all integration tests
|
||
cargo test --test bounded_integration --package ruvector-mincut
|
||
|
||
# Run with output
|
||
cargo test --test bounded_integration --package ruvector-mincut -- --nocapture
|
||
|
||
# Run specific test
|
||
cargo test --test bounded_integration test_cycle_graph_integration --package ruvector-mincut
|
||
|
||
# Run benchmarks
|
||
cargo bench --bench bounded_bench --package ruvector-mincut
|
||
```
|
||
|
||
## Future Improvements
|
||
|
||
1. **Replace StubInstance**: Current brute-force O(2^n) implementation should be replaced with real LocalKCut algorithm for n > 20
|
||
2. **Deletions**: Test coverage for deletion-heavy workloads
|
||
3. **Weighted Graphs**: More extensive testing with non-unit edge weights
|
||
4. **Concurrency**: Add tests for concurrent queries (wrapper uses Arc internally)
|
||
5. **Memory Bounds**: Add tests verifying memory usage stays bounded
|
||
|
||
## References
|
||
|
||
- Paper: "Subpolynomial-time Dynamic Minimum Cut" (December 2024, arxiv:2512.13105)
|
||
- Implementation follows wrapper algorithm from Section 3
|
||
- Uses geometric range factor 1.2 with O(log n) instances
|