z3-z3-4.13.0.src.smt.theory_utvpi_def.h Maven / Gradle / Ivy
The newest version!
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
theory_utvpi_def.h
Abstract:
Implementation of UTVPI solver.
Author:
Nikolaj Bjorner (nbjorner) 2013-04-26
Revision History:
1. introduce x^+ and x^-, such that 2*x := x^+ - x^-
2. rewrite constraints as follows:
x - y <= k => x^+ - y^+ <= k
y^- - x^- <= k
x <= k => x^+ - x^- <= 2k
x + y <= k => x^+ - y^- <= k
y^+ - x^- <= k
- x - y <= k => x^- - y^+ <= k
y^- - x^+ <= k
3. Solve for x^+ and x^-
4. Check parity condition for integers (see Lahiri and Musuvathi 05)
This checks if x^+ and x^- are in the same component but of different
parities.
5. Enforce parity on variables. This checks if x^+ and x^- have different
parities. If they have different parities, the assignment to one
of the variables is decremented (choose the variable that is not tightly
constrained with 0).
The process that adjusts parities converges: Suppose we break a parity
of a different variable y while fixing x's parity. A cyclic breaking/fixing
of parities implies there is a strongly connected component between x, y
and the two polarities of the variables. This contradicts the test in 4.
6. extract model for M(x) := (M(x^+)- M(x^-))/2
--*/
#pragma once
#include "smt/theory_utvpi.h"
#include "util/heap.h"
#include "ast/ast_pp.h"
#include "smt/smt_context.h"
namespace smt {
template
theory_utvpi::theory_utvpi(context& ctx):
theory(ctx, ctx.get_manager().mk_family_id("arith")),
a(ctx.get_manager()),
m_arith_eq_adapter(*this, a),
m_consistent(true),
m_izero(null_theory_var),
m_rzero(null_theory_var),
m_nc_functor(*this),
m_asserted_qhead(0),
m_agility(0.5),
m_lia(false),
m_lra(false),
m_non_utvpi_exprs(false),
m_test(ctx.get_manager()),
m_factory(nullptr),
m_var_value_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)) {
}
template
theory_utvpi::~theory_utvpi() {
reset_eh();
}
template
std::ostream& theory_utvpi::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
theory_var theory_utvpi::mk_var(enode* n) {
th_var v = theory::mk_var(n);
TRACE("utvpi", tout << v << " " << mk_pp(n->get_expr(), m) << "\n";);
m_graph.init_var(to_var(v));
m_graph.init_var(neg(to_var(v)));
ctx.attach_th_var(n, this, v);
return v;
}
template
theory_var theory_utvpi::mk_var(expr* n) {
enode* e = nullptr;
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
void theory_utvpi::reset_eh() {
m_graph .reset();
m_izero = null_theory_var;
m_rzero = 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
void theory_utvpi::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) {
rational k(0);
th_var s = expand(true, v1, k);
th_var t = expand(false, v2, k);
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.
//
// t1 - s1 = k
//
app_ref eq(m), s2(m), t2(m);
app* s1 = get_enode(s)->get_expr();
app* t1 = get_enode(t)->get_expr();
s2 = a.mk_sub(t1, s1);
t2 = a.mk_numeral(k, s2->get_sort());
eq = m.mk_eq(s2.get(), t2.get());
TRACE("utvpi", tout << v1 << " .. " << v2 << "\n" << eq << "\n";);
VERIFY (internalize_atom(eq.get(), false));
literal l(ctx.get_literal(eq.get()));
if (!is_eq) {
l.neg();
}
ctx.assign(l, b_justification(&eq_just), false);
}
}
template
void theory_utvpi::inc_conflicts() {
ctx.push_trail(value_trail(m_consistent));
m_consistent = false;
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
void theory_utvpi::set_conflict() {
inc_conflicts();
literal_vector const& lits = m_nc_functor.get_lits();
IF_VERBOSE(20, ctx.display_literals_smt2(verbose_stream() << "conflict:\n", lits));
TRACE("utvpi", ctx.display_literals_smt2(tout << "conflict:\n", lits););
vector params;
if (m.proofs_enabled()) {
params.push_back(parameter(symbol("farkas")));
for (unsigned i = 0; i < m_nc_functor.get_coeffs().size(); ++i) {
params.push_back(parameter(rational(m_nc_functor.get_coeffs()[i])));
}
}
ctx.set_conflict(
ctx.mk_justification(
ext_theory_conflict_justification(
get_id(), ctx,
lits.size(), lits.data(), 0, nullptr, params.size(), params.data())));
m_nc_functor.reset();
}
template
void theory_utvpi::found_non_utvpi_expr(expr* n) {
if (m_non_utvpi_exprs) {
return;
}
std::stringstream msg;
msg << "found non utvpi logic expression:\n" << mk_pp(n, m) << '\n';
auto str = msg.str();
TRACE("utvpi", tout << str;);
warning_msg("%s", str.c_str());
ctx.push_trail(value_trail(m_non_utvpi_exprs));
m_non_utvpi_exprs = true;
}
template
void theory_utvpi::init_zero() {
if (m_izero == null_theory_var) {
m_izero = mk_var(ctx.mk_enode(a.mk_numeral(rational(0), true), false, false, true));
m_rzero = 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
void theory_utvpi::negate(coeffs& coeffs, rational& weight) {
for (auto& c : coeffs) {
c.second.neg();
}
weight.neg();
}
template
typename theory_utvpi::numeral theory_utvpi::mk_weight(bool is_real, bool is_strict, rational const& w) const {
if (is_strict) {
return numeral(w) + (is_real?Ext::m_epsilon:numeral(1));
}
else {
return numeral(w);
}
}
template
void theory_utvpi::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) {
coeffs.reset();
w = m_test.get_weight();
for (auto const& t : terms) {
coeffs.push_back(std::make_pair(mk_var(t.first), t.second));
}
}
template
void theory_utvpi::internalize_eq_eh(app * atom, bool_var v) {
app * lhs = to_app(atom->get_arg(0));
app * rhs = to_app(atom->get_arg(1));
if (a.is_numeral(rhs)) {
std::swap(rhs, lhs);
}
if (!a.is_numeral(rhs)) {
return;
}
if (a.is_add(lhs) || a.is_sub(lhs)) {
// force axioms for (= (+ x y) k)
// this is necessary because (+ x y) is not expressible as a utvpi term.
m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs));
}
}
template
bool theory_utvpi::internalize_atom(app * n, bool) {
if (!m_consistent)
return false;
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);
if (coeffs.empty()) {
found_non_utvpi_expr(n);
return false;
}
bool_var bv = ctx.mk_bool_var(n);
ctx.set_var_theory(bv, get_id());
literal l(bv);
m_bool_var2atom.insert(bv, m_atoms.size());
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_atoms.push_back(atom(bv, pos, neg));
TRACE("utvpi",
tout << mk_pp(n, m) << "\n";
m_graph.display_edge(tout << "pos: ", pos);
m_graph.display_edge(tout << "neg: ", neg);
);
return true;
}
template
bool theory_utvpi::internalize_term(app * term) {
if (!m_consistent)
return false;
bool result = !ctx.inconsistent() && null_theory_var != mk_term(term);
CTRACE("utvpi", !result, tout << "Did not internalize " << mk_pp(term, m) << "\n";);
return result;
}
template
void theory_utvpi::assign_eh(bool_var v, bool is_true) {
m_stats.m_num_assertions++;
unsigned idx = m_bool_var2atom.find(v);
SASSERT(ctx.get_assignment(v) != l_undef);
SASSERT((ctx.get_assignment(v) == l_true) == is_true);
m_atoms[idx].assign_eh(is_true);
m_asserted_atoms.push_back(idx);
}
template
void theory_utvpi::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
void theory_utvpi::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
final_check_status theory_utvpi::final_check_eh() {
SASSERT(is_consistent());
if (can_propagate()) {
propagate();
return FC_CONTINUE;
}
else if (!check_z_consistency()) {
return FC_CONTINUE;
}
else if (has_shared() && assume_eqs_core()) {
return FC_CONTINUE;
}
else if (m_non_utvpi_exprs) {
return FC_GIVEUP;
}
else {
return FC_DONE;
}
}
template
bool theory_utvpi::has_shared() {
auto sz = static_cast(get_num_vars());
for (theory_var v = 0; v < sz; ++v) {
if (is_relevant_and_shared(get_enode(v)))
return true;
}
return false;
}
template
bool theory_utvpi::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_expr())) {
continue;
}
th_var v1 = to_var(i);
th_var v2 = neg(v1);
rational r1 = m_graph.get_assignment(v1).get_rational();
rational r2 = m_graph.get_assignment(v2).get_rational();
SASSERT(r1.is_int());
SASSERT(r2.is_int());
if (r1.is_even() == r2.is_even()) {
continue;
}
if (scc_id[v1] != scc_id[v2]) {
continue;
}
if (scc_id[v1] == -1) {
continue;
}
// they are in the same SCC and have different parities => contradiction.
m_nc_functor.reset();
VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, UINT_MAX, m_nc_functor));
VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, UINT_MAX, m_nc_functor));
IF_VERBOSE(1, verbose_stream() << "parity conflict " << mk_pp(e->get_expr(), m) << "\n";);
set_conflict();
return false;
}
return true;
}
template
void theory_utvpi::display(std::ostream& out) const {
for (auto const& a : m_atoms) {
a.display(*this, out); out << "\n";
}
m_graph.display(out);
}
template
void theory_utvpi::collect_statistics(::statistics& st) const {
st.update("utvpi conflicts", m_stats.m_num_conflicts);
st.update("utvpi asserts", m_stats.m_num_assertions);
st.update("core->utvpi eqs", m_stats.m_num_core2th_eqs);
st.update("core->utvpi diseqs", m_stats.m_num_core2th_diseqs);
m_arith_eq_adapter.collect_statistics(st);
m_graph.collect_statistics(st);
}
template
void theory_utvpi::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
void theory_utvpi::propagate() {
bool consistent = is_consistent() && !ctx.inconsistent();
while (consistent && can_propagate()) {
unsigned idx = m_asserted_atoms[m_asserted_qhead];
m_asserted_qhead++;
consistent = propagate_atom(m_atoms[idx]);
}
}
template
bool theory_utvpi::propagate_atom(atom const& a) {
TRACE("utvpi", a.display(*this, tout); tout << "\n";);
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
theory_var theory_utvpi::mk_term(app* n) {
TRACE("utvpi", tout << mk_pp(n, m) << "\n";);
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() && ctx.e_internalized(n)) {
return coeffs[0].first;
}
if (coeffs.size() == 2) {
// do not create an alias.
found_non_utvpi_expr(n);
return null_theory_var;
}
for (expr* arg : *n) {
if (!ctx.e_internalized(arg))
ctx.internalize(arg, false);
}
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
theory_var theory_utvpi::mk_num(app* n, rational const& r) {
theory_var v = null_theory_var;
if (r.is_zero()) {
v = get_zero(n);
if (!ctx.e_internalized(n)) {
found_non_utvpi_expr(n);
v = null_theory_var;
}
}
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 {
for (expr* arg : *n) {
if (!ctx.e_internalized(arg))
ctx.internalize(arg, false);
}
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
theory_var theory_utvpi::expand(bool pos, th_var v, rational & k) {
enode* e = get_enode(v);
expr* x, *y;
rational r;
for (;;) {
app* n = e->get_expr();
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
edge_id theory_utvpi::add_ineq(vector > const& terms, numeral const& weight, literal l) {
SASSERT(!terms.empty());
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.empty()) {
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(v2 != null_theory_var);
SASSERT(pos2 || terms[1].second.is_minus_one());
}
TRACE("utvpi", tout << (pos1?"$":"-$") << v1;
if (terms.size() == 2) tout << (pos2?" + $":" - $") << v2;
tout << " + " << weight << " <= 0\n";);
edge_id id = m_graph.get_num_edges();
th_var w1 = to_var(v1), w2 = to_var(v2);
if (terms.size() == 1 && pos1) {
m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2));
m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2));
}
else if (terms.size() == 1 && !pos1) {
m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2));
m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2));
}
else if (pos1 && pos2) {
m_graph.add_edge(neg(w2), pos(w1), -weight, std::make_pair(l,1));
m_graph.add_edge(neg(w1), pos(w2), -weight, std::make_pair(l,1));
}
else if (pos1 && !pos2) {
m_graph.add_edge(pos(w2), pos(w1), -weight, std::make_pair(l,1));
m_graph.add_edge(neg(w1), neg(w2), -weight, std::make_pair(l,1));
}
else if (!pos1 && pos2) {
m_graph.add_edge(neg(w2), neg(w1), -weight, std::make_pair(l,1));
m_graph.add_edge(pos(w1), pos(w2), -weight, std::make_pair(l,1));
}
else {
m_graph.add_edge(pos(w1), neg(w2), -weight, std::make_pair(l,1));
m_graph.add_edge(pos(w2), neg(w1), -weight, std::make_pair(l,1));
}
return id;
}
template
bool theory_utvpi::enable_edge(edge_id id) {
return
(id == null_edge_id) ||
(m_graph.enable_edge(id) && m_graph.enable_edge(id + 1));
}
template
bool theory_utvpi::is_consistent() const {
return m_consistent;
}
template
bool theory_utvpi::is_parity_ok(unsigned i) const {
th_var v1 = to_var(i);
th_var v2 = neg(v1);
rational r1 = m_graph.get_assignment(v1).get_rational();
rational r2 = m_graph.get_assignment(v2).get_rational();
return r1.is_even() == r2.is_even();
}
/**
\brief adjust values for variables in the difference graph
such that for variables of integer sort it is
the case that x^+ - x^- is even.
The informal justification for the procedure enforce_parity relies
on a set of properties:
1. the graph does not contain a strongly connected component where
x^+ and x+- are connected. They can be independently changed.
This is checked prior to enforce_parity.
2. When x^+ - x^- is odd, the values are adjusted by first
decrementing the value of x^+, provided x^- is not 0-dependent.
Otherwise decrement x^-.
x^- is "0-dependent" if there is a set of tight
inequalities from x^+ to x^-.
3. The affinity to x^+ (the same component of x^+) ensures that
the parity is broken only a finite number of times when
traversing that component. Namely, suppose that the parity of y
gets broken when fixing 'x'. Then first note that 'y' cannot
be equal to 'x'. If it were, then we have a state where:
parity(x^+) != parity(x^-) and
parity(y^+) == parity(y^-)
but x^+ and y^+ are tightly connected and x^- and y^- are
also tightly connected using two copies of the same inequalities.
This is a contradiction.
Thus, 'y' cannot be equal to 'x' if 'y's parity gets broken when
repairing 'x'.
*/
template
void theory_utvpi::enforce_parity() {
SASSERT(m_graph.is_feasible_dbg());
unsigned_vector todo;
unsigned sz = get_num_vars();
for (unsigned i = 0; i < sz; ++i) {
enode* e = get_enode(i);
if (a.is_int(e->get_expr()) && !is_parity_ok(i)) {
todo.push_back(i);
}
}
if (todo.empty()) {
return;
}
while (!todo.empty()) {
unsigned i = todo.back();
todo.pop_back();
if (is_parity_ok(i)) {
continue;
}
th_var v1 = to_var(i);
th_var v2 = neg(v1);
int_vector zero_v;
m_graph.compute_zero_succ(v1, zero_v);
for (auto v0 : zero_v) {
if (v0 == v2) {
zero_v.reset();
m_graph.compute_zero_succ(v2, zero_v);
break;
}
}
TRACE("utvpi",
tout << "Disparity: " << v1 << " - " << v2 << "\n";
tout << "decrement: " << zero_v << "\n";
display(tout);
);
for (auto v : zero_v) {
m_graph.inc_assignment(v, numeral(-1));
th_var k = from_var(v);
if (!is_parity_ok(k)) {
todo.push_back(k);
}
}
TRACE("utvpi", display(tout););
SASSERT(m_graph.is_feasible_dbg());
}
DEBUG_CODE(
for (unsigned i = 0; i < sz; ++i) {
enode* e = get_enode(i);
if (a.is_int(e->get_expr()) && !is_parity_ok(i)) {
IF_VERBOSE(0, verbose_stream() << "disparities not fixed\n";);
UNREACHABLE();
}
});
SASSERT(m_graph.is_feasible_dbg());
}
// models:
template
void theory_utvpi::init_model(model_generator & m) {
m_factory = alloc(arith_factory, get_manager());
m.register_factory(m_factory);
init_model();
}
template
void theory_utvpi::init_model() {
enforce_parity();
init_zero();
dl_var vs[4] = { to_var(m_izero), neg(to_var(m_izero)), to_var(m_rzero), neg(to_var(m_rzero)) };
m_graph.set_to_zero(4, vs);
compute_delta();
DEBUG_CODE(model_validate(););
}
template
bool theory_utvpi::assume_eqs_core() {
init_model();
return assume_eqs(m_var_value_table);
}
template
void theory_utvpi::model_validate() {
for (auto const& a : m_atoms) {
bool_var b = a.get_bool_var();
if (!ctx.is_relevant(b)) {
continue;
}
bool ok = true;
expr* e = ctx.bool_var2expr(b);
lbool assign = ctx.get_assignment(b);
switch(assign) {
case l_true:
ok = eval(e);
break;
case l_false:
ok = !eval(e);
break;
default:
break;
}
(void)ok;
CTRACE("utvpi", !ok,
tout << "validation failed:\n";
tout << "Assignment: " << assign << "\n";
tout << mk_pp(e, m) << "\n";
a.display(*this, tout);
tout << "\n";
display(tout);
m_graph.display_agl(tout);
);
// CTRACE("utvpi", ok, tout << "validation success: " << mk_pp(e, m) << "\n";);
SASSERT(ok);
}
}
template
bool theory_utvpi::eval(expr* e) {
expr* e1, *e2;
if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) {
return eval_num(e1) <= eval_num(e2);
}
if (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) {
return eval_num(e1) < eval_num(e2);
}
if (m.is_eq(e, e1, e2)) {
return eval_num(e1) == eval_num(e2);
}
TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, m) << "\n";);
return false;
}
template
rational theory_utvpi::eval_num(expr* e) {
rational r;
expr* e1, *e2;
if (a.is_numeral(e, r)) {
return r;
}
if (a.is_sub(e, e1, e2)) {
return eval_num(e1) - eval_num(e2);
}
if (a.is_add(e)) {
r.reset();
for (expr* arg : *to_app(e)) r+= eval_num(arg);
return r;
}
if (a.is_mul(e)) {
r = rational(1);
for (expr* arg : *to_app(e)) r*= eval_num(arg);
return r;
}
if (a.is_uminus(e, e1)) {
return -eval_num(e1);
}
if (a.is_to_real(e, e1)) {
return eval_num(e1);
}
if (is_uninterp_const(e)) {
return mk_value(mk_var(e), a.is_int(e));
}
TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, m) << "\n";);
UNREACHABLE();
return rational(0);
}
template
rational theory_utvpi::mk_value(th_var v, bool is_int) {
SASSERT(v != null_theory_var);
numeral val1 = m_graph.get_assignment(to_var(v));
numeral val2 = m_graph.get_assignment(neg(to_var(v)));
numeral val = val1 - val2;
rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational());
num = num/rational(2);
SASSERT(!is_int || num.is_int());
TRACE("utvpi",
expr* n = get_enode(v)->get_expr();
tout << mk_pp(n, m) << " |-> (" << val1 << " - " << val2 << ")/2 = " << num << "\n";);
return num;
}
template
model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) {
theory_var v = n->get_th_var(get_id());
bool is_int = a.is_int(n->get_expr());
rational num = mk_value(v, is_int);
TRACE("utvpi", tout << mk_pp(n->get_expr(), m) << " |-> " << num << "\n";);
return alloc(expr_wrapper_proc, m_factory->mk_num_value(num, is_int));
}
/**
\brief Compute numeral values for the infinitesimals to satisfy the inequalities.
*/
template
void theory_utvpi::compute_delta() {
m_delta = rational(1,4);
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/(4*eps_r);
if (new_delta < m_delta) {
m_delta = new_delta;
}
}
}
}
template
theory* theory_utvpi::mk_fresh(context* new_ctx) {
return alloc(theory_utvpi, *new_ctx);
}
};