3
0
Fork 0
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:
Nikolaj Bjorner 2013-04-28 12:47:55 -07:00
parent 4f9247a28a
commit 9158fb17c1
12 changed files with 3397 additions and 208 deletions

View file

@ -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() {

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,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

@ -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();
}

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);
@ -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();

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;
}
}

342
src/smt/theory_horn_ineq.h Normal file
View 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_ */

File diff suppressed because it is too large Load diff

159
src/smt/theory_utvpi.cpp Normal file
View 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
View 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
View 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
View 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_ */