Skip to content

Commit

Permalink
fix(solver): enforce all constraints to be manually posted before sea…
Browse files Browse the repository at this point in the history
…rch/decide/assume
  • Loading branch information
arbimo committed Jan 7, 2025
1 parent 3085f39 commit 8be94ff
Showing 1 changed file with 23 additions and 1 deletion.
24 changes: 23 additions & 1 deletion solver/src/solver/solver_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,14 @@ impl<Lbl: Label> Solver<Lbl> {
(disjuncts.into(), Lit::TRUE)
}

/// Returns true if all constraints are posted.
fn all_constraints_posted(&self) -> bool {
self.next_unposted_constraint == self.model.shape.constraints.len()
}

/// Post all constraints of the model that have not been previously posted.
fn post_constraints(&mut self) -> Result<(), InvalidUpdate> {
if self.next_unposted_constraint == self.model.shape.constraints.len() {
if self.all_constraints_posted() {
return Ok(()); // fast path that avoids updating metrics
}
let start_time = Instant::now();
Expand All @@ -518,6 +523,10 @@ impl<Lbl: Label> Solver<Lbl> {
/// Searches for the first satisfying assignment, returning none if the search
/// space was exhausted without encountering a solution.
pub fn solve(&mut self) -> Result<Option<Arc<SavedAssignment>>, Exit> {
if let Err(_) = self.post_constraints() {
return Ok(None);
}

match self.search()? {
SolveResult::AtSolution => Ok(Some(Arc::new(self.model.state.clone()))),
SolveResult::ExternalSolution(s) => Ok(Some(s)),
Expand Down Expand Up @@ -545,6 +554,10 @@ impl<Lbl: Label> Solver<Lbl> {
);

let mut valid_assignments = Vec::with_capacity(64);
if let Err(_) = self.post_constraints() {
// Trivially UNSAT, return the empty vec of valid assignments
return Ok(valid_assignments);
}
loop {
match self.search()? {
SolveResult::Unsat(_) => return Ok(valid_assignments),
Expand Down Expand Up @@ -612,6 +625,7 @@ impl<Lbl: Label> Solver<Lbl> {
/// In particular, the output distinguishes between whether the solution was found by this
/// solver or another one (i.e. was read from the input channel).
fn search(&mut self) -> Result<SolveResult, Exit> {
assert!(self.all_constraints_posted());
// make sure brancher has knowledge of all variables.
self.brancher.import_vars(&self.model);

Expand Down Expand Up @@ -700,6 +714,12 @@ impl<Lbl: Label> Solver<Lbl> {
assert_eq!(self.last_assumption_level, DecLvl::ROOT);
// best solution found so far
let mut best = None;

if let Err(_) = self.post_constraints() {
// trivially UNSAT
return Ok(None);
}

loop {
let sol = match self.search()? {
SolveResult::AtSolution => {
Expand Down Expand Up @@ -758,6 +778,7 @@ impl<Lbl: Label> Solver<Lbl> {
}

pub fn decide(&mut self, decision: Lit) {
assert!(self.all_constraints_posted());
self.save_state();
log_dec!(
"decision: {:?} -- {} dom:{:?}",
Expand All @@ -776,6 +797,7 @@ impl<Lbl: Label> Solver<Lbl> {
/// If the assumption is accepted, returns an `Ok(x)` result where `x` is true iff the assumption was not already entailed
/// (i.e. something changed in the domains).
pub fn assume(&mut self, assumption: Lit) -> Result<bool, UnsatCore> {
assert!(self.all_constraints_posted());
assert_eq!(self.last_assumption_level, self.decision_level);
debug_assert!(
self.model.state.decisions().is_empty(),
Expand Down

0 comments on commit 8be94ff

Please sign in to comment.