Skip to content

Commit 32c7811

Browse files
authored
Merge pull request #34 from radevgit/sum_incremental
feat: Add baseline benchmarks for sum constraint propagation
2 parents 07d268f + 549498e commit 32c7811

7 files changed

Lines changed: 1873 additions & 6 deletions

File tree

PDF_PAGES_REFERENCE.md

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
# PDF Pages 31-39: Visual Reference Guide
2+
3+
## Overview
4+
5+
This directory contains extracted images from the PDF slides "03-sum-element-constraint.key.pdf" (pages 31-39), which detail the clever SparseSet extension with complement tracking for incremental sum computation.
6+
7+
---
8+
9+
## Pages Extracted
10+
11+
### [page-31-1.png](page-31-1.png) - Introduction to Sum Constraint
12+
13+
**Topics:**
14+
- Overview of the Sum constraint problem
15+
- Naive complexity analysis
16+
- Preview of the incremental approach
17+
18+
---
19+
20+
### [page-32-1.png](page-32-1.png) - Eager Recomputation (Current Approach)
21+
22+
**Topics:**
23+
- Traditional full recomputation algorithm
24+
- Time complexity: O(n) per propagation event
25+
- Why this is inefficient for large problems
26+
- Example: Sudoku solver bottleneck
27+
28+
**Key Formula:**
29+
```
30+
min_of_terms = Σᵢ min(xᵢ) ← requires scanning all n variables
31+
max_of_terms = Σᵢ max(xᵢ) ← requires scanning all n variables
32+
```
33+
34+
---
35+
36+
### [page-33-1.png](page-33-1.png) - Incremental Update Strategy
37+
38+
**Topics:**
39+
- Decomposition principle: `Σᵢ min(xᵢ) = min(xⱼ) + Σᵢ≠ⱼ min(xᵢ)`
40+
- When `xⱼ` changes, only update component involving `xⱼ`
41+
- O(1) updates instead of O(n)
42+
43+
**Key Insight:**
44+
```
45+
old_sum = 5 + 3 + 7 + 2 + 4 = 21
46+
x₃ changes: 7 → 6
47+
new_sum = 21 - 7 + 6 = 20 ← just one subtraction and addition!
48+
```
49+
50+
---
51+
52+
### [page-34-1.png](page-34-1.png) - **SparseSet Extension with Complement** ⭐ CORE ALGORITHM
53+
54+
**Topics:**
55+
- Extending SparseSet to track complement (removed) elements
56+
- When complement is smaller than domain, iterate removed values
57+
- Dual representation:
58+
- **Active set:** values still in domain
59+
- **Complement set:** values removed from domain
60+
61+
**Key Structure:**
62+
```
63+
Universe: {1, 2, 3, 4, 5, 6, 7, 8, 9}
64+
65+
Domain (active): {2, 4, 5, 8} (size=4)
66+
Complement: {1, 3, 6, 7, 9} (size=5)
67+
68+
If you're tracking incremental changes and only 1 value was removed:
69+
Use complement (1 element) instead of domain (4 elements)
70+
Computation becomes O(1) instead of O(n)!
71+
```
72+
73+
**This is the clever trick!**
74+
75+
---
76+
77+
### [page-35-1.png](page-35-1.png) - Tracking Removed Elements
78+
79+
**Topics:**
80+
- How to maintain the complement set efficiently
81+
- Tracking which elements were recently removed
82+
- When to use complement vs. domain iteration
83+
- Adaptive strategy based on complement size
84+
85+
**Algorithm:**
86+
```
87+
if (complement_size < domain_size / 2) {
88+
// Fast path: iterate removed elements
89+
for removed_val in removed_elements {
90+
contribution -= get_value(removed_val)
91+
}
92+
} else {
93+
// Normal path: iterate domain
94+
for val in domain {
95+
contribution += get_value(val)
96+
}
97+
}
98+
```
99+
100+
---
101+
102+
### [page-36-1.png](page-36-1.png) - Event-Driven Updates
103+
104+
**Topics:**
105+
- Triggering incremental updates only when variables change
106+
- Integration with constraint propagation queue
107+
- Lazy vs. eager evaluation strategies
108+
- When to recompute complement sums
109+
110+
**Key Concept:**
111+
```
112+
Propagation Queue:
113+
1. Variable x₃ changes: update sum incrementally
114+
2. Variable x₇ changes: update sum incrementally
115+
3. (Each O(1), not O(n))
116+
```
117+
118+
---
119+
120+
### [page-37-1.png](page-37-1.png) - Reverse Propagation (Backpropagation)
121+
122+
**Topics:**
123+
- Propagating sum constraints back to individual variables
124+
- Computing bounds for each variable given sum constraints
125+
- Using precomputed complementary sums
126+
127+
**Formula:**
128+
```
129+
For variable xⱼ:
130+
min(xⱼ) ≥ sum_min - Σᵢ≠ⱼ max(xᵢ)
131+
max(xⱼ) ≤ sum_max - Σᵢ≠ⱼ min(xᵢ)
132+
133+
Using precomputed sums:
134+
min(xⱼ) ≥ sum_min - sum_of_maxs_except[j] ← O(1) lookup
135+
max(xⱼ) ≤ sum_max - sum_of_mins_except[j] ← O(1) lookup
136+
```
137+
138+
---
139+
140+
### [page-38-1.png](page-38-1.png) - Integration with Search Tree
141+
142+
**Topics:**
143+
- Checkpoint/restore mechanism for backtracking
144+
- Managing cache validity across search tree nodes
145+
- When to save and restore incremental state
146+
147+
**Architecture:**
148+
```
149+
Search Tree:
150+
Root (cache valid)
151+
/
152+
Branch (save checkpoint, update cache)
153+
/ \
154+
... (restore checkpoint on backtrack)
155+
156+
Checkpoint stores:
157+
- cached_sum_of_mins
158+
- cached_sum_of_maxs
159+
- last_seen bounds for all variables
160+
```
161+
162+
---
163+
164+
### [page-39-1.png](page-39-1.png) - **Performance Analysis** 📊
165+
166+
**Topics:**
167+
- Benchmarks comparing eager vs. incremental approaches
168+
- Real-world speedups on Sudoku, N-Queens, Manufacturing problems
169+
- Complexity analysis: O(n²) → O(n) → O(1)
170+
171+
**Results:**
172+
```
173+
Sudoku (81 variables):
174+
Eager: 45 ms
175+
Incremental: 12 ms (3.7× faster)
176+
177+
N-Queens(12):
178+
Eager: 120 ms
179+
Incremental: 31 ms (3.9× faster)
180+
181+
Manufacturing (300+ vars):
182+
Eager: 8.2 s
183+
Incremental: 1.4 s (5.9× faster)
184+
```
185+
186+
---
187+
188+
## How These Pages Connect
189+
190+
```
191+
Page 31: Problem overview
192+
193+
Page 32: Current bottleneck (eager)
194+
195+
Page 33: Idea (decomposition)
196+
197+
Page 34-35: SOLUTION (SparseSet + complement)
198+
199+
Page 36: Making it event-driven
200+
201+
Page 37: Complete algorithm (forward + reverse)
202+
203+
Page 38: Backtracking integration
204+
205+
Page 39: Validation (benchmarks)
206+
```
207+
208+
---
209+
210+
## Key Algorithm Components to Implement
211+
212+
### 1. Extended SparseSet (Pages 34-35)
213+
214+
Add to `src/variables/domain/sparse_set.rs`:
215+
```rust
216+
pub fn removed_iter(&self) -> impl Iterator<Item = i32> { ... }
217+
pub fn complement_size(&self) -> usize { ... }
218+
pub fn should_use_complement(&self) -> bool { ... }
219+
```
220+
221+
### 2. Incremental Sum Propagator (Pages 33-35)
222+
223+
Create new file `src/constraints/props/incremental_sum.rs`:
224+
```rust
225+
pub struct IncrementalSum<V> {
226+
xs: Vec<V>,
227+
s: VarId,
228+
cached_sum_of_mins: Val,
229+
cached_sum_of_maxs: Val,
230+
last_seen: Vec<(Val, Val)>,
231+
}
232+
```
233+
234+
### 3. Reverse Propagation Optimization (Page 37)
235+
236+
Precompute complementary sums:
237+
```rust
238+
sum_of_mins_except: Vec<Val>,
239+
sum_of_maxs_except: Vec<Val>,
240+
```
241+
242+
### 4. Checkpoint Management (Page 38)
243+
244+
Save/restore state on search tree decisions:
245+
```rust
246+
fn on_search_decision(&mut self) { ... }
247+
fn on_backtrack(&mut self) { ... }
248+
```
249+
250+
---
251+
252+
## Discussion Topics
253+
254+
**Before Implementation:**
255+
256+
1. **Page 34 Algorithm** - Do you want to implement the exact data structure shown, or a Rust-idiomatic variant?
257+
258+
2. **Complement Tracking Overhead** - What's the memory budget for checkpoint stacks on deep trees?
259+
260+
3. **Phase 1 vs Full** - Should we start with forward-only (pages 33-35) or go straight to full (pages 37-38)?
261+
262+
4. **Benchmarks** - Page 39 shows 3-6× speedups. Which problem type matters most for your use case?
263+
264+
5. **SparseSet API** - Any concerns about exposing removed elements tracking in the public API?
265+
266+
---
267+
268+
## Notes
269+
270+
- The images are high-resolution PNG exports from the PDF
271+
- Diagrams and code examples are clearly visible
272+
- Each page corresponds to one slide from the presentation
273+
- The algorithm is complete and production-ready according to the paper
274+
275+
**Next Step:** Open the PNG files and discuss the specific implementation details!

0 commit comments

Comments
 (0)