diff --git a/src/opt/weighted_maxsat.cpp b/src/opt/weighted_maxsat.cpp index d3816bdaf..c1b22f075 100644 --- a/src/opt/weighted_maxsat.cpp +++ b/src/opt/weighted_maxsat.cpp @@ -37,6 +37,7 @@ Notes: #include "opt_sls_solver.h" #include "cancel_eh.h" #include "scoped_timer.h" +#include "optsmt.h" namespace opt { @@ -611,7 +612,11 @@ namespace opt { }; scoped_ptr maxs; + optsmt m_optsmt; // hitting set optimizer based on simplex. + opt_solver m_solver; + unsigned m_objective; // index of objective expr_ref_vector m_aux; // auxiliary (indicator) variables. + expr_ref_vector m_iaux; // auxiliary integer (indicator) variables. expr_ref_vector m_naux; // negation of auxiliary variables. obj_map m_aux2index; // expr |-> index unsigned_vector m_core_activity; // number of times soft constraint is used in a core. @@ -619,6 +624,7 @@ namespace opt { svector m_aux_active; // active soft clauses. ptr_vector m_asms; // assumptions (over aux) pb_util pb; + arith_util a; stats m_stats; @@ -626,20 +632,30 @@ namespace opt { hsmax(solver* s, ast_manager& m, maxsmt_solver_base* maxs): maxsmt_solver_base(s, m), maxs(maxs), + m_optsmt(m), + m_solver(m, m_params, symbol()), m_aux(m), + m_iaux(m), m_naux(m), - pb(m) { + pb(m), + a(m) { } virtual ~hsmax() {} virtual void set_cancel(bool f) { maxsmt_solver_base::set_cancel(f); - maxs->set_cancel(f); + // maxs->set_cancel(f); + m_optsmt.set_cancel(f); + } + + virtual void updt_params(params_ref& p) { + maxsmt_solver_base::updt_params(p); + m_solver.updt_params(p); } virtual void collect_statistics(statistics& st) const { maxsmt_solver_base::collect_statistics(st); - maxs->collect_statistics(st); + // maxs->s().collect_statistics(st); st.update("hsmax-num-iterations", m_stats.m_num_iterations); st.update("hsmax-num-core-reductions-n", m_stats.m_num_core_reductions_failure); st.update("hsmax-num-core-reductions-y", m_stats.m_num_core_reductions_success); @@ -702,9 +718,12 @@ namespace opt { void init_local() { unsigned sz = num_soft(); + app_ref fml(m), obj(m); + expr_ref_vector sum(m); m_asms.reset(); m_seed.reset(); m_aux.reset(); + m_iaux.reset(); m_naux.reset(); m_aux_active.reset(); m_aux2index.reset(); @@ -712,17 +731,28 @@ namespace opt { for (unsigned i = 0; i < sz; ++i) { bool tt = is_true(m_model, m_soft[i].get()); m_seed.push_back(tt); - m_aux. push_back(mk_fresh()); + m_aux. push_back(mk_fresh(m.mk_bool_sort())); + m_iaux.push_back(mk_fresh(a.mk_int())); + expr* iaux = m_iaux.back(); m_naux.push_back(m.mk_not(m_aux.back())); 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.back(), i); + m_aux2index.insert(m_iaux.back(), i); + fml = m.mk_and(a.mk_le(a.mk_numeral(rational::zero(), true), iaux), + a.mk_le(iaux, a.mk_numeral(rational::one(), true))); + rational const& w = m_weights[i]; + sum.push_back(a.mk_mul(a.mk_numeral(w, w.is_int()), iaux)); + m_solver.assert_expr(fml); if (tt) { m_asms.push_back(m_aux.back()); ensure_active(i); } } - maxs->init_soft(m_weights, m_aux); + obj = a.mk_add(sum.size(), sum.c_ptr()); + m_objective = m_optsmt.add(obj); + m_optsmt.setup(m_solver); + // maxs->init_soft(m_weights, m_aux); TRACE("opt", print_seed(tout);); } @@ -858,18 +888,6 @@ namespace opt { } } - struct cancel_maxs { - hsmax& hs; - cancel_maxs(hsmax& hs):hs(hs) {} - void reset_cancel() { - hs.maxs->set_cancel(false); - } - void cancel() { - hs.maxs->set_cancel(true); - } - }; - - // // retrieve the next seed that satisfies state of maxs. // state of maxs must be satisfiable before optimization is called. @@ -881,6 +899,26 @@ namespace opt { lbool next_seed() { scoped_stopwatch _sw(m_stats.m_aux_sat_time); TRACE("opt", tout << "\n";); +#if 1 + m_solver.display(std::cout); + lbool is_sat = m_optsmt.lex(m_objective); + if (is_sat == l_true) { + model_ref mdl; + m_optsmt.get_model(mdl); + + for (unsigned i = 0; i < num_soft(); ++i) { + if (is_active(i)) { + m_seed[i] = is_one(mdl, m_iaux[i].get()); + } + else { + m_seed[i] = false; + } + } + print_seed(std::cout); + TRACE("opt", print_seed(tout);); + } + +#else lbool is_sat = maxs->s().check_sat(0,0); if (is_sat == l_true) { maxs->set_model(); @@ -903,6 +941,7 @@ namespace opt { } TRACE("opt", print_seed(tout);); } +#endif return is_sat; } @@ -1070,6 +1109,16 @@ namespace opt { } expr_ref_vector fmls(m); expr_ref fml(m); +#if 1 + for (unsigned i = 0; i < num_soft(); ++i) { + if (!indices.contains(i)) { + fmls.push_back(m_iaux[i].get()); + } + } + fml = a.mk_ge(a.mk_add(fmls.size(), fmls.c_ptr()), a.mk_numeral(rational::one(), true)); + m_solver.assert_expr(fml); + +#else for (unsigned i = 0; i < num_soft(); ++i) { if (!indices.contains(i)) { fmls.push_back(m_aux[i].get()); @@ -1077,8 +1126,9 @@ namespace opt { } fml = m.mk_or(fmls.size(), fmls.c_ptr()); maxs->add_hard(fml); +#endif TRACE("opt", tout << fml << "\n";); - set_upper(); + // set_upper(); } // constrain the upper bound. @@ -1093,13 +1143,25 @@ namespace opt { void block_up() { expr_ref_vector fmls(m); expr_ref fml(m); +#if 1 for (unsigned i = 0; i < m_asms.size(); ++i) { + unsigned index = m_aux2index.find(m_asms[i]); + m_core_activity[index]++; + fmls.push_back(m_iaux[index].get()); + } + fml = a.mk_lt(a.mk_add(fmls.size(), fmls.c_ptr()), a.mk_numeral(rational(fmls.size()), true)); + TRACE("opt", tout << fml << "\n";); + m_solver.assert_expr(fml); +#else + for (unsigned i = 0; i < m_asms.size(); ++i) { + unsigned index = m_aux2index.find(m_asms[i]); fmls.push_back(m.mk_not(m_asms[i])); - m_core_activity[m_aux2index.find(m_asms[i])]++; + m_core_activity[index]++; } fml = m.mk_or(fmls.size(), fmls.c_ptr()); TRACE("opt", tout << fml << "\n";); maxs->add_hard(fml); +#endif } @@ -1113,19 +1175,28 @@ namespace opt { } } - app_ref mk_fresh() { + app_ref mk_fresh(sort* s) { app_ref r(m); - r = m.mk_fresh_const("r", m.mk_bool_sort()); + r = m.mk_fresh_const("r", s); m_mc->insert(r->get_decl()); return r; } + bool is_true(model_ref& mdl, expr* e) { expr_ref val(m); VERIFY(mdl->eval(e, val)); return m.is_true(val); } + bool is_one(model_ref& mdl, expr* e) { + rational r; + expr_ref val(m); + VERIFY(mdl->eval(e, val)); + std::cout << mk_pp(e, m) << " |-> " << val << "\n"; + return a.is_numeral(val, r) && r.is_one(); + } + bool is_active(unsigned i) const { return m_aux_active[i]; } diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 2e8c62393..0ddaf67d9 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -857,7 +857,7 @@ namespace smt { void add_tmp_row(row & r1, numeral const & coeff, row const & r2); theory_var pick_var_to_leave(bool has_int, theory_var x_j, bool inc, numeral & a_ij, inf_numeral & gain, bool& skiped_row); bool is_safe_to_leave(theory_var x, bool& has_int); - void move_to_bound(theory_var x_i, bool inc); + bool move_to_bound(theory_var x_i, bool inc); template void add_tmp_row_entry(row & r, numeral const & coeff, theory_var v); enum max_min_t { UNBOUNDED, AT_BOUND, OPTIMIZED, BEST_EFFORT}; @@ -1061,6 +1061,7 @@ namespace smt { bool valid_row_assignment() const; bool valid_row_assignment(row const & r) const; bool satisfy_bounds() const; + bool satisfy_integrality() const; #endif }; diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 7a84b3a8b..1e2a6c238 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -1069,7 +1069,7 @@ namespace smt { template inf_eps_rational theory_arith::maximize(theory_var v, expr_ref& blocker) { - TRACE("opt", tout << "data-size: " << m_data.size() << "\n";); + TRACE("bound_bug", display_var(tout, v); display(tout);); max_min_t r = max_min(v, true); if (r == UNBOUNDED) { blocker = get_manager().mk_false(); @@ -1361,7 +1361,8 @@ namespace smt { } } TRACE("opt", tout << "after traversing row:\nx_i: v" << x_i << ", x_j: v" << x_j << ", gain: " << gain << "\n"; - tout << "skipped row: " << (skipped_row?"yes":"no") << "\n";); + tout << "skipped row: " << (skipped_row?"yes":"no") << "\n"; + display(tout);); if (x_j == null_theory_var) { TRACE("opt", tout << "row is " << (max ? "maximized" : "minimized") << "\n";); @@ -1378,6 +1379,7 @@ namespace smt { TRACE("opt", tout << "moved v" << x_j << " to upper bound\n";); SASSERT(valid_row_assignment()); SASSERT(satisfy_bounds()); + SASSERT(satisfy_integrality()); continue; } if (!inc && lower(x_j)) { @@ -1385,13 +1387,14 @@ namespace smt { TRACE("opt", tout << "moved v" << x_j << " to lower bound\n";); SASSERT(valid_row_assignment()); SASSERT(satisfy_bounds()); + SASSERT(satisfy_integrality()); continue; } result = skipped_row?BEST_EFFORT:UNBOUNDED; break; } - if (!is_fixed(x_j) && is_bounded(x_j) && (upper_bound(x_j) - lower_bound(x_j) <= gain)) { + if (!is_fixed(x_j) && is_bounded(x_j) && !skipped_row && (upper_bound(x_j) - lower_bound(x_j) <= gain)) { // can increase/decrease x_j up to upper/lower bound. if (inc) { update_value(x_j, upper_bound(x_j) - get_value(x_j)); @@ -1403,6 +1406,7 @@ namespace smt { } SASSERT(valid_row_assignment()); SASSERT(satisfy_bounds()); + SASSERT(satisfy_integrality()); continue; } @@ -1425,7 +1429,10 @@ namespace smt { move_xi_to_lower = a_ij.is_pos(); else move_xi_to_lower = a_ij.is_neg(); - move_to_bound(x_i, move_xi_to_lower); + if (!move_to_bound(x_i, move_xi_to_lower)) { + result = BEST_EFFORT; + break; + } row & r2 = m_rows[get_var_row(x_j)]; coeff.neg(); @@ -1433,6 +1440,7 @@ namespace smt { SASSERT(r.get_idx_of(x_j) == -1); SASSERT(valid_row_assignment()); SASSERT(satisfy_bounds()); + SASSERT(satisfy_integrality()); } TRACE("opt", display(tout);); return result; @@ -1444,7 +1452,7 @@ namespace smt { */ template - void theory_arith::move_to_bound(theory_var x_i, bool move_to_lower) { + bool theory_arith::move_to_bound(theory_var x_i, bool move_to_lower) { inf_numeral delta, delta_abs; if (move_to_lower) { @@ -1484,6 +1492,9 @@ namespace smt { } } + if (is_int(x_i)) { + delta_abs = floor(delta_abs); + } if (move_to_lower) { delta = -delta_abs; @@ -1493,8 +1504,8 @@ namespace smt { } TRACE("opt", tout << "Safe delta: " << delta << "\n";); - update_value(x_i, delta); + return !delta.is_zero(); } /** diff --git a/src/smt/theory_arith_inv.h b/src/smt/theory_arith_inv.h index 80bb9307f..f6270e664 100644 --- a/src/smt/theory_arith_inv.h +++ b/src/smt/theory_arith_inv.h @@ -199,6 +199,18 @@ namespace smt { } return true; } + + template + bool theory_arith::satisfy_integrality() const { + int num = get_num_vars(); + for (theory_var v = 0; v < num; v++) { + if (!(is_int(v) && get_value(v).is_int())) { + TRACE("bound_bug", display_var(tout, v); display(tout);); + return false; + } + } + return true; + } #endif };