3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 09:05:31 +00:00

merged with unstable

This commit is contained in:
Ken McMillan 2013-10-18 17:26:41 -07:00
commit 3a0947b3ba
413 changed files with 31618 additions and 17204 deletions

View file

@ -408,7 +408,7 @@ void asserted_formulas::apply_quasi_macros() {
TRACE("before_quasi_macros", display(tout););
expr_ref_vector new_exprs(m_manager);
proof_ref_vector new_prs(m_manager);
quasi_macros proc(m_manager, m_macro_manager, *m_bsimp, m_simplifier);
quasi_macros proc(m_manager, m_macro_manager, m_simplifier);
while (proc(m_asserted_formulas.size() - m_asserted_qhead,
m_asserted_formulas.c_ptr() + m_asserted_qhead,
m_asserted_formula_prs.c_ptr() + m_asserted_qhead,
@ -653,7 +653,7 @@ void asserted_formulas::propagate_values() {
// will be (silently) eliminated, and models produced by Z3 will not contain them.
flush_cache();
}
TRACE("propagate_values", tout << "afer:\n"; display(tout););
TRACE("propagate_values", tout << "after:\n"; display(tout););
}
void asserted_formulas::propagate_booleans() {

View file

@ -1,4 +1,4 @@
char const * g_pattern_database =
static char const * g_pattern_database =
"(benchmark patterns \n"
" :status unknown \n"
" :logic ALL \n"

View file

@ -311,4 +311,4 @@
(= (?is (?select (?select (?asElems e) a) i)
(?elemtype (?typeof a))) 1)
:pats { (?select (?select (?asElems e) a) i) })
)
)

View file

