Skip to content

Commit 081b682

Browse files
Fix Engine: Add method to assign linear variables and update references to value retrieval
1 parent 0fa52f3 commit 081b682

1 file changed

Lines changed: 53 additions & 24 deletions

File tree

src/lib.rs

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,20 @@ impl Engine {
5959
index
6060
}
6161

62+
pub fn add_lin_var(&mut self, lin: Lin) -> usize {
63+
let index = self.add_var();
64+
self.assignments[index] = self.lin_val(&lin);
65+
self.new_row(index, lin);
66+
index
67+
}
68+
6269
pub fn add_constraint(&mut self) -> usize {
6370
let index = self.constraints.len();
6471
self.constraints.push(Constraint { lbs: HashMap::new(), ubs: HashMap::new() });
6572
index
6673
}
6774

68-
pub fn value(&self, var: usize) -> &InfRational {
75+
pub fn val(&self, var: usize) -> &InfRational {
6976
&self.assignments[var]
7077
}
7178

@@ -80,7 +87,7 @@ impl Engine {
8087
pub fn lin_val(&self, lin: &Lin) -> InfRational {
8188
let mut result = i_rat(lin.known_term);
8289
for (&var, &coeff) in &lin.vars {
83-
result += coeff * self.value(var);
90+
result += coeff * self.val(var);
8491
}
8592
result
8693
}
@@ -141,7 +148,7 @@ impl Engine {
141148
if let Some(reason) = reason {
142149
entry.insert(reason);
143150
}
144-
if self.value(var) < &lb && !self.is_basic(var) {
151+
if self.val(var) < &lb && !self.is_basic(var) {
145152
self.update(var, lb);
146153
}
147154

@@ -176,7 +183,7 @@ impl Engine {
176183
if let Some(reason) = reason {
177184
entry.insert(reason);
178185
}
179-
if self.value(var) > &ub && !self.is_basic(var) {
186+
if self.val(var) > &ub && !self.is_basic(var) {
180187
self.update(var, ub);
181188
}
182189

@@ -206,9 +213,7 @@ impl Engine {
206213
_ => {
207214
// More than one variable, need to add a new tableau row
208215
let val = inf(-std::mem::take(&mut expr.known_term), if strict { r(-1) } else { Rational::ZERO });
209-
let slack = self.add_var();
210-
self.assignments[slack] = self.lin_val(&expr);
211-
self.new_row(slack, expr);
216+
let slack = self.add_lin_var(expr);
212217
self.set_ub(slack, val, reason)
213218
}
214219
}
@@ -241,9 +246,7 @@ impl Engine {
241246
_ => {
242247
// More than one variable, need to add a new tableau row
243248
let val = i_rat(-std::mem::take(&mut expr.known_term));
244-
let slack = self.add_var();
245-
self.assignments[slack] = self.lin_val(&expr);
246-
self.new_row(slack, expr);
249+
let slack = self.add_lin_var(expr);
247250
self.set_lb(slack, val, reason) && self.set_ub(slack, val, reason)
248251
}
249252
}
@@ -266,7 +269,7 @@ impl Engine {
266269
assert!(&new_value >= self.lb(var) && &new_value <= self.ub(var), "New value must be within bounds");
267270

268271
for &watch in &self.t_watches[var] {
269-
let delta = self.tableau[&watch].vars[&var] * (new_value - self.value(var));
272+
let delta = self.tableau[&watch].vars[&var] * (new_value - self.val(var));
270273
self.assignments[watch] += delta;
271274
self.notify(watch);
272275
}
@@ -279,18 +282,18 @@ impl Engine {
279282
loop {
280283
// we search for a basic variable whose value is not within its bounds..
281284
let var = self.tableau.iter().find_map(|(&var, _)| {
282-
if self.value(var) < self.lb(var) {
285+
if self.val(var) < self.lb(var) {
283286
Some((var, *self.lb(var)))
284-
} else if self.value(var) > self.ub(var) {
287+
} else if self.val(var) > self.ub(var) {
285288
Some((var, *self.ub(var)))
286289
} else {
287290
None
288291
}
289292
});
290293
if let Some((leaving, val)) = var {
291294
// .. if we find one, we try to pivot it with a non-basic variable that can take it back within bounds
292-
if self.value(leaving) < &val {
293-
let entering = self.tableau[&leaving].vars.iter().find_map(|(&v, &coeff)| if coeff.is_positive() && self.value(v) < self.ub(v) || coeff.is_negative() && self.value(v) > self.lb(v) { Some(v) } else { None });
295+
if self.val(leaving) < &val {
296+
let entering = self.tableau[&leaving].vars.iter().find_map(|(&v, &coeff)| if coeff.is_positive() && self.val(v) < self.ub(v) || coeff.is_negative() && self.val(v) > self.lb(v) { Some(v) } else { None });
294297
if let Some(entering) = entering {
295298
self.pivot_and_update(entering, leaving, val);
296299
} else {
@@ -311,8 +314,8 @@ impl Engine {
311314
return false;
312315
}
313316
}
314-
if self.value(leaving) > &val {
315-
let entering = self.tableau[&leaving].vars.iter().find_map(|(&v, &coeff)| if coeff.is_positive() && self.value(v) > self.lb(v) || coeff.is_negative() && self.value(v) < self.ub(v) { Some(v) } else { None });
317+
if self.val(leaving) > &val {
318+
let entering = self.tableau[&leaving].vars.iter().find_map(|(&v, &coeff)| if coeff.is_positive() && self.val(v) > self.lb(v) || coeff.is_negative() && self.val(v) < self.ub(v) { Some(v) } else { None });
316319
if let Some(entering) = entering {
317320
self.pivot_and_update(entering, leaving, val);
318321
} else {
@@ -343,7 +346,7 @@ impl Engine {
343346
assert!(self.is_basic(leaving), "Leaving variable must be basic");
344347
assert!(!self.is_basic(entering), "Entering variable must be non-basic");
345348

346-
let theta = (new_value - self.value(leaving)) / &self.tableau[&leaving].vars[&entering];
349+
let theta = (new_value - self.val(leaving)) / &self.tableau[&leaving].vars[&entering];
347350
self.assignments[leaving] = new_value;
348351
self.notify(leaving);
349352
self.assignments[entering] += theta;
@@ -454,7 +457,7 @@ impl Engine {
454457
impl Display for Engine {
455458
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
456459
for i in 0..self.assignments.len() {
457-
writeln!(f, "x{} = {}, [{}, {}]", i, self.value(i), self.lb(i), self.ub(i))?;
460+
writeln!(f, "x{} = {}, [{}, {}]", i, self.val(i), self.lb(i), self.ub(i))?;
458461
}
459462
for (var, lin) in &self.tableau {
460463
writeln!(f, "x{} = {}", var, lin)?;
@@ -476,7 +479,7 @@ mod tests {
476479
let y = e.add_var();
477480
assert_eq!(x, 0);
478481
assert_eq!(y, 1);
479-
assert_eq!(e.value(x), &InfRational::ZERO);
482+
assert_eq!(e.val(x), &InfRational::ZERO);
480483
assert_eq!(e.lb(x), &InfRational::NEGATIVE_INFINITY);
481484
assert_eq!(e.ub(x), &InfRational::POSITIVE_INFINITY);
482485
}
@@ -542,7 +545,7 @@ mod tests {
542545
let x = e.add_var();
543546
assert!(e.new_eq(&v(x), &c(42), None));
544547
assert_eq!(e.lb(x), e.ub(x));
545-
assert_eq!(e.value(x), &i_rat(r(42)));
548+
assert_eq!(e.val(x), &i_rat(r(42)));
546549
}
547550

548551
#[test]
@@ -623,7 +626,7 @@ mod tests {
623626

624627
engine.set_listener(a, move |e, var| {
625628
assert_eq!(var, a);
626-
assert_eq!(e.value(var), &i_rat(r(5)));
629+
assert_eq!(e.val(var), &i_rat(r(5)));
627630
*called_clone.lock().unwrap() = true;
628631
});
629632

@@ -725,6 +728,32 @@ mod tests {
725728
assert!(conflict.contains(&c2));
726729
}
727730

731+
#[test]
732+
fn complex_conflict_explanation_generation() {
733+
let mut e = Engine::new();
734+
let x = e.add_var();
735+
let y = e.add_var();
736+
let s1 = e.add_lin_var(vc(x, -1) + vc(y, 1)); // s1 = y - x
737+
let s2 = e.add_lin_var(vc(x, 1) + vc(y, 1)); // s2 = x + y
738+
739+
let c0 = e.add_constraint();
740+
assert!(e.new_le(&v(x), &c(-4), Some(c0))); // x <= -4
741+
assert!(e.check());
742+
let c1 = e.add_constraint();
743+
assert!(e.new_ge(&v(x), &c(-8), Some(c1))); // x >= -8
744+
assert!(e.check());
745+
let c2 = e.add_constraint();
746+
assert!(e.new_le(&v(s1), &c(1), Some(c2))); // s1 <= 1
747+
assert!(e.check());
748+
let c3 = e.add_constraint();
749+
assert!(e.new_ge(&v(s2), &c(-3), Some(c3))); // s2 >= -3
750+
assert!(!e.check());
751+
let conflict = e.get_conflict_explanation().unwrap();
752+
assert!(conflict.contains(&c0));
753+
assert!(conflict.contains(&c2));
754+
assert!(conflict.contains(&c3));
755+
}
756+
728757
#[test]
729758
fn add_retract_readd() {
730759
let mut e = Engine::new();
@@ -735,7 +764,7 @@ mod tests {
735764
assert!(e.check());
736765
assert!(e.lb(x) == &i_rat(r(5)));
737766
assert!(e.ub(x) == &InfRational::POSITIVE_INFINITY);
738-
assert!(e.value(x) >= &i_rat(r(5)));
767+
assert!(e.val(x) >= &i_rat(r(5)));
739768

740769
// Retract constraint
741770
e.retract(c0);
@@ -748,7 +777,7 @@ mod tests {
748777
assert!(e.check());
749778
assert!(e.lb(x) == &i_rat(r(5)));
750779
assert!(e.ub(x) == &InfRational::POSITIVE_INFINITY);
751-
assert!(e.value(x) >= &i_rat(r(5)));
780+
assert!(e.val(x) >= &i_rat(r(5)));
752781
}
753782

754783
#[test]

0 commit comments

Comments
 (0)