From f14c0361c69de397762aa4a21187a4f2717d45a3 Mon Sep 17 00:00:00 2001 From: "lperron@google.com" Date: Thu, 22 May 2014 17:37:21 +0000 Subject: [PATCH] support failure in constraints propagation and demon executions --- examples/tests/test_cp_api.py | 39 ++++++++++- src/constraint_solver/constraint_solver.cc | 23 ++++++- src/constraint_solver/constraint_solver.h | 5 ++ src/constraint_solver/constraint_solver.swig | 68 +++++++++++++++----- 4 files changed, 116 insertions(+), 19 deletions(-) diff --git a/examples/tests/test_cp_api.py b/examples/tests/test_cp_api.py index f1abae15e1..f04a61d63f 100644 --- a/examples/tests/test_cp_api.py +++ b/examples/tests/test_cp_api.py @@ -64,7 +64,7 @@ def test_search_monitor(): solver.Solve(db, monitor) -class DemonTest(pywrapcp.Demon): +class DemonTest(pywrapcp.PyDemon): def __init__(self, x): pywrapcp.Demon.__init__(self) self._x = x @@ -74,7 +74,7 @@ class DemonTest(pywrapcp.Demon): print 'in Run(), saw ' + str(self._x) -class ConstraintTest(pywrapcp.Constraint): +class ConstraintTest(pywrapcp.PyConstraint): def __init__(self, solver, x): pywrapcp.Constraint.__init__(self, solver) self._x = x @@ -92,6 +92,14 @@ class ConstraintTest(pywrapcp.Constraint): print self._x print 'out of InitialPropagate()' +class InitialPropagateDemon(pywrapcp.PyDemon): + def __init__(self, ct): + pywrapcp.Demon.__init__(self) + self._ct = ct + + def Run(self, solver): + self._ct.InitialPropagate() + def test_demon(): solver = pywrapcp.Solver('test export') @@ -109,6 +117,32 @@ def test_constraint(): solver.Solve(db) +class DumbGreaterOrEqualToFive(pywrapcp.PyConstraint): + def __init__(self, solver, x): + pywrapcp.Constraint.__init__(self, solver) + self._x = x + + def Post(self): + self._demon = InitialPropagateDemon(self) + self._x.WhenBound(self._demon) + + def InitialPropagate(self): + if self._x.Bound(): + if self._x.Value() < 5: + print 'Reject %d' % self._x.Value() + self.solver().Fail() + else: + print 'Accept %d' % self._x.Value() + + +def test_failing_constraint(): + solver = pywrapcp.Solver('test export') + x = solver.IntVar(1, 10, 'x') + myct = DumbGreaterOrEqualToFive(solver, x) + solver.Add(myct) + db = solver.Phase([x], solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE) + solver.Solve(db) + def main(): test_member() @@ -119,6 +153,7 @@ def main(): test_search_monitor() test_demon() test_constraint() + test_failing_constraint() if __name__ == '__main__': diff --git a/src/constraint_solver/constraint_solver.cc b/src/constraint_solver/constraint_solver.cc index cc94c18c64..f6a8f214a1 100644 --- a/src/constraint_solver/constraint_solver.cc +++ b/src/constraint_solver/constraint_solver.cc @@ -242,12 +242,14 @@ class Queue { solver_->TopPeriodicCheck(); } demon->Run(solver_); + solver_->CheckFail(); } else { solver_->GetPropagationMonitor()->BeginDemonRun(demon); if (++solver_->demon_runs_[demon->priority()] % kTestPeriod == 0) { solver_->TopPeriodicCheck(); } demon->Run(solver_); + solver_->CheckFail(); solver_->GetPropagationMonitor()->EndDemonRun(demon); } } @@ -257,6 +259,7 @@ class Queue { solver_->TopPeriodicCheck(); } demon->Run(solver_); + solver_->CheckFail(); } void ProcessInstrumentedNormalDemon(Demon* const demon) { @@ -265,6 +268,7 @@ class Queue { solver_->TopPeriodicCheck(); } demon->Run(solver_); + solver_->CheckFail(); solver_->GetPropagationMonitor()->EndDemonRun(demon); } @@ -307,6 +311,7 @@ class Queue { solver_->TopPeriodicCheck(); } demon->Run(solver_); + solver_->CheckFail(); } } } else { @@ -1420,7 +1425,8 @@ Solver::Solver(const std::string& name, const SolverParameters& parameters) additional_constraint_index_(0), propagation_monitor_(BuildTrace(this)), print_trace_(nullptr), - anonymous_variable_index_(0) { + anonymous_variable_index_(0) , + should_fail_(false) { Init(); } @@ -1453,7 +1459,8 @@ Solver::Solver(const std::string& name) additional_constraint_index_(0), propagation_monitor_(BuildTrace(this)), print_trace_(nullptr), - anonymous_variable_index_(0) { + anonymous_variable_index_(0), + should_fail_(false) { Init(); } @@ -2460,6 +2467,17 @@ void Solver::Fail() { searches_.back()->JumpBack(); } +void Solver::ShouldFail() { + should_fail_ = true; +} + +void Solver::CheckFail() { + if (should_fail_) { + should_fail_ = false; + Fail(); + } +} + // ----- Cast Expression ----- IntExpr* Solver::CastExpression(const IntVar* const var) const { @@ -3214,6 +3232,7 @@ void Constraint::PostAndPropagate() { FreezeQueue(); Post(); InitialPropagate(); + solver()->CheckFail(); UnfreezeQueue(); } diff --git a/src/constraint_solver/constraint_solver.h b/src/constraint_solver/constraint_solver.h index 593eb7c758..150ace8c46 100644 --- a/src/constraint_solver/constraint_solver.h +++ b/src/constraint_solver/constraint_solver.h @@ -2883,6 +2883,10 @@ class Solver { // method returns expr, nullptr otherwise. IntExpr* CastExpression(const IntVar* const var) const; + // Support for swig. + void ShouldFail(); + void CheckFail(); + private: void Init(); // Initialization. To be called by the constructors only. void PushState(MarkerType t, const StateInfo& info); @@ -3021,6 +3025,7 @@ class Solver { std::unique_ptr propagation_monitor_; PropagationMonitor* print_trace_; int anonymous_variable_index_; + bool should_fail_; DISALLOW_COPY_AND_ASSIGN(Solver); }; diff --git a/src/constraint_solver/constraint_solver.swig b/src/constraint_solver/constraint_solver.swig index d6f566a20d..5eb7394445 100644 --- a/src/constraint_solver/constraint_solver.swig +++ b/src/constraint_solver/constraint_solver.swig @@ -260,22 +260,14 @@ gflags.DEFINE_boolean('cp_no_solve', False, gflags.DEFINE_string('cp_profile_file', '', 'exports profiling overview to file.') } -%pythoncode { - -class PyDecisionBuilder(object): - def NextWrapper(self, solver): - result = None - try: - result = self.Next(solver) - except Exception: - return solver.FailDecision() - return result - - def DebugString(self): - return "PyDecisionBuilder" -} } // namespace operations_research +// Rename rules for directors +%rename (InitialPropagateWrapper) + operations_research::Constraint::InitialPropagate; +%rename (RunWrapper) operations_research::Demon::Run; + + // Rename rules on SolutionCollector. %rename (Branches) operations_research::SolutionCollector::branches; %rename (Failures) operations_research::SolutionCollector::failures; @@ -1260,7 +1252,7 @@ struct FailureProtect { // Generic rename rule. %rename("%(camelcase)s", %$isfunction) ""; -// Rename rule on DecisionBuilder; +// Rename rule on directors; %rename (NextWrap) operations_research::DecisionBuilder::Next; // Rename rule on SearchLimit @@ -2098,3 +2090,49 @@ namespace operations_research { typedef Assignment::AssignmentContainer AssignmentContainer; %template(AssignmentIntContainer) AssignmentContainer; } + +#if defined(SWIGPYTHON) +%pythoncode { +class PyDecisionBuilder(object): + def NextWrapper(self, solver): + result = None + try: + result = self.Next(solver) + except Exception: + return solver.FailDecision() + return result + + def DebugString(self): + return "PyDecisionBuilder" + + +class PyConstraint(Constraint): + def __init__(self, solver): + pywrapcp.Constraint.__init__(self, solver) + + def InitialPropagateWrapper(self): + try: + self.InitialPropagate() + except: + self.solver().ShouldFail() + + def DebugString(self): + return "PyConstraint" + + +class PyDemon(Demon): + def __init__(self): + pywrapcp.Demon.__init__(self) + + def RunWrapper(self, solver): + try: + self.Run(solver) + except: + solver.ShouldFail() + + def DebugString(self): + return "PyDemon" + +} +#endif // SWIGPYTHON +