Skip to content

Commit 156ab6d

Browse files
authored
Merge pull request #22 from radevgit/zinc_phase2
Zinc phase2
2 parents 8448598 + 0698a1c commit 156ab6d

File tree

12 files changed

+2386
-2
lines changed

12 files changed

+2386
-2
lines changed

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [Unreleased]
6+
### Added
7+
- Reified comparison constraints for FlatZinc Phase 2 integration:
8+
- `int_lt_reif(x, y, b)` - Reified less-than: b ⇔ (x < y)
9+
- `int_le_reif(x, y, b)` - Reified less-or-equal: b ⇔ (x ≤ y)
10+
- `int_gt_reif(x, y, b)` - Reified greater-than: b ⇔ (x > y)
11+
- `int_ge_reif(x, y, b)` - Reified greater-or-equal: b ⇔ (x ≥ y)
12+
- Comprehensive integration tests for Phase 2 constraints (12 tests, all passing)
13+
- Example demonstrating reification comparison constraints with 5 use cases
14+
- Documentation: `docs/development/PHASE2_REIFICATION_COMPARISON.md`
15+
16+
### Fixed
17+
- Critical bug: Reification constraints were being removed during constraint optimization
18+
- Added all reification constraint types to `constraint_types_to_optimize` list
19+
- Ensures reification propagators are preserved during `prepare_for_search()`
20+
21+
## [0.8.6] - 2025-10-01
22+
- Linear Constraint Helpers
23+
524
## [0.8.5] - 2025-10-01
625
- Implemented reified constraint.
726

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# Phase 2 FlatZinc Integration - Reification Comparison Constraints
2+
3+
## Summary
4+
5+
This document describes the implementation of reified comparison constraints for FlatZinc Phase 2 integration.
6+
7+
## Implementation Date
8+
9+
October 1, 2025
10+
11+
## What Was Implemented
12+
13+
### New Reification Constraints
14+
15+
Added four new reified comparison constraints to complement the existing `int_eq_reif` and `int_ne_reif`:
16+
17+
1. **`int_lt_reif(x, y, b)`** - Reified less-than: `b ⇔ (x < y)`
18+
2. **`int_le_reif(x, y, b)`** - Reified less-or-equal: `b ⇔ (x ≤ y)`
19+
3. **`int_gt_reif(x, y, b)`** - Reified greater-than: `b ⇔ (x > y)`
20+
4. **`int_ge_reif(x, y, b)`** - Reified greater-or-equal: `b ⇔ (x ≥ y)`
21+
22+
### How Reification Works
23+
24+
A reified constraint `b ⇔ C` creates a bidirectional implication:
25+
- If `b = 1` (true), then constraint `C` must hold
26+
- If `b = 0` (false), then constraint `C` must NOT hold
27+
- If `C` holds, then `b` must be 1
28+
- If `C` does not hold, then `b` must be 0
29+
30+
### Propagation Strategy
31+
32+
All reification propagators use **bidirectional propagation** without early returns:
33+
34+
**Direction 1: From variable domains → infer boolean**
35+
- If the constraint is definitely true (domains force it), set `b = 1`
36+
- If the constraint is definitely false (domains prevent it), set `b = 0`
37+
38+
**Direction 2: From boolean → enforce constraint**
39+
- If `b = 1`, enforce the constraint on the variables
40+
- If `b = 0`, enforce the negation of the constraint
41+
42+
This design ensures:
43+
- No dependency on constraint posting order
44+
- Deterministic behavior regardless of propagation scheduling
45+
- Proper interaction with other constraints (bool_clause, linear, etc.)
46+
47+
## Files Modified
48+
49+
### Core Implementation
50+
- `src/constraints/props/reification.rs` - Added 4 new propagator structs
51+
- `src/constraints/props/mod.rs` - Added registration methods and fixed optimization bug
52+
- `src/model/constraints.rs` - Added 4 public API methods
53+
- `src/optimization/constraint_metadata.rs` - Added 4 new constraint types
54+
55+
### Testing
56+
- `tests/test_phase2_integration.rs` - Created 12 comprehensive integration tests
57+
- All tests passing ✅
58+
59+
### Documentation
60+
- `examples/reification_comparison.rs` - Comprehensive examples demonstrating:
61+
- Conditional constraints (if-then-else logic)
62+
- Maximum computation using reification
63+
- Counting constraints
64+
- Partial ordering constraints
65+
- Range membership checking
66+
67+
## Critical Bug Fix
68+
69+
### Problem
70+
The `optimize_universal_constraint_order()` function was removing reification constraints during optimization because they weren't in the `constraint_types_to_optimize` list. This caused reification propagators to be deleted before search started.
71+
72+
### Symptoms
73+
- Reification propagators were created successfully
74+
- They were registered in the dependency graph
75+
- But they disappeared during `prepare_for_search()`
76+
- Tests failed with incorrect solutions
77+
- No errors or warnings were generated
78+
79+
### Root Cause
80+
The optimization function clears `self.state` and rebuilds it with only the constraints in the optimization list:
81+
82+
```rust
83+
// Reorder propagators based on priority
84+
self.state.clear(); // ← CLEARS ALL PROPAGATORS
85+
for &(prop_idx, _, _) in &constraint_priorities {
86+
self.state.push(original_state[prop_idx].clone()); // ← Only adds optimized ones!
87+
}
88+
```
89+
90+
### Solution
91+
Added all 6 reification constraint types to the `constraint_types_to_optimize` array:
92+
93+
```rust
94+
let constraint_types_to_optimize = [
95+
// ... existing types ...
96+
ConstraintType::EqualityReified,
97+
ConstraintType::InequalityReified,
98+
ConstraintType::LessThanReified,
99+
ConstraintType::LessEqualReified,
100+
ConstraintType::GreaterThanReified,
101+
ConstraintType::GreaterEqualReified,
102+
];
103+
```
104+
105+
## Test Results
106+
107+
All 12 integration tests pass consistently:
108+
109+
```
110+
running 12 tests
111+
test test_cnf_with_linear_le ... ok
112+
test test_cnf_with_linear_eq ... ok
113+
test test_multiple_linear_with_clause ... ok
114+
test test_reified_linear_eq ... ok
115+
test test_linear_eq_with_bool_clause ... ok
116+
test test_3sat_with_linear ... ok
117+
test test_reified_ne_with_clause ... ok
118+
test test_negative_coeff_with_clause ... ok
119+
test test_linear_le_with_bool_clause ... ok
120+
test test_chained_reifications ... ok
121+
test test_reified_linear_eq_false ... ok
122+
test test_large_integration ... ok
123+
124+
test result: ok. 12 passed; 0 failed
125+
```
126+
127+
## Usage Examples
128+
129+
### Basic Reification
130+
131+
```rust
132+
use selen::prelude::*;
133+
134+
let mut m = Model::default();
135+
let x = m.int(0, 10);
136+
let y = m.int(0, 10);
137+
let b = m.bool();
138+
139+
// b will be 1 iff x < y
140+
m.int_lt_reif(x, y, b);
141+
```
142+
143+
### Conditional Constraints
144+
145+
```rust
146+
// If x > 5, then y = 10, else y = 0
147+
let x = m.int(0, 10);
148+
let y = m.int(0, 10);
149+
let b = m.bool();
150+
let five = m.int(5, 5);
151+
152+
m.int_gt_reif(x, five, b);
153+
154+
let y_eq_10 = m.bool();
155+
let y_eq_0 = m.bool();
156+
let ten = m.int(10, 10);
157+
let zero = m.int(0, 0);
158+
159+
m.int_eq_reif(y, ten, y_eq_10);
160+
m.int_eq_reif(y, zero, y_eq_0);
161+
162+
// b → y_eq_10 and ¬b → y_eq_0
163+
m.bool_clause(&[y_eq_10], &[b]);
164+
m.bool_clause(&[b, y_eq_0], &[]);
165+
```
166+
167+
### Counting
168+
169+
```rust
170+
// Count how many of x, y, z are greater than 5
171+
let x = m.int(0, 10);
172+
let y = m.int(0, 10);
173+
let z = m.int(0, 10);
174+
let five = m.int(5, 5);
175+
176+
let x_gt_5 = m.bool();
177+
let y_gt_5 = m.bool();
178+
let z_gt_5 = m.bool();
179+
180+
m.int_gt_reif(x, five, x_gt_5);
181+
m.int_gt_reif(y, five, y_gt_5);
182+
m.int_gt_reif(z, five, z_gt_5);
183+
184+
// Require exactly 2 to be > 5
185+
m.int_lin_eq(&[1, 1, 1], &[x_gt_5, y_gt_5, z_gt_5], 2);
186+
```
187+
188+
## Design Decisions
189+
190+
### Why Bidirectional Propagation Without Early Returns?
191+
192+
The original implementation used early returns after checking one direction:
193+
194+
```rust
195+
// OLD - BAD
196+
if x_max < y_min {
197+
self.b.try_set_min(Val::ValI(1), ctx)?;
198+
return Some(()); // ← Early return
199+
}
200+
```
201+
202+
This caused non-deterministic failures when constraints were posted in certain orders. The fix removes early returns and always checks all directions:
203+
204+
```rust
205+
// NEW - GOOD
206+
if x_max < y_min {
207+
self.b.try_set_min(Val::ValI(1), ctx)?;
208+
}
209+
// Continue to check other directions...
210+
```
211+
212+
This ensures the propagator makes progress regardless of when it runs or what other constraints have propagated.
213+
214+
### Why Include Reification in Optimization?
215+
216+
Reification constraints can be expensive to propagate, so including them in constraint optimization allows the solver to:
217+
- Schedule them appropriately relative to other constraints
218+
- Prioritize them based on variable connectivity
219+
- Maintain consistency with the optimization framework
220+
221+
Without inclusion in optimization, they would be silently removed, causing subtle and hard-to-debug failures.
222+
223+
## API Completeness
224+
225+
All six comparison reification constraints are now available:
226+
227+
| Constraint | Meaning | API Method |
228+
|-----------|---------|-----------|
229+
| `b ⇔ (x = y)` | Equality | `int_eq_reif(x, y, b)`|
230+
| `b ⇔ (x ≠ y)` | Inequality | `int_ne_reif(x, y, b)`|
231+
| `b ⇔ (x < y)` | Less than | `int_lt_reif(x, y, b)`|
232+
| `b ⇔ (x ≤ y)` | Less or equal | `int_le_reif(x, y, b)`|
233+
| `b ⇔ (x > y)` | Greater than | `int_gt_reif(x, y, b)`|
234+
| `b ⇔ (x ≥ y)` | Greater or equal | `int_ge_reif(x, y, b)`|
235+
236+
## Next Steps
237+
238+
### Immediate
239+
- ✅ Run full test suite to ensure no regressions
240+
- ✅ Document the implementation
241+
- Commit changes with clear message
242+
243+
### Future Phase 2 Work
244+
- Implement more FlatZinc global constraints
245+
- Add set constraints
246+
- Implement float constraints
247+
- Create FlatZinc parser integration tests
248+
249+
## References
250+
251+
- FlatZinc specification: https://www.minizinc.org/doc-2.5.5/en/fzn-spec.html
252+
- Reification documentation: `docs/development/ZINC_REIFICATION_FIX.md`
253+
- Integration tests: `tests/test_phase2_integration.rs`
254+
- Examples: `examples/reification_comparison.rs`

0 commit comments

Comments
 (0)