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