/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_tactic.cpp Abstract: Tactic for using nonlinear procedure. Author: Leonardo (leonardo) 2012-01-02 Notes: --*/ #include "tactic/tactical.h" #include "nlsat/tactic/goal2nlsat.h" #include "nlsat/nlsat_solver.h" #include "model/model.h" #include "ast/expr2var.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "util/z3_exception.h" #include "math/polynomial/algebraic_numbers.h" #include "ast/ast_pp.h" class nlsat_tactic : public tactic { struct expr_display_var_proc : public nlsat::display_var_proc { ast_manager & m; expr_ref_vector m_var2expr; expr_display_var_proc(ast_manager & _m):m(_m), m_var2expr(_m) {} std::ostream& operator()(std::ostream & out, nlsat::var x) const override { if (x < m_var2expr.size()) return out << mk_ismt2_pp(m_var2expr.get(x), m); else return out << "x!" << x; } }; struct imp { ast_manager & m; params_ref m_params; expr_display_var_proc m_display_var; nlsat::solver m_solver; goal2nlsat m_g2nl; imp(ast_manager & _m, params_ref const & p): m(_m), m_params(p), m_display_var(_m), m_solver(m.limit(), p, false) { } void updt_params(params_ref const & p) { m_params.append(p); m_solver.updt_params(m_params); } bool contains_unsupported(expr_ref_vector & b2a, expr_ref_vector & x2t) { for (unsigned x = 0; x < x2t.size(); ++x) { if (!is_uninterp_const(x2t.get(x))) { TRACE(unsupported, tout << "unsupported atom:\n" << mk_ismt2_pp(x2t.get(x), m) << "\n";); return true; } } for (unsigned b = 0; b < b2a.size(); ++b) { expr * a = b2a.get(b); if (a == nullptr) continue; if (is_uninterp_const(a)) continue; if (m_solver.is_interpreted(b)) continue; // arithmetic atom TRACE(unsupported, tout << "unsupported atom:\n" << mk_ismt2_pp(a, m) << "\n";); return true; // unsupported } return false; } bool eval_model(model& model, goal& g) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; ++i) { if (model.is_false(g.form(i))) { TRACE(nlsat, tout << mk_pp(g.form(i), m) << " -> " << model(g.form(i)) << "\n";); IF_VERBOSE(0, verbose_stream() << mk_pp(g.form(i), m) << " -> " << model(g.form(i)) << "\n";); IF_VERBOSE(1, verbose_stream() << model << "\n"); IF_VERBOSE(1, m_solver.display(verbose_stream())); return false; } } return true; } // Return false if nlsat assigned noninteger value to an integer variable. bool mk_model(goal & g, expr_ref_vector & b2a, expr_ref_vector & x2t, model_converter_ref & mc) { bool ok = true; model_ref md = alloc(model, m); arith_util util(m); for (unsigned x = 0; x < x2t.size(); ++x) { expr * t = x2t.get(x); if (!is_uninterp_const(t)) continue; expr * v; try { v = util.mk_numeral(m_solver.am(), m_solver.value(x), util.is_int(t)); } catch (z3_error & ex) { throw ex; } catch (z3_exception &) { v = util.mk_to_int(util.mk_numeral(m_solver.am(), m_solver.value(x), false)); ok = false; } md->register_decl(to_app(t)->get_decl(), v); } for (unsigned b = 0; b < b2a.size(); ++b) { expr * a = b2a.get(b); if (a == nullptr || !is_uninterp_const(a)) continue; lbool val = m_solver.bvalue(b); if (val == l_undef) continue; // don't care md->register_decl(to_app(a)->get_decl(), val == l_true ? m.mk_true() : m.mk_false()); } #ifdef Z3DEBUG eval_model(*md.get(), g); #endif // VERIFY(eval_model(*md.get(), g)); mc = model2model_converter(md.get()); return ok; } void operator()(goal_ref const & g, goal_ref_buffer & result) { tactic_report report("nlsat", *g); if (g->is_decided()) { result.push_back(g.get()); return; } fail_if_proof_generation("nlsat", g); TRACE(nlsat, g->display(tout);); expr2var a2b(m); expr2var t2x(m); m_g2nl(*g, m_params, m_solver, a2b, t2x); m_display_var.m_var2expr.reset(); t2x.mk_inv(m_display_var.m_var2expr); m_solver.set_display_var(m_display_var); TRACE(nlsat, m_solver.display(tout);); IF_VERBOSE(10000, m_solver.display(verbose_stream())); IF_VERBOSE(10000, g->display(verbose_stream())); lbool st = m_solver.check(); if (st == l_undef) { } else if (st == l_true) { expr_ref_vector x2t(m); expr_ref_vector b2a(m); a2b.mk_inv(b2a); t2x.mk_inv(x2t); if (!contains_unsupported(b2a, x2t)) { // If mk_model is false it means that the model produced by nlsat // assigns noninteger values to integer variables model_converter_ref mc; if (mk_model(*g.get(), b2a, x2t, mc)) { // result goal is trivially SAT g->reset(); g->add(mc.get()); } } } else if (st == l_false) { expr_dependency* lcore = nullptr; if (g->unsat_core_enabled()) { vector assumptions; m_solver.get_core(assumptions); for (nlsat::assumption a : assumptions) { expr_dependency* d = static_cast(a); lcore = m.mk_join(lcore, d); } } g->assert_expr(m.mk_false(), nullptr, lcore); } g->inc_depth(); result.push_back(g.get()); } }; imp * m_imp; params_ref m_params; statistics m_stats; struct scoped_set_imp { nlsat_tactic & m_owner; scoped_set_imp(nlsat_tactic & o, imp & i):m_owner(o) { m_owner.m_imp = &i; } ~scoped_set_imp() { m_owner.m_imp->m_solver.collect_statistics(m_owner.m_stats); m_owner.m_imp = nullptr; } }; public: nlsat_tactic(params_ref const & p): m_params(p) { m_imp = nullptr; } tactic * translate(ast_manager & m) override { return alloc(nlsat_tactic, m_params); } ~nlsat_tactic() override { SASSERT(m_imp == 0); } char const* name() const override { return "nlsat"; } void updt_params(params_ref const & p) override { m_params.append(p); } void collect_param_descrs(param_descrs & r) override { goal2nlsat::collect_param_descrs(r); nlsat::solver::collect_param_descrs(r); algebraic_numbers::manager::collect_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { imp local_imp(in->m(), m_params); scoped_set_imp setter(*this, local_imp); local_imp(in, result); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { // convert all Z3 exceptions into tactic exceptions. throw tactic_exception(ex.what()); } } void cleanup() override {} void collect_statistics(statistics & st) const override { st.copy(m_stats); } void reset_statistics() override { m_stats.reset(); } void user_propagate_initialize_value(expr* var, expr* value) override { } }; tactic * mk_nlsat_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(nlsat_tactic, p)); }