/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_solver.h Abstract: SAT solver main class. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_SOLVER_H_ #define SAT_SOLVER_H_ #include"sat_types.h" #include"sat_clause.h" #include"sat_watched.h" #include"sat_justification.h" #include"sat_var_queue.h" #include"sat_extension.h" #include"sat_config.h" #include"sat_cleaner.h" #include"sat_simplifier.h" #include"sat_scc.h" #include"sat_asymm_branch.h" #include"sat_iff3_finder.h" #include"sat_probing.h" #include"sat_mus.h" #include"sat_par.h" #include"params.h" #include"statistics.h" #include"stopwatch.h" #include"trace.h" #include"rlimit.h" namespace sat { /** \brief Main statistic counters. */ struct stats { unsigned m_mk_var; unsigned m_mk_bin_clause; unsigned m_mk_ter_clause; unsigned m_mk_clause; unsigned m_conflict; unsigned m_propagate; unsigned m_bin_propagate; unsigned m_ter_propagate; unsigned m_decision; unsigned m_restart; unsigned m_gc_clause; unsigned m_del_clause; unsigned m_minimized_lits; unsigned m_dyn_sub_res; unsigned m_non_learned_generation; unsigned m_blocked_corr_sets; stats() { reset(); } void reset(); void collect_statistics(statistics & st) const; }; class solver { public: struct abort_solver {}; protected: reslimit& m_rlimit; bool m_checkpoint_enabled; config m_config; stats m_stats; extension * m_ext; par* m_par; random_gen m_rand; clause_allocator m_cls_allocator; cleaner m_cleaner; model m_model; model_converter m_mc; bool m_model_is_current; simplifier m_simplifier; scc m_scc; asymm_branch m_asymm_branch; probing m_probing; mus m_mus; // MUS for minimal core extraction bool m_inconsistent; // A conflict is usually a single justification. That is, a justification // for false. If m_not_l is not null_literal, then m_conflict is a // justification for l, and the conflict is union of m_no_l and m_conflict; justification m_conflict; literal m_not_l; clause_vector m_clauses; clause_vector m_learned; unsigned m_num_frozen; vector m_watches; char_vector m_assignment; svector m_justification; svector m_decision; svector m_mark; svector m_lit_mark; svector m_eliminated; svector m_external; svector m_level; svector m_activity; unsigned m_activity_inc; svector m_phase; svector m_prev_phase; svector m_assigned_since_gc; bool m_phase_cache_on; unsigned m_phase_counter; var_queue m_case_split_queue; unsigned m_qhead; unsigned m_scope_lvl; literal_vector m_trail; clause_wrapper_vector m_clauses_to_reinit; struct scope { unsigned m_trail_lim; unsigned m_clauses_to_reinit_lim; bool m_inconsistent; }; svector m_scopes; stopwatch m_stopwatch; params_ref m_params; scoped_ptr m_clone; // for debugging purposes literal_vector m_assumptions; // additional assumptions during check literal_set m_assumption_set; // set of enabled assumptions literal_vector m_core; // unsat core unsigned m_par_limit_in; unsigned m_par_limit_out; unsigned m_par_num_vars; void del_clauses(clause * const * begin, clause * const * end); friend class integrity_checker; friend class cleaner; friend class simplifier; friend class scc; friend class elim_eqs; friend class asymm_branch; friend class probing; friend class iff3_finder; friend class mus; friend struct mk_stat; public: solver(params_ref const & p, reslimit& l, extension * ext); ~solver(); // ----------------------- // // Misc // // ----------------------- void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void collect_statistics(statistics & st) const; void reset_statistics(); void display_status(std::ostream & out) const; /** \brief Copy (non learned) clauses from src to this solver. Create missing variables if needed. \pre the model converter of src and this must be empty */ void copy(solver const & src); // ----------------------- // // Variable & Clause creation // // ----------------------- bool_var mk_var(bool ext = false, bool dvar = true); void mk_clause(literal_vector const& lits) { mk_clause(lits.size(), lits.c_ptr()); } void mk_clause(unsigned num_lits, literal * lits); void mk_clause(literal l1, literal l2); void mk_clause(literal l1, literal l2, literal l3); protected: void del_clause(clause & c); clause * mk_clause_core(unsigned num_lits, literal * lits, bool learned); void mk_clause_core(literal_vector const& lits) { mk_clause_core(lits.size(), lits.c_ptr()); } void mk_clause_core(unsigned num_lits, literal * lits) { mk_clause_core(num_lits, lits, false); } void mk_clause_core(literal l1, literal l2) { literal lits[2] = { l1, l2 }; mk_clause_core(2, lits); } void mk_bin_clause(literal l1, literal l2, bool learned); bool propagate_bin_clause(literal l1, literal l2); clause * mk_ter_clause(literal * lits, bool learned); bool attach_ter_clause(clause & c); clause * mk_nary_clause(unsigned num_lits, literal * lits, bool learned); bool attach_nary_clause(clause & c); void attach_clause(clause & c, bool & reinit); void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); } class scoped_detach { solver& s; clause& c; bool m_deleted; public: scoped_detach(solver& s, clause& c): s(s), c(c), m_deleted(false) { s.detach_clause(c); } ~scoped_detach() { if (!m_deleted) s.attach_clause(c); } void del_clause() { if (!m_deleted) { s.del_clause(c); m_deleted = true; } } }; class scoped_disable_checkpoint { solver& s; public: scoped_disable_checkpoint(solver& s): s(s) { s.m_checkpoint_enabled = false; } ~scoped_disable_checkpoint() { s.m_checkpoint_enabled = true; } }; unsigned select_watch_lit(clause const & cls, unsigned starting_at) const; unsigned select_learned_watch_lit(clause const & cls) const; bool simplify_clause(unsigned & num_lits, literal * lits) const; template bool simplify_clause_core(unsigned & num_lits, literal * lits) const; void detach_bin_clause(literal l1, literal l2, bool learned); void detach_clause(clause & c); void detach_nary_clause(clause & c); void detach_ter_clause(clause & c); void push_reinit_stack(clause & c); // ----------------------- // // Basic // // ----------------------- public: bool inconsistent() const { return m_inconsistent; } unsigned num_vars() const { return m_level.size(); } unsigned num_clauses() const; unsigned num_restarts() const { return m_restarts; } bool is_external(bool_var v) const { return m_external[v] != 0; } void set_external(bool_var v) { m_external[v] = true; } bool was_eliminated(bool_var v) const { return m_eliminated[v] != 0; } unsigned scope_lvl() const { return m_scope_lvl; } lbool value(literal l) const { return static_cast(m_assignment[l.index()]); } lbool value(bool_var v) const { return static_cast(m_assignment[literal(v, false).index()]); } unsigned lvl(bool_var v) const { return m_level[v]; } unsigned lvl(literal l) const { return m_level[l.var()]; } unsigned init_trail_size() const { return scope_lvl() == 0 ? m_trail.size() : m_scopes[0].m_trail_lim; } void assign(literal l, justification j) { TRACE("sat_assign", tout << l << " previous value: " << value(l) << "\n";); switch (value(l)) { case l_false: set_conflict(j, ~l); break; case l_undef: assign_core(l, j); break; case l_true: return; } } void assign_core(literal l, justification jst); void set_conflict(justification c, literal not_l); void set_conflict(justification c) { set_conflict(c, null_literal); } lbool status(clause const & c) const; clause_offset get_offset(clause const & c) const { return m_cls_allocator.get_offset(&c); } void checkpoint() { if (!m_checkpoint_enabled) return; if (!m_rlimit.inc()) { m_mc.reset(); m_model_is_current = false; throw solver_exception(Z3_CANCELED_MSG); } ++m_num_checkpoints; if (m_num_checkpoints < 10) return; m_num_checkpoints = 0; if (memory::get_allocation_size() > m_config.m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } void set_par(par* p); bool canceled() { return !m_rlimit.inc(); } config const& get_config() { return m_config; } typedef std::pair bin_clause; protected: watch_list & get_wlist(literal l) { return m_watches[l.index()]; } watch_list const & get_wlist(literal l) const { return m_watches[l.index()]; } watch_list & get_wlist(unsigned l_idx) { return m_watches[l_idx]; } bool is_marked(bool_var v) const { return m_mark[v] != 0; } void mark(bool_var v) { SASSERT(!is_marked(v)); m_mark[v] = true; } void reset_mark(bool_var v) { SASSERT(is_marked(v)); m_mark[v] = false; } bool is_marked_lit(literal l) const { return m_lit_mark[l.index()] != 0; } void mark_lit(literal l) { SASSERT(!is_marked_lit(l)); m_lit_mark[l.index()] = true; } void unmark_lit(literal l) { SASSERT(is_marked_lit(l)); m_lit_mark[l.index()] = false; } bool check_inconsistent(); // ----------------------- // // Propagation // // ----------------------- public: // if update == true, then glue of learned clauses is updated. bool propagate(bool update); protected: bool propagate_core(bool update); // ----------------------- // // Search // // ----------------------- public: lbool check(unsigned num_lits = 0, literal const* lits = 0); model const & get_model() const { return m_model; } bool model_is_current() const { return m_model_is_current; } literal_vector const& get_core() const { return m_core; } model_converter const & get_model_converter() const { return m_mc; } void set_model(model const& mdl); protected: unsigned m_conflicts; unsigned m_restarts; unsigned m_conflicts_since_restart; unsigned m_restart_threshold; unsigned m_luby_idx; unsigned m_conflicts_since_gc; unsigned m_gc_threshold; unsigned m_num_checkpoints; double m_min_d_tk; unsigned m_next_simplify; bool decide(); bool_var next_var(); lbool bounded_search(); lbool final_check(); lbool propagate_and_backjump_step(bool& done); void init_search(); literal_vector m_min_core; bool m_min_core_valid; void init_assumptions(unsigned num_lits, literal const* lits); void reassert_min_core(); void update_min_core(); void resolve_weighted(); void reset_assumptions(); void add_assumption(literal lit); void pop_assumption(); void reinit_assumptions(); bool tracking_assumptions() const; bool is_assumption(literal l) const; void simplify_problem(); void mk_model(); bool check_model(model const & m) const; void restart(); void sort_watch_lits(); void exchange_par(); lbool check_par(unsigned num_lits, literal const* lits); // ----------------------- // // GC // // ----------------------- protected: void gc(); void gc_glue(); void gc_psm(); void gc_glue_psm(); void gc_psm_glue(); void save_psm(); void gc_half(char const * st_name); void gc_dyn_psm(); bool activate_frozen_clause(clause & c); unsigned psm(clause const & c) const; bool can_delete(clause const & c) const { if (c.on_reinit_stack()) return false; if (c.size() == 3) return true; // not needed to justify anything. literal l0 = c[0]; if (value(l0) != l_true) return true; justification const & jst = m_justification[l0.var()]; return !jst.is_clause() || m_cls_allocator.get_clause(jst.get_clause_offset()) != &c; } // ----------------------- // // Conflict resolution // // ----------------------- protected: unsigned m_conflict_lvl; literal_vector m_lemma; literal_vector m_ext_antecedents; bool resolve_conflict(); bool resolve_conflict_core(); unsigned get_max_lvl(literal consequent, justification js); void process_antecedent(literal antecedent, unsigned & num_marks); void resolve_conflict_for_unsat_core(); void process_antecedent_for_unsat_core(literal antecedent); void process_consequent_for_unsat_core(literal consequent, justification const& js); bool resolve_conflict_for_init(); void process_antecedent_for_init(literal antecedent); bool process_consequent_for_init(literal consequent, justification const& js); void fill_ext_antecedents(literal consequent, justification js); unsigned skip_literals_above_conflict_level(); void forget_phase_of_vars(unsigned from_lvl); void updt_phase_counters(); svector m_diff_levels; unsigned num_diff_levels(unsigned num, literal const * lits); // lemma minimization typedef approx_set_tpl level_approx_set; bool_var_vector m_unmark; level_approx_set m_lvl_set; bool_var_vector m_lemma_min_stack; bool process_antecedent_for_minimization(literal antecedent); bool implied_by_marked(literal lit); void reset_unmark(unsigned old_size); void updt_lemma_lvl_set(); void minimize_lemma(); void reset_lemma_var_marks(); void dyn_sub_res(); // ----------------------- // // Backtracking // // ----------------------- void push(); void pop(unsigned num_scopes); void pop_reinit(unsigned num_scopes); void unassign_vars(unsigned old_sz); void reinit_clauses(unsigned old_sz); literal_vector m_user_scope_literals; literal_vector m_aux_literals; svector m_user_bin_clauses; void gc_lit(clause_vector& clauses, literal lit); void gc_bin(bool learned, literal nlit); void gc_var(bool_var v); bool_var max_var(clause_vector& clauses, bool_var v); bool_var max_var(bool learned, bool_var v); public: void user_push(); void user_pop(unsigned num_scopes); void pop_to_base_level(); reslimit& rlimit() { return m_rlimit; } // ----------------------- // // Simplification // // ----------------------- public: void cleanup(); void simplify(bool learned = true); void asymmetric_branching(); unsigned scc_bin(); // ----------------------- // // Auxiliary methods. // // ----------------------- public: lbool find_mutexes(literal_vector const& lits, vector & mutexes); lbool get_consequences(literal_vector const& assms, bool_var_vector const& vars, vector& conseq); private: typedef hashtable index_set; u_map m_antecedents; vector m_binary_clause_graph; bool extract_assumptions(literal lit, index_set& s); bool check_domain(literal lit, literal lit2); std::ostream& display_index_set(std::ostream& out, index_set const& s) const; lbool get_consequences(literal_vector const& assms, literal_vector const& lits, vector& conseq); lbool get_bounded_consequences(literal_vector const& assms, bool_var_vector const& vars, vector& conseq); void delete_unfixed(literal_set& unfixed_lits, bool_var_set& unfixed_vars); void extract_fixed_consequences(unsigned& start, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq); bool extract_fixed_consequences(literal lit, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq); void update_unfixed_literals(literal_set& unfixed_lits, bool_var_set& unfixed_vars); void fixup_consequence_core(); // ----------------------- // // Activity related stuff // // ----------------------- public: void inc_activity(bool_var v) { unsigned & act = m_activity[v]; act += m_activity_inc; m_case_split_queue.activity_increased_eh(v); if (act > (1 << 24)) rescale_activity(); } void decay_activity() { m_activity_inc *= 11; m_activity_inc /= 10; } private: void rescale_activity(); // ----------------------- // // Iterators // // ----------------------- public: clause * const * begin_clauses() const { return m_clauses.begin(); } clause * const * end_clauses() const { return m_clauses.end(); } clause * const * begin_learned() const { return m_learned.begin(); } clause * const * end_learned() const { return m_learned.end(); } void collect_bin_clauses(svector & r, bool learned) const; // ----------------------- // // Debugging // // ----------------------- public: bool check_invariant() const; void display(std::ostream & out) const; void display_watches(std::ostream & out) const; void display_dimacs(std::ostream & out) const; void display_wcnf(std::ostream & out, unsigned sz, literal const* lits, unsigned const* weights) const; void display_assignment(std::ostream & out) const; void display_justification(std::ostream & out, justification const& j) const; protected: void display_binary(std::ostream & out) const; void display_units(std::ostream & out) const; bool is_unit(clause const & c) const; bool is_empty(clause const & c) const; bool check_missed_propagation(clause_vector const & cs) const; bool check_missed_propagation() const; bool check_marks() const; }; struct mk_stat { solver const & m_solver; mk_stat(solver const & s):m_solver(s) {} void display(std::ostream & out) const; }; std::ostream & operator<<(std::ostream & out, mk_stat const & stat); }; #endif