diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index 3d8580178..a2299aec3 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -218,6 +218,34 @@ extern "C" { Z3_CATCH_RETURN(0); } + // get lower value or current approximation + Z3_ast_vector Z3_API Z3_optimize_get_lower_as_vector(Z3_context c, Z3_optimize o, unsigned idx) { + Z3_TRY; + LOG_Z3_optimize_get_lower_as_vector(c, o, idx); + RESET_ERROR_CODE(); + expr_ref_vector es(mk_c(c)->m()); + to_optimize_ptr(o)->get_lower(idx, es); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + mk_c(c)->save_object(v); + v->m_ast_vector.append(es.size(), (ast*const*)es.c_ptr()); + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(0); + } + + // get upper or current approximation + Z3_ast_vector Z3_API Z3_optimize_get_upper_as_vector(Z3_context c, Z3_optimize o, unsigned idx) { + Z3_TRY; + LOG_Z3_optimize_get_upper_as_vector(c, o, idx); + RESET_ERROR_CODE(); + expr_ref_vector es(mk_c(c)->m()); + to_optimize_ptr(o)->get_upper(idx, es); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + mk_c(c)->save_object(v); + v->m_ast_vector.append(es.size(), (ast*const*)es.c_ptr()); + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(0); + } + Z3_string Z3_API Z3_optimize_to_string(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_to_string(c, o); diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index c8954a473..99dd9aac0 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -144,6 +144,23 @@ namespace Microsoft.Z3 { get { return Lower; } } + + /// + /// Retrieve a lower bound for the objective handle. + /// + public ArithExpr[] LowerAsVector + { + get { return opt.GetLowerAsVector(handle); } + } + + /// + /// Retrieve an upper bound for the objective handle. + /// + public ArithExpr[] UpperAsVector + { + get { return opt.GetUpperAsVector(handle); } + } + } /// @@ -255,6 +272,25 @@ namespace Microsoft.Z3 return (ArithExpr)Expr.Create(Context, Native.Z3_optimize_get_upper(Context.nCtx, NativeObject, index)); } + /// + /// Retrieve a lower bound for the objective handle. + /// + private ArithExpr[] GetLowerAsVector(uint index) + { + ASTVector v = new ASTVector(Context, Native.Z3_optimize_get_lower_as_vector(Context.nCtx, NativeObject, index)); + return v.ToArithExprArray(); + } + + + /// + /// Retrieve an upper bound for the objective handle. + /// + private ArithExpr[] GetUpperAsVector(uint index) + { + ASTVector v = new ASTVector(Context, Native.Z3_optimize_get_upper_as_vector(Context.nCtx, NativeObject, index)); + return v.ToArithExprArray(); + } + /// /// Return a string the describes why the last to check returned unknown /// diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 6729d99b5..a0aba95d6 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -6719,12 +6719,24 @@ class OptimizeObjective: opt = self._opt return _to_expr_ref(Z3_optimize_get_upper(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) + def lower_values(self): + opt = self._opt + return AstVector(Z3_optimize_get_lower_as_vector(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) + + def upper_values(self): + opt = self._opt + return AstVector(Z3_optimize_get_upper_as_vector(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) + def value(self): if self._is_max: return self.upper() else: return self.lower() + def __str__(self): + return "%s:%s" % (self._value, self._is_max) + + class Optimize(Z3PPObject): """Optimize API provides methods for solving using objective functions and weighted soft constraints""" @@ -6829,6 +6841,16 @@ class Optimize(Z3PPObject): raise Z3Exception("Expecting objective handle returned by maximize/minimize") return obj.upper() + def lower_values(self, obj): + if not isinstance(obj, OptimizeObjective): + raise Z3Exception("Expecting objective handle returned by maximize/minimize") + return obj.lower_values() + + def upper_values(self, obj): + if not isinstance(obj, OptimizeObjective): + raise Z3Exception("Expecting objective handle returned by maximize/minimize") + return obj.upper_values() + def from_file(self, filename): """Parse assertions and objectives from a file""" Z3_optimize_from_file(self.ctx.ref(), self.optimize, filename) diff --git a/src/api/z3_optimization.h b/src/api/z3_optimization.h index f7a9a8fe9..795b4b8fd 100644 --- a/src/api/z3_optimization.h +++ b/src/api/z3_optimization.h @@ -186,6 +186,33 @@ extern "C" { */ Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx); + + /** + \brief Retrieve lower bound value or approximation for the i'th optimization objective. + The returned vector is of length 3. It always contains numerals. + The three numerals are coefficients a, b, c and encode the result of \c Z3_optimize_get_lower + a * infinity + b + c * epsilon. + + \param c - context + \param o - optimization context + \param idx - index of optimization objective + + def_API('Z3_optimize_get_lower_as_vector', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) + */ + Z3_ast_vector Z3_API Z3_optimize_get_lower_as_vector(Z3_context c, Z3_optimize o, unsigned idx); + + /** + \brief Retrieve upper bound value or approximation for the i'th optimization objective. + + \param c - context + \param o - optimization context + \param idx - index of optimization objective + + def_API('Z3_optimize_get_upper_as_vector', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) + */ + Z3_ast_vector Z3_API Z3_optimize_get_upper_as_vector(Z3_context c, Z3_optimize o, unsigned idx); + + /** \brief Print the current context as a string. \param c - context. diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index 2f7b1ea6e..2b60486f7 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -223,7 +223,7 @@ public: cmd_context::scoped_watch sw(ctx); lbool r = l_undef; try { - r = check_sat(t, g, md, result->labels, pr, core, reason_unknown); + r = check_sat(t, g, md, result->labels, pr, core, reason_unknown); ctx.display_sat_result(r); result->set_status(r); if (r == l_undef) { diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 56b152caa..ebe56381a 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -1290,6 +1290,15 @@ namespace opt { return to_expr(get_upper_as_num(idx)); } + void context::to_exprs(inf_eps const& n, expr_ref_vector& es) { + rational inf = n.get_infinity(); + rational r = n.get_rational(); + rational eps = n.get_infinitesimal(); + es.push_back(m_arith.mk_numeral(inf, inf.is_int())); + es.push_back(m_arith.mk_numeral(r, r.is_int())); + es.push_back(m_arith.mk_numeral(eps, eps.is_int())); + } + expr_ref context::to_expr(inf_eps const& n) { rational inf = n.get_infinity(); rational r = n.get_rational(); @@ -1455,9 +1464,10 @@ namespace opt { void context::validate_maxsat(symbol const& id) { maxsmt& ms = *m_maxsmts.find(id); + TRACE("opt", tout << "Validate: " << id << "\n";); for (unsigned i = 0; i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; - if (obj.m_id == id) { + if (obj.m_id == id && obj.m_type == O_MAXSMT) { SASSERT(obj.m_type == O_MAXSMT); rational value(0); expr_ref val(m); diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 461506258..f51f75830 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -207,6 +207,9 @@ namespace opt { expr_ref get_lower(unsigned idx); expr_ref get_upper(unsigned idx); + void get_lower(unsigned idx, expr_ref_vector& es) { to_exprs(get_lower_as_num(idx), es); } + void get_upper(unsigned idx, expr_ref_vector& es) { to_exprs(get_upper_as_num(idx), es); } + std::string to_string() const; @@ -238,6 +241,7 @@ namespace opt { lbool adjust_unknown(lbool r); bool scoped_lex(); expr_ref to_expr(inf_eps const& n); + void to_exprs(inf_eps const& n, expr_ref_vector& es); void reset_maxsmts(); void import_scoped_state(); diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index bd2c72c3e..ee3171d03 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -25,6 +25,9 @@ Notes: #include"filter_model_converter.h" #include"ast_util.h" #include"solver2tactic.h" +#include"smt_solver.h" +#include"solver.h" +#include"mus.h" typedef obj_map expr2expr_map; @@ -159,6 +162,8 @@ public: ref fmc; if (in->unsat_core_enabled()) { extract_clauses_and_dependencies(in, clauses, assumptions, bool2dep, fmc); + TRACE("mus", in->display_with_dependencies(tout); + tout << clauses << "\n";); if (in->proofs_enabled() && !assumptions.empty()) throw tactic_exception("smt tactic does not support simultaneous generation of proofs and unsat cores"); for (unsigned i = 0; i < clauses.size(); ++i) { @@ -224,8 +229,26 @@ public: pr = m_ctx->get_proof(); if (in->unsat_core_enabled()) { unsigned sz = m_ctx->get_unsat_core_size(); + expr_ref_vector _core(m); + for (unsigned i = 0; i < sz; ++i) { + _core.push_back(m_ctx->get_unsat_core_expr(i)); + } + if (sz > 0 && smt_params_helper(m_params_ref).core_minimize()) { + std::cout << "size1 " << sz << " " << clauses << "\n"; + ref slv = mk_smt_solver(m, m_params_ref, m_logic); + slv->assert_expr(clauses); + mus mus(*slv.get()); + mus.add_soft(_core.size(), _core.c_ptr()); + lbool got_core = mus.get_mus(_core); + sz = _core.size(); + std::cout << "size2 " << sz << "\n"; + if (got_core != l_true) { + r = l_undef; + goto undef_case; + } + } for (unsigned i = 0; i < sz; i++) { - expr * b = m_ctx->get_unsat_core_expr(i); + expr * b = _core[i].get(); SASSERT(is_uninterp_const(b) && m.is_bool(b)); expr * d = bool2dep.find(b); lcore = m.mk_join(lcore, m.mk_leaf(d)); @@ -236,6 +259,7 @@ public: return; } case l_undef: + undef_case: if (m_ctx->canceled()) { throw tactic_exception(Z3_CANCELED_MSG); } diff --git a/src/solver/mus.cpp b/src/solver/mus.cpp index 91d47386e..d536a191b 100644 --- a/src/solver/mus.cpp +++ b/src/solver/mus.cpp @@ -89,6 +89,7 @@ struct mus::imp { lbool get_mus1(expr_ref_vector& mus) { ptr_vector unknown(m_lit2expr.size(), m_lit2expr.c_ptr()); ptr_vector core_exprs; + TRACE("mus", m_solver.display(tout);); while (!unknown.empty()) { IF_VERBOSE(12, verbose_stream() << "(mus reducing core: " << unknown.size() << " new core: " << mus.size() << ")\n";); TRACE("mus", display_vec(tout << "core: ", unknown); display_vec(tout << "mus: ", mus);); diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index b0a4c0e4f..e451f57d4 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -290,3 +290,5 @@ solver_factory * mk_tactic2solver_factory(tactic * t) { solver_factory * mk_tactic_factory2solver_factory(tactic_factory * f) { return alloc(tactic_factory2solver_factory, f); } + + diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 67fce1486..ac63a2d24 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -23,7 +23,6 @@ Notes: #include"model_v2_pp.h" - struct tactic_report::imp { char const * m_id; goal const & m_goal; @@ -175,6 +174,7 @@ void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_conve } } + lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown) { bool models_enabled = g->models_enabled(); bool proofs_enabled = g->proofs_enabled();