Skip to content

Commit

Permalink
Add handling discrete constraints to presolve
Browse files Browse the repository at this point in the history
  • Loading branch information
arcondello committed Oct 18, 2022
1 parent 9cdbdae commit caed3a3
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 0 deletions.
7 changes: 7 additions & 0 deletions dimod/include/dimod/constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ bool Constraint<bias_type, index_type>::is_onehot() const {
// must be linear and must have at least two variables
if (!base_type::is_linear() || base_type::num_variables() < 2) return false;

// must be equality
if (sense_ != Sense::EQ) return false;

// we could check the rhs vs offset, but let's treat it canonically as
// needing no offset
if (base_type::offset()) return false;

// all of out variables must be binary
for (const auto& v : base_type::variables()) {
if (base_type::vartype(v) != Vartype::BINARY) return false;
Expand Down
10 changes: 10 additions & 0 deletions dimod/include/dimod/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ class Expression : public abc::QuadraticModelBase<Bias, Index> {
/// Set the quadratic bias for the given variables.
void set_quadratic(index_type u, index_type v, bias_type bias);

bool shares_variables(const Expression& other) const;

void substitute_variable(index_type v, bias_type multiplier, bias_type offset);

/// Return the upper bound on variable ``v``.
Expand Down Expand Up @@ -584,6 +586,14 @@ void Expression<bias_type, index_type>::set_quadratic(index_type u, index_type v
base_type::set_quadratic(enforce_variable(u), enforce_variable(v), bias);
}

template <class bias_type, class index_type>
bool Expression<bias_type, index_type>::shares_variables(const Expression& other) const {
for (auto& v : variables_) {
if (other.has_variable(v)) return true; // overlap!
}
return false;
}

template <class bias_type, class index_type>
void Expression<bias_type, index_type>::substitute_variable(index_type v, bias_type multiplier,
bias_type offset) {
Expand Down
40 changes: 40 additions & 0 deletions dimod/include/dimod/presolve.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,46 @@ void PreSolver<bias_type, index_type, assignment_type>::apply() {
++v;
}
}

// Cleanup

// *-- remove any invalid discrete markers
std::vector<index_type> discrete;
for (size_type c = 0; c < model_.num_constraints(); ++c) {
auto& constraint = model_.constraint_ref(c);

if (!constraint.marked_discrete()) continue;

// we can check if it's well formed
if (constraint.is_onehot()) {
discrete.push_back(c);
} else {
constraint.mark_discrete(false); // if it's not one-hot, it's not discrete
}
}
// check if they overlap
size_type i = 0;
while (i < discrete.size()) {
// check if ci overlaps with any other constraints
auto& constraint = model_.constraint_ref(discrete[i]);

bool overlap = false;
for (size_type j = i + 1; j < discrete.size(); ++j) {
if (model_.constraint_ref(discrete[j]).shares_variables(constraint)) {
// we have overlap!
overlap = true;
constraint.mark_discrete(false);
break;
}
}

if (overlap) {
discrete.erase(discrete.begin() + i);
continue;
}

++i;
}
}

template <class bias_type, class index_type, class assignment_type>
Expand Down
74 changes: 74 additions & 0 deletions testscpp/tests/test_presolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,80 @@ SCENARIO("constrained quadratic models can be presolved") {
}
}
}

GIVEN("a CQM with 5 binary variables") {
auto cqm = ConstrainedQuadraticModel<double>();
cqm.add_variables(Vartype::BINARY, 5);

WHEN("we add a discrete constraint that has an offset but not a rhs") {
auto& c1 = cqm.constraint_ref(cqm.add_constraint());
c1.set_linear(0, 1);
c1.set_linear(1, 1);
c1.set_offset(-1);
c1.mark_discrete();

AND_WHEN("we presolve is applied") {
auto presolver = presolve::PreSolver<double>(std::move(cqm));
presolver.load_default_presolvers();
presolver.apply();

THEN("the constraint is still marked as discrete") {
REQUIRE(presolver.model().num_constraints() == 1);
CHECK(presolver.model().constraint_ref(0).marked_discrete());
}
}
}
WHEN("we add a discrete constraint that has an offset and a rhs") {
auto& c1 = cqm.constraint_ref(cqm.add_constraint());
c1.set_linear(0, 1);
c1.set_linear(1, 1);
c1.set_rhs(1);
c1.set_offset(-1);
c1.mark_discrete();

AND_WHEN("we presolve is applied") {
auto presolver = presolve::PreSolver<double>(std::move(cqm));
presolver.load_default_presolvers();
presolver.apply();

THEN("the constraint is still marked as discrete") {
REQUIRE(presolver.model().num_constraints() == 1);
CHECK(!presolver.model().constraint_ref(0).marked_discrete());
}
}
}

WHEN("we add two overlapping discrete constraints and one non-overlapping") {
auto& c1 = cqm.constraint_ref(cqm.add_constraint());
c1.set_linear(0, 1);
c1.set_linear(1, 1);
c1.set_rhs(1);
c1.mark_discrete();
auto& c2 = cqm.constraint_ref(cqm.add_constraint());
c2.set_linear(2, 1);
c2.set_linear(1, 1);
c2.set_rhs(1);
c2.mark_discrete();
auto& c3 = cqm.constraint_ref(cqm.add_constraint());
c3.set_linear(3, 1);
c3.set_linear(4, 1);
c3.set_rhs(1);
c3.mark_discrete();

AND_WHEN("we presolve is applied") {
auto presolver = presolve::PreSolver<double>(std::move(cqm));
presolver.load_default_presolvers();
presolver.apply();

THEN("two will still be marked as discrete") {
REQUIRE(presolver.model().num_constraints() == 3);
CHECK(presolver.model().constraint_ref(0).marked_discrete() !=
presolver.model().constraint_ref(1).marked_discrete());
CHECK(presolver.model().constraint_ref(2).marked_discrete());
}
}
}
}
}

} // namespace dimod

0 comments on commit caed3a3

Please sign in to comment.