3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-08 02:15:19 +00:00
z3/src/opt/maxlex.cpp
Nikolaj Bjorner 1ed68906fa fix debug assertion code, make maxlex optional
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2019-01-25 08:23:41 -08:00

268 lines
8.3 KiB
C++

/*++
Copyright (c) 2019 Microsoft Corporation
Module Name:
maxlex.cpp
Abstract:
MaxLex solves weighted max-sat problems where weights impose lexicographic order.
MaxSAT is particularly easy for this class:
In order of highest weight, check if soft constraint can be satisfied.
If so, assert it, otherwise assert the negation and record whether the soft
constraint is true or false in the solution.
Author:
Nikolaj Bjorner (nbjorner) 2019-25-1
--*/
#include "opt/opt_context.h"
#include "opt/maxsmt.h"
#include "opt/maxlex.h"
namespace opt {
bool is_maxlex(weights_t & _ws) {
vector<rational> ws(_ws);
std::sort(ws.begin(), ws.end());
ws.reverse();
rational sum(0);
for (rational const& w : ws) {
sum += w;
}
for (rational const& w : ws) {
if (sum > w + w) return false;
sum -= w;
}
return true;
}
class maxlex : public maxsmt_solver_base {
struct cmp_soft {
bool operator()(soft const& s1, soft const& s2) const {
return s1.weight > s2.weight;
}
};
ast_manager& m;
maxsat_context& m_c;
void update_assignment() {
model_ref mdl;
s().get_model(mdl);
if (mdl) {
m_model = mdl;
m_c.model_updated(mdl.get());
update_assignment(mdl);
}
}
void assert_value(soft& soft) {
switch (soft.value) {
case l_true:
s().assert_expr(soft.s);
break;
case l_false:
s().assert_expr(expr_ref(m.mk_not(soft.s), m));
break;
default:
break;
}
}
void set_value(soft& soft, lbool v) {
soft.set_value(v);
assert_value(soft);
}
void update_assignment(model_ref & mdl) {
bool prefix_defined = true;
for (auto & soft : m_soft) {
if (!prefix_defined) {
set_value(soft, l_undef);
continue;
}
switch (soft.value) {
case l_undef:
prefix_defined = mdl->is_true(soft.s);
set_value(soft, prefix_defined ? l_true : l_undef);
break;
case l_true:
break;
case l_false:
break;
}
}
update_bounds();
}
void update_bounds() {
m_lower.reset();
m_upper.reset();
bool prefix_defined = true;
for (auto & soft : m_soft) {
if (!prefix_defined) {
m_upper += soft.weight;
continue;
}
switch (soft.value) {
case l_undef:
prefix_defined = false;
m_upper += soft.weight;
break;
case l_true:
break;
case l_false:
m_lower += soft.weight;
m_upper += soft.weight;
break;
}
}
trace_bounds("maxlex");
}
void init() {
model_ref mdl;
s().get_model(mdl);
update_assignment(mdl);
}
lbool maxlex1() {
for (auto & soft : m_soft) {
if (soft.value == l_true) {
continue;
}
SASSERT(soft.value == l_undef);
expr* a = soft.s;
lbool is_sat = s().check_sat(1, &a);
switch (is_sat) {
case l_false:
set_value(soft, l_false);
update_bounds();
break;
case l_true:
update_assignment();
SASSERT(soft.value == l_true);
break;
case l_undef:
return l_undef;
}
}
return l_true;
}
// try two literals per round.
// doesn't seem to make a difference based on sample test.
lbool maxlex2() {
unsigned sz = m_soft.size();
for (unsigned i = 0; i < sz; ++i) {
auto& soft = m_soft[i];
if (soft.value != l_undef) {
continue;
}
SASSERT(soft.value == l_undef);
if (i + 1 == sz) {
expr* a = soft.s;
lbool is_sat = s().check_sat(1, &a);
switch (is_sat) {
case l_false:
set_value(soft, l_false);
update_bounds();
break;
case l_true:
update_assignment();
SASSERT(soft.value == l_true);
break;
case l_undef:
return l_undef;
}
}
else {
auto& soft2 = m_soft[i+1];
expr_ref_vector core(m);
expr* a = soft.s;
expr* b = soft2.s;
expr* asms[2] = { a, b };
lbool is_sat = s().check_sat(2, asms);
switch (is_sat) {
case l_true:
update_assignment();
SASSERT(soft.value == l_true);
SASSERT(soft2.value == l_true);
break;
case l_false:
s().get_unsat_core(core);
if (core.contains(b)) {
expr_ref not_b(mk_not(m, b), m);
asms[1] = not_b;
switch (s().check_sat(2, asms)) {
case l_true:
// a, b is unsat, a, not b is sat.
set_value(soft2, l_false);
update_assignment();
SASSERT(soft.value == l_true);
SASSERT(soft2.value == l_false);
break;
case l_false:
// a, b is unsat, a, not b is unsat -> a is unsat
// b is unsat, a, not b is unsat -> not a, not b
set_value(soft, l_false);
if (!core.contains(a)) {
set_value(soft2, l_false);
}
update_bounds();
break;
case l_undef:
return l_undef;
}
}
else {
set_value(soft, l_false);
update_bounds();
}
break;
case l_undef:
return l_undef;
}
}
}
return l_true;
}
public:
maxlex(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& s):
maxsmt_solver_base(c, ws, s),
m(c.get_manager()),
m_c(c) {
// ensure that soft constraints are sorted with largest soft constraints first.
cmp_soft cmp;
std::sort(m_soft.begin(), m_soft.end(), cmp);
}
lbool operator()() override {
init();
return maxlex1();
}
void commit_assignment() override {
for (auto & soft : m_soft) {
if (soft.value == l_undef) {
return;
}
assert_value(soft);
}
}
};
maxsmt_solver_base* mk_maxlex(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& soft) {
return alloc(maxlex, c, id, ws, soft);
}
}