mirror of
https://github.com/Z3Prover/z3
synced 2025-08-20 02:00:22 +00:00
working on upper bound optimziation
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
e5698119d7
commit
c0de1e34ac
17 changed files with 343 additions and 125 deletions
|
@ -159,6 +159,7 @@ public:
|
|||
insert_timeout(p);
|
||||
insert_max_memory(p);
|
||||
p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics.");
|
||||
opt::context::collect_param_descrs(p);
|
||||
}
|
||||
|
||||
virtual char const * get_main_descr() const { return "check sat modulo objective function";}
|
||||
|
|
|
@ -17,9 +17,6 @@ Notes:
|
|||
TODO:
|
||||
|
||||
- there are race conditions for cancelation.
|
||||
- it would also be a good idea to maintain a volatile bool to track
|
||||
cancelation and then bail out of loops inside optimize() and derived
|
||||
functions.
|
||||
|
||||
--*/
|
||||
|
||||
|
@ -30,6 +27,7 @@ Notes:
|
|||
#include "opt_solver.h"
|
||||
#include "arith_decl_plugin.h"
|
||||
#include "th_rewriter.h"
|
||||
#include "opt_params.hpp"
|
||||
|
||||
|
||||
namespace opt {
|
||||
|
@ -154,12 +152,17 @@ namespace opt {
|
|||
}
|
||||
}
|
||||
|
||||
void context::collect_param_descrs(param_descrs & r) {
|
||||
opt_params::collect_param_descrs(r);
|
||||
}
|
||||
|
||||
void context::updt_params(params_ref& p) {
|
||||
m_params.append(p);
|
||||
if (m_solver) {
|
||||
m_solver->updt_params(m_params);
|
||||
}
|
||||
|
||||
opt_params _p(m_params);
|
||||
m_opt_objectives.set_engine(_p.engine());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -68,6 +68,8 @@ namespace opt {
|
|||
|
||||
void collect_statistics(statistics& stats);
|
||||
|
||||
static void collect_param_descrs(param_descrs & r);
|
||||
|
||||
void updt_params(params_ref& p);
|
||||
|
||||
private:
|
||||
|
|
|
@ -101,13 +101,7 @@ namespace opt {
|
|||
smt::theory_opt& opt = get_optimizer();
|
||||
for (unsigned i = 0; i < m_objective_vars.size(); ++i) {
|
||||
smt::theory_var v = m_objective_vars[i];
|
||||
bool is_bounded = opt.maximize(v);
|
||||
if (is_bounded) {
|
||||
m_objective_values.push_back(opt.get_objective_value(v));
|
||||
} else {
|
||||
inf_eps r(rational(1), inf_rational(0));
|
||||
m_objective_values.push_back(r);
|
||||
}
|
||||
m_objective_values.push_back(opt.maximize(v));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
|
|
|
@ -62,26 +62,18 @@ namespace opt {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Enumerate locally optimal assignments until fixedpoint.
|
||||
*/
|
||||
lbool optimize_objectives::basic_opt(app_ref_vector& objectives) {
|
||||
arith_util autil(m);
|
||||
|
||||
opt_solver::scoped_push _push(*s);
|
||||
|
||||
for (unsigned i = 0; i < objectives.size(); ++i) {
|
||||
m_vars.push_back(s->add_objective(objectives[i].get()));
|
||||
}
|
||||
|
||||
lbool optimize_objectives::basic_opt() {
|
||||
opt_solver::toggle_objective _t(*s, true);
|
||||
lbool is_sat = l_true;
|
||||
// Disabled while testing and tuning:
|
||||
// is_sat = update_upper();
|
||||
opt_solver::toggle_objective _t(*s, true);
|
||||
|
||||
while (is_sat == l_true && !m_cancel) {
|
||||
is_sat = update_lower();
|
||||
is_sat = s->check_sat(0, 0);
|
||||
if (is_sat == l_true) {
|
||||
update_lower();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cancel || is_sat == l_undef) {
|
||||
|
@ -90,73 +82,117 @@ namespace opt {
|
|||
return l_true;
|
||||
}
|
||||
|
||||
lbool optimize_objectives::update_lower() {
|
||||
lbool is_sat = s->check_sat(0, 0);
|
||||
if (is_sat == l_true) {
|
||||
model_ref md;
|
||||
s->get_model(md);
|
||||
set_max(m_lower, s->get_objective_values());
|
||||
IF_VERBOSE(1,
|
||||
for (unsigned i = 0; i < m_lower.size(); ++i) {
|
||||
verbose_stream() << m_lower[i] << " ";
|
||||
}
|
||||
verbose_stream() << "\n";
|
||||
// model_pp(verbose_stream(), *md);
|
||||
);
|
||||
expr_ref_vector disj(m);
|
||||
expr_ref constraint(m);
|
||||
|
||||
for (unsigned i = 0; i < m_lower.size(); ++i) {
|
||||
inf_eps const& v = m_lower[i];
|
||||
disj.push_back(s->block_lower_bound(i, v));
|
||||
}
|
||||
constraint = m.mk_or(disj.size(), disj.c_ptr());
|
||||
s->assert_expr(constraint);
|
||||
/*
|
||||
Enumerate locally optimal assignments until fixedpoint.
|
||||
*/
|
||||
lbool optimize_objectives::farkas_opt() {
|
||||
smt::theory_opt& opt = s->get_optimizer();
|
||||
|
||||
IF_VERBOSE(1, verbose_stream() << typeid(opt).name() << "\n";);
|
||||
if (typeid(smt::theory_inf_arith) != typeid(opt)) {
|
||||
return l_undef;
|
||||
}
|
||||
return is_sat;
|
||||
|
||||
opt_solver::toggle_objective _t(*s, true);
|
||||
lbool is_sat= l_true;
|
||||
|
||||
while (is_sat == l_true && !m_cancel) {
|
||||
is_sat = update_upper();
|
||||
}
|
||||
|
||||
if (m_cancel || is_sat == l_undef) {
|
||||
return l_undef;
|
||||
}
|
||||
return l_true;
|
||||
}
|
||||
|
||||
void optimize_objectives::update_lower() {
|
||||
model_ref md;
|
||||
s->get_model(md);
|
||||
set_max(m_lower, s->get_objective_values());
|
||||
IF_VERBOSE(1,
|
||||
for (unsigned i = 0; i < m_lower.size(); ++i) {
|
||||
verbose_stream() << m_lower[i] << " ";
|
||||
}
|
||||
verbose_stream() << "\n";
|
||||
// model_pp(verbose_stream(), *md);
|
||||
);
|
||||
expr_ref_vector disj(m);
|
||||
expr_ref constraint(m);
|
||||
|
||||
for (unsigned i = 0; i < m_lower.size(); ++i) {
|
||||
inf_eps const& v = m_lower[i];
|
||||
disj.push_back(s->block_lower_bound(i, v));
|
||||
}
|
||||
constraint = m.mk_or(disj.size(), disj.c_ptr());
|
||||
s->assert_expr(constraint);
|
||||
}
|
||||
|
||||
lbool optimize_objectives::update_upper() {
|
||||
smt::theory_opt& opt = s->get_optimizer();
|
||||
|
||||
IF_VERBOSE(1, verbose_stream() << typeid(opt).name() << "\n";);
|
||||
if (typeid(smt::theory_inf_arith) != typeid(opt)) {
|
||||
return l_true;
|
||||
}
|
||||
SASSERT(typeid(smt::theory_inf_arith) == typeid(opt));
|
||||
|
||||
smt::theory_inf_arith& th = dynamic_cast<smt::theory_inf_arith&>(opt);
|
||||
|
||||
expr_ref bound(m);
|
||||
expr_ref_vector bounds(m);
|
||||
|
||||
opt_solver::scoped_push _push(*s);
|
||||
|
||||
//
|
||||
// NB: we have to create all bound expressions before calling check_sat
|
||||
// because the state after check_sat is not at base level.
|
||||
//
|
||||
|
||||
vector<inf_eps> mid;
|
||||
|
||||
for (unsigned i = 0; i < m_lower.size() && !m_cancel; ++i) {
|
||||
if (m_lower[i] < m_upper[i]) {
|
||||
SASSERT(m_upper[i].get_infinity().is_pos());
|
||||
smt::theory_var v = m_vars[i];
|
||||
bound = th.block_upper_bound(v, m_upper[i]);
|
||||
mid.push_back((m_upper[i]+m_lower[i])/rational(2));
|
||||
bound = th.block_upper_bound(v, mid[i]);
|
||||
bounds.push_back(bound);
|
||||
}
|
||||
else {
|
||||
bounds.push_back(0);
|
||||
mid.push_back(inf_eps());
|
||||
}
|
||||
}
|
||||
bool progress = false;
|
||||
for (unsigned i = 0; i < m_lower.size() && !m_cancel; ++i) {
|
||||
if (m_lower[i] < m_upper[i]) {
|
||||
if (m_lower[i] <= mid[i] && mid[i] <= m_upper[i] && m_lower[i] < m_upper[i]) {
|
||||
th.enable_record_conflict(bounds[i].get());
|
||||
lbool is_sat = s->check_sat(1, bounds.c_ptr() + i);
|
||||
if (is_sat == l_true) {
|
||||
th.enable_record_conflict(0);
|
||||
switch(is_sat) {
|
||||
case l_true:
|
||||
IF_VERBOSE(2, verbose_stream() << "Setting lower bound for v" << m_vars[i] << " to " << m_upper[i] << "\n";);
|
||||
m_lower[i] = m_upper[i];
|
||||
}
|
||||
else if (is_sat == l_false) {
|
||||
// else: TBD extract Farkas coefficients.
|
||||
m_lower[i] = mid[i];
|
||||
update_lower();
|
||||
break;
|
||||
case l_false:
|
||||
if (!th.conflict_minimize().get_infinity().is_zero()) {
|
||||
// bounds is not in the core. The context is unsat.
|
||||
m_upper[i] = m_lower[i];
|
||||
return l_false;
|
||||
}
|
||||
else {
|
||||
m_upper[i] = std::min(m_upper[i], th.conflict_minimize());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return l_undef;
|
||||
}
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
if (m_cancel) {
|
||||
return l_undef;
|
||||
}
|
||||
if (!progress) {
|
||||
return l_false;
|
||||
}
|
||||
return l_true;
|
||||
}
|
||||
|
||||
|
@ -177,7 +213,24 @@ namespace opt {
|
|||
// First check_sat call to initialize theories
|
||||
lbool is_sat = s->check_sat(0, 0);
|
||||
if (is_sat == l_true) {
|
||||
is_sat = basic_opt(objectives);
|
||||
opt_solver::scoped_push _push(*s);
|
||||
|
||||
for (unsigned i = 0; i < objectives.size(); ++i) {
|
||||
m_vars.push_back(s->add_objective(objectives[i].get()));
|
||||
}
|
||||
|
||||
if (m_engine == symbol("basic")) {
|
||||
is_sat = basic_opt();
|
||||
}
|
||||
else if (m_engine == symbol("farkas")) {
|
||||
is_sat = farkas_opt();
|
||||
}
|
||||
else {
|
||||
// TODO: implement symba engine
|
||||
// report error on bad configuration.
|
||||
NOT_IMPLEMENTED_YET();
|
||||
UNREACHABLE();
|
||||
}
|
||||
values.reset();
|
||||
values.append(m_lower);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace opt {
|
|||
vector<inf_eps> m_lower;
|
||||
vector<inf_eps> m_upper;
|
||||
svector<smt::theory_var> m_vars;
|
||||
symbol m_engine;
|
||||
public:
|
||||
optimize_objectives(ast_manager& m): m(m), s(0), m_cancel(false) {}
|
||||
|
||||
|
@ -41,13 +42,17 @@ namespace opt {
|
|||
|
||||
void set_cancel(bool f);
|
||||
|
||||
void set_engine(symbol const& e) { m_engine = e; }
|
||||
|
||||
private:
|
||||
|
||||
lbool basic_opt(app_ref_vector& objectives);
|
||||
lbool basic_opt();
|
||||
|
||||
lbool farkas_opt();
|
||||
|
||||
void set_max(vector<inf_eps>& dst, vector<inf_eps> const& src);
|
||||
|
||||
lbool update_lower();
|
||||
void update_lower();
|
||||
|
||||
lbool update_upper();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue