3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-13 04:28:17 +00:00
z3/src/smt/theory_horn_ineq_def.h
Nikolaj Bjorner 717f131942 fix warnings and errors from the mint64 build
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2013-05-01 19:54:40 +01:00

1167 lines
39 KiB
C++

/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
theory_horn_ineq_def.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.
Author:
Nikolaj Bjorner (nbjorner) 2013-04-18
Revision History:
--*/
#ifndef _THEORY_HORN_INEQ_DEF_H_
#define _THEORY_HORN_INEQ_DEF_H_
#include "theory_horn_ineq.h"
#include "ast_pp.h"
#include "smt_context.h"
namespace smt {
static const unsigned null_clause_id = UINT_MAX;
/**
A clause represents an inequality of the form
v1*c1 + v2*c2 + .. + v_n*c_n + w <= v*c
where
- m_vars: [v1,v2,...,v_n]
- m_coeffs: [c1,c2,...,c_n]
- m_var: v
- m_coeff: c
- m_weight: w
*/
template<typename Ext>
class theory_horn_ineq<Ext>::clause {
vector<rational> m_coeffs; // coefficients of body.
svector<th_var> m_vars; // variables of body.
rational m_coeff; // coefficient of head.
th_var m_var; // head variable.
numeral m_weight; // constant to add
literal m_explanation;
bool m_enabled;
public:
clause(unsigned sz, rational const* coeffs, th_var const* vars,
rational const& coeff, th_var var, numeral const& w,
const literal& ex):
m_coeffs(sz, coeffs),
m_vars(sz, vars),
m_coeff(coeff),
m_var(var),
m_weight(w),
m_explanation(ex),
m_enabled(false) {
DEBUG_CODE(
{
for (unsigned i = 0; i < size(); ++i) {
SASSERT(coeffs[i].is_pos());
}
SASSERT(coeff.is_pos());
});
}
th_var vars(unsigned i) const { return m_vars[i]; }
rational const& coeffs(unsigned i) const { return m_coeffs[i]; }
th_var var() const { return m_var; }
rational const& coeff() const { return m_coeff; }
const numeral & get_weight() const { return m_weight; }
const literal & get_explanation() const { return m_explanation; }
bool is_enabled() const { return m_enabled; }
unsigned size() const { return m_vars.size(); }
void enable() { m_enabled = true; }
void disable() { m_enabled = false; }
void display(std::ostream& out) const {
out << (is_enabled()?"+ ":"- ");
for (unsigned i = 0; i < size(); ++i) {
if (i > 0 && coeffs(i).is_pos()) {
out << " + ";
}
display(out, coeffs(i), vars(i));
}
if (!get_weight().is_zero()) {
out << " + " << get_weight();
}
display(out << " <= ", coeff(), var());
out << "\n";
}
private:
void display(std::ostream& out, rational const& c, th_var v) const {
if (!c.is_one()) {
out << c << "*";
}
out << "v" << v;
}
};
template<typename Ext>
class theory_horn_ineq<Ext>::assignment_trail {
th_var m_var;
numeral m_old_value;
public:
assignment_trail(th_var v, const numeral & val):
m_var(v),
m_old_value(val) {}
th_var get_var() const { return m_var; }
const numeral & get_old_value() const { return m_old_value; }
};
template<typename Ext>
class theory_horn_ineq<Ext>::parent_trail {
th_var m_var;
clause_id m_old_value;
public:
parent_trail(th_var v, clause_id val):
m_var(v),
m_old_value(val) {}
th_var get_var() const { return m_var; }
clause_id get_old_value() const { return m_old_value; }
};
template<typename Ext>
class theory_horn_ineq<Ext>::graph : private Ext {
typedef vector<assignment_trail> assignment_stack;
typedef vector<parent_trail> parent_stack;
typedef unsigned_vector clause_id_vector;
struct stats {
unsigned m_propagation_cost;
void reset() {
memset(this, 0, sizeof(*this));
}
};
struct scope {
unsigned m_clauses_lim;
unsigned m_enabled_clauses_lim;
unsigned m_assignment_lim;
unsigned m_parent_lim;
scope(unsigned e, unsigned enabled, unsigned alim, unsigned plim):
m_clauses_lim(e),
m_enabled_clauses_lim(enabled),
m_assignment_lim(alim),
m_parent_lim(plim) {
}
};
stats m_stats;
vector<clause> m_clauses;
vector<numeral> m_assignment; // per var
clause_id_vector m_parent; // per var
assignment_stack m_assignment_stack; // stack for restoring the assignment
parent_stack m_parent_stack; // stack for restoring parents
clause_id_vector m_enabled_clauses;
vector<clause_id_vector> m_out_clauses; // use-list for clauses.
vector<clause_id_vector> m_in_clauses; // clauses that have variable in head.
// forward reachability
unsigned_vector m_onstack;
unsigned m_ts;
unsigned_vector m_todo;
literal_vector m_lits;
vector<rational> m_coeffs;
th_var m_zero;
clause_id m_unsat_clause;
svector<scope> m_trail_stack;
public:
graph(): m_ts(0), m_zero(null_theory_var), m_unsat_clause(null_clause_id) {}
void reset() {
m_clauses .reset();
m_assignment .reset();
m_parent .reset();
m_assignment_stack .reset();
m_parent_stack .reset();
m_out_clauses .reset();
m_in_clauses .reset();
m_enabled_clauses .reset();
m_onstack .reset();
m_ts = 0;
m_lits .reset();
m_trail_stack .reset();
m_unsat_clause = null_clause_id;
}
void traverse_neg_cycle1(bool /*stronger_lemmas*/) {
TRACE("horn_ineq", display(tout););
SASSERT(!m_enabled_clauses.empty());
clause_id id = m_unsat_clause;
SASSERT(id != null_clause_id);
SASSERT(!is_feasible(m_clauses[id]));
clause_id_vector todo;
vector<rational> muls;
todo.push_back(id);
muls.push_back(rational(1));
u_map<rational> lits;
while (!todo.empty()) {
id = todo.back();
rational mul = muls.back();
todo.pop_back();
muls.pop_back();
clause const& cl = m_clauses[id];
literal lit = cl.get_explanation();
if (lit != null_literal) {
lits.insert_if_not_there2(id, rational(0))->get_data().m_value += mul;
}
for (unsigned i = 0; i < cl.size(); ++i) {
id = m_parent[cl.vars(i)];
if (id != null_clause_id) {
todo.push_back(id);
muls.push_back(mul*cl.coeffs(i));
}
}
}
u_map<rational>::iterator it = lits.begin(), end = lits.end();
m_lits.reset();
m_coeffs.reset();
for (; it != end; ++it) {
m_lits.push_back(m_clauses[it->m_key].get_explanation());
m_coeffs.push_back(it->m_value);
}
// TODO: use topological sort to tune traversal of parents to linear.
// (current traversal can be exponential).
// TODO: negative cycle traversal with inline resolution to find
// stronger conflict clauses.
// follow heuristic used in theory_diff_logic_def.h:
}
unsigned get_num_clauses() const {
return m_clauses.size();
}
literal_vector const& get_lits() const {
return m_lits;
}
vector<rational> const& get_coeffs() const {
return m_coeffs;
}
numeral get_assignment(th_var v) const {
return m_assignment[v];
}
numeral eval_body(clause const& cl) const {
numeral v(0);
for (unsigned i = 0; i < cl.size(); ++i) {
v += cl.coeffs(i)*m_assignment[cl.vars(i)];
}
v += cl.get_weight();
return v;
}
numeral eval_body(clause_id id) const {
return eval_body(m_clauses[id]);
}
numeral eval_head(clause_id id) const {
return eval_head(m_clauses[id]);
}
numeral eval_head(clause const& cl) const {
return cl.coeff()*m_assignment[cl.var()];
}
clause const& get_clause(clause_id id) const {
return m_clauses[id];
}
void display_clause(std::ostream& out, clause_id id) const {
if (id == null_clause_id) {
out << "null\n";
}
else {
m_clauses[id].display(out);
}
}
void display(std::ostream& out) const {
for (unsigned i = 0; i < m_clauses.size(); ++i) {
display_clause(out, i);
}
for (unsigned i = 0; i < m_assignment.size(); ++i) {
out << m_assignment[i] << "\n";
}
}
void collect_statistics(::statistics& st) const {
st.update("hi_propagation_cst", m_stats.m_propagation_cost);
}
void push() {
m_trail_stack.push_back(scope(m_clauses.size(), m_enabled_clauses.size(),
m_assignment_stack.size(), m_parent_stack.size()));
}
void pop(unsigned num_scopes) {
unsigned lvl = m_trail_stack.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
scope & s = m_trail_stack[new_lvl];
// restore enabled clauses
for (unsigned i = m_enabled_clauses.size(); i > s.m_enabled_clauses_lim; ) {
--i;
m_clauses[m_enabled_clauses[i]].disable();
}
m_enabled_clauses.shrink(s.m_enabled_clauses_lim);
// restore assignment stack
for (unsigned i = m_assignment_stack.size(); i > s.m_assignment_lim; ) {
--i;
m_assignment[m_assignment_stack[i].get_var()] = m_assignment_stack[i].get_old_value();
}
m_assignment_stack.shrink(s.m_assignment_lim);
// restore parent stack
for (unsigned i = m_parent_stack.size(); i > s.m_parent_lim; ) {
--i;
m_parent[m_parent_stack[i].get_var()] = m_parent_stack[i].get_old_value();
}
m_assignment_stack.shrink(s.m_assignment_lim);
// restore clauses
unsigned old_num_clauses = s.m_clauses_lim;
unsigned num_clauses = m_clauses.size();
SASSERT(old_num_clauses <= num_clauses);
unsigned to_delete = num_clauses - old_num_clauses;
for (unsigned i = 0; i < to_delete; i++) {
const clause & cl = m_clauses.back();
TRACE("horn_ineq", tout << "deleting clause:\n"; cl.display(tout););
for (unsigned j = 0; j < cl.size(); ++j) {
m_out_clauses[cl.vars(j)].pop_back();
}
m_in_clauses[cl.var()].pop_back();
m_clauses.pop_back();
}
m_trail_stack.shrink(new_lvl);
SASSERT(check_invariant());
}
/**
\brief Add clause z <= z and the assignment z := 0
Then z cannot be incremented without causing a loop (and therefore a contradiction).
*/
void set_to_zero(th_var z) {
m_zero = z;
}
bool enable_clause(clause_id id) {
if (id == null_clause_id) {
return true;
}
clause& cl = m_clauses[id];
bool r = true;
if (!cl.is_enabled()) {
cl.enable();
if (!is_feasible(cl)) {
r = make_feasible(id);
}
m_enabled_clauses.push_back(id);
}
return r;
}
void init_var(th_var v) {
unsigned sz = static_cast<unsigned>(v);
while (m_assignment.size() <= sz) {
m_assignment.push_back(Ext::m_minus_infty);
m_out_clauses.push_back(clause_id_vector());
m_in_clauses.push_back(clause_id_vector());
m_parent.push_back(null_clause_id);
m_onstack.push_back(0);
}
m_assignment[v] = Ext::m_minus_infty;
SASSERT(m_out_clauses[v].empty());
SASSERT(m_in_clauses[v].empty());
SASSERT(check_invariant());
}
clause_id add_ineq(vector<std::pair<th_var, rational> > const& terms, numeral const& weight, literal l) {
vector<rational> coeffs;
svector<th_var> vars;
rational coeff(1);
th_var var = null_theory_var;
bool found_negative = false;
for (unsigned i = 0; i < terms.size(); ++i) {
rational const& r = terms[i].second;
if (r.is_pos()) {
coeffs.push_back(terms[i].second);
vars.push_back(terms[i].first);
}
else if (found_negative) {
return null_clause_id;
}
else {
SASSERT(r.is_neg());
found_negative = true;
coeff = -r;
var = terms[i].first;
}
}
if (!found_negative) {
coeff = rational(1);
var = m_zero;
}
if (!coeff.is_one()) {
// so far just support unit coefficients on right.
return null_clause_id;
}
clause_id id = m_clauses.size();
m_clauses.push_back(clause(coeffs.size(), coeffs.c_ptr(), vars.c_ptr(), coeff, var, weight, l));
for (unsigned i = 0; i < vars.size(); ++i) {
m_out_clauses[vars[i]].push_back(id);
}
m_in_clauses[var].push_back(id);
return id;
}
bool is_feasible() const {
for (unsigned i = 0; i < m_clauses.size(); ++i) {
if (!is_feasible(m_clauses[i])) {
return false;
}
}
return true;
}
private:
bool check_invariant() {
return true;
}
/**
assignments are fully retraced on backtracking.
This is not always necessary.
*/
void acc_assignment(th_var v, const numeral & inc) {
m_assignment_stack.push_back(assignment_trail(v, m_assignment[v]));
m_assignment[v] += inc;
}
void acc_parent(th_var v, clause_id parent) {
m_parent[v] = parent;
m_parent_stack.push_back(parent_trail(v, parent));
}
numeral get_delta(const clause & cl) const {
SASSERT(cl.coeff().is_one() && "Not yet support for non-units");
return eval_body(cl) - eval_head(cl);
}
void set_onstack(th_var v) {
SASSERT(m_ts != 0);
m_onstack[v] = m_ts;
}
void reset_onstack(th_var v) {
m_onstack[v] = 0;
}
bool is_onstack(th_var v) const {
return m_onstack[v] == m_ts;
}
void inc_ts() {
m_ts++;
if (m_ts == 0) {
m_ts++;
m_onstack.reset();
m_onstack.resize(m_assignment.size(), 0);
}
}
// Make the assignment feasible. An assignment is feasible if
// Forall clause cl. eval_body(cl) <= eval_head(cl)
//
// This method assumes that if the assignment is not feasible,
// then the only infeasible clause is the last added clause.
//
// Traversal is by naive DFS search.
//
bool make_feasible(clause_id id) {
SASSERT(is_almost_feasible(id));
SASSERT(!m_clauses.empty());
SASSERT(!is_feasible(m_clauses[id]));
const clause & cl0 = m_clauses[id];
inc_ts();
for (unsigned i = 0; i < cl0.size(); ++i) {
set_onstack(cl0.vars(i));
}
th_var source = cl0.var();
numeral delta = get_delta(cl0);
acc_parent(source, id);
SASSERT(delta.is_pos());
acc_assignment(source, delta);
m_todo.reset();
m_todo.push_back(source);
TRACE("horn_ineq", cl0.display(tout););
do {
++m_stats.m_propagation_cost;
typename clause_id_vector::iterator it = m_out_clauses[source].begin();
typename clause_id_vector::iterator end = m_out_clauses[source].end();
for (; it != end; ++it) {
clause & cl = m_clauses[*it];
if (!cl.is_enabled()) {
continue;
}
delta = get_delta(cl);
if (delta.is_pos()) {
TRACE("horn_ineq", cl.display(tout););
th_var target = cl.var();
if (is_onstack(target)) {
m_unsat_clause = *it;
return false;
}
else {
acc_assignment(target, delta);
acc_parent(target, *it);
m_todo.push_back(target);
}
}
}
set_onstack(source);
source = m_todo.back();
// pop stack until there is a new variable to process.
while (is_onstack(source)) {
m_todo.pop_back();
reset_onstack(source);
if (m_todo.empty()) {
break;
}
source = m_todo.back();
}
}
while (!m_todo.empty());
return true;
}
bool is_almost_feasible(clause_id id) const {
for (unsigned i = 0; i < m_clauses.size(); ++i) {
if (id != static_cast<clause_id>(i) && !is_feasible(m_clauses[i])) {
return false;
}
}
return true;
}
bool is_feasible(const clause & cl) const {
return !cl.is_enabled() || get_delta(cl).is_nonpos();
}
};
template<typename Ext>
theory_horn_ineq<Ext>::theory_horn_ineq(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_graph(0),
m_asserted_qhead(0),
m_agility(0.5),
m_lia(false),
m_lra(false),
m_non_horn_ineq_exprs(false),
m_test(m),
m_factory(0) {
m_graph = alloc(graph);
}
template<typename Ext>
theory_horn_ineq<Ext>::~theory_horn_ineq() {
reset_eh();
dealloc(m_graph);
}
template<typename Ext>
std::ostream& theory_horn_ineq<Ext>::atom::display(theory_horn_ineq const& th, std::ostream& out) const {
context& ctx = th.get_context();
lbool asgn = ctx.get_assignment(m_bvar);
bool sign = (l_undef == asgn) || m_true;
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_clause(out, get_asserted_edge());
}
return out;
}
template<typename Ext>
theory_var theory_horn_ineq<Ext>::mk_var(enode* n) {
th_var v = theory::mk_var(n);
m_graph->init_var(v);
get_context().attach_th_var(n, this, v);
return v;
}
template<typename Ext>
theory_var theory_horn_ineq<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_horn_ineq_expr(n);
}
return v;
}
template<typename Ext>
void theory_horn_ineq<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_horn_ineq_exprs = false;
theory::reset_eh();
}
template<typename Ext>
void theory_horn_ineq<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("horn_ineq",
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_horn_ineq<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_horn_ineq<Ext>::set_neg_cycle_conflict() {
m_graph->traverse_neg_cycle1(m_params.m_arith_stronger_lemmas);
inc_conflicts();
literal_vector const& lits = m_graph->get_lits();
context & ctx = get_context();
TRACE("horn_ineq",
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")));
vector<rational> const& coeffs = m_graph->get_coeffs();
for (unsigned i = 0; i < coeffs.size(); ++i) {
params.push_back(parameter(coeffs[i]));
}
}
ctx.set_conflict(
ctx.mk_justification(
ext_theory_conflict_justification(
get_id(), ctx.get_region(),
lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr())));
}
template<typename Ext>
void theory_horn_ineq<Ext>::found_non_horn_ineq_expr(expr* n) {
if (!m_non_horn_ineq_exprs) {
TRACE("horn_ineq", tout << "found non horn logic expression:\n" << mk_pp(n, get_manager()) << "\n";);
get_context().push_trail(value_trail<context, bool>(m_non_horn_ineq_exprs));
m_non_horn_ineq_exprs = true;
}
}
template<typename Ext>
void theory_horn_ineq<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));
m_graph->set_to_zero(m_zero_int);
m_graph->set_to_zero(m_zero_real);
}
/**
\brief Create negated literal.
The negation of: E <= 0
-E + epsilon <= 0
or
-E + 1 <= 0
*/
template<typename Ext>
void theory_horn_ineq<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_horn_ineq<Ext>::numeral theory_horn_ineq<Ext>::mk_weight(bool is_real, bool is_strict, rational const& w) const {
if (is_strict) {
return numeral(inf_numeral(w)) + (is_real?Ext::m_epsilon:numeral(1));
}
else {
return numeral(inf_numeral(w));
}
}
template<typename Ext>
void theory_horn_ineq<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_horn_ineq<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_horn_ineq_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);
horn_ineq_tester::classify_t cl = m_test.linearize(e1, e2);
if (cl == horn_ineq_tester::non_horn) {
found_non_horn_ineq_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);
clause_id pos = m_graph->add_ineq(coeffs, w1, l);
negate(coeffs, w);
numeral w2 = mk_weight(a.is_real(e1), !is_strict, w);
clause_id neg = m_graph->add_ineq(coeffs, w2, ~l);
m_bool_var2atom.insert(bv, m_atoms.size());
m_atoms.push_back(atom(bv, pos, neg));
TRACE("horn_ineq",
tout << mk_pp(n, get_manager()) << "\n";
m_graph->display_clause(tout << "pos: ", pos);
m_graph->display_clause(tout << "neg: ", neg);
);
return true;
}
template<typename Ext>
bool theory_horn_ineq<Ext>::internalize_term(app * term) {
bool result = null_theory_var != mk_term(term);
CTRACE("horn_ineq", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";);
TRACE("horn_ineq", tout << "Terms may not be internalized " << mk_pp(term, get_manager()) << "\n";);
found_non_horn_ineq_expr(term);
return result;
}
template<typename Ext>
void theory_horn_ineq<Ext>::internalize_eq_eh(app * atom, bool_var) {
// noop
}
template<typename Ext>
void theory_horn_ineq<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_horn_ineq<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_horn_ineq<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_horn_ineq<Ext>::final_check_eh() {
SASSERT(is_consistent());
TRACE("horn_ineq", display(tout););
if (can_propagate()) {
propagate_core();
return FC_CONTINUE;
}
else if (m_non_horn_ineq_exprs) {
return FC_GIVEUP;
}
else {
return FC_DONE;
}
}
template<typename Ext>
void theory_horn_ineq<Ext>::propagate() {
propagate_core();
}
template<typename Ext>
void theory_horn_ineq<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_horn_ineq<Ext>::collect_statistics(::statistics& st) const {
st.update("horn ineq conflicts", m_stats.m_num_conflicts);
st.update("horn ineq assertions", m_stats.m_num_assertions);
m_arith_eq_adapter.collect_statistics(st);
m_graph->collect_statistics(st);
}
template<typename Ext>
void theory_horn_ineq<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_horn_ineq<Ext>::propagate_core() {
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_horn_ineq<Ext>::propagate_atom(atom const& a) {
context& ctx = get_context();
TRACE("horn_ineq", a.display(*this, tout); tout << "\n";);
if (ctx.inconsistent()) {
return false;
}
int clause_id = a.get_asserted_edge();
if (!m_graph->enable_clause(clause_id)) {
set_neg_cycle_conflict();
return false;
}
return true;
}
template<typename Ext>
theory_var theory_horn_ineq<Ext>::mk_term(app* n) {
context& ctx = get_context();
horn_ineq_tester::classify_t cl = m_test.linearize(n);
if (cl == horn_ineq_tester::non_horn) {
found_non_horn_ineq_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(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal)));
negate(coeffs, w);
VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal)));
return target;
}
template<typename Ext>
theory_var theory_horn_ineq<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(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(r)), null_literal)));
coeffs.back().second.neg();
VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(-r)), null_literal)));
}
return v;
}
template<typename Ext>
theory_var theory_horn_ineq<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;
}
template<typename Ext>
bool theory_horn_ineq<Ext>::is_consistent() const {
return m_graph->is_feasible();
}
// models:
template<typename Ext>
void theory_horn_ineq<Ext>::init_model(model_generator & m) {
m_factory = alloc(arith_factory, get_manager());
m.register_factory(m_factory);
compute_delta();
}
template<typename Ext>
model_value_proc * theory_horn_ineq<Ext>::mk_value(enode * n, model_generator & mg) {
theory_var v = n->get_th_var(get_id());
SASSERT(v != null_theory_var);
numeral val = m_graph->get_assignment(v);
rational num = val.get_infinity()*m_lambda + val.get_rational() + val.get_infinitesimal()*m_delta;
TRACE("horn_ineq", 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_horn_ineq<Ext>::compute_delta() {
m_delta = rational(1);
m_lambda = rational(0);
unsigned sz = m_graph->get_num_clauses();
for (unsigned i = 0; i < sz; ++i) {
if (!m_graph->get_clause(i).is_enabled()) {
continue;
}
numeral b = m_graph->eval_body(i);
numeral h = m_graph->eval_head(i);
if (b.get_infinity() < h.get_infinity()) {
continue;
}
SASSERT(b.get_infinity() == h.get_infinity());
// b <= h
// suppose that h.eps < b.eps
// then we have h.num > b.num
// but also h.num + delta*h.eps >= b.num + delta*b.eps
// <=>
// (h.num - b.num)/(b.eps - h.eps) >= delta
rational num_r = h.get_rational() - b.get_rational();
rational eps_r = b.get_infinitesimal() - h.get_infinitesimal();
if (eps_r.is_pos()) {
SASSERT(num_r.is_pos());
rational new_delta = num_r/eps_r;
if (new_delta < m_delta) {
m_delta = new_delta;
}
}
}
for (unsigned i = 0; i < sz; ++i) {
if (!m_graph->get_clause(i).is_enabled()) {
continue;
}
numeral b = m_graph->eval_body(i);
numeral h = m_graph->eval_head(i);
rational ir = h.get_infinity() - b.get_infinity();
rational hr = b.get_rational() - h.get_rational();
rational num_r = hr + m_delta*(b.get_infinitesimal() - h.get_infinitesimal());
SASSERT(b.get_infinity() <= h.get_infinity());
// b <= h
// suppose that h.finite < b.finite
// then we have h.infinite > b.infinite
// but also
// h.infinite*lambda + h.finite >= b.infinite*lambda + b.finite
// <=>
// lambda >= (b.finite - h.finite) / (h.infinite - b.infinite)
if (num_r.is_pos()) {
SASSERT(ir.is_pos());
rational new_lambda = num_r/ir;
if (new_lambda > m_lambda) {
m_lambda = new_lambda;
}
}
}
}
};
#endif