@ -21,8 +21,10 @@ Revision History:
#include"vector.h"
#include"heap.h"
#include"statistics.h"
#include"trace.h"
#include"warning.h"
#include"uint_set.h"
typedef int dl_var;
@ -118,7 +120,7 @@ const edge_id null_edge_id = -1;
template<typename Ext>
class dl_graph {
struct statistics {
struct stats {
unsigned m_propagation_cost;
unsigned m_implied_literal_cost;
unsigned m_num_implied_literals;
@ -131,16 +133,16 @@ class dl_graph {
m_num_helpful_implied_literals = 0;
m_num_relax = 0;
}
statistics() { reset(); }
void display(std::ostream& out) const {
out << "num. prop. steps. " << m_propagation_cost << "\n";
out << "num. impl. steps. " << m_implied_literal_cost << "\n";
out << "num. impl. lits. " << m_num_implied_literals << "\n";
out << "num. impl. conf lits. " << m_num_helpful_implied_literals << "\n";
out << "num. bound relax. " << m_num_relax << "\n";
stats() { reset(); }
void collect_statistics(::statistics& st) const {
st.update("dl prop steps", m_propagation_cost);
st.update("dl impl steps", m_implied_literal_cost);
st.update("dl impl lits", m_num_implied_literals);
st.update("dl impl conf lits", m_num_helpful_implied_literals);
st.update("dl bound relax", m_num_relax);
}
};
statistics m_stats;
stats m_stats;
typedef typename Ext::numeral numeral;
typedef typename Ext::explanation explanation;
typedef vector<numeral> assignment;
@ -264,7 +266,6 @@ class dl_graph {
m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight();
}
public:
// An assignment is feasible if all edges are feasible.
bool is_feasible() const {
@ -306,14 +307,6 @@ private:
return true;
}
// Update the assignment of variable v, that is,
// m_assignment[v] += inc
// This method also stores the old value of v in the assignment stack.
void acc_assignment(dl_var v, const numeral & inc) {
TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";);
m_assignment_stack.push_back(assignment_trail(v, m_assignment[v]));
m_assignment[v] += inc;
}
// Restore the assignment using the information in m_assignment_stack.
// This method is called when make_feasible fails.
@ -472,8 +465,9 @@ public:
m_bw(m_mark) {
}
void display_statistics(std::ostream& out) const {
m_stats.display(out);
void collect_statistics(::statistics& st) const {
m_stats.collect_statistics(st);
}
// Create/Initialize a variable with the given id.
@ -655,10 +649,8 @@ public:
throw default_exception("edges are not inconsistent");
}
#if 1
// experimental feature:
// allow theory to introduce shortcut lemmas.
prune_edges(edges, f);
#endif
for (unsigned i = 0; i < edges.size(); ++i) {
edge const& e = m_edges[edges[i]];
@ -752,7 +744,6 @@ public:
f.new_edge(src, dst, idx2-idx1+1, edges.begin()+idx1);
}
// Create a new scope.
// That is, save the number of edges in the graph.
void push() {
@ -829,6 +820,16 @@ public:
}
}
// Update the assignment of variable v, that is,
// m_assignment[v] += inc
// This method also stores the old value of v in the assignment stack.
void acc_assignment(dl_var v, const numeral & inc) {
TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";);
m_assignment_stack.push_back(assignment_trail(v, m_assignment[v]));
m_assignment[v] += inc;
}
struct every_var_proc {
bool operator()(dl_var v) const {
return true;
@ -839,6 +840,36 @@ public:
display_core(out, every_var_proc());
}
void display_agl(std::ostream & out) const {
uint_set vars;
typename edges::const_iterator it = m_edges.begin();
typename edges::const_iterator end = m_edges.end();
for (; it != end; ++it) {
edge const& e = *it;
if (e.is_enabled()) {
vars.insert(e.get_source());
vars.insert(e.get_target());
}
}
out << "digraph "" {\n";
unsigned n = m_assignment.size();
for (unsigned v = 0; v < n; v++) {
if (vars.contains(v)) {
out << "\"" << v << "\" [label=\"" << v << ":" << m_assignment[v] << "\"]\n";
}
}
it = m_edges.begin();
for (; it != end; ++it) {
edge const& e = *it;
if (e.is_enabled()) {
out << "\"" << e.get_source() << "\"->\"" << e.get_target() << "\"[label=\"" << e.get_weight() << "\"]\n";
}
}
out << "}\n";
}
template<typename FilterAssignmentProc>
void display_core(std::ostream & out, FilterAssignmentProc p) const {
display_edges(out);
@ -1002,6 +1033,38 @@ public:
}
}
void compute_zero_succ(dl_var v, int_vector& succ) {
unsigned n = m_assignment.size();
m_dfs_time.reset();
m_dfs_time.resize(n, -1);
m_dfs_time[v] = 0;
succ.push_back(v);
numeral gamma;
for (unsigned i = 0; i < succ.size(); ++i) {
v = succ[i];
edge_id_vector & edges = m_out_edges[v];
typename edge_id_vector::iterator it = edges.begin();
typename edge_id_vector::iterator end = edges.end();
for (; it != end; ++it) {
edge_id e_id = *it;
edge & e = m_edges[e_id];
if (!e.is_enabled()) {
continue;
}
SASSERT(e.get_source() == v);
set_gamma(e, gamma);
if (gamma.is_zero()) {
dl_var target = e.get_target();
if (m_dfs_time[target] == -1) {
succ.push_back(target);
m_dfs_time[target] = 0;
}
}
}
}
}
numeral get_assignment(dl_var v) const {
return m_assignment[v];
}
@ -1643,7 +1706,3 @@ public:
#endif /* _DIFF_LOGIC_H_ */
#if 0
#endif

View file

@ -311,7 +311,7 @@ bool expr_context_simplifier::is_false(expr* e) const {
//
expr_strong_context_simplifier::expr_strong_context_simplifier(smt_params& p, ast_manager& m):
m_manager(m), m_params(p), m_arith(m), m_id(0), m_fn(0,m), m_solver(m, p) {
m_manager(m), m_arith(m), m_fn(0,m), m_solver(m, p) {
sort* i_sort = m_arith.mk_int();
m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort());
}

View file

@ -57,9 +57,7 @@ private:
class expr_strong_context_simplifier {
ast_manager& m_manager;
smt_params & m_params;
arith_util m_arith;
unsigned m_id;
func_decl_ref m_fn;
smt::kernel m_solver;

View file

@ -1849,11 +1849,9 @@ namespace smt {
unsigned m_curr_max_generation; // temporary var used to store a copy of m_max_generation
unsigned m_num_args;
unsigned m_oreg;
unsigned m_ireg;
enode * m_n1;
enode * m_n2;
enode * m_app;
instruction * m_alt;
const bind * m_b;
ptr_vector<enode> m_used_enodes;
unsigned m_curr_used_enodes_size;

View file

@ -29,6 +29,7 @@ def_module_params(module_name='smt',
('qi.cost', STRING, '(+ weight generation)', 'expression specifying what is the cost of a given quantifier instantiation'),
('qi.max_multi_patterns', UINT, 0, 'specify the number of extra multi patterns'),
('bv.reflect', BOOL, True, 'create enode for every bit-vector term'),
('bv.enable_int2bv', BOOL, False, 'enable support for int2bv and bv2int operators'),
('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'),
('arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination'),
('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation'),

View file

@ -26,7 +26,9 @@ enum arith_solver_id {
AS_NO_ARITH,
AS_DIFF_LOGIC,
AS_ARITH,
AS_DENSE_DIFF_LOGIC
AS_DENSE_DIFF_LOGIC,
AS_UTVPI,
AS_HORN
};
enum bound_prop_mode {

View file

@ -22,4 +22,5 @@ Revision History:
void theory_bv_params::updt_params(params_ref const & _p) {
smt_params_helper p(_p);
m_bv_reflect = p.bv_reflect();
m_bv_enable_int2bv2int = p.bv_enable_int2bv();
}

View file

@ -67,6 +67,7 @@ expr * datatype_factory::get_almost_fresh_value(sort * s) {
value_set * set = get_value_set(s);
if (set->empty()) {
expr * val = get_some_value(s);
SASSERT(val);
if (m_util.is_recursive(s))
m_last_fresh_value.insert(s, val);
return val;
@ -185,10 +186,16 @@ expr * datatype_factory::get_fresh_value(sort * s) {
if (!found_sibling && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) {
found_sibling = true;
expr * maybe_new_arg = get_almost_fresh_value(s_arg);
if (!maybe_new_arg) {
maybe_new_arg = m_model.get_some_value(s_arg);
found_sibling = false;
}
SASSERT(maybe_new_arg);
args.push_back(maybe_new_arg);
}
else {
expr * some_arg = m_model.get_some_value(s_arg);
SASSERT(some_arg);
args.push_back(some_arg);
}
}

View file

@ -180,10 +180,22 @@ public:
value_set * set = get_value_set(s);
bool is_new = false;
expr * result = 0;
sort_info* s_info = s->get_info();
sort_size const* sz = s_info?&s_info->get_num_elements():0;
bool has_max = false;
Number max_size;
if (sz && sz->is_finite() && sz->size() < UINT_MAX) {
unsigned usz = static_cast<unsigned>(sz->size());
max_size = Number(usz);
has_max = true;
}
Number & next = set->m_next;
while (!is_new) {
result = mk_value(next, s, is_new);
next++;
if (has_max && next > max_size + set->m_next) {
return 0;
}
}
SASSERT(result != 0);
return result;

View file

@ -94,7 +94,7 @@ namespace smt {
}
obj_map<expr, unsigned> const & get_elems() const { return m_elems; }
void insert(expr * n, unsigned generation) {
if (m_elems.contains(n))
return;
@ -102,6 +102,14 @@ namespace smt {
m_elems.insert(n, generation);
SASSERT(!m_manager.is_model_value(n));
}
void remove(expr * n) {
// We can only remove n if it is in m_elems, AND m_inv was not initialized yet.
SASSERT(m_elems.contains(n));
SASSERT(m_inv.empty());
m_elems.erase(n);
m_manager.dec_ref(n);
}
void display(std::ostream & out) const {
obj_map<expr, unsigned>::iterator it = m_elems.begin();
@ -525,6 +533,30 @@ namespace smt {
}
}
// For each instantiation_set, reemove entries that do not evaluate to values.
void cleanup_instantiation_sets() {
ptr_vector<expr> to_delete;
ptr_vector<node>::const_iterator it = m_nodes.begin();
ptr_vector<node>::const_iterator end = m_nodes.end();
for (; it != end; ++it) {
node * curr = *it;
if (curr->is_root()) {
instantiation_set * s = curr->get_instantiation_set();
to_delete.reset();
obj_map<expr, unsigned> const & elems = s->get_elems();
for (obj_map<expr, unsigned>::iterator it = elems.begin(); it != elems.end(); it++) {
expr * n = it->m_key;
expr * n_val = eval(n, true);
if (!m_manager.is_value(n_val))
to_delete.push_back(n);
}
for (ptr_vector<expr>::iterator it = to_delete.begin(); it != to_delete.end(); it++) {
s->remove(*it);
}
}
}
}
void display_nodes(std::ostream & out) const {
display_key2node(out, m_uvars);
display_A_f_is(out);
@ -545,6 +577,7 @@ namespace smt {
r = 0;
else
r = tmp;
TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n----->\n" << mk_pp(r, m_manager) << "\n";);
m_eval_cache.insert(n, r);
m_eval_cache_range.push_back(r);
return r;
@ -1047,6 +1080,7 @@ namespace smt {
public:
void fix_model(expr_ref_vector & new_constraints) {
cleanup_instantiation_sets();
m_new_constraints = &new_constraints;
func_decl_set partial_funcs;
collect_partial_funcs(partial_funcs);
@ -1535,8 +1569,23 @@ namespace smt {
n1->insert_exception(m_t);
}
virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) {
// do nothing...
virtual void populate_inst_sets(quantifier * q, auf_solver & slv, context * ctx) {
unsigned num_vars = q->get_num_decls();
ast_manager & m = ctx->get_manager();
sort * s = q->get_decl_sort(num_vars - m_var_i - 1);
if (m.is_uninterp(s)) {
// For uninterpreted sorst, we add all terms in the context.
// See Section 4.1 in the paper "Complete Quantifier Instantiation"
node * S_q_i = slv.get_uvar(q, m_var_i);
ptr_vector<enode>::const_iterator it = ctx->begin_enodes();
ptr_vector<enode>::const_iterator end = ctx->end_enodes();
for (; it != end; ++it) {
enode * n = *it;
if (ctx->is_relevant(n) && get_sort(n->get_owner()) == s) {
S_q_i->insert(n->get_owner(), n->get_generation());
}
}
}
}
};
@ -1924,7 +1973,8 @@ namespace smt {
m_mutil.mk_add(t1, t2, r);
}
bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) const {
bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t, bool & inv) const {
inv = false; // true if invert the sign
TRACE("is_var_and_ground", tout << "is_var_and_ground: " << mk_ismt2_pp(lhs, m_manager) << " " << mk_ismt2_pp(rhs, m_manager) << "\n";);
if (is_var(lhs) && is_ground(rhs)) {
v = to_var(lhs);
@ -1939,7 +1989,6 @@ namespace smt {
return true;
}
else {
bool inv = false; // true if invert the sign
expr_ref tmp(m_manager);
if (is_var_plus_ground(lhs, inv, v, tmp) && is_ground(rhs)) {
if (inv)
@ -1959,6 +2008,11 @@ namespace smt {
return false;
}
bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) const {
bool inv;
return is_var_and_ground(lhs, rhs, v, t, inv);
}
bool is_x_eq_t_atom(expr * n, var * & v, expr_ref & t) const {
if (!is_app(n))
return false;
@ -2011,22 +2065,28 @@ namespace smt {
if (sign) {
bool r = is_le_ge(atom) && is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, t);
CTRACE("is_x_gle_t", r, tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n"
<< mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n";);
<< mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n";
tout << "sign: " << sign << "\n";);
return r;
}
else {
if (is_le_ge(atom)) {
expr_ref tmp(m_manager);
if (is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, tmp)) {
bool le = is_le(atom);
bool inv = false;
if (is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, tmp, inv)) {
if (inv)
le = !le;
sort * s = m_manager.get_sort(tmp);
expr_ref one(m_manager);
one = mk_one(s);
if (is_le(atom))
if (le)
mk_add(tmp, one, t);
else
mk_sub(tmp, one, t);
TRACE("is_x_gle_t", tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n"
<< mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n";);
<< mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n";
tout << "sign: " << sign << "\n";);
return true;
}
}

View file

@ -22,6 +22,8 @@ Revision History:
#include"theory_arith.h"
#include"theory_dense_diff_logic.h"
#include"theory_diff_logic.h"
#include"theory_horn_ineq.h"
#include"theory_utvpi.h"
#include"theory_array.h"
#include"theory_array_full.h"
#include"theory_bv.h"
@ -723,6 +725,18 @@ namespace smt {
m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params));
}
break;
case AS_HORN:
if (m_params.m_arith_int_only)
m_context.register_plugin(alloc(smt::theory_ihi, m_manager));
else
m_context.register_plugin(alloc(smt::theory_rhi, m_manager));
break;
case AS_UTVPI:
if (m_params.m_arith_int_only)
m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager));
else
m_context.register_plugin(alloc(smt::theory_rutvpi, m_manager));
break;
default:
if (m_params.m_arith_int_only)
m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params));

View file

@ -23,7 +23,7 @@ Notes:
#include"smt_kernel.h"
#include"ast_pp.h"
#include"mk_simplified_app.h"
#include"ast_util.h"
class ctx_solver_simplify_tactic : public tactic {
ast_manager& m;
@ -33,10 +33,14 @@ class ctx_solver_simplify_tactic : public tactic {
arith_util m_arith;
mk_simplified_app m_mk_app;
func_decl_ref m_fn;
obj_map<sort, func_decl*> m_fns;
unsigned m_num_steps;
volatile bool m_cancel;
public:
ctx_solver_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()):
m(m), m_params(p), m_solver(m, m_front_p), m_arith(m), m_mk_app(m), m_fn(m), m_num_steps(0) {
m(m), m_params(p), m_solver(m, m_front_p),
m_arith(m), m_mk_app(m), m_fn(m), m_num_steps(0),
m_cancel(false) {
sort* i_sort = m_arith.mk_int();
m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort());
}
@ -45,7 +49,13 @@ public:
return alloc(ctx_solver_simplify_tactic, m, m_params);
}
virtual ~ctx_solver_simplify_tactic() {}
virtual ~ctx_solver_simplify_tactic() {
obj_map<sort, func_decl*>::iterator it = m_fns.begin(), end = m_fns.end();
for (; it != end; ++it) {
m.dec_ref(it->m_value);
}
m_fns.reset();
}
virtual void updt_params(params_ref const & p) {
m_solver.updt_params(p);
@ -76,23 +86,53 @@ public:
virtual void cleanup() {
reset_statistics();
m_solver.reset();
m_cancel = false;
}
protected:
virtual void set_cancel(bool f) {
m_solver.set_cancel(f);
m_cancel = false;
}
void reduce(goal& g) {
SASSERT(g.is_well_sorted());
m_num_steps = 0;
expr_ref fml(m);
tactic_report report("ctx-solver-simplify", g);
if (g.inconsistent())
return;
ptr_vector<expr> fmls;
g.get_formulas(fmls);
fml = m.mk_and(fmls.size(), fmls.c_ptr());
fml = mk_and(m, fmls.size(), fmls.c_ptr());
m_solver.push();
reduce(fml);
m_solver.pop(1);
SASSERT(m_solver.get_scope_level() == 0);
TRACE("ctx_solver_simplify_tactic",
for (unsigned i = 0; i < fmls.size(); ++i) {
tout << mk_pp(fmls[i], m) << "\n";
}
tout << "=>\n";
tout << mk_pp(fml, m) << "\n";);
DEBUG_CODE(
{
m_solver.push();
expr_ref fml1(m);
fml1 = mk_and(m, fmls.size(), fmls.c_ptr());
fml1 = m.mk_iff(fml, fml1);
fml1 = m.mk_not(fml1);
m_solver.assert_expr(fml1);
lbool is_sat = m_solver.check();
TRACE("ctx_solver_simplify_tactic", tout << "is non-equivalence sat?: " << is_sat << "\n";);
if (is_sat != l_false) {
TRACE("ctx_solver_simplify_tactic",
tout << "result is not equivalent to input\n";
tout << mk_pp(fml1, m) << "\n";);
UNREACHABLE();
}
m_solver.pop(1);
});
g.reset();
g.assert_expr(fml, 0, 0);
IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-solver-simplify :num-steps " << m_num_steps << ")\n";);
@ -106,21 +146,23 @@ protected:
svector<bool> is_checked;
svector<unsigned> parent_ids, self_ids;
expr_ref_vector fresh_vars(m), trail(m);
expr_ref res(m);
obj_map<expr,std::pair<unsigned, expr*> > cache;
expr_ref res(m), tmp(m);
obj_map<expr,std::pair<unsigned, expr*> > cache;
unsigned id = 1;
expr* n = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true));
expr* n2, *fml;
expr_ref n2(m), fml(m);
unsigned path_id = 0, self_pos = 0;
app * a;
unsigned sz;
std::pair<unsigned,expr*> path_r;
ptr_vector<expr> found;
expr_ref_vector args(m);
expr_ref n = mk_fresh(id, m.mk_bool_sort());
trail.push_back(n);
fml = result.get();
m_solver.assert_expr(m.mk_not(m.mk_iff(fml, n)));
tmp = m.mk_not(m.mk_iff(fml, n));
m_solver.assert_expr(tmp);
trail.push_back(n);
todo.push_back(fml);
names.push_back(n);
is_checked.push_back(false);
@ -128,9 +170,9 @@ protected:
self_ids.push_back(0);
m_solver.push();
while (!todo.empty()) {
while (!todo.empty() && !m_cancel) {
expr_ref res(m);
ptr_buffer<expr> args;
args.reset();
expr* e = todo.back();
unsigned pos = parent_ids.back();
n = names.back();
@ -139,11 +181,8 @@ protected:
if (cache.contains(e)) {
goto done;
}
if (!m.is_bool(e)) {
res = e;
goto done;
}
if (m.is_bool(e) && !checked && simplify_bool(n, res)) {
TRACE("ctx_solver_simplify_tactic", tout << "simplified: " << mk_pp(e, m) << " |-> " << mk_pp(res, m) << "\n";);
goto done;
}
if (!is_app(e)) {
@ -164,35 +203,35 @@ protected:
found.reset(); // arguments already simplified.
for (unsigned i = 0; i < sz; ++i) {
expr* arg = a->get_arg(i);
if (!m.is_bool(arg)) {
args.push_back(arg);
}
else if (cache.find(arg, path_r) && !found.contains(arg)) {
if (cache.find(arg, path_r) && !found.contains(arg)) {
//
// This is a single traversal version of the context
// simplifier. It simplifies only the first occurrence of
// a formula with respect to the context.
// a sub-term with respect to the context.
//
found.push_back(arg);
if (path_r.first == self_pos) {
TRACE("ctx_solver_simplify_tactic", tout << "cached " << mk_pp(arg, m) << "\n";);
TRACE("ctx_solver_simplify_tactic", tout << "cached " << mk_pp(arg, m) << " |-> " << mk_pp(path_r.second, m) << "\n";);
args.push_back(path_r.second);
}
else {
else if (m.is_bool(arg)) {
res = local_simplify(a, n, id, i);
TRACE("ctx_solver_simplify_tactic",
tout << "Already cached: " << path_r.first << " " << mk_pp(res, m) << "\n";);
tout << "Already cached: " << path_r.first << " " << mk_pp(arg, m) << " |-> " << mk_pp(res, m) << "\n";);
args.push_back(res);
}
else {
args.push_back(arg);
}
}
else if (!n2 && !found.contains(arg)) {
n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true));
n2 = mk_fresh(id, m.get_sort(arg));
trail.push_back(n2);
todo.push_back(arg);
parent_ids.push_back(self_pos);
self_ids.push_back(0);
names.push_back(n2);
trail.push_back(n2);
args.push_back(n2);
is_checked.push_back(false);
}
@ -205,7 +244,8 @@ protected:
// child needs to be visited.
if (n2) {
m_solver.push();
m_solver.assert_expr(m.mk_eq(res, n));
tmp = m.mk_eq(res, n);
m_solver.assert_expr(tmp);
continue;
}
@ -224,12 +264,14 @@ protected:
is_checked.pop_back();
m_solver.pop(1);
}
VERIFY(cache.find(fml, path_r));
result = path_r.second;
if (!m_cancel) {
VERIFY(cache.find(fml, path_r));
result = path_r.second;
}
}
bool simplify_bool(expr* n, expr_ref& res) {
expr_ref tmp(m);
m_solver.push();
m_solver.assert_expr(n);
lbool is_sat = m_solver.check();
@ -240,7 +282,8 @@ protected:
}
m_solver.push();
m_solver.assert_expr(m.mk_not(n));
tmp = m.mk_not(n);
m_solver.assert_expr(tmp);
is_sat = m_solver.check();
m_solver.pop(1);
if (is_sat == l_false) {
@ -251,11 +294,25 @@ protected:
return false;
}
expr_ref mk_fresh(unsigned& id, sort* s) {
func_decl* fn;
if (m.is_bool(s)) {
fn = m_fn;
}
else if (!m_fns.find(s, fn)) {
fn = m.mk_func_decl(symbol(0xbeef101 + id), m_arith.mk_int(), s);
m.inc_ref(fn);
m_fns.insert(s, fn);
}
return expr_ref(m.mk_app(fn, m_arith.mk_numeral(rational(id++), true)), m);
}
expr_ref local_simplify(app* a, expr* n, unsigned& id, unsigned index) {
SASSERT(index < a->get_num_args());
SASSERT(m.is_bool(a->get_arg(index)));
expr_ref n2(m), result(m);
n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true));
expr_ref n2(m), result(m), tmp(m);
n2 = mk_fresh(id, m.get_sort(a->get_arg(index)));
ptr_buffer<expr> args;
for (unsigned i = 0; i < a->get_num_args(); ++i) {
if (i == index) {
@ -267,9 +324,10 @@ protected:
}
m_mk_app(a->get_decl(), args.size(), args.c_ptr(), result);
m_solver.push();
m_solver.assert_expr(m.mk_eq(result, n));
tmp = m.mk_eq(result, n);
m_solver.assert_expr(tmp);
if (!simplify_bool(n2, result)) {
result = a;
result = a->get_arg(index);
}
m_solver.pop(1);
return result;

View file

@ -0,0 +1,151 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
unit_subsumption_tactic.cpp
Abstract:
Simplify goal using subsumption based on unit propagation.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-6
--*/
#include "unit_subsumption_tactic.h"
#include "smt_context.h"
struct unit_subsumption_tactic : public tactic {
ast_manager& m;
params_ref m_params;
smt_params m_fparams;
volatile bool m_cancel;
smt::context m_context;
expr_ref_vector m_clauses;
unsigned m_clause_count;
bit_vector m_is_deleted;
unsigned_vector m_deleted;
unit_subsumption_tactic(
ast_manager& m,
params_ref const& p):
m(m),
m_params(p),
m_cancel(false),
m_context(m, m_fparams, p),
m_clauses(m) {
}
void set_cancel(bool f) {
m_cancel = f;
m_context.set_cancel_flag(f);
}
virtual void cleanup() {
set_cancel(false);
}
virtual void operator()(/* in */ goal_ref const & in,
/* out */ goal_ref_buffer & result,
/* out */ model_converter_ref & mc,
/* out */ proof_converter_ref & pc,
/* out */ expr_dependency_ref & core) {
reduce_core(in, result);
}
virtual void updt_params(params_ref const& p) {
m_params = p;
// m_context.updt_params(p); does not exist.
}
virtual tactic* translate(ast_manager& m) {
return alloc(unit_subsumption_tactic, m, m_params);
}
void checkpoint() {
if (m_cancel) {
throw tactic_exception(TACTIC_CANCELED_MSG);
}
}
void reduce_core(goal_ref const& g, goal_ref_buffer& result) {
init(g);
m_context.push();
assert_clauses(g);
m_context.push(); // internalize assertions.
prune_clauses();
goal_ref r(g);
insert_result(r);
r->elim_true();
result.push_back(r.get());
m_context.pop(2);
TRACE("unit_subsumption_tactic", g->display(tout); r->display(tout););
}
void assert_clauses(goal_ref const& g) {
for (unsigned i = 0; i < g->size(); ++i) {
m_context.assert_expr(m.mk_iff(new_clause(), g->form(i)));
}
}
void prune_clauses() {
for (unsigned i = 0; i < m_clause_count; ++i) {
prune_clause(i);
}
}
void prune_clause(unsigned i) {
m_context.push();
for (unsigned j = 0; j < m_clause_count; ++j) {
if (i == j) {
m_context.assert_expr(m.mk_not(m_clauses[j].get()));
}
else if (!m_is_deleted.get(j)) {
m_context.assert_expr(m_clauses[j].get());
}
}
m_context.push(); // force propagation
bool is_unsat = m_context.inconsistent();
m_context.pop(2);
if (is_unsat) {
TRACE("unit_subsumption_tactic", tout << "Removing clause " << i << "\n";);
m_is_deleted.set(i, true);
m_deleted.push_back(i);
}
}
void insert_result(goal_ref& result) {
for (unsigned i = 0; i < m_deleted.size(); ++i) {
result->update(m_deleted[i], m.mk_true()); // TBD proof?
}
}
void init(goal_ref const& g) {
m_clause_count = 0;
m_is_deleted.reset();
m_is_deleted.resize(g->size());
m_deleted.reset();
}
expr* new_bool(unsigned& count, expr_ref_vector& v, char const* name) {
SASSERT(count <= v.size());
if (count == v.size()) {
v.push_back(m.mk_fresh_const(name, m.mk_bool_sort()));
}
return v[count++].get();
}
expr* new_clause() {
return new_bool(m_clause_count, m_clauses, "#clause");
}
};
tactic * mk_unit_subsumption_tactic(ast_manager & m, params_ref const & p) {
return alloc(unit_subsumption_tactic, m, p);
}

View file

@ -0,0 +1,33 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
unit_subsumption_tactic.h
Abstract:
Simplify goal using subsumption based on unit propagation.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-6
Notes:
Background: PDR generates several clauses that subsume each-other.
Simplify a goal assuming it is a conjunction of clauses.
Subsumed clauses are simplified by using unit-propagation
It uses the smt_context for the solver.
--*/
#ifndef _UNIT_SUBSUMPTION_TACTIC_H_
#define _UNIT_SUBSUMPTION_TACTIC_H_
#include "tactic.h"
tactic * mk_unit_subsumption_tactic(ast_manager & m, params_ref const & p = params_ref());
/*
ADD_TACTIC("unit-subsume-simplify", "unit subsumption simplification.", "mk_unit_subsumption_tactic(m, p)")
*/
#endif

View file

@ -408,7 +408,7 @@ namespace smt {
mk_axiom(eqz, upper);
rational k;
if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) &&
k.is_pos() && k < rational(512)) {
k.is_pos() && k < rational(8)) {
rational j(0);
#if 1
literal_buffer lits;

View file

@ -688,7 +688,7 @@ namespace smt {
}
}
}
void theory_array_base::propagate_select_to_store_parents(enode * r, enode * sel, svector<enode_pair> & todo) {
SASSERT(r->get_root() == r);
SASSERT(is_select(sel));
@ -880,7 +880,7 @@ namespace smt {
}
else {
theory_var r = mg_find(v);
void * else_val = m_else_values[r];
void * else_val = m_else_values[r];
// DISABLED. It seems wrong, since different nodes can share the same
// else_val according to the mg class.
// SASSERT(else_val == 0 || get_context().is_relevant(UNTAG(app*, else_val)));

View file

@ -46,7 +46,6 @@ namespace smt {
unsigned m_num_conflicts;
unsigned m_num_assertions;
unsigned m_num_th2core_eqs;
unsigned m_num_th2core_prop;
unsigned m_num_core2th_eqs;
unsigned m_num_core2th_diseqs;
@ -59,109 +58,30 @@ namespace smt {
}
};
class dl_conflict : public simple_justification {
public:
dl_conflict(region & r, unsigned nls, literal const * lits): simple_justification(r, nls, lits) { }
virtual proof * mk_proof(conflict_resolution & cr) {
NOT_IMPLEMENTED_YET();
return 0;
}
};
template<typename Ext>
class theory_diff_logic : public theory, private Ext {
typedef typename Ext::numeral numeral;
class implied_eq_justification : public justification {
theory_diff_logic & m_theory;
theory_var m_v1;
theory_var m_v2;
unsigned m_timestamp;
public:
implied_eq_justification(theory_diff_logic & theory, theory_var v1, theory_var v2, unsigned ts):
m_theory(theory),
m_v1(v1),
m_v2(v2),
m_timestamp(ts) {
}
virtual void get_antecedents(conflict_resolution & cr) {
m_theory.get_eq_antecedents(m_v1, m_v2, m_timestamp, cr);
}
virtual proof * mk_proof(conflict_resolution & cr) { NOT_IMPLEMENTED_YET(); return 0; }
};
class implied_bound_justification : public justification {
theory_diff_logic& m_theory;
edge_id m_subsumed_edge;
edge_id m_bridge_edge;
public:
implied_bound_justification(theory_diff_logic & theory, edge_id se, edge_id be):
m_theory(theory),
m_subsumed_edge(se),
m_bridge_edge(be) {
}
virtual void get_antecedents(conflict_resolution & cr) {
m_theory.get_implied_bound_antecedents(m_bridge_edge, m_subsumed_edge, cr);
}
virtual proof * mk_proof(conflict_resolution & cr) { NOT_IMPLEMENTED_YET(); return 0; }
};
enum atom_kind {
LE_ATOM,
EQ_ATOM
};
class atom {
protected:
atom_kind m_kind;
bool_var m_bvar;
bool m_true;
int m_pos;
int m_neg;
public:
atom(atom_kind k, bool_var bv) : m_kind(k), m_bvar(bv), m_true(false) {}
virtual ~atom() {}
atom_kind kind() const { return m_kind; }
bool_var get_bool_var() const { return m_bvar; }
bool is_true() const { return m_true; }
void assign_eh(bool is_true) { m_true = is_true; }
virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const;
};
class le_atom : public atom {
int m_pos;
int m_neg;
public:
le_atom(bool_var bv, int pos, int neg):
atom(LE_ATOM, bv),
atom(bool_var bv, int pos, int neg):
m_bvar(bv), m_true(false),
m_pos(pos),
m_neg(neg) {
}
virtual ~le_atom() {}
~atom() {}
bool_var get_bool_var() const { return m_bvar; }
bool is_true() const { return m_true; }
void assign_eh(bool is_true) { m_true = is_true; }
int get_asserted_edge() const { return this->m_true?m_pos:m_neg; }
int get_pos() const { return m_pos; }
int get_neg() const { return m_neg; }
virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const;
};
class eq_atom : public atom {
app_ref m_le;
app_ref m_ge;
public:
eq_atom(bool_var bv, app_ref& le, app_ref& ge):
atom(EQ_ATOM, bv),
m_le(le),
m_ge(ge)
{}
virtual ~eq_atom() {}
virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const;
app* get_le() const { return m_le.get(); }
app* get_ge() const { return m_ge.get(); }
std::ostream& display(theory_diff_logic const& th, std::ostream& out) const;
};
typedef ptr_vector<atom> atoms;
@ -239,19 +159,7 @@ namespace smt {
unsigned m_asserted_qhead_old;
};
class theory_diff_logic_del_eh : public clause_del_eh {
theory_diff_logic& m_super;
public:
theory_diff_logic_del_eh(theory_diff_logic& s) : m_super(s) {}
virtual ~theory_diff_logic_del_eh() {}
virtual void operator()(ast_manager&, clause* cls) {
TRACE("dl_activity", tout << "deleting " << cls << "\n";);
m_super.del_clause_eh(cls);
dealloc(this);
}
};
smt_params & m_params;
smt_params & m_params;
arith_util m_util;
arith_eq_adapter m_arith_eq_adapter;
theory_diff_logic_statistics m_stats;
@ -259,8 +167,6 @@ namespace smt {
theory_var m_zero_int; // cache the variable representing the zero variable.
theory_var m_zero_real; // cache the variable representing the zero variable.
int_vector m_scc_id; // Cheap equality propagation
bool m_modified_since_eq_prop; // true if new constraints were asserted
// since last eq propagation.
eq_prop_info_set m_eq_prop_info_set; // set of existing equality prop infos
ptr_vector<eq_prop_info> m_eq_prop_infos;
@ -289,20 +195,14 @@ namespace smt {
virtual theory_var mk_var(enode* n);
virtual theory_var mk_var(app* n);
void mark_as_modified_since_eq_prop();
void unmark_as_modified_since_eq_prop();
bool propagate_cheap_equalities();
void compute_delta();
void found_non_diff_logic_expr(expr * n);
bool is_interpreted(app* n) const;
void del_clause_eh(clause* cls);
bool is_interpreted(app* n) const {
return get_family_id() == n->get_family_id();
}
public:
theory_diff_logic(ast_manager& m, smt_params & params):
@ -312,7 +212,6 @@ namespace smt {
m_arith_eq_adapter(*this, params, m_util),
m_zero_int(null_theory_var),
m_zero_real(null_theory_var),
m_modified_since_eq_prop(false),
m_asserted_qhead(0),
m_num_core_conflicts(0),
m_num_propagation_calls(0),
@ -323,7 +222,7 @@ namespace smt {
m_nc_functor(*this) {
}
~theory_diff_logic() {
virtual ~theory_diff_logic() {
reset_eh();
}
@ -360,7 +259,7 @@ namespace smt {
m_arith_eq_adapter.restart_eh();
}
virtual void relevant_eh(app* e);
virtual void relevant_eh(app* e) {}
virtual void init_search_eh() {
m_arith_eq_adapter.init_search_eh();

View file

@ -31,34 +31,15 @@ Revision History:
using namespace smt;
template<typename Ext>
std::ostream& theory_diff_logic<Ext>::atom::display(theory_diff_logic const& th, std::ostream& out) const {
std::ostream& theory_diff_logic<Ext>::atom::display(theory_diff_logic const& th, std::ostream& out) const {
context& ctx = th.get_context();
lbool asgn = ctx.get_assignment(m_bvar);
//SASSERT(asgn == l_undef || ((asgn == l_true) == m_true));
bool sign = (l_undef == asgn) || m_true;
return out << literal(m_bvar, sign)
<< " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " ";
}
template<typename Ext>
std::ostream& theory_diff_logic<Ext>::eq_atom::display(theory_diff_logic const& th, std::ostream& out) const {
atom::display(th, out);
lbool asgn = th.get_context().get_assignment(this->m_bvar);
if (l_undef == asgn) {
out << "unassigned\n";
}
else {
out << mk_pp(m_le.get(), m_le.get_manager()) << " "
<< mk_pp(m_ge.get(), m_ge.get_manager()) << "\n";
}
return out;
}
template<typename Ext>
std::ostream& theory_diff_logic<Ext>::le_atom::display(theory_diff_logic const& th, std::ostream& out) const {
atom::display(th, out);
lbool asgn = th.get_context().get_assignment(this->m_bvar);
if (l_undef == asgn) {
out << "unassigned\n";
}
@ -94,7 +75,6 @@ void theory_diff_logic<Ext>::init(context * ctx) {
e = ctx->mk_enode(zero, false, false, true);
SASSERT(!is_attached_to_var(e));
m_zero_real = mk_var(e);
}
@ -277,7 +257,7 @@ bool theory_diff_logic<Ext>::internalize_atom(app * n, bool gate_ctx) {
k -= this->m_epsilon;
}
edge_id neg = m_graph.add_edge(target, source, k, ~l);
le_atom * a = alloc(le_atom, bv, pos, neg);
atom * a = alloc(atom, bv, pos, neg);
m_atoms.push_back(a);
m_bool_var2atom.insert(bv, a);
@ -318,7 +298,7 @@ template<typename Ext>
void theory_diff_logic<Ext>::assign_eh(bool_var v, bool is_true) {
m_stats.m_num_assertions++;
atom * a = 0;
m_bool_var2atom.find(v, a);
VERIFY (m_bool_var2atom.find(v, a));
SASSERT(a);
SASSERT(get_context().get_assignment(v) != l_undef);
SASSERT((get_context().get_assignment(v) == l_true) == is_true);
@ -330,10 +310,11 @@ void theory_diff_logic<Ext>::assign_eh(bool_var v, bool is_true) {
template<typename Ext>
void theory_diff_logic<Ext>::collect_statistics(::statistics & st) const {
st.update("dl conflicts", m_stats.m_num_conflicts);
st.update("dl propagations", m_stats.m_num_th2core_prop);
st.update("dl asserts", m_stats.m_num_assertions);
st.update("core->dl eqs", m_stats.m_num_core2th_eqs);
st.update("core->dl diseqs", m_stats.m_num_core2th_diseqs);
m_arith_eq_adapter.collect_statistics(st);
m_graph.collect_statistics(st);
}
template<typename Ext>
@ -374,15 +355,6 @@ final_check_status theory_diff_logic<Ext>::final_check_eh() {
// either will already be zero (as we don't do mixed constraints).
m_graph.set_to_zero(m_zero_int, m_zero_real);
SASSERT(is_consistent());
#if 0
TBD:
if (propagate_cheap_equalities()) {
return FC_CONTINUE;
}
#endif
if (m_non_diff_logic_exprs) {
return FC_GIVEUP;
}
@ -506,61 +478,14 @@ bool theory_diff_logic<Ext>::propagate_atom(atom* a) {
if (ctx.inconsistent()) {
return false;
}
switch(a->kind()) {
case LE_ATOM: {
int edge_id = dynamic_cast<le_atom*>(a)->get_asserted_edge();
if (!m_graph.enable_edge(edge_id)) {
set_neg_cycle_conflict();
return false;
}
#if 0
if (m_params.m_arith_bound_prop != BP_NONE) {
svector<int> subsumed;
m_graph.find_subsumed1(edge_id, subsumed);
for (unsigned i = 0; i < subsumed.size(); ++i) {
int subsumed_edge_id = subsumed[i];
literal l = m_graph.get_explanation(subsumed_edge_id);
context & ctx = get_context();
region& r = ctx.get_region();
++m_stats.m_num_th2core_prop;
ctx.assign(l, new (r) implied_bound_justification(*this, subsumed_edge_id, edge_id));
}
}
#endif
break;
}
case EQ_ATOM:
if (!a->is_true()) {
SASSERT(ctx.get_assignment(a->get_bool_var()) == l_false);
// eq_atom * ea = dynamic_cast<eq_atom*>(a);
}
break;
int edge_id = a->get_asserted_edge();
if (!m_graph.enable_edge(edge_id)) {
set_neg_cycle_conflict();
return false;
}
return true;
}
template<typename Ext>
void theory_diff_logic<Ext>::mark_as_modified_since_eq_prop() {
if (!m_modified_since_eq_prop) {
get_context().push_trail(value_trail<context, bool>(m_modified_since_eq_prop));
m_modified_since_eq_prop = true;
}
}
template<typename Ext>
void theory_diff_logic<Ext>::unmark_as_modified_since_eq_prop() {
get_context().push_trail(value_trail<context, bool>(m_modified_since_eq_prop));
m_modified_since_eq_prop = false;
}
template<typename Ext>
void theory_diff_logic<Ext>::del_clause_eh(clause* cls) {
}
template<typename Ext>
void theory_diff_logic<Ext>::new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {
@ -609,7 +534,7 @@ void theory_diff_logic<Ext>::new_edge(dl_var src, dl_var dst, unsigned num_edges
atom* a = 0;
m_bool_var2atom.find(bv, a);
SASSERT(a);
edge_id e_id = static_cast<le_atom*>(a)->get_pos();
edge_id e_id = a->get_pos();
literal_vector lits;
for (unsigned i = 0; i < num_edges; ++i) {
@ -633,11 +558,7 @@ void theory_diff_logic<Ext>::new_edge(dl_var src, dl_var dst, unsigned num_edges
lits.size(), lits.c_ptr(),
params.size(), params.c_ptr());
}
clause_del_eh* del_eh = alloc(theory_diff_logic_del_eh, *this);
clause* cls = ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh);
if (!cls) {
dealloc(del_eh);
}
ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0);
if (dump_lemmas()) {
char const * logic = m_is_lia ? "QF_LIA" : "QF_LRA";
ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic);
@ -677,7 +598,6 @@ void theory_diff_logic<Ext>::set_neg_cycle_conflict() {
literal_vector const& lits = m_nc_functor.get_lits();
context & ctx = get_context();
TRACE("arith_conflict",
//display(tout);
tout << "conflict: ";
for (unsigned i = 0; i < lits.size(); ++i) {
ctx.display_literal_info(tout, lits[i]);
@ -802,7 +722,6 @@ theory_var theory_diff_logic<Ext>::mk_num(app* n, rational const& r) {
template<typename Ext>
theory_var theory_diff_logic<Ext>::mk_var(enode* n) {
mark_as_modified_since_eq_prop();
theory_var v = theory::mk_var(n);
TRACE("diff_logic_vars", tout << "mk_var: " << v << "\n";);
m_graph.init_var(v);
@ -810,10 +729,6 @@ theory_var theory_diff_logic<Ext>::mk_var(enode* n) {
return v;
}
template<typename Ext>
bool theory_diff_logic<Ext>::is_interpreted(app* n) const {
return n->get_family_id() == get_family_id();
}
template<typename Ext>
theory_var theory_diff_logic<Ext>::mk_var(app* n) {
@ -854,7 +769,6 @@ void theory_diff_logic<Ext>::reset_eh() {
m_asserted_atoms .reset();
m_stats .reset();
m_scopes .reset();
m_modified_since_eq_prop = false;
m_asserted_qhead = 0;
m_num_core_conflicts = 0;
m_num_propagation_calls = 0;
@ -865,70 +779,6 @@ void theory_diff_logic<Ext>::reset_eh() {
}
template<typename Ext>
bool theory_diff_logic<Ext>::propagate_cheap_equalities() {
bool result = false;
TRACE("dl_new_eq", get_context().display(tout););
context& ctx = get_context();
region& reg = ctx.get_region();
SASSERT(m_eq_prop_info_set.empty());
SASSERT(m_eq_prop_infos.empty());
if (m_modified_since_eq_prop) {
m_graph.compute_zero_edge_scc(m_scc_id);
int n = get_num_vars();
for (theory_var v = 0; v < n; v++) {
rational delta_r;
theory_var x_v = expand(true, v, delta_r);
numeral delta(delta_r);
int scc_id = m_scc_id[x_v];
if (scc_id != -1) {
delta += m_graph.get_assignment(x_v);
TRACE("eq_scc", tout << v << " " << x_v << " " << scc_id << " " << delta << "\n";);
eq_prop_info info(scc_id, delta);
typename eq_prop_info_set::entry * entry = m_eq_prop_info_set.find_core(&info);
if (entry == 0) {
eq_prop_info * new_info = alloc(eq_prop_info, scc_id, delta, v);
m_eq_prop_info_set.insert(new_info);
m_eq_prop_infos.push_back(new_info);
}
else {
// new equality found
theory_var r = entry->get_data()->get_root();
enode * n1 = get_enode(v);
enode * n2 = get_enode(r);
if (n1->get_root() != n2->get_root()) {
// r may be an alias (i.e., it is not realy in the graph). So, I should expand it.
// nsb: ??
rational r_delta;
theory_var x_r = expand(true, r, r_delta);
justification* j = new (reg) implied_eq_justification(*this, x_v, x_r, m_graph.get_timestamp());
(void)j;
m_stats.m_num_th2core_eqs++;
// TBD: get equality into core.
NOT_IMPLEMENTED_YET();
// new_eq_eh(x_v, x_r, *j);
result = true;
}
}
}
}
m_eq_prop_info_set.reset();
std::for_each(m_eq_prop_infos.begin(), m_eq_prop_infos.end(), delete_proc<eq_prop_info>());
m_eq_prop_infos.reset();
unmark_as_modified_since_eq_prop();
}
TRACE("dl_new_eq", get_context().display(tout););
SASSERT(!m_modified_since_eq_prop);
return result;
}
template<typename Ext>
void theory_diff_logic<Ext>::compute_delta() {
m_delta = rational(1);
@ -1001,30 +851,9 @@ bool theory_diff_logic<Ext>::is_consistent() const {
lbool asgn = ctx.get_assignment(bv);
if (ctx.is_relevant(ctx.bool_var2expr(bv)) && asgn != l_undef) {
SASSERT((asgn == l_true) == a->is_true());
switch(a->kind()) {
case LE_ATOM: {
le_atom* le = dynamic_cast<le_atom*>(a);
int edge_id = le->get_asserted_edge();
SASSERT(m_graph.is_enabled(edge_id));
SASSERT(m_graph.is_feasible(edge_id));
break;
}
case EQ_ATOM: {
eq_atom* ea = dynamic_cast<eq_atom*>(a);
bool_var bv1 = ctx.get_bool_var(ea->get_le());
bool_var bv2 = ctx.get_bool_var(ea->get_ge());
lbool val1 = ctx.get_assignment(bv1);
lbool val2 = ctx.get_assignment(bv2);
if (asgn == l_true) {
SASSERT(val1 == l_true);
SASSERT(val2 == l_true);
}
else {
SASSERT(val1 == l_false || val2 == l_false);
}
break;
}
}
int edge_id = a->get_asserted_edge();
SASSERT(m_graph.is_enabled(edge_id));
SASSERT(m_graph.is_feasible(edge_id));
}
}
return m_graph.is_feasible();
@ -1087,7 +916,6 @@ void theory_diff_logic<Ext>::new_eq_or_diseq(bool is_eq, theory_var v1, theory_v
// assign the corresponding equality literal.
//
mark_as_modified_since_eq_prop();
app_ref eq(m), s2(m), t2(m);
app* s1 = get_enode(s)->get_owner();
@ -1142,10 +970,6 @@ void theory_diff_logic<Ext>::new_diseq_eh(theory_var v1, theory_var v2) {
}
template<typename Ext>
void theory_diff_logic<Ext>::relevant_eh(app* e) {
}
struct imp_functor {
conflict_resolution & m_cr;

View file

@ -68,14 +68,11 @@ namespace smt {
bv_util& b() { return m_bv; }
class dl_value_proc : public smt::model_value_proc {
smt::model_generator & m_mg;
theory_dl& m_th;
smt::enode* m_node;
public:
dl_value_proc(smt::model_generator & m, theory_dl& th, smt::enode* n):
m_mg(m), m_th(th), m_node(n)
{ }
dl_value_proc(theory_dl& th, smt::enode* n) : m_th(th), m_node(n) {}
virtual void get_dependencies(buffer<smt::model_value_dependency> & result) {}
@ -94,7 +91,7 @@ namespace smt {
rational val;
if (ctx.e_internalized(rep_of) && th_bv &&
th_bv->get_fixed_value(rep_of.get(), val)) {
result = m_th.u().mk_numeral(val.get_int64(), s);
result = m_th.u().mk_numeral(val.get_int64(), s);
}
else {
result = m_th.u().mk_numeral(0, s);
@ -165,8 +162,8 @@ namespace smt {
m.register_factory(alloc(dl_factory, m_util, m.get_model()));
}
virtual smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator & m) {
return alloc(dl_value_proc, m, *this, n);
virtual smt::model_value_proc * mk_value(smt::enode * n) {
return alloc(dl_value_proc, *this, n);
}
virtual void apply_sort_cnstr(enode * n, sort * s) {

View file

@ -0,0 +1,236 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
theory_horn_ineq.h
Author:
Nikolaj Bjorner (nbjorner) 2013-04-18
Revision History:
The implementaton is derived from theory_diff_logic.
--*/
#include "theory_horn_ineq.h"
#include "theory_horn_ineq_def.h"
namespace smt {
template class theory_horn_ineq<ihi_ext>;
template class theory_horn_ineq<rhi_ext>;
// similar to test_diff_logic:
horn_ineq_tester::horn_ineq_tester(ast_manager& m): m(m), a(m) {}
bool horn_ineq_tester::operator()(expr* e) {
m_todo.reset();
m_pols.reset();
pos_mark.reset();
neg_mark.reset();
m_todo.push_back(e);
m_pols.push_back(l_true);
while (!m_todo.empty()) {
expr* e = m_todo.back();
lbool p = m_pols.back();
m_todo.pop_back();
m_pols.pop_back();
switch (p) {
case l_true:
if (pos_mark.is_marked(e)) {
continue;
}
pos_mark.mark(e, true);
break;
case l_false:
if (neg_mark.is_marked(e)) {
continue;
}
neg_mark.mark(e, true);
break;
case l_undef:
if (pos_mark.is_marked(e) && neg_mark.is_marked(e)) {
continue;
}
pos_mark.mark(e, true);
neg_mark.mark(e, true);
break;
}
if (!test_expr(p, e)) {
return false;
}
}
return true;
}
vector<std::pair<expr*,rational> > const& horn_ineq_tester::get_linearization() const {
return m_terms;
}
bool horn_ineq_tester::test_expr(lbool p, expr* e) {
expr* e1, *e2, *e3;
if (is_var(e)) {
return true;
}
if (!is_app(e)) {
return false;
}
app* ap = to_app(e);
if (m.is_and(ap) || m.is_or(ap)) {
for (unsigned i = 0; i < ap->get_num_args(); ++i) {
m_todo.push_back(ap->get_arg(i));
m_pols.push_back(p);
}
}
else if (m.is_not(e, e1)) {
m_todo.push_back(e);
m_pols.push_back(~p);
}
else if (m.is_ite(e, e1, e2, e3)) {
m_todo.push_back(e1);
m_pols.push_back(l_undef);
m_todo.push_back(e2);
m_pols.push_back(p);
m_todo.push_back(e3);
m_pols.push_back(p);
}
else if (m.is_iff(e, e1, e2)) {
m_todo.push_back(e1);
m_pols.push_back(l_undef);
m_todo.push_back(e2);
m_pols.push_back(l_undef);
m_todo.push_back(e2);
}
else if (m.is_implies(e, e1, e2)) {
m_todo.push_back(e1);
m_pols.push_back(~p);
m_todo.push_back(e2);
m_pols.push_back(p);
}
else if (m.is_eq(e, e1, e2)) {
return linearize(e1, e2) == diff;
}
else if (m.is_true(e) || m.is_false(e)) {
// no-op
}
else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) ||
a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) {
if (p == l_false) {
std::swap(e2, e1);
}
classify_t cl = linearize(e1, e2);
switch(p) {
case l_undef:
return cl == diff;
case l_true:
case l_false:
return cl == horn || cl == diff;
}
}
else if (!is_uninterp_const(e)) {
return false;
}
return true;
}
bool horn_ineq_tester::operator()(unsigned num_fmls, expr* const* fmls) {
for (unsigned i = 0; i < num_fmls; ++i) {
if (!(*this)(fmls[i])) {
return false;
}
}
return true;
}
horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e) {
m_terms.reset();
m_terms.push_back(std::make_pair(e, rational(1)));
return linearize();
}
horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e1, expr* e2) {
m_terms.reset();
m_terms.push_back(std::make_pair(e1, rational(1)));
m_terms.push_back(std::make_pair(e2, rational(-1)));
return linearize();
}
horn_ineq_tester::classify_t horn_ineq_tester::linearize() {
m_weight.reset();
m_coeff_map.reset();
while (!m_terms.empty()) {
expr* e1, *e2;
rational num;
rational mul = m_terms.back().second;
expr* e = m_terms.back().first;
m_terms.pop_back();
if (a.is_add(e)) {
for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) {
m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul));
}
}
else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) {
m_terms.push_back(std::make_pair(e2, mul*num));
}
else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) {
m_terms.push_back(std::make_pair(e2, mul*num));
}
else if (a.is_sub(e, e1, e2)) {
m_terms.push_back(std::make_pair(e1, mul));
m_terms.push_back(std::make_pair(e2, -mul));
}
else if (a.is_uminus(e, e1)) {
m_terms.push_back(std::make_pair(e1, -mul));
}
else if (a.is_numeral(e, num)) {
m_weight += num*mul;
}
else if (a.is_to_real(e, e1)) {
m_terms.push_back(std::make_pair(e1, mul));
}
else if (!is_uninterp_const(e)) {
return non_horn;
}
else {
m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul;
}
}
unsigned num_negative = 0;
unsigned num_positive = 0;
bool is_unit_pos = true, is_unit_neg = true;
obj_map<expr, rational>::iterator it = m_coeff_map.begin();
obj_map<expr, rational>::iterator end = m_coeff_map.end();
for (; it != end; ++it) {
rational r = it->m_value;
if (r.is_zero()) {
continue;
}
m_terms.push_back(std::make_pair(it->m_key, r));
if (r.is_pos()) {
is_unit_pos = is_unit_pos && r.is_one();
num_positive++;
}
else {
is_unit_neg = is_unit_neg && r.is_minus_one();
num_negative++;
}
}
if (num_negative <= 1 && is_unit_pos && is_unit_neg && num_positive <= 1) {
return diff;
}
if (num_positive <= 1 && is_unit_pos) {
return horn;
}
if (num_negative <= 1 && is_unit_neg) {
return co_horn;
}
return non_horn;
}
}

328
src/smt/theory_horn_ineq.h Normal file
View file

@ -0,0 +1,328 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
theory_horn_ineq.h
Abstract:
A*x <= weight + D*x, coefficients to A and D are non-negative,
D is a diagonal matrix.
Coefficients to weight may have both signs.
Label variables by weight.
Select inequality that is not satisfied.
Set delta(LHS) := 0
Set delta(RHS(x)) := weight(x) - b
Propagate weight increment through inequalities.
Author:
Nikolaj Bjorner (nbjorner) 2013-04-18
Revision History:
The implementaton is derived from theory_diff_logic.
--*/
#ifndef _THEORY_HORN_INEQ_H_
#define _THEORY_HORN_INEQ_H_
#include"rational.h"
#include"inf_rational.h"
#include"inf_int_rational.h"
#include"inf_eps_rational.h"
#include"smt_theory.h"
#include"arith_decl_plugin.h"
#include"smt_justification.h"
#include"map.h"
#include"smt_params.h"
#include"arith_eq_adapter.h"
#include"smt_model_generator.h"
#include"numeral_factory.h"
#include"smt_clause.h"
namespace smt {
class horn_ineq_tester {
ast_manager& m;
arith_util a;
ptr_vector<expr> m_todo;
svector<lbool> m_pols;
ast_mark pos_mark, neg_mark;
obj_map<expr, rational> m_coeff_map;
rational m_weight;
vector<std::pair<expr*, rational> > m_terms;
public:
enum classify_t {
co_horn,
horn,
diff,
non_horn
};
horn_ineq_tester(ast_manager& m);
// test if formula is in the Horn inequality fragment:
bool operator()(expr* fml);
bool operator()(unsigned num_fmls, expr* const* fmls);
// linearize inequality/equality
classify_t linearize(expr* e);
classify_t linearize(expr* e1, expr* e2);
// retrieve linearization
vector<std::pair<expr*,rational> > const& get_linearization() const;
rational const& get_weight() const { return m_weight; }
private:
bool test_expr(lbool p, expr* e);
classify_t linearize();
};
template<typename Ext>
class theory_horn_ineq : public theory, private Ext {
typedef typename Ext::numeral numeral;
typedef typename Ext::inf_numeral inf_numeral;
typedef literal explanation;
typedef theory_var th_var;
typedef svector<th_var> th_var_vector;
typedef unsigned clause_id;
typedef vector<std::pair<th_var, rational> > coeffs;
class clause;
class graph;
class assignment_trail;
class parent_trail;
class atom {
protected:
bool_var m_bvar;
bool m_true;
int m_pos;
int m_neg;
public:
atom(bool_var bv, int pos, int neg) :
m_bvar(bv), m_true(false),
m_pos(pos), m_neg(neg) {}
~atom() {}
bool_var get_bool_var() const { return m_bvar; }
bool is_true() const { return m_true; }
void assign_eh(bool is_true) { m_true = is_true; }
int get_asserted_edge() const { return this->m_true?m_pos:m_neg; }
int get_pos() const { return m_pos; }
int get_neg() const { return m_neg; }
std::ostream& display(theory_horn_ineq const& th, std::ostream& out) const;
};
typedef svector<atom> atoms;
struct scope {
unsigned m_atoms_lim;
unsigned m_asserted_atoms_lim;
unsigned m_asserted_qhead_old;
};
struct stats {
unsigned m_num_conflicts;
unsigned m_num_assertions;
unsigned m_num_core2th_eqs;
unsigned m_num_core2th_diseqs;
void reset() {
memset(this, 0, sizeof(*this));
}
stats() {
reset();
}
};
stats m_stats;
smt_params m_params;
arith_util a;
arith_eq_adapter m_arith_eq_adapter;
th_var m_zero_int; // cache the variable representing the zero variable.
th_var m_zero_real; // cache the variable representing the zero variable.
graph* m_graph;
atoms m_atoms;
unsigned_vector m_asserted_atoms; // set of asserted atoms
unsigned m_asserted_qhead;
u_map<unsigned> m_bool_var2atom;
svector<scope> m_scopes;
double m_agility;
bool m_lia;
bool m_lra;
bool m_non_horn_ineq_exprs;
horn_ineq_tester m_test;
arith_factory * m_factory;
rational m_delta;
rational m_lambda;
// Set a conflict due to a negative cycle.
void set_neg_cycle_conflict();
// Create a new theory variable.
virtual th_var mk_var(enode* n);
virtual th_var mk_var(expr* n);
void compute_delta();
void found_non_horn_ineq_expr(expr * n);
bool is_interpreted(app* n) const {
return n->get_family_id() == get_family_id();
}
public:
theory_horn_ineq(ast_manager& m);
virtual ~theory_horn_ineq();
virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_horn_ineq, get_manager()); }
virtual char const * get_name() const { return "horn-inequality-logic"; }
/**
\brief See comment in theory::mk_eq_atom
*/
virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); }
virtual void init(context * ctx);
virtual bool internalize_atom(app * atom, bool gate_ctx);
virtual bool internalize_term(app * term);
virtual void internalize_eq_eh(app * atom, bool_var v);
virtual void assign_eh(bool_var v, bool is_true);
virtual void new_eq_eh(th_var v1, th_var v2) {
m_arith_eq_adapter.new_eq_eh(v1, v2);
}
virtual bool use_diseqs() const { return true; }
virtual void new_diseq_eh(th_var v1, th_var v2) {
m_arith_eq_adapter.new_diseq_eh(v1, v2);
}
virtual void push_scope_eh();
virtual void pop_scope_eh(unsigned num_scopes);
virtual void restart_eh() {
m_arith_eq_adapter.restart_eh();
}
virtual void relevant_eh(app* e) {}
virtual void init_search_eh() {
m_arith_eq_adapter.init_search_eh();
}
virtual final_check_status final_check_eh();
virtual bool is_shared(th_var v) const {
return false;
}
virtual bool can_propagate() {
SASSERT(m_asserted_qhead <= m_asserted_atoms.size());
return m_asserted_qhead != m_asserted_atoms.size();
}
virtual void propagate();
virtual justification * why_is_diseq(th_var v1, th_var v2) {
UNREACHABLE();
return 0;
}
virtual void reset_eh();
virtual void init_model(model_generator & m);
virtual model_value_proc * mk_value(enode * n, model_generator & mg);
virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const {
return true;
}
virtual void display(std::ostream & out) const;
virtual void collect_statistics(::statistics & st) const;
private:
virtual void new_eq_eh(th_var v1, th_var v2, justification& j) {
m_stats.m_num_core2th_eqs++;
new_eq_or_diseq(true, v1, v2, j);
}
virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) {
m_stats.m_num_core2th_diseqs++;
new_eq_or_diseq(false, v1, v2, j);
}
void negate(coeffs& coeffs, rational& weight);
numeral mk_weight(bool is_real, bool is_strict, rational const& w) const;
void mk_coeffs(vector<std::pair<expr*,rational> >const& terms, coeffs& coeffs, rational& w);
void del_atoms(unsigned old_size);
void propagate_core();
bool propagate_atom(atom const& a);
th_var mk_term(app* n);
th_var mk_num(app* n, rational const& r);
bool is_consistent() const;
th_var expand(bool pos, th_var v, rational & k);
void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just);
th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; }
th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); }
void inc_conflicts();
};
struct rhi_ext {
typedef inf_rational inf_numeral;
typedef inf_eps_rational<inf_rational> numeral;
numeral m_epsilon;
numeral m_minus_infty;
rhi_ext() : m_epsilon(inf_rational(rational(), true)), m_minus_infty(rational(-1),inf_rational()) {}
};
struct ihi_ext {
typedef rational inf_numeral;
typedef inf_eps_rational<rational> numeral;
numeral m_epsilon;
numeral m_minus_infty;
ihi_ext() : m_epsilon(rational(1)), m_minus_infty(rational(-1),rational(0)) {}
};
typedef theory_horn_ineq<rhi_ext> theory_rhi;
typedef theory_horn_ineq<ihi_ext> theory_ihi;
};
#endif /* _THEORY_HORN_INEQ_H_ */

File diff suppressed because it is too large Load diff

160
src/smt/theory_utvpi.cpp Normal file
View file

@ -0,0 +1,160 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
theory_utvpi.h
Author:
Nikolaj Bjorner (nbjorner) 2013-04-26
Revision History:
The implementaton is derived from theory_diff_logic.
--*/
#include "theory_utvpi.h"
#include "theory_utvpi_def.h"
namespace smt {
template class theory_utvpi<idl_ext>;
template class theory_utvpi<rdl_ext>;
// similar to test_diff_logic:
utvpi_tester::utvpi_tester(ast_manager& m): m(m), a(m) {}
bool utvpi_tester::operator()(expr* e) {
m_todo.reset();
m_mark.reset();
m_todo.push_back(e);
expr* e1, *e2;
while (!m_todo.empty()) {
expr* e = m_todo.back();
m_todo.pop_back();
if (!m_mark.is_marked(e)) {
m_mark.mark(e, true);
if (is_var(e)) {
continue;
}
if (!is_app(e)) {
return false;
}
app* ap = to_app(e);
if (m.is_eq(ap, e1, e2)) {
if (!linearize(e1, e2)) {
return false;
}
}
else if (ap->get_family_id() == m.get_basic_family_id()) {
continue;
}
else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) ||
a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) {
if (!linearize(e1, e2)) {
return false;
}
}
else if (is_uninterp_const(e)) {
continue;
}
else {
return false;
}
}
}
return true;
}
vector<std::pair<expr*, rational> > const& utvpi_tester::get_linearization() const {
SASSERT(m_terms.size() <= 2);
return m_terms;
}
bool utvpi_tester::operator()(unsigned num_fmls, expr* const* fmls) {
for (unsigned i = 0; i < num_fmls; ++i) {
if (!(*this)(fmls[i])) {
return false;
}
}
return true;
}
bool utvpi_tester::linearize(expr* e) {
m_terms.reset();
m_terms.push_back(std::make_pair(e, rational(1)));
return linearize();
}
bool utvpi_tester::linearize(expr* e1, expr* e2) {
m_terms.reset();
m_terms.push_back(std::make_pair(e1, rational(1)));
m_terms.push_back(std::make_pair(e2, rational(-1)));
return linearize();
}
bool utvpi_tester::linearize() {
m_weight.reset();
m_coeff_map.reset();
while (!m_terms.empty()) {
expr* e1, *e2;
rational num;
rational mul = m_terms.back().second;
expr* e = m_terms.back().first;
m_terms.pop_back();
if (a.is_add(e)) {
for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) {
m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul));
}
}
else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) {
m_terms.push_back(std::make_pair(e2, mul*num));
}
else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) {
m_terms.push_back(std::make_pair(e2, mul*num));
}
else if (a.is_sub(e, e1, e2)) {
m_terms.push_back(std::make_pair(e1, mul));
m_terms.push_back(std::make_pair(e2, -mul));
}
else if (a.is_uminus(e, e1)) {
m_terms.push_back(std::make_pair(e1, -mul));
}
else if (a.is_numeral(e, num)) {
m_weight += num*mul;
}
else if (a.is_to_real(e, e1)) {
m_terms.push_back(std::make_pair(e1, mul));
}
else if (!is_uninterp_const(e)) {
return false;
}
else {
m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul;
}
}
obj_map<expr, rational>::iterator it = m_coeff_map.begin();
obj_map<expr, rational>::iterator end = m_coeff_map.end();
for (; it != end; ++it) {
rational r = it->m_value;
if (r.is_zero()) {
continue;
}
m_terms.push_back(std::make_pair(it->m_key, r));
if (m_terms.size() > 2) {
return false;
}
if (!r.is_one() && !r.is_minus_one()) {
return false;
}
}
return true;
}
}

343
src/smt/theory_utvpi.h Normal file
View file

@ -0,0 +1,343 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
theory_utvpi.h
Abstract:
use Bellman Ford traversal algorithm for UTVPI.
Author:
Nikolaj Bjorner (nbjorner) 2013-04-26
Revision History:
The implementaton is derived from theory_diff_logic.
--*/
#ifndef _THEORY_UTVPI_H_
#define _THEORY_UTVPI_H_
#include"theory_diff_logic.h"
namespace smt {
class utvpi_tester {
ast_manager& m;
arith_util a;
ptr_vector<expr> m_todo;
ast_mark m_mark;
obj_map<expr, rational> m_coeff_map;
rational m_weight;
vector<std::pair<expr*, rational> > m_terms;
public:
utvpi_tester(ast_manager& m);
// test if formula is in the Horn inequality fragment:
bool operator()(expr* fml);
bool operator()(unsigned num_fmls, expr* const* fmls);
// linearize inequality/equality
bool linearize(expr* e);
bool linearize(expr* e1, expr* e2);
// retrieve linearization
vector<std::pair<expr*, rational> > const& get_linearization() const;
rational const& get_weight() const { return m_weight; }
private:
bool linearize();
};
template<typename Ext>
class theory_utvpi : public theory, private Ext {
typedef typename Ext::numeral numeral;
typedef theory_var th_var;
typedef svector<th_var> th_var_vector;
typedef vector<std::pair<th_var, rational> > coeffs;
class assignment_trail;
class parent_trail;
struct GExt : public Ext {
typedef std::pair<literal,unsigned> explanation;
};
class atom {
protected:
bool_var m_bvar;
bool m_true;
int m_pos;
int m_neg;
public:
atom(bool_var bv, int pos, int neg) :
m_bvar(bv), m_true(false),
m_pos(pos), m_neg(neg) {}
~atom() {}
bool_var get_bool_var() const { return m_bvar; }
void assign_eh(bool is_true) { m_true = is_true; }
int get_asserted_edge() const { return this->m_true?m_pos:m_neg; }
int get_pos() const { return m_pos; }
int get_neg() const { return m_neg; }
std::ostream& display(theory_utvpi const& th, std::ostream& out) const;
};
typedef svector<atom> atoms;
struct scope {
unsigned m_atoms_lim;
unsigned m_asserted_atoms_lim;
unsigned m_asserted_qhead_old;
};
struct stats {
unsigned m_num_conflicts;
unsigned m_num_assertions;
unsigned m_num_core2th_eqs;
unsigned m_num_core2th_diseqs;
void reset() {
memset(this, 0, sizeof(*this));
}
stats() {
reset();
}
};
// Functor used to collect the proofs for a conflict due to
// a negative cycle.
class nc_functor {
literal_vector m_antecedents;
unsigned_vector m_coeffs;
theory_utvpi& m_super;
public:
nc_functor(theory_utvpi& s) : m_super(s) {}
void reset() { m_antecedents.reset(); m_coeffs.reset(); }
literal_vector const& get_lits() const { return m_antecedents; }
unsigned_vector const& get_coeffs() const { return m_coeffs; }
void operator()(std::pair<literal,unsigned> const & ex) {
if (ex.first != null_literal) {
m_antecedents.push_back(ex.first);
m_coeffs.push_back(ex.second);
}
}
void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {
m_super.new_edge(src, dst, num_edges, edges);
}
};
stats m_stats;
smt_params m_params;
arith_util a;
arith_eq_adapter m_arith_eq_adapter;
th_var m_zero_int; // cache the variable representing the zero variable.
th_var m_zero_real; // cache the variable representing the zero variable.
dl_graph<GExt> m_graph;
nc_functor m_nc_functor;
atoms m_atoms;
unsigned_vector m_asserted_atoms; // set of asserted atoms
unsigned m_asserted_qhead;
u_map<unsigned> m_bool_var2atom;
svector<scope> m_scopes;
double m_agility;
bool m_lia;
bool m_lra;
bool m_non_utvpi_exprs;
utvpi_tester m_test;
arith_factory * m_factory;
rational m_delta;
// Set a conflict due to a negative cycle.
void set_conflict();
void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {}
// Create a new theory variable.
virtual th_var mk_var(enode* n);
virtual th_var mk_var(expr* n);
void compute_delta();
void found_non_utvpi_expr(expr * n);
bool is_interpreted(app* n) const {
return n->get_family_id() == get_family_id();
}
public:
theory_utvpi(ast_manager& m);
virtual ~theory_utvpi();
virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_utvpi, get_manager()); }
virtual char const * get_name() const { return "utvpi-logic"; }
/**
\brief See comment in theory::mk_eq_atom
*/
virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); }
virtual void init(context * ctx);
virtual bool internalize_atom(app * atom, bool gate_ctx);
virtual bool internalize_term(app * term);
virtual void internalize_eq_eh(app * atom, bool_var v);
virtual void assign_eh(bool_var v, bool is_true);
virtual void new_eq_eh(th_var v1, th_var v2) {
m_stats.m_num_core2th_eqs++;
m_arith_eq_adapter.new_eq_eh(v1, v2);
}
virtual bool use_diseqs() const { return true; }
virtual void new_diseq_eh(th_var v1, th_var v2) {
m_arith_eq_adapter.new_diseq_eh(v1, v2);
}
virtual void push_scope_eh();
virtual void pop_scope_eh(unsigned num_scopes);
virtual void restart_eh() {
m_arith_eq_adapter.restart_eh();
}
virtual void relevant_eh(app* e) {}
virtual void init_search_eh() {
m_arith_eq_adapter.init_search_eh();
}
virtual final_check_status final_check_eh();
virtual bool is_shared(th_var v) const {
return false;
}
virtual bool can_propagate() {
SASSERT(m_asserted_qhead <= m_asserted_atoms.size());
return m_asserted_qhead != m_asserted_atoms.size();
}
virtual void propagate();
virtual justification * why_is_diseq(th_var v1, th_var v2) {
UNREACHABLE();
return 0;
}
virtual void reset_eh();
virtual void init_model(model_generator & m);
virtual model_value_proc * mk_value(enode * n, model_generator & mg);
virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const {
return true;
}
virtual void display(std::ostream & out) const;
virtual void collect_statistics(::statistics & st) const;
private:
rational mk_value(theory_var v, bool is_int);
bool is_parity_ok(unsigned v) const;
void enforce_parity();
void validate_model();
bool eval(expr* e);
rational eval_num(expr* e);
bool check_z_consistency();
virtual void new_eq_eh(th_var v1, th_var v2, justification& j) {
m_stats.m_num_core2th_eqs++;
new_eq_or_diseq(true, v1, v2, j);
}
virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) {
m_stats.m_num_core2th_diseqs++;
new_eq_or_diseq(false, v1, v2, j);
}
void negate(coeffs& coeffs, rational& weight);
numeral mk_weight(bool is_real, bool is_strict, rational const& w) const;
void mk_coeffs(vector<std::pair<expr*,rational> >const& terms, coeffs& coeffs, rational& w);
void del_atoms(unsigned old_size);
bool propagate_atom(atom const& a);
th_var mk_term(app* n);
th_var mk_num(app* n, rational const& r);
bool is_consistent() const;
th_var expand(bool pos, th_var v, rational & k);
void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just);
th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; }
th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); }
void inc_conflicts();
edge_id add_ineq(vector<std::pair<th_var, rational> > const& terms, numeral const& weight, literal l);
bool enable_edge(edge_id id);
th_var to_var(th_var v) const {
return 2*v;
}
th_var from_var(th_var v) const {
return v/2;
}
th_var pos(th_var v) const {
return v & 0xFFFFFFFE;
}
th_var neg(th_var v) const {
return v | 0x1;
}
};
typedef theory_utvpi<rdl_ext> theory_rutvpi;
typedef theory_utvpi<idl_ext> theory_iutvpi;
};
#endif /* _THEORY_UTVPI_H_ */

954
src/smt/theory_utvpi_def.h Normal file
View file

@ -0,0 +1,954 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
theory_utvpi_def.h
Abstract:
Implementation of UTVPI solver.
Author:
Nikolaj Bjorner (nbjorner) 2013-04-26
Revision History:
1. introduce x^+ and x^-, such that 2*x := x^+ - x^-
2. rewrite constraints as follows:
x - y <= k => x^+ - y^+ <= k
y^- - x^- <= k
x <= k => x^+ - x^- <= 2k
x + y <= k => x^+ - y^- <= k
y^+ - x^- <= k
- x - y <= k => x^- - y^+ <= k
y^- - x^+ <= k
3. Solve for x^+ and x^-
4. Check parity condition for integers (see Lahiri and Musuvathi 05)
This checks if x^+ and x^- are in the same component but of different
parities.
5. Enforce parity on variables. This checks if x^+ and x^- have different
parities. If they have different parities, the assignment to one
of the variables is decremented (choose the variable that is not tightly
constrained with 0).
The process that adjusts parities converges: Suppose we break a parity
of a different variable y while fixing x's parity. A cyclic breaking/fixing
of parities implies there is a strongly connected component between x, y
and the two polarities of the variables. This contradicts the test in 4.
6. extract model for M(x) := (M(x^+)- M(x^-))/2
--*/
#ifndef _THEORY_UTVPI_DEF_H_
#define _THEORY_UTVPI_DEF_H_
#include "theory_utvpi.h"
#include "heap.h"
#include "ast_pp.h"
#include "smt_context.h"
namespace smt {
template<typename Ext>
theory_utvpi<Ext>::theory_utvpi(ast_manager& m):
theory(m.mk_family_id("arith")),
a(m),
m_arith_eq_adapter(*this, m_params, a),
m_zero_int(null_theory_var),
m_zero_real(null_theory_var),
m_nc_functor(*this),
m_asserted_qhead(0),
m_agility(0.5),
m_lia(false),
m_lra(false),
m_non_utvpi_exprs(false),
m_test(m),
m_factory(0) {
}
template<typename Ext>
theory_utvpi<Ext>::~theory_utvpi() {
reset_eh();
}
template<typename Ext>
std::ostream& theory_utvpi<Ext>::atom::display(theory_utvpi const& th, std::ostream& out) const {
context& ctx = th.get_context();
lbool asgn = ctx.get_assignment(m_bvar);
bool sign = (l_undef == l_false);
return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " ";
if (l_undef == asgn) {
out << "unassigned\n";
}
else {
th.m_graph.display_edge(out, get_asserted_edge());
}
return out;
}
template<typename Ext>
theory_var theory_utvpi<Ext>::mk_var(enode* n) {
th_var v = theory::mk_var(n);
TRACE("utvpi", tout << v << " " << mk_pp(n->get_owner(), get_manager()) << "\n";);
m_graph.init_var(to_var(v));
m_graph.init_var(neg(to_var(v)));
get_context().attach_th_var(n, this, v);
return v;
}
template<typename Ext>
theory_var theory_utvpi<Ext>::mk_var(expr* n) {
context & ctx = get_context();
enode* e = 0;
th_var v = null_theory_var;
m_lia |= a.is_int(n);
m_lra |= a.is_real(n);
if (!is_app(n)) {
return v;
}
if (ctx.e_internalized(n)) {
e = ctx.get_enode(n);
v = e->get_th_var(get_id());
}
else {
ctx.internalize(n, false);
e = ctx.get_enode(n);
}
if (v == null_theory_var) {
v = mk_var(e);
}
if (is_interpreted(to_app(n))) {
found_non_utvpi_expr(n);
}
return v;
}
template<typename Ext>
void theory_utvpi<Ext>::reset_eh() {
m_graph .reset();
m_zero_int = null_theory_var;
m_zero_real = null_theory_var;
m_atoms .reset();
m_asserted_atoms .reset();
m_stats .reset();
m_scopes .reset();
m_asserted_qhead = 0;
m_agility = 0.5;
m_lia = false;
m_lra = false;
m_non_utvpi_exprs = false;
theory::reset_eh();
}
template<typename Ext>
void theory_utvpi<Ext>::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) {
rational k;
th_var s = expand(true, v1, k);
th_var t = expand(false, v2, k);
context& ctx = get_context();
ast_manager& m = get_manager();
if (s == t) {
if (is_eq != k.is_zero()) {
// conflict 0 /= k;
inc_conflicts();
ctx.set_conflict(&eq_just);
}
}
else {
//
// Create equality ast, internalize_atom
// assign the corresponding equality literal.
//
app_ref eq(m), s2(m), t2(m);
app* s1 = get_enode(s)->get_owner();
app* t1 = get_enode(t)->get_owner();
s2 = a.mk_sub(t1, s1);
t2 = a.mk_numeral(k, m.get_sort(s2.get()));
// t1 - s1 = k
eq = m.mk_eq(s2.get(), t2.get());
TRACE("utvpi",
tout << v1 << " .. " << v2 << "\n";
tout << mk_pp(eq.get(), m) <<"\n";);
if (!internalize_atom(eq.get(), false)) {
UNREACHABLE();
}
literal l(ctx.get_literal(eq.get()));
if (!is_eq) {
l = ~l;
}
ctx.assign(l, b_justification(&eq_just), false);
}
}
template<typename Ext>
void theory_utvpi<Ext>::inc_conflicts() {
m_stats.m_num_conflicts++;
if (m_params.m_arith_adaptive) {
double g = m_params.m_arith_adaptive_propagation_threshold;
m_agility = m_agility*g + 1 - g;
}
}
template<typename Ext>
void theory_utvpi<Ext>::set_conflict() {
inc_conflicts();
literal_vector const& lits = m_nc_functor.get_lits();
context & ctx = get_context();
IF_VERBOSE(2,
verbose_stream() << "conflict:\n";
for (unsigned i = 0; i < lits.size(); ++i) {
ast_manager& m = get_manager();
expr_ref e(m);
ctx.literal2expr(lits[i], e);
verbose_stream() << mk_pp(e, m) << "\n";
}
verbose_stream() << "\n";);
TRACE("utvpi",
tout << "conflict: ";
for (unsigned i = 0; i < lits.size(); ++i) {
ctx.display_literal_info(tout, lits[i]);
}
tout << "\n";
);
if (m_params.m_arith_dump_lemmas) {
char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA";
ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic);
}
vector<parameter> params;
if (get_manager().proofs_enabled()) {
params.push_back(parameter(symbol("farkas")));
for (unsigned i = 0; i < m_nc_functor.get_coeffs().size(); ++i) {
params.push_back(parameter(rational(m_nc_functor.get_coeffs()[i])));
}
}
ctx.set_conflict(
ctx.mk_justification(
ext_theory_conflict_justification(
get_id(), ctx.get_region(),
lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr())));
m_nc_functor.reset();
}
template<typename Ext>
void theory_utvpi<Ext>::found_non_utvpi_expr(expr* n) {
if (!m_non_utvpi_exprs) {
std::stringstream msg;
msg << "found non utvpi logic expression:\n" << mk_pp(n, get_manager()) << "\n";
TRACE("utvpi", tout << msg.str(););
warning_msg(msg.str().c_str());
get_context().push_trail(value_trail<context, bool>(m_non_utvpi_exprs));
m_non_utvpi_exprs = true;
}
}
template<typename Ext>
void theory_utvpi<Ext>::init(context* ctx) {
theory::init(ctx);
m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true));
m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true));
}
/**
\brief Create negated literal.
The negation of: E <= 0
-E + epsilon <= 0
or
-E + 1 <= 0
*/
template<typename Ext>
void theory_utvpi<Ext>::negate(coeffs& coeffs, rational& weight) {
for (unsigned i = 0; i < coeffs.size(); ++i) {
coeffs[i].second.neg();
}
weight.neg();
}
template<typename Ext>
typename theory_utvpi<Ext>::numeral theory_utvpi<Ext>::mk_weight(bool is_real, bool is_strict, rational const& w) const {
if (is_strict) {
return numeral(w) + (is_real?Ext::m_epsilon:numeral(1));
}
else {
return numeral(w);
}
}
template<typename Ext>
void theory_utvpi<Ext>::mk_coeffs(vector<std::pair<expr*,rational> > const& terms, coeffs& coeffs, rational& w) {
coeffs.reset();
w = m_test.get_weight();
for (unsigned i = 0; i < terms.size(); ++i) {
coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second));
}
}
template<typename Ext>
void theory_utvpi<Ext>::internalize_eq_eh(app * atom, bool_var v) {
context & ctx = get_context();
app * lhs = to_app(atom->get_arg(0));
app * rhs = to_app(atom->get_arg(1));
if (a.is_numeral(rhs)) {
std::swap(rhs, lhs);
}
if (!a.is_numeral(rhs)) {
return;
}
if (a.is_add(lhs) || a.is_sub(lhs)) {
// force axioms for (= (+ x y) k)
// this is necessary because (+ x y) is not expressible as a utvpi term.
m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs));
}
}
template<typename Ext>
bool theory_utvpi<Ext>::internalize_atom(app * n, bool) {
context & ctx = get_context();
if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) {
found_non_utvpi_expr(n);
return false;
}
SASSERT(!ctx.b_internalized(n));
expr* e1 = n->get_arg(0), *e2 = n->get_arg(1);
if (a.is_ge(n) || a.is_gt(n)) {
std::swap(e1, e2);
}
bool is_strict = a.is_gt(n) || a.is_lt(n);
bool cl = m_test.linearize(e1, e2);
if (!cl) {
found_non_utvpi_expr(n);
return false;
}
rational w;
coeffs coeffs;
mk_coeffs(m_test.get_linearization(), coeffs, w);
bool_var bv = ctx.mk_bool_var(n);
ctx.set_var_theory(bv, get_id());
literal l(bv);
numeral w1 = mk_weight(a.is_real(e1), is_strict, w);
edge_id pos = add_ineq(coeffs, w1, l);
negate(coeffs, w);
numeral w2 = mk_weight(a.is_real(e1), !is_strict, w);
edge_id neg = add_ineq(coeffs, w2, ~l);
m_bool_var2atom.insert(bv, m_atoms.size());
m_atoms.push_back(atom(bv, pos, neg));
TRACE("utvpi",
tout << mk_pp(n, get_manager()) << "\n";
m_graph.display_edge(tout << "pos: ", pos);
m_graph.display_edge(tout << "neg: ", neg);
);
return true;
}
template<typename Ext>
bool theory_utvpi<Ext>::internalize_term(app * term) {
bool result = null_theory_var != mk_term(term);
CTRACE("utvpi", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";);
return result;
}
template<typename Ext>
void theory_utvpi<Ext>::assign_eh(bool_var v, bool is_true) {
m_stats.m_num_assertions++;
unsigned idx = m_bool_var2atom.find(v);
SASSERT(get_context().get_assignment(v) != l_undef);
SASSERT((get_context().get_assignment(v) == l_true) == is_true);
m_atoms[idx].assign_eh(is_true);
m_asserted_atoms.push_back(idx);
}
template<typename Ext>
void theory_utvpi<Ext>::push_scope_eh() {
theory::push_scope_eh();
m_graph.push();
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_atoms_lim = m_atoms.size();
s.m_asserted_atoms_lim = m_asserted_atoms.size();
s.m_asserted_qhead_old = m_asserted_qhead;
}
template<typename Ext>
void theory_utvpi<Ext>::pop_scope_eh(unsigned num_scopes) {
unsigned lvl = m_scopes.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
scope & s = m_scopes[new_lvl];
del_atoms(s.m_atoms_lim);
m_asserted_atoms.shrink(s.m_asserted_atoms_lim);
m_asserted_qhead = s.m_asserted_qhead_old;
m_scopes.shrink(new_lvl);
m_graph.pop(num_scopes);
theory::pop_scope_eh(num_scopes);
}
template<typename Ext>
final_check_status theory_utvpi<Ext>::final_check_eh() {
SASSERT(is_consistent());
if (can_propagate()) {
propagate();
return FC_CONTINUE;
}
else if (!check_z_consistency()) {
return FC_CONTINUE;
}
else if (m_non_utvpi_exprs) {
return FC_GIVEUP;
}
else {
return FC_DONE;
}
}
template<typename Ext>
bool theory_utvpi<Ext>::check_z_consistency() {
int_vector scc_id;
m_graph.compute_zero_edge_scc(scc_id);
unsigned sz = get_num_vars();
for (unsigned i = 0; i < sz; ++i) {
enode* e = get_enode(i);
if (!a.is_int(e->get_owner())) {
continue;
}
th_var v1 = to_var(i);
th_var v2 = neg(v1);
rational r1 = m_graph.get_assignment(v1).get_rational();
rational r2 = m_graph.get_assignment(v2).get_rational();
SASSERT(r1.is_int());
SASSERT(r2.is_int());
if (r1.is_even() == r2.is_even()) {
continue;
}
if (scc_id[v1] != scc_id[v2]) {
continue;
}
if (scc_id[v1] == -1) {
continue;
}
// they are in the same SCC and have different parities => contradiction.
m_nc_functor.reset();
VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, UINT_MAX, m_nc_functor));
VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, UINT_MAX, m_nc_functor));
IF_VERBOSE(1, verbose_stream() << "parity conflict " << mk_pp(e->get_owner(), get_manager()) << "\n";);
set_conflict();
return false;
}
return true;
}
template<typename Ext>
void theory_utvpi<Ext>::display(std::ostream& out) const {
for (unsigned i = 0; i < m_atoms.size(); ++i) {
m_atoms[i].display(*this, out); out << "\n";
}
m_graph.display(out);
}
template<typename Ext>
void theory_utvpi<Ext>::collect_statistics(::statistics& st) const {
st.update("utvpi conflicts", m_stats.m_num_conflicts);
st.update("utvpi asserts", m_stats.m_num_assertions);
st.update("core->utvpi eqs", m_stats.m_num_core2th_eqs);
st.update("core->utvpi diseqs", m_stats.m_num_core2th_diseqs);
m_arith_eq_adapter.collect_statistics(st);
m_graph.collect_statistics(st);
}
template<typename Ext>
void theory_utvpi<Ext>::del_atoms(unsigned old_size) {
typename atoms::iterator begin = m_atoms.begin() + old_size;
typename atoms::iterator it = m_atoms.end();
while (it != begin) {
--it;
m_bool_var2atom.erase(it->get_bool_var());
}
m_atoms.shrink(old_size);
}
template<typename Ext>
void theory_utvpi<Ext>::propagate() {
bool consistent = true;
while (consistent && can_propagate()) {
unsigned idx = m_asserted_atoms[m_asserted_qhead];
m_asserted_qhead++;
consistent = propagate_atom(m_atoms[idx]);
}
}
template<typename Ext>
bool theory_utvpi<Ext>::propagate_atom(atom const& a) {
context& ctx = get_context();
TRACE("utvpi", a.display(*this, tout); tout << "\n";);
if (ctx.inconsistent()) {
return false;
}
int edge_id = a.get_asserted_edge();
if (!enable_edge(edge_id)) {
m_graph.traverse_neg_cycle2(m_params.m_arith_stronger_lemmas, m_nc_functor);
set_conflict();
return false;
}
return true;
}
template<typename Ext>
theory_var theory_utvpi<Ext>::mk_term(app* n) {
TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n";);
context& ctx = get_context();
bool cl = m_test.linearize(n);
if (!cl) {
found_non_utvpi_expr(n);
return null_theory_var;
}
coeffs coeffs;
rational w;
mk_coeffs(m_test.get_linearization(), coeffs, w);
if (coeffs.empty()) {
return mk_num(n, w);
}
if (coeffs.size() == 1 && coeffs[0].second.is_one()) {
return coeffs[0].first;
}
if (coeffs.size() == 2) {
// do not create an alias.
return null_theory_var;
}
for (unsigned i = 0; i < n->get_num_args(); ++i) {
mk_term(to_app(n->get_arg(i)));
}
th_var target = mk_var(ctx.mk_enode(n, false, false, true));
coeffs.push_back(std::make_pair(target, rational(-1)));
VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal)));
negate(coeffs, w);
VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal)));
return target;
}
template<typename Ext>
theory_var theory_utvpi<Ext>::mk_num(app* n, rational const& r) {
theory_var v = null_theory_var;
context& ctx = get_context();
if (r.is_zero()) {
v = a.is_int(n)?m_zero_int:m_zero_real;
}
else if (ctx.e_internalized(n)) {
enode* e = ctx.get_enode(n);
v = e->get_th_var(get_id());
SASSERT(v != null_theory_var);
}
else {
v = mk_var(ctx.mk_enode(n, false, false, true));
// v = k: v <= k k <= v
coeffs coeffs;
coeffs.push_back(std::make_pair(v, rational(-1)));
VERIFY(enable_edge(add_ineq(coeffs, numeral(r), null_literal)));
coeffs.back().second.neg();
VERIFY(enable_edge(add_ineq(coeffs, numeral(-r), null_literal)));
}
return v;
}
template<typename Ext>
theory_var theory_utvpi<Ext>::expand(bool pos, th_var v, rational & k) {
context& ctx = get_context();
enode* e = get_enode(v);
expr* x, *y;
rational r;
for (;;) {
app* n = e->get_owner();
if (a.is_add(n, x, y)) {
if (a.is_numeral(x, r)) {
e = ctx.get_enode(y);
}
else if (a.is_numeral(y, r)) {
e = ctx.get_enode(x);
}
v = e->get_th_var(get_id());
SASSERT(v != null_theory_var);
if (v == null_theory_var) {
break;
}
if (pos) {
k += r;
}
else {
k -= r;
}
}
else {
break;
}
}
return v;
}
// m_graph(source, target, weight, ex);
// target - source <= weight
template<typename Ext>
edge_id theory_utvpi<Ext>::add_ineq(vector<std::pair<th_var, rational> > const& terms, numeral const& weight, literal l) {
SASSERT(terms.size() <= 2);
SASSERT(terms.size() < 1 || terms[0].second.is_one() || terms[0].second.is_minus_one());
SASSERT(terms.size() < 2 || terms[1].second.is_one() || terms[1].second.is_minus_one());
th_var v1 = null_theory_var, v2 = null_theory_var;
bool pos1 = true, pos2 = true;
if (terms.size() >= 1) {
v1 = terms[0].first;
pos1 = terms[0].second.is_one();
SASSERT(v1 != null_theory_var);
SASSERT(pos1 || terms[0].second.is_minus_one());
}
if (terms.size() >= 2) {
v2 = terms[1].first;
pos2 = terms[1].second.is_one();
SASSERT(v1 != null_theory_var);
SASSERT(pos2 || terms[1].second.is_minus_one());
}
// TRACE("utvpi", tout << (pos1?"$":"-$") << v1 << (pos2?" + $":" - $") << v2 << " + " << weight << " <= 0\n";);
edge_id id = m_graph.get_num_edges();
th_var w1 = to_var(v1), w2 = to_var(v2);
if (terms.size() == 1 && pos1) {
m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2));
m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2));
}
else if (terms.size() == 1 && !pos1) {
m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2));
m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2));
}
else if (pos1 && pos2) {
m_graph.add_edge(neg(w2), pos(w1), -weight, std::make_pair(l,1));
m_graph.add_edge(neg(w1), pos(w2), -weight, std::make_pair(l,1));
}
else if (pos1 && !pos2) {
m_graph.add_edge(pos(w2), pos(w1), -weight, std::make_pair(l,1));
m_graph.add_edge(neg(w1), neg(w2), -weight, std::make_pair(l,1));
}
else if (!pos1 && pos2) {
m_graph.add_edge(neg(w2), neg(w1), -weight, std::make_pair(l,1));
m_graph.add_edge(pos(w1), pos(w2), -weight, std::make_pair(l,1));
}
else {
m_graph.add_edge(pos(w1), neg(w2), -weight, std::make_pair(l,1));
m_graph.add_edge(pos(w2), neg(w1), -weight, std::make_pair(l,1));
}
return id;
}
template<typename Ext>
bool theory_utvpi<Ext>::enable_edge(edge_id id) {
return (id == null_edge_id) || (m_graph.enable_edge(id) && m_graph.enable_edge(id+1));
}
template<typename Ext>
bool theory_utvpi<Ext>::is_consistent() const {
return m_graph.is_feasible();
}
template<typename Ext>
bool theory_utvpi<Ext>::is_parity_ok(unsigned i) const {
th_var v1 = to_var(i);
th_var v2 = neg(v1);
rational r1 = m_graph.get_assignment(v1).get_rational();
rational r2 = m_graph.get_assignment(v2).get_rational();
return r1.is_even() == r2.is_even();
}
/**
\brief adjust values for variables in the difference graph
such that for variables of integer sort it is
the case that x^+ - x^- is even.
The informal justification for the procedure enforce_parity relies
on a set of properties:
1. the graph does not contain a strongly connected component where
x^+ and x+- are connected. They can be independently changed.
This is checked prior to enforce_parity.
2. When x^+ - x^- is odd, the values are adjusted by first
decrementing the value of x^+, provided x^- is not 0-dependent.
Otherwise decrement x^-.
x^- is "0-dependent" if there is a set of tight
inequalities from x^+ to x^-.
3. The affinity to x^+ (the same component of x^+) ensures that
the parity is broken only a finite number of times when
traversing that component. Namely, suppose that the parity of y
gets broken when fixing 'x'. Then first note that 'y' cannot
be equal to 'x'. If it were, then we have a state where:
parity(x^+) != parity(x^-) and
parity(y^+) == parity(y^-)
but x^+ and y^+ are tightly connected and x^- and y^- are
also tightly connected using two copies of the same inequalities.
This is a contradiction.
Thus, 'y' cannot be equal to 'x' if 'y's parity gets broken when
repairing 'x'.
*/
template<typename Ext>
void theory_utvpi<Ext>::enforce_parity() {
unsigned_vector todo;
unsigned sz = get_num_vars();
for (unsigned i = 0; i < sz; ++i) {
enode* e = get_enode(i);
if (a.is_int(e->get_owner()) && !is_parity_ok(i)) {
todo.push_back(i);
}
}
if (todo.empty()) {
return;
}
while (!todo.empty()) {
unsigned i = todo.back();
todo.pop_back();
if (is_parity_ok(i)) {
continue;
}
th_var v1 = to_var(i);
th_var v2 = neg(v1);
int_vector zero_v;
m_graph.compute_zero_succ(v1, zero_v);
for (unsigned j = 0; j < zero_v.size(); ++j) {
if (zero_v[j] == v2) {
zero_v.reset();
m_graph.compute_zero_succ(v2, zero_v);
break;
}
}
TRACE("utvpi",
tout << "Disparity: " << v1 << "\n";
for (unsigned j = 0; j < zero_v.size(); ++j) {
tout << "decrement: " << zero_v[j] << "\n";
});
for (unsigned j = 0; j < zero_v.size(); ++j) {
int v = zero_v[j];
m_graph.acc_assignment(v, numeral(-1));
th_var k = from_var(v);
if (!is_parity_ok(k)) {
todo.push_back(k);
}
}
}
SASSERT(m_graph.is_feasible());
DEBUG_CODE(
for (unsigned i = 0; i < sz; ++i) {
enode* e = get_enode(i);
if (a.is_int(e->get_owner()) && !is_parity_ok(i)) {
IF_VERBOSE(0, verbose_stream() << "disparities not fixed\n";);
UNREACHABLE();
}
});
}
// models:
template<typename Ext>
void theory_utvpi<Ext>::init_model(model_generator & m) {
m_factory = alloc(arith_factory, get_manager());
m.register_factory(m_factory);
enforce_parity();
m_graph.set_to_zero(to_var(m_zero_int), to_var(m_zero_real));
m_graph.set_to_zero(neg(to_var(m_zero_int)), neg(to_var(m_zero_real)));
m_graph.set_to_zero(to_var(m_zero_int), neg(to_var(m_zero_int)));
compute_delta();
DEBUG_CODE(validate_model(););
}
template<typename Ext>
void theory_utvpi<Ext>::validate_model() {
context& ctx = get_context();
unsigned sz = m_atoms.size();
for (unsigned i = 0; i < sz; ++i) {
bool_var b = m_atoms[i].get_bool_var();
if (!ctx.is_relevant(b)) {
continue;
}
bool ok = true;
expr* e = ctx.bool_var2expr(b);
lbool assign = ctx.get_assignment(b);
switch(assign) {
case l_true:
ok = eval(e);
break;
case l_false:
ok = !eval(e);
break;
default:
break;
}
CTRACE("utvpi", !ok,
tout << "validation failed:\n";
tout << "Assignment: " << assign << "\n";
m_atoms[i].display(*this, tout);
tout << "\n";
display(tout);
m_graph.display_agl(tout);
);
if (!ok) {
std::cout << "validation failed:\n";
std::cout << "Assignment: " << assign << "\n";
m_atoms[i].display(*this, std::cout);
std::cout << "\n";
display(std::cout);
m_graph.display_agl(std::cout);
}
// CTRACE("utvpi", ok, tout << "validation success: " << mk_pp(e, get_manager()) << "\n";);
SASSERT(ok);
}
}
template<typename Ext>
bool theory_utvpi<Ext>::eval(expr* e) {
expr* e1, *e2;
if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) {
return eval_num(e1) <= eval_num(e2);
}
if (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) {
return eval_num(e1) < eval_num(e2);
}
if (get_manager().is_eq(e, e1, e2)) {
return eval_num(e1) == eval_num(e2);
}
TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";);
return false;
}
template<typename Ext>
rational theory_utvpi<Ext>::eval_num(expr* e) {
rational r;
expr* e1, *e2;
if (a.is_numeral(e, r)) {
return r;
}
if (a.is_sub(e, e1, e2)) {
return eval_num(e1) - eval_num(e2);
}
if (a.is_add(e)) {
r.reset();
for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) {
r += eval_num(to_app(e)->get_arg(i));
}
return r;
}
if (a.is_mul(e)) {
r = rational(1);
for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) {
r *= eval_num(to_app(e)->get_arg(i));
}
return r;
}
if (a.is_uminus(e, e1)) {
return -eval_num(e1);
}
if (a.is_to_real(e, e1)) {
return eval_num(e1);
}
if (is_uninterp_const(e)) {
return mk_value(mk_var(e), a.is_int(e));
}
TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";);
UNREACHABLE();
return rational(0);
}
template<typename Ext>
rational theory_utvpi<Ext>::mk_value(th_var v, bool is_int) {
SASSERT(v != null_theory_var);
numeral val1 = m_graph.get_assignment(to_var(v));
numeral val2 = m_graph.get_assignment(neg(to_var(v)));
numeral val = val1 - val2;
rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational());
num = num/rational(2);
SASSERT(!is_int || num.is_int());
TRACE("utvpi",
expr* n = get_enode(v)->get_owner();
tout << mk_pp(n, get_manager()) << " |-> (" << val1 << " - " << val2 << ")/2 = " << num << "\n";);
return num;
}
template<typename Ext>
model_value_proc * theory_utvpi<Ext>::mk_value(enode * n, model_generator & mg) {
theory_var v = n->get_th_var(get_id());
bool is_int = a.is_int(n->get_owner());
rational num = mk_value(v, is_int);
TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";);
return alloc(expr_wrapper_proc, m_factory->mk_value(num, is_int));
}
/**
\brief Compute numeral values for the infinitesimals to satisfy the inequalities.
*/
template<typename Ext>
void theory_utvpi<Ext>::compute_delta() {
m_delta = rational(1);
unsigned sz = m_graph.get_num_edges();
for (unsigned i = 0; i < sz; ++i) {
if (!m_graph.is_enabled(i)) {
continue;
}
numeral w = m_graph.get_weight(i);
numeral tgt = m_graph.get_assignment(m_graph.get_target(i));
numeral src = m_graph.get_assignment(m_graph.get_source(i));
numeral b = tgt - src - w;
SASSERT(b.is_nonpos());
rational eps_r = b.get_infinitesimal();
// Given: b <= 0
// suppose that 0 < b.eps
// then we have 0 > b.num
// then delta must ensure:
// 0 >= b.num + delta*b.eps
// <=>
// -b.num/b.eps >= delta
if (eps_r.is_pos()) {
rational num_r = -b.get_rational();
SASSERT(num_r.is_pos());
rational new_delta = num_r/eps_r;
if (new_delta < m_delta) {
m_delta = new_delta;
}
}
}
}
};
#endif