3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-14 21:08:46 +00:00

A rudimentary version of MathSAT optimization

Remarks:
(1) The core procedure accepts maximization only
(2) Add lazy initialization to min_maximize_cmd
(3) The procedure isn't working with composite objective yet.
This commit is contained in:
Anh-Dung Phan 2013-10-18 18:00:24 -07:00
parent 898609a3ef
commit a44044fb15
8 changed files with 114 additions and 33 deletions

View file

@ -22,18 +22,32 @@ Notes:
#include "opt_context.h"
class opt_context {
cmd_context& ctx;
scoped_ptr<opt::context> m_opt;
public:
opt_context(cmd_context& ctx): ctx(ctx) {}
opt::context& operator()() {
if (!m_opt) {
m_opt = alloc(opt::context, ctx.m());
}
return *m_opt;
}
};
class assert_weighted_cmd : public cmd {
opt::context* m_opt_ctx;
opt_context* m_opt_ctx;
unsigned m_idx;
expr_ref m_formula;
expr* m_formula;
rational m_weight;
public:
assert_weighted_cmd(cmd_context& ctx, opt::context* opt_ctx):
assert_weighted_cmd(cmd_context& ctx, opt_context* opt_ctx):
cmd("assert-weighted"),
m_opt_ctx(opt_ctx),
m_idx(0),
m_formula(ctx.m()),
m_formula(0),
m_weight(0)
{}
@ -42,6 +56,9 @@ public:
}
virtual void reset(cmd_context & ctx) {
if (m_formula) {
ctx.m().dec_ref(m_formula);
}
m_idx = 0;
m_formula = 0;
}
@ -68,6 +85,7 @@ public:
throw cmd_exception("Invalid type for expression. Expected Boolean type.");
}
m_formula = t;
ctx.m().inc_ref(t);
++m_idx;
}
@ -76,7 +94,7 @@ public:
}
virtual void execute(cmd_context & ctx) {
m_opt_ctx->add_soft_constraint(m_formula, m_weight);
(*m_opt_ctx)().add_soft_constraint(m_formula, m_weight);
reset(ctx);
}
@ -91,10 +109,10 @@ public:
// to do the feasibility check.
class min_maximize_cmd : public cmd {
bool m_is_max;
opt::context* m_opt_ctx;
opt_context* m_opt_ctx;
public:
min_maximize_cmd(cmd_context& ctx, opt::context* opt_ctx, bool is_max):
min_maximize_cmd(cmd_context& ctx, opt_context* opt_ctx, bool is_max):
cmd(is_max?"maximize":"minimize"),
m_is_max(is_max),
m_opt_ctx(opt_ctx)
@ -113,7 +131,7 @@ public:
virtual void set_next_arg(cmd_context & ctx, expr * t) {
// TODO: type check objective term. It should pass basic sanity being
// integer, real (, bit-vector) or other supported objective function type.
m_opt_ctx->add_objective(t, m_is_max);
(*m_opt_ctx)().add_objective(t, m_is_max);
}
virtual void failure_cleanup(cmd_context & ctx) {
@ -127,9 +145,9 @@ public:
};
class optimize_cmd : public cmd {
opt::context* m_opt_ctx;
opt_context* m_opt_ctx;
public:
optimize_cmd(opt::context* opt_ctx):
optimize_cmd(opt_context* opt_ctx):
cmd("optimize"),
m_opt_ctx(opt_ctx)
{}
@ -145,9 +163,9 @@ public:
ptr_vector<expr>::const_iterator it = ctx.begin_assertions();
ptr_vector<expr>::const_iterator end = ctx.end_assertions();
for (; it != end; ++it) {
m_opt_ctx->add_hard_constraint(*it);
(*m_opt_ctx)().add_hard_constraint(*it);
}
m_opt_ctx->optimize();
(*m_opt_ctx)().optimize();
}
@ -157,7 +175,7 @@ private:
};
void install_opt_cmds(cmd_context & ctx) {
opt::context* opt_ctx = alloc(opt::context, ctx.m());
opt_context* opt_ctx = alloc(opt_context, ctx);
ctx.insert(alloc(assert_weighted_cmd, ctx, opt_ctx));
ctx.insert(alloc(min_maximize_cmd, ctx, opt_ctx, true));
ctx.insert(alloc(min_maximize_cmd, ctx, opt_ctx, false));

View file

@ -73,8 +73,13 @@ namespace opt {
if (is_sat != l_true) {
return;
}
for (unsigned i = 0; i < values.size(); ++i) {
// display
if (values[i]) {
std::cout << "objective function: " << mk_pp(m_objectives[i].get(), m) << " -> " << *values[i] << "\n";
} else {
std::cout << "objective function: " << mk_pp(m_objectives[i].get(), m) << " -> unbounded\n";
}
}
}

View file

@ -7,6 +7,7 @@ namespace opt {
opt_solver::opt_solver(ast_manager & m, params_ref const & p, symbol const & l):
solver_na2as(m),
m_manager(m),
m_params(p),
m_context(m, m_params),
m_objective_enabled(false) {
@ -48,6 +49,7 @@ namespace opt {
smt::context& ctx = m_context.get_context();
smt::theory_id arith_id = m_context.m().get_family_id("arith");
smt::theory* arith_theory = ctx.get_theory(arith_id);
if (typeid(smt::theory_mi_arith) == typeid(*arith_theory)) {
return dynamic_cast<smt::theory_mi_arith&>(*arith_theory);
}
@ -64,8 +66,14 @@ namespace opt {
lbool opt_solver::check_sat_core(unsigned num_assumptions, expr * const * assumptions) {
TRACE("opt_solver_na2as", tout << "smt_opt_solver::check_sat_core: " << num_assumptions << "\n";);
lbool r = m_context.check(num_assumptions, assumptions);
if (r == l_true &&& m_objective_enabled) {
VERIFY(get_optimizer().max_min(m_objective_var, false));
if (r == l_true && m_objective_enabled) {
bool is_bounded = get_optimizer().max(m_objective_var);
if (is_bounded) {
m_objective_value = get_optimizer().get_objective_value(m_objective_var);
} else {
optional<rational> r;
m_objective_value = r;
}
}
return r;
}
@ -123,4 +131,9 @@ namespace opt {
void opt_solver::toggle_objective(bool enable) {
m_objective_enabled = enable;
}
optional<rational> opt_solver::get_objective_value() {
return m_objective_value;
}
}

View file

@ -37,6 +37,8 @@ namespace opt {
symbol m_logic;
bool m_objective_enabled;
smt::theory_var m_objective_var;
ast_manager& m_manager;
optional<rational> m_objective_value;
public:
opt_solver(ast_manager & m, params_ref const & p, symbol const & l);
virtual ~opt_solver();
@ -61,6 +63,8 @@ namespace opt {
void set_objective(app* term);
void toggle_objective(bool enable);
optional<rational> get_objective_value();
private:
smt::theory_opt& get_optimizer();
};

View file

@ -22,6 +22,7 @@ Notes:
#include "optimize_objectives.h"
#include "opt_solver.h"
#include "arith_decl_plugin.h"
namespace opt {
@ -31,23 +32,47 @@ namespace opt {
lbool mathsat_style_opt(opt_solver& s,
expr_ref_vector& objectives, svector<bool> const& is_max,
vector<optional<rational> >& values) {
lbool is_sat;
is_sat = s.check_sat(0,0);
if (is_sat != l_true) {
return is_sat;
enable_trace("maximize");
// First check_sat call for initialize theories
lbool is_sat = s.check_sat(0, 0);
if (is_sat == l_false) {
return l_false;
}
// assume that s is instrumented to produce locally optimal assignments.
// Assume there is only one objective function
ast_manager& m = objectives.get_manager();
arith_util autil(m);
app* objective = is_max[0] ? (app*) objectives[0].get() : autil.mk_uminus(objectives[0].get());
s.set_objective(objective);
s.toggle_objective(true);
is_sat = s.check_sat(0, 0);
while (is_sat != l_false) {
model_ref model;
s.get_model(model);
// extract values for objectives.
// store them in values.
// assert there must be something better.
is_sat = s.check_sat(0,0);
}
return l_true;
// Extract values for objectives.
optional<rational> rat;
rat = s.get_objective_value();
// Unbounded objective
if (!rat) {
values.reset();
values.push_back(rat);
return l_true;
}
// If values have initial data, they will be dropped.
values.reset();
values.push_back(rat);
// Assert there must be something better.
expr_ref_vector assumptions(m);
expr* bound = m.mk_fresh_const("bound", m.mk_bool_sort());
assumptions.push_back(bound);
expr* r = autil.mk_numeral(*rat, false);
s.assert_expr(m.mk_eq(bound, is_max[0] ? autil.mk_gt(objectives[0].get(), r) : autil.mk_lt(objectives[0].get(), r)));
is_sat = s.check_sat(1, assumptions.c_ptr());
}
return l_true;
}
/**

View file

@ -855,6 +855,7 @@ namespace smt {
theory_var pick_var_to_leave(theory_var x_j, bool inc, numeral & a_ij, inf_numeral & gain);
template<bool invert>
void add_tmp_row_entry(row & r, numeral const & coeff, theory_var v);
bool max_min(theory_var v, bool max);
bool max_min(row & r, bool max);
bool max_min(svector<theory_var> const & vars);
@ -991,8 +992,10 @@ namespace smt {
// Optimization
//
// -----------------------------------
virtual bool max_min(theory_var v, bool max);
virtual bool max(theory_var v) { return max_min(v, true); }
virtual theory_var add_objective(app* term);
virtual optional<rational> get_objective_value(theory_var v);
inf_rational m_objective;
// -----------------------------------
//

View file

@ -956,7 +956,16 @@ namespace smt {
template<typename Ext>
theory_var theory_arith<Ext>::add_objective(app* term) {
return internalize_term(term);
return internalize_term_core(term);
}
template<typename Ext>
optional<rational> theory_arith<Ext>::get_objective_value(theory_var v) {
optional<rational> rat;
if (m_objective.is_rational()) {
rat = m_objective.get_rational();
};
return rat;
}
/**
@ -1106,7 +1115,7 @@ namespace smt {
\brief Maximize/Minimize the given variable. The bounds of v are update if procedure succeeds.
*/
template<typename Ext>
bool theory_arith<Ext>::max_min(theory_var v, bool max) {
bool theory_arith<Ext>::max_min(theory_var v, bool max) {
TRACE("maximize", tout << (max ? "maximizing" : "minimizing") << " v" << v << "...\n";);
SASSERT(valid_row_assignment());
SASSERT(satisfy_bounds());
@ -1130,7 +1139,10 @@ namespace smt {
TRACE("maximize", tout << "v" << v << " " << (max ? "max" : "min") << " value is: " << get_value(v) << "\n";
display_row(tout, m_tmp_row, true); display_row_info(tout, m_tmp_row););
m_objective = get_value(v);
mk_bound_from_row(v, get_value(v), max ? B_UPPER : B_LOWER, m_tmp_row);
return true;
}
return false;

View file

@ -24,8 +24,9 @@ Notes:
namespace smt {
class theory_opt {
public:
virtual bool max_min(theory_var v, bool max) { UNREACHABLE(); return false; };
virtual bool max(theory_var v) { UNREACHABLE(); return false; };
virtual theory_var add_objective(app* term) { UNREACHABLE(); return null_theory_var; }
virtual optional<rational> get_objective_value(theory_var v) { UNREACHABLE(); optional<rational> r; return r;}
};
}