3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-06 22:23:22 +00:00

updated maxhs

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2014-05-27 11:38:43 -07:00
parent 698705b7fa
commit e370fbb7ed

View file

@ -590,9 +590,10 @@ namespace opt {
// ---------------------------------- // ----------------------------------
// MaxSatHS+MSS // MaxSatHS+MSS
// variant of MaxSAT-HS that also refines // variant of MaxSAT-HS (Algorithm 9)
// upper bound. Lower-bounds are also // that also refines upper bound during progressive calls
// refined in a partial way // to the underlying optimization solver for the soft constraints.
//
class hsmax : public maxsmt_solver_base { class hsmax : public maxsmt_solver_base {
struct stats { struct stats {
@ -605,17 +606,18 @@ namespace opt {
unsigned m_num_model_expansions_failure; unsigned m_num_model_expansions_failure;
double m_core_reduction_time; double m_core_reduction_time;
double m_model_expansion_time; double m_model_expansion_time;
double m_aux_sat_time; double m_aux_sat_time;
double m_disjoint_cores_time;
}; };
scoped_ptr<maxsmt_solver_base> maxs; scoped_ptr<maxsmt_solver_base> maxs;
expr_ref_vector m_aux; // auxiliary (indicator) variables. expr_ref_vector m_aux; // auxiliary (indicator) variables.
expr_ref_vector m_naux; // negation of auxiliary variables. expr_ref_vector m_naux; // negation of auxiliary variables.
obj_map<expr, unsigned> m_aux2index; // expr |-> index obj_map<expr, unsigned> m_aux2index; // expr |-> index
unsigned_vector m_core_activity; // number of times soft constraint is used in a core.
svector<bool> m_seed; // clause selected in current model. svector<bool> m_seed; // clause selected in current model.
svector<bool> m_aux_active; // active soft clauses. svector<bool> m_aux_active; // active soft clauses.
ptr_vector<expr> m_asms; // assumptions (over aux) ptr_vector<expr> m_asms; // assumptions (over aux)
bool m_partial_check; // last check was partial.
pb_util pb; pb_util pb;
stats m_stats; stats m_stats;
@ -626,7 +628,6 @@ namespace opt {
maxs(maxs), maxs(maxs),
m_aux(m), m_aux(m),
m_naux(m), m_naux(m),
m_partial_check(false),
pb(m) { pb(m) {
} }
virtual ~hsmax() {} virtual ~hsmax() {}
@ -647,51 +648,50 @@ namespace opt {
st.update("hsmax-core-reduction-time", m_stats.m_core_reduction_time); st.update("hsmax-core-reduction-time", m_stats.m_core_reduction_time);
st.update("hsmax-model-expansion-time", m_stats.m_model_expansion_time); st.update("hsmax-model-expansion-time", m_stats.m_model_expansion_time);
st.update("hsmax-aux-sat-time", m_stats.m_aux_sat_time); st.update("hsmax-aux-sat-time", m_stats.m_aux_sat_time);
st.update("hsmax-disj-core-time", m_stats.m_disjoint_cores_time);
} }
lbool operator()() { lbool operator()() {
ptr_vector<expr> hs;
init(); init();
init_local(); init_local();
lbool is_sat = l_true; if (!disjoint_cores(hs)) {
bool is_first = true; return l_undef;
while (is_sat != l_false && m_lower < m_upper) { }
seed2assumptions();
while (m_lower < m_upper) {
++m_stats.m_num_iterations; ++m_stats.m_num_iterations;
IF_VERBOSE(1, verbose_stream() << IF_VERBOSE(1, verbose_stream() <<
"(wmaxsat.hsmax [" << m_lower << ":" << m_upper << "])\n";); "(wmaxsat.hsmax [" << m_lower << ":" << m_upper << "])\n";);
if (m_cancel) if (m_cancel) {
return l_undef; return l_undef;
switch(is_sat) { }
lbool core_found = generate_cores(hs);
switch(core_found) {
case l_undef: case l_undef:
return l_undef; return l_undef;
case l_false: case l_true: {
m_lower = m_upper; lbool is_sat = next_seed();
return l_true;
case l_true:
TRACE("opt", tout << "is_first: " << is_first << "\n";);
if (!is_first) {
is_sat = check_subset();
}
is_first = false;
switch(is_sat) { switch(is_sat) {
case l_undef: case l_true:
return l_undef; seed2hs(hs);
case l_true:
if (grow())
block_down();
else
return l_undef;
break; break;
case l_false: case l_false:
if (shrink()) TRACE("opt", tout << "no more seeds\n";);
block_up(); m_lower = m_upper;
else return l_true;
return l_undef; case l_undef:
break; return l_undef;
} }
break;
}
case l_false:
TRACE("opt", tout << "no more cores\n";);
m_lower = m_upper;
return l_true;
} }
is_sat = next_seed();
} }
m_lower = m_upper;
return l_true; return l_true;
} }
@ -701,17 +701,20 @@ namespace opt {
void init_local() { void init_local() {
unsigned sz = num_soft(); unsigned sz = num_soft();
m_asms.reset();
m_seed.reset(); m_seed.reset();
m_aux.reset(); m_aux.reset();
m_naux.reset(); m_naux.reset();
m_aux_active.reset(); m_aux_active.reset();
m_aux2index.reset(); m_aux2index.reset();
m_core_activity.reset();
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
bool tt = is_true(m_model, m_soft[i].get()); bool tt = is_true(m_model, m_soft[i].get());
m_seed.push_back(tt); m_seed.push_back(tt);
m_aux. push_back(mk_fresh()); m_aux. push_back(mk_fresh());
m_naux.push_back(m.mk_not(m_aux.back())); m_naux.push_back(m.mk_not(m_aux.back()));
m_aux_active.push_back(false); m_aux_active.push_back(false);
m_core_activity.push_back(0);
m_aux2index.insert(m_aux[i].get(), i); m_aux2index.insert(m_aux[i].get(), i);
if (tt) { if (tt) {
m_asms.push_back(m_aux.back()); m_asms.push_back(m_aux.back());
@ -722,10 +725,132 @@ namespace opt {
TRACE("opt", print_seed(tout);); TRACE("opt", print_seed(tout););
} }
void seed2assumptions() {
m_asms.reset();
for (unsigned i = 0; i < num_soft(); ++i) {
if (m_seed[i]) {
m_asms.push_back(m_aux[i].get());
}
}
}
void hs2seed(ptr_vector<expr> const& hs) {
for (unsigned i = 0; i < num_soft(); ++i) {
m_seed[i] = true;
}
for (unsigned i = 0; i < hs.size(); ++i) {
m_seed[m_aux2index.find(hs[i])] = false;
}
}
void seed2hs(ptr_vector<expr>& hs) {
hs.reset();
for (unsigned i = 0; i < num_soft(); ++i) {
if (!m_seed[i]) {
hs.push_back(m_aux[i].get());
}
}
}
// //
// retrieve the next seed that satisfies state of maxs. // Find disjoint cores for soft constraints.
// state of maxs must be satisfiable before optimization is called. //
bool disjoint_cores(ptr_vector<expr>& hs) {
scoped_stopwatch _sw(m_stats.m_disjoint_cores_time);
m_asms.reset();
svector<bool> active(num_soft(), true);
rational lower(0);
update_assumptions(active, lower, hs);
SASSERT(lower.is_zero());
while (true) {
lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr());
switch (is_sat) {
case l_true:
if (lower > m_lower) {
m_lower = lower;
}
return true;
case l_false:
if (!shrink()) return false;
block_up();
update_assumptions(active, lower, hs);
break;
case l_undef:
return false;
}
}
}
void update_assumptions(svector<bool>& active, rational& lower, ptr_vector<expr>& hs) {
rational arg_min(0);
expr* e = 0;
for (unsigned i = 0; i < m_asms.size(); ++i) {
unsigned index = m_aux2index.find(m_asms[i]);
active[index] = false;
if (arg_min.is_zero() || arg_min > m_weights[index]) {
arg_min = m_weights[index];
e = m_asms[i];
}
}
if (e) {
hs.push_back(e);
lower += arg_min;
}
m_asms.reset();
for (unsigned i = 0; i < num_soft(); ++i) {
if (active[i]) {
m_asms.push_back(m_aux[i].get());
ensure_active(i);
}
}
}
//
// Auxiliary Algorithm 10 for producing cores.
// //
lbool generate_cores(ptr_vector<expr>& hs) {
bool core = false;
while (true) {
hs2seed(hs);
lbool is_sat = check_subset();
switch(is_sat) {
case l_undef:
return l_undef;
case l_true:
if (!grow()) return l_undef;
block_down();
return core?l_true:l_false;
case l_false:
if (!shrink()) return l_undef;
find_non_optimal_hitting_set(hs);
break;
}
}
}
struct lt_activity {
hsmax& hs;
lt_activity(hsmax& hs):hs(hs) {}
bool operator()(expr* a, expr* b) const {
unsigned w1 = hs.m_core_activity[hs.m_aux2index.find(a)];
unsigned w2 = hs.m_core_activity[hs.m_aux2index.find(b)];
return w1 < w2;
}
};
//
// produce the non-optimal hitting set by using the 10% heuristic.
// of most active cores constraints.
//
void find_non_optimal_hitting_set(ptr_vector<expr>& hs) {
// m_asms contains the current core.
std::sort(m_asms.begin(), m_asms.end(), lt_activity(*this));
for (unsigned i = m_asms.size(); i > 9*m_asms.size()/10;) {
--i;
hs.push_back(m_asms[i]);
}
}
struct cancel_maxs { struct cancel_maxs {
hsmax& hs; hsmax& hs;
cancel_maxs(hsmax& hs):hs(hs) {} cancel_maxs(hsmax& hs):hs(hs) {}
@ -737,43 +862,26 @@ namespace opt {
} }
}; };
// //
// find some satisfying assignment to maxs state. // retrieve the next seed that satisfies state of maxs.
// improve it towards lower bound within some resource // state of maxs must be satisfiable before optimization is called.
// limits (or skip improvement steps all-together, //
// to enable improving upper bound more often). //
// This is the next satisfying assignment. // find a satisfying assignment to maxs state, that
// minimizes objective function.
// //
lbool next_seed() { lbool next_seed() {
scoped_stopwatch _sw(m_stats.m_aux_sat_time); scoped_stopwatch _sw(m_stats.m_aux_sat_time);
lbool is_sat = maxs->s().check_sat(0,0); lbool is_sat = maxs->s().check_sat(0,0);
m_partial_check = true;
if (is_sat == l_true) { if (is_sat == l_true) {
maxs->set_model(); maxs->set_model();
} }
else { else {
return is_sat; return is_sat;
} }
if (false) { is_sat = (*maxs)();
unsigned timeout = 1000; // fixme, make adaptive
cancel_maxs ch(*this);
cancel_eh<cancel_maxs> eh(ch);
{
scoped_timer timer(timeout, &eh);
is_sat = (*maxs)();
// revert timeout.
if (is_sat == l_undef && !m_cancel) {
IF_VERBOSE(1, verbose_stream() << "(wmaxsat.opt-timeout)\n";);
TRACE("opt", tout << "(wmaxsat.opt-timeout)\n";);
maxs->set_cancel(false);
is_sat = l_true;
}
else {
m_partial_check = false;
}
}
}
if (is_sat == l_true) { if (is_sat == l_true) {
model_ref mdl; model_ref mdl;
maxs->get_model(mdl); maxs->get_model(mdl);
@ -812,9 +920,6 @@ namespace opt {
update_model(); update_model();
break; break;
case l_false: case l_false:
if (m_lower < maxs->get_lower()) {
m_lower = maxs->get_lower();
}
break; break;
default: default:
break; break;
@ -832,7 +937,6 @@ namespace opt {
// assignment improves the previous m_upper). // assignment improves the previous m_upper).
// //
bool grow() { bool grow() {
m_upper.reset();
scoped_stopwatch _sw(m_stats.m_model_expansion_time); scoped_stopwatch _sw(m_stats.m_model_expansion_time);
for (unsigned i = 0; i < num_soft(); ++i) { for (unsigned i = 0; i < num_soft(); ++i) {
if (!m_seed[i]) { if (!m_seed[i]) {
@ -854,7 +958,6 @@ namespace opt {
return false; return false;
case l_false: case l_false:
++m_stats.m_num_model_expansions_failure; ++m_stats.m_num_model_expansions_failure;
m_upper += m_weights[i];
m_asms.pop_back(); m_asms.pop_back();
break; break;
case l_true: case l_true:
@ -880,13 +983,13 @@ namespace opt {
return true; return true;
} }
//
// remove soft constraints from the current core. // remove soft constraints from the current core.
// //
bool shrink() { bool shrink() {
scoped_stopwatch _sw(m_stats.m_core_reduction_time); scoped_stopwatch _sw(m_stats.m_core_reduction_time);
m_asms.reset(); m_asms.reset();
s().get_unsat_core(m_asms); s().get_unsat_core(m_asms);
return true; // disabled pending configuration experiment.
TRACE("opt", print_asms(tout);); TRACE("opt", print_asms(tout););
obj_map<expr, unsigned> asm2index; obj_map<expr, unsigned> asm2index;
for (unsigned i = 0; i < m_asms.size(); ++i) { for (unsigned i = 0; i < m_asms.size(); ++i) {
@ -983,6 +1086,7 @@ namespace opt {
expr_ref fml(m); expr_ref fml(m);
for (unsigned i = 0; i < m_asms.size(); ++i) { for (unsigned i = 0; i < m_asms.size(); ++i) {
fmls.push_back(m.mk_not(m_asms[i])); fmls.push_back(m.mk_not(m_asms[i]));
m_core_activity[m_aux2index.find(m_asms[i])]++;
} }
fml = m.mk_or(fmls.size(), fmls.c_ptr()); fml = m.mk_or(fmls.size(), fmls.c_ptr());
TRACE("opt", tout << fml << "\n";); TRACE("opt", tout << fml << "\n";);