From 901e48da319611be75b643b661cbc9d15cce6394 Mon Sep 17 00:00:00 2001 From: Alexander Condello Date: Wed, 19 Oct 2022 16:33:36 -0700 Subject: [PATCH] Fix two presolve edge cases --- dimod/include/dimod/presolve.h | 11 ++++++++- dimod/libcpp/presolve.pxd | 2 +- testscpp/tests/test_presolve.cpp | 41 +++++++++++++++++++++++++++----- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/dimod/include/dimod/presolve.h b/dimod/include/dimod/presolve.h index 529030e01..bcdf49947 100644 --- a/dimod/include/dimod/presolve.h +++ b/dimod/include/dimod/presolve.h @@ -147,6 +147,10 @@ class PreSolver { // we haven't seen this variable before model_.add_variable(model_.vartype(v), model_.lower_bound(v), model_.upper_bound(v)); + + // equality constraint + model_.add_linear_constraint({v, out.first->second}, {1, -1}, Sense::EQ, 0); + postsolver_.add_variable(out.first->second); } @@ -230,7 +234,12 @@ void PreSolver::apply() { if (constraint.num_variables() == 0) { // remove after checking feasibity - throw std::logic_error("not implemented - infeasible"); + if (constraint.offset() != constraint.rhs()) { + throw std::logic_error("infeasible"); + } + model_.remove_constraint(c); + changes = true; + continue; } else if (constraint.num_variables() == 1 && !constraint.is_soft()) { index_type v = constraint.variables()[0]; diff --git a/dimod/libcpp/presolve.pxd b/dimod/libcpp/presolve.pxd index d3bb68bf5..e9f80b138 100644 --- a/dimod/libcpp/presolve.pxd +++ b/dimod/libcpp/presolve.pxd @@ -26,7 +26,7 @@ cdef extern from "dimod/presolve.h" namespace "dimod::presolve" nogil: PreSolver() PreSolver(model_type) - void apply() + void apply() except+ void load_default_presolvers() model_type& model() diff --git a/testscpp/tests/test_presolve.cpp b/testscpp/tests/test_presolve.cpp index a8e3eec06..fca784d16 100644 --- a/testscpp/tests/test_presolve.cpp +++ b/testscpp/tests/test_presolve.cpp @@ -142,11 +142,11 @@ SCENARIO("constrained quadratic models can be presolved") { presolver.load_default_presolvers(); presolver.apply(); - THEN("the self-loops are removed") { + THEN("the self-loops are removed and an equality is added") { auto& model = presolver.model(); REQUIRE(model.num_variables() == 8); - REQUIRE(model.num_constraints() == 1); + REQUIRE(model.num_constraints() == 4); CHECK(model.objective.num_interactions() == 2); CHECK(model.objective.quadratic(0, 5) == 1.5); CHECK(model.objective.quadratic(3, 6) == 3.5); @@ -154,12 +154,41 @@ SCENARIO("constrained quadratic models can be presolved") { CHECK(model.constraint_ref(0).num_interactions() == 2); CHECK(model.constraint_ref(0).quadratic(4, 7) == 5); CHECK(model.constraint_ref(0).quadratic(3, 6) == 6); + + // v0 == v0 + CHECK(model.constraint_ref(1).num_variables() == 2); + CHECK(model.constraint_ref(1).is_linear()); + CHECK(model.constraint_ref(1).variables() == std::vector{0, 5}); + CHECK(model.constraint_ref(1).linear(0) + model.constraint_ref(1).linear(5) == 0); + CHECK(model.constraint_ref(1).rhs() == 0); + CHECK(model.constraint_ref(1).sense() == Sense::EQ); } - AND_WHEN("we then undo the transformation") { - auto original = presolver.postsolver().apply(std::vector{1, 2, 3, 4, 5, 6, 7, 8}); - CHECK(original == std::vector{1, 2, 3, 4, 5}); - } + AND_WHEN("we then undo the transformation") { + auto original = + presolver.postsolver().apply(std::vector{1, 2, 3, 4, 5, 6, 7, 8}); + CHECK(original == std::vector{1, 2, 3, 4, 5}); + } + } + } + + GIVEN("a CQM with a constraint with no variables") { + auto cqm = ConstrainedQuadraticModel(); + cqm.add_variable(Vartype::BINARY); + + // 5 == 5 constraint + auto& constraint = cqm.constraint_ref(cqm.add_constraint()); + constraint.set_offset(5); + constraint.set_rhs(5); + + WHEN("we presolve is applied") { + auto presolver = presolve::PreSolver(std::move(cqm)); + presolver.load_default_presolvers(); + presolver.apply(); + + THEN("the constraint is removed") { + CHECK(cqm.num_constraints() == 0); + } } }