mirror of
https://github.com/Z3Prover/z3
synced 2025-04-12 12:08:18 +00:00
add special procedures for UTVPI and horn arithmetic
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
4f9247a28a
commit
9158fb17c1
|
@ -118,7 +118,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 +131,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,6 +264,12 @@ class dl_graph {
|
|||
m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight();
|
||||
}
|
||||
|
||||
bool is_tight(edge_id e) const {
|
||||
edge const& edge = m_edges[e];
|
||||
return edge.is_enabled() &&
|
||||
m_assignment[edge.get_target()] - m_assignment[e.get_source()] == e.get_weight();
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
// An assignment is feasible if all edges are feasible.
|
||||
|
@ -472,8 +478,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 +662,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 +757,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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -59,109 +59,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 +160,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;
|
||||
|
@ -296,8 +205,6 @@ namespace smt {
|
|||
return get_family_id() == n->get_family_id();
|
||||
}
|
||||
|
||||
void del_clause_eh(clause* cls);
|
||||
|
||||
public:
|
||||
theory_diff_logic(ast_manager& m, smt_params & params):
|
||||
theory(m.mk_family_id("arith")),
|
||||
|
@ -316,7 +223,7 @@ namespace smt {
|
|||
m_nc_functor(*this) {
|
||||
}
|
||||
|
||||
~theory_diff_logic() {
|
||||
virtual ~theory_diff_logic() {
|
||||
reset_eh();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -334,6 +314,7 @@ void theory_diff_logic<Ext>::collect_statistics(::statistics & st) const {
|
|||
st.update("dl asserts", m_stats.m_num_assertions);
|
||||
st.update("core->dl eqs", m_stats.m_num_core2th_eqs);
|
||||
m_arith_eq_adapter.collect_statistics(st);
|
||||
m_graph.collect_statistics(st);
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
|
@ -497,45 +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>::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) {
|
||||
|
||||
|
@ -584,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) {
|
||||
|
@ -608,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);
|
||||
}
|
||||
clause* cls = 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);
|
||||
|
@ -906,30 +852,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();
|
||||
|
|
236
src/smt/theory_horn_ineq.cpp
Normal file
236
src/smt/theory_horn_ineq.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
342
src/smt/theory_horn_ineq.h
Normal file
342
src/smt/theory_horn_ineq.h
Normal file
|
@ -0,0 +1,342 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
theory_horn_ineq.h
|
||||
|
||||
Abstract:
|
||||
|
||||
|
||||
A*x <= b + D*x, coefficients to A and D are non-negative,
|
||||
D is a diagonal matrix.
|
||||
Coefficients to b may have both signs.
|
||||
|
||||
|
||||
Ford-Fulkerson variant:
|
||||
Label variables by weight.
|
||||
Select inequality that is not satisfied.
|
||||
Set gamma(LHS) := 0
|
||||
Set gamma(RHS(x)) := weight(x) - b
|
||||
Propagate gamma through inequalities.
|
||||
Gamma is the increment.
|
||||
Maintain Heap of variables weighted by gamma.
|
||||
When processing inequality,
|
||||
then update gamma of variables by gamma := A(gamma + weight) - b
|
||||
If some variable in the premise of the original rule gets
|
||||
relabeled (assignment is increased), then the set of
|
||||
inequalities is unsatisfiable.
|
||||
|
||||
Propagation updates lower bounds on gamma by taking into
|
||||
account integer inequalities. The greatest lower bound
|
||||
is computable by taking integer floor/ceilings.
|
||||
|
||||
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 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;
|
||||
static const clause_id null_clause_id = UINT_MAX;
|
||||
|
||||
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) {}
|
||||
virtual ~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_ */
|
1166
src/smt/theory_horn_ineq_def.h
Normal file
1166
src/smt/theory_horn_ineq_def.h
Normal file
File diff suppressed because it is too large
Load diff
159
src/smt/theory_utvpi.cpp
Normal file
159
src/smt/theory_utvpi.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*++
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
331
src/smt/theory_utvpi.h
Normal file
331
src/smt/theory_utvpi.h
Normal file
|
@ -0,0 +1,331 @@
|
|||
/*++
|
||||
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 literal 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) {}
|
||||
virtual ~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;
|
||||
theory_utvpi& m_super;
|
||||
public:
|
||||
nc_functor(theory_utvpi& s) : m_super(s) {}
|
||||
void reset() { m_antecedents.reset(); }
|
||||
literal_vector const& get_lits() const { return m_antecedents; }
|
||||
|
||||
void operator()(literal const & ex) {
|
||||
if (ex != null_literal) {
|
||||
m_antecedents.push_back(ex);
|
||||
}
|
||||
}
|
||||
|
||||
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_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:
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
th_var not(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_ */
|
694
src/smt/theory_utvpi_def.h
Normal file
694
src/smt/theory_utvpi_def.h
Normal file
|
@ -0,0 +1,694 @@
|
|||
/*++
|
||||
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)
|
||||
5. 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_asserted_qhead(0),
|
||||
m_nc_functor(*this),
|
||||
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();
|
||||
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")));
|
||||
params.resize(lits.size()+1, parameter(rational(1)));
|
||||
}
|
||||
|
||||
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) {
|
||||
TRACE("utvpi", tout << "found non horn logic expression:\n" << mk_pp(n, get_manager()) << "\n";);
|
||||
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?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>
|
||||
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";);
|
||||
TRACE("utvpi", tout << "Terms may not be internalized " << mk_pp(term, get_manager()) << "\n";);
|
||||
found_non_utvpi_expr(term);
|
||||
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());
|
||||
TRACE("utvpi", display(tout););
|
||||
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 {
|
||||
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)));
|
||||
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));
|
||||
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 assignments", m_stats.m_num_assertions);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
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, l);
|
||||
m_graph.add_edge(neg(w1), pos(w1), -weight-weight, l);
|
||||
}
|
||||
else if (terms.size() == 1 && !pos1) {
|
||||
m_graph.add_edge(pos(w1), neg(w1), -weight-weight, l);
|
||||
m_graph.add_edge(pos(w1), neg(w1), -weight-weight, l);
|
||||
}
|
||||
else if (pos1 && pos2) {
|
||||
m_graph.add_edge(neg(w2), pos(w1), -weight, l);
|
||||
m_graph.add_edge(neg(w1), pos(w2), -weight, l);
|
||||
}
|
||||
else if (pos1 && !pos2) {
|
||||
m_graph.add_edge(pos(w2), pos(w1), -weight, l);
|
||||
m_graph.add_edge(neg(w1), neg(w2), -weight, l);
|
||||
}
|
||||
else if (!pos1 && pos2) {
|
||||
m_graph.add_edge(neg(w2), neg(w1), -weight, l);
|
||||
m_graph.add_edge(pos(w1), pos(w2), -weight, l);
|
||||
}
|
||||
else {
|
||||
m_graph.add_edge(pos(w1), neg(w2), -weight, l);
|
||||
m_graph.add_edge(pos(w2), neg(w1), -weight, l);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
// 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);
|
||||
// TBD: enforce strong or tight coherence?
|
||||
compute_delta();
|
||||
}
|
||||
|
||||
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());
|
||||
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);
|
||||
TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";);
|
||||
return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner())));
|
||||
}
|
||||
|
||||
/**
|
||||
\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
|
||||
|
409
src/util/inf_eps_rational.h
Normal file
409
src/util/inf_eps_rational.h
Normal file
|
@ -0,0 +1,409 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
inf_eps_rational.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Rational numbers with infinity and epsilon.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-4-23.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _INF_EPS_RATIONAL_H_
|
||||
#define _INF_EPS_RATIONAL_H_
|
||||
#include<stdlib.h>
|
||||
#include<string>
|
||||
#include"debug.h"
|
||||
#include"vector.h"
|
||||
#include"rational.h"
|
||||
|
||||
template<typename Numeral>
|
||||
class inf_eps_rational {
|
||||
rational m_infty;
|
||||
Numeral m_r;
|
||||
public:
|
||||
|
||||
unsigned hash() const {
|
||||
return m_infty.hash() ^ m_r.hash();
|
||||
}
|
||||
|
||||
struct hash_proc { unsigned operator()(inf_eps_rational const& r) const { return r.hash(); } };
|
||||
|
||||
struct eq_proc { bool operator()(inf_eps_rational const& r1, inf_eps_rational const& r2) const { return r1 == r2; } };
|
||||
|
||||
void swap(inf_eps_rational & n) {
|
||||
m_infty.swap(n.m_infty);
|
||||
m_r.swap(n.m_r);
|
||||
}
|
||||
|
||||
std::string to_string() const {
|
||||
if (m_infty.is_zero()) {
|
||||
return m_r.to_string();
|
||||
}
|
||||
std::string si;
|
||||
if (m_infty.is_one()) {
|
||||
si = "oo";
|
||||
}
|
||||
else if (m_infty.is_minus_one()) {
|
||||
si = "-oo";
|
||||
}
|
||||
else {
|
||||
si = m_infty.to_string() += "*oo";
|
||||
}
|
||||
if (m_r.is_zero()) {
|
||||
return si;
|
||||
}
|
||||
std::string s = "(";
|
||||
s += si;
|
||||
s += " + ";
|
||||
s += m_r.to_string();
|
||||
s += ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
inf_eps_rational():
|
||||
m_infty(),
|
||||
m_r()
|
||||
{}
|
||||
|
||||
inf_eps_rational(const inf_eps_rational & r):
|
||||
m_infty(r.m_infty),
|
||||
m_r(r.m_r)
|
||||
{}
|
||||
|
||||
explicit inf_eps_rational(int n):
|
||||
m_infty(),
|
||||
m_r(n)
|
||||
{}
|
||||
|
||||
explicit inf_eps_rational(Numeral const& r):
|
||||
m_infty(),
|
||||
m_r(r)
|
||||
{}
|
||||
|
||||
explicit inf_eps_rational(rational const& i, Numeral const& r):
|
||||
m_infty(i),
|
||||
m_r(r) {
|
||||
}
|
||||
|
||||
~inf_eps_rational() {}
|
||||
|
||||
/**
|
||||
\brief Set inf_eps_rational to 0.
|
||||
*/
|
||||
void reset() {
|
||||
m_infty.reset();
|
||||
m_r.reset();
|
||||
}
|
||||
|
||||
bool is_int() const {
|
||||
return m_infty.is_zero() && m_r.is_int();
|
||||
}
|
||||
|
||||
bool is_int64() const {
|
||||
return m_infty.is_zero() && m_r.is_int64();
|
||||
}
|
||||
|
||||
bool is_uint64() const {
|
||||
return m_infty.is_zero() && m_r.is_uint64();
|
||||
}
|
||||
|
||||
bool is_rational() const { return m_infty.is_zero() && m_r.is_rational(); }
|
||||
|
||||
int64 get_int64() const {
|
||||
SASSERT(is_int64());
|
||||
return m_r.get_int64();
|
||||
}
|
||||
|
||||
uint64 get_uint64() const {
|
||||
SASSERT(is_uint64());
|
||||
return m_r.get_uint64();
|
||||
}
|
||||
|
||||
rational const& get_rational() const {
|
||||
return m_r.get_rational();
|
||||
}
|
||||
|
||||
rational const& get_infinitesimal() const {
|
||||
return m_r.get_infinitesimal();
|
||||
}
|
||||
|
||||
rational const& get_infinity() const {
|
||||
return m_infty;
|
||||
}
|
||||
|
||||
inf_eps_rational & operator=(const inf_eps_rational & r) {
|
||||
m_infty = r.m_infty;
|
||||
m_r = r.m_r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inf_eps_rational & operator=(const rational & r) {
|
||||
m_infty.reset();
|
||||
m_r = r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inf_eps_rational & operator+=(const inf_eps_rational & r) {
|
||||
m_infty += r.m_infty;
|
||||
m_r += r.m_r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inf_eps_rational & operator-=(const inf_eps_rational & r) {
|
||||
m_infty -= r.m_infty;
|
||||
m_r -= r.m_r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inf_eps_rational & operator+=(const rational & r) {
|
||||
m_r += r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inf_eps_rational & operator-=(const rational & r) {
|
||||
m_r -= r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inf_eps_rational & operator*=(const rational & r1) {
|
||||
m_infty *= r1;
|
||||
m_r *= r1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inf_eps_rational & operator/=(const rational & r) {
|
||||
m_infty /= r;
|
||||
m_r /= r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
inf_eps_rational & operator++() {
|
||||
++m_r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const inf_eps_rational operator++(int) { inf_eps_rational tmp(*this); ++(*this); return tmp; }
|
||||
|
||||
inf_eps_rational & operator--() {
|
||||
--m_r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const inf_eps_rational operator--(int) { inf_eps_rational tmp(*this); --(*this); return tmp; }
|
||||
|
||||
friend inline bool operator==(const inf_eps_rational & r1, const inf_eps_rational & r2) {
|
||||
return r1.m_infty == r2.m_infty && r1.m_r == r2.m_r;
|
||||
}
|
||||
|
||||
friend inline bool operator==(const rational & r1, const inf_eps_rational & r2) {
|
||||
return r1 == r2.m_infty && r2.m_r.is_zero();
|
||||
}
|
||||
|
||||
friend inline bool operator==(const inf_eps_rational & r1, const rational & r2) {
|
||||
return r1.m_infty == r2 && r1.m_r.is_zero();
|
||||
}
|
||||
|
||||
friend inline bool operator<(const inf_eps_rational & r1, const inf_eps_rational & r2) {
|
||||
return
|
||||
(r1.m_infty < r2.m_infty) ||
|
||||
(r1.m_infty == r2.m_infty && r1.m_r < r2.m_r);
|
||||
}
|
||||
|
||||
friend inline bool operator<(const rational & r1, const inf_eps_rational & r2) {
|
||||
return
|
||||
r2.m_infty.is_pos() ||
|
||||
(r2.m_infty.is_zero() && r1 < r2.m_r);
|
||||
}
|
||||
|
||||
friend inline bool operator<(const inf_eps_rational & r1, const rational & r2) {
|
||||
return
|
||||
r1.m_infty.is_neg() ||
|
||||
(r1.m_infty.is_zero() && r1.m_r < r2);
|
||||
}
|
||||
|
||||
void neg() {
|
||||
m_infty.neg();
|
||||
m_r.neg();
|
||||
}
|
||||
|
||||
bool is_zero() const {
|
||||
return m_infty.is_zero() && m_r.is_zero();
|
||||
}
|
||||
|
||||
bool is_one() const {
|
||||
return m_infty.is_zero() && m_r.is_one();
|
||||
}
|
||||
|
||||
bool is_minus_one() const {
|
||||
return m_infty.is_zero() && m_r.is_minus_one();
|
||||
}
|
||||
|
||||
bool is_neg() const {
|
||||
return
|
||||
m_infty.is_neg() ||
|
||||
(m_infty.is_zero() && m_r.is_neg());
|
||||
}
|
||||
|
||||
bool is_pos() const {
|
||||
return
|
||||
m_infty.is_pos() ||
|
||||
(m_infty.is_zero() && m_r.is_pos());
|
||||
}
|
||||
|
||||
bool is_nonneg() const {
|
||||
return
|
||||
m_infty.is_pos() ||
|
||||
(m_infty.is_zero() && m_r.is_nonneg());
|
||||
}
|
||||
|
||||
bool is_nonpos() const {
|
||||
return
|
||||
m_infty.is_neg() ||
|
||||
(m_infty.is_zero() && m_r.is_nonpos());
|
||||
}
|
||||
|
||||
friend inline rational floor(const inf_eps_rational & r) {
|
||||
SASSERT(r.m_infty.is_zero());
|
||||
return floor(r.m_r);
|
||||
}
|
||||
|
||||
friend inline rational ceil(const inf_eps_rational & r) {
|
||||
SASSERT(r.m_infty.is_zero());
|
||||
return ceil(r.m_r);
|
||||
}
|
||||
|
||||
|
||||
// Perform: this += c * k
|
||||
void addmul(const rational & c, const inf_eps_rational & k) {
|
||||
m_infty.addmul(c, k.m_infty);
|
||||
m_r.addmul(c, k.m_r);
|
||||
}
|
||||
|
||||
// Perform: this += c * k
|
||||
void submul(const rational & c, const inf_eps_rational & k) {
|
||||
m_infty.submul(c, k.m_infty);
|
||||
m_r.submul(c, k.m_r);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename N>
|
||||
inline bool operator!=(const inf_eps_rational<N> & r1, const inf_eps_rational<N> & r2) {
|
||||
return !operator==(r1, r2);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator!=(const rational & r1, const inf_eps_rational<N> & r2) {
|
||||
return !operator==(r1, r2);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator!=(const inf_eps_rational<N> & r1, const rational & r2) {
|
||||
return !operator==(r1, r2);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator>(const inf_eps_rational<N> & r1, const inf_eps_rational<N> & r2) {
|
||||
return operator<(r2, r1);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator>(const inf_eps_rational<N> & r1, const rational & r2) {
|
||||
return operator<(r2, r1);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator>(const rational & r1, const inf_eps_rational<N> & r2) {
|
||||
return operator<(r2, r1);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator<=(const inf_eps_rational<N> & r1, const inf_eps_rational<N> & r2) {
|
||||
return !operator>(r1, r2);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator<=(const rational & r1, const inf_eps_rational<N> & r2) {
|
||||
return !operator>(r1, r2);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator<=(const inf_eps_rational<N> & r1, const rational & r2) {
|
||||
return !operator>(r1, r2);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator>=(const inf_eps_rational<N> & r1, const inf_eps_rational<N> & r2) {
|
||||
return !operator<(r1, r2);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator>=(const rational & r1, const inf_eps_rational<N> & r2) {
|
||||
return !operator<(r1, r2);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline bool operator>=(const inf_eps_rational<N> & r1, const rational & r2) {
|
||||
return !operator<(r1, r2);
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline inf_eps_rational<N> operator+(const inf_eps_rational<N> & r1, const inf_eps_rational<N> & r2) {
|
||||
return inf_eps_rational<N>(r1) += r2;
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline inf_eps_rational<N> operator-(const inf_eps_rational<N> & r1, const inf_eps_rational<N> & r2) {
|
||||
return inf_eps_rational<N>(r1) -= r2;
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline inf_eps_rational<N> operator-(const inf_eps_rational<N> & r) {
|
||||
inf_eps_rational<N> result(r);
|
||||
result.neg();
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline inf_eps_rational<N> operator*(const rational & r1, const inf_eps_rational<N> & r2) {
|
||||
inf_eps_rational<N> result(r2);
|
||||
result *= r1;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline inf_eps_rational<N> operator*(const inf_eps_rational<N> & r1, const rational & r2) {
|
||||
return r2 * r1;
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline inf_eps_rational<N> operator/(const inf_eps_rational<N> & r1, const rational & r2) {
|
||||
inf_eps_rational<N> result(r1);
|
||||
result /= r2;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline std::ostream & operator<<(std::ostream & target, const inf_eps_rational<N> & r) {
|
||||
target << r.to_string();
|
||||
return target;
|
||||
}
|
||||
|
||||
template<typename N>
|
||||
inline inf_eps_rational<N> abs(const inf_eps_rational<N> & r) {
|
||||
inf_eps_rational<N> result(r);
|
||||
if (result.is_neg()) {
|
||||
result.neg();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* _INF_EPS_RATIONAL_H_ */
|
Loading…
Reference in a new issue