1 /*++
2 Copyright (c) 2014 Microsoft Corporation
3
4 Module Name:
5
6 maxsres.cpp
7
8 Abstract:
9
10 MaxRes (weighted) max-sat algorithms:
11
12 - mus: max-sat algorithm by Nina and Bacchus, AAAI 2014.
13 - mus-mss: based on dual refinement of bounds.
14
15 MaxRes is a core-guided approach to maxsat.
16 MusMssMaxRes extends the core-guided approach by
17 leveraging both cores and satisfying assignments
18 to make progress towards a maximal satisfying assignment.
19
20 Given a (minimal) unsatisfiable core for the soft
21 constraints the approach works like max-res.
22 Given a (maximal) satisfying subset of the soft constraints
23 the approach updates the upper bound if the current assignment
24 improves the current best assignmet.
25 Furthermore, take the soft constraints that are complements
26 to the current satisfying subset.
27 E.g, if F are the hard constraints and
28 s1,...,sn, t1,..., tm are the soft clauses and
29 F & s1 & ... & sn is satisfiable, then the complement
30 of of the current satisfying subset is t1, .., tm.
31 Update the hard constraint:
32 F := F & (t1 or ... or tm)
33 Replace t1, .., tm by m-1 new soft clauses:
34 t1 & t2, t3 & (t1 or t2), t4 & (t1 or t2 or t3), ..., tn & (t1 or ... t_{n-1})
35 Claim:
36 If k of these soft clauses are satisfied, then k+1 of
37 the previous soft clauses are satisfied.
38 If k of these soft clauses are false in the satisfying assignment
39 for the updated F, then k of the original soft clauses are also false
40 under the assignment.
41 In summary: any assignment to the new clauses that satsfies F has the
42 same cost.
43 Claim:
44 If there are no satisfying assignments to F, then the current best assignment
45 is the optimum.
46
47 Author:
48
49 Nikolaj Bjorner (nbjorner) 2014-20-7
50
51 Notes:
52
53 --*/
54
55 #include "ast/ast_pp.h"
56 #include "ast/pb_decl_plugin.h"
57 #include "ast/ast_util.h"
58 #include "model/model_smt2_pp.h"
59 #include "solver/solver.h"
60 #include "solver/mus.h"
61 #include "sat/sat_solver/inc_sat_solver.h"
62 #include "smt/smt_solver.h"
63 #include "opt/opt_context.h"
64 #include "opt/opt_params.hpp"
65 #include "opt/opt_lns.h"
66 #include "opt/maxsmt.h"
67 #include "opt/maxres.h"
68
69 using namespace opt;
70
71 class maxres : public maxsmt_solver_base {
72 public:
73 enum strategy_t {
74 s_primal,
75 s_primal_dual
76 };
77 private:
78 struct stats {
79 unsigned m_num_cores;
80 unsigned m_num_cs;
statsmaxres::stats81 stats() { reset(); }
resetmaxres::stats82 void reset() {
83 memset(this, 0, sizeof(*this));
84 }
85 };
86
87 struct lns_maxres : public lns_context {
88 maxres& i;
lns_maxresmaxres::lns_maxres89 lns_maxres(maxres& i) :i(i) {}
~lns_maxresmaxres::lns_maxres90 ~lns_maxres() override {}
update_modelmaxres::lns_maxres91 void update_model(model_ref& mdl) override { i.update_assignment(mdl); }
relax_coresmaxres::lns_maxres92 void relax_cores(vector<expr_ref_vector> const& cores) override { i.relax_cores(cores); }
costmaxres::lns_maxres93 rational cost(model& mdl) override { return i.cost(mdl); }
weightmaxres::lns_maxres94 rational weight(expr* e) override { return i.m_asm2weight[e]; }
softmaxres::lns_maxres95 expr_ref_vector const& soft() override { return i.m_asms; }
96 };
97
98 unsigned m_index;
99 stats m_stats;
100 expr_ref_vector m_B;
101 expr_ref_vector m_asms;
102 expr_ref_vector m_defs;
103 obj_map<expr, rational> m_asm2weight;
104 expr_ref_vector m_new_core;
105 mus m_mus;
106 expr_ref_vector m_trail;
107 strategy_t m_st;
108 rational m_max_upper;
109 model_ref m_csmodel;
110 lns_maxres m_lnsctx;
111 lns m_lns;
112 unsigned m_correction_set_size;
113 bool m_found_feasible_optimum;
114 bool m_hill_climb; // prefer large weight soft clauses for cores
115 unsigned m_last_index; // last index used during hill-climbing
116 bool m_add_upper_bound_block; // restrict upper bound with constraint
117 unsigned m_max_num_cores; // max number of cores per round.
118 unsigned m_max_core_size; // max core size per round.
119 bool m_maximize_assignment; // maximize assignment to find MCS
120 unsigned m_max_correction_set_size;// maximal set of correction set that is tolerated.
121 bool m_wmax; // Block upper bound using wmax
122 // this option is disabled if SAT core is used.
123 bool m_pivot_on_cs; // prefer smaller correction set to core.
124 bool m_dump_benchmarks; // display benchmarks (into wcnf format)
125 bool m_enable_lns { false }; // enable LNS improvements
126 unsigned m_lns_conflicts { 1000 }; // number of conflicts used for LNS improvement
127
128 std::string m_trace_id;
129 typedef ptr_vector<expr> exprs;
130
131 public:
maxres(maxsat_context & c,unsigned index,weights_t & ws,expr_ref_vector const & soft,strategy_t st)132 maxres(maxsat_context& c, unsigned index,
133 weights_t& ws, expr_ref_vector const& soft,
134 strategy_t st):
135 maxsmt_solver_base(c, ws, soft),
136 m_index(index),
137 m_B(m), m_asms(m), m_defs(m),
138 m_new_core(m),
139 m_mus(c.get_solver()),
140 m_trail(m),
141 m_st(st),
142 m_lnsctx(*this),
143 m_lns(s(), m_lnsctx),
144 m_correction_set_size(0),
145 m_found_feasible_optimum(false),
146 m_hill_climb(true),
147 m_last_index(0),
148 m_add_upper_bound_block(false),
149 m_max_num_cores(UINT_MAX),
150 m_max_core_size(3),
151 m_maximize_assignment(false),
152 m_max_correction_set_size(3),
153 m_pivot_on_cs(true)
154 {
155 switch(st) {
156 case s_primal:
157 m_trace_id = "maxres";
158 break;
159 case s_primal_dual:
160 m_trace_id = "pd-maxres";
161 break;
162 }
163 }
164
~maxres()165 ~maxres() override {}
166
is_literal(expr * l)167 bool is_literal(expr* l) {
168 return
169 is_uninterp_const(l) ||
170 (m.is_not(l, l) && is_uninterp_const(l));
171 }
172
add(expr_ref_vector const & es)173 void add(expr_ref_vector const& es) {
174 for (expr* e : es) add(e);
175 }
176
add(expr * e)177 void add(expr* e) {
178 s().assert_expr(e);
179 }
180
add_soft(expr * e,rational const & w)181 void add_soft(expr* e, rational const& w) {
182 TRACE("opt", tout << mk_pp(e, m) << " |-> " << w << "\n";);
183 expr_ref asum(m), fml(m);
184 app_ref cls(m);
185 rational weight(0);
186 if (m_asm2weight.find(e, weight)) {
187 weight += w;
188 m_asm2weight.insert(e, weight);
189 return;
190 }
191 if (is_literal(e)) {
192 asum = e;
193 }
194 else {
195 asum = mk_fresh_bool("soft");
196 fml = m.mk_iff(asum, e);
197 m_defs.push_back(fml);
198 add(fml);
199 }
200 new_assumption(asum, w);
201 }
202
new_assumption(expr * e,rational const & w)203 void new_assumption(expr* e, rational const& w) {
204 IF_VERBOSE(13, verbose_stream() << "new assumption " << mk_pp(e, m) << " " << w << "\n";);
205 m_asm2weight.insert(e, w);
206 m_asms.push_back(e);
207 m_trail.push_back(e);
208 TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n";
209 tout << m_asms << " " << "\n"; );
210 }
211
trace()212 void trace() {
213 trace_bounds(m_trace_id.c_str());
214 }
215
mus_solver()216 lbool mus_solver() {
217 lbool is_sat = l_true;
218 if (!init()) return l_undef;
219 is_sat = init_local();
220 trace();
221 improve_model();
222 if (is_sat != l_true) return is_sat;
223 while (m_lower < m_upper) {
224 TRACE("opt_verbose",
225 s().display(tout << m_asms << "\n") << "\n";
226 display(tout););
227 is_sat = check_sat_hill_climb(m_asms);
228 if (!m.inc()) {
229 return l_undef;
230 }
231 switch (is_sat) {
232 case l_true:
233 CTRACE("opt", m_model->is_false(m_asms),
234 tout << *m_model << "assumptions: ";
235 for (expr* a : m_asms) tout << mk_pp(a, m) << " -> " << (*m_model)(a) << " ";
236 tout << "\n";);
237 SASSERT(!m_model->is_false(m_asms) || m.limit().is_canceled());
238 found_optimum();
239 return l_true;
240 case l_false:
241 is_sat = process_unsat();
242 if (is_sat == l_false) {
243 m_lower = m_upper;
244 }
245 if (is_sat == l_undef) {
246 return is_sat;
247 }
248 break;
249 case l_undef:
250 return l_undef;
251 default:
252 break;
253 }
254 }
255 found_optimum();
256 trace();
257 return l_true;
258 }
259
primal_dual_solver()260 lbool primal_dual_solver() {
261 if (!init()) return l_undef;
262 lbool is_sat = init_local();
263 trace();
264 exprs cs;
265 if (is_sat != l_true) return is_sat;
266 while (m_lower < m_upper) {
267 is_sat = check_sat_hill_climb(m_asms);
268 if (!m.inc()) {
269 return l_undef;
270 }
271 switch (is_sat) {
272 case l_true:
273 get_current_correction_set(cs);
274 if (cs.empty()) {
275 m_found_feasible_optimum = m_model.get() != nullptr;
276 m_lower = m_upper;
277 }
278 else {
279 process_sat(cs);
280 }
281 break;
282 case l_false:
283 is_sat = process_unsat();
284 if (is_sat == l_false) {
285 m_lower = m_upper;
286 }
287 if (is_sat == l_undef) {
288 return is_sat;
289 }
290 break;
291 case l_undef:
292 return l_undef;
293 default:
294 break;
295 }
296 }
297 m_lower = m_upper;
298 trace();
299 return l_true;
300 }
301
check_sat_hill_climb(expr_ref_vector & asms1)302 lbool check_sat_hill_climb(expr_ref_vector& asms1) {
303 expr_ref_vector asms(asms1);
304 lbool is_sat = l_true;
305 if (m_hill_climb) {
306 /**
307 Give preference to cores that have large minimal values.
308 */
309 sort_assumptions(asms);
310 m_last_index = 0;
311 unsigned index = 0;
312 bool first = index > 0;
313 SASSERT(index < asms.size() || asms.empty());
314 IF_VERBOSE(10, verbose_stream() << "start hill climb " << index << " asms: " << asms.size() << "\n";);
315 while (index < asms.size() && is_sat == l_true) {
316 while (!first && asms.size() > 20*(index - m_last_index) && index < asms.size()) {
317 index = next_index(asms, index);
318 }
319 first = false;
320 m_last_index = index;
321 is_sat = check_sat(index, asms.data());
322 }
323 }
324 else {
325 is_sat = check_sat(asms.size(), asms.data());
326 }
327 return is_sat;
328 }
329
check_sat(unsigned sz,expr * const * asms)330 lbool check_sat(unsigned sz, expr* const* asms) {
331 lbool r = s().check_sat(sz, asms);
332 if (r == l_true) {
333 model_ref mdl;
334 s().get_model(mdl);
335 TRACE("opt", tout << *mdl;);
336 if (mdl.get()) {
337 update_assignment(mdl);
338 }
339 }
340 return r;
341 }
342
found_optimum()343 void found_optimum() {
344 IF_VERBOSE(1, verbose_stream() << "found optimum\n";);
345 verify_assumptions();
346 m_lower.reset();
347 for (soft& s : m_soft) {
348 s.set_value(m_model->is_true(s.s));
349 if (!s.is_true()) {
350 m_lower += s.weight;
351 }
352 }
353 m_upper = m_lower;
354 m_found_feasible_optimum = true;
355 }
356
357
operator ()()358 lbool operator()() override {
359 m_defs.reset();
360 switch(m_st) {
361 case s_primal:
362 return mus_solver();
363 case s_primal_dual:
364 return primal_dual_solver();
365 }
366 return l_undef;
367 }
368
collect_statistics(statistics & st) const369 void collect_statistics(statistics& st) const override {
370 st.update("maxres-cores", m_stats.m_num_cores);
371 st.update("maxres-correction-sets", m_stats.m_num_cs);
372 }
373
374 struct weighted_core {
375 exprs m_core;
376 rational m_weight;
weighted_coremaxres::weighted_core377 weighted_core(exprs const& c, rational const& w):
378 m_core(c), m_weight(w) {}
379 };
380
get_cores(vector<weighted_core> & cores)381 lbool get_cores(vector<weighted_core>& cores) {
382 // assume m_s is unsat.
383 lbool is_sat = l_false;
384 cores.reset();
385 exprs core;
386 while (is_sat == l_false) {
387 core.reset();
388 expr_ref_vector _core(m);
389 s().get_unsat_core(_core);
390 model_ref mdl;
391 get_mus_model(mdl);
392 is_sat = minimize_core(_core);
393 core.append(_core.size(), _core.data());
394 DEBUG_CODE(verify_core(core););
395 ++m_stats.m_num_cores;
396 if (is_sat != l_true) {
397 IF_VERBOSE(100, verbose_stream() << "(opt.maxres minimization failed)\n";);
398 break;
399 }
400 if (core.empty()) {
401 IF_VERBOSE(100, verbose_stream() << "(opt.maxres core is empty)\n";);
402 TRACE("opt", tout << "empty core\n";);
403 cores.reset();
404 m_lower = m_upper;
405 return l_true;
406 }
407
408
409 // 1. remove all core literals from m_asms
410 // 2. re-add literals of higher weight than min-weight.
411 // 3. 'core' stores the core literals that are
412 // re-encoded as assumptions, afterwards
413 cores.push_back(weighted_core(core, core_weight(core)));
414
415 remove_soft(core, m_asms);
416 split_core(core);
417
418 if (core.size() >= m_max_core_size) break;
419 if (cores.size() >= m_max_num_cores) break;
420
421 is_sat = check_sat_hill_climb(m_asms);
422 }
423
424 TRACE("opt",
425 tout << "sat: " << is_sat << " num cores: " << cores.size() << "\n";
426 for (auto const& c : cores) display_vec(tout, c.m_core);
427 tout << "num assumptions: " << m_asms.size() << "\n";);
428
429 return is_sat;
430 }
431
get_current_correction_set(exprs & cs)432 void get_current_correction_set(exprs& cs) {
433 model_ref mdl;
434 s().get_model(mdl);
435 update_assignment(mdl);
436 get_current_correction_set(mdl.get(), cs);
437 }
438
get_current_correction_set(model * mdl,exprs & cs)439 void get_current_correction_set(model* mdl, exprs& cs) {
440 cs.reset();
441 if (!mdl) return;
442 for (expr* a : m_asms) {
443 if (mdl->is_false(a)) {
444 cs.push_back(a);
445 }
446 }
447 TRACE("opt", display_vec(tout << "new correction set: ", cs););
448 }
449
450 struct compare_asm {
451 maxres& mr;
compare_asmmaxres::compare_asm452 compare_asm(maxres& mr):mr(mr) {}
operator ()maxres::compare_asm453 bool operator()(expr* a, expr* b) const {
454 rational w1 = mr.get_weight(a);
455 rational w2 = mr.get_weight(b);
456 return w1 > w2 || (w1 == w2 && a->get_id() > b->get_id());
457 }
458 };
459
sort_assumptions(expr_ref_vector & _asms)460 void sort_assumptions(expr_ref_vector& _asms) {
461 compare_asm comp(*this);
462 exprs asms(_asms.size(), _asms.data());
463 expr_ref_vector trail(_asms);
464 std::sort(asms.begin(), asms.end(), comp);
465 _asms.reset();
466 _asms.append(asms.size(), asms.data());
467 DEBUG_CODE(
468 for (unsigned i = 0; i + 1 < asms.size(); ++i) {
469 SASSERT(get_weight(asms[i]) >= get_weight(asms[i+1]));
470 });
471 }
472
next_index(expr_ref_vector const & asms,unsigned index)473 unsigned next_index(expr_ref_vector const& asms, unsigned index) {
474 if (index < asms.size()) {
475 rational w = get_weight(asms[index]);
476 ++index;
477 for (; index < asms.size() && w == get_weight(asms[index]); ++index);
478 }
479 return index;
480 }
481
process_sat(exprs const & corr_set)482 void process_sat(exprs const& corr_set) {
483 ++m_stats.m_num_cs;
484 expr_ref fml(m), tmp(m);
485 TRACE("opt", display_vec(tout << "corr_set: ", corr_set););
486 remove_soft(corr_set, m_asms);
487 rational w = split_core(corr_set);
488 cs_max_resolve(corr_set, w);
489 IF_VERBOSE(2, verbose_stream() << "(opt.maxres.correction-set " << corr_set.size() << ")\n";);
490 m_csmodel = nullptr;
491 m_correction_set_size = 0;
492 }
493
process_unsat()494 lbool process_unsat() {
495 vector<weighted_core> cores;
496 lbool is_sat = get_cores(cores);
497 if (is_sat != l_true) {
498 return is_sat;
499 }
500 if (cores.empty()) {
501 return l_false;
502 }
503 else {
504 process_unsat(cores);
505 return l_true;
506 }
507 }
508
max_core_size(vector<exprs> const & cores)509 unsigned max_core_size(vector<exprs> const& cores) {
510 unsigned result = 0;
511 for (auto const& c : cores) {
512 result = std::max(c.size(), result);
513 }
514 return result;
515 }
516
process_unsat(vector<weighted_core> const & cores)517 void process_unsat(vector<weighted_core> const& cores) {
518 for (auto const & c : cores) {
519 process_unsat(c.m_core, c.m_weight);
520 }
521 improve_model(m_model);
522 }
523
update_model(expr * def,expr * value)524 void update_model(expr* def, expr* value) {
525 SASSERT(is_uninterp_const(def));
526 if (m_csmodel)
527 m_csmodel->register_decl(to_app(def)->get_decl(), (*m_csmodel)(value));
528 if (m_model)
529 m_model->register_decl(to_app(def)->get_decl(), (*m_model)(value));
530 }
531
process_unsat(exprs const & core,rational w)532 void process_unsat(exprs const& core, rational w) {
533 IF_VERBOSE(3, verbose_stream() << "(maxres cs model valid: " << (m_csmodel.get() != nullptr) << " cs size:" << m_correction_set_size << " core: " << core.size() << ")\n";);
534 expr_ref fml(m);
535 SASSERT(!core.empty());
536 TRACE("opt", display_vec(tout << "minimized core: ", core););
537 IF_VERBOSE(10, display_vec(verbose_stream() << "core: ", core););
538 max_resolve(core, w);
539 fml = mk_not(m, mk_and(m, core.size(), core.data()));
540 add(fml);
541 // save small cores such that lex-combinations of maxres can reuse these cores.
542 if (core.size() <= 2) {
543 m_defs.push_back(fml);
544 }
545 m_lower += w;
546 if (m_st == s_primal_dual) {
547 m_lower = std::min(m_lower, m_upper);
548 }
549 if (m_csmodel.get() && m_correction_set_size > 0) {
550 // this estimate can overshoot for weighted soft constraints.
551 --m_correction_set_size;
552 }
553 trace();
554 if (m_c.num_objectives() == 1 && m_pivot_on_cs && m_csmodel.get() && m_correction_set_size < core.size()) {
555 exprs cs;
556 get_current_correction_set(m_csmodel.get(), cs);
557 m_correction_set_size = cs.size();
558 TRACE("opt", tout << "cs " << m_correction_set_size << " " << core.size() << "\n";);
559 if (m_correction_set_size >= core.size())
560 return;
561 rational w(0);
562 for (expr* a : m_asms) {
563 rational w1 = m_asm2weight[a];
564 if (w != 0 && w1 != w) return;
565 w = w1;
566 }
567 process_sat(cs);
568 }
569 }
570
get_mus_model(model_ref & mdl)571 bool get_mus_model(model_ref& mdl) {
572 rational w(0);
573 if (m_c.sat_enabled()) {
574 // SAT solver core extracts some model
575 // during unsat core computation.
576 mdl = nullptr;
577 s().get_model(mdl);
578 }
579 else {
580 w = m_mus.get_best_model(mdl);
581 }
582 if (mdl.get() && w < m_upper) {
583 update_assignment(mdl);
584 }
585 return nullptr != mdl.get();
586 }
587
minimize_core(expr_ref_vector & core)588 lbool minimize_core(expr_ref_vector& core) {
589 if (core.empty()) {
590 return l_true;
591 }
592 if (m_c.sat_enabled()) {
593 return l_true;
594 }
595 m_mus.reset();
596 m_mus.add_soft(core.size(), core.data());
597 lbool is_sat = m_mus.get_mus(m_new_core);
598 if (is_sat != l_true) {
599 return is_sat;
600 }
601 core.reset();
602 core.append(m_new_core);
603 return l_true;
604 }
605
get_weight(expr * e) const606 rational get_weight(expr* e) const {
607 return m_asm2weight.find(e);
608 }
609
core_weight(exprs const & core)610 rational core_weight(exprs const& core) {
611 if (core.empty()) return rational(0);
612 // find the minimal weight:
613 rational w = get_weight(core[0]);
614 for (unsigned i = 1; i < core.size(); ++i) {
615 w = std::min(w, get_weight(core[i]));
616 }
617 return w;
618 }
619
split_core(exprs const & core)620 rational split_core(exprs const& core) {
621 rational w = core_weight(core);
622 // add fresh soft clauses for weights that are above w.
623 for (expr* e : core) {
624 rational w2 = get_weight(e);
625 if (w2 > w) {
626 rational w3 = w2 - w;
627 new_assumption(e, w3);
628 }
629 }
630 return w;
631 }
632
display_vec(std::ostream & out,exprs const & exprs)633 void display_vec(std::ostream& out, exprs const& exprs) {
634 display_vec(out, exprs.size(), exprs.data());
635 }
636
display_vec(std::ostream & out,expr_ref_vector const & exprs)637 void display_vec(std::ostream& out, expr_ref_vector const& exprs) {
638 display_vec(out, exprs.size(), exprs.data());
639 }
640
display_vec(std::ostream & out,unsigned sz,expr * const * args) const641 void display_vec(std::ostream& out, unsigned sz, expr* const* args) const {
642 for (unsigned i = 0; i < sz; ++i) {
643 out << mk_pp(args[i], m) << " : " << get_weight(args[i]) << " ";
644 }
645 out << "\n";
646 }
647
display(std::ostream & out)648 void display(std::ostream& out) {
649 for (expr * a : m_asms) {
650 out << mk_pp(a, m) << " : " << get_weight(a) << "\n";
651 }
652 }
653
max_resolve(exprs const & core,rational const & w)654 void max_resolve(exprs const& core, rational const& w) {
655 SASSERT(!core.empty());
656 expr_ref fml(m), asum(m);
657 app_ref cls(m), d(m), dd(m);
658 m_B.reset();
659 m_B.append(core.size(), core.data());
660 //
661 // d_0 := true
662 // d_i := b_{i-1} and d_{i-1} for i = 1...sz-1
663 // soft (b_i or !d_i)
664 // == (b_i or !(!b_{i-1} or d_{i-1}))
665 // == (b_i or b_0 & b_1 & ... & b_{i-1})
666 //
667 // Soft constraint is satisfied if previous soft constraint
668 // holds or if it is the first soft constraint to fail.
669 //
670 // Soundness of this rule can be established using MaxRes
671 //
672 for (unsigned i = 1; i < core.size(); ++i) {
673 expr* b_i = core[i-1];
674 expr* b_i1 = core[i];
675 if (i == 1) {
676 d = to_app(b_i);
677 }
678 else if (i == 2) {
679 d = m.mk_and(b_i, d);
680 m_trail.push_back(d);
681 }
682 else {
683 dd = mk_fresh_bool("d");
684 fml = m.mk_implies(dd, d);
685 add(fml);
686 m_defs.push_back(fml);
687 fml = m.mk_implies(dd, b_i);
688 add(fml);
689 m_defs.push_back(fml);
690 fml = m.mk_and(d, b_i);
691 update_model(dd, fml);
692 d = dd;
693 }
694 asum = mk_fresh_bool("a");
695 cls = m.mk_or(b_i1, d);
696 fml = m.mk_implies(asum, cls);
697 update_model(asum, cls);
698 new_assumption(asum, w);
699 add(fml);
700 m_defs.push_back(fml);
701 }
702 }
703
704 // cs is a correction set (a complement of a (maximal) satisfying assignment).
cs_max_resolve(exprs const & cs,rational const & w)705 void cs_max_resolve(exprs const& cs, rational const& w) {
706 if (cs.empty()) return;
707 TRACE("opt", display_vec(tout << "correction set: ", cs););
708 expr_ref fml(m), asum(m);
709 app_ref cls(m), d(m), dd(m);
710 m_B.reset();
711 m_B.append(cs.size(), cs.data());
712 d = m.mk_false();
713 //
714 // d_0 := false
715 // d_i := b_{i-1} or d_{i-1} for i = 1...sz-1
716 // soft (b_i and d_i)
717 // == (b_i and (b_0 or b_1 or ... or b_{i-1}))
718 //
719 // asm => b_i
720 // asm => d_{i-1} or b_{i-1}
721 // d_i => d_{i-1} or b_{i-1}
722 //
723 for (unsigned i = 1; i < cs.size(); ++i) {
724 expr* b_i = cs[i - 1];
725 expr* b_i1 = cs[i];
726 cls = m.mk_or(b_i, d);
727 if (i > 2) {
728 d = mk_fresh_bool("d");
729 fml = m.mk_implies(d, cls);
730 update_model(d, cls);
731 add(fml);
732 m_defs.push_back(fml);
733 }
734 else {
735 d = cls;
736 }
737 asum = mk_fresh_bool("a");
738 fml = m.mk_implies(asum, b_i1);
739 add(fml);
740 m_defs.push_back(fml);
741 fml = m.mk_implies(asum, cls);
742 add(fml);
743 m_defs.push_back(fml);
744 new_assumption(asum, w);
745
746 fml = m.mk_and(b_i1, cls);
747 update_model(asum, fml);
748 }
749 fml = m.mk_or(cs);
750 add(fml);
751 }
752
improve_model()753 void improve_model() {
754 if (!m_enable_lns)
755 return;
756 model_ref mdl;
757 s().get_model(mdl);
758 if (mdl)
759 update_assignment(mdl);
760 }
761
762
763
improve_model(model_ref & mdl)764 void improve_model(model_ref& mdl) {
765 if (!m_enable_lns)
766 return;
767 flet<bool> _disable_lns(m_enable_lns, false);
768 m_lns.climb(mdl);
769 }
770
relax_cores(vector<expr_ref_vector> const & cores)771 void relax_cores(vector<expr_ref_vector> const& cores) {
772 vector<weighted_core> wcores;
773 for (auto & core : cores) {
774 exprs _core(core.size(), core.data());
775 wcores.push_back(weighted_core(_core, core_weight(_core)));
776 remove_soft(_core, m_asms);
777 split_core(_core);
778 }
779 process_unsat(wcores);
780 }
781
cost(model & mdl)782 rational cost(model& mdl) {
783 rational upper(0);
784 for (soft& s : m_soft)
785 if (!mdl.is_true(s.s))
786 upper += s.weight;
787 return upper;
788 }
789
update_assignment(model_ref & mdl)790 void update_assignment(model_ref & mdl) {
791 improve_model(mdl);
792 mdl->set_model_completion(true);
793 unsigned correction_set_size = 0;
794 for (expr* a : m_asms) {
795 if (mdl->is_false(a)) {
796 ++correction_set_size;
797 }
798 }
799 if (!m_csmodel.get() || correction_set_size < m_correction_set_size) {
800 m_csmodel = mdl;
801 m_correction_set_size = correction_set_size;
802 }
803
804 TRACE("opt_verbose", tout << *mdl;);
805
806 rational upper = cost(*mdl);
807
808 if (upper > m_upper) {
809 TRACE("opt", tout << "new upper: " << upper << " vs existing upper: " << m_upper << "\n";);
810 return;
811 }
812
813 if (!m_c.verify_model(m_index, mdl.get(), upper)) {
814 return;
815 }
816
817 m_model = mdl;
818 m_c.model_updated(mdl.get());
819
820 TRACE("opt", tout << "updated upper: " << upper << "\n";);
821
822 for (soft& s : m_soft) {
823 s.set_value(m_model->is_true(s.s));
824 }
825
826 verify_assignment();
827
828 m_upper = upper;
829
830 trace();
831
832 add_upper_bound_block();
833 }
834
add_upper_bound_block()835 void add_upper_bound_block() {
836 if (!m_add_upper_bound_block) return;
837 pb_util u(m);
838 expr_ref_vector nsoft(m);
839 vector<rational> weights;
840 expr_ref fml(m);
841 for (soft& s : m_soft) {
842 nsoft.push_back(mk_not(m, s.s));
843 weights.push_back(s.weight);
844 }
845 fml = u.mk_lt(nsoft.size(), weights.data(), nsoft.data(), m_upper);
846 TRACE("opt", tout << "block upper bound " << fml << "\n";);;
847 add(fml);
848 }
849
remove_soft(exprs const & core,expr_ref_vector & asms)850 void remove_soft(exprs const& core, expr_ref_vector& asms) {
851 TRACE("opt", tout << "before remove: " << asms << "\n";);
852 unsigned j = 0;
853 for (expr* a : asms)
854 if (!core.contains(a))
855 asms[j++] = a;
856 asms.shrink(j);
857 TRACE("opt", tout << "after remove: " << asms << "\n";);
858 }
859
updt_params(params_ref & _p)860 void updt_params(params_ref& _p) override {
861 maxsmt_solver_base::updt_params(_p);
862 opt_params p(_p);
863 m_hill_climb = p.maxres_hill_climb();
864 m_add_upper_bound_block = p.maxres_add_upper_bound_block();
865 m_max_num_cores = p.maxres_max_num_cores();
866 m_max_core_size = p.maxres_max_core_size();
867 m_maximize_assignment = p.maxres_maximize_assignment();
868 m_max_correction_set_size = p.maxres_max_correction_set_size();
869 m_pivot_on_cs = p.maxres_pivot_on_correction_set();
870 m_wmax = p.maxres_wmax();
871 m_dump_benchmarks = p.dump_benchmarks();
872 m_enable_lns = p.enable_lns();
873 m_lns_conflicts = p.lns_conflicts();
874 if (m_c.num_objectives() > 1)
875 m_add_upper_bound_block = false;
876 }
877
init_local()878 lbool init_local() {
879 m_lower.reset();
880 m_trail.reset();
881 lbool is_sat = l_true;
882 obj_map<expr, rational> new_soft;
883 is_sat = find_mutexes(new_soft);
884 if (is_sat != l_true) {
885 return is_sat;
886 }
887 for (auto const& kv : new_soft) {
888 add_soft(kv.m_key, kv.m_value);
889 }
890 m_max_upper = m_upper;
891 m_found_feasible_optimum = false;
892 m_last_index = 0;
893 add_upper_bound_block();
894 m_csmodel = nullptr;
895 m_correction_set_size = 0;
896 return l_true;
897 }
898
commit_assignment()899 void commit_assignment() override {
900 if (m_found_feasible_optimum) {
901 add(m_defs);
902 add(m_asms);
903 TRACE("opt", tout << "Committing feasible solution\ndefs:" << m_defs << "\nasms:" << m_asms << "\n";);
904
905 }
906 // else: there is only a single assignment to these soft constraints.
907 }
908
verify_core(exprs const & core)909 void verify_core(exprs const& core) {
910 return;
911 IF_VERBOSE(1, verbose_stream() << "verify core " << s().check_sat(core.size(), core.data()) << "\n";);
912 ref<solver> _solver = mk_smt_solver(m, m_params, symbol());
913 _solver->assert_expr(s().get_assertions());
914 _solver->assert_expr(core);
915 lbool is_sat = _solver->check_sat(0, nullptr);
916 IF_VERBOSE(0, verbose_stream() << "core status (l_false:) " << is_sat << " core size " << core.size() << "\n");
917 CTRACE("opt", is_sat != l_false,
918 for (expr* c : core) tout << "core: " << mk_pp(c, m) << "\n";
919 _solver->display(tout);
920 tout << "other solver\n";
921 s().display(tout);
922 );
923 VERIFY(is_sat == l_false || !m.inc());
924 }
925
verify_assumptions()926 void verify_assumptions() {
927 return;
928 IF_VERBOSE(1, verbose_stream() << "verify assumptions\n";);
929 ref<solver> _solver = mk_smt_solver(m, m_params, symbol());
930 _solver->assert_expr(s().get_assertions());
931 _solver->assert_expr(m_asms);
932 lbool is_sat = _solver->check_sat(0, nullptr);
933 IF_VERBOSE(1, verbose_stream() << "assignment status (l_true) " << is_sat << "\n";);
934 VERIFY(is_sat == l_true);
935 }
936
verify_assignment()937 void verify_assignment() {
938 return;
939 IF_VERBOSE(1, verbose_stream() << "verify assignment\n";);
940 ref<solver> _solver = mk_smt_solver(m, m_params, symbol());
941 _solver->assert_expr(s().get_assertions());
942 expr_ref n(m);
943 for (soft& s : m_soft) {
944 n = s.s;
945 if (!s.is_true()) {
946 n = mk_not(m, n);
947 }
948 _solver->assert_expr(n);
949 }
950 lbool is_sat = _solver->check_sat(0, nullptr);
951 IF_VERBOSE(1, verbose_stream() << "assignment status (l_true) " << is_sat << "\n");
952 VERIFY(is_sat == l_true);
953 }
954 };
955
mk_maxres(maxsat_context & c,unsigned id,weights_t & ws,expr_ref_vector const & soft)956 opt::maxsmt_solver_base* opt::mk_maxres(
957 maxsat_context& c, unsigned id, weights_t& ws, expr_ref_vector const& soft) {
958 return alloc(maxres, c, id, ws, soft, maxres::s_primal);
959 }
960
mk_primal_dual_maxres(maxsat_context & c,unsigned id,weights_t & ws,expr_ref_vector const & soft)961 opt::maxsmt_solver_base* opt::mk_primal_dual_maxres(
962 maxsat_context& c, unsigned id, weights_t& ws, expr_ref_vector const& soft) {
963 return alloc(maxres, c, id, ws, soft, maxres::s_primal_dual);
964 }
965
966