3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 01:25:31 +00:00

checkpoint

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2012-10-21 13:32:12 -07:00
parent 4722fdfca5
commit add684d8e9
377 changed files with 204 additions and 62 deletions

View file

@ -0,0 +1,316 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_eq_adapter.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-25.
Revision History:
--*/
#include"smt_context.h"
#include"arith_eq_adapter.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"stats.h"
#include"simplifier.h"
#include"ast_smt2_pp.h"
namespace smt {
class already_processed_trail : public trail<context> {
// Remark: it is safer to use a trail object, because it guarantees that the enodes
// are still alive when the undo operation is performed.
//
// If a local backtracking stack is used in the class arith_eq_adapter is used,
// then we cannot guarantee that.
arith_eq_adapter::already_processed & m_already_processed;
enode * m_n1;
enode * m_n2;
public:
already_processed_trail(arith_eq_adapter::already_processed & m, enode * n1, enode * n2):
m_already_processed(m),
m_n1(n1),
m_n2(n2) {
}
virtual void undo(context & ctx) {
m_already_processed.erase(m_n1, m_n2);
TRACE("arith_eq_adapter_profile", tout << "del #" << m_n1->get_owner_id() << " #" << m_n2->get_owner_id() << "\n";);
}
};
/**
\brief The atoms m_eq, m_le, and m_ge should be marked as relevant only after
m_n1 and m_n2 are marked as relevant.
*/
class arith_eq_relevancy_eh : public relevancy_eh {
expr * m_n1;
expr * m_n2;
expr * m_eq;
expr * m_le;
expr * m_ge;
public:
arith_eq_relevancy_eh(expr * n1, expr * n2, expr * eq, expr * le, expr * ge):
m_n1(n1),
m_n2(n2),
m_eq(eq),
m_le(le),
m_ge(ge) {
}
virtual ~arith_eq_relevancy_eh() {}
virtual void operator()(relevancy_propagator & rp) {
if (!rp.is_relevant(m_n1))
return;
if (!rp.is_relevant(m_n2))
return;
rp.mark_as_relevant(m_eq);
rp.mark_as_relevant(m_le);
rp.mark_as_relevant(m_ge);
}
};
arith_simplifier_plugin * arith_eq_adapter::get_simplifier() {
if (!m_as) {
simplifier & s = get_context().get_simplifier();
m_as = static_cast<arith_simplifier_plugin*>(s.get_plugin(m_owner.get_family_id()));
}
return m_as;
}
void arith_eq_adapter::mk_axioms(enode * n1, enode * n2) {
SASSERT(n1 != n2);
ast_manager & m = get_manager();
TRACE("arith_eq_adapter_mk_axioms", tout << "#" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";
tout << mk_ismt2_pp(n1->get_owner(), m) << "\n" << mk_ismt2_pp(n2->get_owner(), m) << "\n";);
if (n1->get_owner_id() > n2->get_owner_id())
std::swap(n1, n2);
app * t1 = n1->get_owner();
app * t2 = n2->get_owner();
if (m.is_value(t1) && m.is_value(t2)) {
// Nothing to be done
// We don't need to create axioms for 2 = 3
return;
}
context & ctx = get_context();
CTRACE("arith_eq_adapter_relevancy", !(ctx.is_relevant(n1) && ctx.is_relevant(n2)),
tout << "is_relevant(n1): #" << n1->get_owner_id() << " " << ctx.is_relevant(n1) << "\n";
tout << "is_relevant(n2): #" << n2->get_owner_id() << " " << ctx.is_relevant(n2) << "\n";
tout << mk_pp(n1->get_owner(), get_manager()) << "\n";
tout << mk_pp(n2->get_owner(), get_manager()) << "\n";
ctx.display(tout););
//
// The atoms d.m_t1_eq_t2, d.m_le, and d.m_ge should only be marked as relevant
// after n1 and n2 are marked as relevant.
//
data d;
if (m_already_processed.find(n1, n2, d))
return;
TRACE("arith_eq_adapter_profile", tout << "mk #" << n1->get_owner_id() << " #" << n2->get_owner_id() << " " <<
m_already_processed.size() << " " << ctx.get_scope_level() << "\n";);
m_stats.m_num_eq_axioms++;
TRACE("arith_eq_adapter_profile_detail",
tout << "mk_detail " << mk_bounded_pp(n1->get_owner(), m, 5) << " " <<
mk_bounded_pp(n2->get_owner(), m, 5) << "\n";);
app_ref t1_eq_t2(m);
t1_eq_t2 = ctx.mk_eq_atom(t1, t2);
SASSERT(!m.is_false(t1_eq_t2));
TRACE("arith_eq_adapter_bug", tout << mk_bounded_pp(t1_eq_t2, m) << "\n"
<< mk_bounded_pp(t1, m) << "\n"
<< mk_bounded_pp(t2, m) << "\n";);
// UNRESOLVED ISSUE:
//
// arith_eq_adapter is still creating problems.
// The following disabled code fixes the issues, but create performance problems.
// The alternative does not works 100%. It generates problems when the literal
// created by the adapter during the search is included in a learned clause.
// Here is a sequence of events that triggers a crash:
// 1) The terms t1 >= t2 and t1 <= t2 are not in simplified form.
// For example, let us assume t1 := (* -1 x) and t2 := x.
// Since, t1 and t2 were internalized at this point, the following code works.
// That is the arith internalizer accepts the formula (+ (* -1 x) (* -1 x))
// that is not in simplified form. Let s be the term (+ (* -1 x) (* -1 x))
// 2) Assume now that a conflict is detected a lemma containing s is created.
// 3) The enodes associated with t1, t2 and s are destroyed during backtracking.
// 4) The term s is reinternalized at smt::context::reinit_clauses. Term t2 is
// also reinitialized, but t1 is not. We only create a "name" for a term (* -1 x)
// if it is embedded in a function application.
// 5) theory_arith fails to internalize (+ (* -1 x) (* -1 x)), and Z3 crashes.
//
#if 0
// This block of code uses the simplifier for creating the literals t1 >= t2 and t1 <= t2.
// It has serious performance problems in VCC benchmarks.
// The problem seems to be the following:
// t1 and t2 are slacks (i.e., names for linear polynomials).
// The simplifier will create inequalities that will indirectly imply that t1 >= t2 and t1 <= t2.
// Example if: t1 := 1 + a
// t2 := 2 + b
// the simplifier will create
// a - b >= -1
// a - b <= -1
// These inequalities imply that 1+a >= 2+b and 1+a <= 2+b,
// but the tableau is complete different.
// BTW, note that we don't really need to handle the is_numeral case when using
// the simplifier. However, doing that, it seems we minimize the performance problem.
expr_ref le(m);
expr_ref ge(m);
if (m_util.is_numeral(t1))
std::swap(t1, t2);
if (m_util.is_numeral(t2)) {
le = m_util.mk_le(t1, t2);
ge = m_util.mk_ge(t1, t2);
}
else {
arith_simplifier_plugin & s = *(get_simplifier());
s.mk_le(t1, t2, le);
s.mk_ge(t1, t2, ge);
}
TRACE("arith_eq_adapter_perf", tout << mk_ismt2_pp(t1_eq_t2, m) << "\n" << mk_ismt2_pp(le, m) << "\n" << mk_ismt2_pp(ge, m) << "\n";);
#else
// Old version that used to be buggy.
// I fixed the theory arithmetic internalizer to accept non simplified terms of the form t1 - t2
// if t1 and t2 already have slacks (theory variables) associated with them.
app * le = 0;
app * ge = 0;
if (m_util.is_numeral(t1))
std::swap(t1, t2);
if (m_util.is_numeral(t2)) {
le = m_util.mk_le(t1, t2);
ge = m_util.mk_ge(t1, t2);
}
else {
sort * st = m.get_sort(t1);
app * minus_one = m_util.mk_numeral(rational::minus_one(), st);
app * zero = m_util.mk_numeral(rational::zero(), st);
app_ref s(m_util.mk_add(t1, m_util.mk_mul(minus_one, t2)), m);
le = m_util.mk_le(s, zero);
ge = m_util.mk_ge(s, zero);
}
TRACE("arith_eq_adapter_perf",
tout << mk_ismt2_pp(t1_eq_t2, m) << "\n" << mk_ismt2_pp(le, m) << "\n" << mk_ismt2_pp(ge, m) << "\n";);
#endif
ctx.push_trail(already_processed_trail(m_already_processed, n1, n2));
m_already_processed.insert(n1, n2, data(t1_eq_t2, le, ge));
TRACE("arith_eq_adapter_profile", tout << "insert #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";);
ctx.internalize(t1_eq_t2, true);
literal t1_eq_t2_lit(ctx.get_bool_var(t1_eq_t2));
TRACE("interface_eq",
tout << "core should try true phase first for the equality: " << t1_eq_t2_lit << "\n";
tout << "#" << n1->get_owner_id() << " == #" << n2->get_owner_id() << "\n";
tout << "try_true_first: " << ctx.try_true_first(t1_eq_t2_lit.var()) << "\n";);
TRACE("arith_eq_adapter_bug",
tout << "le: " << mk_ismt2_pp(le, m) << "\nge: " << mk_ismt2_pp(ge, m) << "\n";);
ctx.internalize(le, true);
ctx.internalize(ge, true);
SASSERT(ctx.lit_internalized(le));
SASSERT(ctx.lit_internalized(ge));
literal le_lit = ctx.get_literal(le);
literal ge_lit = ctx.get_literal(ge);
if (ctx.try_true_first(t1_eq_t2_lit.var())) {
// Remark: I need to propagate the try_true_first flag to the auxiliary atom le_lit and ge_lit.
// Otherwise model based theory combination will be ineffective, because if the core
// case splits in le_lit and ge_lit before t1_eq_t2_lit it will essentially assign an arbitrary phase to t1_eq_t2_lit.
ctx.set_true_first_flag(le_lit.var());
ctx.set_true_first_flag(ge_lit.var());
}
theory_id tid = m_owner.get_id();
if (m.proofs_enabled() && m_proof_hint.empty()) {
m_proof_hint.push_back(parameter(symbol("triangle-eq")));
}
ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, le_lit, m_proof_hint.size(), m_proof_hint.c_ptr());
ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, ge_lit, m_proof_hint.size(), m_proof_hint.c_ptr());
ctx.mk_th_axiom(tid, t1_eq_t2_lit, ~le_lit, ~ge_lit, m_proof_hint.size(), m_proof_hint.c_ptr());
TRACE("arith_eq_adapter", tout << "internalizing: "
<< " " << mk_pp(le, m) << ": " << le_lit
<< " " << mk_pp(ge, m) << ": " << ge_lit
<< " " << mk_pp(t1_eq_t2, m) << ": " << t1_eq_t2_lit << "\n";);
if (m_params.m_arith_add_binary_bounds) {
TRACE("arith_eq_adapter", tout << "adding binary bounds...\n";);
ctx.mk_th_axiom(tid, le_lit, ge_lit, 3, m_proof_hint.c_ptr());
}
if (ctx.relevancy()) {
relevancy_eh * eh = ctx.mk_relevancy_eh(arith_eq_relevancy_eh(n1->get_owner(), n2->get_owner(), t1_eq_t2, le, ge));
ctx.add_relevancy_eh(n1->get_owner(), eh);
ctx.add_relevancy_eh(n2->get_owner(), eh);
}
if (!m_params.m_arith_lazy_adapter && !ctx.at_base_level() &&
n1->get_iscope_lvl() <= ctx.get_base_level() && n2->get_iscope_lvl() <= ctx.get_base_level()) {
m_restart_pairs.push_back(enode_pair(n1, n2));
}
TRACE("arith_eq_adapter_detail", ctx.display(tout););
}
void arith_eq_adapter::new_eq_eh(theory_var v1, theory_var v2) {
TRACE("arith_eq_adapter", tout << "v" << v1 << " = v" << v2 << " #" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";);
TRACE("arith_eq_adapter_bug", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";);
mk_axioms(get_enode(v1), get_enode(v2));
}
void arith_eq_adapter::new_diseq_eh(theory_var v1, theory_var v2) {
TRACE("arith_eq_adapter", tout << "v" << v1 << " != v" << v2 << " #" << get_enode(v1)->get_owner_id() << " != #" << get_enode(v2)->get_owner_id() << "\n";);
mk_axioms(get_enode(v1), get_enode(v2));
}
void arith_eq_adapter::init_search_eh() {
m_restart_pairs.reset();
}
void arith_eq_adapter::reset_eh() {
TRACE("arith_eq_adapter", tout << "reset\n";);
m_already_processed .reset();
m_restart_pairs .reset();
m_stats .reset();
}
void arith_eq_adapter::restart_eh() {
TRACE("arith_eq_adapter", tout << "restart\n";);
svector<enode_pair> tmp(m_restart_pairs);
svector<enode_pair>::iterator it = tmp.begin();
svector<enode_pair>::iterator end = tmp.end();
m_restart_pairs.reset();
for (; it != end; ++it) {
TRACE("arith_eq_adapter", tout << "creating arith_eq_adapter axioms at the base level #" << it->first->get_owner_id() << " #" <<
it->second->get_owner_id() << "\n";);
mk_axioms(it->first, it->second);
}
}
void arith_eq_adapter::collect_statistics(::statistics & st) const {
st.update("eq adapter", m_stats.m_num_eq_axioms);
}
void arith_eq_adapter::display_already_processed(std::ostream & out) const {
obj_pair_map<enode, enode, data>::iterator it = m_already_processed.begin();
obj_pair_map<enode, enode, data>::iterator end = m_already_processed.end();
for (; it != end; ++it) {
enode * n1 = it->get_key1();
enode * n2 = it->get_key2();
out << "eq_adapter: #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";
}
}
};

View file

@ -0,0 +1,97 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_eq_adapter.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-25.
Revision History:
--*/
#ifndef _ARITH_EQ_ADAPTER_H_
#define _ARITH_EQ_ADAPTER_H_
#include"smt_theory.h"
#include"obj_pair_hashtable.h"
#include"arith_decl_plugin.h"
#include"statistics.h"
#include"arith_simplifier_plugin.h"
namespace smt {
struct arith_eq_adapter_stats {
unsigned m_num_eq_axioms;
void reset() { m_num_eq_axioms = 0; }
arith_eq_adapter_stats() { reset(); }
};
/**
\brief Auxiliary class used to convert (dis) equalities
propagated from the core into arith equalities/inequalities
atoms. This class is used by the arithmetic theories to
handle the (dis) equalities propagated from the logical context.
- config 1:
recreate axioms at restart
- config 2:
lazy diseq split
*/
class arith_eq_adapter {
public:
arith_eq_adapter_stats m_stats;
private:
struct data {
expr * m_t1_eq_t2;
expr * m_le;
expr * m_ge;
data():m_t1_eq_t2(0), m_le(0), m_ge(0) {}
data(expr * t1_eq_t2, expr * le, expr * ge):m_t1_eq_t2(t1_eq_t2), m_le(le), m_ge(ge) {}
};
public:
typedef obj_pair_map<enode, enode, data> already_processed;
private:
theory & m_owner;
theory_arith_params & m_params;
arith_util & m_util;
arith_simplifier_plugin * m_as;
already_processed m_already_processed;
svector<enode_pair> m_restart_pairs;
svector<parameter> m_proof_hint;
context & get_context() const { return m_owner.get_context(); }
ast_manager & get_manager() const { return m_owner.get_manager(); }
enode * get_enode(theory_var v) const { return m_owner.get_enode(v); }
arith_simplifier_plugin * get_simplifier();
public:
arith_eq_adapter(theory & owner, theory_arith_params & params, arith_util & u):m_owner(owner), m_params(params), m_util(u), m_as(0) {}
void new_eq_eh(theory_var v1, theory_var v2);
void new_diseq_eh(theory_var v1, theory_var v2);
void reset_eh();
void init_search_eh();
void restart_eh();
/**
\brief Add the eq axioms for n1 and n2.
*/
void mk_axioms(enode * n1, enode * n2);
void collect_statistics(::statistics & st) const;
void display_already_processed(std::ostream & out) const;
};
};
#endif /* _ARITH_EQ_ADAPTER_H_ */

632
src/smt/arith_eq_solver.cpp Normal file
View file

@ -0,0 +1,632 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
arith_eq_solver.cpp
Abstract:
Solver for linear arithmetic equalities.
Author:
Nikolaj Bjorner (nbjorner) 2012-02-25
--*/
#include"arith_eq_solver.h"
arith_eq_solver::~arith_eq_solver() {
}
arith_eq_solver::arith_eq_solver(ast_manager & m, params_ref const& p):
m(m),
m_params(p),
m_util(m),
m_arith_rewriter(m)
{
m_params.set_bool(":gcd-rounding", true);
// m_params.set_bool(":sum", true);
m_arith_rewriter.updt_params(m_params);
}
/**
\brief Return true if the first monomial of t is negative.
*/
bool arith_eq_solver::is_neg_poly(expr * t) const {
if (m_util.is_add(t)) {
t = to_app(t)->get_arg(0);
}
if (m_util.is_mul(t)) {
t = to_app(t)->get_arg(0);
rational r;
bool is_int;
if (m_util.is_numeral(t, r, is_int))
return r.is_neg();
}
return false;
}
void arith_eq_solver::prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result) {
SASSERT(m_util.is_int(e));
SASSERT(k.is_int() && k.is_pos());
numeral n;
bool is_int;
if (depth == 0) {
result = e;
}
else if (m_util.is_add(e) || m_util.is_mul(e)) {
expr_ref_vector args(m);
expr_ref tmp(m);
app* a = to_app(e);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
prop_mod_const(a->get_arg(i), depth - 1, k, tmp);
args.push_back(tmp);
}
m_arith_rewriter.mk_app(a->get_decl(), args.size(), args.c_ptr(), result);
}
else if (m_util.is_numeral(e, n, is_int) && is_int) {
result = m_util.mk_numeral(mod(n, k), true);
}
else {
result = e;
}
}
void arith_eq_solver::gcd_normalize(vector<numeral>& values) {
numeral g(0);
for (unsigned i = 0; !g.is_one() && i < values.size(); ++i) {
SASSERT(values[i].is_int());
if (!values[i].is_zero()) {
if (g.is_zero()) {
g = abs(values[i]);
}
else {
g = gcd(abs(values[i]), g);
}
}
}
if (g.is_zero() || g.is_one()) {
return;
}
for (unsigned i = 0; i < values.size(); ++i) {
values[i] = values[i] / g;
SASSERT(values[i].is_int());
}
}
unsigned arith_eq_solver::find_abs_min(vector<numeral>& values) {
SASSERT(values.size() >= 2);
unsigned index = 0;
numeral v(0);
for (unsigned i = 1; i < values.size(); ++i) {
numeral w = abs(values[i]);
if (v.is_zero() || (!w.is_zero() && w < v)) {
index = i;
v = w;
}
}
return index;
}
static void print_row(std::ostream& out, vector<rational> const& row) {
for(unsigned i = 0; i < row.size(); ++i) {
out << row[i] << " ";
}
out << "\n";
}
static void print_rows(std::ostream& out, vector<vector<rational> > const& rows) {
for (unsigned i = 0; i < rows.size(); ++i) {
print_row(out, rows[i]);
}
}
//
// The gcd of the coefficients to variables have to divide the
// coefficient to the constant.
// The constant is the last value in the array.
//
bool arith_eq_solver::gcd_test(vector<numeral>& values) {
SASSERT(values.size() > 0);
numeral g(0);
numeral first_value = values[0];
for (unsigned i = 1; !g.is_one() && i < values.size(); ++i) {
if (!values[i].is_zero()) {
if (g.is_zero()) {
g = abs(values[i]);
}
else {
g = gcd(abs(values[i]), g);
}
}
}
if (g.is_one()) {
return true;
}
if (g.is_zero()) {
return first_value.is_zero();
}
numeral r = first_value/g;
return r.is_int();
}
bool arith_eq_solver::solve_integer_equation(
vector<numeral>& values,
unsigned& index,
bool& is_fresh
)
{
TRACE("arith_eq_solver",
tout << "solving: ";
print_row(tout, values);
);
//
// perform one step of the omega test equality elimination.
//
// Given:
// a1*x1 + a2*x2 + .. + a_n*x_n + a_{n+1} = 0
//
// Assume gcd(a1,..,a_n,a_{n+1}) = 1
// Assume gcd(a1,...,a_n) divides a_{n+1} (eg. gcd(a1,..,an) = 1)
//
// post-condition: values[index] = -1.
//
// Let a_index be index of least absolute value.
//
// If |a_index| = 1, then return row and index.
// Otherwise:
// Let m = |a_index| + 1
// Set
//
// m*x_index'
// =
// ((a1 mod_hat m)*x1 + (a2 mod_hat m)*x2 + .. + (a_n mod_hat m)*x_n + (k mod_hat m))
// =
// (a1'*x1 + a2'*x2 + .. (-)1*x_index + ...)
//
// <=> Normalize signs so that sign to x_index is -1.
// (-)a1'*x1 + (-)a2'*x2 + .. -1*x_index + ... + m*x_index' = 0
//
// Return row, where the coefficient to x_index is implicit.
// Instead used the coefficient 'm' at position 'index'.
//
gcd_normalize(values);
if (!gcd_test(values)) {
TRACE("arith_eq_solver", tout << "not sat\n";
print_row(tout, values););
return false;
}
index = find_abs_min(values);
SASSERT(1 <= index && index < values.size());
numeral a = values[index];
numeral abs_a = abs(a);
if (abs_a.is_zero()) {
// The equation is trivial.
return true;
}
if (a.is_one()) {
for (unsigned i = 0; i < values.size(); ++i) {
values[i].neg();
}
}
is_fresh = !abs_a.is_one();
if (is_fresh) {
numeral m = abs_a + numeral(1);
for (unsigned i = 0; i < values.size(); ++i) {
values[i] = mod_hat(values[i], m);
}
if (values[index].is_one()) {
for (unsigned i = 0; i < values.size(); ++i) {
values[i].neg();
}
}
SASSERT(values[index].is_minus_one());
values[index] = m;
}
TRACE("arith_eq_solver",
tout << "solved at index " << index << ": ";
print_row(tout, values);
);
return true;
}
void arith_eq_solver::substitute(
row& r,
row const& s,
unsigned index
)
{
SASSERT(1 <= index && index < s.size());
TRACE("arith_eq_solver",
tout << "substitute " << index << ":\n";
print_row(tout, r);
print_row(tout, s);
);
if (index >= r.size()) {
return;
}
numeral c = r[index];
if (c.is_zero()) {
// no-op
}
else if (abs(s[index]).is_one()) {
//
// s encodes an equation that contains a variable
// with a unit coefficient.
//
// Let
// c = r[index]
// s = s[index]*x + s'*y = 0
// r = c*x + r'*y = 0
//
// =>
//
// 0
// =
// -sign(s[index])*c*s + r
// =
// -s[index]*sign(s[index])*c*x - sign(s[index])*c*s'*y + c*x + r'*y
// =
// -c*x - sign(s[index])*c*s'*y + c*x + r'*y
// =
// -sign(s[index])*c*s'*y + r'*y
//
numeral sign_s = s[index].is_pos()?numeral(1):numeral(-1);
for (unsigned i = 0; i < r.size(); ++i) {
r[i] -= c*sign_s*s[i];
}
for (unsigned i = r.size(); i < s.size(); ++i) {
r.push_back(-c*sign_s*s[i]);
}
}
else {
//
// s encodes a substitution using an auxiliary variable.
// the auxiliary variable is at position 'index'.
//
// Let
// c = r[index]
// s = s[index]*x + s'*y = 0
// r = c*x + r'*y = 0
//
// s encodes : x |-> s[index]*x' + s'*y
//
// Set:
//
// r := c*s + r'*y
//
r[index] = numeral(0);
for (unsigned i = 0; i < r.size(); ++i) {
r[i] += c*s[i];
}
for (unsigned i = r.size(); i < s.size(); ++i) {
r.push_back(c*s[i]);
}
}
TRACE("arith_eq_solver",
tout << "result: ";
print_row(tout, r);
);
}
bool arith_eq_solver::solve_integer_equations(
vector<row>& rows,
row& unsat_row
)
{
// return solve_integer_equations_units(rows, unsat_row);
return solve_integer_equations_gcd(rows, unsat_row);
}
//
// Naive integer equation solver where only units are eliminated.
//
bool arith_eq_solver::solve_integer_equations_units(
vector<row>& rows,
row& unsat_row
)
{
TRACE("arith_eq_solver", print_rows(tout << "solving:\n", rows););
unsigned_vector todo, done;
for (unsigned i = 0; i < rows.size(); ++i) {
todo.push_back(i);
row& r = rows[i];
gcd_normalize(r);
if (!gcd_test(r)) {
unsat_row = r;
TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); );
return false;
}
}
for (unsigned i = 0; i < todo.size(); ++i) {
row& r = rows[todo[i]];
gcd_normalize(r);
if (!gcd_test(r)) {
unsat_row = r;
TRACE("arith_eq_solver", print_row(tout << "unsat: ", unsat_row); );
return false;
}
unsigned index = find_abs_min(r);
SASSERT(1 <= index && index < r.size());
numeral a = r[index];
numeral abs_a = abs(a);
if (abs_a.is_zero()) {
continue;
}
else if (abs_a.is_one()) {
for (unsigned j = i+1; j < todo.size(); ++j) {
substitute(rows[todo[j]], r, index);
}
for (unsigned j = 0; j < done.size(); ++j) {
row& r2 = rows[done[j]];
if (!r2[index].is_zero()) {
substitute(r2, r, index);
todo.push_back(done[j]);
done.erase(done.begin()+j);
--j;
}
}
}
else {
done.push_back(todo[i]);
}
}
TRACE("arith_eq_solver",
tout << ((done.size()<=1)?"solved ":"incomplete check ") << done.size() << "\n";
for (unsigned i = 0; i < done.size(); ++i) {
print_row(tout, rows[done[i]]);
}
);
return true;
}
//
// Partial solver based on the omega test equalities.
// unsatisfiability is not preserved when eliminating
// auxiliary variables.
//
bool arith_eq_solver::solve_integer_equations_omega(
vector<row> & rows,
row& unsat_row
)
{
unsigned index;
bool is_fresh;
vector<row> rows_solved;
unsigned_vector indices;
unsigned_vector aux_indices;
for (unsigned i = 0; i < rows.size(); ++i) {
rows_solved.push_back(rows[i]);
row& r = rows_solved.back();
for (unsigned j = 0; j + 1 < rows_solved.size(); ++j) {
substitute(r, rows_solved[j], indices[j]);
}
if (!solve_integer_equation(r, index, is_fresh)) {
unsat_row = r;
gcd_normalize(unsat_row);
// invert the substitution for every index that is fresh.
TRACE("arith_eq_solver",
tout << "unsat:\n";
print_row(tout, unsat_row);
for (unsigned l = 0; l + 1< rows_solved.size(); ++l) {
print_row(tout, rows_solved[l]);
});
for (unsigned j = rows_solved.size()-1; j > 0; ) {
--j;
row& solved_row = rows_solved[j];
unsigned index_j = indices[j];
unsigned aux_index_j = aux_indices[j];
SASSERT(index_j <= aux_index_j);
if (unsat_row.size() <= aux_index_j) {
unsat_row.resize(aux_index_j+1);
}
numeral m = solved_row[aux_index_j];
numeral k = unsat_row[aux_index_j];
if (aux_index_j != index_j && !k.is_zero()) {
//
// solved_row: -x_index + m*sigma + r1 = 0
// unsat_row: k*sigma + r2 = 0
//
// <=>
//
// solved_row: -k*x_index + k*m*sigma + k*r1 = 0
// unsat_row: m*k*sigma + m*r2 = 0
//
// =>
//
// m*k*sigma + m*r2 + k*x_index - k*m*sigma - k*r1 = 0
//
for (unsigned l = 0; l < unsat_row.size(); ++l) {
unsat_row[l] *= m;
unsat_row[l] -= k*solved_row[l];
}
for (unsigned l = unsat_row.size(); l < solved_row.size(); ++l) {
unsat_row.push_back(solved_row[l]);
}
gcd_normalize(unsat_row);
TRACE("arith_eq_solver",
tout << "gcd: ";
print_row(tout, solved_row);
print_row(tout, unsat_row);
);
}
if (gcd_test(unsat_row)) {
TRACE("arith_eq_solver", tout << "missed pure explanation\n";);
return true;
}
SASSERT(!gcd_test(unsat_row));
}
return false;
}
else if (r[index].is_zero()) {
// Row is trival
rows_solved.pop_back();
continue;
}
else if (!abs(r[index]).is_one()) {
//
// The solution introduces a fresh auxiliary variable.
// Make space for this variable as a fresh numeral.
//
indices.push_back(index);
aux_indices.push_back(r.size());
r.push_back(r[index]);
r[index] = numeral(-1);
// re-solve the same row.
--i;
}
else {
indices.push_back(index);
aux_indices.push_back(index);
}
}
return true;
}
//
// Eliminate variables by searching for combination of rows where
// the coefficients have gcd = 1.
//
bool arith_eq_solver::solve_integer_equations_gcd(
vector<row> & rows,
row& unsat_row
)
{
unsigned_vector live, useful, gcd_pos;
vector<rational> gcds;
rational u, v;
if (rows.empty()) {
return true;
}
for (unsigned i = 0; i < rows.size(); ++i) {
live.push_back(i);
row& r = rows[i];
gcd_normalize(r);
if (!gcd_test(r)) {
unsat_row = r;
TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); );
return false;
}
}
unsigned max_column = rows[0].size();
bool change = true;
while (change && !live.empty()) {
change = false;
for (unsigned i = 1; i < max_column; ++i) {
rational g(0);
gcds.reset();
gcd_pos.reset();
unsigned j = 0;
for (; j < live.size(); ++j) {
rational const& k = rows[live[j]][i];
if (k.is_zero()) {
continue;
}
if (g.is_zero()) {
g = abs(k);
}
else {
g = gcd(g, abs(k));
}
if (abs(g).is_one()) {
break;
}
gcds.push_back(g);
gcd_pos.push_back(live[j]);
}
if (j == live.size()) {
continue;
}
change = true;
// found gcd, now identify reduced set of rows with GCD = 1.
g = abs(rows[live[j]][i]);
useful.push_back(live[j]);
unsigned live_pos = j;
for (j = gcds.size(); !g.is_one() && j > 0; ) {
SASSERT(g.is_pos());
--j;
if (j == 0 || !gcd(g, gcds[j-1]).is_one()) {
useful.push_back(gcd_pos[j]);
g = gcd(g, gcds[j]);
SASSERT(j == 0 || gcd(g,gcds[j-1]).is_one());
}
}
//
// we now have a set "useful" of rows whose combined GCD = 1.
// pivot the remaining with the first row.
//
row& r0 = rows[useful[0]];
for (j = 1; j < useful.size(); ++j) {
row& r1 = rows[useful[j]];
g = gcd(r0[i], r1[i], u, v);
for (unsigned k = 0; k < max_column; ++k) {
r0[k] = u*r0[k] + v*r1[k];
}
SASSERT(g == r0[i]);
}
SASSERT(abs(r0[i]).is_one());
live.erase(live.begin()+live_pos);
for (j = 0; j < live.size(); ++j) {
row& r = rows[live[j]];
if (!r[i].is_zero()) {
substitute(r, r0, i);
gcd_normalize(r);
if (!gcd_test(r)) {
unsat_row = r;
TRACE("arith_eq_solver", print_row(tout << "unsat: ", unsat_row); );
return false;
}
}
}
}
}
TRACE("arith_eq_solver",
tout << ((live.size()<=1)?"solved ":"incomplete check ") << live.size() << "\n";
for (unsigned i = 0; i < live.size(); ++i) {
print_row(tout, rows[live[i]]);
}
);
return true;
}

108
src/smt/arith_eq_solver.h Normal file
View file

@ -0,0 +1,108 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
arith_eq_solver.h
Abstract:
Solver for linear arithmetic equalities.
Author:
Nikolaj Bjorner (nbjorner) 2012-02-25
--*/
#ifndef _ARITH_EQ_SOLVER_H_
#define _ARITH_EQ_SOLVER_H_
#include"arith_decl_plugin.h"
#include"arith_rewriter.h"
/**
\brief Simplifier for the arith family.
*/
class arith_eq_solver {
typedef rational numeral;
ast_manager& m;
params_ref m_params;
arith_util m_util;
arith_rewriter m_arith_rewriter;
bool is_neg_poly(expr * t) const;
void prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result);
bool gcd_test(vector<numeral>& value);
unsigned find_abs_min(vector<numeral>& values);
void gcd_normalize(vector<numeral>& values);
void substitute(vector<numeral>& r, vector<numeral> const& s, unsigned index);
bool solve_integer_equations_units(
vector<vector<numeral> > & rows,
vector<numeral>& unsat_row
);
bool solve_integer_equations_omega(
vector<vector<numeral> > & rows,
vector<numeral>& unsat_row
);
void compute_hnf(vector<vector<numeral> >& A);
bool solve_integer_equations_hermite(
vector<vector<numeral> > & rows,
vector<numeral>& unsat_row
);
bool solve_integer_equations_gcd(
vector<vector<numeral> > & rows,
vector<numeral>& unsat_row
);
public:
arith_eq_solver(ast_manager & m, params_ref const& p = params_ref());
~arith_eq_solver();
// Integer linear solver for a single equation.
// The array values contains integer coefficients
//
// Determine integer solutions to:
//
// a+k = 0
//
// where a = sum_i a_i*k_i
//
typedef vector<numeral> row;
typedef vector<row> matrix;
bool solve_integer_equation(
row& values,
unsigned& index,
bool& is_fresh
);
// Integer linear solver.
// Determine integer solutions to:
//
// a+k = 0
//
// where a = sum_i a_i*k_i
//
// Solution, if there is any, is returned as a substitution.
// The return value is "true".
// If there is no solution, then return "false".
// together with equality "eq_unsat", such that
//
// eq_unsat = 0
//
// is implied and is unsatisfiable over the integers.
//
bool solve_integer_equations(vector<row>& rows, row& unsat_row);
};
#endif /* _ARITH_EQ_SOLVER_H_ */

View file

@ -0,0 +1,104 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_solver_plugin.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-30.
Revision History:
--*/
#include"arith_solver_plugin.h"
#include"ast_pp.h"
arith_solver_plugin::arith_solver_plugin(arith_simplifier_plugin & simp):
solver_plugin(symbol("arith"), simp.get_manager()),
m_simplifier(simp) {
}
bool arith_solver_plugin::solve(expr * lhs, expr * rhs, expr_mark const & forbidden, app_ref & var, expr_ref & subst) {
rational k;
if (!m_simplifier.is_numeral(rhs, k))
return false;
bool _is_int = m_simplifier.is_int(lhs);
ptr_buffer<expr> monomials;
ptr_buffer<expr> todo;
bool already_found = false;
rational c;
todo.push_back(lhs);
while (!todo.empty()) {
expr * curr = todo.back();
todo.pop_back();
rational coeff;
if (m_simplifier.is_add(curr)) {
SASSERT(to_app(curr)->get_num_args() == 2);
todo.push_back(to_app(curr)->get_arg(1));
todo.push_back(to_app(curr)->get_arg(0));
}
else {
if (!already_found) {
if (m_simplifier.is_mul(curr) &&
m_simplifier.is_numeral(to_app(curr)->get_arg(0), coeff) && !coeff.is_zero() && (!_is_int || coeff.is_minus_one()) &&
is_uninterp_const(to_app(curr)->get_arg(1)) &&
!forbidden.is_marked(to_app(curr)->get_arg(1))) {
c = coeff;
var = to_app(to_app(curr)->get_arg(1));
already_found = true;
}
else if (is_uninterp_const(curr) && !forbidden.is_marked(curr)) {
c = rational::one();
var = to_app(curr);
already_found = true;
}
else {
monomials.push_back(curr);
}
}
else {
monomials.push_back(curr);
}
}
}
if (!already_found)
return false;
SASSERT(!c.is_zero());
k /= c;
expr_ref_vector new_monomials(m_manager);
if (!k.is_zero())
new_monomials.push_back(m_simplifier.mk_numeral(k, _is_int));
c.neg();
expr_ref inv_c(m_manager);
if (!c.is_one()) {
rational inv(1);
inv /= c;
inv_c = m_simplifier.mk_numeral(inv, _is_int);
}
// divide monomials by c
unsigned sz = monomials.size();
for (unsigned i = 0; i < sz; i++) {
expr * m = monomials[i];
expr_ref new_m(m_manager);
if (!c.is_one())
m_simplifier.mk_mul(inv_c, m, new_m);
else
new_m = m;
new_monomials.push_back(new_m);
}
if (new_monomials.empty())
subst = m_simplifier.mk_numeral(rational(0), _is_int);
else
m_simplifier.mk_add(new_monomials.size(), new_monomials.c_ptr(), subst);
TRACE("arith_solver", tout << "solving:\n" << mk_pp(lhs, m_manager) << "\n" << mk_pp(rhs, m_manager)
<< "\nresult:\n" << mk_pp(var, m_manager) << "\n" << mk_pp(subst, m_manager) << "\n";);
return true;
}

View file

@ -0,0 +1,34 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_solver_plugin.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-30.
Revision History:
--*/
#ifndef _ARITH_SOLVER_PLUGIN_H_
#define _ARITH_SOLVER_PLUGIN_H_
#include"solver_plugin.h"
#include"arith_simplifier_plugin.h"
class arith_solver_plugin : public solver_plugin {
arith_simplifier_plugin & m_simplifier;
public:
arith_solver_plugin(arith_simplifier_plugin & simp);
virtual ~arith_solver_plugin() {}
virtual bool solve(expr * lhs, expr * rhs, expr_mark const & forbidden, app_ref & var, expr_ref & subst);
};
#endif /* _ARITH_SOLVER_PLUGIN_H_ */

File diff suppressed because it is too large Load diff

204
src/smt/asserted_formulas.h Normal file
View file

@ -0,0 +1,204 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
asserted_formulas.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-11.
Revision History:
--*/
#ifndef _ASSERTED_FORMULAS_H_
#define _ASSERTED_FORMULAS_H_
#include"front_end_params.h"
#include"simplifier.h"
#include"basic_simplifier_plugin.h"
#include"static_features.h"
#include"macro_manager.h"
#include"macro_finder.h"
#include"defined_names.h"
#include"solver_plugin.h"
#include"maximise_ac_sharing.h"
#include"bit2int.h"
// #include"qe.h"
#include"statistics.h"
#include"user_rewriter.h"
#include"pattern_inference.h"
class arith_simplifier_plugin;
class bv_simplifier_plugin;
class asserted_formulas {
ast_manager & m_manager;
front_end_params & m_params;
scoped_ptr<pattern_database> m_database;
simplifier m_pre_simplifier;
subst_simplifier m_simplifier;
basic_simplifier_plugin * m_bsimp;
bv_simplifier_plugin * m_bvsimp;
defined_names m_defined_names;
static_features m_static_features;
expr_ref_vector m_asserted_formulas; // formulas asserted by user
proof_ref_vector m_asserted_formula_prs; // proofs for the asserted formulas.
unsigned m_asserted_qhead;
expr_map m_subst;
ptr_vector<app> m_vars; // domain of m_subst
unsigned m_vars_qhead;
expr_mark m_forbidden;
ptr_vector<app> m_forbidden_vars;
macro_manager m_macro_manager;
scoped_ptr<macro_finder> m_macro_finder;
typedef plugin_manager<solver_plugin> solver_plugins;
solver_plugins m_solver_plugins;
bit2int m_bit2int;
maximise_bv_sharing m_bv_sharing;
user_rewriter m_user_rewriter;
bool m_inconsistent;
// qe::expr_quant_elim_star1 m_quant_elim;
struct scope {
unsigned m_asserted_formulas_lim;
unsigned m_vars_lim;
unsigned m_forbidden_vars_lim;
bool m_inconsistent_old;
};
svector<scope> m_scopes;
volatile bool m_cancel_flag;
void setup_simplifier_plugins(simplifier & s, basic_simplifier_plugin * & bsimp, arith_simplifier_plugin * & asimp, bv_simplifier_plugin * & bvsimp);
bool trivial_solve(expr * lhs, expr * rhs, app_ref & var, expr_ref & subst, proof_ref& pr);
bool is_pos_literal(expr * n);
bool is_neg_literal(expr * n);
bool solve_ite_definition_core(expr * lhs1, expr * rhs1, expr * lhs2, expr * rhs2, expr * cond, app_ref & var, expr_ref & subst);
bool solve_ite_definition(expr * arg1, expr * arg2, expr * arg3, app_ref & var, expr_ref & subst);
bool solve_core(expr * n, app_ref & var, expr_ref & subst, proof_ref& pr);
bool solve_core();
void solve();
void reduce_asserted_formulas();
void swap_asserted_formulas(expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
void find_macros_core();
void find_macros();
void expand_macros();
void apply_demodulators();
void apply_quasi_macros();
void nnf_cnf();
bool apply_eager_bit_blaster();
bool apply_user_rewriter();
void infer_patterns();
void eliminate_term_ite();
void reduce_and_solve();
void flush_cache() { m_pre_simplifier.reset(); m_simplifier.reset(); }
void restore_subst(unsigned old_size);
void restore_forbidden_vars(unsigned old_size);
void set_eliminate_and(bool flag);
void propagate_values();
void propagate_booleans();
bool pull_cheap_ite_trees();
bool pull_nested_quantifiers();
void push_assertion(expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs);
void context_simplifier();
void strong_context_simplifier();
void eliminate_and();
void refine_inj_axiom();
bool cheap_quant_fourier_motzkin();
bool quant_elim();
bool apply_der_core();
void apply_der();
void apply_distribute_forall();
bool apply_bit2int();
void lift_ite();
bool elim_bvs_from_quantifiers();
void ng_lift_ite();
#ifdef Z3DEBUG
bool check_well_sorted() const;
#endif
unsigned get_total_size() const;
bool has_bv() const;
void max_bv_sharing();
bool canceled() { return m_cancel_flag; }
public:
asserted_formulas(ast_manager & m, front_end_params & p);
~asserted_formulas();
void setup();
void assert_expr(expr * e, proof * in_pr);
void assert_expr(expr * e);
void reset();
void set_cancel_flag(bool f);
void push_scope();
void pop_scope(unsigned num_scopes);
bool inconsistent() const { return m_inconsistent; }
proof * get_inconsistency_proof() const;
void reduce();
unsigned get_num_formulas() const { return m_asserted_formulas.size(); }
unsigned get_formulas_last_level() const;
unsigned get_qhead() const { return m_asserted_qhead; }
void commit();
expr * get_formula(unsigned idx) const { return m_asserted_formulas.get(idx); }
proof * get_formula_proof(unsigned idx) const { return m_manager.proofs_enabled() ? m_asserted_formula_prs.get(idx) : 0; }
expr * const * get_formulas() const { return m_asserted_formulas.c_ptr(); }
proof * const * get_formula_proofs() const { return m_asserted_formula_prs.c_ptr(); }
void init(unsigned num_formulas, expr * const * formulas, proof * const * prs);
void register_simplifier_plugin(simplifier_plugin * p) { m_simplifier.register_plugin(p); }
simplifier & get_simplifier() { return m_simplifier; }
void set_user_rewriter(void* ctx, user_rewriter::fn* rw) { m_user_rewriter.set_rewriter(ctx, rw); }
void get_assertions(ptr_vector<expr> & result);
bool empty() const { return m_asserted_formulas.empty(); }
void collect_static_features();
void display(std::ostream & out) const;
void display_ll(std::ostream & out, ast_mark & pp_visited) const;
void collect_statistics(statistics & st) const;
// TODO: improve precision of the following method.
bool has_quantifiers() const { return m_simplifier.visited_quantifier(); /* approximation */ }
void set_pattern_database(pattern_database * db) { m_database = db; }
// -----------------------------------
//
// Macros
//
// -----------------------------------
unsigned get_num_macros() const { return m_macro_manager.get_num_macros(); }
unsigned get_first_macro_last_level() const { return m_macro_manager.get_first_macro_last_level(); }
func_decl * get_macro_func_decl(unsigned i) const { return m_macro_manager.get_macro_func_decl(i); }
func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const { return m_macro_manager.get_macro_interpretation(i, interp); }
quantifier * get_macro_quantifier(func_decl * f) const { return m_macro_manager.get_macro_quantifier(f); }
// auxiliary function used to create a logic context based on a model.
void insert_macro(func_decl * f, quantifier * m, proof * pr) { m_macro_manager.insert(f, m, pr); }
// -----------------------------------
//
// Eliminated vars
//
// -----------------------------------
ptr_vector<app>::const_iterator begin_subst_vars() const { return m_vars.begin(); }
ptr_vector<app>::const_iterator end_subst_vars() const { return m_vars.end(); }
ptr_vector<app>::const_iterator begin_subst_vars_last_level() const {
unsigned sidx = m_scopes.empty() ? 0 : m_scopes.back().m_vars_lim;
return m_vars.begin() + sidx;
}
expr * get_subst(app * var) { expr * def = 0; proof * pr; m_subst.get(var, def, pr); return def; }
bool is_subst(app * var) const { return m_subst.contains(var); }
void get_ordered_subst_vars(ptr_vector<app> & ordered_vars);
};
#endif /* _ASSERTED_FORMULAS_H_ */

View file

@ -0,0 +1,79 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
cached_var_subst.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2009-01-23.
Revision History:
--*/
#include"cached_var_subst.h"
bool cached_var_subst::key_eq_proc::operator()(cached_var_subst::key * k1, cached_var_subst::key * k2) const {
if (k1->m_qa != k2->m_qa)
return false;
if (k1->m_num_bindings != k2->m_num_bindings)
return false;
for (unsigned i = 0; i < k1->m_num_bindings; i++)
if (k1->m_bindings[i] != k2->m_bindings[i])
return false;
return true;
}
cached_var_subst::cached_var_subst(ast_manager & m):
m_proc(m),
m_refs(m) {
}
void cached_var_subst::reset() {
m_refs.reset();
m_instances.reset();
m_region.reset();
m_new_keys.reset();
}
void cached_var_subst::operator()(quantifier * qa, unsigned num_bindings, smt::enode * const * bindings, expr_ref & result) {
m_new_keys.reserve(num_bindings+1, 0);
key * new_key = m_new_keys[num_bindings];
if (new_key == 0)
new_key = static_cast<key*>(m_region.allocate(sizeof(key) + sizeof(expr*)*num_bindings));
new_key->m_qa = qa;
new_key->m_num_bindings = num_bindings;
for (unsigned i = 0; i < num_bindings; i++)
new_key->m_bindings[i] = bindings[i]->get_owner();
instances::entry * entry = m_instances.insert_if_not_there2(new_key, 0);
if (entry->get_data().m_key != new_key) {
SASSERT(entry->get_data().m_value != 0);
// entry was already there
m_new_keys[num_bindings] = new_key; // recycle key
result = entry->get_data().m_value;
return;
}
m_proc(qa->get_expr(), new_key->m_num_bindings, new_key->m_bindings, result);
// cache result
entry->get_data().m_value = result;
// remove key from cache
m_new_keys[num_bindings] = 0;
// increment reference counters
m_refs.push_back(qa);
for (unsigned i = 0; i < new_key->m_num_bindings; i++)
m_refs.push_back(new_key->m_bindings[i]);
m_refs.push_back(result);
}

View file

@ -0,0 +1,53 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
cached_var_subst.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2009-01-23.
Revision History:
--*/
#ifndef _CACHED_VAR_SUBST_H_
#define _CACHED_VAR_SUBST_H_
#include"var_subst.h"
#include"map.h"
#include"smt_enode.h"
class cached_var_subst {
struct key {
quantifier * m_qa;
unsigned m_num_bindings;
expr * m_bindings[0];
};
struct key_hash_proc {
unsigned operator()(key * k) const {
return string_hash(reinterpret_cast<char const *>(k->m_bindings), sizeof(expr *) * k->m_num_bindings, k->m_qa->get_id());
}
};
struct key_eq_proc {
bool operator()(key * k1, key * k2) const;
};
typedef map<key *, expr *, key_hash_proc, key_eq_proc> instances;
var_subst m_proc;
expr_ref_vector m_refs;
instances m_instances;
region m_region;
ptr_vector<key> m_new_keys; // mapping from num_bindings -> next key
public:
cached_var_subst(ast_manager & m);
void operator()(quantifier * qa, unsigned num_bindings, smt::enode * const * bindings, expr_ref & result);
void reset();
};
#endif /* _CACHED_VAR_SUBST_H_ */

104
src/smt/cost_evaluator.cpp Normal file
View file

@ -0,0 +1,104 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
cost_evaluator.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-14.
Revision History:
--*/
#include"cost_evaluator.h"
#include"warning.h"
cost_evaluator::cost_evaluator(ast_manager & m):
m_manager(m),
m_util(m) {
}
float cost_evaluator::eval(expr * f) const {
#define E(IDX) eval(to_app(f)->get_arg(IDX))
if (is_app(f)) {
unsigned num_args;
family_id fid = to_app(f)->get_family_id();
if (fid == m_manager.get_basic_family_id()) {
switch (to_app(f)->get_decl_kind()) {
case OP_TRUE: return 1.0f;
case OP_FALSE: return 0.0f;
case OP_NOT: return E(0) == 0.0f ? 1.0f : 0.0f;
case OP_AND:
num_args = to_app(f)->get_num_args();
for (unsigned i = 0; i < num_args; i++)
if (E(i) == 0.0f)
return 0.0f;
return 1.0f;
case OP_OR:
num_args = to_app(f)->get_num_args();
for (unsigned i = 0; i < num_args; i++)
if (E(i) != 0.0f)
return 1.0f;
return 0.0f;
case OP_ITE: return E(0) != 0.0f ? E(1) : E(2);
case OP_EQ:
case OP_IFF: return E(0) == E(1) ? 1.0f : 0.0f;
case OP_XOR: return E(0) != E(1) ? 1.0f : 0.0f;
case OP_IMPLIES:
if (E(0) == 0.0f)
return 1.0f;
return E(1) != 0.0f ? 1.0f : 0.0f;
default:
;
}
}
else if (fid == m_util.get_family_id()) {
switch (to_app(f)->get_decl_kind()) {
case OP_NUM: {
rational r = to_app(f)->get_decl()->get_parameter(0).get_rational();
return static_cast<float>(numerator(r).get_int64())/static_cast<float>(denominator(r).get_int64());
}
case OP_LE: return E(0) <= E(1) ? 1.0f : 0.0f;
case OP_GE: return E(0) >= E(1) ? 1.0f : 0.0f;
case OP_LT: return E(0) < E(1) ? 1.0f : 0.0f;
case OP_GT: return E(0) > E(1) ? 1.0f : 0.0f;
case OP_ADD: return E(0) + E(1);
case OP_SUB: return E(0) - E(1);
case OP_UMINUS: return - E(0);
case OP_MUL: return E(0) * E(1);
case OP_DIV: {
float q = E(1);
if (q == 0.0f) {
warning_msg("cost function division by zero");
return 1.0f;
}
return E(0) / q;
}
default:
;
}
}
}
else if (is_var(f)) {
unsigned idx = to_var(f)->get_idx();
if (idx < m_num_args)
return m_args[m_num_args - idx - 1];
}
warning_msg("cost function evaluation error");
return 1.0f;
}
float cost_evaluator::operator()(expr * f, unsigned num_args, float const * args) {
m_num_args = num_args;
m_args = args;
return eval(f);
}

43
src/smt/cost_evaluator.h Normal file
View file

@ -0,0 +1,43 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
cost_evaluator.h
Abstract:
Simple evaluator for cost function
Author:
Leonardo de Moura (leonardo) 2008-06-14.
Revision History:
--*/
#ifndef _COST_EVALUATOR_H_
#define _COST_EVALUATOR_H_
#include"ast.h"
#include"arith_decl_plugin.h"
class cost_evaluator {
ast_manager & m_manager;
arith_util m_util;
unsigned m_num_args;
float const * m_args;
float eval(expr * f) const;
public:
cost_evaluator(ast_manager & m);
/**
I'm using the same standard used in quantifier instantiation.
(VAR 0) is stored in the last position of the array.
...
(VAR (num_args - 1)) is stored in the first position of the array.
*/
float operator()(expr * f, unsigned num_args, float const * args);
};
#endif /* _COST_EVALUATOR_H_ */

316
src/smt/database.h Normal file
View file

@ -0,0 +1,316 @@
char const * g_pattern_database =
"(benchmark patterns \n"
" :status unknown \n"
" :logic ALL \n"
" :extrafuns ((?f1 Int Int Int Int) (?f2 Int Int Int) (?f3 Int Int Int) (?f4 Int Int Int)\n"
" (?f5 Int Int Int) (?f6 Int Int) (?f7 Int Int) (?f8 Int Int Int) (?f9 Int Int Int)\n"
" (?f10 Int) (?f11 Int) (?f12 Int Int) (?f13 Int Int) (?f14 Int Int Int) \n"
" (?f15 Int Int) (?f16 Int Int) (?f17 Int Int) (?f18 Int Int) (?f19 Int Int)\n"
" (?f20 Int Int) (?f21 Int) (?f22 Int) (?f23 Int) (?f24 Int Int) (?f25 Int Int)\n"
" )\n"
"\n"
" :formula (forall (a Int) (i Int) (e Int) \n"
" (= (?f2 (?f1 a i e) i) e)\n"
" :pats { (?f1 a i e) }\n"
" :weight { 0 })\n"
"\n"
" :formula (forall (a Int) (i Int) (j Int) (e Int) \n"
" (or (= i j) (= (?f2 (?f1 a i e) j) (?f2 a j)))\n"
" :pats { (?f2 (?f1 a i e) j) }\n"
" :weight { 0 })\n"
"\n"
" :formula (forall (t0 Int) (t1 Int) (t2 Int) \n"
" (or (not (= (?f3 t0 t1) 1))\n"
" (not (= (?f3 t1 t2) 1))\n"
" (= (?f3 t0 t2) 1))\n"
" :pats { (?f3 t0 t1) (?f3 t1 t2) })\n"
"\n"
" :formula (forall (t0 Int) (t1 Int) \n"
" (or (not (= (?f3 t0 t1) 1))\n"
" (not (= (?f3 t1 t0) 1))\n"
" (= t0 t1))\n"
" :pats { (?f3 t0 t1) (?f3 t1 t0) })\n"
"\n"
" :formula (forall (t0 Int) (t1 Int) (t2 Int) \n"
" (or (not (= (?f3 t0 (?f4 t1 t2)) 1))\n"
" (= (?f5 t2 t0) (?f4 t1 t2)))\n"
" :pats { (?f3 t0 (?f4 t1 t2)) })\n"
"\n"
" :formula (forall (t Int) \n"
" (= (?f25 (?f24 t)) t)\n"
" :pats { (?f24 t) })\n"
"\n"
" :formula (forall (t0 Int) (t1 Int) \n"
" (iff (= (?f3 t0 (?f6 t1)) 1)\n"
" (not (or (not (= t0 (?f6 (?f7 t0))))\n"
" (not (= (?f3 (?f7 t0) t1) 1)))))\n"
" :pats { (?f3 t0 (?f6 t1)) })\n"
"\n"
" :formula (forall (x Int) (t Int) \n"
" (or (not (= (?f8 x t) 1))\n"
" (= (?f9 x t) x))\n"
" :pats { (?f9 x t) })\n"
"\n"
" :formula (forall (x Int) (t Int) \n"
" (or (not (= (?f3 t ?f10) 1))\n"
" (iff (= (?f8 x t) 1)\n"
" (or (= x ?f11)\n"
" (= (?f3 (?f12 x) t) 1))))\n"
" :pats { (?f3 t ?f10) (?f8 x t) })\n"
"\n"
" :formula (forall (e Int) (a Int) (i Int) \n"
" (= (?f8 (?f2 (?f2 (?f13 e) a) i)\n"
" (?f7 (?f12 a))) 1)\n"
" :pats { (?f2 (?f2 (?f13 e) a) i) })\n"
"\n"
" :formula (forall (x Int) (f Int) (a0 Int) \n"
" (or (<= (+ a0 (* -1 (?f15 f))) 0)\n"
" (not (= (?f14 x a0) 1))\n"
" (= (?f14 (?f2 f x) a0) 1))\n"
" :pats { (?f14 (?f2 f x) a0) })\n"
"\n"
" :formula (forall (a Int) (e Int) (i Int) (a0 Int) \n"
" (or (<= (+ a0 (* -1 (?f16 e))) 0)\n"
" (not (= (?f14 a a0) 1))\n"
" (= (?f14 (?f2 (?f2 e a) i) a0) 1))\n"
" :pats { (?f14 (?f2 (?f2 e a) i) a0) })\n"
"\n"
" :formula (forall (S Int) \n"
" (= (?f2 (?f18 S) (?f17 (?f18 S))) 1)\n"
" :pats { (?f2 (?f18 S) (?f17 (?f18 S))) })\n"
"\n"
" :formula (forall (s Int) \n"
" (or (not (= 1 (?f19 s)))\n"
" (= (?f3 (?f12 s) ?f23) 1))\n"
" :pats { (?f19 s) })\n"
"\n"
" :formula (forall (t Int) \n"
" (not (or (= (?f20 t) ?f11)\n"
" (not (= (?f8 (?f20 t) ?f21) 1))\n"
" (not (= (?f14 (?f20 t) ?f22) 1))))\n"
" :pats { (?f20 t) })\n"
"\n"
" :extrafuns ((?f26 Int Int Int Int) \n"
" (?f27 Int Int Int Int Int))\n"
" \n"
" :formula (forall (A Int) (o Int) (f Int) (v Int)\n"
" (= (?f26 (?f27 A o f v) o f) v)\n"
" :pats { (?f27 A o f v) }\n"
" :weight { 0 }) \n"
"\n"
" :formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int)\n"
" (or (= o p) (= (?f26 (?f27 A o f v) p g) (?f26 A p g)))\n"
" :pats { (?f26 (?f27 A o f v) p g) }\n"
" :weight { 0 })\n"
"\n"
" :formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int)\n"
" (or (= f g) (= (?f26 (?f27 A o f v) p g) (?f26 A p g)))\n"
" :pats { (?f26 (?f27 A o f v) p g) }\n"
" :weight { 0 })\n"
"\n"
" :extrapreds ((?f28 Int Int))\n"
"\n"
" :formula (forall (t Int) (u Int) (v Int)\n"
" (or (not (?f28 t u))\n"
" (not (?f28 u v))\n"
" (?f28 t v))\n"
" :pat {(?f28 t u) (?f28 u v)})\n"
"\n"
" :formula (forall (t Int) (u Int)\n"
" (or (not (?f28 t u))\n"
" (not (?f28 u t))\n"
" (= t u))\n"
" :pat {(?f28 t u) (?f28 u t)})\n"
"\n"
" :extrafuns ((?f29 Int Int) (?f30 Int Int) (?f31 Int Int Int) (?f32 Int) (?f33 Int) (?f34 Int Int Int)\n"
" (?f35 Int Int) (?f36 Int) (?f37 Int) (?f38 Int) (?f39 Int Int) (?f40 Int)\n"
" (?f41 Int) (?f42 Int Int) (?f43 Int Int) (?f44 Int) (?f45 Int Int))\n"
"\n"
" :formula (forall (x Int) (p Int)\n"
" (or (not (?f28 (?f30 (?f31 x p)) ?f32))\n"
" (not (= (?f31 x p) p))\n"
" (= x p))\n"
" :pat { (?f28 (?f30 (?f31 x p)) ?f32)} )\n"
" \n"
" :formula (forall (h Int) (o Int) (f Int) (T Int)\n"
" (or \n"
" (not (= (?f39 h) ?f33))\n"
" (= (?f26 h o (?f34 f T)) ?f36)\n"
" (not (or (not (= (?f26 h (?f26 h o (?f34 f T)) ?f37) o))\n"
" (not (= (?f26 h (?f26 h o (?f34 f T)) ?f38) T)))))\n"
" :pat {(?f26 h o (?f34 f T))})\n"
"\n"
" :formula (forall (h Int) (o Int) (f Int)\n"
" (or\n"
" (not (= (?f39 h) ?f33))\n"
" (= (?f26 h o (?f35 f)) ?f36)\n"
" (not (or (not (= (?f26 h (?f26 h o (?f35 f)) ?f37) (?f26 h o ?f37)))\n"
" (not (= (?f26 h (?f26 h o (?f35 f)) ?f38) (?f26 h o ?f38))))))\n"
" :pat {(?f26 h o (?f35 f))})\n"
" \n"
" :formula (forall (h Int) (o Int)\n"
" (or \n"
" (not (= (?f39 h) ?f33))\n"
" (= (?f26 h o ?f38) ?f44)\n"
" (not (?f28 (?f26 h (?f26 h o ?f37) ?f41) (?f26 h o ?f38)))\n"
" (= (?f26 h (?f26 h o ?f37) ?f40) (?f42 (?f26 h o ?f38)))\n"
" (not (or (not (= (?f26 h o ?f41) (?f43 o)))\n"
" (not (= (?f26 h o ?f40) (?f43 o))))))\n"
" :pat {(?f28 (?f26 h (?f26 h o ?f37) ?f41) (?f26 h o ?f38))})\n"
"\n"
" :formula (forall (T Int) (h Int)\n"
" (or (not (= (?f39 h) ?f33))\n"
" (= (?f26 h (?f45 T) ?f38) ?f44))\n"
" :pat {(?f26 h (?f45 T) ?f38)})\n"
"\n"
" :extrafuns ((?f46 Int Int Int)\n"
" (?f47 Int Int Int)\n"
" (?f48 Int Int Int)\n"
" (?f49 Int)\n"
" (?f50 Int Int Int)\n"
" (?f51 Int Int Int)\n"
" (?f52 Int Int)\n"
" )\n"
"\n"
" :formula (forall (a Int) (T Int) (i Int) (r Int) (heap Int)\n"
" (or (not (= (?f39 heap) ?f33))\n"
" (not (?f28 (?f43 a) (?f46 T r)))\n"
" (= (?f47 (?f48 (?f26 heap a ?f49) i) T) ?f33))\n"
" :pat {(?f28 (?f43 a) (?f46 T r)) (?f48 (?f26 heap a ?f49) i)})\n"
"\n"
" :formula (forall (a Int) (T Int) (r Int)\n"
" (or (= a ?f36) \n"
" (not (?f28 (?f43 a) (?f46 T r)))\n"
" (= (?f52 a) r))\n"
" :pat {(?f28 (?f43 a) (?f46 T r))})\n"
"\n"
" :extrafuns ((?f53 Int Int Int)\n"
" (?f54 Int Int)\n"
" (?f55 Int)\n"
" (?f56 Int Int)\n"
" (?f57 Int)\n"
" (?f58 Int)\n"
" (?f59 Int Int Int)\n"
" (?f60 Int Int Int)\n"
" (?f61 Int Int Int)\n"
" )\n"
"\n"
" :extrapreds ((?f62 Int Int))\n"
" \n"
" :formula (forall (T Int) (ET Int) (r Int)\n"
" (or (not (?f28 T (?f53 ET r)))\n"
" (= (?f54 T) ?f55))\n"
" :pat {(?f28 T (?f53 ET r))})\n"
"\n"
" :formula (forall (A Int) (r Int) (T Int)\n"
" (or\n"
" (not (?f28 T (?f46 A r)))\n"
" (not (or (not (= T (?f46 (?f56 T) r)))\n"
" (not (?f28 (?f56 T) A)))))\n"
" :pat {(?f28 T (?f46 A r))})\n"
"\n"
" :formula (forall (A Int) (r Int) (T Int)\n"
" (or (not (?f28 T (?f53 A r)))\n"
" (= T (?f53 A r)))\n"
" :pat {(?f28 T (?f53 A r))})\n"
"\n"
" :extrafuns ((?f63 Int Int Int)\n"
" (?f64 Int Int Int)\n"
" )\n"
"\n"
" :formula (forall (A Int) (B Int) (C Int)\n"
" (or (not (?f28 C (?f63 B A)))\n"
" (= (?f64 C A) B))\n"
" :pat {(?f28 C (?f63 B A))})\n"
" \n"
" :formula (forall (o Int) (T Int)\n"
" (iff (= (?f47 o T) ?f33)\n"
" (or (= o ?f36)\n"
" (?f28 (?f43 o) T)))\n"
" :pat {(?f47 o T)})\n"
"\n"
" :formula (forall (o Int) (T Int)\n"
" (iff (= (?f51 o T) ?f33)\n"
" (or (= o ?f36)\n"
" (not (= (?f47 o T) ?f33))))\n"
" :pat {(?f51 o T)})\n"
"\n"
" :formula (forall (h Int) (o Int)\n"
" (or (not (= (?f39 h) ?f33))\n"
" (= o ?f36)\n"
" (not (?f28 (?f43 o) ?f57))\n"
" (not (or (not (= (?f26 h o ?f41) (?f43 o)))\n"
" (not (= (?f26 h o ?f40) (?f43 o))))))\n"
" :pat {(?f28 (?f43 o) ?f57) (?f26 h o ?f41)})\n"
"\n"
" :formula (forall (h Int) (o Int) (f Int) (T Int)\n"
" (or (not (= (?f39 h) ?f33))\n"
" (?f62 (?f26 h o (?f60 f T)) T))\n"
" :pat {(?f26 h o (?f60 f T))})\n"
"\n"
" :formula (forall (h Int) (o Int) (f Int)\n"
" (or\n"
" (not (= (?f39 h) ?f33))\n"
" (not (= (?f26 h o ?f58) ?f33))\n"
" (= (?f61 h (?f26 h o f)) ?f33))\n"
" :pat {(?f61 h (?f26 h o f))})\n"
"\n"
" :formula (forall (h Int) (s Int) (f Int)\n"
" (or (not (= (?f61 h s) ?f33))\n"
" (= (?f61 h (?f59 s f)) ?f33))\n"
" :pat {(?f61 h (?f59 s f))})\n"
"\n"
" :extrapreds ((?f65 Int Int))\n"
"\n"
" :formula (forall (x Int) (f Int) (a0 Int)\n"
" (or (<= (+ a0 (* -1 (?f15 f))) 0)\n"
" (not (?f65 x a0))\n"
" (?f65 (?f2 f x) a0))\n"
" :pat {(?f65 (?f2 f x) a0)})\n"
"\n"
" :formula (forall (a Int) (e Int) (i Int) (a0 Int) \n"
" (or (<= (+ a0 (* -1 (?f16 e))) 0)\n"
" (not (?f65 a a0))\n"
" (?f65 (?f2 (?f2 e a) i) a0))\n"
" :pats { (?f65 (?f2 (?f2 e a) i) a0) })\n"
"\n"
" :formula (forall (e Int) (a Int) (i Int) \n"
" (= (?f8 (?f2 (?f2 (?f13 e) a) i)\n"
" (?f7 (?f12 a))) ?f33)\n"
" :pats { (?f2 (?f2 (?f13 e) a) i) })\n"
"\n"
" :formula (forall (t0 Int) (t1 Int)\n"
" (iff (?f28 t0 (?f6 t1))\n"
" (not (or (not (= t0 (?f6 (?f7 t0))))\n"
" (not (?f28 (?f7 t0) t1)))))\n"
" :pat {(?f28 t0 (?f6 t1))})\n"
"\n"
" :formula (forall (t0 Int) (t1 Int) (t2 Int) \n"
" (or (not (?f28 t0 (?f4 t1 t2)))\n"
" (= (?f5 t2 t0) (?f4 t1 t2)))\n"
" :pats { (?f28 t0 (?f4 t1 t2)) })\n"
"\n"
" :formula (forall (t0 Int) (t1 Int) \n"
" (iff (?f28 t0 (?f6 t1))\n"
" (not (or (not (= t0 (?f6 (?f7 t0))))\n"
" (not (?f28 (?f7 t0) t1)))))\n"
" :pats { (?f28 t0 (?f6 t1)) })\n"
"\n"
" :formula (forall (x Int) (t Int) \n"
" (or (not (= (?f8 x t) ?f33))\n"
" (= (?f9 x t) x))\n"
" :pats { (?f9 x t) })\n"
"\n"
" :formula (forall (x Int) (t Int) \n"
" (or (not (?f28 t ?f10))\n"
" (iff (= (?f8 x t) ?f33)\n"
" (or (= x ?f11)\n"
" (?f28 (?f12 x) t))))\n"
" :pats { (?f28 t ?f10) (?f8 x t) })\n"
"\n"
" :formula (forall (e Int) (a Int) (i Int) \n"
" (= (?f8 (?f2 (?f2 (?f13 e) a) i)\n"
" (?f7 (?f12 a))) 1)\n"
" :pats { (?f2 (?f2 (?f13 e) a) i) })\n"
" )\n"
;

314
src/smt/database.smt Normal file
View file

@ -0,0 +1,314 @@
(benchmark patterns
:status unknown
:logic ALL
:extrafuns ((?store Int Int Int Int) (?select Int Int Int) (?PO Int Int Int) (?asChild Int Int Int)
(?classDown Int Int Int) (?array Int Int) (?elemtype Int Int) (?is Int Int Int) (?cast Int Int Int)
(?Object Int) (?null Int) (?typeof Int Int) (?asElems Int Int) (?isAllocated Int Int Int)
(?fClosedTime Int Int) (?eClosedTime Int Int) (?max Int Int) (?asLockSet Int Int) (?isNewArray Int Int)
(?classLiteral Int Int) (?Class Int) (?alloc Int) (?arrayType Int) (?f Int Int) (?finv Int Int)
)
:formula (forall (a Int) (i Int) (e Int)
(= (?select (?store a i e) i) e)
:pats { (?store a i e) }
:weight { 0 })
:formula (forall (a Int) (i Int) (j Int) (e Int)
(or (= i j) (= (?select (?store a i e) j) (?select a j)))
:pats { (?select (?store a i e) j) }
:weight { 0 })
:formula (forall (t0 Int) (t1 Int) (t2 Int)
(or (not (= (?PO t0 t1) 1))
(not (= (?PO t1 t2) 1))
(= (?PO t0 t2) 1))
:pats { (?PO t0 t1) (?PO t1 t2) })
:formula (forall (t0 Int) (t1 Int)
(or (not (= (?PO t0 t1) 1))
(not (= (?PO t1 t0) 1))
(= t0 t1))
:pats { (?PO t0 t1) (?PO t1 t0) })
:formula (forall (t0 Int) (t1 Int) (t2 Int)
(or (not (= (?PO t0 (?asChild t1 t2)) 1))
(= (?classDown t2 t0) (?asChild t1 t2)))
:pats { (?PO t0 (?asChild t1 t2)) })
:formula (forall (t Int)
(= (?finv (?f t)) t)
:pats { (?f t) })
:formula (forall (t0 Int) (t1 Int)
(iff (= (?PO t0 (?array t1)) 1)
(not (or (not (= t0 (?array (?elemtype t0))))
(not (= (?PO (?elemtype t0) t1) 1)))))
:pats { (?PO t0 (?array t1)) })
:formula (forall (x Int) (t Int)
(or (not (= (?is x t) 1))
(= (?cast x t) x))
:pats { (?cast x t) })
:formula (forall (x Int) (t Int)
(or (not (= (?PO t ?Object) 1))
(iff (= (?is x t) 1)
(or (= x ?null)
(= (?PO (?typeof x) t) 1))))
:pats { (?PO t ?Object) (?is x t) })
:formula (forall (e Int) (a Int) (i Int)
(= (?is (?select (?select (?asElems e) a) i)
(?elemtype (?typeof a))) 1)
:pats { (?select (?select (?asElems e) a) i) })
:formula (forall (x Int) (f Int) (a0 Int)
(or (<= (+ a0 (* -1 (?fClosedTime f))) 0)
(not (= (?isAllocated x a0) 1))
(= (?isAllocated (?select f x) a0) 1))
:pats { (?isAllocated (?select f x) a0) })
:formula (forall (a Int) (e Int) (i Int) (a0 Int)
(or (<= (+ a0 (* -1 (?eClosedTime e))) 0)
(not (= (?isAllocated a a0) 1))
(= (?isAllocated (?select (?select e a) i) a0) 1))
:pats { (?isAllocated (?select (?select e a) i) a0) })
:formula (forall (S Int)
(= (?select (?asLockSet S) (?max (?asLockSet S))) 1)
:pats { (?select (?asLockSet S) (?max (?asLockSet S))) })
:formula (forall (s Int)
(or (not (= 1 (?isNewArray s)))
(= (?PO (?typeof s) ?arrayType) 1))
:pats { (?isNewArray s) })
:formula (forall (t Int)
(not (or (= (?classLiteral t) ?null)
(not (= (?is (?classLiteral t) ?Class) 1))
(not (= (?isAllocated (?classLiteral t) ?alloc) 1))))
:pats { (?classLiteral t) })
:extrafuns ((?select2 Int Int Int Int)
(?store2 Int Int Int Int Int))
:formula (forall (A Int) (o Int) (f Int) (v Int)
(= (?select2 (?store2 A o f v) o f) v)
:pats { (?store2 A o f v) }
:weight { 0 })
:formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int)
(or (= o p) (= (?select2 (?store2 A o f v) p g) (?select2 A p g)))
:pats { (?select2 (?store2 A o f v) p g) }
:weight { 0 })
:formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int)
(or (= f g) (= (?select2 (?store2 A o f v) p g) (?select2 A p g)))
:pats { (?select2 (?store2 A o f v) p g) }
:weight { 0 })
:extrapreds ((?subtypes Int Int))
:formula (forall (t Int) (u Int) (v Int)
(or (not (?subtypes t u))
(not (?subtypes u v))
(?subtypes t v))
:pat {(?subtypes t u) (?subtypes u v)})
:formula (forall (t Int) (u Int)
(or (not (?subtypes t u))
(not (?subtypes u t))
(= t u))
:pat {(?subtypes t u) (?subtypes u t)})
:extrafuns ((?Unbox Int Int) (?UnboxedType Int Int) (?Box Int Int Int) (?System.Object Int) (?Smt.true Int) (?AsRepField Int Int Int)
(?AsPeerField Int Int) (?nullObject Int) (?ownerRef_ Int) (?ownerFrame_ Int) (IntsHeap Int Int) (?localinv_ Int)
(?inv_ Int) (?BaseClass_ Int Int) (?typeof_ Int Int) (?PeerGroupPlaceholder_ Int) (?ClassRepr Int Int))
:formula (forall (x Int) (p Int)
(or (not (?subtypes (?UnboxedType (?Box x p)) ?System.Object))
(not (= (?Box x p) p))
(= x p))
:pat { (?subtypes (?UnboxedType (?Box x p)) ?System.Object)} )
:formula (forall (h Int) (o Int) (f Int) (T Int)
(or
(not (= (IntsHeap h) ?Smt.true))
(= (?select2 h o (?AsRepField f T)) ?nullObject)
(not (or (not (= (?select2 h (?select2 h o (?AsRepField f T)) ?ownerRef_) o))
(not (= (?select2 h (?select2 h o (?AsRepField f T)) ?ownerFrame_) T)))))
:pat {(?select2 h o (?AsRepField f T))})
:formula (forall (h Int) (o Int) (f Int)
(or
(not (= (IntsHeap h) ?Smt.true))
(= (?select2 h o (?AsPeerField f)) ?nullObject)
(not (or (not (= (?select2 h (?select2 h o (?AsPeerField f)) ?ownerRef_) (?select2 h o ?ownerRef_)))
(not (= (?select2 h (?select2 h o (?AsPeerField f)) ?ownerFrame_) (?select2 h o ?ownerFrame_))))))
:pat {(?select2 h o (?AsPeerField f))})
:formula (forall (h Int) (o Int)
(or
(not (= (IntsHeap h) ?Smt.true))
(= (?select2 h o ?ownerFrame_) ?PeerGroupPlaceholder_)
(not (?subtypes (?select2 h (?select2 h o ?ownerRef_) ?inv_) (?select2 h o ?ownerFrame_)))
(= (?select2 h (?select2 h o ?ownerRef_) ?localinv_) (?BaseClass_ (?select2 h o ?ownerFrame_)))
(not (or (not (= (?select2 h o ?inv_) (?typeof_ o)))
(not (= (?select2 h o ?localinv_) (?typeof_ o))))))
:pat {(?subtypes (?select2 h (?select2 h o ?ownerRef_) ?inv_) (?select2 h o ?ownerFrame_))})
:formula (forall (T Int) (h Int)
(or (not (= (IntsHeap h) ?Smt.true))
(= (?select2 h (?ClassRepr T) ?ownerFrame_) ?PeerGroupPlaceholder_))
:pat {(?select2 h (?ClassRepr T) ?ownerFrame_)})
:extrafuns ((?RefArray Int Int Int)
(Ints_ Int Int Int)
(?RefArrayGet Int Int Int)
(?elements_ Int)
(?NonNullRefArray Int Int Int)
(IntsNotNull_ Int Int Int)
(?Rank_ Int Int)
)
:formula (forall (a Int) (T Int) (i Int) (r Int) (heap Int)
(or (not (= (IntsHeap heap) ?Smt.true))
(not (?subtypes (?typeof_ a) (?RefArray T r)))
(= (Ints_ (?RefArrayGet (?select2 heap a ?elements_) i) T) ?Smt.true))
:pat {(?subtypes (?typeof_ a) (?RefArray T r)) (?RefArrayGet (?select2 heap a ?elements_) i)})
:formula (forall (a Int) (T Int) (r Int)
(or (= a ?nullObject)
(not (?subtypes (?typeof_ a) (?RefArray T r)))
(= (?Rank_ a) r))
:pat {(?subtypes (?typeof_ a) (?RefArray T r))})
:extrafuns ((?ValueArray Int Int Int)
(?ArrayCategory_ Int Int)
(?ArrayCategoryValue_ Int)
(?ElementType_ Int Int)
(?System.Array Int)
(?allocated_ Int)
(?StructGet_ Int Int Int)
(?AsRangeField Int Int Int)
(IntsAllocated Int Int Int)
)
:extrapreds ((IntnRange Int Int))
:formula (forall (T Int) (ET Int) (r Int)
(or (not (?subtypes T (?ValueArray ET r)))
(= (?ArrayCategory_ T) ?ArrayCategoryValue_))
:pat {(?subtypes T (?ValueArray ET r))})
:formula (forall (A Int) (r Int) (T Int)
(or
(not (?subtypes T (?RefArray A r)))
(not (or (not (= T (?RefArray (?ElementType_ T) r)))
(not (?subtypes (?ElementType_ T) A)))))
:pat {(?subtypes T (?RefArray A r))})
:formula (forall (A Int) (r Int) (T Int)
(or (not (?subtypes T (?ValueArray A r)))
(= T (?ValueArray A r)))
:pat {(?subtypes T (?ValueArray A r))})
:extrafuns ((?AsDirectSubClass Int Int Int)
(?OneClassDown Int Int Int)
)
:formula (forall (A Int) (B Int) (C Int)
(or (not (?subtypes C (?AsDirectSubClass B A)))
(= (?OneClassDown C A) B))
:pat {(?subtypes C (?AsDirectSubClass B A))})
:formula (forall (o Int) (T Int)
(iff (= (Ints_ o T) ?Smt.true)
(or (= o ?nullObject)
(?subtypes (?typeof_ o) T)))
:pat {(Ints_ o T)})
:formula (forall (o Int) (T Int)
(iff (= (IntsNotNull_ o T) ?Smt.true)
(or (= o ?nullObject)
(not (= (Ints_ o T) ?Smt.true))))
:pat {(IntsNotNull_ o T)})
:formula (forall (h Int) (o Int)
(or (not (= (IntsHeap h) ?Smt.true))
(= o ?nullObject)
(not (?subtypes (?typeof_ o) ?System.Array))
(not (or (not (= (?select2 h o ?inv_) (?typeof_ o)))
(not (= (?select2 h o ?localinv_) (?typeof_ o))))))
:pat {(?subtypes (?typeof_ o) ?System.Array) (?select2 h o ?inv_)})
:formula (forall (h Int) (o Int) (f Int) (T Int)
(or (not (= (IntsHeap h) ?Smt.true))
(IntnRange (?select2 h o (?AsRangeField f T)) T))
:pat {(?select2 h o (?AsRangeField f T))})
:formula (forall (h Int) (o Int) (f Int)
(or
(not (= (IntsHeap h) ?Smt.true))
(not (= (?select2 h o ?allocated_) ?Smt.true))
(= (IntsAllocated h (?select2 h o f)) ?Smt.true))
:pat {(IntsAllocated h (?select2 h o f))})
:formula (forall (h Int) (s Int) (f Int)
(or (not (= (IntsAllocated h s) ?Smt.true))
(= (IntsAllocated h (?StructGet_ s f)) ?Smt.true))
:pat {(IntsAllocated h (?StructGet_ s f))})
:extrapreds ((?isAllocated_ Int Int))
:formula (forall (x Int) (f Int) (a0 Int)
(or (<= (+ a0 (* -1 (?fClosedTime f))) 0)
(not (?isAllocated_ x a0))
(?isAllocated_ (?select f x) a0))
:pat {(?isAllocated_ (?select f x) a0)})
:formula (forall (a Int) (e Int) (i Int) (a0 Int)
(or (<= (+ a0 (* -1 (?eClosedTime e))) 0)
(not (?isAllocated_ a a0))
(?isAllocated_ (?select (?select e a) i) a0))
:pats { (?isAllocated_ (?select (?select e a) i) a0) })
:formula (forall (e Int) (a Int) (i Int)
(= (?is (?select (?select (?asElems e) a) i)
(?elemtype (?typeof a))) ?Smt.true)
:pats { (?select (?select (?asElems e) a) i) })
:formula (forall (t0 Int) (t1 Int)
(iff (?subtypes t0 (?array t1))
(not (or (not (= t0 (?array (?elemtype t0))))
(not (?subtypes (?elemtype t0) t1)))))
:pat {(?subtypes t0 (?array t1))})
:formula (forall (t0 Int) (t1 Int) (t2 Int)
(or (not (?subtypes t0 (?asChild t1 t2)))
(= (?classDown t2 t0) (?asChild t1 t2)))
:pats { (?subtypes t0 (?asChild t1 t2)) })
:formula (forall (t0 Int) (t1 Int)
(iff (?subtypes t0 (?array t1))
(not (or (not (= t0 (?array (?elemtype t0))))
(not (?subtypes (?elemtype t0) t1)))))
:pats { (?subtypes t0 (?array t1)) })
:formula (forall (x Int) (t Int)
(or (not (= (?is x t) ?Smt.true))
(= (?cast x t) x))
:pats { (?cast x t) })
:formula (forall (x Int) (t Int)
(or (not (?subtypes t ?Object))
(iff (= (?is x t) ?Smt.true)
(or (= x ?null)
(?subtypes (?typeof x) t))))
:pats { (?subtypes t ?Object) (?is x t) })
:formula (forall (e Int) (a Int) (i Int)
(= (?is (?select (?select (?asElems e) a) i)
(?elemtype (?typeof a))) 1)
:pats { (?select (?select (?asElems e) a) i) })
)

906
src/smt/demodulator.cpp Normal file
View file

@ -0,0 +1,906 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
demodulator.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-12.
Revision History:
Christoph Wintersteiger 2010-04-21: Implementation
--*/
#include"ast_pp.h"
#include"demodulator.h"
#include"for_each_expr.h"
#include"var_subst.h"
#include"uint_set.h"
demodulator::demodulator(ast_manager & m, basic_simplifier_plugin & p):
m_manager(m),
m_match_subst(m),
m_bsimp(p),
m_todo(m),
m_rewrite_todo(m),
m_rewrite_cache(m),
m_new_exprs(m) {
}
demodulator::~demodulator() {
reset_dealloc_values(m_fwd_idx);
reset_dealloc_values(m_back_idx);
for (demodulator2lhs_rhs::iterator it = m_demodulator2lhs_rhs.begin(); it != m_demodulator2lhs_rhs.end(); it++) {
m_manager.dec_ref(it->m_key);
m_manager.dec_ref(it->m_value.first);
m_manager.dec_ref(it->m_value.second);
}
}
bool demodulator::is_demodulator(expr * e, expr_ref & large, expr_ref & small) const {
if (e->get_kind() == AST_QUANTIFIER) {
quantifier * q = to_quantifier(e);
if (q->is_forall()) {
expr * qe = q->get_expr();
if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) {
app * eq = to_app(q->get_expr());
expr * lhs = eq->get_arg(0);
expr * rhs = eq->get_arg(1);
int subset = is_subset(lhs, rhs);
int smaller = is_smaller(lhs, rhs);
TRACE("demodulator", tout << "testing is_demodulator:\n"
<< mk_pp(lhs, m_manager) << "\n"
<< mk_pp(rhs, m_manager) << "\n"
<< "subset: " << subset << ", smaller: " << smaller << "\n";);
// We only track uninterpreted functions, everything else is likely too expensive.
if ((subset == +1 || subset == +2) && smaller == +1) {
if (is_uninterp(rhs)) {
large = rhs;
small = lhs;
return true;
}
#if 1
// lhs = (not rhs) --> (not lhs) = rhs
expr * not_rhs;
if (m_manager.is_not(rhs, not_rhs) && is_uninterp(not_rhs)) {
large = not_rhs;
small = m_manager.mk_not(lhs);
return true;
}
#endif
}
if ((subset == -1 || subset == +2) && smaller == -1) {
if (is_uninterp(lhs)) {
large = lhs;
small = rhs;
return true;
}
#if 1
// (not lhs) = rhs --> lhs = (not rhs)
expr * not_lhs;
if (m_manager.is_not(lhs, not_lhs) && is_uninterp(not_lhs)) {
large = not_lhs;
small = m_manager.mk_not(rhs);
return true;
}
#endif
}
} else if (m_manager.is_not(qe) && is_uninterp(to_app(qe)->get_arg(0))) {
// this is like (not (f ... )) --> (= (f ...) false)
large = to_app(qe)->get_arg(0);
small = m_manager.mk_false();
return true;
} else if (is_uninterp(qe)) {
// this is like (f ... ) --> (= (f ...) true)
large = to_app(qe);
small = m_manager.mk_true();
return true;
}
}
}
return false;
}
class var_set_proc {
uint_set & m_set;
public:
var_set_proc(uint_set &s):m_set(s) {}
void operator()(var * n) { m_set.insert(n->get_idx()); }
void operator()(quantifier * n) {}
void operator()(app * n) {}
};
int demodulator::is_subset(expr * e1, expr * e2) const {
uint_set ev1, ev2;
if (m_manager.is_value(e1))
return 1; // values are always a subset!
var_set_proc proc1(ev1);
for_each_expr(proc1, e1);
var_set_proc proc2(ev2);
for_each_expr(proc2, e2);
return (ev1==ev2 ) ? +2 : // We return +2 if the sets are equal.
(ev1.subset_of(ev2)) ? +1 :
(ev2.subset_of(ev1)) ? -1 :
0 ;
}
int demodulator::is_smaller(expr * e1, expr * e2) const {
unsigned sz1 = 0, sz2 = 0;
// values are always smaller!
if (m_manager.is_value(e1))
return +1;
else if (m_manager.is_value(e2))
return -1;
// interpreted stuff is always better than uninterpreted.
if (!is_uninterp(e1) && is_uninterp(e2))
return +1;
else if (is_uninterp(e1) && !is_uninterp(e2))
return -1;
// two uninterpreted functions are ordered first by the number of
// arguments, then by their id.
if (is_uninterp(e1) && is_uninterp(e2)) {
if (to_app(e1)->get_num_args() < to_app(e2)->get_num_args())
return +1;
else if (to_app(e1)->get_num_args() > to_app(e2)->get_num_args())
return -1;
else {
unsigned a = to_app(e1)->get_decl()->get_id();
unsigned b = to_app(e2)->get_decl()->get_id();
if (a < b)
return +1;
else if (a > b)
return -1;
}
}
switch (e1->get_kind()) {
case AST_VAR: sz1 = 1; break;
case AST_QUANTIFIER: sz1 = to_quantifier(e1)->get_depth(); break;
case AST_APP: sz1 = to_app(e1)->get_depth(); break;
default: UNREACHABLE();
}
switch (e2->get_kind()) {
case AST_VAR: sz2 = 1; break;
case AST_QUANTIFIER: sz2 = to_quantifier(e2)->get_depth(); break;
case AST_APP: sz2 = to_app(e2)->get_depth(); break;
default: UNREACHABLE();
}
return (sz1 == sz2) ? 0 :
(sz1 < sz2) ? +1 :
-1 ;
}
class max_var_id_proc {
unsigned m_max_var_id;
public:
max_var_id_proc(void):m_max_var_id(0) {}
void operator()(var * n) {
if(n->get_idx() > m_max_var_id)
m_max_var_id = n->get_idx();
}
void operator()(quantifier * n) {}
void operator()(app * n) {}
unsigned get_max(void) { return m_max_var_id; }
};
unsigned demodulator::max_var_id(expr * e)
{
max_var_id_proc proc;
for_each_expr(proc, e);
return proc.get_max();
}
void demodulator::insert_fwd_idx(expr * large, expr * small, quantifier * demodulator) {
SASSERT(large->get_kind() == AST_APP);
SASSERT(demodulator);
SASSERT(large && small);
TRACE("demodulator_fwd", tout << "INSERT: " << mk_pp(demodulator, m_manager) << std::endl; );
func_decl * fd = to_app(large)->get_decl();
fwd_idx_map::iterator it = m_fwd_idx.find_iterator(fd);
if (it == m_fwd_idx.end()) {
quantifier_set * qs = alloc(quantifier_set, 1);
m_fwd_idx.insert(fd, qs);
it = m_fwd_idx.find_iterator(fd);
}
SASSERT(it->m_value);
it->m_value->insert(demodulator);
m_manager.inc_ref(demodulator);
m_manager.inc_ref(large);
m_manager.inc_ref(small);
m_demodulator2lhs_rhs.insert(demodulator, expr_pair(large, small));
}
void demodulator::remove_fwd_idx(func_decl * f, quantifier * demodulator) {
TRACE("demodulator_fwd", tout << "REMOVE: " << std::hex << (size_t)demodulator << std::endl; );
fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f);
if (it != m_fwd_idx.end()) {
demodulator2lhs_rhs::iterator fit = m_demodulator2lhs_rhs.find_iterator(demodulator);
m_manager.dec_ref(fit->m_value.first);
m_manager.dec_ref(fit->m_value.second);
m_manager.dec_ref(demodulator);
m_demodulator2lhs_rhs.erase(demodulator);
it->m_value->erase(demodulator);
} else {
SASSERT(m_demodulator2lhs_rhs.contains(demodulator));
}
}
bool demodulator::check_fwd_idx_consistency(void) {
for (fwd_idx_map::iterator it = m_fwd_idx.begin(); it != m_fwd_idx.end() ; it++ ) {
quantifier_set * set = it->m_value;
SASSERT(set);
for (quantifier_set::iterator sit = set->begin(); sit != set->end(); sit++) {
if (!m_demodulator2lhs_rhs.contains(*sit)) return false;
}
}
return true;
}
void demodulator::show_fwd_idx(std::ostream & out) {
for (fwd_idx_map::iterator it = m_fwd_idx.begin(); it != m_fwd_idx.end() ; it++ ) {
quantifier_set * set = it->m_value;
SASSERT(!set);
out << it->m_key->get_name() << ": " << std::endl;
for (quantifier_set::iterator sit = set->begin(); sit != set->end(); sit++) {
out << std::hex << (size_t)*sit << std::endl;
}
}
out << "D2LR: " << std::endl;
for (demodulator2lhs_rhs::iterator it = m_demodulator2lhs_rhs.begin(); it != m_demodulator2lhs_rhs.end() ; it++) {
out << (size_t) it->m_key << std::endl;
}
}
bool demodulator::rewrite1(func_decl * f, ptr_vector<expr> & m_new_args, expr_ref & np) {
fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f);
if (it != m_fwd_idx.end()) {
TRACE("demodulator_bug", tout << "trying to rewrite: " << f->get_name() << " args:\n";
for (unsigned i = 0; i < m_new_args.size(); i++) { tout << mk_pp(m_new_args[i], m_manager) << "\n"; });
quantifier_set::iterator dit = it->m_value->begin();
quantifier_set::iterator dend = it->m_value->end();
for ( ; dit != dend ; dit++ ) {
quantifier * d = *dit;
SASSERT(m_demodulator2lhs_rhs.contains(d));
expr_pair l_s;
m_demodulator2lhs_rhs.find(d, l_s);
app * large = to_app(l_s.first);
if (large->get_num_args() != m_new_args.size())
continue;
TRACE("demodulator_bug", tout << "Matching with demodulator: " << mk_pp(d, m_manager) << std::endl; );
SASSERT(large->get_decl() == f);
if (m_match_subst(large, l_s.second, m_new_args.c_ptr(), np)) {
TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(l_s.second, m_manager) << "\n===>\n" << mk_pp(np, m_manager) << "\n";);
return true;
}
}
}
return false;
}
bool demodulator::rewrite_visit_children(app * a) {
bool res=true;
unsigned j = a->get_num_args();
while (j > 0) {
expr * e = a->get_arg(--j);
if (!m_rewrite_cache.contains(e) || !m_rewrite_cache.get(e).second) {
m_rewrite_todo.push_back(e);
res = false;
}
}
return res;
}
void demodulator::rewrite_cache(expr * e, expr * new_e, bool done) {
m_rewrite_cache.insert(e, expr_bool_pair(new_e, done));
}
expr * demodulator::rewrite(expr * n) {
if (m_fwd_idx.empty())
return n;
TRACE("demodulator", tout << "rewrite: " << mk_pp(n, m_manager) << std::endl; );
app * a;
SASSERT(m_rewrite_todo.empty());
m_rewrite_cache.reset();
m_rewrite_todo.push_back(n);
while (!m_rewrite_todo.empty()) {
TRACE("demodulator_stack", tout << "STACK: " << std::endl;
for ( unsigned i = 0; i<m_rewrite_todo.size(); i++)
tout << std::dec << i << ": " << std::hex << (size_t)m_rewrite_todo[i] << std::endl;
);
expr * e = m_rewrite_todo.back();
expr * actual = e;
if (m_rewrite_cache.contains(e)) {
const expr_bool_pair &ebp = m_rewrite_cache.get(e);
if (ebp.second) {
m_rewrite_todo.pop_back();
continue;
}
else {
actual = ebp.first;
}
}
switch (actual->get_kind()) {
case AST_VAR:
rewrite_cache(e, actual, true);
m_rewrite_todo.pop_back();
break;
case AST_APP:
a = to_app(actual);
if (rewrite_visit_children(a)) {
func_decl * f = a->get_decl();
m_new_args.reset();
unsigned num_args = a->get_num_args();
bool all_untouched=true;
for (unsigned i = 0 ; i < num_args ; i++ ) {
expr * o_child = a->get_arg(i);
expr * n_child;
SASSERT(m_rewrite_cache.contains(o_child) && m_rewrite_cache.get(o_child).second);
expr_bool_pair const & ebp = m_rewrite_cache.get(o_child);
n_child = ebp.first;
if (n_child != o_child)
all_untouched = false;
m_new_args.push_back(n_child);
}
expr_ref np(m_manager);
if (rewrite1(f, m_new_args, np)) {
rewrite_cache(e, np, false);
// No pop.
} else {
if(all_untouched) {
rewrite_cache(e, actual, true);
}
else {
expr_ref na(m_manager);
if (f->get_family_id() != m_manager.get_basic_family_id())
na = m_manager.mk_app(f, m_new_args.size(), m_new_args.c_ptr());
else
m_bsimp.reduce(f, m_new_args.size(), m_new_args.c_ptr(), na);
TRACE("demodulator_bug", tout << "e:\n" << mk_pp(e, m_manager) << "\nnew_args: \n";
for (unsigned i = 0; i < m_new_args.size(); i++) { tout << mk_pp(m_new_args[i], m_manager) << "\n"; }
tout << "=====>\n";
tout << "na:\n " << mk_pp(na, m_manager) << "\n";);
rewrite_cache(e, na, true);
}
m_rewrite_todo.pop_back();
}
}
break;
case AST_QUANTIFIER: {
expr * body = to_quantifier(actual)->get_expr();
if (m_rewrite_cache.contains(body)) {
const expr_bool_pair ebp = m_rewrite_cache.get(body);
SASSERT(ebp.second);
expr * new_body = ebp.first;
quantifier_ref q(m_manager);
q = m_manager.update_quantifier(to_quantifier(actual), new_body);
m_new_exprs.push_back(q);
expr_ref new_q(m_manager);
elim_unused_vars(m_manager, q, new_q);
m_new_exprs.push_back(new_q);
rewrite_cache(e, new_q, true);
m_rewrite_todo.pop_back();
} else {
m_rewrite_todo.push_back(body);
}
break;
}
default:
UNREACHABLE();
}
}
SASSERT(m_rewrite_cache.contains(n));
const expr_bool_pair & ebp = m_rewrite_cache.get(n);
SASSERT(ebp.second);
expr * r = ebp.first;
TRACE("demodulator", tout << "rewrite result: " << mk_pp(r, m_manager) << std::endl; );
return r;
}
class demodulator::add_back_idx_proc {
ast_manager & m_manager;
back_idx_map & m_back_idx;
expr * m_expr;
public:
add_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {}
void operator()(var * n) {}
void operator()(quantifier * n) {}
void operator()(app * n) {
// We track only uninterpreted and constant functions.
if (n->get_num_args()==0) return;
SASSERT(m_expr && m_expr != (expr*) 0x00000003);
func_decl * d=n->get_decl();
if (d->get_family_id() == null_family_id) {
back_idx_map::iterator it = m_back_idx.find_iterator(d);
if (it != m_back_idx.end()) {
SASSERT(it->m_value);
it->m_value->insert(m_expr);
} else {
expr_set * e = alloc(expr_set);
e->insert(m_expr);
m_back_idx.insert(d, e);
}
}
}
};
class demodulator::remove_back_idx_proc {
ast_manager & m_manager;
back_idx_map & m_back_idx;
expr * m_expr;
public:
remove_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {}
void operator()(var * n) {}
void operator()(quantifier * n) {}
void operator()(app * n) {
// We track only uninterpreted and constant functions.
if (n->get_num_args()==0) return;
func_decl * d=n->get_decl();
if (d->get_family_id() == null_family_id) {
back_idx_map::iterator it = m_back_idx.find_iterator(d);
if (it != m_back_idx.end()) {
SASSERT(it->m_value);
it->m_value->remove(m_expr);
}
}
}
};
void demodulator::reschedule_processed(func_decl * f) {
//use m_back_idx to find all formulas p in m_processed that contains f {
back_idx_map::iterator it = m_back_idx.find_iterator(f);
if (it != m_back_idx.end()) {
SASSERT(it->m_value);
expr_set temp;
expr_set::iterator sit = it->m_value->begin();
expr_set::iterator send = it->m_value->end();
for ( ; sit != send ; sit++ ) {
expr * p = *sit;
if (m_processed.contains(p))
temp.insert(p);
}
sit = temp.begin();
send = temp.end();
for ( ; sit != send; sit++) {
expr * p = *sit;
// remove p from m_processed and m_back_idx
m_processed.remove(p);
remove_back_idx_proc proc(m_manager, m_back_idx, p); // this could change it->m_value, thus we need the `temp' set.
for_each_expr(proc, p);
// insert p into m_todo
m_todo.push_back(p);
}
}
}
bool demodulator::can_rewrite(expr * n, expr * lhs) {
// this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'.
// we cannot use the trick used for m_processed, since the main loop would not terminate.
ptr_vector<expr> stack;
expr * curr;
expr_mark visited;
stack.push_back(n);
while (!stack.empty()) {
curr = stack.back();
if (visited.is_marked(curr)) {
stack.pop_back();
continue;
}
switch(curr->get_kind()) {
case AST_VAR:
visited.mark(curr, true);
stack.pop_back();
break;
case AST_APP:
if (for_each_expr_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) {
if (m_match_subst(lhs, curr))
return true;
visited.mark(curr, true);
stack.pop_back();
}
break;
case AST_QUANTIFIER:
if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_patterns(),
to_quantifier(curr)->get_patterns())) {
break;
}
if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_no_patterns(),
to_quantifier(curr)->get_no_patterns())) {
break;
}
if (!visited.is_marked(to_quantifier(curr)->get_expr())) {
stack.push_back(to_quantifier(curr)->get_expr());
break;
}
stack.pop_back();
break;
default:
UNREACHABLE();
}
}
return false;
}
void demodulator::reschedule_demodulators(func_decl * f, expr * lhs) {
// use m_back_idx to find all demodulators d in m_fwd_idx that contains f {
//ptr_vector<expr> to_remove;
back_idx_map::iterator it = m_back_idx.find_iterator(f);
if (it != m_back_idx.end()) {
SASSERT(it->m_value);
expr_set all_occurrences;
expr_ref l(m_manager);
expr_set::iterator esit = it->m_value->begin();
expr_set::iterator esend = it->m_value->end();
for ( ; esit != esend ; esit++)
all_occurrences.insert(*esit);
// Run over all f-demodulators
esit = all_occurrences.begin();
esend = all_occurrences.end();
for ( ; esit != esend ; esit++ ) {
expr * occ = *esit;
if (!is_quantifier(occ))
continue;
// Use the fwd idx to find out whether this is a demodulator.
demodulator2lhs_rhs::iterator d2lr_it = m_demodulator2lhs_rhs.find_iterator(to_quantifier(occ));
if (d2lr_it != m_demodulator2lhs_rhs.end()) {
l = d2lr_it->m_value.first;
quantifier_ref d(m_manager);
func_decl_ref df(m_manager);
d = to_quantifier(occ);
df = to_app(l)->get_decl();
// Now we know there is an occurrence of f in d
// if n' can rewrite d {
if (can_rewrite(d, lhs)) {
TRACE("demodulator", tout << "Rescheduling: " << std::endl << mk_pp(d, m_manager) << std::endl; );
// remove d from m_fwd_idx
remove_fwd_idx(df, d);
// remove d from m_back_idx
// just remember it here, because otherwise it and/or esit might become invalid?
// to_remove.insert(d);
remove_back_idx_proc proc(m_manager, m_back_idx, d);
for_each_expr(proc, d);
// insert d into m_todo
m_todo.push_back(d);
}
}
}
}
//for (ptr_vector<expr>::iterator it = to_remove.begin(); it != to_remove.end(); it++) {
// expr * d = *it;
// remove_back_idx_proc proc(m_manager, m_back_idx, d);
// for_each_expr(proc, d);
//}
}
void demodulator::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
if (m_manager.proofs_enabled()) {
// Let us not waste time with proof production
warning_msg("PRE_DEMODULATOR=true is not supported when proofs are enabled.");
new_exprs.append(n, exprs);
new_prs.append(n, prs);
return;
}
TRACE("demodulator", tout << "before demodulator:\n";
for ( unsigned i = 0 ; i < n ; i++ )
tout << mk_pp(exprs[i], m_manager) << std::endl; );
// Initially, m_todo contains all formulas. That is, it contains the argument exprs. m_fwd_idx, m_processed, m_back_idx are empty.
unsigned max_vid = 0;
for ( unsigned i = 0 ; i < n ; i++ ) {
m_todo.push_back(exprs[i]);
max_vid = std::max(max_vid, max_var_id(exprs[i]));
}
m_match_subst.reserve(max_vid);
while (!m_todo.empty()) {
// let n be the next formula in m_todo.
expr_ref cur(m_manager);
cur = m_todo.back();
m_todo.pop_back();
// rewrite cur using m_fwd_idx, and let n' be the result.
expr * np = rewrite(cur);
// at this point, it should be the case that there is no demodulator in m_fwd_idx that can rewrite n'.
SASSERT(rewrite(np)==np);
// if (n' is not a demodulator) {
expr_ref large(m_manager), small(m_manager);
if (!is_demodulator(np, large, small)) {
// insert n' into m_processed
m_processed.insert(np);
// update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx)
add_back_idx_proc proc(m_manager, m_back_idx, np);
for_each_expr(proc, np);
} else {
// np is a demodulator that allows us to replace 'large' with 'small'.
TRACE("demodulator", tout << "Found demodulator: " << std::endl;
tout << mk_pp(large.get(), m_manager) << std::endl << " ---> " <<
std::endl << mk_pp(small.get(), m_manager) << std::endl; );
TRACE("demodulator_s", tout << "Found demodulator: " << std::endl;
tout << to_app(large)->get_decl()->get_name() <<
"[" << to_app(large)->get_depth() << "]" << " ---> ";
if (is_app(small))
tout << to_app(small)->get_decl()->get_name() <<
"[" << to_app(small)->get_depth() << "]" << std::endl;
else
tout << mk_pp(small.get(), m_manager) << std::endl; );
// let f be the top symbol of n'
SASSERT(is_app(large));
func_decl * f = to_app(large)->get_decl();
reschedule_processed(f);
reschedule_demodulators(f, large);
// insert n' into m_fwd_idx
insert_fwd_idx(large, small, to_quantifier(np));
// update m_back_idx
add_back_idx_proc proc(m_manager, m_back_idx, np);
for_each_expr(proc, np);
}
}
// the result is the contents of m_processed + all demodulators in m_fwd_idx.
obj_hashtable<expr>::iterator pit = m_processed.begin();
obj_hashtable<expr>::iterator pend = m_processed.end();
for ( ; pit != pend ; pit++ ) {
new_exprs.push_back(*pit);
TRACE("demodulator", tout << mk_pp(*pit, m_manager) << std::endl; );
}
fwd_idx_map::iterator fit = m_fwd_idx.begin();
fwd_idx_map::iterator fend = m_fwd_idx.end();
for ( ; fit != fend ; fit++ ) {
if (fit->m_value) {
quantifier_set::iterator dit = fit->m_value->begin();
quantifier_set::iterator dend = fit->m_value->end();
for ( ; dit != dend ; dit++ ) {
expr * e = *dit;
new_exprs.push_back(e);
TRACE("demodulator", tout << mk_pp(*dit, m_manager) << std::endl; );
}
}
}
TRACE("demodulator", tout << "after demodulator:\n";
for ( unsigned i = 0 ; i < new_exprs.size() ; i++ )
tout << mk_pp(new_exprs[i].get(), m_manager) << std::endl; );
}
demodulator::match_subst::match_subst(ast_manager & m):
m_manager(m),
m_subst(m) {
}
/**
\brief Auxiliary functor used to implement optimization in match_args. See comment there.
*/
struct match_args_aux_proc {
substitution & m_subst;
struct no_match {};
match_args_aux_proc(substitution & s):m_subst(s) {}
void operator()(var * n) {
expr_offset r;
if (m_subst.find(n, 0, r)) {
if (r.get_expr() != n) {
SASSERT(r.get_offset() == 1);
throw no_match();
}
else {
m_subst.insert(n, 0, expr_offset(n, 1));
}
}
}
void operator()(quantifier * n) { throw no_match(); }
void operator()(app * n) {}
};
bool demodulator::match_subst::match_args(app * lhs, expr * const * args) {
m_cache.reset();
m_todo.reset();
// fill todo-list, and perform quick success/failure tests
m_all_args_eq = true;
unsigned num_args = lhs->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * t_arg = lhs->get_arg(i);
expr * i_arg = args[i];
if (t_arg != i_arg)
m_all_args_eq = false;
if (is_app(t_arg) && is_app(i_arg) && to_app(t_arg)->get_decl() != to_app(i_arg)->get_decl()) {
// quick failure...
return false;
}
m_todo.push_back(expr_pair(t_arg, i_arg));
}
if (m_all_args_eq) {
// quick success worked...
return true;
}
m_subst.reset();
while (!m_todo.empty()) {
expr_pair const & p = m_todo.back();
if (is_var(p.first)) {
expr_offset r;
if (m_subst.find(to_var(p.first), 0, r)) {
if (r.get_expr() != p.second)
return false;
}
else {
m_subst.insert(to_var(p.first), 0, expr_offset(p.second, 1));
}
m_todo.pop_back();
continue;
}
if (is_var(p.second))
return false;
// we may have nested quantifiers.
if (is_quantifier(p.first) || is_quantifier(p.second))
return false;
SASSERT(is_app(p.first) && is_app(p.second));
if (to_app(p.first)->is_ground() && !to_app(p.second)->is_ground())
return false;
if (p.first == p.second && to_app(p.first)->is_ground()) {
SASSERT(to_app(p.second)->is_ground());
m_todo.pop_back();
continue;
}
if (m_cache.contains(p)) {
m_todo.pop_back();
continue;
}
if (p.first == p.second) {
// p.first and p.second is not ground...
// Traverse p.first and check whether every variable X:0 in p.first
// 1) is unbounded (then we bind X:0 -> X:1)
// 2) or, is already bounded to X:1
// If that is, the case, we execute:
// m_todo.pop_back();
// m_cache.insert(p);
// continue;
// Otherwise
// return false;
match_args_aux_proc proc(m_subst);
try {
for_each_expr(proc, p.first);
// succeeded
m_todo.pop_back();
m_cache.insert(p);
continue;
}
catch (match_args_aux_proc::no_match) {
return false;
}
}
app * n1 = to_app(p.first);
app * n2 = to_app(p.second);
if (n1->get_decl() != n2->get_decl())
return false;
unsigned num_args1 = n1->get_num_args();
if (num_args1 != n2->get_num_args())
return false;
m_todo.pop_back();
if (num_args1 == 0)
continue;
m_cache.insert(p);
unsigned j = num_args1;
while (j > 0) {
--j;
m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j)));
}
}
return true;
}
bool demodulator::match_subst::operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs) {
if (match_args(lhs, args)) {
if (m_all_args_eq) {
// quick success...
new_rhs = rhs;
return true;
}
unsigned deltas[2] = { 0, 0 };
m_subst.apply(2, deltas, expr_offset(rhs, 0), new_rhs);
return true;
}
return false;
}
bool demodulator::match_subst::operator()(expr * t, expr * i) {
m_cache.reset();
m_todo.reset();
if (is_var(t))
return true;
if (is_app(t) && is_app(i) && to_app(t)->get_decl() == to_app(i)->get_decl() && to_app(t)->get_num_args() == to_app(i)->get_num_args()) {
return match_args(to_app(t), to_app(i)->get_args());
}
return false;
}

265
src/smt/demodulator.h Normal file
View file

@ -0,0 +1,265 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
demodulator.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-12.
Revision History:
--*/
#ifndef _DEMODULATOR_H_
#define _DEMODULATOR_H_
#include"ast.h"
#include"substitution.h"
#include"obj_hashtable.h"
#include"obj_pair_hashtable.h"
#include"array_map.h"
#include"basic_simplifier_plugin.h"
/**
\brief Apply demodulators as a preprocessing technique.
In first-order theorem proving (FOTP), a demodulator is a universally quantified formula of the form:
Forall X1, ..., Xn. L[X1, ..., Xn] = R[X1, ..., Xn]
Where L[X1, ..., Xn] contains all variables in R[X1, ..., Xn], and
L[X1, ..., Xn] is "bigger" than R[X1, ...,Xn].
The idea is to replace something big L[X1, ..., Xn] with something smaller R[X1, ..., Xn].
In FOTP, they use term orderings to decide what does it mean to be smaller.
We are using demodulators in a different context (pre-processing).
So, I suggest we have a virtual method is_smaller for comparing expressions.
The default implementation just compares the size of the expressions.
Similarly, in our context, formulas using iff are also demodulators.
Forall X1, ..., Xn. L[X1, ..., Xn] iff R[X1, ..., Xn]
After selecting the demodulators, we traverse the rest of the formula looking for instances of L[X1, ..., Xn].
Whenever we find an instance, we replace it with the associated instance of R[X1, ..., Xn].
For example, suppose we have
Forall x, y. f(x+y, y) = y
and
f(g(b) + h(c), h(c)) <= 0
The term f(g(b) + h(c), h(c)) is an instance of f(x+y, y) if we replace x <- g(b) and y <- h(c).
So, we can replace it with "y" which is bound to h(c) in this example. So, the result of the transformation is:
Forall x, y. f(x+y, y) = y
and
h(c) <= 0
In the first implementation, let us ignore theory matching. That is,
for us the term f(a+1) is not an instance of f(1+x), because the
matcher doesn't know + is commutative. Observe the demodulator is
*not* copied to the macro manager in this case.
Another complication is when we are looking for instances inside other universally quantified formulas.
The problem is that both formulas (demodular) and target are reusing variables names (ids).
To avoid renaming, we use offsets. The idea is to represent renames implicitly. In this case,
each offset is a different "variable bank". A pair (expr, offset) is essentially an expression
where every variable in expr is assumed to be from the "bank" offset.
The class substitution (in substitution.h) manages offsets for us.
The class matcher (in matcher.h) can be use to test whether an expression is an instance of another one.
Finally, there is the problem when we have N demodulators (where N is big), and a big formula, and we want
to traverse the formula only once looking for opportunities for applying these N demodulators.
We want to efficiently find the applicable demodulars.
We can start with a simple optimization that given a func_decl it returns the set of demodulators that start with this declaration.
For example, suppose we have the demodulators.
forall x, f(x, g(0)) = 10
forall x, f(g(h(x)), g(1)) = 20
Then, in our "index" f would map to these two demodulators.
As a final optimization, we should adapt the code to use substitution-trees.
The current implementation in Z3 is not efficient, and I think it is buggy.
So, it would be great to replace it with a new one.
The code in spc_rewriter.* does something like that. We cannot reuse this code directly since it is meant
for the superposion engine in Z3, but we can adapt it for our needs in the preprocessor.
*/
class demodulator {
class rewrite_proc;
class add_back_idx_proc;
class remove_back_idx_proc;
class can_rewrite_proc;
typedef std::pair<expr *, bool> expr_bool_pair;
class plugin {
ast_manager& m_manager;
public:
plugin(ast_manager& m): m_manager(m) { }
void ins_eh(expr* k, expr_bool_pair v) { m_manager.inc_ref(k); m_manager.inc_ref(v.first); }
void del_eh(expr* k, expr_bool_pair v) { m_manager.dec_ref(k); m_manager.dec_ref(v.first); }
static unsigned to_int(expr const * k) { return k->get_id(); }
};
typedef array_map<expr*, expr_bool_pair, plugin> expr_map;
typedef std::pair<expr *, expr *> expr_pair;
typedef obj_hashtable<expr> expr_set;
typedef obj_map<func_decl, expr_set *> back_idx_map;
typedef obj_hashtable<quantifier> quantifier_set;
typedef obj_map<func_decl, quantifier_set *> fwd_idx_map;
typedef obj_map<quantifier, expr_pair> demodulator2lhs_rhs;
typedef expr_map rewrite_cache_map;
/**
\brief Custom matcher & substitution application
*/
class match_subst {
typedef std::pair<expr *, expr *> expr_pair;
typedef obj_pair_hashtable<expr, expr> cache;
void reset();
ast_manager & m_manager;
substitution m_subst;
cache m_cache;
svector<expr_pair> m_todo;
bool m_all_args_eq;
bool match_args(app * t, expr * const * args);
public:
match_subst(ast_manager & m);
void reserve(unsigned max_vid) { m_subst.reserve(2, max_vid+1); }
/**
\brief Let f be the top symbol of lhs. If (f args) is an
instance of lhs, that is, there is a substitution s
s.t. s[lhs] = (f args), then return true and store s[rhs]
into new_rhs. Where s[t] represents the application of the
substitution s into t.
Assumptions, the variables in lhs and (f args) are assumed to be distinct.
So, (f x y) matches (f y x).
Moreover, the result should be in terms of the variables in (f args).
*/
bool operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs);
/**
\brief Return true if \c i is an instance of \c t.
*/
bool operator()(expr * t, expr * i);
};
ast_manager & m_manager;
match_subst m_match_subst;
basic_simplifier_plugin & m_bsimp;
fwd_idx_map m_fwd_idx;
back_idx_map m_back_idx;
demodulator2lhs_rhs m_demodulator2lhs_rhs;
expr_ref_buffer m_todo;
obj_hashtable<expr> m_processed;
ptr_vector<expr> m_new_args;
expr_ref_buffer m_rewrite_todo;
rewrite_cache_map m_rewrite_cache;
expr_ref_buffer m_new_exprs;
void insert_fwd_idx(expr * large, expr * small, quantifier * demodulator);
void remove_fwd_idx(func_decl * f, quantifier * demodulator);
bool check_fwd_idx_consistency(void);
void show_fwd_idx(std::ostream & out);
bool is_demodulator(expr * e, expr_ref & large, expr_ref & small) const;
bool can_rewrite(expr * n, expr * lhs);
expr * rewrite(expr * n);
bool rewrite1(func_decl * f, ptr_vector<expr> & m_new_args, expr_ref & np);
bool rewrite_visit_children(app * a);
void rewrite_cache(expr * e, expr * new_e, bool done);
void reschedule_processed(func_decl * f);
void reschedule_demodulators(func_decl * f, expr * np);
unsigned max_var_id(expr * e);
protected:
// is_smaller returns -1 for e1<e2, 0 for e1==e2 and +1 for e1>e2.
virtual int is_smaller(expr * e1, expr * e2) const;
// is_subset returns -1 for e1 subset e2, +1 for e2 subset e1, 0 else.
virtual int is_subset(expr * e1, expr * e2) const;
public:
demodulator(ast_manager & m, basic_simplifier_plugin & p);
virtual ~demodulator();
void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
/**
Given a demodulator (aka rewrite rule) of the form
Forall X. L[X] = R[X]
We say the top symbol is the first symbol in L.
For example: f is the top symbol in
Forall x, f(h(x)) = x + 1
The rewrite engine main loop is based on the DISCOUNT loop used in first-order theorem provers.
Main structures:
- m_todo: The todo-stack of formulas to be processed.
- m_fwd_idx: "Forward index" for finding efficiently which demodulators can be used to rewrite an expression.
We organize this set as a mapping from func_decl to a set of demodulators which start with the same top symbol.
- m_processed: The set of already processed formulas. We can represent it using a hashtable.
- m_back_idx: "Backward index" we use it to find efficiently which already processed expressions and demodulators may be rewritten
by a new demodulator. Again, we use a very simple index, for each uninterpreted function symbol (ignore constants)
f, store the expressions in m_processed and the demodulators that contain f.
If you prefer, you may use two m_back_idxs (one for formulas in m_processed and another for demodulators in m_fwd_idx).
Initially, m_todo contains all formulas. That is, it contains the argument exprs. m_fwd_idx, m_processed, m_back_idx are empty.
while (m_todo is not empty) {
let n be the next formula in m_todo.
rewrite n using m_fwd_idx, and let n' be the result.
// at this point, it should be the case that there is no demodulator in m_fwd_idx that can rewrite n'.
if (n' is not a demodulator) {
insert n' into m_processed
update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx)
}
else {
let f be the top symbol of n'
use m_back_idx to find all formulas p in m_processed that contains f {
remove p from m_processed
remove p from m_back_idx
insert p into m_todo
}
use m_back_idx to find all demodulators d in m_fwd_idx that contains f {
if n' can rewrite d {
// this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'.
// we cannot use the trick used for m_processed, since the main loop would not terminate.
remove d from m_fwd_idx
remode d from m_back_idx
insert p into m_todo
}
}
insert n' into m_fwd_idx
update m_back_idx
}
}
the result is the contents of m_processed + all demodulators in m_fwd_idx.
Note: to remove p from m_back_idx, we need to traverse p, and for every function declartion f in p, we should remove the entry f->p from m_back_idx.
Note: we can implement m_back_idx for formulas as:
typedef obj_hashtable<expr> expr_set;
obj_map<func_decl, expr_set *> m_back_idx;
we should represent the sets as hashtables because we want to be able to efficiently remove elements from these sets.
ptr_vector<expr_set> m_expr_set_to_delete; // same trick we used in macro_manager.
we can use a similar structure for m_back_idx and m_fwd_idx for demodulators.
Note: m_processed should be obj_hashtable<expr> since we want to remove elements from there efficiently.
*/
};
#endif /* _DEMODULATOR_H_ */

524
src/smt/dyn_ack.cpp Normal file
View file

@ -0,0 +1,524 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dyn_ack.cpp
Abstract:
Dynamic Ackermann's reduction
Author:
Leonardo de Moura (leonardo) 2007-04-24.
Revision History:
--*/
#include"smt_context.h"
#include"dyn_ack.h"
#include"ast_pp.h"
namespace smt {
/**
\brief Justification for dynamic ackermann clause
*/
class dyn_ack_justification : public justification {
app * m_app1;
app * m_app2;
public:
dyn_ack_justification(app * n1, app * n2):
justification(false), // dyn_ack_justifications are not stored in regions.
m_app1(n1),
m_app2(n2) {
SASSERT(m_app1->get_num_args() == m_app2->get_num_args());
SASSERT(m_app1->get_decl() == m_app2->get_decl());
SASSERT(m_app1->get_num_args() > 0);
SASSERT(m_app1->get_id() < m_app2->get_id());
}
virtual char const * get_name() const { return "dyn-ack"; }
virtual void get_antecedents(conflict_resolution & cr) {
}
virtual void display_debug_info(conflict_resolution & cr, std::ostream & out) {
ast_manager & m = cr.get_manager();
out << "m_app1:\n" << mk_pp(m_app1, m) << "\n";
out << "m_app2:\n" << mk_pp(m_app2, m) << "\n";
}
/**
\brief Make a hypothesis (= lhs rhs) for the given equality.
The arguments of the given equality eq may have been swapped. That is, \c eq is of the form (= rhs lhs).
In this case, we also apply a symmetry rule.
\remark if negate == true, then the hypothesis is actually (not (= lhs rhs))
*/
proof * mk_hypothesis(ast_manager & m, app * eq, bool negate, expr * lhs, expr * rhs) {
SASSERT(m.is_eq(eq));
SASSERT((eq->get_arg(0) == lhs && eq->get_arg(1) == rhs) ||
(eq->get_arg(0) == rhs && eq->get_arg(1) == lhs));
app * h = negate ? m.mk_not(eq) : eq;
if (eq->get_arg(0) == lhs && eq->get_arg(1) == rhs) {
return m.mk_hypothesis(h);
}
else {
return m.mk_symmetry(m.mk_hypothesis(h));
}
}
virtual proof * mk_proof(conflict_resolution & cr) {
ast_manager & m = cr.get_manager();
context & ctx = cr.get_context();
unsigned num_args = m_app1->get_num_args();
ptr_buffer<proof> prs;
ptr_buffer<expr> lits;
for (unsigned i = 0; i < num_args; i++) {
expr * arg1 = m_app1->get_arg(i);
expr * arg2 = m_app2->get_arg(i);
if (arg1 != arg2) {
app * eq = ctx.mk_eq_atom(arg1, arg2);
app * neq = m.mk_not(eq);
if (std::find(lits.begin(), lits.end(), neq) == lits.end()) {
lits.push_back(neq);
prs.push_back(mk_hypothesis(m, eq, false, arg1, arg2));
}
}
}
proof * antecedents[2];
antecedents[0] = m.mk_congruence(m_app1, m_app2, prs.size(), prs.c_ptr());
app * eq = ctx.mk_eq_atom(m_app1, m_app2);
antecedents[1] = mk_hypothesis(m, eq, true, m_app1, m_app2);
proof * false_pr = m.mk_unit_resolution(2, antecedents);
lits.push_back(eq);
SASSERT(lits.size() >= 2);
app * lemma = m.mk_or(lits.size(), lits.c_ptr());
TRACE("dyn_ack", tout << mk_pp(lemma, m) << "\n";);
TRACE("dyn_ack", tout << mk_pp(false_pr, m) << "\n";);
return m.mk_lemma(false_pr, lemma);
}
};
dyn_ack_manager::dyn_ack_manager(context & ctx, dyn_ack_params & p):
m_context(ctx),
m_manager(ctx.get_manager()),
m_params(p) {
}
dyn_ack_manager::~dyn_ack_manager() {
reset_app_pairs();
reset_app_triples();
}
void dyn_ack_manager::reset_app_pairs() {
svector<app_pair>::iterator it = m_app_pairs.begin();
svector<app_pair>::iterator end = m_app_pairs.end();
for (; it != end; ++it) {
app_pair & p = *it;
m_manager.dec_ref(p.first);
m_manager.dec_ref(p.second);
}
m_app_pairs.reset();
}
void dyn_ack_manager::init_search_eh() {
m_app_pair2num_occs.reset();
reset_app_pairs();
m_to_instantiate.reset();
m_qhead = 0;
m_num_instances = 0;
m_num_propagations_since_last_gc = 0;
m_triple.m_app2num_occs.reset();
reset_app_triples();
m_triple.m_to_instantiate.reset();
m_triple.m_qhead = 0;
}
void dyn_ack_manager::cg_eh(app * n1, app * n2) {
SASSERT(n1->get_decl() == n2->get_decl());
SASSERT(n1->get_num_args() == n2->get_num_args());
SASSERT(n1 != n2);
if (m_manager.is_eq(n1))
return;
if (n1->get_id() > n2->get_id())
std::swap(n1,n2);
app_pair p(n1, n2);
if (m_instantiated.contains(p))
return;
unsigned num_occs = 0;
if (m_app_pair2num_occs.find(n1, n2, num_occs)) {
TRACE("dyn_ack", tout << "used_cg_eh:\n" << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\nnum_occs: " << num_occs << "\n";);
num_occs++;
}
else {
num_occs = 1;
m_manager.inc_ref(n1);
m_manager.inc_ref(n2);
m_app_pairs.push_back(p);
}
SASSERT(num_occs > 0);
m_app_pair2num_occs.insert(n1, n2, num_occs);
#ifdef Z3DEBUG
unsigned num_occs2 = 0;
SASSERT(m_app_pair2num_occs.find(n1, n2, num_occs2) && num_occs == num_occs2);
#endif
if (num_occs == m_params.m_dack_threshold) {
TRACE("dyn_ack", tout << "found candidate:\n" << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\nnum_occs: " << num_occs << "\n";);
m_to_instantiate.push_back(p);
}
}
void dyn_ack_manager::eq_eh(app * n1, app * n2, app* r) {
if (n1 == n2 || r == n1 || r == n2 || m_manager.is_bool(n1)) {
return;
}
if (n1->get_id() > n2->get_id())
std::swap(n1,n2);
TRACE("dyn_ack",
tout << mk_pp(n1, m_manager) << " = " << mk_pp(n2, m_manager)
<< " = " << mk_pp(r, m_manager) << "\n";);
app_triple tr(n1, n2, r);
if (m_triple.m_instantiated.contains(tr))
return;
unsigned num_occs = 0;
if (m_triple.m_app2num_occs.find(n1, n2, r, num_occs)) {
TRACE("dyn_ack", tout << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager)
<< mk_pp(r, m_manager) << "\n" << "\nnum_occs: " << num_occs << "\n";);
num_occs++;
}
else {
num_occs = 1;
m_manager.inc_ref(n1);
m_manager.inc_ref(n2);
m_manager.inc_ref(r);
m_triple.m_apps.push_back(tr);
}
SASSERT(num_occs > 0);
m_triple.m_app2num_occs.insert(n1, n2, r, num_occs);
#ifdef Z3DEBUG
unsigned num_occs2 = 0;
SASSERT(m_triple.m_app2num_occs.find(n1, n2, r, num_occs2) && num_occs == num_occs2);
#endif
if (num_occs == m_params.m_dack_threshold) {
TRACE("dyn_ack", tout << "found candidate:\n" << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager)
<< "\n" << mk_pp(r, m_manager)
<< "\nnum_occs: " << num_occs << "\n";);
m_triple.m_to_instantiate.push_back(tr);
}
}
struct app_pair_lt {
typedef std::pair<app *, app *> app_pair;
typedef obj_pair_map<app, app, unsigned> app_pair2num_occs;
app_pair2num_occs & m_app_pair2num_occs;
app_pair_lt(app_pair2num_occs & m):
m_app_pair2num_occs(m) {
}
bool operator()(app_pair const & p1, app_pair const & p2) const {
unsigned n1 = 0;
unsigned n2 = 0;
m_app_pair2num_occs.find(p1.first, p1.second, n1);
m_app_pair2num_occs.find(p2.first, p2.second, n2);
SASSERT(n1 > 0);
SASSERT(n2 > 0);
return n1 > n2;
}
};
void dyn_ack_manager::gc() {
TRACE("dyn_ack", tout << "dyn_ack GC\n";);
unsigned num_deleted = 0;
m_to_instantiate.reset();
m_qhead = 0;
svector<app_pair>::iterator it = m_app_pairs.begin();
svector<app_pair>::iterator end = m_app_pairs.end();
svector<app_pair>::iterator it2 = it;
for (; it != end; ++it) {
app_pair & p = *it;
if (m_instantiated.contains(p)) {
TRACE("dyn_ack", tout << "1) erasing:\n" << mk_pp(p.first, m_manager) << "\n" << mk_pp(p.second, m_manager) << "\n";);
m_manager.dec_ref(p.first);
m_manager.dec_ref(p.second);
SASSERT(!m_app_pair2num_occs.contains(p.first, p.second));
continue;
}
unsigned num_occs = 0;
m_app_pair2num_occs.find(p.first, p.second, num_occs);
// The following invariant is not true. p.first and
// p.second may have been instantiated, and removed from
// m_app_pair2num_occs, but not from m_app_pairs.
//
// SASSERT(num_occs > 0);
num_occs = static_cast<unsigned>(num_occs * m_params.m_dack_gc_inv_decay);
if (num_occs <= 1) {
num_deleted++;
TRACE("dyn_ack", tout << "2) erasing:\n" << mk_pp(p.first, m_manager) << "\n" << mk_pp(p.second, m_manager) << "\n";);
m_app_pair2num_occs.erase(p.first, p.second);
m_manager.dec_ref(p.first);
m_manager.dec_ref(p.second);
continue;
}
*it2 = p;
++it2;
SASSERT(num_occs > 0);
m_app_pair2num_occs.insert(p.first, p.second, num_occs);
if (num_occs >= m_params.m_dack_threshold)
m_to_instantiate.push_back(p);
}
m_app_pairs.set_end(it2);
app_pair_lt f(m_app_pair2num_occs);
// app_pair_lt is not a total order on pairs of expressions.
// So, we should use stable_sort to avoid different behavior in different platforms.
std::stable_sort(m_to_instantiate.begin(), m_to_instantiate.end(), f);
// IF_VERBOSE(10, if (num_deleted > 0) verbose_stream() << "dynamic ackermann GC: " << num_deleted << "\n";);
}
class dyn_ack_clause_del_eh : public clause_del_eh {
dyn_ack_manager & m_manager;
public:
dyn_ack_clause_del_eh(dyn_ack_manager & m):
m_manager(m) {
}
virtual ~dyn_ack_clause_del_eh() {}
virtual void operator()(ast_manager & m, clause * cls) {
m_manager.del_clause_eh(cls);
dealloc(this);
}
};
void dyn_ack_manager::del_clause_eh(clause * cls) {
TRACE("dyn_ack", tout << "del_clause_eh: "; m_context.display_clause(tout, cls); tout << "\n";);
m_context.m_stats.m_num_del_dyn_ack++;
app_pair p((app*)0,(app*)0);
if (m_clause2app_pair.find(cls, p)) {
SASSERT(p.first && p.second);
m_instantiated.erase(p);
SASSERT(!m_app_pair2num_occs.contains(p.first, p.second));
return;
}
app_triple tr(0,0,0);
if (m_triple.m_clause2apps.find(cls, tr)) {
SASSERT(tr.first && tr.second && tr.third);
m_triple.m_instantiated.erase(tr);
SASSERT(!m_triple.m_app2num_occs.contains(tr.first, tr.second, tr.third));
return;
}
}
void dyn_ack_manager::propagate_eh() {
if (m_params.m_dack == DACK_DISABLED)
return;
m_num_propagations_since_last_gc++;
if (m_num_propagations_since_last_gc > m_params.m_dack_gc) {
gc();
m_num_propagations_since_last_gc = 0;
}
unsigned max_instances = static_cast<unsigned>(m_context.get_num_conflicts() * m_params.m_dack_factor);
while (m_num_instances < max_instances && m_qhead < m_to_instantiate.size()) {
app_pair & p = m_to_instantiate[m_qhead];
m_qhead++;
m_num_instances++;
instantiate(p.first, p.second);
}
while (m_num_instances < max_instances && m_triple.m_qhead < m_triple.m_to_instantiate.size()) {
app_triple & p = m_triple.m_to_instantiate[m_triple.m_qhead];
m_triple.m_qhead++;
m_num_instances++;
instantiate(p.first, p.second, p.third);
}
}
literal dyn_ack_manager::mk_eq(expr * n1, expr * n2) {
app * eq = m_context.mk_eq_atom(n1, n2);
m_context.internalize(eq, true);
literal l = m_context.get_literal(eq);
TRACE("dyn_ack", tout << "eq:\n" << mk_pp(eq, m_manager) << "\nliteral: ";
m_context.display_literal(tout, l); tout << "\n";);
return l;
}
void dyn_ack_manager::instantiate(app * n1, app * n2) {
SASSERT(m_params.m_dack != DACK_DISABLED);
SASSERT(n1->get_decl() == n2->get_decl());
SASSERT(n1->get_num_args() == n2->get_num_args());
SASSERT(n1 != n2);
m_context.m_stats.m_num_dyn_ack++;
TRACE("dyn_ack_inst", tout << "dyn_ack: " << n1->get_id() << " " << n2->get_id() << "\n";);
TRACE("dyn_ack", tout << "expanding Ackermann's rule for:\n" << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n";);
unsigned num_args = n1->get_num_args();
literal_buffer lits;
for (unsigned i = 0; i < num_args; i++) {
expr * arg1 = n1->get_arg(i);
expr * arg2 = n2->get_arg(i);
if (arg1 != arg2)
lits.push_back(~mk_eq(arg1, arg2));
}
app_pair p(n1, n2);
SASSERT(m_app_pair2num_occs.contains(n1, n2));
m_app_pair2num_occs.erase(n1, n2);
// pair n1,n2 is still in m_app_pairs
m_instantiated.insert(p);
lits.push_back(mk_eq(n1, n2));
clause_del_eh * del_eh = alloc(dyn_ack_clause_del_eh, *this);
justification * js = 0;
if (m_manager.proofs_enabled())
js = alloc(dyn_ack_justification, n1, n2);
clause * cls = m_context.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh);
if (!cls) {
dealloc(del_eh);
return;
}
TRACE("dyn_ack_clause", tout << "new clause:\n"; m_context.display_clause_detail(tout, cls); tout << "\n";);
m_clause2app_pair.insert(cls, p);
}
void dyn_ack_manager::reset() {
init_search_eh();
m_instantiated.reset();
m_clause2app_pair.reset();
m_triple.m_instantiated.reset();
m_triple.m_clause2apps.reset();
}
void dyn_ack_manager::reset_app_triples() {
svector<app_triple>::iterator it = m_triple.m_apps.begin();
svector<app_triple>::iterator end = m_triple.m_apps.end();
for (; it != end; ++it) {
app_triple & p = *it;
m_manager.dec_ref(p.first);
m_manager.dec_ref(p.second);
m_manager.dec_ref(p.third);
}
m_triple.m_apps.reset();
}
void dyn_ack_manager::instantiate(app * n1, app * n2, app* r) {
SASSERT(m_params.m_dack != DACK_DISABLED);
SASSERT(n1 != n2 && n1 != r && n2 != r);
m_context.m_stats.m_num_dyn_ack++;
TRACE("dyn_ack_inst", tout << "dyn_ack: " << n1->get_id() << " " << n2->get_id() << " " << r->get_id() << "\n";);
TRACE("dyn_ack", tout << "expanding Ackermann's rule for:\n" << mk_pp(n1, m_manager) << "\n"
<< mk_pp(n2, m_manager) << "\n"
<< mk_pp(r, m_manager) << "\n";
);
app_triple tr(n1, n2, r);
SASSERT(m_triple.m_app2num_occs.contains(n1, n2, r));
m_triple.m_app2num_occs.erase(n1, n2, r);
// pair n1,n2 is still in m_triple.m_apps
m_triple.m_instantiated.insert(tr);
literal_buffer lits;
lits.push_back(~mk_eq(n1, r));
lits.push_back(~mk_eq(n2, r));
lits.push_back(mk_eq(n1, n2));
clause_del_eh * del_eh = alloc(dyn_ack_clause_del_eh, *this);
justification * js = 0;
if (m_manager.proofs_enabled())
js = alloc(dyn_ack_justification, n1, n2);
clause * cls = m_context.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh);
if (!cls) {
dealloc(del_eh);
return;
}
TRACE("dyn_ack_clause", tout << "new clause:\n"; m_context.display_clause_detail(tout, cls); tout << "\n";);
m_triple.m_clause2apps.insert(cls, tr);
}
struct app_triple_lt {
typedef triple<app *, app *, app*> app_triple;
typedef obj_triple_map<app, app, app, unsigned> app_triple2num_occs;
app_triple2num_occs & m_app_triple2num_occs;
app_triple_lt(app_triple2num_occs & m):
m_app_triple2num_occs(m) {
}
bool operator()(app_triple const & p1, app_triple const & p2) const {
unsigned n1 = 0;
unsigned n2 = 0;
m_app_triple2num_occs.find(p1.first, p1.second, p1.third, n1);
m_app_triple2num_occs.find(p2.first, p2.second, p2.third, n2);
SASSERT(n1 > 0);
SASSERT(n2 > 0);
return n1 > n2;
}
};
void dyn_ack_manager::gc_triples() {
TRACE("dyn_ack", tout << "dyn_ack GC\n";);
unsigned num_deleted = 0;
m_triple.m_to_instantiate.reset();
m_triple.m_qhead = 0;
svector<app_triple>::iterator it = m_triple.m_apps.begin();
svector<app_triple>::iterator end = m_triple.m_apps.end();
svector<app_triple>::iterator it2 = it;
for (; it != end; ++it) {
app_triple & p = *it;
if (m_triple.m_instantiated.contains(p)) {
TRACE("dyn_ack", tout << "1) erasing:\n" << mk_pp(p.first, m_manager) << "\n" << mk_pp(p.second, m_manager) << "\n";);
m_manager.dec_ref(p.first);
m_manager.dec_ref(p.second);
m_manager.dec_ref(p.third);
SASSERT(!m_triple.m_app2num_occs.contains(p.first, p.second, p.third));
continue;
}
unsigned num_occs = 0;
m_triple.m_app2num_occs.find(p.first, p.second, p.third, num_occs);
// The following invariant is not true. p.first and
// p.second may have been instantiated, and removed from
// m_app_triple2num_occs, but not from m_app_triples.
//
// SASSERT(num_occs > 0);
num_occs = static_cast<unsigned>(num_occs * m_params.m_dack_gc_inv_decay);
if (num_occs <= 1) {
num_deleted++;
TRACE("dyn_ack", tout << "2) erasing:\n" << mk_pp(p.first, m_manager) << "\n" << mk_pp(p.second, m_manager) << "\n";);
m_triple.m_app2num_occs.erase(p.first, p.second, p.third);
m_manager.dec_ref(p.first);
m_manager.dec_ref(p.second);
m_manager.dec_ref(p.third);
continue;
}
*it2 = p;
++it2;
SASSERT(num_occs > 0);
m_triple.m_app2num_occs.insert(p.first, p.second, p.third, num_occs);
if (num_occs >= m_params.m_dack_threshold)
m_triple.m_to_instantiate.push_back(p);
}
m_triple.m_apps.set_end(it2);
app_triple_lt f(m_triple.m_app2num_occs);
// app_triple_lt is not a total order
std::stable_sort(m_triple.m_to_instantiate.begin(), m_triple.m_to_instantiate.end(), f);
// IF_VERBOSE(10, if (num_deleted > 0) verbose_stream() << "dynamic ackermann GC: " << num_deleted << "\n";);
}
#ifdef Z3DEBUG
bool dyn_ack_manager::check_invariant() const {
clause2app_pair::iterator it = m_clause2app_pair.begin();
clause2app_pair::iterator end = m_clause2app_pair.end();
for (; it != end; ++it) {
app_pair const & p = it->get_value();
SASSERT(m_instantiated.contains(p));
SASSERT(!m_app_pair2num_occs.contains(p.first, p.second));
}
return true;
}
#endif
};

137
src/smt/dyn_ack.h Normal file
View file

@ -0,0 +1,137 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dyn_ack.h
Abstract:
Support code for implementing Dynamic Ackermann's reduction
Author:
Leonardo de Moura (leonardo) 2007-04-12.
Revision History:
--*/
#ifndef _DYN_ACK_H_
#define _DYN_ACK_H_
#include"ast.h"
#include"dyn_ack_params.h"
#include"obj_hashtable.h"
#include"obj_pair_hashtable.h"
#include"obj_triple_hashtable.h"
#include"smt_clause.h"
namespace smt {
class context;
class dyn_ack_manager {
typedef std::pair<app *, app *> app_pair;
typedef obj_pair_map<app, app, unsigned> app_pair2num_occs;
typedef svector<app_pair> app_pair_vector;
typedef obj_pair_hashtable<app, app> app_pair_set;
typedef obj_map<clause, app_pair> clause2app_pair;
typedef triple<app *, app *,app *> app_triple;
typedef obj_triple_map<app, app, app, unsigned> app_triple2num_occs;
typedef svector<app_triple> app_triple_vector;
typedef obj_triple_hashtable<app, app, app> app_triple_set;
typedef obj_map<clause, app_triple> clause2app_triple;
context & m_context;
ast_manager & m_manager;
dyn_ack_params & m_params;
app_pair2num_occs m_app_pair2num_occs;
app_pair_vector m_app_pairs;
app_pair_vector m_to_instantiate;
unsigned m_qhead;
unsigned m_num_instances;
unsigned m_num_propagations_since_last_gc;
app_pair_set m_instantiated;
clause2app_pair m_clause2app_pair;
struct _triple {
app_triple2num_occs m_app2num_occs;
app_triple_vector m_apps;
app_triple_vector m_to_instantiate;
unsigned m_qhead;
unsigned m_num_instances;
unsigned m_num_propagations_since_last_gc;
app_triple_set m_instantiated;
clause2app_triple m_clause2apps;
};
_triple m_triple;
void gc();
void reset_app_pairs();
friend class dyn_ack_clause_del_eh;
void del_clause_eh(clause * cls);
void instantiate(app * n1, app * n2);
literal mk_eq(expr * n1, expr * n2);
void cg_eh(app * n1, app * n2);
void eq_eh(app * n1, app * n2, app* r);
void instantiate(app * n1, app * n2, app* r);
void reset_app_triples();
void gc_triples();
public:
dyn_ack_manager(context & ctx, dyn_ack_params & p);
~dyn_ack_manager();
void setup() {
}
/**
\brief This method is invoked before the beginning of the search.
*/
void init_search_eh();
/**
\brief This method is invoked when the congruence rule was used during conflict resolution.
*/
void used_cg_eh(app * n1, app * n2) {
if (m_params.m_dack == DACK_CR)
cg_eh(n1, n2);
}
/**
\brief This method is invoked when the congruence rule is the root of a conflict.
*/
void cg_conflict_eh(app * n1, app * n2) {
if (m_params.m_dack == DACK_ROOT)
cg_eh(n1, n2);
}
/**
\brief This method is invoked when equalities are used during conflict resolution.
*/
void used_eq_eh(app * n1, app * n2, app* r) {
if (m_params.m_dack_eq)
eq_eh(n1, n2, r);
}
/**
\brief This method is invoked when it is safe to expand the new ackermann rule entries.
*/
void propagate_eh();
void reset();
#ifdef Z3DEBUG
bool check_invariant() const;
#endif
};
};
#endif /* _DYN_ACK_H_ */

View file

@ -0,0 +1,753 @@
/*++
Copyright (c) 2008 Microsoft Corporation
Module Name:
expr_context_simplifier.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2008-06-03
Revision History:
--*/
#include "expr_context_simplifier.h"
#include "ast_pp.h"
#include "obj_hashtable.h"
#include "smt_solver.h"
#include "for_each_expr.h"
// table lookup before/after simplification.
expr_context_simplifier::expr_context_simplifier(ast_manager& m):
m_manager(m), m_arith(m), m_trail(m), m_simp(m), m_forward(true) {}
void expr_context_simplifier::reduce(expr * m, expr_ref & result) {
expr_ref tmp(m_manager);
m_mark.reset();
unsigned trail_size = m_trail.size();
m_forward = true;
reduce_rec(m, tmp);
m_mark.reset();
m_forward = false;
reduce_rec(tmp.get(), result);
clean_trail(trail_size);
}
void expr_context_simplifier::reduce_fix(expr * m, expr_ref & result) {
expr_ref tmp(m_manager);
result = m;
do {
tmp = result.get();
reduce(tmp.get(), result);
}
while (tmp.get() != result.get());
}
void expr_context_simplifier::reduce_rec(expr * m, expr_ref & result) {
//
// reduce expr in context evaluation.
//
bool polarity;
if (m_context.find(m, polarity)) {
result = polarity ? m_manager.mk_true() : m_manager.mk_false();
}
else if (m_mark.is_marked(m) && !m_manager.is_not(m)) {
result = m;
}
else if (is_quantifier(m)) {
reduce_rec(to_quantifier(m), result);
m_mark.mark(m, true);
}
else if (is_app(m)) {
reduce_rec(to_app(m), result);
m_mark.mark(m, true);
}
else if (is_var(m)) {
result = m;
m_mark.mark(m, true);
}
else {
UNREACHABLE();
result = m;
}
}
void expr_context_simplifier::reduce_rec(quantifier* q, expr_ref & result) {
result = q;
#if 0
//
// The context assumes that asserted expressions are in NNF with
// respect to the quantifier occurrences.
// This can be disabled if the strong context simplifier
// is called from the API over the Z3_simplify method.
//
expr_context_simplifier nested(m_manager);
expr_ref body_r(m_manager);
nested.reduce(q->get_expr(), body_r);
if (body_r.get() != q->get_expr()) {
result = m_manager.update_quantifier(q, body_r.get());
}
else {
result = q;
}
#endif
}
void expr_context_simplifier::reduce_rec(app * a, expr_ref & result) {
if (m_manager.get_basic_family_id() == a->get_family_id()) {
switch(a->get_decl_kind()) {
case OP_AND:
reduce_and(a->get_num_args(), a->get_args(), result);
return;
case OP_OR:
reduce_or(a->get_num_args(), a->get_args(), result);
return;
case OP_IFF: {
expr_ref tmp1(m_manager), tmp2(m_manager);
reduce_rec(a->get_arg(0), tmp1);
reduce_rec(a->get_arg(1), tmp2);
m_simp.mk_iff(tmp1.get(), tmp2.get(), result);
return;
}
case OP_XOR: {
expr_ref tmp1(m_manager), tmp2(m_manager);
reduce_rec(a->get_arg(0), tmp1);
reduce_rec(a->get_arg(1), tmp2);
m_simp.mk_xor(tmp1.get(), tmp2.get(), result);
return;
}
case OP_NOT: {
expr_ref tmp(m_manager);
reduce_rec(a->get_arg(0), tmp);
m_simp.mk_not(tmp.get(), result);
return;
}
case OP_IMPLIES: {
app_ref tmp(m_manager);
tmp = m_manager.mk_not(a->get_arg(0));
expr* args[2] = { tmp.get(), a->get_arg(1) };
reduce_or(2, args, result);
return;
}
case OP_ITE: {
expr_ref tmp(m_manager), tmp1(m_manager), tmp2(m_manager);
reduce_rec(a->get_arg(0), tmp);
if (is_true(tmp.get())) {
reduce_rec(a->get_arg(1), result);
}
else if (is_false(tmp.get())) {
reduce_rec(a->get_arg(2), result);
}
else {
unsigned trail_size = m_trail.size();
insert_context(tmp.get(), true);
reduce_rec(a->get_arg(1), tmp1);
clean_trail(trail_size);
insert_context(tmp.get(), false);
reduce_rec(a->get_arg(2), tmp2);
clean_trail(trail_size);
m_simp.mk_ite(tmp.get(), tmp1.get(), tmp2.get(), result);
}
return;
}
default:
break;
}
}
expr_ref_vector args(m_manager);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
expr_ref tmp(m_manager);
reduce_rec(a->get_arg(i), tmp);
args.push_back(tmp.get());
}
result = m_manager.mk_app(a->get_decl(), args.size(), args.c_ptr());
}
void expr_context_simplifier::clean_trail(unsigned old_lim) {
for (unsigned i = m_trail.size(); i > old_lim; ) {
--i;
m_context.erase(m_trail[i].get());
}
m_trail.resize(old_lim);
}
void expr_context_simplifier::insert_context(expr* e, bool polarity) {
TRACE("expr_context_simplifier", tout << mk_pp(e, m_manager) << "\n";);
if (m_manager.is_not(e)) {
e = to_app(e)->get_arg(0);
polarity = !polarity;
}
if (!m_context.contains(e)) {
m_context.insert(e, polarity);
m_trail.push_back(e);
}
}
bool expr_context_simplifier::insert_arg(bool is_and, expr* arg, expr_ref_vector& args) {
expr_ref tmp(m_manager);
reduce_rec(arg, tmp);
TRACE("expr_context_simplifier", tout << mk_pp(arg, m_manager) << " -> " << mk_pp(tmp.get(), m_manager) << "\n";);
if (is_true(tmp.get()) && is_and) {
// skip.
}
else if (is_false(tmp.get()) && !is_and) {
// skip.
}
else if (is_false(tmp.get()) && is_and) {
return true;
}
else if (is_true(tmp.get()) && !is_and) {
return true;
}
else {
insert_context(tmp.get(), is_and);
if (arg != tmp.get()) {
insert_context(arg, is_and); // allow to also use un-simplified version
}
args.push_back(tmp.get());
}
return false;
}
void expr_context_simplifier::reduce_and_or(bool is_and, unsigned num_args, expr * const* args, expr_ref & result) {
expr_ref tmp(m_manager);
expr_ref_vector args1(m_manager);
unsigned trail_size = m_trail.size();
if (m_forward) {
for (unsigned i = 0; i < num_args; ++i) {
if (insert_arg(is_and, args[i], args1)) {
clean_trail(trail_size);
result = is_and?m_manager.mk_false():m_manager.mk_true();
return;
}
}
}
else {
for (unsigned i = num_args; i > 0; ) {
--i;
if (insert_arg(is_and, args[i], args1)) {
clean_trail(trail_size);
result = is_and?m_manager.mk_false():m_manager.mk_true();
return;
}
}
}
clean_trail(trail_size);
if (is_and) {
m_simp.mk_and(args1.size(), args1.c_ptr(), result);
}
else {
m_simp.mk_or(args1.size(), args1.c_ptr(), result);
}
}
void expr_context_simplifier::reduce_and(unsigned num_args, expr * const* args, expr_ref & result) {
reduce_and_or(true, num_args, args, result);
}
void expr_context_simplifier::reduce_or(unsigned num_args, expr * const* args, expr_ref & result) {
reduce_and_or(false, num_args, args, result);
}
bool expr_context_simplifier::is_true(expr* e) const {
return
m_manager.is_true(e) ||
(m_manager.is_not(e) && m_manager.is_false(to_app(e)->get_arg(0)));
}
bool expr_context_simplifier::is_false(expr* e) const {
return
m_manager.is_false(e) ||
(m_manager.is_not(e) && m_manager.is_true(to_app(e)->get_arg(0)));
}
//
// This routine performs strong context simplification.
// It replaces sub-formulas by a fresh name
// and checks if the original formula is equivalent
// to the resulting formula if the fresh name is set to
// true or false.
// otherwise it recursively expands the definition of the
// fresh name to match the original formula.
//
// assert ! (fml <=> n)
//
//for (fml', n')
// check visited
// check n'
// check !n'
// if each a is visited,
// fml' by fml'[a/a']
// pop_scope
// ow,
// let a be a non-visited argument.
// push_scope
// push (a, n'')
// assert (n' <=> f(visited_args, n'', visited_or_non_visited_args))
//
// The implementation avoid the stack. It uses the following vectors:
// todo - DFS stack
// names - collection of fresh names.
// is_checked - Boolean to control if contextual equivalence with T or F was checked.
// parent_ids - stack of IDs used to identify path down to expression on first visit.
// self_ids - stack of IDs used to identify path down to children on first visit.
// The parent_ids, self_ids stacks are used to ensure that caching results can be done
// in a context dependent way. A cached result is only valid for simplification if
// it occurs in the context (on the path) where it was inserted.
//
expr_strong_context_simplifier::expr_strong_context_simplifier(front_end_params& p, ast_manager& m):
m_manager(m), m_params(p), m_arith(m), m_id(0), m_fn(0,m), m_solver(m, p) {
sort* i_sort = m_arith.mk_int();
m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort());
}
void expr_strong_context_simplifier::simplify_basic(expr* fml, expr_ref& result) {
ast_manager& m = m_manager;
//
// The context assumes that asserted expressions are in NNF with
// respect to the quantifier occurrences.
// This can be disabled if the strong context simplifier
// is called from the API over the Z3_simplify method.
//
if (!m.is_bool(fml) || has_quantifiers(fml)) {
result = fml;
return;
}
flet<bool> fl1(m_params.m_strong_context_simplifier, false);
ptr_vector<expr> todo;
ptr_vector<expr> names;
svector<bool> is_checked;
svector<unsigned> parent_ids, self_ids;
expr_ref_vector fresh_vars(m);
expr_ref_vector trail(m);
obj_map<expr,std::pair<unsigned, expr*> > cache;
m_solver.push();
unsigned id = 1;
expr* n = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true));
expr* r, *n2;
lbool is_sat;
unsigned path_id = 0, self_pos = 0;
app * a;
unsigned sz;
trail.push_back(n);
m_solver.assert_expr(m.mk_not(m.mk_iff(fml, n)));
todo.push_back(fml);
names.push_back(n);
is_checked.push_back(false);
parent_ids.push_back(0);
self_ids.push_back(0);
std::pair<unsigned,expr*> path_r;
m_solver.push();
while (!todo.empty()) {
r = 0;
ptr_buffer<expr> args;
expr* e = todo.back();
unsigned pos = parent_ids.back();
n = names.back();
bool checked = is_checked.back();
if (cache.contains(e)) {
goto done;
}
if (!m.is_bool(e)) {
r = e;
goto done;
}
if (m.is_bool(e) && !checked) {
m_solver.push();
m_solver.assert_expr(n);
is_sat = m_solver.check();
m_solver.pop(1);
if (is_sat == l_false) {
r = m.mk_true();
goto done;
}
}
if (m.is_bool(e) && !checked) {
m_solver.push();
m_solver.assert_expr(m.mk_not(n));
is_sat = m_solver.check();
m_solver.pop(1);
if (is_sat == l_false) {
r = m.mk_false();
goto done;
}
}
if (!is_app(e)) {
r = e;
goto done;
}
a = to_app(e);
if (!is_checked.back()) {
self_ids.back() = ++path_id;
is_checked.back() = true;
}
self_pos = self_ids.back();
sz = a->get_num_args();
n2 = 0;
for (unsigned i = 0; i < sz; ++i) {
expr* arg = a->get_arg(i);
if (cache.find(arg, path_r)) {
if (path_r.first == self_pos) {
args.push_back(path_r.second);
}
else {
args.push_back(arg);
}
}
else if (!m.is_bool(arg)) {
args.push_back(arg);
}
else if (!n2) {
n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true));
todo.push_back(arg);
parent_ids.push_back(self_pos);
self_ids.push_back(0);
names.push_back(n2);
trail.push_back(n2);
args.push_back(n2);
is_checked.push_back(false);
}
else {
args.push_back(arg);
}
}
r = m.mk_app(a->get_decl(), args.size(), args.c_ptr());
trail.push_back(r);
if (n2) {
m_solver.push();
m_solver.assert_expr(m.mk_eq(r, n));
continue;
}
done:
if (r) {
cache.insert(e, std::make_pair(pos, r));
}
TRACE("expr_context_simplifier",
tout << mk_pp(e, m_manager)
<< " checked: " << checked
<< " cached: "
<< mk_pp(r?r:e, m_manager) << "\n";);
todo.pop_back();
parent_ids.pop_back();
self_ids.pop_back();
names.pop_back();
is_checked.pop_back();
m_solver.pop(1);
}
VERIFY(cache.find(fml, path_r));
m_solver.pop(1);
result = path_r.second;
}
void expr_strong_context_simplifier::simplify_model_based(expr* fml, expr_ref& result) {
ast_manager& m = m_manager;
//
// The context assumes that asserted expressions are in NNF with
// respect to the quantifier occurrences.
// This can be disabled if the strong context simplifier
// is called from the API over the Z3_simplify method.
//
if (!m.is_bool(fml) || has_quantifiers(fml)) {
result = fml;
return;
}
flet<bool> fl1(m_params.m_strong_context_simplifier, false);
ptr_vector<expr> todo;
ptr_vector<expr> names;
svector<bool> is_checked;
svector<unsigned> parent_ids, self_ids;
expr_ref_vector fresh_vars(m);
expr_ref_vector trail(m);
obj_map<expr,std::pair<unsigned, expr*> > cache;
lbool is_sat;
expr_ref_vector assignments(m);
m_solver.push();
m_solver.assert_expr(fml);
is_sat = m_solver.check();
if (is_sat != l_false) {
m_solver.get_assignments(assignments);
}
m_solver.pop(1);
if (is_sat == l_false) {
result = m.mk_false();
return;
}
// Collect assignments to sub-formulas from satisfying assignment.
obj_map<expr,lbool> assignment_map;
{
expr* n1, *n2;
for (unsigned i = 0; i < assignments.size(); ++i) {
if (m.is_not(assignments[i].get(), n1)) {
assignment_map.insert(n1, l_false);
}
else {
assignment_map.insert(assignments[i].get(), l_true);
}
}
todo.push_back(fml);
while (!todo.empty()) {
expr* n = todo.back();
if (!is_app(n)) {
assignment_map.insert(n, l_undef);
todo.pop_back();
continue;
}
app* a = to_app(n);
unsigned sz = a->get_num_args();
bool all_visit = true;
for (unsigned i = 0; i < sz; ++i) {
if (!assignment_map.contains(a->get_arg(i))) {
todo.push_back(a->get_arg(i));
all_visit = false;
}
}
if (!all_visit) {
continue;
}
todo.pop_back();
lbool value = l_undef;
if (m.is_and(a)) {
value = l_true;
for (unsigned i = 0; value != l_false && i < sz; ++i) {
switch(assignment_map.find(a->get_arg(i))) {
case l_false: value = l_false; break;
case l_undef: value = l_undef; break;
default: break;
}
}
assignment_map.insert(a, value);
}
else if (m.is_or(a)) {
value = l_false;
for (unsigned i = 0; value != l_true && i < sz; ++i) {
switch(assignment_map.find(a->get_arg(i))) {
case l_true: value = l_true; break;
case l_undef: value = l_undef; break;
default: break;
}
}
assignment_map.insert(a, value);
}
else if (m.is_not(a)) {
switch(assignment_map.find(a->get_arg(0))) {
case l_true: value = l_false; break;
case l_false: value = l_true; break;
default: value = l_undef; break;
}
assignment_map.insert(a, value);
}
else if (m.is_implies(a, n1, n2)) {
lbool v1 = assignment_map.find(n1);
lbool v2 = assignment_map.find(n2);
if (v1 == l_false || v2 == l_true) {
value = l_true;
}
else if (v1 == l_true && v2 == l_false) {
value = l_false;
}
else {
value = l_undef;
}
assignment_map.insert(a, value);
}
else if (m.is_iff(a, n1, n2) || m.is_eq(a, n1, n2)) {
lbool v1 = assignment_map.find(n1);
lbool v2 = assignment_map.find(n2);
if (v1 == l_undef || v2 == l_undef) {
value = l_undef;
}
else if (v1 == v2) {
value = l_true;
}
else {
value = l_false;
}
assignment_map.insert(a, value);
}
else {
assignment_map.insert(a, l_undef);
}
}
}
m_solver.push();
unsigned id = 1;
expr* n = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true));
expr* r, *n2;
unsigned path_id = 0, self_pos = 0;
app * a;
unsigned sz;
trail.push_back(n);
m_solver.assert_expr(m.mk_not(m.mk_iff(fml, n)));
todo.push_back(fml);
names.push_back(n);
is_checked.push_back(false);
parent_ids.push_back(0);
self_ids.push_back(0);
std::pair<unsigned,expr*> path_r;
m_solver.push();
while (!todo.empty()) {
r = 0;
ptr_buffer<expr> args;
expr* e = todo.back();
unsigned pos = parent_ids.back();
n = names.back();
bool checked = is_checked.back();
if (cache.contains(e)) {
goto done;
}
if (!m.is_bool(e)) {
r = e;
goto done;
}
if (m.is_bool(e) && !checked) {
lbool value = l_undef;
assignment_map.find(e, value);
switch(value) {
case l_true:
if (is_forced(n, m.mk_true())) {
r = m.mk_true();
goto done;
}
break;
case l_false:
if (is_forced(n, m.mk_false())) {
r = m.mk_false();
goto done;
}
break;
default:
// NB. assignments contain just internalized literals,
// the literals in the input may not be internalized.
// we therefore fall back to the default behavior, which
// is to check both cases.
if (is_forced(n, m.mk_true())) {
r = m.mk_true();
goto done;
}
if (is_forced(n, m.mk_false())) {
r = m.mk_false();
goto done;
}
break;
}
}
if (!is_app(e)) {
r = e;
goto done;
}
a = to_app(e);
if (!is_checked.back()) {
self_ids.back() = ++path_id;
is_checked.back() = true;
}
self_pos = self_ids.back();
sz = a->get_num_args();
n2 = 0;
for (unsigned i = 0; i < sz; ++i) {
expr* arg = a->get_arg(i);
if (cache.find(arg, path_r)) {
if (path_r.first == self_pos) {
args.push_back(path_r.second);
}
else {
args.push_back(arg);
}
}
else if (!m.is_bool(arg)) {
args.push_back(arg);
}
else if (!n2) {
n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true));
todo.push_back(arg);
parent_ids.push_back(self_pos);
self_ids.push_back(0);
names.push_back(n2);
trail.push_back(n2);
args.push_back(n2);
is_checked.push_back(false);
}
else {
args.push_back(arg);
}
}
r = m.mk_app(a->get_decl(), args.size(), args.c_ptr());
trail.push_back(r);
if (n2) {
m_solver.push();
m_solver.assert_expr(m.mk_eq(r, n));
continue;
}
done:
if (r) {
cache.insert(e, std::make_pair(pos, r));
}
TRACE("expr_context_simplifier",
tout << mk_pp(e, m_manager)
<< " checked: " << checked
<< " cached: "
<< mk_pp(r?r:e, m_manager) << "\n";);
todo.pop_back();
parent_ids.pop_back();
self_ids.pop_back();
names.pop_back();
is_checked.pop_back();
m_solver.pop(1);
}
VERIFY(cache.find(fml, path_r));
m_solver.pop(1);
result = path_r.second;
}
bool expr_strong_context_simplifier::is_forced(expr* e, expr* v) {
m_solver.push();
m_solver.assert_expr(m_manager.mk_eq(e, v));
lbool is_sat = m_solver.check();
m_solver.pop(1);
return (is_sat == l_false);
}

View file

@ -0,0 +1,86 @@
/*++
Copyright (c) 2008 Microsoft Corporation
Module Name:
expr_context_simplifier.h
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2008-06-03
Revision History:
--*/
#ifndef _EXPR_CONTEXT_SIMPLIFIER_H_
#define _EXPR_CONTEXT_SIMPLIFIER_H_
#include "ast.h"
#include "obj_hashtable.h"
#include "basic_simplifier_plugin.h"
#include "front_end_params.h"
#include "smt_solver.h"
#include "arith_decl_plugin.h"
class expr_context_simplifier {
typedef obj_map<expr, bool> context_map;
ast_manager& m_manager;
arith_util m_arith;
context_map m_context;
expr_ref_vector m_trail;
basic_simplifier_plugin m_simp;
expr_mark m_mark;
bool m_forward;
public:
expr_context_simplifier(ast_manager & m);
void reduce_fix(expr * m, expr_ref & result);
void operator()(expr * m, expr_ref & result) { reduce(m, result); }
void insert_context(expr* e, bool polarity);
private:
void reduce(expr * m, expr_ref & result);
void reduce_rec(expr * m, expr_ref & result);
void reduce_rec(quantifier* q, expr_ref & result);
void reduce_rec(app * a, expr_ref & result);
void clean_trail(unsigned old_lim);
bool insert_arg(bool is_and, expr* arg, expr_ref_vector& args);
void reduce_and_or(bool is_and, unsigned num_args, expr * const* args, expr_ref & result);
void reduce_and(unsigned num_args, expr * const* args, expr_ref & result);
void reduce_or(unsigned num_args, expr * const* args, expr_ref & result);
bool is_true(expr* e) const;
bool is_false(expr* e) const;
};
class expr_strong_context_simplifier {
ast_manager& m_manager;
front_end_params & m_params;
arith_util m_arith;
unsigned m_id;
func_decl_ref m_fn;
smt::solver m_solver;
void simplify(expr* e, expr_ref& result) { simplify_model_based(e, result); }
void simplify_basic(expr* fml, expr_ref& result);
void simplify_model_based(expr* fml, expr_ref& result);
bool is_forced(expr* e, expr* v);
public:
expr_strong_context_simplifier(front_end_params& p, ast_manager& m);
void operator()(expr* e, expr_ref& result) { simplify(e, result); }
void operator()(expr_ref& result) { simplify(result.get(), result); }
void push() { m_solver.push(); }
void pop() { m_solver.pop(1); }
void assert(expr* e) { m_solver.assert_expr(e); }
void collect_statistics(statistics & st) const { m_solver.collect_statistics(st); }
void reset_statistics() { m_solver.reset_statistics(); }
void set_cancel(bool f) { m_solver.set_cancel(f); }
};
#endif /* _EXPR_CONTEXT_SIMPLIFIER_H__ */

156
src/smt/fingerprints.cpp Normal file
View file

@ -0,0 +1,156 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
fingerprints.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2007-02-24.
Revision History:
--*/
#include"fingerprints.h"
namespace smt {
fingerprint::fingerprint(region & r, void * d, unsigned d_h, unsigned n, enode * const * args):
m_data(d),
m_data_hash(d_h),
m_num_args(n),
m_args(0) {
m_args = new (r) enode*[n];
memcpy(m_args, args, sizeof(enode*) * n);
}
bool fingerprint_set::fingerprint_eq_proc::operator()(fingerprint const * f1, fingerprint const * f2) const {
if (f1->get_data() != f2->get_data())
return false;
if (f1->get_num_args() != f2->get_num_args())
return false;
unsigned n = f1->get_num_args();
for(unsigned i = 0; i < n; i++)
if (f1->get_arg(i) != f2->get_arg(i))
return false;
return true;
}
fingerprint * fingerprint_set::mk_dummy(void * data, unsigned data_hash, unsigned num_args, enode * const * args) {
m_tmp.reset();
m_tmp.append(num_args, args);
m_dummy.m_data = data;
m_dummy.m_data_hash = data_hash;
m_dummy.m_num_args = num_args;
m_dummy.m_args = m_tmp.c_ptr();
return &m_dummy;
}
fingerprint * fingerprint_set::insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args) {
fingerprint * d = mk_dummy(data, data_hash, num_args, args);
if (m_set.contains(d))
return 0;
TRACE("fingerprint_bug", tout << "1) inserting: " << data_hash << " num_args: " << num_args;
for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_owner_id();
tout << "\n";);
for (unsigned i = 0; i < num_args; i++)
d->m_args[i] = d->m_args[i]->get_root();
if (m_set.contains(d)) {
TRACE("fingerprint_bug", tout << "failed: " << data_hash << " num_args: " << num_args;
for (unsigned i = 0; i < num_args; i++) tout << " " << d->m_args[i]->get_owner_id();
tout << "\n";);
return 0;
}
TRACE("fingerprint_bug", tout << "2) inserting: " << data_hash << " num_args: " << num_args;
for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_owner_id();
tout << "\n";);
fingerprint * f = new (m_region) fingerprint(m_region, data, data_hash, num_args, d->m_args);
m_fingerprints.push_back(f);
m_set.insert(f);
return f;
}
bool fingerprint_set::contains(void * data, unsigned data_hash, unsigned num_args, enode * const * args) {
fingerprint * d = mk_dummy(data, data_hash, num_args, args);
if (m_set.contains(d))
return true;
for (unsigned i = 0; i < num_args; i++)
d->m_args[i] = d->m_args[i]->get_root();
if (m_set.contains(d))
return true;
return false;
}
void fingerprint_set::reset() {
m_set.reset();
m_fingerprints.reset();
}
void fingerprint_set::push_scope() {
m_scopes.push_back(m_fingerprints.size());
}
void fingerprint_set::pop_scope(unsigned num_scopes) {
unsigned lvl = m_scopes.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
unsigned old_size = m_scopes[new_lvl];
unsigned size = m_fingerprints.size();
for (unsigned i = old_size; i < size; i++)
m_set.erase(m_fingerprints[i]);
m_fingerprints.shrink(old_size);
m_scopes.shrink(new_lvl);
}
void fingerprint_set::display(std::ostream & out) const {
out << "fingerprints:\n";
SASSERT(m_set.size() == m_fingerprints.size());
ptr_vector<fingerprint>::const_iterator it = m_fingerprints.begin();
ptr_vector<fingerprint>::const_iterator end = m_fingerprints.end();
for (; it != end; ++it) {
fingerprint const * f = *it;
out << f->get_data() << " #" << f->get_data_hash();
for (unsigned i = 0; i < f->get_num_args(); i++)
out << " #" << f->get_arg(i)->get_owner_id();
out << "\n";
}
}
#ifdef Z3DEBUG
/**
\brief Slow function for checking if there is a fingerprint congruent to (data args[0] ... args[num_args-1])
*/
bool fingerprint_set::slow_contains(void const * data, unsigned data_hash, unsigned num_args, enode * const * args) const {
ptr_vector<fingerprint>::const_iterator it = m_fingerprints.begin();
ptr_vector<fingerprint>::const_iterator end = m_fingerprints.end();
for (; it != end; ++it) {
fingerprint const * f = *it;
if (f->get_data() != data)
continue;
if (f->get_num_args() != num_args)
continue;
unsigned i = 0;
for (i = 0; i < num_args; i++)
if (f->get_arg(i)->get_root() != args[i]->get_root())
break;
if (i == num_args) {
TRACE("missing_instance_detail", tout << "found instance data: " << data << "=" << f->get_data() << " hash: " << f->get_data_hash();
for (unsigned i = 0; i < num_args; i++) {
tout << " " << f->get_arg(i)->get_owner_id() << ":" << f->get_arg(i)->get_root()->get_owner_id() << "="
<< args[i]->get_owner_id() << ":" << args[i]->get_root()->get_owner_id();
}
tout << "\n";);
return true;
}
}
return false;
}
#endif
};

85
src/smt/fingerprints.h Normal file
View file

@ -0,0 +1,85 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
fingerprints.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2007-02-24.
Revision History:
--*/
#ifndef _FINGERPRINTS_H_
#define _FINGERPRINTS_H_
#include"smt_enode.h"
namespace smt {
class fingerprint {
protected:
void * m_data;
unsigned m_data_hash;
unsigned m_num_args;
enode * * m_args;
friend class fingerprint_set;
fingerprint() {}
public:
fingerprint(region & r, void * d, unsigned d_hash, unsigned n, enode * const * args);
void * get_data() const { return m_data; }
unsigned get_data_hash() const { return m_data_hash; }
unsigned get_num_args() const { return m_num_args; }
enode * const * get_args() const { return m_args; }
enode * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; }
};
class fingerprint_set {
struct fingerprint_khasher {
unsigned operator()(fingerprint const * f) const { return f->get_data_hash(); }
};
struct fingerprint_chasher {
unsigned operator()(fingerprint const * f, unsigned idx) const { return f->get_arg(idx)->hash(); }
};
struct fingerprint_hash_proc {
unsigned operator()(fingerprint const * f) const {
return get_composite_hash<fingerprint *, fingerprint_khasher, fingerprint_chasher>(const_cast<fingerprint*>(f), f->get_num_args());
}
};
struct fingerprint_eq_proc { bool operator()(fingerprint const * f1, fingerprint const * f2) const; };
typedef ptr_hashtable<fingerprint, fingerprint_hash_proc, fingerprint_eq_proc> set;
region & m_region;
set m_set;
ptr_vector<fingerprint> m_fingerprints;
unsigned_vector m_scopes;
ptr_vector<enode> m_tmp;
fingerprint m_dummy;
fingerprint * mk_dummy(void * data, unsigned data_hash, unsigned num_args, enode * const * args);
public:
fingerprint_set(region & r):m_region(r) {}
fingerprint * insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args);
unsigned size() const { return m_fingerprints.size(); }
bool contains(void * data, unsigned data_hash, unsigned num_args, enode * const * args);
void reset();
void push_scope();
void pop_scope(unsigned num_scopes);
void display(std::ostream & out) const;
#ifdef Z3DEBUG
bool slow_contains(void const * data, unsigned data_hash, unsigned num_args, enode * const * args) const;
#endif
};
};
#endif /* _FINGERPRINTS_H_ */

3987
src/smt/mam.cpp Normal file

File diff suppressed because it is too large Load diff

74
src/smt/mam.h Normal file
View file

@ -0,0 +1,74 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
mam.h
Abstract:
Matching Abstract Machine
Author:
Leonardo de Moura (leonardo) 2007-02-13.
Revision History:
--*/
#ifndef _MAM_H_
#define _MAM_H_
#include"ast.h"
#include"smt_types.h"
namespace smt {
/**
\brief Matching Abstract Machine (MAM)
*/
class mam {
protected:
context & m_context;
std::ostream * m_trace_stream;
public:
mam(context & ctx, std::ostream *trace):
m_context(ctx),
m_trace_stream(trace) {
}
virtual ~mam() {
}
virtual void add_pattern(quantifier * q, app * mp) = 0;
virtual void push_scope() = 0;
virtual void pop_scope(unsigned num_scopes) = 0;
virtual void match() = 0;
virtual void rematch(bool use_irrelevant = false) = 0;
virtual bool has_work() const = 0;
virtual void relevant_eh(enode * n, bool lazy) = 0;
virtual void add_eq_eh(enode * r1, enode * r2) = 0;
virtual void reset() = 0;
virtual void display(std::ostream& out) = 0;
virtual void on_match(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, ptr_vector<enode> & used_enodes) = 0;
virtual bool is_shared(enode * n) const = 0;
#ifdef Z3DEBUG
virtual bool check_missing_instances() = 0;
#endif
};
mam * mk_mam(context & ctx, std::ostream *trace);
};
#endif /* _MAM_H_ */

484
src/smt/qi_queue.cpp Normal file
View file

@ -0,0 +1,484 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
qi_queue.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-15.
Revision History:
--*/
#include"smt_context.h"
#include"qi_queue.h"
#include"warning.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"var_subst.h"
#include"stats.h"
namespace smt {
qi_queue::qi_queue(quantifier_manager & qm, context & ctx, qi_params & params, std::ostream *trace_stream):
m_qm(qm),
m_context(ctx),
m_manager(m_context.get_manager()),
m_params(params),
m_checker(m_context),
m_cost_function(m_manager),
m_new_gen_function(m_manager),
m_parser(m_manager),
m_evaluator(m_manager),
m_subst(m_manager),
m_trace_stream(trace_stream),
m_instances(m_manager) {
init_parser_vars();
m_vals.resize(15, 0.0f);
}
qi_queue::~qi_queue() {
}
void qi_queue::setup() {
TRACE("qi_cost", tout << "qi_cost: " << m_params.m_qi_cost << "\n";);
if (!m_parser.parse_string(m_params.m_qi_cost.c_str(), m_cost_function))
throw default_exception("invalid cost function %s", m_params.m_qi_cost.c_str());
if (!m_parser.parse_string(m_params.m_qi_new_gen.c_str(), m_new_gen_function))
throw default_exception("invalid new-gen function %s", m_params.m_qi_new_gen.c_str());
m_eager_cost_threshold = m_params.m_qi_eager_threshold;
}
void qi_queue::init_parser_vars() {
#define COST 14
m_parser.add_var("cost");
#define MIN_TOP_GENERATION 13
m_parser.add_var("min_top_generation");
#define MAX_TOP_GENERATION 12
m_parser.add_var("max_top_generation");
#define INSTANCES 11
m_parser.add_var("instances");
#define SIZE 10
m_parser.add_var("size");
#define DEPTH 9
m_parser.add_var("depth");
#define GENERATION 8
m_parser.add_var("generation");
#define QUANT_GENERATION 7
m_parser.add_var("quant_generation");
#define WEIGHT 6
m_parser.add_var("weight");
#define VARS 5
m_parser.add_var("vars");
#define PATTERN_WIDTH 4
m_parser.add_var("pattern_width");
#define TOTAL_INSTANCES 3
m_parser.add_var("total_instances");
#define SCOPE 2
m_parser.add_var("scope");
#define NESTED_QUANTIFIERS 1
m_parser.add_var("nested_quantifiers");
#define CS_FACTOR 0
m_parser.add_var("cs_factor");
}
quantifier_stat * qi_queue::set_values(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation, float cost) {
quantifier_stat * stat = m_qm.get_stat(q);
m_vals[COST] = cost;
m_vals[MIN_TOP_GENERATION] = static_cast<float>(min_top_generation);
m_vals[MAX_TOP_GENERATION] = static_cast<float>(max_top_generation);
m_vals[INSTANCES] = static_cast<float>(stat->get_num_instances_curr_branch());
m_vals[SIZE] = static_cast<float>(stat->get_size());
m_vals[DEPTH] = static_cast<float>(stat->get_depth());
m_vals[GENERATION] = static_cast<float>(generation);
m_vals[QUANT_GENERATION] = static_cast<float>(stat->get_generation());
m_vals[WEIGHT] = static_cast<float>(q->get_weight());
m_vals[VARS] = static_cast<float>(q->get_num_decls());
m_vals[PATTERN_WIDTH] = pat ? static_cast<float>(pat->get_num_args()) : 1.0f;
m_vals[TOTAL_INSTANCES] = static_cast<float>(stat->get_num_instances_curr_search());
m_vals[SCOPE] = static_cast<float>(m_context.get_scope_level());
m_vals[NESTED_QUANTIFIERS] = static_cast<float>(stat->get_num_nested_quantifiers());
m_vals[CS_FACTOR] = static_cast<float>(stat->get_case_split_factor());
TRACE("qi_queue_detail", for (unsigned i = 0; i < m_vals.size(); i++) { tout << m_vals[i] << " "; } tout << "\n";);
return stat;
}
float qi_queue::get_cost(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation) {
quantifier_stat * stat = set_values(q, pat, generation, min_top_generation, max_top_generation, 0);
float r = m_evaluator(m_cost_function, m_vals.size(), m_vals.c_ptr());
stat->update_max_cost(r);
return r;
}
unsigned qi_queue::get_new_gen(quantifier * q, unsigned generation, float cost) {
// max_top_generation and min_top_generation are not available for computing inc_gen
set_values(q, 0, generation, 0, 0, cost);
float r = m_evaluator(m_new_gen_function, m_vals.size(), m_vals.c_ptr());
return static_cast<unsigned>(r);
}
void qi_queue::insert(fingerprint * f, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation) {
quantifier * q = static_cast<quantifier*>(f->get_data());
float cost = get_cost(q, pat, generation, min_top_generation, max_top_generation);
TRACE("qi_queue_detail",
tout << "new instance of " << q->get_qid() << ", weight " << q->get_weight()
<< ", generation: " << generation << ", scope_level: " << m_context.get_scope_level() << ", cost: " << cost << "\n";
for (unsigned i = 0; i < f->get_num_args(); i++) {
tout << "#" << f->get_arg(i)->get_owner_id() << " ";
}
tout << "\n";);
TRACE("new_entries_bug", tout << "[qi:insert]\n";);
m_new_entries.push_back(entry(f, cost, generation));
}
void qi_queue::instantiate() {
svector<entry>::iterator it = m_new_entries.begin();
svector<entry>::iterator end = m_new_entries.end();
unsigned since_last_check = 0;
for (; it != end; ++it) {
entry & curr = *it;
fingerprint * f = curr.m_qb;
quantifier * qa = static_cast<quantifier*>(f->get_data());
if (curr.m_cost <= m_eager_cost_threshold) {
instantiate(curr);
}
else if (m_params.m_qi_promote_unsat && m_checker.is_unsat(qa->get_expr(), f->get_num_args(), f->get_args())) {
// do not delay instances that produce a conflict.
TRACE("qi_unsat", tout << "promoting instance that produces a conflict\n" << mk_pp(qa, m_manager) << "\n";);
instantiate(curr);
}
else {
TRACE("qi_queue", tout << "delaying quantifier instantiation... " << f << "\n" << mk_pp(qa, m_manager) << "\ncost: " << curr.m_cost << "\n";);
m_delayed_entries.push_back(curr);
}
// Periodically check if we didn't run out of time/memory.
if (since_last_check++ > 100) {
if (m_context.resource_limits_exceeded()) {
// verbose_stream() << "EXCEEDED...\n";
break;
}
since_last_check = 0;
}
}
m_new_entries.reset();
TRACE("new_entries_bug", tout << "[qi:instatiate]\n";);
}
void qi_queue::display_instance_profile(fingerprint * f, quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned proof_id, unsigned generation) {
#ifndef SMTCOMP
if (m_trace_stream != NULL) {
*m_trace_stream << "[instance] ";
#if 1
*m_trace_stream << static_cast<void*>(f);
#else
for (unsigned i = 0; i < num_bindings; i++) {
// I don't want to use mk_pp because it creates expressions for pretty printing.
// This nasty side-effect may change the behavior of Z3.
*m_trace_stream << " #" << bindings[i]->get_owner_id();
}
#endif
if (m_manager.proofs_enabled())
*m_trace_stream << " #" << proof_id;
*m_trace_stream << " ; " << generation;
*m_trace_stream << "\n";
}
#endif
}
void qi_queue::instantiate(entry & ent) {
fingerprint * f = ent.m_qb;
quantifier * q = static_cast<quantifier*>(f->get_data());
unsigned generation = ent.m_generation;
unsigned num_bindings = f->get_num_args();
enode * const * bindings = f->get_args();
ent.m_instantiated = true;
TRACE("qi_queue_profile",
tout << q->get_qid() << ", gen: " << generation;
for (unsigned i = 0; i < num_bindings; i++) tout << " #" << bindings[i]->get_owner_id();
tout << "\n";);
if (m_checker.is_sat(q->get_expr(), num_bindings, bindings)) {
TRACE("checker", tout << "instance already satisfied\n";);
return;
}
expr_ref instance(m_manager);
m_subst(q, num_bindings, bindings, instance);
TRACE("qi_queue", tout << "new instance:\n" << mk_pp(instance, m_manager) << "\n";);
TRACE("qi_queue_instance", tout << "new instance:\n" << mk_pp(instance, m_manager) << "\n";);
expr_ref s_instance(m_manager);
proof_ref pr(m_manager);
simplifier & simp = m_context.get_simplifier();
simp(instance, s_instance, pr);
TRACE("qi_queue_bug", tout << "new instance after simplification:\n" << mk_pp(s_instance, m_manager) << "\n";);
if (m_manager.is_true(s_instance)) {
TRACE("checker", tout << "reduced to true, before:\n" << mk_ll_pp(instance, m_manager););
#ifndef SMTCOMP
if (m_trace_stream != NULL)
*m_trace_stream << "[end-of-instance]\n";
#endif
return;
}
quantifier_stat * stat = m_qm.get_stat(q);
stat->inc_num_instances();
if (stat->get_num_instances() % m_params.m_qi_profile_freq == 0) {
m_qm.display_stats(verbose_stream(), q);
}
expr_ref lemma(m_manager);
if (m_manager.is_or(s_instance)) {
ptr_vector<expr> args;
args.push_back(m_manager.mk_not(q));
args.append(to_app(s_instance)->get_num_args(), to_app(s_instance)->get_args());
lemma = m_manager.mk_or(args.size(), args.c_ptr());
}
else if (m_manager.is_false(s_instance)) {
lemma = m_manager.mk_not(q);
}
else if (m_manager.is_true(s_instance)) {
lemma = s_instance;
}
else {
lemma = m_manager.mk_or(m_manager.mk_not(q), s_instance);
}
m_instances.push_back(lemma);
proof_ref pr1(m_manager);
unsigned proof_id = 0;
if (m_manager.proofs_enabled()) {
expr_ref_vector bindings_e(m_manager);
for (unsigned i = 0; i < num_bindings; ++i) {
bindings_e.push_back(bindings[i]->get_owner());
}
app * bare_lemma = m_manager.mk_or(m_manager.mk_not(q), instance);
proof * qi_pr = m_manager.mk_quant_inst(bare_lemma, num_bindings, bindings_e.c_ptr());
proof_id = qi_pr->get_id();
if (bare_lemma == lemma) {
pr1 = qi_pr;
}
else if (instance == s_instance) {
proof * rw = m_manager.mk_rewrite(bare_lemma, lemma);
pr1 = m_manager.mk_modus_ponens(qi_pr, rw);
}
else {
app * bare_s_lemma = m_manager.mk_or(m_manager.mk_not(q), s_instance);
proof * prs[1] = { pr.get() };
proof * cg = m_manager.mk_congruence(bare_lemma, bare_s_lemma, 1, prs);
proof * rw = m_manager.mk_rewrite(bare_s_lemma, lemma);
proof * tr = m_manager.mk_transitivity(cg, rw);
pr1 = m_manager.mk_modus_ponens(qi_pr, tr);
}
m_instances.push_back(pr1);
}
TRACE("qi_queue", tout << mk_pp(lemma, m_manager) << "\n#" << lemma->get_id() << ":=\n" << mk_ll_pp(lemma, m_manager););
m_stats.m_num_instances++;
unsigned gen = get_new_gen(q, generation, ent.m_cost);
display_instance_profile(f, q, num_bindings, bindings, proof_id, gen);
m_context.internalize_instance(lemma, pr1, gen);
TRACE_CODE({
static unsigned num_useless = 0;
if (m_manager.is_or(lemma)) {
app * n = to_app(lemma);
bool has_unassigned = false;
expr * true_child = 0;
for (unsigned i = 0; i < n->get_num_args(); i++) {
expr * arg = n->get_arg(i);
switch(m_context.get_assignment(arg)) {
case l_undef: has_unassigned = true; break;
case l_true: true_child = arg; break;
default:
break;
}
}
if (true_child && has_unassigned) {
TRACE("qi_queue_profile_detail", tout << "missed:\n" << mk_ll_pp(s_instance, m_manager) << "\n#" << true_child->get_id() << "\n";);
num_useless++;
if (num_useless % 10 == 0) {
TRACE("qi_queue_profile", tout << "num useless: " << num_useless << "\n";);
}
}
}
});
#ifndef SMTCOMP
if (m_trace_stream != NULL)
*m_trace_stream << "[end-of-instance]\n";
#endif
}
void qi_queue::push_scope() {
TRACE("new_entries_bug", tout << "[qi:push-scope]\n";);
m_scopes.push_back(scope());
SASSERT(m_new_entries.empty());
scope & s = m_scopes.back();
s.m_delayed_entries_lim = m_delayed_entries.size();
s.m_instances_lim = m_instances.size();
s.m_instantiated_trail_lim = m_instantiated_trail.size();
}
void qi_queue::pop_scope(unsigned num_scopes) {
unsigned new_lvl = m_scopes.size() - num_scopes;
scope & s = m_scopes[new_lvl];
unsigned old_sz = s.m_instantiated_trail_lim;
unsigned sz = m_instantiated_trail.size();
for (unsigned i = old_sz; i < sz; i++)
m_delayed_entries[m_instantiated_trail[i]].m_instantiated = false;
m_instantiated_trail.shrink(old_sz);
m_delayed_entries.shrink(s.m_delayed_entries_lim);
m_instances.shrink(s.m_instances_lim);
m_new_entries.reset();
m_scopes.shrink(new_lvl);
TRACE("new_entries_bug", tout << "[qi:pop-scope]\n";);
}
void qi_queue::reset() {
m_new_entries.reset();
m_delayed_entries.reset();
m_instances.reset();
m_scopes.reset();
}
void qi_queue::init_search_eh() {
m_subst.reset();
}
bool qi_queue::final_check_eh() {
TRACE("qi_queue", display_delayed_instances_stats(tout); tout << "lazy threshold: " << m_params.m_qi_lazy_threshold
<< ", scope_level: " << m_context.get_scope_level() << "\n";);
if (m_params.m_qi_conservative_final_check) {
bool init = false;
float min_cost;
unsigned sz = m_delayed_entries.size();
for (unsigned i = 0; i < sz; i++) {
entry & e = m_delayed_entries[i];
fingerprint * f = e.m_qb;
TRACE("qi_queue", tout << f << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";);
if (!e.m_instantiated && e.m_cost <= m_params.m_qi_lazy_threshold && (!init || e.m_cost < min_cost)) {
init = true;
min_cost = e.m_cost;
}
}
TRACE("qi_queue_min_cost", tout << "min_cost: " << min_cost << ", scope_level: " << m_context.get_scope_level() << "\n";);
bool result = true;
for (unsigned i = 0; i < sz; i++) {
entry & e = m_delayed_entries[i];
fingerprint * f = e.m_qb;
quantifier * qa = static_cast<quantifier*>(f->get_data());
TRACE("qi_queue", tout << f << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";);
if (!e.m_instantiated && e.m_cost <= min_cost) {
TRACE("qi_queue",
tout << "lazy quantifier instantiation...\n" << mk_pp(qa, m_manager) << "\ncost: " << e.m_cost << "\n";);
result = false;
m_instantiated_trail.push_back(i);
m_stats.m_num_lazy_instances++;
instantiate(e);
}
}
return result;
}
bool result = true;
for (unsigned i = 0; i < m_delayed_entries.size(); i++) {
entry & e = m_delayed_entries[i];
fingerprint * f = e.m_qb;
quantifier * qa = static_cast<quantifier*>(f->get_data());
TRACE("qi_queue", tout << f << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";);
if (!e.m_instantiated && e.m_cost <= m_params.m_qi_lazy_threshold) {
TRACE("qi_queue",
tout << "lazy quantifier instantiation...\n" << mk_pp(qa, m_manager) << "\ncost: " << e.m_cost << "\n";);
result = false;
m_instantiated_trail.push_back(i);
m_stats.m_num_lazy_instances++;
instantiate(e);
}
}
return result;
}
struct delayed_qa_info {
unsigned m_num;
float m_min_cost;
float m_max_cost;
delayed_qa_info():m_num(0), m_min_cost(0.0f), m_max_cost(0.0f) {}
};
void qi_queue::display_delayed_instances_stats(std::ostream & out) const {
obj_map<quantifier, delayed_qa_info> qa2info;
ptr_vector<quantifier> qas;
svector<entry>::const_iterator it = m_delayed_entries.begin();
svector<entry>::const_iterator end = m_delayed_entries.end();
for (; it != end; ++it) {
entry const & e = *it;
if (e.m_instantiated)
continue;
quantifier * qa = static_cast<quantifier*>(e.m_qb->get_data());
delayed_qa_info info;
if (qa2info.find(qa, info)) {
info.m_num++;
info.m_min_cost = std::min(info.m_min_cost, e.m_cost);
info.m_max_cost = std::min(info.m_max_cost, e.m_cost);
}
else {
qas.push_back(qa);
info.m_num = 1;
info.m_min_cost = e.m_cost;
info.m_max_cost = e.m_cost;
}
qa2info.insert(qa, info);
}
ptr_vector<quantifier>::iterator it2 = qas.begin();
ptr_vector<quantifier>::iterator end2 = qas.end();
for (; it2 != end2; ++it2) {
quantifier * qa = *it2;
delayed_qa_info info;
qa2info.find(qa, info);
out << qa->get_qid() << ": " << info.m_num << " [" << info.m_min_cost << ", " << info.m_max_cost << "]\n";
}
}
void qi_queue::get_min_max_costs(float & min, float & max) const {
min = 0.0f;
max = 0.0f;
bool found = false;
for (unsigned i = 0; i < m_delayed_entries.size(); i++) {
if (!m_delayed_entries[i].m_instantiated) {
float c = m_delayed_entries[i].m_cost;
if (found) {
min = std::min(min, c);
max = std::max(max, c);
}
else {
found = true;
min = c;
max = c;
}
}
}
}
void qi_queue::collect_statistics(::statistics & st) const {
st.update("quant instantiations", m_stats.m_num_instances);
st.update("lazy quant instantiations", m_stats.m_num_lazy_instances);
st.update("missed quant instantiations", m_delayed_entries.size());
float min, max;
get_min_max_costs(min, max);
st.update("min missed qa cost", min);
st.update("max missed qa cost", max);
#if 0
if (m_params.m_qi_profile) {
out << "missed/delayed quantifier instances:\n";
display_delayed_instances_stats(out);
}
#endif
}
};

105
src/smt/qi_queue.h Normal file
View file

@ -0,0 +1,105 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
qi_queue.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-15.
Revision History:
--*/
#ifndef _QI_QUEUE_H_
#define _QI_QUEUE_H_
#include"ast.h"
#include"smt_quantifier_stat.h"
#include"smt_checker.h"
#include"smt_quantifier.h"
#include"qi_params.h"
#include"fingerprints.h"
#include"cost_parser.h"
#include"cost_evaluator.h"
#include"cached_var_subst.h"
#include"statistics.h"
namespace smt {
class context;
struct qi_queue_stats {
unsigned m_num_instances, m_num_lazy_instances;
void reset() { memset(this, 0, sizeof(qi_queue_stats)); }
qi_queue_stats() { reset(); }
};
class qi_queue {
quantifier_manager & m_qm;
context & m_context;
ast_manager & m_manager;
qi_params & m_params;
qi_queue_stats m_stats;
checker m_checker;
expr_ref m_cost_function;
expr_ref m_new_gen_function;
cost_parser m_parser;
cost_evaluator m_evaluator;
cached_var_subst m_subst;
svector<float> m_vals;
double m_eager_cost_threshold;
std::ostream * m_trace_stream;
struct entry {
fingerprint * m_qb;
float m_cost;
unsigned m_generation:31;
unsigned m_instantiated:1;
entry(fingerprint * f, float c, unsigned g):m_qb(f), m_cost(c), m_generation(g), m_instantiated(false) {}
};
svector<entry> m_new_entries;
svector<entry> m_delayed_entries;
expr_ref_vector m_instances;
unsigned_vector m_instantiated_trail;
struct scope {
unsigned m_delayed_entries_lim;
unsigned m_instances_lim;
unsigned m_instantiated_trail_lim;
};
svector<scope> m_scopes;
void init_parser_vars();
quantifier_stat * set_values(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation, float cost);
float get_cost(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation);
unsigned get_new_gen(quantifier * q, unsigned generation, float cost);
void instantiate(entry & ent);
void get_min_max_costs(float & min, float & max) const;
void display_instance_profile(fingerprint * f, quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned proof_id, unsigned generation);
public:
qi_queue(quantifier_manager & qm, context & ctx, qi_params & params, std::ostream *trace);
~qi_queue();
void setup();
/**
\brief Insert a new quantifier in the queue, f contains the quantifier and bindings.
f->get_data() is the quantifier.
*/
void insert(fingerprint * f, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation);
void instantiate();
bool has_work() const { return !m_new_entries.empty(); }
void init_search_eh();
bool final_check_eh();
void push_scope();
void pop_scope(unsigned num_scopes);
void reset();
void display_delayed_instances_stats(std::ostream & out) const;
void collect_statistics(::statistics & st) const;
};
};
#endif /* _QI_QUEUE_H_ */

View file

@ -0,0 +1,127 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_almost_cg_table.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2009-03-06.
Revision History:
--*/
#include"smt_almost_cg_table.h"
namespace smt {
inline unsigned almost_cg_table::cg_hash::arg_hash(enode * n, unsigned idx) const {
enode * arg = n->get_arg(idx)->get_root();
if (arg == m_r1 || arg == m_r2)
return 17;
return arg->hash();
}
unsigned almost_cg_table::cg_hash::operator()(enode * n) const {
unsigned num_args = n->get_num_args();
unsigned kind_hash = n->get_decl_id();
if (num_args == 1)
return kind_hash;
unsigned a = 0x9e3779b9;
unsigned b = 0x9e3779b9;
unsigned c = 11;
switch (num_args) {
case 2:
a += kind_hash;
b += arg_hash(n, 0);
c += arg_hash(n, 1);
mix(a, b, c);
return c;
case 3:
a += kind_hash;
b += arg_hash(n, 0);
c += arg_hash(n, 1);
mix(a, b, c);
c += arg_hash(n, 1);
mix(a, b, c);
return c;
default:
while (num_args >= 3) {
num_args--;
a += arg_hash(n, num_args);
num_args--;
b += arg_hash(n, num_args);
num_args--;
c += arg_hash(n, num_args);
mix(a, b, c);
}
a += kind_hash;
switch (num_args) {
case 2:
b += arg_hash(n, 1);
__fallthrough;
case 1:
c += arg_hash(n, 0);
}
mix(a, b, c);
return c;
}
}
bool almost_cg_table::cg_eq::operator()(enode * n1, enode * n2) const {
if (n1->get_owner()->get_decl() != n2->get_owner()->get_decl())
return false;
unsigned num_args = n1->get_num_args();
if (num_args != n2->get_num_args())
return false;
for (unsigned j = 0; j < num_args; j++) {
enode * arg1 = n1->get_arg(j)->get_root();
enode * arg2 = n2->get_arg(j)->get_root();
if (arg1 == arg2)
continue;
if ((arg1 == m_r1 || arg1 == m_r2) &&
(arg2 == m_r1 || arg2 == m_r2))
continue;
return false;
}
return true;
}
almost_cg_table::almost_cg_table(enode * r1, enode * r2):
m_r1(r1),
m_r2(r2),
m_table(cg_hash(m_r1, m_r2), cg_eq(m_r1, m_r2)) {
}
void almost_cg_table::reset() {
m_region.reset();
m_table.reset();
}
void almost_cg_table::insert(enode * n) {
table::entry * entry = m_table.find_core(n);
if (entry == 0) {
list<enode*> * new_lst = new (m_region) list<enode*>(n, 0);
m_table.insert(n, new_lst);
}
else {
list<enode*> * new_lst = new (m_region) list<enode*>(n, entry->get_data().m_value);
entry->get_data().m_value = new_lst;
}
}
list<enode*> * almost_cg_table::find(enode * n) {
list<enode*> * result = 0;
m_table.find(n, result);
return result;
}
};

View file

@ -0,0 +1,72 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_almost_cg_table.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2009-03-06.
Revision History:
--*/
#ifndef _SMT_ALMOST_CG_TABLE_H_
#define _SMT_ALMOST_CG_TABLE_H_
#include"smt_enode.h"
#include"map.h"
namespace smt {
/**
\brief An index for detecting 'almost' congruences.
We say (f t_1 ... t_n) is almost congruent to (f s_1 ... s_n) with respect to (r1,r2) iff
for all j in [1,n] j t_j = s_j or (t_j = r1 and s_j = r1) or (t_j = r1 and s_j = r2) or (t_j = r2 and s_j = r1) or (t_j = r2 and s_j = r2)
This index is used to speedup is_ext_diseq.
*/
class almost_cg_table {
struct cg_hash {
enode * & m_r1;
enode * & m_r2;
unsigned arg_hash(enode * n, unsigned idx) const;
public:
cg_hash(enode * & r1, enode * & r2):m_r1(r1), m_r2(r2) {}
unsigned operator()(enode * n) const;
};
struct cg_eq {
enode * & m_r1;
enode * & m_r2;
public:
cg_eq(enode * & r1, enode * & r2):m_r1(r1), m_r2(r2) {}
bool operator()(enode * n1, enode * n2) const;
};
typedef map<enode *, list<enode*> *, cg_hash, cg_eq> table;
region m_region;
enode * m_r1;
enode * m_r2;
table m_table;
public:
almost_cg_table(enode * r1 = 0, enode * r2 = 0);
void reset(enode * r1, enode * r2) { m_r1 = r1->get_root(); m_r2 = r2->get_root(); reset(); }
void reset();
void insert(enode *);
void erase(enode * n) { m_table.erase(n); }
list<enode*> * find(enode *);
bool empty() const { return m_table.empty(); }
};
};
#endif /* _SMT_ALMOST_CG_TABLE_H_ */

View file

@ -0,0 +1,100 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_b_justification.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#ifndef _SMT_B_JUSTIFICATION_H_
#define _SMT_B_JUSTIFICATION_H_
#include"smt_literal.h"
#include"smt_clause.h"
namespace smt {
/**
\brief Proof like object used to track dependencies of boolean propagation.
The idea is to reduce the cost of dependency tracking for the most common
justifications used during boolean propagation: unit propagation
*/
class b_justification {
void * m_data;
public:
enum kind {
CLAUSE, //!< clause of arbitrary size
BIN_CLAUSE, //!< binary clause
AXIOM, //!< no justification, it is only use if proof generation is disabled
JUSTIFICATION //!< fallback
};
b_justification():
m_data(reinterpret_cast<void*>(static_cast<size_t>(AXIOM))) {}
b_justification(b_justification const & source):
m_data(source.m_data) {
}
explicit b_justification(clause * c):
m_data(TAG(void*, c, CLAUSE)) {
}
explicit b_justification(literal l):
m_data(BOXTAGINT(void*, l.index(), BIN_CLAUSE)) {
}
explicit b_justification(justification * js):
m_data(TAG(void*, js, JUSTIFICATION)) {
SASSERT(js);
}
kind get_kind() const {
return static_cast<kind>(GET_TAG(m_data));
}
clause * get_clause() const {
SASSERT(get_kind() == CLAUSE);
return UNTAG(clause*, m_data);
}
justification * get_justification() const {
SASSERT(get_kind() == JUSTIFICATION);
return UNTAG(justification*, m_data);
}
literal get_literal() const {
SASSERT(get_kind() == BIN_CLAUSE);
return to_literal(UNBOXINT(m_data));
}
bool operator==(b_justification const & other) const {
return m_data == other.m_data;
}
bool operator!=(b_justification const & other) const {
return !operator==(other);
}
static b_justification mk_axiom() {
return b_justification();
}
};
const b_justification null_b_justification(static_cast<clause*>(0));
typedef std::pair<literal, b_justification> justified_literal;
};
#endif /* _SMT_B_JUSTIFICATION_H_ */

128
src/smt/smt_bool_var_data.h Normal file
View file

@ -0,0 +1,128 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_bool_var_data.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-25.
Revision History:
--*/
#ifndef _SMT_BOOL_VAR_DATA_H_
#define _SMT_BOOL_VAR_DATA_H_
#include"smt_b_justification.h"
namespace smt {
struct bool_var_data {
b_justification m_justification;
unsigned m_scope_lvl:24; //!< scope level of when the variable was assigned.
unsigned m_mark:1;
unsigned m_assumption:1;
unsigned m_phase_available:1;
unsigned m_phase:1;
private:
unsigned m_eq:1;
unsigned m_true_first:1; //!< If True, when case splitting try the true phase first. Otherwise, you default phase selection heuristic.
unsigned m_enode:1; //!< has enode associated with it.
unsigned m_quantifier:1; //!< true if bool var is attached to a quantifier
unsigned m_iscope_lvl:23; //!< scope level of when the variable was internalized.
unsigned m_atom:1; //!< logical or of m_eq, m_enode, m_quantifier, and m_notify_theory != 0
unsigned m_notify_theory:8;
void update_atom_flag() {
m_atom = m_eq || m_notify_theory != 0 || m_quantifier || m_enode;
}
public:
unsigned get_intern_level() const { return m_iscope_lvl; }
bool is_atom() const { return m_atom; }
theory_id get_theory() const {
return m_notify_theory == 0 ? null_theory_id : static_cast<theory_id>(m_notify_theory);
}
bool is_theory_atom() const { return m_notify_theory != 0; }
void set_notify_theory(theory_id thid) {
SASSERT(thid > 0 && thid <= 255);
m_notify_theory = thid;
m_atom = true;
}
void reset_notify_theory() {
m_notify_theory = 0;
update_atom_flag();
}
bool is_enode() const { return m_enode; }
void set_enode_flag() {
m_enode = true;
m_atom = true;
}
void reset_enode_flag() {
m_enode = false;
update_atom_flag();
}
bool is_quantifier() const { return m_quantifier; }
void set_quantifier_flag() {
m_quantifier = true;
m_atom = true;
}
bool is_eq() const { return m_eq; }
void set_eq_flag() {
m_eq = true;
m_atom = true;
}
void reset_eq_flag() {
m_eq = false;
update_atom_flag();
}
bool try_true_first() const { return m_true_first; }
void set_true_first_flag() {
m_true_first = true;
}
void reset_true_first_flag() {
m_true_first = false;
}
void init(unsigned iscope_lvl) {
m_justification = null_b_justification;
m_scope_lvl = 0;
m_mark = false;
m_assumption = false;
m_phase_available = false;
m_phase = false;
m_iscope_lvl = iscope_lvl;
m_eq = false;
m_true_first = false;
m_notify_theory = 0;
m_enode = false;
m_quantifier = false;
m_atom = false;
}
};
};
#endif /* _SMT_BOOL_VAR_DATA_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_case_split_queue.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2009-01-20.
Revision History:
--*/
#ifndef _SMT_CASE_SPLIT_QUEUE_H_
#define _SMT_CASE_SPLIT_QUEUE_H_
#include"smt_types.h"
#include"heap.h"
#include"smt_params.h"
namespace smt {
class context;
/**
\brief Abstract case split queue.
*/
class case_split_queue {
public:
virtual void activity_increased_eh(bool_var v) = 0;
virtual void mk_var_eh(bool_var v) = 0;
virtual void del_var_eh(bool_var v) = 0;
virtual void assign_lit_eh(literal l) {}
virtual void unassign_var_eh(bool_var v) = 0;
virtual void relevant_eh(expr * n) = 0;
virtual void init_search_eh() = 0;
virtual void end_search_eh() = 0;
virtual void internalize_instance_eh(expr * e, unsigned gen) {}
virtual void reset() = 0;
virtual void push_scope() = 0;
virtual void pop_scope(unsigned num_scopes) = 0;
virtual void next_case_split(bool_var & next, lbool & phase) = 0;
virtual void display(std::ostream & out) = 0;
virtual ~case_split_queue() {}
};
case_split_queue * mk_case_split_queue(context & ctx, front_end_params & p);
};
#endif /* _SMT_CASE_SPLIT_QUEUE_H_ */

250
src/smt/smt_cg_table.cpp Normal file
View file

@ -0,0 +1,250 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_cg_table.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#include"smt_cg_table.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
namespace smt {
#if 0
unsigned cg_table::cg_hash::operator()(enode * n) const {
if (n->is_commutative()) {
return combine_hash(n->get_decl_id(),
n->get_arg(0)->get_root()->hash() *
n->get_arg(1)->get_root()->hash());
}
else {
unsigned num = n->get_num_args();
switch (num) {
case 0: UNREACHABLE(); return 0;
case 1:
return combine_hash(n->get_decl_id(), n->get_arg(0)->get_root()->hash());
case 2: {
unsigned a = n->get_decl_id();
unsigned b = n->get_arg(0)->get_root()->hash();
unsigned c = n->get_arg(1)->get_root()->hash();
mix(a,b,c);
return c;
}
default:
return get_composite_hash<enode *, cg_khasher, cg_chasher>(n, n->get_num_args(), m_khasher, m_chasher);
}
}
}
bool cg_table::cg_eq::operator()(enode * n1, enode * n2) const {
#if 0
static unsigned counter = 0;
static unsigned failed = 0;
bool r = congruent(n1, n2, m_commutativity);
if (!r)
failed++;
counter++;
if (counter % 100000 == 0)
std::cout << "cg_eq: " << counter << " " << failed << "\n";
return r;
#else
return congruent(n1, n2, m_commutativity);
#endif
}
cg_table::cg_table(ast_manager & m):
m_manager(m),
m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, cg_hash(), cg_eq(m_commutativity)) {
}
void cg_table::display(std::ostream & out) const {
out << "congruence table:\n";
table::iterator it = m_table.begin();
table::iterator end = m_table.end();
for (; it != end; ++it) {
enode * n = *it;
out << mk_pp(n->get_owner(), m_manager) << "\n";
}
}
void cg_table::display_compact(std::ostream & out) const {
if (!m_table.empty()) {
out << "congruence table:\n";
table::iterator it = m_table.begin();
table::iterator end = m_table.end();
for (; it != end; ++it) {
enode * n = *it;
out << "#" << n->get_owner()->get_id() << " ";
}
out << "\n";
}
}
#ifdef Z3DEBUG
bool cg_table::check_invariant() const {
table::iterator it = m_table.begin();
table::iterator end = m_table.end();
for (; it != end; ++it) {
enode * n = *it;
CTRACE("cg_table", !contains_ptr(n), tout << "#" << n->get_owner_id() << "\n";);
SASSERT(contains_ptr(n));
}
return true;
}
#endif
#else
// one table per func_decl implementation
unsigned cg_table::cg_hash::operator()(enode * n) const {
SASSERT(n->get_decl()->is_flat_associative() || n->get_num_args() >= 3);
unsigned a, b, c;
a = b = 0x9e3779b9;
c = 11;
unsigned i = n->get_num_args();
while (i >= 3) {
i--;
a += n->get_arg(i)->get_root()->hash();
i--;
b += n->get_arg(i)->get_root()->hash();
i--;
c += n->get_arg(i)->get_root()->hash();
mix(a, b, c);
}
switch (i) {
case 2:
b += n->get_arg(1)->get_root()->hash();
__fallthrough;
case 1:
c += n->get_arg(0)->get_root()->hash();
}
mix(a, b, c);
return c;
}
bool cg_table::cg_eq::operator()(enode * n1, enode * n2) const {
SASSERT(n1->get_num_args() == n2->get_num_args());
SASSERT(n1->get_decl() == n2->get_decl());
unsigned num = n1->get_num_args();
for (unsigned i = 0; i < num; i++)
if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root())
return false;
return true;
}
cg_table::cg_table(ast_manager & m):
m_manager(m) {
}
cg_table::~cg_table() {
reset();
}
void * cg_table::mk_table_for(func_decl * d) {
void * r;
SASSERT(d->get_arity() >= 1);
switch (d->get_arity()) {
case 1:
r = TAG(void*, alloc(unary_table), UNARY);
SASSERT(GET_TAG(r) == UNARY);
return r;
case 2:
if (d->is_flat_associative()) {
// applications of declarations that are flat-assoc (e.g., +) may have many arguments.
r = TAG(void*, alloc(table), NARY);
SASSERT(GET_TAG(r) == NARY);
return r;
}
else if (d->is_commutative()) {
r = TAG(void*, alloc(comm_table, cg_comm_hash(), cg_comm_eq(m_commutativity)), BINARY_COMM);
SASSERT(GET_TAG(r) == BINARY_COMM);
return r;
}
else {
r = TAG(void*, alloc(binary_table), BINARY);
SASSERT(GET_TAG(r) == BINARY);
return r;
}
default:
r = TAG(void*, alloc(table), NARY);
SASSERT(GET_TAG(r) == NARY);
return r;
}
}
unsigned cg_table::set_func_decl_id(enode * n) {
func_decl * f = n->get_decl();
unsigned tid;
if (!m_func_decl2id.find(f, tid)) {
tid = m_tables.size();
m_func_decl2id.insert(f, tid);
m_manager.inc_ref(f);
SASSERT(tid <= m_tables.size());
m_tables.push_back(mk_table_for(f));
}
SASSERT(tid < m_tables.size());
n->set_func_decl_id(tid);
DEBUG_CODE({
unsigned tid_prime;
SASSERT(m_func_decl2id.find(n->get_decl(), tid_prime) && tid == tid_prime);
});
return tid;
}
void cg_table::reset() {
ptr_vector<void>::iterator it = m_tables.begin();
ptr_vector<void>::iterator end = m_tables.end();
for (; it != end; ++it) {
void * t = *it;
switch (GET_TAG(t)) {
case UNARY:
dealloc(UNTAG(unary_table*, t));
break;
case BINARY:
dealloc(UNTAG(binary_table*, t));
break;
case BINARY_COMM:
dealloc(UNTAG(comm_table*, t));
break;
case NARY:
dealloc(UNTAG(table*, t));
break;
}
}
m_tables.reset();
obj_map<func_decl, unsigned>::iterator it2 = m_func_decl2id.begin();
obj_map<func_decl, unsigned>::iterator end2 = m_func_decl2id.end();
for (; it2 != end2; ++it2)
m_manager.dec_ref(it2->m_key);
m_func_decl2id.reset();
}
void cg_table::display(std::ostream & out) const {
}
void cg_table::display_compact(std::ostream & out) const {
}
#ifdef Z3DEBUG
bool cg_table::check_invariant() const {
return true;
}
#endif
#endif
};

354
src/smt/smt_cg_table.h Normal file
View file

@ -0,0 +1,354 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_cg_table.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#ifndef _SMT_CG_TABLE_H_
#define _SMT_CG_TABLE_H_
#include"smt_enode.h"
#include"hashtable.h"
#include"chashtable.h"
namespace smt {
typedef std::pair<enode *, bool> enode_bool_pair;
#if 0
/**
\brief Congruence table.
*/
class cg_table {
struct cg_khasher {
unsigned operator()(enode const * n) const { return n->get_decl_id(); }
};
struct cg_chasher {
unsigned operator()(enode const * n, unsigned idx) const {
return n->get_arg(idx)->get_root()->hash();
}
};
struct cg_hash {
cg_khasher m_khasher;
cg_chasher m_chasher;
public:
unsigned operator()(enode * n) const;
};
struct cg_eq {
bool & m_commutativity;
cg_eq(bool & comm):m_commutativity(comm) {}
bool operator()(enode * n1, enode * n2) const;
};
typedef ptr_hashtable<enode, cg_hash, cg_eq> table;
ast_manager & m_manager;
bool m_commutativity; //!< true if the last found congruence used commutativity
table m_table;
public:
cg_table(ast_manager & m);
/**
\brief Try to insert n into the table. If the table already
contains an element n' congruent to n, then do nothing and
return n' and a boolean indicating whether n and n' are congruence
modulo commutativity, otherwise insert n and return (n,false).
*/
enode_bool_pair insert(enode * n) {
// it doesn't make sense to insert a constant.
SASSERT(n->get_num_args() > 0);
m_commutativity = false;
enode * n_prime = m_table.insert_if_not_there(n);
SASSERT(contains(n));
return enode_bool_pair(n_prime, m_commutativity);
}
void erase(enode * n) {
SASSERT(n->get_num_args() > 0);
m_table.erase(n);
SASSERT(!contains(n));
}
bool contains(enode * n) const {
return m_table.contains(n);
}
enode * find(enode * n) const {
enode * r = 0;
return m_table.find(n, r) ? r : 0;
}
bool contains_ptr(enode * n) const {
enode * n_prime;
return m_table.find(n, n_prime) && n == n_prime;
}
void reset() {
m_table.reset();
}
void display(std::ostream & out) const;
void display_compact(std::ostream & out) const;
#ifdef Z3DEBUG
bool check_invariant() const;
#endif
};
#else
// one table per function symbol
/**
\brief Congruence table.
*/
class cg_table {
struct cg_unary_hash {
unsigned operator()(enode * n) const {
SASSERT(n->get_num_args() == 1);
return n->get_arg(0)->get_root()->hash();
}
};
struct cg_unary_eq {
bool operator()(enode * n1, enode * n2) const {
SASSERT(n1->get_num_args() == 1);
SASSERT(n2->get_num_args() == 1);
SASSERT(n1->get_decl() == n2->get_decl());
return n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root();
}
};
typedef chashtable<enode *, cg_unary_hash, cg_unary_eq> unary_table;
struct cg_binary_hash {
unsigned operator()(enode * n) const {
SASSERT(n->get_num_args() == 2);
// too many collisions
// unsigned r = 17 + n->get_arg(0)->get_root()->hash();
// return r * 31 + n->get_arg(1)->get_root()->hash();
return combine_hash(n->get_arg(0)->get_root()->hash(), n->get_arg(1)->get_root()->hash());
}
};
struct cg_binary_eq {
bool operator()(enode * n1, enode * n2) const {
SASSERT(n1->get_num_args() == 2);
SASSERT(n2->get_num_args() == 2);
SASSERT(n1->get_decl() == n2->get_decl());
#if 1
return
n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root() &&
n1->get_arg(1)->get_root() == n2->get_arg(1)->get_root();
#else
bool r =
n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root() &&
n1->get_arg(1)->get_root() == n2->get_arg(1)->get_root();
static unsigned counter = 0;
static unsigned failed = 0;
if (!r)
failed++;
counter++;
if (counter % 100000 == 0)
std::cerr << "[cg_eq] " << counter << " " << failed << "\n";
return r;
#endif
}
};
typedef chashtable<enode*, cg_binary_hash, cg_binary_eq> binary_table;
struct cg_comm_hash {
unsigned operator()(enode * n) const {
SASSERT(n->get_num_args() == 2);
unsigned h1 = n->get_arg(0)->get_root()->hash();
unsigned h2 = n->get_arg(1)->get_root()->hash();
if (h1 > h2)
std::swap(h1, h2);
return hash_u((h1 << 16) | (h2 & 0xFFFF));
}
};
struct cg_comm_eq {
bool & m_commutativity;
cg_comm_eq(bool & c):m_commutativity(c) {}
bool operator()(enode * n1, enode * n2) const {
SASSERT(n1->get_num_args() == 2);
SASSERT(n2->get_num_args() == 2);
SASSERT(n1->get_decl() == n2->get_decl());
enode * c1_1 = n1->get_arg(0)->get_root();
enode * c1_2 = n1->get_arg(1)->get_root();
enode * c2_1 = n2->get_arg(0)->get_root();
enode * c2_2 = n2->get_arg(1)->get_root();
if (c1_1 == c2_1 && c1_2 == c2_2) {
return true;
}
if (c1_1 == c2_2 && c1_2 == c2_1) {
m_commutativity = true;
return true;
}
return false;
}
};
typedef chashtable<enode*, cg_comm_hash, cg_comm_eq> comm_table;
struct cg_hash {
unsigned operator()(enode * n) const;
};
struct cg_eq {
bool operator()(enode * n1, enode * n2) const;
};
typedef chashtable<enode*, cg_hash, cg_eq> table;
ast_manager & m_manager;
bool m_commutativity; //!< true if the last found congruence used commutativity
ptr_vector<void> m_tables;
obj_map<func_decl, unsigned> m_func_decl2id;
enum table_kind {
UNARY,
BINARY,
BINARY_COMM,
NARY
};
void * mk_table_for(func_decl * d);
unsigned set_func_decl_id(enode * n);
void * get_table(enode * n) {
unsigned tid = n->get_func_decl_id();
if (tid == UINT_MAX)
tid = set_func_decl_id(n);
SASSERT(tid < m_tables.size());
return m_tables[tid];
}
public:
cg_table(ast_manager & m);
~cg_table();
/**
\brief Try to insert n into the table. If the table already
contains an element n' congruent to n, then do nothing and
return n' and a boolean indicating whether n and n' are congruence
modulo commutativity, otherwise insert n and return (n,false).
*/
enode_bool_pair insert(enode * n) {
// it doesn't make sense to insert a constant.
SASSERT(n->get_num_args() > 0);
enode * n_prime;
void * t = get_table(n);
switch (static_cast<table_kind>(GET_TAG(t))) {
case UNARY:
n_prime = UNTAG(unary_table*, t)->insert_if_not_there(n);
return enode_bool_pair(n_prime, false);
case BINARY:
n_prime = UNTAG(binary_table*, t)->insert_if_not_there(n);
return enode_bool_pair(n_prime, false);
case BINARY_COMM:
m_commutativity = false;
n_prime = UNTAG(comm_table*, t)->insert_if_not_there(n);
return enode_bool_pair(n_prime, m_commutativity);
default:
n_prime = UNTAG(table*, t)->insert_if_not_there(n);
return enode_bool_pair(n_prime, false);
}
}
void erase(enode * n) {
SASSERT(n->get_num_args() > 0);
void * t = get_table(n);
switch (static_cast<table_kind>(GET_TAG(t))) {
case UNARY:
UNTAG(unary_table*, t)->erase(n);
break;
case BINARY:
UNTAG(binary_table*, t)->erase(n);
break;
case BINARY_COMM:
UNTAG(comm_table*, t)->erase(n);
break;
default:
UNTAG(table*, t)->erase(n);
break;
}
}
bool contains(enode * n) const {
SASSERT(n->get_num_args() > 0);
void * t = const_cast<cg_table*>(this)->get_table(n);
switch (static_cast<table_kind>(GET_TAG(t))) {
case UNARY:
return UNTAG(unary_table*, t)->contains(n);
case BINARY:
return UNTAG(binary_table*, t)->contains(n);
case BINARY_COMM:
return UNTAG(comm_table*, t)->contains(n);
default:
return UNTAG(table*, t)->contains(n);
}
}
enode * find(enode * n) const {
SASSERT(n->get_num_args() > 0);
enode * r = 0;
void * t = const_cast<cg_table*>(this)->get_table(n);
switch (static_cast<table_kind>(GET_TAG(t))) {
case UNARY:
return UNTAG(unary_table*, t)->find(n, r) ? r : 0;
case BINARY:
return UNTAG(binary_table*, t)->find(n, r) ? r : 0;
case BINARY_COMM:
return UNTAG(comm_table*, t)->find(n, r) ? r : 0;
default:
return UNTAG(table*, t)->find(n, r) ? r : 0;
}
}
bool contains_ptr(enode * n) const {
enode * r;
SASSERT(n->get_num_args() > 0);
void * t = const_cast<cg_table*>(this)->get_table(n);
switch (static_cast<table_kind>(GET_TAG(t))) {
case UNARY:
return UNTAG(unary_table*, t)->find(n, r) && n == r;
case BINARY:
return UNTAG(binary_table*, t)->find(n, r) && n == r;
case BINARY_COMM:
return UNTAG(comm_table*, t)->find(n, r) && n == r;
default:
return UNTAG(table*, t)->find(n, r) && n == r;
}
}
void reset();
void display(std::ostream & out) const;
void display_compact(std::ostream & out) const;
#ifdef Z3DEBUG
bool check_invariant() const;
#endif
};
#endif
};
#endif /* _SMT_CG_TABLE_H_ */

188
src/smt/smt_checker.cpp Normal file
View file

@ -0,0 +1,188 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_checker.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-20.
Revision History:
--*/
#include"smt_context.h"
#include"smt_checker.h"
#include"ast_ll_pp.h"
namespace smt {
bool checker::all_args(app * a, bool is_true) {
unsigned num_args = a->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
if (!check(a->get_arg(i), is_true))
return false;
}
return true;
}
bool checker::any_arg(app * a, bool is_true) {
unsigned num_args = a->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
if (check(a->get_arg(i), is_true))
return true;
}
return false;
}
bool checker::check_core(expr * n, bool is_true) {
SASSERT(m_manager.is_bool(n));
if (m_context.b_internalized(n) && m_context.is_relevant(n)) {
lbool val = m_context.get_assignment(n);
return val != l_undef && is_true == (val == l_true);
}
if (!is_app(n))
return false;
app * a = to_app(n);
if (a->get_family_id() == m_manager.get_basic_family_id()) {
switch (a->get_decl_kind()) {
case OP_TRUE:
return is_true;
case OP_FALSE:
return !is_true;
case OP_NOT:
return check(a->get_arg(0), !is_true);
case OP_OR:
return is_true ? any_arg(a, true) : all_args(a, false);
case OP_AND:
return is_true ? all_args(a, true) : any_arg(a, false);
case OP_IFF:
if (is_true) {
return
(check(a->get_arg(0), true) &&
check(a->get_arg(1), true)) ||
(check(a->get_arg(0), false) &&
check(a->get_arg(1), false));
}
else {
return
(check(a->get_arg(0), true) &&
check(a->get_arg(1), false)) ||
(check(a->get_arg(0), false) &&
check(a->get_arg(1), true));
}
case OP_ITE: {
if (m_context.lit_internalized(a->get_arg(0)) && m_context.is_relevant(a->get_arg(0))) {
switch (m_context.get_assignment(a->get_arg(0))) {
case l_false: return check(a->get_arg(2), is_true);
case l_undef: return false;
case l_true: return check(a->get_arg(1), is_true);
}
}
return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true);
}
case OP_EQ: {
enode * lhs = get_enode_eq_to(a->get_arg(0));
enode * rhs = get_enode_eq_to(a->get_arg(1));
if (lhs && rhs && m_context.is_relevant(lhs) && m_context.is_relevant(rhs)) {
if (is_true && lhs->get_root() == rhs->get_root())
return true;
// if (!is_true && m_context.is_ext_diseq(lhs, rhs, 2))
if (!is_true && m_context.is_diseq(lhs, rhs))
return true;
}
return false;
}
default:
break;
}
}
enode * e = get_enode_eq_to(a);
if (e && e->is_bool() && m_context.is_relevant(e)) {
lbool val = m_context.get_assignment(e->get_owner());
return val != l_undef && is_true == (val == l_true);
}
return false;
}
bool checker::check(expr * n, bool is_true) {
bool r;
if (n->get_ref_count() > 1 && m_is_true_cache[is_true].find(n, r))
return r;
r = check_core(n, is_true);
if (n->get_ref_count() > 1)
m_is_true_cache[is_true].insert(n, r);
return r;
}
enode * checker::get_enode_eq_to_core(app * n) {
ptr_buffer<enode> buffer;
unsigned num = n->get_num_args();
for (unsigned i = 0; i < num; i++) {
enode * arg = get_enode_eq_to(n->get_arg(i));
if (arg == 0)
return 0;
buffer.push_back(arg);
}
enode * e = m_context.get_enode_eq_to(n->get_decl(), num, buffer.c_ptr());
if (e == 0)
return 0;
return m_context.is_relevant(e) ? e : 0;
}
enode * checker::get_enode_eq_to(expr * n) {
if (is_var(n)) {
unsigned idx = to_var(n)->get_idx();
if (idx >= m_num_bindings)
return 0;
return m_bindings[m_num_bindings - idx - 1];
}
if (m_context.e_internalized(n) && m_context.is_relevant(n))
return m_context.get_enode(n);
if (!is_app(n) || to_app(n)->get_num_args() == 0)
return 0;
enode * r = 0;
if (n->get_ref_count() > 1 && m_to_enode_cache.find(n, r))
return r;
r = get_enode_eq_to_core(to_app(n));
if (n->get_ref_count() > 1)
m_to_enode_cache.insert(n, r);
return r;
}
bool checker::is_sat(expr * n, unsigned num_bindings, enode * const * bindings) {
flet<unsigned> l1(m_num_bindings, num_bindings);
flet<enode * const *> l2(m_bindings, bindings);
bool r = check(n, true);
m_is_true_cache[0].reset();
m_is_true_cache[1].reset();
m_to_enode_cache.reset();
return r;
}
bool checker::is_unsat(expr * n, unsigned num_bindings, enode * const * bindings) {
flet<unsigned> l1(m_num_bindings, num_bindings);
flet<enode * const *> l2(m_bindings, bindings);
bool r = check(n, false);
m_is_true_cache[0].reset();
m_is_true_cache[1].reset();
m_to_enode_cache.reset();
return r;
}
checker::checker(context & c):
m_context(c),
m_manager(c.get_manager()),
m_num_bindings(0),
m_bindings(0) {
}
};

57
src/smt/smt_checker.h Normal file
View file

@ -0,0 +1,57 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_checker.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-20.
Revision History:
--*/
#ifndef _SMT_CHECKER_H_
#define _SMT_CHECKER_H_
#include"ast.h"
#include"obj_hashtable.h"
namespace smt {
class context;
class checker {
typedef obj_map<expr, bool> expr2bool;
typedef obj_map<expr, enode *> expr2enode;
context & m_context;
ast_manager & m_manager;
expr2bool m_is_true_cache[2];
expr2enode m_to_enode_cache;
unsigned m_num_bindings;
enode * const * m_bindings;
bool all_args(app * a, bool is_true);
bool any_arg(app * a, bool is_true);
bool check_core(expr * n, bool is_true);
bool check(expr * n, bool is_true);
enode * get_enode_eq_to_core(app * n);
enode * get_enode_eq_to(expr * n);
public:
checker(context & c);
bool is_sat(expr * n, unsigned num_bindings = 0, enode * const * bindings = 0);
bool is_unsat(expr * n, unsigned num_bindings = 0, enode * const * bindings = 0);
};
};
#endif /* _SMT_CHECKER_H_ */

118
src/smt/smt_clause.cpp Normal file
View file

@ -0,0 +1,118 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_clause.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#include"smt_clause.h"
#include"smt_justification.h"
#include"ast_ll_pp.h"
namespace smt {
/**
\brief Create a new clause.
bool_var2expr_map is a mapping from bool_var -> expr, it is only used if save_atoms == true.
*/
clause * clause::mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js,
clause_del_eh * del_eh, bool save_atoms, expr * const * bool_var2expr_map) {
SASSERT(k == CLS_AUX || js == 0 || !js->in_region());
SASSERT(num_lits >= 2);
unsigned sz = get_obj_size(num_lits, k, save_atoms, del_eh != 0, js != 0);
void * mem = m.get_allocator().allocate(sz);
clause * cls = new (mem) clause();
cls->m_num_literals = num_lits;
cls->m_capacity = num_lits;
cls->m_kind = k;
cls->m_reinit = save_atoms;
cls->m_reinternalize_atoms = save_atoms;
cls->m_has_atoms = save_atoms;
cls->m_has_del_eh = del_eh != 0;
cls->m_has_justification = js != 0;
cls->m_deleted = false;
SASSERT(!m.proofs_enabled() || js != 0);
memcpy(cls->m_lits, lits, sizeof(literal) * num_lits);
if (cls->is_lemma())
cls->set_activity(1);
if (del_eh)
*(const_cast<clause_del_eh **>(cls->get_del_eh_addr())) = del_eh;
if (js)
*(const_cast<justification **>(cls->get_justification_addr())) = js;
if (save_atoms) {
for (unsigned i = 0; i < num_lits; i++) {
expr * atom = bool_var2expr_map[lits[i].var()];
m.inc_ref(atom);
const_cast<expr**>(cls->get_atoms_addr())[i] = TAG(expr*, atom, lits[i].sign());
}
}
DEBUG_CODE({
SASSERT(!cls->is_lemma() || cls->get_activity() == 1);
SASSERT(cls->get_del_eh() == del_eh);
SASSERT(cls->get_justification() == js);
for (unsigned i = 0; i < num_lits; i++) {
SASSERT(cls->get_literal(i) == lits[i]);
SASSERT(!save_atoms || cls->get_atom(i) == bool_var2expr_map[lits[i].var()]);
}});
return cls;
}
void clause::deallocate(ast_manager & m) {
clause_del_eh * del_eh = get_del_eh();
if (del_eh)
(*del_eh)(m, this);
if (is_lemma() && m_has_justification) {
justification * js = get_justification();
if (js) {
SASSERT(!js->in_region());
js->del_eh(m);
dealloc(js);
}
}
unsigned num_atoms = get_num_atoms();
for (unsigned i = 0; i < num_atoms; i++) {
SASSERT(m_reinit || get_atom(i) == 0);
m.dec_ref(get_atom(i));
}
m.get_allocator().deallocate(get_obj_size(m_capacity, get_kind(), m_has_atoms, m_has_del_eh, m_has_justification), this);
}
void clause::release_atoms(ast_manager & m) {
unsigned num_atoms = get_num_atoms();
for (unsigned i = 0; i < num_atoms; i++) {
m.dec_ref(get_atom(i));
const_cast<expr**>(get_atoms_addr())[i] = 0;
}
}
void clause::display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const {
out << "(clause";
for (unsigned i = 0; i < m_num_literals; i++) {
out << " ";
m_lits[i].display(out, m, bool_var2expr_map);
}
out << ")";
}
void clause::display_compact(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const {
out << "(clause";
for (unsigned i = 0; i < m_num_literals; i++) {
out << " ";
m_lits[i].display_compact(out, bool_var2expr_map);
}
out << ")";
}
};

258
src/smt/smt_clause.h Normal file
View file

@ -0,0 +1,258 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_clause.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-18.
Revision History:
--*/
#ifndef _SMT_CLAUSE_H_
#define _SMT_CLAUSE_H_
#include"ast.h"
#include"smt_literal.h"
#include"tptr.h"
#include"obj_hashtable.h"
#include"smt_justification.h"
namespace smt {
class clause;
/**
\brief Abstract functor: clause deletion event handler.
*/
class clause_del_eh {
public:
virtual ~clause_del_eh() {}
virtual void operator()(ast_manager & m, clause * cls) = 0;
};
enum clause_kind {
CLS_AUX,
CLS_LEARNED,
CLS_AUX_LEMMA
};
/**
\brief A SMT clause.
A clause has several optional fields, I store space for them only if they are actually used.
*/
class clause {
unsigned m_num_literals;
unsigned m_capacity:24; //!< some of the clause literals can be simplified and removed, this field contains the original number of literals (used for GC).
unsigned m_kind:2; //!< kind
unsigned m_reinit:1; //!< true if the clause is in the reinit stack (only for learned clauses and aux_lemmas)
unsigned m_reinternalize_atoms:1; //!< true if atoms must be reinitialized during reinitialization
unsigned m_has_atoms:1; //!< true if the clause has memory space for storing atoms.
unsigned m_has_del_eh:1; //!< true if must notify event handler when deleted.
unsigned m_has_justification:1; //!< true if the clause has a justification attached to it.
unsigned m_deleted:1; //!< true if the clause is marked for deletion by was not deleted yet because it is referenced by some data-structure (e.g., m_lemmas)
literal m_lits[0];
static unsigned get_obj_size(unsigned num_lits, clause_kind k, bool has_atoms, bool has_del_eh, bool has_justification) {
unsigned r = sizeof(clause) + sizeof(literal) * num_lits;
if (k != CLS_AUX)
r += sizeof(unsigned);
if (has_atoms)
r += sizeof(expr*) * num_lits;
if (has_del_eh)
r += sizeof(clause_del_eh *);
if (has_justification)
r += sizeof(justification *);
return r;
}
unsigned const * get_activity_addr() const {
return reinterpret_cast<unsigned const *>(m_lits + m_capacity);
}
unsigned * get_activity_addr() {
return reinterpret_cast<unsigned *>(m_lits + m_capacity);
}
clause_del_eh * const * get_del_eh_addr() const {
unsigned const * addr = get_activity_addr();
if (is_lemma())
addr ++;
return reinterpret_cast<clause_del_eh * const *>(addr);
}
justification * const * get_justification_addr() const {
clause_del_eh * const * addr = get_del_eh_addr();
if (m_has_del_eh)
addr ++;
return reinterpret_cast<justification * const *>(addr);
}
expr * const * get_atoms_addr() const {
justification * const * addr = get_justification_addr();
if (m_has_justification)
addr ++;
return reinterpret_cast<expr * const *>(addr);
}
friend class context;
void swap_lits(unsigned idx1, unsigned idx2) {
literal tmp = get_literal(idx1);
set_literal(idx1, get_literal(idx2));
set_literal(idx2, tmp);
}
bool is_watch(literal l) const {
return m_lits[0] == l || m_lits[1] == l;
}
void set_literal(unsigned idx, literal l) {
m_lits[idx] = l;
}
void set_num_literals(unsigned n) {
SASSERT(n <= m_num_literals);
SASSERT(!m_reinit);
m_num_literals = n;
}
void set_justification(justification * new_js) {
SASSERT(m_has_justification);
SASSERT(!m_reinit);
SASSERT(!is_lemma() || new_js == 0 || !new_js->in_region());
justification ** js_addr = const_cast<justification**>(get_justification_addr());
*js_addr = new_js;
}
void release_atoms(ast_manager & m);
public:
static clause * mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js = 0,
clause_del_eh * del_eh = 0, bool save_atoms = false, expr * const * bool_var2expr_map = 0);
void deallocate(ast_manager & m);
clause_kind get_kind() const {
return static_cast<clause_kind>(m_kind);
}
bool is_lemma() const {
return get_kind() != CLS_AUX;
}
bool is_learned() const {
return get_kind() == CLS_LEARNED;
}
bool is_aux_lemma() const {
return get_kind() == CLS_AUX_LEMMA;
}
bool in_reinit_stack() const {
return m_reinit;
}
bool reinternalize_atoms() const {
return m_reinternalize_atoms;
}
unsigned get_num_literals() const {
return m_num_literals;
}
literal get_literal(unsigned idx) const {
SASSERT(idx < m_num_literals);
return m_lits[idx];
}
literal & get_literal(unsigned idx) {
SASSERT(idx < m_num_literals);
return m_lits[idx];
}
literal * begin_literals() { return m_lits; }
literal * end_literals() { return m_lits + m_num_literals; }
literal const * begin_literals() const { return m_lits; }
literal const * end_literals() const { return m_lits + m_num_literals; }
unsigned get_activity() const {
SASSERT(is_lemma());
return *(get_activity_addr());
}
void set_activity(unsigned act) {
SASSERT(is_lemma());
*(get_activity_addr()) = act;
}
clause_del_eh * get_del_eh() const {
return m_has_del_eh ? *(get_del_eh_addr()) : 0;
}
justification * get_justification() const {
return m_has_justification ? *(get_justification_addr()) : 0;
}
unsigned get_num_atoms() const {
return m_reinternalize_atoms ? m_num_literals : 0;
}
expr * get_atom(unsigned idx) const {
SASSERT(idx < get_num_atoms());
return UNTAG(expr*, get_atoms_addr()[idx]);
}
bool get_atom_sign(unsigned idx) const {
SASSERT(idx < get_num_atoms());
return GET_TAG(get_atoms_addr()[idx]) == 1;
}
bool erase_atom(unsigned idx);
void inc_clause_activity() {
SASSERT(is_lemma());
set_activity(get_activity() + 1);
}
void display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const;
void display_compact(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const;
unsigned hash() const {
return get_ptr_hash(this);
}
void mark_as_deleted(ast_manager & m) {
SASSERT(!m_deleted);
m_deleted = true;
clause_del_eh * del_eh = get_del_eh();
if (del_eh) {
(*del_eh)(m, this);
*(const_cast<clause_del_eh **>(get_del_eh_addr())) = 0;
}
}
bool deleted() const {
return m_deleted;
}
};
typedef ptr_vector<clause> clause_vector;
typedef obj_hashtable<clause> clause_set;
};
#endif /* _SMT_CLAUSE_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,278 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_conflict_resolution.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-25.
Revision History:
--*/
#ifndef _SMT_CONFLICT_RESOLUTION_H_
#define _SMT_CONFLICT_RESOLUTION_H_
#include"smt_literal.h"
#include"smt_bool_var_data.h"
#include"smt_justification.h"
#include"smt_enode.h"
#include"dyn_ack.h"
#include"obj_pair_hashtable.h"
#include"front_end_params.h"
#include"obj_pair_hashtable.h"
#include"map.h"
#include"watch_list.h"
#include"obj_pair_set.h"
typedef approx_set_tpl<unsigned, u2u, unsigned> level_approx_set;
namespace smt {
typedef std::pair<enode *, enode *> enode_pair;
/**
\brief Base conflict resolution class.
It implements the FUIP strategy.
*/
class conflict_resolution {
protected:
typedef obj_pair_set<enode, enode> enode_pair_set;
ast_manager & m_manager;
front_end_params const & m_params;
context & m_ctx;
dyn_ack_manager & m_dyn_ack_manager;
literal_vector const & m_assigned_literals;
unsigned m_conflict_lvl;
literal_vector m_lemma;
expr_ref_vector m_lemma_atoms;
unsigned m_new_scope_lvl;
unsigned m_lemma_iscope_lvl;
justification_vector m_todo_js;
unsigned m_todo_js_qhead;
svector<enode_pair> m_todo_eqs;
enode_pair_set m_already_processed_eqs;
literal_vector * m_antecedents;
// Reference for watch lists are used to implement subsumption resolution
vector<watch_list> & m_watches; //!< per literal
// ---------------------------
//
// Proof generation
//
// ---------------------------
typedef obj_map<justification, proof *> js2proof;
typedef obj_pair_map<enode, enode, proof *> eq2proof;
typedef map<literal, proof *, obj_hash<literal>, default_eq<literal> > lit2proof;
/**
\brief Element for the todo-list used to build proofs.
*/
struct tp_elem {
enum {
JUSTIFICATION,
EQUALITY,
LITERAL
} m_kind;
union {
justification * m_js;
unsigned m_lidx;
struct {
enode * m_lhs;
enode * m_rhs;
};
};
tp_elem(literal l):m_kind(LITERAL), m_lidx(l.index()) {}
tp_elem(enode * lhs, enode * rhs):m_kind(EQUALITY), m_lhs(lhs), m_rhs(rhs) {}
tp_elem(justification * js):m_kind(JUSTIFICATION), m_js(js) {}
};
svector<tp_elem> m_todo_pr;
js2proof m_js2proof;
eq2proof m_eq2proof;
lit2proof m_lit2proof;
proof_ref_vector m_new_proofs;
proof_ref m_lemma_proof;
literal_vector m_assumptions;
public:
void setup() {
}
void mark_justification(justification * js) {
if (!js->is_marked()) {
TRACE("conflict_detail", tout << "marking: " << js << "\n";);
js->set_mark();
m_todo_js.push_back(js);
}
}
void mark_eq(enode * n1, enode * n2) {
if (n1 != n2) {
if (n1->get_owner_id() > n2->get_owner_id())
std::swap(n1, n2);
enode_pair p(n1, n2);
if (m_already_processed_eqs.insert_if_not_there(p)) {
TRACE("conflict_detail", tout << "marking eq #" << p.first->get_owner_id() << " = #" <<
p.second->get_owner_id() << "\n";);
m_todo_eqs.push_back(p);
SASSERT(m_already_processed_eqs.contains(p));
}
}
}
void mark_literal(literal l) {
SASSERT(m_antecedents);
m_antecedents->push_back(l);
}
void mark_justified_eq(enode * lhs, enode * rhs, eq_justification js) {
eq_justification2literals(lhs, rhs, js);
}
proof * norm_eq_proof(enode * n1, enode * n2, proof * pr);
proof * get_proof(enode_pair const & p);
proof * get_proof(enode * n1, enode * n2);
proof * get_proof(enode * n1, enode * n2, eq_justification js);
proof * get_proof(literal l);
proof * get_proof(literal l, b_justification js);
proof * get_proof(justification * js);
bool visit_b_justification(literal l, b_justification js);
void mk_proof(literal l, b_justification js);
bool visit_trans_proof(enode * lhs, enode * rhs);
bool visit_eq_justications(enode * lhs, enode * rhs);
void mk_proof(enode * lhs, enode * rhs, ptr_buffer<proof> & result);
void mk_proof(enode * lhs, enode * rhs);
void init_mk_proof();
void mk_conflict_proof(b_justification conflict, literal not_l);
protected:
template<bool Set>
void mark_enodes_in_trans(enode * n);
enode * find_common_ancestor(enode * n1, enode * n2);
void eq_justification2literals(enode * lhs, enode * rhs, eq_justification js);
void eq_branch2literals(enode * n1, enode * n2);
void eq2literals(enode * n1, enode * n2);
void justification2literals_core(justification * js, literal_vector & result);
void unmark_justifications(unsigned old_js_qhead);
void justification2literals(justification * js, literal_vector & result);
literal_vector m_tmp_literal_vector;
unsigned get_justification_max_lvl(justification * js);
unsigned get_max_lvl(literal consequent, b_justification js);
unsigned skip_literals_above_conflict_level();
void process_antecedent(literal antecedent, unsigned & num_marks);
void process_justification(justification * js, unsigned & num_marks);
bool_var_vector m_unmark;
bool_var_vector m_lemma_min_stack;
level_approx_set m_lvl_set;
level_approx_set get_lemma_approx_level_set();
void reset_unmark(unsigned old_size);
void reset_unmark_and_justifications(unsigned old_size, unsigned old_js_qhead);
bool process_antecedent_for_minimization(literal antecedent);
bool process_justification_for_minimization(justification * js);
bool implied_by_marked(literal lit);
void minimize_lemma();
void structural_minimization();
void process_antecedent_for_unsat_core(literal antecedent);
void process_justification_for_unsat_core(justification * js);
void mk_unsat_core(b_justification conflict, literal not_l);
bool initialize_resolve(b_justification conflict, literal not_l, b_justification & js, literal & consequent);
void finalize_resolve(b_justification conflict, literal not_l);
public:
conflict_resolution(ast_manager & m,
context & ctx,
dyn_ack_manager & dack_manager,
front_end_params const & params,
literal_vector const & assigned_literals,
vector<watch_list> & watches
);
virtual ~conflict_resolution() {}
virtual bool resolve(b_justification conflict, literal not_l);
context & get_context() {
return m_ctx;
}
ast_manager & get_manager() {
return m_manager;
}
unsigned get_new_scope_lvl() const {
return m_new_scope_lvl;
}
unsigned get_lemma_intern_lvl() const {
return m_lemma_iscope_lvl;
}
unsigned get_lemma_num_literals() const {
return m_lemma.size();
}
literal * get_lemma_literals() {
return m_lemma.c_ptr();
}
expr * * get_lemma_atoms() {
return m_lemma_atoms.c_ptr();
}
void release_lemma_atoms() {
m_lemma_atoms.reset();
}
proof * get_lemma_proof() {
return m_lemma_proof;
}
literal_vector::const_iterator begin_unsat_core() const {
return m_assumptions.begin();
}
literal_vector::const_iterator end_unsat_core() const {
return m_assumptions.end();
}
};
inline void mark_literals(conflict_resolution & cr, unsigned sz, literal const * ls) {
for (unsigned i = 0; i < sz; i++)
cr.mark_literal(ls[i]);
}
conflict_resolution * mk_conflict_resolution(ast_manager & m,
context & ctx,
dyn_ack_manager & dack_manager,
front_end_params const & params,
literal_vector const & assigned_literals,
vector<watch_list> & watches
);
};
#endif /* _SMT_CONFLICT_RESOLUTION_H_ */

4207
src/smt/smt_context.cpp Normal file

File diff suppressed because it is too large Load diff

1462
src/smt/smt_context.h Normal file

File diff suppressed because it is too large Load diff

403
src/smt/smt_context_inv.cpp Normal file
View file

@ -0,0 +1,403 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_context_inv.cpp
Abstract:
SMT logical contexts: invariant
Author:
Leonardo de Moura (leonardo) 2008-02-21.
Revision History:
--*/
#include"smt_context.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"ast_smt2_pp.h"
namespace smt {
#ifdef Z3DEBUG
bool context::is_watching_clause(literal l, clause const * cls) const {
watch_list & wl = const_cast<watch_list &>(m_watches[l.index()]);
return wl.find_clause(cls) != wl.end_clause();
}
bool context::check_clause(clause const * cls) const {
SASSERT(is_watching_clause(~cls->get_literal(0), cls));
SASSERT(is_watching_clause(~cls->get_literal(1), cls));
if (lit_occs_enabled()) {
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal l = cls->get_literal(i);
SASSERT(m_lit_occs[l.index()].contains(const_cast<clause*>(cls)));
}
}
return true;
}
bool context::check_clauses(clause_vector const & v) const {
clause_vector::const_iterator it = v.begin();
clause_vector::const_iterator end = v.end();
for (; it != end; ++it) {
clause * cls = *it;
if (!cls->deleted())
check_clause(cls);
}
return true;
}
bool context::check_watch_list(literal l) const {
watch_list & wl = const_cast<watch_list &>(m_watches[l.index()]);
l.neg();
watch_list::clause_iterator it = wl.begin_clause();
watch_list::clause_iterator end = wl.end_clause();
for (; it != end; ++it) {
clause * cls = *it;
TRACE("watch_list", tout << "l: "; display_literal(tout, l); tout << "\n";
display_clause(tout, cls); tout << "\n";);
SASSERT(l == cls->get_literal(0) || l == cls->get_literal(1));
}
return true;
}
bool context::check_watch_list(unsigned l_idx) const {
return check_watch_list(to_literal(l_idx));
}
bool context::check_bin_watch_lists() const {
if (binary_clause_opt_enabled()) {
vector<watch_list>::const_iterator it = m_watches.begin();
vector<watch_list>::const_iterator end = m_watches.end();
for (unsigned l_idx = 0; it != end; ++it, ++l_idx) {
literal l1 = to_literal(l_idx);
watch_list const & wl = *it;
literal const * it2 = wl.begin_literals();
literal const * end2 = wl.end_literals();
for (; it2 != end2; ++it2) {
literal l2 = *it2;
watch_list const & wl = m_watches[(~l2).index()];
SASSERT(wl.find_literal(~l1) != wl.end_literals());
}
}
}
return true;
}
bool context::check_lit_occs(literal l) const {
clause_set const & v = m_lit_occs[l.index()];
clause_set::iterator it = v.begin();
clause_set::iterator end = v.end();
for (; it != end; ++it) {
clause * cls = *it;
unsigned num = cls->get_num_literals();
unsigned i = 0;
for (; i < num; i++)
if (cls->get_literal(i) == l)
break;
CTRACE("lit_occs", !(i < num), tout << i << " " << num << "\n"; display_literal(tout, l); tout << "\n";
display_clause(tout, cls); tout << "\n";
tout << "l: " << l.index() << " cls: ";
for (unsigned j = 0; j < num; j++) {
tout << cls->get_literal(j).index() << " ";
}
tout << "\n";
display_clause_detail(tout, cls); tout << "\n";);
SASSERT(i < num);
}
return true;
}
bool context::check_lit_occs() const {
if (lit_occs_enabled()) {
unsigned num_lits = get_num_bool_vars() * 2;
for (unsigned l_idx = 0; l_idx < num_lits; ++l_idx) {
check_lit_occs(to_literal(l_idx));
}
}
return true;
}
bool context::check_enode(enode * n) const {
SASSERT(n->check_invariant());
bool is_true_eq = n->is_true_eq();
bool cg_inv =
n->get_num_args() == 0 ||
(!is_true_eq && (!n->is_cgc_enabled() || n->is_cgr() == (m_cg_table.contains_ptr(n)))) ||
(is_true_eq && !m_cg_table.contains_ptr(n));
CTRACE("check_enode", !cg_inv,
tout << "n: #" << n->get_owner_id() << ", m_cg: #" << n->m_cg->get_owner_id() << ", contains: " << m_cg_table.contains(n) << "\n"; display(tout););
SASSERT(cg_inv);
return true;
}
bool context::check_enodes() const {
ptr_vector<enode>::const_iterator it = m_enodes.begin();
ptr_vector<enode>::const_iterator end = m_enodes.end();
for (; it != end; ++it) {
check_enode(*it);
}
return true;
}
bool context::check_invariant() const {
check_lit_occs();
check_bin_watch_lists();
check_clauses(m_aux_clauses);
check_clauses(m_lemmas);
check_enodes();
SASSERT(m_cg_table.check_invariant());
return true;
}
bool context::check_missing_clause_propagation(clause_vector const & v) const {
clause_vector::const_iterator it = v.begin();
clause_vector::const_iterator end = v.end();
for (; it != end; ++it) {
CTRACE("missing_propagation", is_unit_clause(*it), display_clause_detail(tout, *it); tout << "\n";);
SASSERT(!is_unit_clause(*it));
}
return true;
}
bool context::check_missing_bin_clause_propagation() const {
if (binary_clause_opt_enabled()) {
SASSERT(m_watches.size() == m_assignment.size());
vector<watch_list>::const_iterator it = m_watches.begin();
vector<watch_list>::const_iterator end = m_watches.end();
for (unsigned l_idx = 0; it != end; ++it, ++l_idx) {
literal l = to_literal(l_idx);
watch_list const & wl = *it;
if (get_assignment(l) == l_true) {
literal const * it2 = wl.begin_literals();
literal const * end2 = wl.end_literals();
for (; it2 != end2; ++it2) {
literal l2 = *it2;
SASSERT(get_assignment(l2) == l_true);
}
}
}
}
return true;
}
bool context::check_missing_eq_propagation() const {
ptr_vector<enode>::const_iterator it = m_enodes.begin();
ptr_vector<enode>::const_iterator end = m_enodes.end();
for (; it != end; ++it) {
enode * n = *it;
SASSERT(!n->is_true_eq() || get_assignment(n) == l_true);
if (n->is_eq() && get_assignment(n) == l_true) {
SASSERT(n->is_true_eq());
}
}
return true;
}
bool context::check_missing_congruence() const {
ptr_vector<enode>::const_iterator it = m_enodes.begin();
ptr_vector<enode>::const_iterator end = m_enodes.end();
for (; it != end; ++it) {
enode * n = *it;
ptr_vector<enode>::const_iterator it2 = m_enodes.begin();
for (; it2 != end; ++it2) {
enode * n2 = *it2;
if (n->get_root() != n2->get_root()) {
if (n->is_true_eq() && n2->is_true_eq())
continue;
CTRACE("missing_propagation", congruent(n, n2),
tout << mk_pp(n->get_owner(), m_manager) << "\n" << mk_pp(n2->get_owner(), m_manager) << "\n";
display(tout););
SASSERT(!congruent(n, n2));
}
}
}
return true;
}
bool context::check_missing_bool_enode_propagation() const {
ptr_vector<enode>::const_iterator it = m_enodes.begin();
ptr_vector<enode>::const_iterator end = m_enodes.end();
for (; it != end; ++it) {
enode * n = *it;
if (m_manager.is_bool(n->get_owner()) && get_assignment(n) == l_undef) {
enode * first = n;
do {
CTRACE("missing_propagation", get_assignment(n) != l_undef,
tout << mk_pp(first->get_owner(), m_manager) << "\nassignment: " << get_assignment(first) << "\n"
<< mk_pp(n->get_owner(), m_manager) << "\nassignment: " << get_assignment(n) << "\n";);
SASSERT(get_assignment(n) == l_undef);
n = n->get_next();
}
while (n != first);
}
}
return true;
}
bool context::check_missing_propagation() const {
check_missing_clause_propagation(m_lemmas);
check_missing_clause_propagation(m_aux_clauses);
check_missing_bin_clause_propagation();
// check_missing_eq_propagation();
check_missing_congruence();
check_missing_bool_enode_propagation();
return true;
}
bool context::check_relevancy(expr_ref_vector const & v) const {
return m_relevancy_propagator->check_relevancy(v);
}
bool context::check_relevancy() const {
if (!relevancy())
return true;
check_relevancy(m_b_internalized_stack);
check_relevancy(m_e_internalized_stack);
unsigned sz = m_asserted_formulas.get_num_formulas();
for (unsigned i = 0; i < sz; i++) {
expr * n = m_asserted_formulas.get_formula(i);
if (m_manager.is_or(n)) {
CTRACE("relevancy_bug", !is_relevant(n), tout << "n: " << mk_ismt2_pp(n, m_manager) << "\n";);
SASSERT(is_relevant(n));
TRACE("check_relevancy", tout << "checking:\n" << mk_ll_pp(n, m_manager) << "\n";);
SASSERT(m_relevancy_propagator->check_relevancy_or(to_app(n), true));
}
else if (m_manager.is_not(n)) {
CTRACE("relevancy_bug", !is_relevant(to_app(n)->get_arg(0)), tout << "n: " << mk_ismt2_pp(n, m_manager) << "\n";);
SASSERT(is_relevant(to_app(n)->get_arg(0)));
}
else {
CTRACE("relevancy_bug", !is_relevant(n), tout << "n: " << mk_ismt2_pp(n, m_manager) << "\n";);
SASSERT(is_relevant(n));
}
}
return true;
}
/**
\brief Check if expressions attached to bool_variables and enodes have a consistent assignment.
For all a, b. root(a) == root(b) ==> get_assignment(a) == get_assignment(b)
*/
bool context::check_eqc_bool_assignment() const {
ptr_vector<enode>::const_iterator it = m_enodes.begin();
ptr_vector<enode>::const_iterator end = m_enodes.end();
for (; it != end; ++it) {
enode * e = *it;
if (m_manager.is_bool(e->get_owner())) {
enode * r = e->get_root();
CTRACE("eqc_bool", get_assignment(e) != get_assignment(r),
tout << "#" << e->get_owner_id() << "\n" << mk_pp(e->get_owner(), m_manager) << "\n";
tout << "#" << r->get_owner_id() << "\n" << mk_pp(r->get_owner(), m_manager) << "\n";
tout << "assignments: " << get_assignment(e) << " " << get_assignment(r) << "\n";
display(tout););
SASSERT(get_assignment(e) == get_assignment(r));
}
}
return true;
}
bool context::check_bool_var_vector_sizes() const {
SASSERT(m_assignment.size() == 2 * m_bdata.size());
SASSERT(m_watches.size() == 2 * m_bdata.size());
SASSERT(m_bdata.size() == m_activity.size());
SASSERT(m_bool_var2expr.size() == m_bdata.size());
return true;
}
/**
\brief Check the following property:
- for every equality atom (= lhs rhs) assigned to false, relevant:
if lhs->get_root() and rhs->get_root() are attached to theory variables v1 and v2 of theory t,
then there is an entry (t, v1', v2') in m_propagated_th_diseqs such that,
(= get_enode(v1') get_enode(v2')) is congruent to (= lhs rhs).
*/
bool context::check_th_diseq_propagation() const {
TRACE("check_th_diseq_propagation", tout << "m_propagated_th_diseqs.size() " << m_propagated_th_diseqs.size() << "\n";);
int num = get_num_bool_vars();
for (bool_var v = 0; v < num; v++) {
if (has_enode(v)) {
enode * n = bool_var2enode(v);
if (n->is_eq() && is_relevant(n) && get_assignment(v) == l_false) {
TRACE("check_th_diseq_propagation", tout << "checking: #" << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), m_manager) << "\n";);
enode * lhs = n->get_arg(0)->get_root();
enode * rhs = n->get_arg(1)->get_root();
if (rhs->is_interpreted() && lhs->is_interpreted())
continue;
TRACE("check_th_diseq_propagation", tout << "num. theory_vars: " << lhs->get_num_th_vars() << " "
<< mk_pp(m_manager.get_sort(lhs->get_owner()), m_manager) << "\n";);
theory_var_list * l = lhs->get_th_var_list();
while (l) {
theory_id th_id = l->get_th_id();
theory * th = get_theory(th_id);
TRACE("check_th_diseq_propagation", tout << "checking theory: " << m_manager.get_family_name(th_id) << "\n";);
// if the theory doesn't use diseqs, then the diseqs are not propagated.
if (th->use_diseqs() && rhs->get_th_var(th_id) != null_theory_var) {
// lhs and rhs are attached to theory th_id
svector<new_th_eq>::const_iterator it = m_propagated_th_diseqs.begin();
svector<new_th_eq>::const_iterator end = m_propagated_th_diseqs.end();
for (; it != end; ++it) {
if (it->m_th_id == th_id) {
enode * lhs_prime = th->get_enode(it->m_lhs)->get_root();
enode * rhs_prime = th->get_enode(it->m_rhs)->get_root();
TRACE("check_th_diseq_propagation",
tout << m_manager.get_family_name(it->m_th_id) << "\n";);
if ((lhs == lhs_prime && rhs == rhs_prime) ||
(rhs == lhs_prime && lhs == rhs_prime)) {
TRACE("check_th_diseq_propagation", tout << "ok v" << v << " " << get_assignment(v) << "\n";);
break;
}
}
}
if (it == end) {
// missed theory diseq propagation
display(std::cout);
std::cout << "checking theory: " << m_manager.get_family_name(th_id) << "\n";
std::cout << "root: #" << n->get_root()->get_owner_id() << " node: #" << n->get_owner_id() << "\n";
std::cout << mk_pp(n->get_owner(), m_manager) << "\n";
std::cout << "lhs: #" << lhs->get_owner_id() << ", rhs: #" << rhs->get_owner_id() << "\n";
std::cout << mk_bounded_pp(lhs->get_owner(), m_manager) << " " << mk_bounded_pp(rhs->get_owner(), m_manager) << "\n";
}
SASSERT(it != end);
}
l = l->get_next();
}
}
}
}
return true;
}
bool context::check_missing_diseq_conflict() const {
svector<enode_pair>::const_iterator it = m_diseq_vector.begin();
svector<enode_pair>::const_iterator end = m_diseq_vector.end();
for (; it != end; ++it) {
enode * n1 = it->first;
enode * n2 = it->second;
if (n1->get_root() == n2->get_root()) {
TRACE("diseq_bug",
tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() <<
", r: #" << n1->get_root()->get_owner_id() << "\n";
tout << "n1 parents:\n"; display_parent_eqs(tout, n1);
tout << "n2 parents:\n"; display_parent_eqs(tout, n2);
tout << "r parents:\n"; display_parent_eqs(tout, n1->get_root());
);
UNREACHABLE();
}
}
return true;
}
#endif
};

621
src/smt/smt_context_pp.cpp Normal file
View file

@ -0,0 +1,621 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_context_pp.cpp
Abstract:
SMT logical context: pretty printing
Author:
Leonardo de Moura (leonardo) 2008-02-21.
Revision History:
--*/
#include"smt_context.h"
#include"ast_ll_pp.h"
#include"ast_pp.h"
#include"ast_smt_pp.h"
#include"stats.h"
namespace smt {
std::ostream& context::display_last_failure(std::ostream& out) const {
switch(m_last_search_failure) {
case OK:
return out << "OK";
case UNKNOWN:
return out << "UNKNOWN";
case TIMEOUT:
return out << "TIMEOUT";
case MEMOUT:
return out << "MEMOUT";
case CANCELED:
return out << "CANCELED";
case NUM_CONFLICTS:
return out << "NUM_CONFLICTS";
case THEORY:
if (!m_incomplete_theories.empty()) {
ptr_vector<theory>::const_iterator it = m_incomplete_theories.begin();
ptr_vector<theory>::const_iterator end = m_incomplete_theories.end();
for (bool first = true; it != end; ++it) {
if (first) first = false; else out << " ";
out << (*it)->get_name();
}
}
else {
out << "THEORY";
}
return out;
case QUANTIFIERS:
return out << "QUANTIFIERS";
}
UNREACHABLE();
return out << "?";
}
std::string context::last_failure_as_string() const {
std::string r;
switch(m_last_search_failure) {
case OK: r = "ok"; break;
case TIMEOUT: r = "timeout"; break;
case MEMOUT: r = "memout"; break;
case CANCELED: r = "canceled"; break;
case NUM_CONFLICTS: r = "max-conflicts-reached"; break;
case THEORY: {
r = "(incomplete (theory";
ptr_vector<theory>::const_iterator it = m_incomplete_theories.begin();
ptr_vector<theory>::const_iterator end = m_incomplete_theories.end();
for (; it != end; ++it) {
r += " ";
r += (*it)->get_name();
}
r += "))";
break;
}
case QUANTIFIERS: r = "(incomplete quantifiers)"; break;
case UNKNOWN: r = "incomplete"; break;
}
return r;
}
void context::display_asserted_formulas(std::ostream & out) const {
m_asserted_formulas.display_ll(out, get_pp_visited());
}
void context::display_literal(std::ostream & out, literal l) const {
l.display_compact(out, m_bool_var2expr.c_ptr());
}
void context::display_literals(std::ostream & out, unsigned num_lits, literal const * lits) const {
display_compact(out, num_lits, lits, m_bool_var2expr.c_ptr());
}
void context::display_literals_verbose(std::ostream & out, unsigned num_lits, literal const * lits) const {
display_verbose(out, m_manager, num_lits, lits, m_bool_var2expr.c_ptr());
}
void context::display_literal_info(std::ostream & out, literal l) const {
l.display_compact(out, m_bool_var2expr.c_ptr());
if (l.sign())
out << " (not " << mk_bounded_pp(bool_var2expr(l.var()), m_manager, 10) << ")\n";
else
out << " " << mk_bounded_pp(bool_var2expr(l.var()), m_manager, 10) << "\n";
out << "relevant: " << is_relevant(bool_var2expr(l.var())) << ", val: " << get_assignment(l) << "\n";
}
void context::display_watch_list(std::ostream & out, literal l) const {
display_literal(out, l); out << " watch_list:\n";
watch_list & wl = const_cast<watch_list &>(m_watches[l.index()]);
watch_list::clause_iterator it = wl.begin_clause();
watch_list::clause_iterator end = wl.end_clause();
for (; it != end; ++it) {
display_clause(out, *it); out << "\n";
}
}
void context::display_watch_lists(std::ostream & out) const {
unsigned s = m_watches.size();
for (unsigned l_idx = 0; l_idx < s; l_idx++) {
literal l = to_literal(l_idx);
display_watch_list(out, l);
out << "\n";
}
}
void context::display_enode_defs(std::ostream & out) const {
ptr_vector<enode>::const_iterator it = m_enodes.begin();
ptr_vector<enode>::const_iterator end = m_enodes.end();
for (; it != end; ++it) {
expr * n = (*it)->get_owner();
ast_def_ll_pp(out, m_manager, n, get_pp_visited(), true, false);
}
}
void context::display_bool_var_defs(std::ostream & out) const {
unsigned num = get_num_bool_vars();
for (unsigned v = 0; v < num; v++) {
expr * n = m_bool_var2expr[v];
ast_def_ll_pp(out, m_manager, n, get_pp_visited(), true, false);
}
}
void context::display_clause_detail(std::ostream & out, clause const * cls) const {
out << "lemma: " << cls->is_lemma() << "\n";
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal l = cls->get_literal(i);
display_literal(out, l);
out << ", val: " << get_assignment(l) << ", lvl: " << get_assign_level(l)
<< ", ilvl: " << get_intern_level(l.var()) << ", var: " << l.var() << "\n"
<< mk_pp(bool_var2expr(l.var()), m_manager) << "\n\n";
}
}
void context::display_clause(std::ostream & out, clause const * cls) const {
cls->display_compact(out, m_manager, m_bool_var2expr.c_ptr());
}
void context::display_clauses(std::ostream & out, ptr_vector<clause> const & v) const {
ptr_vector<clause>::const_iterator it = v.begin();
ptr_vector<clause>::const_iterator end = v.end();
for (; it != end; ++it) {
display_clause(out, *it);
out << "\n";
}
}
void context::display_binary_clauses(std::ostream & out) const {
bool first = true;
vector<watch_list>::const_iterator it = m_watches.begin();
vector<watch_list>::const_iterator end = m_watches.end();
for (unsigned l_idx = 0; it != end; ++it, ++l_idx) {
literal l1 = to_literal(l_idx);
literal neg_l1 = ~l1;
watch_list const & wl = *it;
literal const * it2 = wl.begin_literals();
literal const * end2 = wl.end_literals();
for (; it2 != end2; ++it2) {
literal l2 = *it2;
if (l1.index() < l2.index()) {
if (first) {
out << "binary clauses:\n";
first = false;
}
out << "(clause ";
display_literal(out, neg_l1);
out << " ";
display_literal(out, l2);
out << ")\n";
}
}
}
}
void context::display_assignment(std::ostream & out) const {
if (!m_assigned_literals.empty()) {
out << "current assignment:\n";
literal_vector::const_iterator it = m_assigned_literals.begin();
literal_vector::const_iterator end = m_assigned_literals.end();
for (; it != end; ++it) {
display_literal(out, *it);
out << " ";
}
out << "\n";
}
}
void context::display_assignment_as_smtlib2(std::ostream& out, char const* logic) const {
ast_smt_pp pp(m_manager);
pp.set_benchmark_name("lemma");
pp.set_status("unknown");
pp.set_logic(logic);
literal_vector::const_iterator it = m_assigned_literals.begin();
literal_vector::const_iterator end = m_assigned_literals.end();
for (; it != end; ++it) {
expr_ref n(m_manager);
literal2expr(*it, n);
pp.add_assumption(n);
}
pp.display_smt2(out, m_manager.mk_true());
}
void context::display_eqc(std::ostream & out) const {
bool first = true;
ptr_vector<enode>::const_iterator it = m_enodes.begin();
ptr_vector<enode>::const_iterator end = m_enodes.end();
for (; it != end; ++it) {
expr * n = (*it)->get_owner();
expr * r = (*it)->get_root()->get_owner();
if (n != r) {
if (first) {
out << "equivalence classes:\n";
first = false;
}
out << "#" << n->get_id() << " -> #" << r->get_id() << "\n";
}
}
}
void context::display_app_enode_map(std::ostream & out) const {
if (!m_e_internalized_stack.empty()) {
out << "expresion -> enode:\n";
unsigned sz = m_e_internalized_stack.size();
for (unsigned i = 0; i < sz; i++) {
expr * n = m_e_internalized_stack.get(i);
out << "(#" << n->get_id() << " -> e!" << i << ") ";
}
out << "\n";
}
}
void context::display_expr_bool_var_map(std::ostream & out) const {
if (!m_b_internalized_stack.empty()) {
out << "expresion -> bool_var:\n";
unsigned sz = m_b_internalized_stack.size();
for (unsigned i = 0; i < sz; i++) {
expr * n = m_b_internalized_stack.get(i);
bool_var v = get_bool_var_of_id(n->get_id());
out << "(#" << n->get_id() << " -> p!" << v << ") ";
}
out << "\n";
}
}
void context::display_hot_bool_vars(std::ostream & out) const {
out << "hot bool vars:\n";
int num = get_num_bool_vars();
for (bool_var v = 0; v < num; v++) {
double val = get_activity(v)/m_bvar_inc;
if (val > 10.00) {
expr * n = m_b_internalized_stack.get(v);
out << "#";
out.width(5);
out << std::left;
out << n->get_id();
out << " ";
out.width(12);
out << std::right;
out << get_activity(v) << " ";
out.width(12);
out << val;
out << "\n";
}
}
}
void context::display_relevant_exprs(std::ostream & out) const {
m_relevancy_propagator->display(out);
}
void context::display_theories(std::ostream & out) const {
ptr_vector<theory>::const_iterator it = m_theory_set.begin();
ptr_vector<theory>::const_iterator end = m_theory_set.end();
for (; it != end; ++it) {
theory * th = *it;
th->display(out);
}
}
void context::display(std::ostream & out) const {
get_pp_visited().reset();
out << "Logical context:\n";
out << "scope-lvl: " << m_scope_lvl << "\n";
out << "base-lvl: " << m_base_lvl << "\n";
out << "search-lvl: " << m_search_lvl << "\n";
out << "inconsistent(): " << inconsistent() << "\n";
out << "m_asserted_formulas.inconsistent(): " << m_asserted_formulas.inconsistent() << "\n";
display_bool_var_defs(out);
display_enode_defs(out);
display_asserted_formulas(out);
if (!m_aux_clauses.empty()) {
out << "auxiliary clauses:\n";
display_clauses(out, m_aux_clauses);
}
if (!m_lemmas.empty()) {
out << "lemmas:\n";
display_clauses(out, m_lemmas);
}
display_binary_clauses(out);
display_assignment(out);
display_eqc(out);
m_cg_table.display_compact(out);
m_case_split_queue->display(out);
display_expr_bool_var_map(out);
display_app_enode_map(out);
display_relevant_exprs(out);
display_theories(out);
display_decl2enodes(out);
display_hot_bool_vars(out);
}
void context::display_eq_detail(std::ostream & out, enode * n) const {
SASSERT(n->is_eq());
out << "#" << n->get_owner_id()
<< ", root: #" << n->get_root()->get_owner_id()
<< ", cg: #" << n->m_cg->get_owner_id()
<< ", val: " << get_assignment(enode2bool_var(n))
<< ", lhs: #" << n->get_arg(0)->get_owner_id()
<< ", rhs: #" << n->get_arg(1)->get_owner_id()
<< ", lhs->root: #" << n->get_arg(0)->get_root()->get_owner_id()
<< ", rhs->root: #" << n->get_arg(1)->get_root()->get_owner_id()
<< ", is_marked: " << n->is_marked()
<< ", is_relevant: " << is_relevant(n)
<< ", iscope_lvl: " << n->get_iscope_lvl() << "\n";
}
void context::display_parent_eqs(std::ostream & out, enode * n) const {
enode_vector::iterator it = n->begin_parents();
enode_vector::iterator end = n->end_parents();
for (; it != end; ++it) {
enode * parent = *it;
if (parent->is_eq())
display_eq_detail(out, parent);
}
}
void context::display_unsat_core(std::ostream & out) const {
unsigned sz = m_unsat_core.size();
for (unsigned i = 0; i < sz; i++)
out << mk_pp(m_unsat_core.get(i), m_manager) << "\n";
}
void context::collect_statistics(::statistics & st) const {
st.update("conflicts", m_stats.m_num_conflicts);
st.update("decisions", m_stats.m_num_decisions);
st.update("propagations", m_stats.m_num_propagations + m_stats.m_num_bin_propagations);
st.update("binary propagations", m_stats.m_num_bin_propagations);
st.update("restarts", m_stats.m_num_restarts);
st.update("final checks", m_stats.m_num_final_checks);
st.update("added eqs", m_stats.m_num_add_eq);
st.update("mk clause", m_stats.m_num_mk_clause);
st.update("del clause", m_stats.m_num_del_clause);
st.update("dyn ack", m_stats.m_num_dyn_ack);
st.update("interface eqs", m_stats.m_num_interface_eqs);
st.update("max generation", m_stats.m_max_generation);
st.update("minimized lits", m_stats.m_num_minimized_lits);
st.update("num checks", m_stats.m_num_checks);
#if 0
// missing?
st.update("sat conflicts", m_stats.m_num_sat_conflicts);
st.update("mk bool var", m_stats.m_num_mk_bool_var);
st.update("del bool var", m_stats.m_num_del_bool_var);
st.update("mk enode", m_stats.m_num_mk_enode);
st.update("del enode", m_stats.m_num_del_enode);
st.update("mk bin clause", m_stats.m_num_mk_bin_clause);
st.update("mk lit", m_stats.m_num_mk_lits);
st.update("backwd subs", m_stats.m_num_bs);
st.update("backwd subs res", m_stats.m_num_bsr);
st.update("frwrd subs res", m_stats.m_num_fsr);
#endif
m_qmanager->collect_statistics(st);
m_asserted_formulas.collect_statistics(st);
ptr_vector<theory>::const_iterator it = m_theory_set.begin();
ptr_vector<theory>::const_iterator end = m_theory_set.end();
for (; it != end; ++it) {
(*it)->collect_statistics(st);
}
}
void context::display_statistics(std::ostream & out) const {
::statistics st;
collect_statistics(st);
st.display(out);
}
void context::display_istatistics(std::ostream & out) const {
::statistics st;
collect_statistics(st);
st.display_internal(out);
}
void context::display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, literal consequent, const char * logic) const {
ast_smt_pp pp(m_manager);
pp.set_benchmark_name("lemma");
pp.set_status("unsat");
pp.set_logic(logic);
for (unsigned i = 0; i < num_antecedents; i++) {
literal l = antecedents[i];
expr_ref n(m_manager);
literal2expr(l, n);
pp.add_assumption(n);
}
expr_ref n(m_manager);
literal2expr(~consequent, n);
pp.display(out, n);
}
static unsigned g_lemma_id = 0;
#define BUFFER_SZ 128
void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent, const char * logic) const {
char buffer[BUFFER_SZ];
#ifdef _WINDOWS
sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt", g_lemma_id);
#else
sprintf(buffer, "lemma_%d.smt", g_lemma_id);
#endif
std::ofstream out(buffer);
display_lemma_as_smt_problem(out, num_antecedents, antecedents, consequent, logic);
out.close();
g_lemma_id++;
}
void context::display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents,
unsigned num_eq_antecedents, enode_pair const * eq_antecedents,
literal consequent, const char * logic) const {
ast_smt_pp pp(m_manager);
pp.set_benchmark_name("lemma");
pp.set_status("unsat");
pp.set_logic(logic);
for (unsigned i = 0; i < num_antecedents; i++) {
literal l = antecedents[i];
expr_ref n(m_manager);
literal2expr(l, n);
pp.add_assumption(n);
}
for (unsigned i = 0; i < num_eq_antecedents; i++) {
enode_pair const & p = eq_antecedents[i];
expr_ref eq(m_manager);
eq = m_manager.mk_eq(p.first->get_owner(), p.second->get_owner());
pp.add_assumption(eq);
}
expr_ref n(m_manager);
literal2expr(~consequent, n);
pp.display(out, n);
}
void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents,
unsigned num_eq_antecedents, enode_pair const * eq_antecedents,
literal consequent, const char * logic) const {
char buffer[BUFFER_SZ];
#ifdef _WINDOWS
sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt", g_lemma_id);
#else
sprintf(buffer, "lemma_%d.smt", g_lemma_id);
#endif
std::ofstream out(buffer);
display_lemma_as_smt_problem(out, num_antecedents, antecedents, num_eq_antecedents, eq_antecedents, consequent, logic);
out.close();
g_lemma_id++;
}
/**
\brief Display enode definitions #n := (f #i_1 ... #i_n), where #i_k is the root
of the k-th argument of the enode #n.
*/
void context::display_normalized_enodes(std::ostream & out) const {
out << "normalized enodes:\n";
ptr_vector<enode>::const_iterator it = m_enodes.begin();
ptr_vector<enode>::const_iterator end = m_enodes.end();
for (; it != end; ++it) {
enode * n = *it;
out << "#";
out.width(5);
out << std::left << n->get_owner_id() << " #";
out.width(5);
out << n->get_root()->get_owner_id() << " := " << std::right;
unsigned num = n->get_owner()->get_num_args();
if (num > 0)
out << "(";
out << n->get_decl()->get_name();
if (!n->get_decl()->private_parameters())
display_parameters(out, n->get_decl()->get_num_parameters(), n->get_decl()->get_parameters());
for (unsigned i = 0; i < num; i++) {
expr * arg = n->get_owner()->get_arg(i);
if (e_internalized(arg)) {
enode * n = get_enode(arg)->get_root();
out << " #" << n->get_owner_id();
}
else {
out << " #" << arg->get_id();
}
}
if (num > 0)
out << ")";
if (is_relevant(n))
out << "\t*";
out << "\n";
}
}
void context::display_enodes_lbls(std::ostream & out) const {
ptr_vector<enode>::const_iterator it = m_enodes.begin();
ptr_vector<enode>::const_iterator end = m_enodes.end();
for (; it != end; ++it) {
enode * n = *it;
n->display_lbls(out);
}
}
void context::display_decl2enodes(std::ostream & out) const {
out << "decl2enodes:\n";
vector<enode_vector>::const_iterator it1 = m_decl2enodes.begin();
vector<enode_vector>::const_iterator end1 = m_decl2enodes.end();
for (unsigned id = 0; it1 != end1; ++it1, ++id) {
enode_vector const & v = *it1;
if (!v.empty()) {
out << "id " << id << " ->";
enode_vector::const_iterator it2 = v.begin();
enode_vector::const_iterator end2 = v.end();
for (; it2 != end2; ++it2)
out << " #" << (*it2)->get_owner_id();
out << "\n";
}
}
}
void context::display_subexprs_info(std::ostream & out, expr * n) const {
ptr_buffer<expr> todo;
todo.push_back(n);
while (!todo.empty()) {
expr * n = todo.back();
todo.pop_back();
out << "#";
out.width(6);
out << std::left << n->get_id();
out << ", relevant: " << is_relevant(n);
if (m_manager.is_bool(n)) {
out << ", val: ";
out.width(7);
out << std::right;
if (lit_internalized(n))
out << get_assignment(n);
else
out << "l_undef";
}
if (e_internalized(n)) {
enode * e = get_enode(n);
out << ", root: #" << e->get_root()->get_owner_id();
}
out << "\n";
if (is_app(n)) {
for (unsigned i = 0; i < to_app(n)->get_num_args(); i++)
todo.push_back(to_app(n)->get_arg(i));
}
}
}
void context::trace_assign(literal l, b_justification j, bool decision) const {
std::ostream & out = *m_fparams.m_trace_stream;
out << "[assign] ";
display_literal(out, l);
if (decision)
out << " decision";
out << " ";
switch (j.get_kind()) {
case b_justification::AXIOM:
out << "axiom";
break;
case b_justification::BIN_CLAUSE: {
literal l2 = j.get_literal();
out << "bin-clause ";
display_literal(out, l);
out << " ";
display_literal(out, l2);
break;
}
case b_justification::CLAUSE: {
clause * cls = j.get_clause();
out << "clause ";
display_literals(out, cls->get_num_literals(), cls->begin_literals());
break;
}
case b_justification::JUSTIFICATION:
out << "justification";
break;
default:
UNREACHABLE();
break;
}
out << "\n";
}
};

View file

@ -0,0 +1,159 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_context_stat.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-03-06.
Revision History:
--*/
#include"smt_context.h"
#include"ast_pp.h"
namespace smt {
unsigned context::get_lemma_avg_activity() const {
if (m_lemmas.empty())
return 0;
unsigned long long acc = 0;
clause_vector::const_iterator it = m_lemmas.begin();
clause_vector::const_iterator end = m_lemmas.end();
for (; it != end; ++it)
acc += (*it)->get_activity();
return static_cast<unsigned>(acc / m_lemmas.size());
}
void acc_num_occs(clause * cls, unsigned_vector & lit2num_occs) {
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal l = cls->get_literal(i);
lit2num_occs[l.index()]++;
}
}
void acc_num_occs(clause_vector const & v, unsigned_vector & lit2num_occs) {
clause_vector::const_iterator it = v.begin();
clause_vector::const_iterator end = v.end();
for (; it != end; ++it)
acc_num_occs(*it, lit2num_occs);
}
void context::display_literal_num_occs(std::ostream & out) const {
unsigned num_lits = m_assignment.size();
unsigned_vector lit2num_occs;
lit2num_occs.resize(num_lits, 0);
acc_num_occs(m_aux_clauses, lit2num_occs);
acc_num_occs(m_lemmas, lit2num_occs);
for (unsigned lidx = 0; lidx < num_lits; lidx++) {
literal l = to_literal(lidx);
if (lit2num_occs[lidx] > 0) {
out << lit2num_occs[lidx] << " ";
// display_literal(out, l);
out << l.sign() << " " << mk_pp(bool_var2expr(l.var()), m_manager);
out << "\n";
}
}
}
void context::display_num_assigned_literals_per_lvl(std::ostream & out) const {
unsigned n = 0;
svector<scope>::const_iterator it = m_scopes.begin();
svector<scope>::const_iterator end = m_scopes.end();
out << "[";
for (; it != end; ++it) {
scope const & s = *it;
SASSERT(n <= s.m_assigned_literals_lim);
out << (s.m_assigned_literals_lim - n) << " ";
n = s.m_assigned_literals_lim;
}
SASSERT(n <= m_assigned_literals.size());
out << (m_assigned_literals.size() - n) << "]";
}
void acc_var_num_occs(clause * cls, unsigned_vector & var2num_occs) {
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal l = cls->get_literal(i);
var2num_occs[l.var()]++;
}
}
void acc_var_num_occs(clause_vector const & v, unsigned_vector & var2num_occs) {
clause_vector::const_iterator it = v.begin();
clause_vector::const_iterator end = v.end();
for (; it != end; ++it)
acc_var_num_occs(*it, var2num_occs);
}
void context::display_var_occs_histogram(std::ostream & out) const {
unsigned num_vars = get_num_bool_vars();
unsigned_vector var2num_occs;
var2num_occs.resize(num_vars, 0);
acc_var_num_occs(m_aux_clauses, var2num_occs);
acc_var_num_occs(m_lemmas, var2num_occs);
unsigned_vector histogram;
for (unsigned v = 0; v < num_vars; v++) {
unsigned num_occs = var2num_occs[v];
histogram.reserve(num_occs+1, 0);
histogram[num_occs]++;
}
out << "number of atoms having k occs:\n";
unsigned sz = histogram.size();
for (unsigned i = 1; i < sz; i++)
if (histogram[i] > 0)
out << i << ":" << histogram[i] << " ";
out << "\n";
}
void acc_var_num_min_occs(clause * cls, unsigned_vector & var2num_min_occs) {
unsigned num_lits = cls->get_num_literals();
bool_var min_var = cls->get_literal(0).var();
for (unsigned i = 1; i < num_lits; i++) {
bool_var v = cls->get_literal(i).var();
if (v < min_var)
min_var = v;
}
var2num_min_occs[min_var]++;
}
void acc_var_num_min_occs(clause_vector const & v, unsigned_vector & var2num_min_occs) {
clause_vector::const_iterator it = v.begin();
clause_vector::const_iterator end = v.end();
for (; it != end; ++it)
acc_var_num_min_occs(*it, var2num_min_occs);
}
void context::display_num_min_occs(std::ostream & out) const {
unsigned num_vars = get_num_bool_vars();
unsigned_vector var2num_min_occs;
var2num_min_occs.resize(num_vars, 0);
acc_var_num_min_occs(m_aux_clauses, var2num_min_occs);
acc_var_num_min_occs(m_lemmas, var2num_min_occs);
out << "number of min occs:\n";
for (unsigned v = 0; v < num_vars; v++) {
if (var2num_min_occs[v] > 0)
out << v << ":" << var2num_min_occs[v] << " ";
}
out << "\n";
}
void context::display_profile_res_sub(std::ostream & out) const {
display_var_occs_histogram(std::cerr);
display_num_min_occs(std::cerr);
std::cerr << "\n";
}
void context::display_profile(std::ostream & out) const {
if (m_fparams.m_profile_res_sub)
display_profile_res_sub(out);
}
};

453
src/smt/smt_enode.cpp Normal file
View file

@ -0,0 +1,453 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_enode.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#include"smt_context.h"
#include"smt_enode.h"
namespace smt {
/**
\brief Initialize an enode in the given memory position.
*/
enode * enode::init(ast_manager & m, void * mem, ptr_vector<enode> const & app2enode, app * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent) {
SASSERT(m.is_bool(owner) || !merge_tf);
enode * n = new (mem) enode();
n->m_owner = owner;
n->m_root = n;
n->m_next = n;
n->m_cg = 0;
n->m_class_size = 1;
n->m_generation = generation;
n->m_func_decl_id = UINT_MAX;
n->m_mark = false;
n->m_mark2 = false;
n->m_interpreted = false;
n->m_suppress_args = suppress_args;
n->m_eq = m.is_eq(owner);
n->m_commutative = n->get_num_args() == 2 && owner->get_decl()->is_commutative();
n->m_bool = m.is_bool(owner);
n->m_merge_tf = merge_tf;
n->m_cgc_enabled = cgc_enabled;
n->m_iscope_lvl = iscope_lvl;
n->m_lbl_hash = -1;
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
enode * arg = app2enode[owner->get_arg(i)->get_id()];
n->m_args[i] = arg;
SASSERT(n->get_arg(i) == arg);
if (update_children_parent)
arg->get_root()->m_parents.push_back(n);
}
TRACE("mk_enode_detail", tout << "new enode suppress_args: " << n->m_suppress_args << "\n";);
SASSERT(n->m_suppress_args == suppress_args);
return n;
}
enode * enode::mk(ast_manager & m, region & r, ptr_vector<enode> const & app2enode, app * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent) {
SASSERT(m.is_bool(owner) || !merge_tf);
unsigned sz = get_enode_size(suppress_args ? 0 : owner->get_num_args());
void * mem = r.allocate(sz);
return init(m, mem, app2enode, owner, generation, suppress_args, merge_tf, iscope_lvl, cgc_enabled, update_children_parent);
}
enode * enode::mk_dummy(ast_manager & m, ptr_vector<enode> const & app2enode, app * owner) {
unsigned sz = get_enode_size(owner->get_num_args());
void * mem = alloc_svect(char, sz);
return init(m, mem, app2enode, owner, 0, false, false, 0, true, false);
}
void enode::del_eh(ast_manager & m, bool update_children_parent) {
SASSERT(m_class_size == 1);
SASSERT(m_root == this);
SASSERT(m_next == this);
unsigned num_args = get_num_args();
for (unsigned i = 0; i < num_args; i++) {
enode * arg = get_arg(i);
if (update_children_parent) {
SASSERT(arg->get_root()->m_parents.back() == this);
arg->get_root()->m_parents.pop_back();
}
}
this->~enode();
}
unsigned enode::get_num_th_vars() const {
unsigned r = 0;
theory_var_list const * l = get_th_var_list();
while(l) {
r++;
l = l->get_next();
}
return r;
}
/**
\brief Return the theory var (in theory th_id) associated with
the enode.
Return null_theory_var if the enode is not associated
with a variable of theory th_id
*/
theory_var enode::get_th_var(theory_id th_id) const {
if (m_th_var_list.get_th_var() == null_theory_var)
return null_theory_var;
theory_var_list const * l = &m_th_var_list;
while (l) {
if (l->get_th_id() == th_id) {
return l->get_th_var();
}
l = l->get_next();
}
return null_theory_var;
}
/**
\brief Add the entry (v, id) to the list of theory variables.
*/
void enode::add_th_var(theory_var v, theory_id id, region & r) {
#ifdef Z3DEBUG
unsigned old_size = get_num_th_vars();
#endif
SASSERT(get_th_var(id) == null_theory_var);
if (m_th_var_list.get_th_var() == null_theory_var) {
m_th_var_list.set_th_var(v);
m_th_var_list.set_th_id(id);
m_th_var_list.set_next(0);
}
else {
theory_var_list * l = &m_th_var_list;
while (l->get_next() != 0) {
SASSERT(l->get_th_id() != id);
l = l->get_next();
}
SASSERT(l);
SASSERT(l->get_next() == 0);
theory_var_list * new_cell = new (r) theory_var_list(id, v);
l->set_next(new_cell);
}
SASSERT(get_num_th_vars() == old_size + 1);
SASSERT(get_th_var(id) == v);
}
/**
\brief Replace the entry (v', id) with the entry (v, id).
The enode must have an entry (v', id)
*/
void enode::replace_th_var(theory_var v, theory_id id) {
SASSERT(get_th_var(id) != null_theory_var);
theory_var_list * l = get_th_var_list();
while (l) {
if (l->get_th_id() == id) {
l->set_th_var(v);
return;
}
l = l->get_next();
}
UNREACHABLE();
}
/**
\brief Delete theory variable. It assumes the
enode is associated with a variable of the given theory.
*/
void enode::del_th_var(theory_id id) {
SASSERT(get_th_var(id) != null_theory_var);
if (m_th_var_list.get_th_id() == id) {
theory_var_list * next = m_th_var_list.get_next();
if (next == 0) {
// most common case
m_th_var_list.set_th_var(null_theory_var);
m_th_var_list.set_th_id(null_theory_id);
m_th_var_list.set_next(0);
}
else {
m_th_var_list = *next;
}
}
else {
theory_var_list * prev = get_th_var_list();
theory_var_list * l = prev->get_next();
while (l) {
SASSERT(prev->get_next() == l);
if (l->get_th_id() == id) {
prev->set_next(l->get_next());
return;
}
prev = l;
l = l->get_next();
}
UNREACHABLE();
}
}
/**
\brief Push old value of generation on the context trail stack
and update the generation.
*/
void enode::set_generation(context & ctx, unsigned generation) {
if (m_generation == generation)
return;
ctx.push_trail(value_trail<context, unsigned>(m_generation));
m_generation = generation;
}
void enode::set_lbl_hash(context & ctx) {
SASSERT(m_lbl_hash == -1);
// m_lbl_hash should be different from -1, if and only if,
// there is a pattern that contains the enode. So,
// I use a trail to restore the value of m_lbl_hash to -1.
ctx.push_trail(value_trail<context, char>(m_lbl_hash));
unsigned h = hash_u(get_owner_id());
m_lbl_hash = h & (APPROX_SET_CAPACITY - 1);
// propagate modification to the root m_lbls set.
approx_set & r_lbls = m_root->m_lbls;
if (!r_lbls.may_contain(m_lbl_hash)) {
ctx.push_trail(value_trail<context, approx_set>(r_lbls));
r_lbls.insert(m_lbl_hash);
}
}
enode * enode::get_eq_enode_with_min_gen() {
if (m_generation == 0)
return this;
enode * r = this;
enode * curr = this;
do {
if (curr->m_generation < r->m_generation) {
r = curr;
if (r->m_generation == 0)
return r;
}
curr = curr->m_next;
}
while (curr != this);
return r;
}
#ifdef Z3DEBUG
bool enode::check_invariant() const {
unsigned class_size = 0;
bool found_root = false;
bool found_this = false;
bool has_interpreted = false;
// "Equivalence" class structure.
enode const * curr = this;
do {
SASSERT(curr->m_root == m_root);
class_size++;
if (curr == m_root)
found_root = true;
if (curr == this)
found_this = true;
if (curr->is_interpreted())
has_interpreted = true;
curr = curr->m_next;
}
while (curr != this);
SASSERT(found_root);
SASSERT(found_this);
SASSERT(this != m_root || class_size == m_class_size);
SASSERT(!has_interpreted || m_root->is_interpreted());
// Parent use-list
if (this == m_root) {
enode_vector::const_iterator it2 = m_parents.begin();
enode_vector::const_iterator end2 = m_parents.end();
for (; it2 != end2; ++it2) {
enode const * parent = *it2;
unsigned i = 0;
unsigned num_args = parent->get_num_args();
SASSERT(num_args > 0);
for (; i < num_args; i++) {
enode * arg = parent->get_arg(i);
if (arg->get_root() == m_root)
break;
}
SASSERT(i < num_args);
}
}
// Proof tree
// m_root is reachable from "this" by following the transitivity proof
SASSERT(trans_reaches(m_root));
SASSERT(check_parent_invariant());
return true;
}
/**
\brief Return true if the node is n or n is reached following the
m_proof.m_target pointers
*/
bool enode::trans_reaches(enode * n) const {
const enode * curr = this;
while (curr != 0) {
if (curr == n) {
return true;
}
curr = curr->m_trans.m_target;
}
return false;
}
bool enode::check_parent_invariant() const {
if (this != m_root)
return true;
enode const * curr = m_root;
do {
if (curr != m_root) {
enode_vector::const_iterator it = curr->m_parents.begin();
enode_vector::const_iterator end = curr->m_parents.end();
for (; it != end; ++it) {
enode * p = *it;
if (!p->is_true_eq() && !m_root->contains_parent_congruent_to(p)) {
UNREACHABLE();
}
}
}
curr = curr->m_next;
}
while (curr != m_root);
return true;
}
bool enode::contains_parent_congruent_to(enode * p) const {
enode_vector::const_iterator it = m_parents.begin();
enode_vector::const_iterator end = m_parents.end();
for (; it != end; ++it) {
enode * curr = *it;
if (congruent(curr, p))
return true;
}
return false;
}
#endif
void enode::display_lbls(std::ostream & out) const {
out << "#" << get_owner_id() << " -> #" << get_root()->get_owner_id() << ", lbls: " << get_lbls() << ", plbls: " << get_plbls()
<< ", root->lbls: " << get_root()->get_lbls() << ", root->plbls: " << get_root()->get_plbls();
if (has_lbl_hash())
out << ", lbl-hash: " << static_cast<int>(get_lbl_hash());
out << "\n";
}
bool congruent(enode * n1, enode * n2, bool & comm) {
comm = false;
if (n1->get_owner()->get_decl() != n2->get_owner()->get_decl())
return false;
unsigned num_args = n1->get_num_args();
if (num_args != n2->get_num_args())
return false;
if (n1->is_commutative()) {
enode * c1_1 = n1->get_arg(0)->get_root();
enode * c1_2 = n1->get_arg(1)->get_root();
enode * c2_1 = n2->get_arg(0)->get_root();
enode * c2_2 = n2->get_arg(1)->get_root();
if (c1_1 == c2_1 && c1_2 == c2_2) {
return true;
}
if (c1_1 == c2_2 && c1_2 == c2_1) {
comm = true;
return true;
}
return false;
}
else {
for (unsigned i = 0; i < num_args; i++)
if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root())
return false;
return true;
}
}
unsigned get_max_generation(unsigned num_enodes, enode * const * enodes) {
unsigned max = 0;
for (unsigned i = 0; i < num_enodes; i++) {
unsigned curr = enodes[i]->get_generation();
if (curr > max)
max = curr;
}
return max;
}
void unmark_enodes(unsigned num_enodes, enode * const * enodes) {
for (unsigned i = 0; i < num_enodes; i++)
enodes[i]->unset_mark();
}
void unmark_enodes2(unsigned num_enodes, enode * const * enodes) {
for (unsigned i = 0; i < num_enodes; i++)
enodes[i]->unset_mark2();
}
tmp_enode::tmp_enode():
m_app(0),
m_capacity(0),
m_enode_data(0) {
SASSERT(m_app.get_app()->get_decl() == 0);
set_capacity(5);
}
tmp_enode::~tmp_enode() {
dealloc_svect(m_enode_data);
}
void tmp_enode::set_capacity(unsigned new_capacity) {
SASSERT(new_capacity > m_capacity);
if (m_enode_data)
dealloc_svect(m_enode_data);
m_capacity = new_capacity;
unsigned sz = sizeof(enode) + m_capacity * sizeof(enode*);
m_enode_data = alloc_svect(char, sz);
memset(m_enode_data, 0, sz);
enode * n = get_enode();
n->m_owner = m_app.get_app();
n->m_root = n;
n->m_next = n;
n->m_class_size = 1;
n->m_cgc_enabled = true;
n->m_func_decl_id = UINT_MAX;
}
enode * tmp_enode::set(func_decl * f, unsigned num_args, enode * const * args) {
if (num_args > m_capacity)
set_capacity(num_args * 2);
enode * r = get_enode();
if (m_app.get_app()->get_decl() != f) {
r->m_func_decl_id = UINT_MAX;
}
m_app.set_decl(f);
m_app.set_num_args(num_args);
r->m_commutative = num_args == 2 && f->is_commutative();
memcpy(get_enode()->m_args, args, sizeof(enode*)*num_args);
return r;
}
void tmp_enode::reset() {
get_enode()->m_func_decl_id = UINT_MAX;
}
};

382
src/smt/smt_enode.h Normal file
View file

@ -0,0 +1,382 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_enode.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-18.
Revision History:
--*/
#ifndef _SMT_ENODE_H_
#define _SMT_ENODE_H_
#include"ast.h"
#include"smt_types.h"
#include"smt_eq_justification.h"
#include"smt_theory_var_list.h"
#include"approx_set.h"
namespace smt {
/**
\brief Justification for the transitivity rule.
*/
struct trans_justification {
enode * m_target;
eq_justification m_justification;
trans_justification():
m_target(0),
m_justification(null_eq_justification) {
}
};
class tmp_enode;
/**
\brief Aditional data-structure for implementing congruence closure,
equality propagation, and the theory central bus of equalities.
*/
class enode {
app * m_owner; //!< The application that 'owns' this enode.
enode * m_root; //!< Representative of the equivalence class
enode * m_next; //!< Next element in the equivalence class.
enode * m_cg;
unsigned m_class_size; //!< Size of the equivalence class if the enode is the root.
unsigned m_generation; //!< Tracks how many quantifier instantiation rounds were needed to generate this enode.
unsigned m_func_decl_id; //!< Id generated by the congruence table for fast indexing.
unsigned m_mark:1; //!< Multi-purpose auxiliary mark.
unsigned m_mark2:1; //!< Multi-purpose auxiliary mark.
unsigned m_interpreted:1; //!< True if the node is an interpreted constant.
unsigned m_suppress_args:1; //!< True if the arguments of m_owner should not be accessed by this enode.
unsigned m_eq:1; //!< True if it is an equality
unsigned m_commutative:1; //!< True if commutative app
unsigned m_bool:1; //!< True if it is a boolean enode
unsigned m_merge_tf:1; //!< True if the enode should be merged with true/false when the associated boolean variable is assigned.
unsigned m_cgc_enabled:1; //!< True if congruence closure is enabled for this enode.
unsigned m_iscope_lvl; //!< When the enode was internalized
/*
The following property is valid for m_parents
If this = m_root, then for every term f(a) such that a->get_root() == m_root,
there is a f(b) in m_parents such that b->get_root() == m_root, and f(a) and f(b) are
congruent.
Remark: f(a) and f(b) may have other arguments.
Exception: If f(a) and f(b) are terms of the form (= a c) and (= b d), then
m_parents will not contains (= b d) if b->get_root() == d->get_root().
Remark regarding relevancy propagation: relevancy is propagated to all
elements of an equivalence class. So, if there is a f(a) that is relevant,
then the congruent f(b) in m_parents will also be relevant.
*/
enode_vector m_parents; //!< Parent enodes of the equivalence class.
theory_var_list m_th_var_list; //!< List of theories that 'care' about this enode.
trans_justification m_trans; //!< A justification for the enode being equal to its root.
char m_lbl_hash; //!< It is different from -1, if enode is used in a pattern
approx_set m_lbls;
approx_set m_plbls;
enode * m_args[0]; //!< Cached args
friend class context;
friend class euf_manager;
friend class conflict_resolution;
theory_var_list * get_th_var_list() {
return m_th_var_list.get_th_var() == null_theory_var ? 0 : &m_th_var_list;
}
friend class set_merge_tf_trail;
/**
\brief Return true if the enode should be merged with the true (false) enodes when
the associated boolean variable is assigned to true (false).
*/
bool merge_tf() const {
return m_merge_tf;
}
friend class add_th_var_trail;
friend class replace_th_var_trail;
void add_th_var(theory_var v, theory_id id, region & r);
void replace_th_var(theory_var v, theory_id id);
void del_th_var(theory_id id);
friend class tmp_enode;
static enode * init(ast_manager & m, void * mem, ptr_vector<enode> const & app2enode, app * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent);
public:
static unsigned get_enode_size(unsigned num_args) {
return sizeof(enode) + num_args * sizeof(enode*);
}
static enode * mk(ast_manager & m, region & r, ptr_vector<enode> const & app2enode, app * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent);
static enode * mk_dummy(ast_manager & m, ptr_vector<enode> const & app2enode, app * owner);
static void del_dummy(enode * n) { dealloc_svect(reinterpret_cast<char*>(n)); }
unsigned get_func_decl_id() const {
return m_func_decl_id;
}
void set_func_decl_id(unsigned id) {
m_func_decl_id = id;
}
void mark_as_interpreted() {
SASSERT(!m_interpreted);
SASSERT(m_owner->get_num_args() == 0);
SASSERT(m_class_size == 1);
m_interpreted = true;
}
void del_eh(ast_manager & m, bool update_children_parent = true);
app * get_owner() const {
return m_owner;
}
unsigned get_owner_id() const {
return m_owner->get_id();
}
func_decl * get_decl() const {
return m_owner->get_decl();
}
unsigned get_decl_id() const {
return m_owner->get_decl()->get_decl_id();
}
unsigned hash() const {
return m_owner->hash();
}
enode * get_root() const {
return m_root;
}
enode * get_next() const {
return m_next;
}
unsigned get_num_args() const {
return m_suppress_args ? 0 : m_owner->get_num_args();
}
enode * get_arg(unsigned idx) const {
SASSERT(idx < get_num_args());
return m_args[idx];
}
enode * const * get_args() const {
return m_args;
}
// unsigned get_id() const {
// return m_id;
// }
unsigned get_class_size() const {
return m_class_size;
}
bool is_bool() const {
return m_bool;
}
bool is_eq() const {
return m_eq;
}
bool is_true_eq() const {
return m_eq && get_arg(0)->get_root() == get_arg(1)->get_root();
}
bool is_marked() const {
return m_mark;
}
void set_mark() {
SASSERT(!m_mark); m_mark = true;
}
void unset_mark() {
SASSERT(m_mark); m_mark = false;
}
bool is_marked2() const {
return m_mark2;
}
void set_mark2() {
SASSERT(!m_mark2); m_mark2 = true;
}
void unset_mark2() {
SASSERT(m_mark2); m_mark2 = false;
}
bool is_interpreted() const {
return m_interpreted;
}
/**
\brief Return true if node is not a constant and it is the root of its congruence class.
\remark if get_num_args() == 0, then is_cgr() = false.
*/
bool is_cgr() const {
return m_cg == this;
}
enode * get_cg() const {
return m_cg;
}
bool is_cgc_enabled() const {
return m_cgc_enabled;
}
bool is_commutative() const {
return m_commutative;
}
unsigned get_num_parents() const {
return m_parents.size();
}
enode_vector::iterator begin_parents() {
return m_parents.begin();
}
enode_vector::iterator end_parents() {
return m_parents.end();
}
enode_vector::const_iterator begin_parents() const {
return m_parents.begin();
}
enode_vector::const_iterator end_parents() const {
return m_parents.end();
}
theory_var_list const * get_th_var_list() const {
return m_th_var_list.get_th_var() == null_theory_var ? 0 : &m_th_var_list;
}
bool has_th_vars() const {
return m_th_var_list.get_th_var() != null_theory_var;
}
unsigned get_num_th_vars() const;
theory_var get_th_var(theory_id th_id) const;
unsigned get_generation() const {
return m_generation;
}
void set_generation(context & ctx, unsigned generation);
/**
\brief Return the enode n that is in the eqc of *this, and has the minimal generation.
That is, there is no other enode with smaller generation.
*/
enode * get_eq_enode_with_min_gen();
unsigned get_iscope_lvl() const {
return m_iscope_lvl;
}
void set_lbl_hash(context & ctx);
bool has_lbl_hash() const {
return m_lbl_hash >= 0;
}
unsigned char get_lbl_hash() const {
SASSERT(m_lbl_hash >= 0 && static_cast<unsigned>(m_lbl_hash) < approx_set_traits<unsigned long long>::capacity);
return static_cast<unsigned char>(m_lbl_hash);
}
approx_set & get_lbls() {
return m_lbls;
}
approx_set & get_plbls() {
return m_plbls;
}
const approx_set & get_lbls() const {
return m_lbls;
}
const approx_set & get_plbls() const {
return m_plbls;
}
void display_lbls(std::ostream & out) const;
#ifdef Z3DEBUG
bool check_invariant() const;
bool trans_reaches(enode * n) const;
bool check_parent_invariant() const;
bool contains_parent_congruent_to(enode * p) const;
#endif
};
inline bool same_eqc(enode const * n1 , enode const * n2) { return n1->get_root() == n2->get_root(); }
/**
\brief Return true, if n1 and n2 are congruent.
Set comm to true, if the nodes are congruent modulo commutativity.
*/
bool congruent(enode * n1, enode * n2, bool & comm);
inline bool congruent(enode * n1, enode * n2) {
bool aux;
return congruent(n1, n2, aux);
}
unsigned get_max_generation(unsigned num_enodes, enode * const * enodes);
void unmark_enodes(unsigned num_enodes, enode * const * enodes);
void unmark_enodes2(unsigned num_enodes, enode * const * enodes);
class tmp_enode {
tmp_app m_app;
unsigned m_capacity;
char * m_enode_data;
enode * get_enode() { return reinterpret_cast<enode*>(m_enode_data); }
void set_capacity(unsigned new_capacity);
public:
tmp_enode();
~tmp_enode();
enode * set(func_decl * f, unsigned num_args, enode * const * args);
void reset();
};
};
#endif /* _SMT_ENODE_H_ */

View file

@ -0,0 +1,85 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_eq_justification.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#ifndef _SMT_EQ_JUSTIFICATION_H_
#define _SMT_EQ_JUSTIFICATION_H_
#include"smt_literal.h"
#include"tptr.h"
namespace smt {
/**
\brief Proof like object used to track dependencies of equality propagation.
The idea is to reduce the cost of dependency tracking for the most common
justifications used during equality propagation: (asserted equality & congruence).
*/
class eq_justification {
void * m_data;
public:
enum kind {
AXIOM, //!< no justification, it is only used when proof generation is disabled
CONGRUENCE,
EQUATION, //!< asserted equation
JUSTIFICATION //!< fallback
};
explicit eq_justification():
m_data(reinterpret_cast<void*>(static_cast<size_t>(AXIOM))) {
}
/**
\brief Create a justification for the congruence rule.
If commutativity == true, then it means it is a combined justification: commutativity + congruence.
*/
explicit eq_justification(bool commutativity):
m_data(BOXTAGINT(void*, static_cast<unsigned>(commutativity), CONGRUENCE)) {
}
explicit eq_justification(literal l):
m_data(BOXTAGINT(void*, l.index(), EQUATION)) {
}
explicit eq_justification(justification * js):
m_data(TAG(void*, js, JUSTIFICATION)) {
}
kind get_kind() const {
return static_cast<kind>(GET_TAG(m_data));
}
literal get_literal() const { SASSERT(get_kind() == EQUATION); return to_literal(UNBOXINT(m_data)); }
justification * get_justification() const { SASSERT(get_kind() == JUSTIFICATION); return UNTAG(justification*, m_data); }
bool used_commutativity() const { SASSERT(get_kind() == CONGRUENCE); return UNBOXINT(m_data) != 0; }
static eq_justification mk_axiom() {
return eq_justification();
}
static eq_justification mk_cg(bool comm = false) {
return eq_justification(comm);
}
};
const eq_justification null_eq_justification(static_cast<justification*>(0));
};
#endif /* _SMT_EQ_JUSTIFICATION_H_ */

40
src/smt/smt_failure.h Normal file
View file

@ -0,0 +1,40 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
smt_failure.h
Abstract:
Failures
Author:
Leonardo de Moura (leonardo) 2012-02-09.
Revision History:
--*/
#ifndef _SMT_FAILURE_H_
#define _SMT_FAILURE_H_
namespace smt {
/**
\brief Reason for a l_undef result in the check method.
*/
enum failure {
OK,
UNKNOWN,
TIMEOUT,
MEMOUT,
CANCELED, //!< External cancel flag was set
NUM_CONFLICTS, //!< Maximum number of conflicts was reached
THEORY, //!< Theory is incomplete
QUANTIFIERS //!< Logical context contains universal quantifiers.
};
};
#endif

View file

@ -0,0 +1,298 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_for_each_relevant_expr.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2009-01-05.
Revision History:
--*/
#include"smt_context.h"
#include"smt_for_each_relevant_expr.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
namespace smt {
bool check_at_labels::check(expr* n) {
m_first = true;
return count_at_labels_pos(n) <= 1;
}
unsigned check_at_labels::count_at_labels_lit(expr* n, bool polarity) {
unsigned count = 0;
buffer<symbol> lbls;
bool pos;
if (m_manager.is_label_lit(n, lbls) || (m_manager.is_label(n, pos, lbls) && pos == polarity)) {
buffer<symbol>::const_iterator it = lbls.begin();
buffer<symbol>::const_iterator end = lbls.end();
for (; it != end; ++it) {
symbol const & s = *it;
if (s.contains('@')) {
TRACE("for_each_relevant_expr", tout << "@ label: " << mk_pp(n, m_manager) << "\n";);
count += 1;
}
}
}
return count;
}
unsigned check_at_labels::count_at_labels_neg(expr* n) {
if (!is_app(n)) {
return 0;
}
app* a = to_app(n);
unsigned sz = a->get_num_args();
unsigned count = count_at_labels_lit(n, false);
if (m_manager.is_or(n)) {
for (unsigned i = 0; i < sz; ++i) {
count += count_at_labels_neg(a->get_arg(i));
}
}
else if (m_manager.is_not(n)) {
count = count_at_labels_pos(a->get_arg(0));
}
else if (m_manager.is_implies(n)) {
count += count_at_labels_pos(a->get_arg(0));
count += count_at_labels_neg(a->get_arg(1));
}
else if (m_manager.is_and(n)) {
for (unsigned i = 0; i < sz; ++i) {
count = std::max(count, count_at_labels_neg(a->get_arg(i)));
}
}
if (count > 1 && m_first) {
TRACE("for_each_relevant_expr", tout << mk_pp(n, m_manager) << "\n";);
m_first = false;
}
return count;
}
unsigned check_at_labels::count_at_labels_pos(expr* n) {
if (!is_app(n)) {
return 0;
}
app* a = to_app(n);
unsigned sz = a->get_num_args();
unsigned count = count_at_labels_lit(n, true);
if (m_manager.is_and(n)) {
for (unsigned i = 0; i < sz; ++i) {
count += count_at_labels_pos(a->get_arg(i));
}
}
else if (m_manager.is_not(n)) {
count = count_at_labels_neg(a->get_arg(0));
}
else if (m_manager.is_implies(n)) {
count = std::max(count, count_at_labels_neg(a->get_arg(0)));
count = std::max(count, count_at_labels_pos(a->get_arg(1)));
}
else if (m_manager.is_or(n)) {
for (unsigned i = 0; i < sz; ++i) {
count = std::max(count, count_at_labels_pos(a->get_arg(i)));
}
}
if (count > 1 && m_first) {
TRACE("for_each_relevant_expr", tout << mk_pp(n, m_manager) << "\n";);
m_first = false;
}
return count;
}
for_each_relevant_expr::for_each_relevant_expr(context & ctx):
m_manager(ctx.get_manager()),
m_context(ctx) {
}
void for_each_relevant_expr::operator()(expr * n) {
TRACE("for_each_relevant_expr", tout << "#" << n->get_id() << "\n";);
}
void for_each_relevant_expr::reset() {
m_todo.reset();
m_cache.reset();
}
inline bool for_each_relevant_expr::is_relevant(expr * n) {
return m_context.is_relevant(n);
}
inline lbool for_each_relevant_expr::get_assignment(expr * n) {
if (!m_context.lit_internalized(n))
return l_true; // assume it is a top-level label
return m_context.get_assignment(n);
}
void for_each_relevant_expr::process(expr * n) {
TRACE("for_each_relevant_expr", tout << "processing:\n" << mk_bounded_pp(n, m_manager) << "\n";);
TRACE("for_each_relevant_expr", tout << "processing:\n" << mk_pp(n, m_manager) << "\n";);
if (m_cache.contains(n))
return;
m_todo.reset();
m_todo.push_back(n);
while (!m_todo.empty()) {
expr * curr = m_todo.back();
m_todo.pop_back();
SASSERT(is_relevant(curr));
if (m_cache.contains(curr))
continue;
operator()(curr);
m_cache.insert(curr);
if (!is_app(curr))
continue;
if (to_app(curr)->get_family_id() == m_manager.get_basic_family_id()) {
switch (to_app(curr)->get_decl_kind()) {
case OP_OR:
process_or(to_app(curr));
break;
case OP_AND:
process_and(to_app(curr));
break;
case OP_ITE:
process_ite(to_app(curr));
break;
default:
process_app(to_app(curr));
}
}
else {
process_app(to_app(curr));
}
}
}
void for_each_relevant_expr::process_app(app * n) {
unsigned sz = n->get_num_args();
for (unsigned i = 0; i < sz; i++) {
expr * arg = n->get_arg(i);
if (m_cache.contains(arg))
continue;
SASSERT(is_relevant(arg));
m_todo.push_back(arg);
}
}
/**
\brief Add a relevant child of n (that is assigned to val) to m_todo.
\remark Give preference to a child that is already in the cache.
*/
void for_each_relevant_expr::process_relevant_child(app * n, lbool val) {
unsigned sz = n->get_num_args();
TRACE("for_each_relevant_expr", tout << val << " " << mk_bounded_pp(n, m_manager) << "\n";);
for (unsigned i = 0; i < sz; i++) {
expr * arg = n->get_arg(i);
if (!is_relevant(arg))
continue;
if (get_assignment(arg) != val)
continue;
if (m_cache.contains(arg)) {
TRACE("for_each_relevant_expr", tout << "justified by: " << mk_bounded_pp(arg, m_manager) << "\n";);
return; // the current child justifies n.
}
}
for (unsigned i = 0; i < sz; i++) {
expr * arg = n->get_arg(i);
if (!is_relevant(arg))
continue;
if (get_assignment(arg) != val)
continue;
TRACE("for_each_relevant_expr", tout << "to_process: " << mk_bounded_pp(arg, m_manager) << "\n";);
m_todo.push_back(arg);
return;
}
UNREACHABLE();
}
void for_each_relevant_expr::process_and(app * n) {
switch (get_assignment(n)) {
case l_undef:
UNREACHABLE();
break;
case l_false:
process_relevant_child(n, l_false);
break;
case l_true:
process_app(n);
break;
}
}
void for_each_relevant_expr::process_or(app * n) {
switch (get_assignment(n)) {
case l_undef:
UNREACHABLE();
break;
case l_false:
process_app(n);
break;
case l_true:
process_relevant_child(n, l_true);
break;
}
}
void for_each_relevant_expr::process_ite(app * n) {
if (!m_cache.contains(n->get_arg(0)))
m_todo.push_back(n->get_arg(0));
switch (get_assignment(n->get_arg(0))) {
case l_false:
if (!m_cache.contains(n->get_arg(2)))
m_todo.push_back(n->get_arg(2));
break;
case l_undef:
UNREACHABLE();
break;
case l_true:
if (!m_cache.contains(n->get_arg(1)))
m_todo.push_back(n->get_arg(1));
break;
}
}
void collect_relevant_label_lits::operator()(expr * n) {
TRACE("for_each_relevant_expr",
tout << "label: " << m_manager.is_label_lit(n) << " " << " " << get_assignment(n)
<< " " << mk_bounded_pp(n, m_manager) << "\n";);
if (!m_manager.is_label_lit(n))
return;
if (get_assignment(n) != l_true)
return;
m_manager.is_label_lit(n, m_buffer); // copy symbols to buffer
}
void collect_relevant_labels::operator()(expr * n) {
bool pos;
TRACE("for_each_relevant_expr",
tout << "label: " << m_manager.is_label(n) << " " << get_assignment(n)
<< " " << mk_bounded_pp(n, m_manager) << "\n";);
if (!m_manager.is_label(n, pos))
return;
if (pos && (get_assignment(n) != l_true))
return;
if (!pos && (get_assignment(n) != l_false))
return;
m_manager.is_label(n, pos, m_buffer); // copy symbols to buffer
}
};

View file

@ -0,0 +1,114 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_for_each_relevant_expr.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2009-01-05.
Revision History:
--*/
#ifndef _SMT_FOR_EACH_RELEVANT_EXPR_H_
#define _SMT_FOR_EACH_RELEVANT_EXPR_H_
#include"ast.h"
#include"obj_hashtable.h"
#include"vector.h"
namespace smt {
class context;
class check_at_labels {
ast_manager & m_manager;
bool m_first;
unsigned count_at_labels_pos(expr* n);
unsigned count_at_labels_neg(expr* n);
unsigned count_at_labels_lit(expr* n, bool polarity);
public:
check_at_labels(ast_manager& m) : m_manager(m) {};
/**
\brief Check that 'n' as a formula contains at most one @ label within each and-or path.
*/
bool check(expr* cnstr);
};
/**
\brief Functor used to traverse the relevant expressions in a logical context.
*/
class for_each_relevant_expr {
protected:
ast_manager & m_manager;
context & m_context;
obj_hashtable<expr> m_cache;
ptr_vector<expr> m_todo;
bool m_first;
void process_app(app * n);
void process_relevant_child(app * n, lbool val);
void process_and(app * n);
void process_or(app * n);
void process_ite(app * n);
lbool get_assignment(expr * n);
bool is_relevant(expr * n);
public:
for_each_relevant_expr(context & ctx);
virtual ~for_each_relevant_expr() {}
/**
\brief Visit the relevant sub-expressions of n.
That is, only subexpressions m of n, such that m_context.is_relevant(m).
This method also tries to minimize the number of subexpressions visited.
For each visited expression the method operator() is invoked.
Only not-already-visited expressions are visited.
*/
void process(expr * n);
/**
\see process
*/
virtual void operator()(expr * n);
/**
\brief Reset the cache of already visited expressions.
*/
void reset();
};
class collect_relevant_label_lits : public for_each_relevant_expr {
buffer<symbol> & m_buffer;
public:
collect_relevant_label_lits(context & ctx, buffer<symbol> & b):
for_each_relevant_expr(ctx),
m_buffer(b) {
}
virtual ~collect_relevant_label_lits() {}
virtual void operator()(expr * n);
};
class collect_relevant_labels : public for_each_relevant_expr {
buffer<symbol> & m_buffer;
public:
collect_relevant_labels(context & ctx, buffer<symbol> & b):
for_each_relevant_expr(ctx),
m_buffer(b) {
}
virtual ~collect_relevant_labels() {}
virtual void operator()(expr * n);
};
};
#endif /* _SMT_FOR_EACH_RELEVANT_EXPR_H_ */

1700
src/smt/smt_internalizer.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,419 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_justification.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-25.
Revision History:
--*/
#include"smt_context.h"
#include"smt_conflict_resolution.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
namespace smt {
justification_proof_wrapper::justification_proof_wrapper(context & ctx, proof * pr, bool in_region):
justification(in_region),
m_proof(pr) {
ctx.get_manager().inc_ref(pr);
}
void justification_proof_wrapper::del_eh(ast_manager & m) {
m.dec_ref(m_proof);
}
proof * justification_proof_wrapper::mk_proof(conflict_resolution & cr) {
return m_proof;
}
unit_resolution_justification::unit_resolution_justification(region & r,
justification * js,
unsigned num_lits,
literal const * lits):
m_antecedent(js),
m_num_literals(num_lits) {
SASSERT(!js || js->in_region());
m_literals = new (r) literal[num_lits];
memcpy(m_literals, lits, sizeof(literal) * num_lits);
TRACE("unit_resolution_justification_bug",
for (unsigned i = 0; i < num_lits; i++) {
tout << lits[i] << " ";
}
tout << "\n";);
}
unit_resolution_justification::unit_resolution_justification(justification * js,
unsigned num_lits,
literal const * lits):
justification(false), // object is not allocated in a region
m_antecedent(js),
m_num_literals(num_lits) {
SASSERT(!js || !js->in_region());
m_literals = alloc_vect<literal>(num_lits);
memcpy(m_literals, lits, sizeof(literal) * num_lits);
TRACE("unit_resolution_justification_bug",
for (unsigned i = 0; i < num_lits; i++) {
tout << lits[i] << " ";
}
tout << "\n";);
}
unit_resolution_justification::~unit_resolution_justification() {
if (!in_region()) {
dealloc_svect(m_literals); // I don't need to invoke destructor...
dealloc(m_antecedent);
}
}
void unit_resolution_justification::get_antecedents(conflict_resolution & cr) {
if (m_antecedent)
cr.mark_justification(m_antecedent);
for (unsigned i = 0; i < m_num_literals; i++)
cr.mark_literal(m_literals[i]);
}
proof * unit_resolution_justification::mk_proof(conflict_resolution & cr) {
SASSERT(m_antecedent);
ptr_buffer<proof> prs;
proof * pr = cr.get_proof(m_antecedent);
bool visited = pr != 0;
prs.push_back(pr);
for (unsigned i = 0; i < m_num_literals; i++) {
proof * pr = cr.get_proof(m_literals[i]);
if (pr == 0)
visited = false;
else
prs.push_back(pr);
}
if (!visited)
return 0;
ast_manager & m = cr.get_manager();
TRACE("unit_resolution_justification_bug",
tout << "in mk_proof\n";
for (unsigned i = 0; i < m_num_literals; i++) {
tout << m_literals[i] << " ";
}
tout << "\n";
for (unsigned i = 0; i < prs.size(); i++) {
tout << mk_ll_pp(m.get_fact(prs[i]), m);
});
return m.mk_unit_resolution(prs.size(), prs.c_ptr());
}
void eq_conflict_justification::get_antecedents(conflict_resolution & cr) {
SASSERT(m_node1->get_root()->is_interpreted());
SASSERT(m_node2->get_root()->is_interpreted());
cr.mark_eq(m_node1, m_node1->get_root());
cr.mark_eq(m_node2, m_node2->get_root());
cr.mark_justified_eq(m_node1, m_node2, m_js);
}
proof * eq_conflict_justification::mk_proof(conflict_resolution & cr) {
ast_manager & m = cr.get_manager();
bool visited = true;
ptr_buffer<proof> prs;
if (m_node1 != m_node1->get_root()) {
proof * pr = cr.get_proof(m_node1, m_node1->get_root());
if (pr && m.fine_grain_proofs())
pr = m.mk_symmetry(pr);
prs.push_back(pr);
if (!pr)
visited = false;
}
SASSERT(m_node1 != m_node2);
proof * pr = cr.get_proof(m_node1, m_node2, m_js);
prs.push_back(pr);
if (!pr)
visited = false;
if (m_node2 != m_node2->get_root()) {
proof * pr = cr.get_proof(m_node2, m_node2->get_root());
prs.push_back(pr);
if (!pr)
visited = false;
}
if (!visited)
return 0;
expr * lhs = m_node1->get_root()->get_owner();
expr * rhs = m_node2->get_root()->get_owner();
proof * pr1 = m.mk_transitivity(prs.size(), prs.c_ptr(), lhs, rhs);
proof * pr2 = m.mk_rewrite(m.mk_eq(lhs, rhs), m.mk_false());
return m.mk_modus_ponens(pr1, pr2);
}
void eq_root_propagation_justification::get_antecedents(conflict_resolution & cr) {
cr.mark_eq(m_node, m_node->get_root());
}
proof * eq_root_propagation_justification::mk_proof(conflict_resolution & cr) {
ast_manager & m = cr.get_manager();
expr * var = m_node->get_owner();
expr * val = m_node->get_root()->get_owner();
SASSERT(m.is_true(val) || m.is_false(val));
proof * pr1 = cr.get_proof(m_node, m_node->get_root());
if (pr1) {
expr * lit;
if (m.is_true(val))
lit = var;
else
lit = m.mk_not(var);
proof * pr2 = m.mk_rewrite(m.get_fact(pr1), lit);
return m.mk_modus_ponens(pr1, pr2);
}
return 0;
}
void eq_propagation_justification::get_antecedents(conflict_resolution & cr) {
cr.mark_eq(m_node1, m_node2);
}
proof * eq_propagation_justification::mk_proof(conflict_resolution & cr) {
return cr.get_proof(m_node1, m_node2);
}
void mp_iff_justification::get_antecedents(conflict_resolution & cr) {
SASSERT(m_node1 != m_node2);
cr.mark_eq(m_node1, m_node2);
context & ctx = cr.get_context();
bool_var v = ctx.enode2bool_var(m_node1);
lbool val = ctx.get_assignment(v);
literal l(v, val == l_false);
cr.mark_literal(l);
}
proof * mp_iff_justification::mk_proof(conflict_resolution & cr) {
proof * pr1 = cr.get_proof(m_node1, m_node2);
context & ctx = cr.get_context();
bool_var v = ctx.enode2bool_var(m_node1);
lbool val = ctx.get_assignment(v);
literal l(v, val == l_false);
proof * pr2 = cr.get_proof(l);
if (pr1 && pr2) {
ast_manager & m = cr.get_manager();
proof * pr;
SASSERT(m.has_fact(pr1));
SASSERT(m.has_fact(pr2));
app* fact1 = to_app(m.get_fact(pr1));
app* fact2 = to_app(m.get_fact(pr2));
SASSERT(m.is_iff(fact1));
if (fact1->get_arg(1) == fact2) {
pr1 = m.mk_symmetry(pr1);
fact1 = to_app(m.get_fact(pr1));
}
SASSERT(m.is_iff(fact1));
if (l.sign()) {
SASSERT(m.is_not(fact2));
expr* lhs = fact1->get_arg(0);
expr* rhs = fact1->get_arg(1);
if (lhs != fact2->get_arg(0)) {
pr1 = m.mk_symmetry(pr1);
fact1 = to_app(m.get_fact(pr1));
std::swap(lhs, rhs);
}
SASSERT(lhs == fact2->get_arg(0));
app* new_lhs = fact2;
app* new_rhs = m.mk_not(rhs);
pr1 = m.mk_congruence(new_lhs, new_rhs, 1, &pr1);
}
pr = m.mk_modus_ponens(pr2, pr1);
TRACE("mp_iff_justification", tout << mk_pp(fact1, m) << "\n" << mk_pp(fact2, m) << "\n" <<
mk_pp(m.get_fact(pr), m) << "\n";);
return pr;
}
return 0;
}
simple_justification::simple_justification(region & r, unsigned num_lits, literal const * lits):
m_num_literals(num_lits) {
m_literals = new (r) literal[num_lits];
memcpy(m_literals, lits, sizeof(literal) * num_lits);
#ifdef Z3DEBUG
for (unsigned i = 0; i < num_lits; i++) {
SASSERT(lits[i] != null_literal);
}
#endif
}
void simple_justification::get_antecedents(conflict_resolution & cr) {
for (unsigned i = 0; i < m_num_literals; i++)
cr.mark_literal(m_literals[i]);
}
bool simple_justification::antecedent2proof(conflict_resolution & cr, ptr_buffer<proof> & result) {
bool visited = true;
for (unsigned i = 0; i < m_num_literals; i++) {
proof * pr = cr.get_proof(m_literals[i]);
if (pr == 0)
visited = false;
else
result.push_back(pr);
}
return visited;
}
proof * theory_axiom_justification::mk_proof(conflict_resolution & cr) {
context & ctx = cr.get_context();
ast_manager & m = cr.get_manager();
expr_ref_vector lits(m);
for (unsigned i = 0; i < m_num_literals; i++) {
expr_ref l(m);
ctx.literal2expr(m_literals[i], l);
lits.push_back(l);
}
if (lits.size() == 1)
return m.mk_th_lemma(m_th_id, lits.get(0), 0, 0, m_params.size(), m_params.c_ptr());
else
return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, 0, m_params.size(), m_params.c_ptr());
}
proof * theory_propagation_justification::mk_proof(conflict_resolution & cr) {
ptr_buffer<proof> prs;
if (!antecedent2proof(cr, prs))
return 0;
context & ctx = cr.get_context();
ast_manager & m = cr.get_manager();
expr_ref fact(m);
ctx.literal2expr(m_consequent, fact);
return m.mk_th_lemma(m_th_id, fact, prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr());
}
proof * theory_conflict_justification::mk_proof(conflict_resolution & cr) {
ptr_buffer<proof> prs;
if (!antecedent2proof(cr, prs))
return 0;
ast_manager & m = cr.get_manager();
return m.mk_th_lemma(m_th_id, m.mk_false(), prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr());
}
ext_simple_justification::ext_simple_justification(region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs):
simple_justification(r, num_lits, lits),
m_num_eqs(num_eqs) {
m_eqs = new (r) enode_pair[num_eqs];
memcpy(m_eqs, eqs, sizeof(enode_pair) * num_eqs);
DEBUG_CODE({
for (unsigned i = 0; i < num_eqs; i++) {
SASSERT(eqs[i].first->get_root() == eqs[i].second->get_root());
}
});
}
void ext_simple_justification::get_antecedents(conflict_resolution & cr) {
simple_justification::get_antecedents(cr);
for (unsigned i = 0; i < m_num_eqs; i++) {
enode_pair const & p = m_eqs[i];
cr.mark_eq(p.first, p.second);
}
}
bool ext_simple_justification::antecedent2proof(conflict_resolution & cr, ptr_buffer<proof> & result) {
bool visited = simple_justification::antecedent2proof(cr, result);
for (unsigned i = 0; i < m_num_eqs; i++) {
enode_pair const & p = m_eqs[i];
proof * pr = cr.get_proof(p.first, p.second);
if (pr == 0)
visited = false;
else
result.push_back(pr);
}
return visited;
}
proof * ext_theory_propagation_justification::mk_proof(conflict_resolution & cr) {
ptr_buffer<proof> prs;
if (!antecedent2proof(cr, prs))
return 0;
context & ctx = cr.get_context();
ast_manager & m = cr.get_manager();
expr_ref fact(m);
ctx.literal2expr(m_consequent, fact);
return m.mk_th_lemma(m_th_id, fact, prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr());
}
proof * ext_theory_conflict_justification::mk_proof(conflict_resolution & cr) {
ptr_buffer<proof> prs;
if (!antecedent2proof(cr, prs))
return 0;
ast_manager & m = cr.get_manager();
return m.mk_th_lemma(m_th_id, m.mk_false(), prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr());
}
proof * ext_theory_eq_propagation_justification::mk_proof(conflict_resolution & cr) {
ptr_buffer<proof> prs;
if (!antecedent2proof(cr, prs))
return 0;
ast_manager & m = cr.get_manager();
context & ctx = cr.get_context();
expr * fact = ctx.mk_eq_atom(m_lhs->get_owner(), m_rhs->get_owner());
return m.mk_th_lemma(m_th_id, fact, prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr());
}
theory_lemma_justification::theory_lemma_justification(family_id fid, context & ctx, unsigned num_lits, literal const * lits,
unsigned num_params, parameter* params):
justification(false),
m_th_id(fid),
m_params(num_params, params),
m_num_literals(num_lits) {
ast_manager & m = ctx.get_manager();
m_literals = alloc_svect(expr*, num_lits);
for (unsigned i = 0; i < num_lits; i++) {
bool sign = lits[i].sign();
expr * v = ctx.bool_var2expr(lits[i].var());
m.inc_ref(v);
m_literals[i] = TAG(expr*, v, sign);
}
SASSERT(!in_region());
}
theory_lemma_justification::~theory_lemma_justification() {
SASSERT(!in_region());
dealloc_svect(m_literals);
}
void theory_lemma_justification::del_eh(ast_manager & m) {
for (unsigned i = 0; i < m_num_literals; i++) {
m.dec_ref(UNTAG(expr*, m_literals[i]));
}
m_params.reset();
}
proof * theory_lemma_justification::mk_proof(conflict_resolution & cr) {
ast_manager & m = cr.get_manager();
expr_ref_vector lits(m);
for (unsigned i = 0; i < m_num_literals; i++) {
bool sign = GET_TAG(m_literals[i]) != 0;
expr * v = UNTAG(expr*, m_literals[i]);
expr_ref l(m);
if (sign)
l = m.mk_not(v);
else
l = v;
lits.push_back(l);
}
if (lits.size() == 1)
return m.mk_th_lemma(m_th_id, lits.get(0), 0, 0, m_params.size(), m_params.c_ptr());
else
return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, 0, m_params.size(), m_params.c_ptr());
}
};

412
src/smt/smt_justification.h Normal file
View file

@ -0,0 +1,412 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_justification.h
Abstract:
Proof-like objects for tracking dependencies in the SMT engine, and generating proofs.
Author:
Leonardo de Moura (leonardo) 2008-02-18.
Revision History:
--*/
#ifndef _SMT_JUSTIFICATION_H_
#define _SMT_JUSTIFICATION_H_
#include"ast.h"
#include"smt_types.h"
#include"smt_literal.h"
#include"smt_eq_justification.h"
namespace smt {
class conflict_resolution;
typedef ptr_vector<justification> justification_vector;
/**
\brief Pseudo-proof objects. They are mainly used to track dependencies.
When proof generation is enabled, they are also used to produce proofs.
Justification objects are allocated using a stack based policy.
Actually, there is one exception: justification of lemmas.
Lemmas created at scope level n may remain alive even after scope level n is backtracked.
Lemmas are deleted by a GC that runs from time to time. So, a justification attached
to a lemma will may remain alive after scope level n is backtracked.
So, I allow justification objects to be allocated in regions and
in the regular heap. The method in_region() should return true if the object is
allocated in a region.
*/
class justification {
unsigned m_mark:1;
unsigned m_in_region:1; // true if the object was allocated in a region.
public:
justification(bool in_region = true):m_mark(false), m_in_region(in_region) {}
virtual ~justification() {}
/**
\brief This method should return true if the method del_eh needs to be invoked
to free resources.
*/
virtual bool has_del_eh() const {
return false;
}
/**
\brief Free the resources allocated by this object.
*/
virtual void del_eh(ast_manager & m) {
}
/**
\brief Mark the antecedents the justification object.
The antecedents are marked using the mark methods of the
conflict_resolution object.
*/
virtual void get_antecedents(conflict_resolution & cr){
}
/**
\brief Return the id of the theory that produced the proof object.
*/
virtual theory_id get_from_theory() const {
return null_theory_id;
}
void set_mark() { SASSERT(!m_mark); m_mark = true; }
void unset_mark() { SASSERT(m_mark); m_mark = false; }
bool is_marked() const { return m_mark; }
unsigned hash() const { return get_ptr_hash(this); }
virtual proof * mk_proof(conflict_resolution & cr) = 0;
bool in_region() const { return m_in_region; }
virtual char const * get_name() const { return "unknown"; }
virtual void display_debug_info(conflict_resolution & cr, std::ostream & out) { /* do nothing */ }
};
class justification_proof_wrapper : public justification {
proof * m_proof;
public:
justification_proof_wrapper(context & ctx, proof * pr, bool in_region = true);
virtual bool has_del_eh() const {
return true;
}
virtual void del_eh(ast_manager & m);
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "proof-wrapper"; }
};
class unit_resolution_justification : public justification {
justification * m_antecedent;
unsigned m_num_literals;
literal * m_literals;
public:
unit_resolution_justification(region & r, justification * js, unsigned num_lits, literal const * lits);
unit_resolution_justification(justification * js, unsigned num_lits, literal const * lits);
~unit_resolution_justification();
virtual bool has_del_eh() const {
return !in_region() && m_antecedent && m_antecedent->has_del_eh();
}
virtual void del_eh(ast_manager & m) {
if (!in_region() && m_antecedent) m_antecedent->del_eh(m);
}
virtual void get_antecedents(conflict_resolution & cr);
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "unit-resolution"; }
};
class eq_conflict_justification : public justification {
enode * m_node1;
enode * m_node2;
eq_justification m_js;
public:
eq_conflict_justification(enode * n1, enode * n2, eq_justification js):
m_node1(n1),
m_node2(n2),
m_js(js) {
}
virtual void get_antecedents(conflict_resolution & cr);
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "eq-conflict"; }
};
/**
\brief Justification for m_node = root
*/
class eq_root_propagation_justification : public justification {
enode * m_node;
public:
eq_root_propagation_justification(enode * n):m_node(n) {
}
virtual void get_antecedents(conflict_resolution & cr);
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "eq-root"; }
};
/**
\brief Justification for m_node1 = m_node2
*/
class eq_propagation_justification : public justification {
enode * m_node1;
enode * m_node2;
public:
eq_propagation_justification(enode * n1, enode * n2):m_node1(n1), m_node2(n2) {
}
virtual void get_antecedents(conflict_resolution & cr);
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "eq-propagation"; }
};
/**
\brief Justification for p(x) <=> p(y), p(x) ===> p(y)
*/
class mp_iff_justification : public justification {
enode * m_node1; // p(x)
enode * m_node2; // p(y)
public:
mp_iff_justification(enode * n1, enode * n2):m_node1(n1), m_node2(n2) {
}
virtual void get_antecedents(conflict_resolution & cr);
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "mp-iff"; }
};
/**
\brief Abstract class for justifications that contains set of literals.
*/
class simple_justification : public justification {
protected:
unsigned m_num_literals;
literal * m_literals;
bool antecedent2proof(conflict_resolution & cr, ptr_buffer<proof> & result);
public:
simple_justification(region & r, unsigned num_lits, literal const * lits);
virtual void get_antecedents(conflict_resolution & cr);
virtual proof * mk_proof(conflict_resolution & cr) = 0;
virtual char const * get_name() const { return "simple"; }
};
class simple_theory_justification : public simple_justification {
protected:
family_id m_th_id;
vector<parameter> m_params;
public:
simple_theory_justification(
family_id fid, region & r,
unsigned num_lits, literal const * lits,
unsigned num_params, parameter* params):
simple_justification(r, num_lits, lits),
m_th_id(fid), m_params(num_params, params) {}
virtual ~simple_theory_justification() {}
virtual bool has_del_eh() const { return !m_params.empty(); }
virtual void del_eh(ast_manager & m) { m_params.reset(); }
virtual theory_id get_from_theory() const { return m_th_id; }
};
class theory_axiom_justification : public simple_theory_justification {
public:
theory_axiom_justification(family_id fid, region & r,
unsigned num_lits, literal const * lits,
unsigned num_params = 0, parameter* params = 0):
simple_theory_justification(fid, r, num_lits, lits, num_params, params) {}
virtual void get_antecedents(conflict_resolution & cr) {}
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "theory-axiom"; }
};
class theory_propagation_justification : public simple_theory_justification {
literal m_consequent;
public:
theory_propagation_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, literal consequent,
unsigned num_params = 0, parameter* params = 0):
simple_theory_justification(fid, r, num_lits, lits, num_params, params), m_consequent(consequent) {}
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "theory-propagation"; }
};
class theory_conflict_justification : public simple_theory_justification {
public:
theory_conflict_justification(family_id fid, region & r, unsigned num_lits, literal const * lits,
unsigned num_params = 0, parameter* params = 0):
simple_theory_justification(fid, r, num_lits, lits, num_params, params) {}
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "theory-conflict"; }
};
/**
\brief Abstract class for justifications that contains set of literals and equalities.
*/
class ext_simple_justification : public simple_justification {
protected:
unsigned m_num_eqs;
enode_pair * m_eqs;
bool antecedent2proof(conflict_resolution & cr, ptr_buffer<proof> & result);
public:
ext_simple_justification(region & r, unsigned num_lits, literal const * lits,
unsigned num_eqs, enode_pair const * eqs);
virtual void get_antecedents(conflict_resolution & cr);
virtual proof * mk_proof(conflict_resolution & cr) = 0;
virtual char const * get_name() const { return "ext-simple"; }
};
/**
\brief Abstract class for justifications that contains set of literals and equalities.
*/
class ext_theory_simple_justification : public ext_simple_justification {
protected:
family_id m_th_id;
vector<parameter> m_params;
public:
ext_theory_simple_justification(family_id fid, region & r, unsigned num_lits, literal const * lits,
unsigned num_eqs, enode_pair const * eqs,
unsigned num_params = 0, parameter* params = 0):
ext_simple_justification(r, num_lits, lits, num_eqs, eqs), m_th_id(fid), m_params(num_params, params) {}
virtual ~ext_theory_simple_justification() {}
virtual bool has_del_eh() const { return !m_params.empty(); }
virtual void del_eh(ast_manager & m) { m_params.reset(); }
virtual theory_id get_from_theory() const { return m_th_id; }
};
class ext_theory_propagation_justification : public ext_theory_simple_justification {
literal m_consequent;
public:
ext_theory_propagation_justification(family_id fid, region & r,
unsigned num_lits, literal const * lits,
unsigned num_eqs, enode_pair const * eqs,
literal consequent,
unsigned num_params = 0, parameter* params = 0):
ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params),
m_consequent(consequent) {}
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "ext-theory-propagation"; }
};
class ext_theory_conflict_justification : public ext_theory_simple_justification {
public:
ext_theory_conflict_justification(family_id fid, region & r, unsigned num_lits, literal const * lits,
unsigned num_eqs, enode_pair const * eqs,
unsigned num_params = 0, parameter* params = 0):
ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params) {}
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "ext-theory-conflict"; }
};
class ext_theory_eq_propagation_justification : public ext_theory_simple_justification {
enode * m_lhs;
enode * m_rhs;
public:
ext_theory_eq_propagation_justification(
family_id fid, region & r,
unsigned num_lits, literal const * lits,
unsigned num_eqs, enode_pair const * eqs,
enode * lhs, enode * rhs,
unsigned num_params = 0, parameter* params = 0):
ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params), m_lhs(lhs), m_rhs(rhs) {}
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "ext-theory-eq-propagation"; }
};
/**
\brief A theory lemma is similar to a theory axiom, but it is attached to a CLS_AUX_LEMMA clause instead of CLS_AUX.
So, it cannot be stored in the heap, and it is unsafe to store literals, since it may be deleted during backtracking.
Instead, they store a set of pairs (sign, expr). This pair is represented as a tagged pointer.
*/
class theory_lemma_justification : public justification {
family_id m_th_id;
vector<parameter> m_params;
unsigned m_num_literals;
expr ** m_literals;
public:
theory_lemma_justification(family_id fid, context & ctx, unsigned num_lits, literal const * lits,
unsigned num_params = 0, parameter* params = 0);
virtual ~theory_lemma_justification();
virtual bool has_del_eh() const {
return true;
}
virtual void del_eh(ast_manager & m);
virtual void get_antecedents(conflict_resolution & cr) {}
virtual proof * mk_proof(conflict_resolution & cr);
virtual char const * get_name() const { return "theory-lemma"; }
};
};
#endif /* _SMT_JUSTIFICATION_H_ */

100
src/smt/smt_literal.cpp Normal file
View file

@ -0,0 +1,100 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_literal.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-18.
Revision History:
--*/
#include"smt_literal.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
namespace smt {
void literal::display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const {
if (*this == true_literal)
out << "true";
else if (*this == false_literal)
out << "false";
else if (sign())
out << "(not " << mk_pp(bool_var2expr_map[var()], m) << ")";
else
out << mk_pp(bool_var2expr_map[var()], m);
}
void literal::display_compact(std::ostream & out, expr * const * bool_var2expr_map) const {
if (*this == true_literal)
out << "true";
else if (*this == false_literal)
out << "false";
else if (sign())
out << "(not #" << bool_var2expr_map[var()]->get_id() << ")";
else
out << "#" << bool_var2expr_map[var()]->get_id();
}
std::ostream & operator<<(std::ostream & out, literal l) {
if (l == true_literal)
out << "true";
else if (l == false_literal)
out << "false";
else if (l.sign())
out << "(not p" << l.var() << ")";
else
out << "p" << l.var();
return out;
}
std::ostream & operator<<(std::ostream & out, const literal_vector & v) {
display(out, v.begin(), v.end());
return out;
}
void display_compact(std::ostream & out, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map) {
for (unsigned i = 0; i < num_lits; i++) {
if (i > 0)
out << " ";
lits[i].display_compact(out, bool_var2expr_map);
}
}
void display_verbose(std::ostream & out, ast_manager& m, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map) {
for (unsigned i = 0; i < num_lits; i++) {
if (i > 0)
out << " ";
lits[i].display(out, m, bool_var2expr_map);
}
}
/**
\brief Return true if lits1 subsumes lits2.
That is every literal in lits1 is in lits2
*/
bool backward_subsumption(unsigned num_lits1, literal const * lits1, unsigned num_lits2, literal const * lits2) {
unsigned i = 0;
for (; i < num_lits1; i++) {
literal l1 = lits1[i];
unsigned j = 0;
for (; j < num_lits2; j++)
if (l1 == lits2[j])
break;
if (j == num_lits2)
break;
}
return i == num_lits1;
}
};

122
src/smt/smt_literal.h Normal file
View file

@ -0,0 +1,122 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_literal.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-18.
Revision History:
--*/
#ifndef _SMT_LITERAL_H_
#define _SMT_LITERAL_H_
#include"ast.h"
#include"smt_types.h"
#include"approx_set.h"
namespace smt {
/**
\brief The literal b is represented by the value 2*b, and
the literal (not b) by the value 2*b + 1
*/
class literal {
int m_val;
public:
literal():m_val(-2) {
SASSERT(var() == null_bool_var && !sign());
}
explicit literal(bool_var v, bool sign = false):
m_val((v << 1) + static_cast<int>(sign)) {
}
bool_var var() const {
return m_val >> 1;
}
bool sign() const {
return m_val & 1;
}
int index() const {
return m_val;
}
void neg() {
m_val = m_val ^ 1;
}
friend literal operator~(literal l);
friend literal to_literal(int x);
void display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const;
void display_compact(std::ostream & out, expr * const * bool_var2expr_map) const;
unsigned hash() const { return m_val; }
};
inline bool operator==(literal l1, literal l2) {
return l1.index() == l2.index();
}
inline bool operator!=(literal l1, literal l2) {
return l1.index() != l2.index();
}
inline bool operator<(literal l1, literal l2) {
return l1.index() < l2.index();
}
inline literal operator~(literal l) {
literal r;
r.m_val = l.m_val ^ 1;
return r;
}
inline literal to_literal(int x) {
literal l;
l.m_val = x;
return l;
}
const literal null_literal;
const literal true_literal(true_bool_var, false);
const literal false_literal(true_bool_var, true);
typedef svector<literal> literal_vector;
typedef sbuffer<literal> literal_buffer;
std::ostream & operator<<(std::ostream & out, literal l);
std::ostream & operator<<(std::ostream & out, const literal_vector & v);
void display_compact(std::ostream & out, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map);
void display_verbose(std::ostream & out, ast_manager& m, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map);
template<typename T>
void neg_literals(unsigned num_lits, literal const * lits, T & result) {
for (unsigned i = 0; i < num_lits; ++i)
result.push_back(~lits[i]);
}
struct literal2unsigned { unsigned operator()(literal l) const { return l.index(); } };
typedef approx_set_tpl<literal, literal2unsigned> literal_approx_set;
bool backward_subsumption(unsigned num_lits1, literal const * lits1, unsigned num_lits2, literal const * lits2);
};
#endif /* _SMT_LITERAL_H_ */

View file

@ -0,0 +1,419 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_model_checker.cpp
Abstract:
Model checker
Author:
Leonardo de Moura (leonardo) 2010-12-03.
Revision History:
--*/
#include"smt_model_checker.h"
#include"smt_context.h"
#include"smt_model_finder.h"
#include"pull_quant.h"
#include"for_each_expr.h"
#include"var_subst.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"model_pp.h"
#include"ast_smt2_pp.h"
namespace smt {
model_checker::model_checker(ast_manager & m, qi_params const & p, model_finder & mf):
m_manager(m),
m_params(p),
m_qm(0),
m_context(0),
m_root2value(0),
m_model_finder(mf),
m_max_cexs(1),
m_iteration_idx(0),
m_curr_model(0),
m_new_instances_bindings(m) {
}
model_checker::~model_checker() {
m_aux_context = 0; // delete aux context before fparams
m_fparams = 0;
}
quantifier * model_checker::get_flat_quantifier(quantifier * q) {
return m_model_finder.get_flat_quantifier(q);
}
void model_checker::set_qm(quantifier_manager & qm) {
SASSERT(m_qm == 0);
SASSERT(m_context == 0);
m_qm = &qm;
m_context = &(m_qm->kernel());
}
/**
\brief Return a term in the context that evaluates to val.
*/
expr * model_checker::get_term_from_ctx(expr * val) {
if (m_value2expr.empty()) {
// populate m_value2expr
obj_map<enode, app *>::iterator it = m_root2value->begin();
obj_map<enode, app *>::iterator end = m_root2value->end();
for (; it != end; ++it) {
enode * n = (*it).m_key;
expr * val = (*it).m_value;
n = n->get_eq_enode_with_min_gen();
m_value2expr.insert(val, n->get_owner());
}
}
expr * t = 0;
m_value2expr.find(val, t);
return t;
}
/**
\brief Assert in m_aux_context, the constraint
sk = e_1 OR ... OR sk = e_n
where {e_1, ..., e_n} is the universe.
*/
void model_checker::restrict_to_universe(expr * sk, obj_hashtable<expr> const & universe) {
SASSERT(!universe.empty());
ptr_buffer<expr> eqs;
obj_hashtable<expr>::iterator it = universe.begin();
obj_hashtable<expr>::iterator end = universe.end();
for (; it != end; ++it) {
expr * e = *it;
eqs.push_back(m_manager.mk_eq(sk, e));
}
m_aux_context->assert_expr(m_manager.mk_or(eqs.size(), eqs.c_ptr()));
}
#define PP_DEPTH 8
/**
\brief Assert the negation of q after applying the interpretation in m_curr_model to the uninterpreted symbols in q.
The variables are replaced by skolem constants. These constants are stored in sks.
*/
void model_checker::assert_neg_q_m(quantifier * q, expr_ref_vector & sks) {
expr_ref tmp(m_manager);
m_curr_model->eval(q->get_expr(), tmp, true);
TRACE("model_checker", tout << "q after applying interpretation:\n" << mk_ismt2_pp(tmp, m_manager) << "\n";);
ptr_buffer<expr> subst_args;
unsigned num_decls = q->get_num_decls();
subst_args.resize(num_decls, 0);
sks.resize(num_decls, 0);
for (unsigned i = 0; i < num_decls; i++) {
sort * s = q->get_decl_sort(num_decls - i - 1);
expr * sk = m_manager.mk_fresh_const(0, s);
sks[num_decls - i - 1] = sk;
subst_args[num_decls - i - 1] = sk;
if (m_curr_model->is_finite(s)) {
restrict_to_universe(sk, m_curr_model->get_known_universe(s));
}
}
expr_ref sk_body(m_manager);
var_subst s(m_manager);
s(tmp, subst_args.size(), subst_args.c_ptr(), sk_body);
expr_ref r(m_manager);
r = m_manager.mk_not(sk_body);
TRACE("model_checker", tout << "mk_neg_q_m:\n" << mk_ismt2_pp(r, m_manager) << "\n";);
m_aux_context->assert_expr(r);
}
bool model_checker::add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv) {
if (cex == 0)
return false; // no model available.
unsigned num_decls = q->get_num_decls();
unsigned num_sks = sks.size();
// Remark: sks were created for the flat version of q.
SASSERT(num_sks >= num_decls);
expr_ref_buffer bindings(m_manager);
bindings.resize(num_decls);
unsigned max_generation = 0;
for (unsigned i = 0; i < num_decls; i++) {
expr * sk = sks.get(num_decls - i - 1);
func_decl * sk_d = to_app(sk)->get_decl();
expr_ref sk_value(m_manager);
sk_value = cex->get_const_interp(sk_d);
if (sk_value == 0) {
sk_value = cex->get_some_value(sk_d->get_range());
if (sk_value == 0)
return false; // get_some_value failed... giving up
}
if (use_inv) {
unsigned sk_term_gen;
expr * sk_term = m_model_finder.get_inv(q, i, sk_value, sk_term_gen);
if (sk_term != 0) {
SASSERT(!m_manager.is_model_value(sk_term));
if (sk_term_gen > max_generation)
max_generation = sk_term_gen;
sk_value = sk_term;
}
else {
return false;
}
}
else {
expr * sk_term = get_term_from_ctx(sk_value);
if (sk_term != 0) {
sk_value = sk_term;
}
else {
if (m_manager.is_model_value(sk_value))
return false;
}
}
bindings.set(num_decls - i - 1, sk_value);
}
TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance:\n";
for (unsigned i = 0; i < num_decls; i++) {
tout << mk_ismt2_pp(bindings[i], m_manager) << "\n";
});
for (unsigned i = 0; i < num_decls; i++)
m_new_instances_bindings.push_back(bindings[i]);
void * mem = m_new_instances_region.allocate(instance::get_obj_size(q->get_num_decls()));
instance * new_inst = new (mem) instance(q, bindings.c_ptr(), max_generation);
m_new_instances.push_back(new_inst);
return true;
}
bool model_checker::add_blocking_clause(model * cex, expr_ref_vector & sks) {
SASSERT(cex != 0);
unsigned num_sks = sks.size();
expr_ref_buffer diseqs(m_manager);
for (unsigned i = 0; i < num_sks; i++) {
expr * sk = sks.get(i);
func_decl * sk_d = to_app(sk)->get_decl();
expr_ref sk_value(m_manager);
sk_value = cex->get_const_interp(sk_d);
if (sk_value == 0) {
sk_value = cex->get_some_value(sk_d->get_range());
if (sk_value == 0)
return false; // get_some_value failed... aborting add_blocking_clause
}
diseqs.push_back(m_manager.mk_not(m_manager.mk_eq(sk, sk_value)));
}
expr_ref blocking_clause(m_manager);
blocking_clause = m_manager.mk_or(diseqs.size(), diseqs.c_ptr());
TRACE("model_checker", tout << "blocking clause:\n" << mk_ismt2_pp(blocking_clause, m_manager) << "\n";);
m_aux_context->assert_expr(blocking_clause);
return true;
}
/**
\brief Return true if q is satisfied by m_curr_model.
*/
bool model_checker::check(quantifier * q) {
SASSERT(!m_aux_context->relevancy());
m_aux_context->push();
quantifier * flat_q = get_flat_quantifier(q);
TRACE("model_checker", tout << "model checking:\n" << mk_ismt2_pp(q->get_expr(), m_manager) << "\n" <<
mk_ismt2_pp(flat_q->get_expr(), m_manager) << "\n";);
expr_ref_vector sks(m_manager);
assert_neg_q_m(flat_q, sks);
TRACE("model_checker", tout << "skolems:\n";
for (unsigned i = 0; i < sks.size(); i++) {
expr * sk = sks.get(i);
tout << mk_ismt2_pp(sk, m_manager) << " " << mk_pp(m_manager.get_sort(sk), m_manager) << "\n";
});
lbool r = m_aux_context->check();
TRACE("model_checker", tout << "[complete] model-checker result: " << to_sat_str(r) << "\n";);
if (r == l_false) {
m_aux_context->pop(1);
return true; // quantifier is satisfied by m_curr_model
}
model_ref complete_cex;
m_aux_context->get_model(complete_cex);
// try to find new instances using instantiation sets.
m_model_finder.restrict_sks_to_inst_set(m_aux_context.get(), q, sks);
unsigned num_new_instances = 0;
while (true) {
lbool r = m_aux_context->check();
TRACE("model_checker", tout << "[restricted] model-checker (" << (num_new_instances+1) << ") result: " << to_sat_str(r) << "\n";);
if (r == l_false)
break;
model_ref cex;
m_aux_context->get_model(cex);
if (add_instance(q, cex.get(), sks, true)) {
num_new_instances++;
if (num_new_instances < m_max_cexs) {
if (!add_blocking_clause(cex.get(), sks))
break; // add_blocking_clause failed... stop the search for new counter-examples...
}
}
else {
break;
}
if (num_new_instances >= m_max_cexs)
break;
}
if (num_new_instances == 0) {
// failed to create instances when restricting to inst sets... then use result of the complete model check
TRACE("model_checker", tout << "using complete_cex result:\n"; model_pp(tout, *complete_cex););
add_instance(q, complete_cex.get(), sks, false);
}
m_aux_context->pop(1);
return false;
}
void model_checker::init_aux_context() {
if (!m_fparams) {
m_fparams = alloc(front_end_params, m_context->get_fparams());
m_fparams->m_relevancy_lvl = 0; // no relevancy since the model checking problems are quantifier free
}
if (!m_aux_context) {
symbol logic;
m_aux_context = m_context->mk_fresh(&logic, m_fparams.get());
}
}
struct scoped_set_relevancy {
};
bool model_checker::check(proto_model * md, obj_map<enode, app *> const & root2value) {
SASSERT(md != 0);
m_root2value = &root2value;
ptr_vector<quantifier>::const_iterator it = m_qm->begin_quantifiers();
ptr_vector<quantifier>::const_iterator end = m_qm->end_quantifiers();
if (it == end)
return true;
if (m_iteration_idx >= m_params.m_mbqi_max_iterations)
return false;
m_curr_model = md;
m_value2expr.reset();
md->compress();
TRACE("model_checker", tout << "MODEL_CHECKER INVOKED\n";
tout << "model:\n"; model_pp(tout, *m_curr_model););
if (m_params.m_mbqi_trace) {
verbose_stream() << "[mbqi] started\n";
}
init_aux_context();
bool found_relevant = false;
unsigned num_failures = 0;
for (; it != end; ++it) {
quantifier * q = *it;
if (m_context->is_relevant(q) && m_context->get_assignment(q) == l_true) {
if (m_params.m_mbqi_trace && q->get_qid() != symbol::null) {
verbose_stream() << "[mbqi] checking: " << q->get_qid() << "\n";
}
found_relevant = true;
if (!check(q)) {
IF_VERBOSE(5, verbose_stream() << "current model does not satisfy: " << q->get_qid() << "\n";);
if (m_params.m_mbqi_trace) {
verbose_stream() << "[mbqi] failed " << q->get_qid() << "\n";
}
num_failures++;
}
}
}
if (found_relevant)
m_iteration_idx++;
TRACE("model_checker", tout << "model after check:\n"; model_pp(tout, *md););
TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";);
m_max_cexs += m_params.m_mbqi_max_cexs;
if (num_failures == 0)
m_curr_model->cleanup();
if (m_params.m_mbqi_trace) {
if (num_failures == 0)
verbose_stream() << "[mbqi] succeeded\n";
else
verbose_stream() << "[mbqi] num failures " << num_failures << "\n";
}
return num_failures == 0;
}
void model_checker::init_search_eh() {
m_max_cexs = m_params.m_mbqi_max_cexs;
m_iteration_idx = 0;
}
void model_checker::restart_eh() {
IF_VERBOSE(100, verbose_stream() << "instantiating new instances...\n";);
assert_new_instances();
reset_new_instances();
}
bool model_checker::has_new_instances() {
return !m_new_instances.empty();
}
void model_checker::reset_new_instances() {
m_new_instances_region.reset();
m_new_instances.reset();
}
void model_checker::reset() {
reset_new_instances();
}
void model_checker::assert_new_instances() {
TRACE("model_checker_bug_detail", tout << "assert_new_instances, inconsistent: " << m_context->inconsistent() << "\n";);
ptr_buffer<enode> bindings;
ptr_vector<enode> dummy;
ptr_vector<instance>::iterator it = m_new_instances.begin();
ptr_vector<instance>::iterator end = m_new_instances.end();
for (; it != end; ++it) {
instance * inst = *it;
quantifier * q = inst->m_q;
if (m_context->b_internalized(q)) {
bindings.reset();
unsigned num_decls = q->get_num_decls();
unsigned gen = inst->m_generation;
for (unsigned i = 0; i < num_decls; i++) {
expr * b = inst->m_bindings[i];
if (!m_context->e_internalized(b)) {
TRACE("model_checker_bug_detail", tout << "internalizing b:\n" << mk_pp(b, m_manager) << "\n";);
m_context->internalize(b, false, gen);
}
bindings.push_back(m_context->get_enode(b));
}
TRACE("model_checker_bug_detail", tout << "instantiating... q:\n" << mk_pp(q, m_manager) << "\n";
tout << "inconsistent: " << m_context->inconsistent() << "\n";
tout << "bindings:\n";
for (unsigned i = 0; i < num_decls; i++) {
expr * b = inst->m_bindings[i];
tout << mk_pp(b, m_manager) << "\n";
});
TRACE("model_checker_instance",
expr_ref inst_expr(m_manager);
instantiate(m_manager, q, inst->m_bindings, inst_expr);
tout << "(assert " << mk_ismt2_pp(inst_expr, m_manager) << ")\n";);
m_context->add_instance(q, 0, num_decls, bindings.c_ptr(), gen, gen, gen, dummy);
TRACE("model_checker_bug_detail", tout << "after instantiating, inconsistent: " << m_context->inconsistent() << "\n";);
}
}
}
};

View file

@ -0,0 +1,98 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_model_checker.h
Abstract:
Model checker
AND
Model-based quantifier instantiation.
Author:
Leonardo de Moura (leonardo) 2010-12-03.
Revision History:
--*/
#ifndef _SMT_MODEL_CHECKER_H_
#define _SMT_MODEL_CHECKER_H_
#include"ast.h"
#include"obj_hashtable.h"
#include"qi_params.h"
#include"front_end_params.h"
#include"region.h"
class proto_model;
class model;
namespace smt {
class context;
class enode;
class model_finder;
class quantifier_manager;
class model_checker {
ast_manager & m_manager;
qi_params const & m_params;
// copy of front_end_params for auxiliary context.
// the idea is to use a different configuration for the aux context (e.g., disable relevancy)
scoped_ptr<front_end_params> m_fparams;
quantifier_manager * m_qm;
context * m_context; // owner of the model checker
obj_map<enode, app *> const * m_root2value; // temp field to store mapping received in the check method.
model_finder & m_model_finder;
scoped_ptr<context> m_aux_context; // Auxiliary context used for model checking quantifiers.
unsigned m_max_cexs;
unsigned m_iteration_idx;
proto_model * m_curr_model;
obj_map<expr, expr *> m_value2expr;
friend class instantiation_set;
void init_aux_context();
expr * get_term_from_ctx(expr * val);
void restrict_to_universe(expr * sk, obj_hashtable<expr> const & universe);
void assert_neg_q_m(quantifier * q, expr_ref_vector & sks);
bool add_blocking_clause(model * cex, expr_ref_vector & sks);
bool check(quantifier * q);
struct instance {
quantifier * m_q;
unsigned m_generation;
expr * m_bindings[0];
static unsigned get_obj_size(unsigned num_bindings) { return sizeof(instance) + num_bindings * sizeof(expr*); }
instance(quantifier * q, expr * const * bindings, unsigned gen):m_q(q), m_generation(gen) {
memcpy(m_bindings, bindings, q->get_num_decls() * sizeof(expr*));
}
};
region m_new_instances_region;
expr_ref_vector m_new_instances_bindings;
ptr_vector<instance> m_new_instances;
bool add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv);
void reset_new_instances();
void assert_new_instances();
quantifier * get_flat_quantifier(quantifier * q);
public:
model_checker(ast_manager & m, qi_params const & p, model_finder & mf);
~model_checker();
void set_qm(quantifier_manager & qm);
context * get_context() const { return m_context; }
bool check(proto_model * md, obj_map<enode, app *> const & root2value);
bool has_new_instances();
void init_search_eh();
void restart_eh();
void reset();
};
};
#endif // _SMT_MODEL_CHECKER_H_

3471
src/smt/smt_model_finder.cpp Normal file

File diff suppressed because it is too large Load diff

125
src/smt/smt_model_finder.h Normal file
View file

@ -0,0 +1,125 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_model_finder.h
Abstract:
Model finding goodies for universally quantified formulas.
During the search, the finder store information about the quantifiers
that are internalized. In an ideal world, quantifiers are only internalized
at base level.
Given a satisfiable ground formula, Z3 will restrict the interpretation
of uninterpreted functions in a finite subset of its domain.
The model finder tries to produce a complete interpretation that will
also satisfy all universally quantified formulas.
During model construction, the model finder will complete the interpretation
of uninterpreted functions by propagating basic constraints induced by the
body of universally quantified formulas.
More information can be found in the following papers:
- Complete instantiation for quantified SMT formulas, Yeting Ge
and Leonardo de Moura, Conference on Computer Aided Verification
(CAV 2009), Grenoble, France, 2009.
- Efficiently Solving Quantified Bit-Vector Formula, Christoph
Wintersteiger, Youssef Hamadi and Leonardo de Moura, FMCAD,
Lugano, Switzerland, 2010.
- Bugs, Moles and Skeletons: Symbolic Reasoning for Software
Development, Leonardo de Moura, Nikolaj Bjørner, IJCAR,
Edinburgh, Scotland, 2010.
Author:
Leonardo de Moura (leonardo) 2010-12-17.
Revision History:
--*/
#ifndef _SMT_MODEL_FINDER_H_
#define _SMT_MODEL_FINDER_H_
#include"ast.h"
#include"func_decl_dependencies.h"
#include"simplifier.h"
#include"proto_model.h"
namespace smt {
class context;
namespace mf {
class quantifier_info;
class quantifier_analyzer;
class auf_solver;
class simple_macro_solver;
class hint_solver;
class non_auf_macro_solver;
class instantiation_set;
};
class model_finder {
typedef mf::quantifier_analyzer quantifier_analyzer;
typedef mf::quantifier_info quantifier_info;
typedef mf::auf_solver auf_solver;
typedef mf::simple_macro_solver simple_macro_solver;
typedef mf::hint_solver hint_solver;
typedef mf::non_auf_macro_solver non_auf_macro_solver;
typedef mf::instantiation_set instantiation_set;
ast_manager & m_manager;
context * m_context;
scoped_ptr<quantifier_analyzer> m_analyzer;
scoped_ptr<auf_solver> m_auf_solver;
obj_map<quantifier, quantifier_info *> m_q2info;
ptr_vector<quantifier> m_quantifiers;
func_decl_dependencies m_dependencies;
scoped_ptr<simple_macro_solver> m_sm_solver;
scoped_ptr<hint_solver> m_hint_solver;
scoped_ptr<non_auf_macro_solver> m_nm_solver;
struct scope {
unsigned m_quantifiers_lim;
};
svector<scope> m_scopes;
expr_ref_vector m_new_constraints; // new constraints for fresh constants created by the model finder
void restore_quantifiers(unsigned old_size);
quantifier_info * get_quantifier_info(quantifier * q) const;
void collect_relevant_quantifiers(ptr_vector<quantifier> & qs) const;
void cleanup_quantifier_infos(ptr_vector<quantifier> const & qs);
void process_simple_macros(ptr_vector<quantifier> & qs, ptr_vector<quantifier> & residue, proto_model * m);
void process_hint_macros(ptr_vector<quantifier> & qs, ptr_vector<quantifier> & residue, proto_model * m);
void process_non_auf_macros(ptr_vector<quantifier> & qs, ptr_vector<quantifier> & residue, proto_model * m);
void process_auf(ptr_vector<quantifier> const & qs, proto_model * m);
instantiation_set const * get_uvar_inst_set(quantifier * q, unsigned i) const;
public:
model_finder(ast_manager & m, simplifier & s);
~model_finder();
void set_context(context * ctx);
void register_quantifier(quantifier * q);
void push_scope();
void pop_scope(unsigned num_scopes);
void reset();
void init_search_eh();
void fix_model(proto_model * m);
quantifier * get_flat_quantifier(quantifier * q) const;
expr * get_inv(quantifier * q, unsigned i, expr * val, unsigned & generation) const;
bool restrict_sks_to_inst_set(context * aux_ctx, quantifier * q, expr_ref_vector const & sks);
void restart_eh();
};
};
#endif

View file

@ -0,0 +1,631 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_model_generator.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-29.
Revision History:
--*/
#include"smt_context.h"
#include"smt_model_generator.h"
#include"proto_model.h"
#include"for_each_expr.h"
#include"ast_ll_pp.h"
#include"ast_pp.h"
#include"ast_smt2_pp.h"
namespace smt {
model_generator::model_generator(ast_manager & m):
m_manager(m),
m_context(0),
m_fresh_idx(1),
m_asts(m_manager),
m_model(0) {
}
model_generator::~model_generator() {
}
void model_generator::reset() {
m_extra_fresh_values.reset();
m_fresh_idx = 1;
m_root2value.reset();
m_asts.reset();
m_model = 0;
}
void model_generator::init_model() {
SASSERT(!m_model);
m_model = alloc(proto_model, m_manager, m_context->get_simplifier(), m_context->get_fparams());
ptr_vector<theory>::const_iterator it = m_context->begin_theories();
ptr_vector<theory>::const_iterator end = m_context->end_theories();
for (; it != end; ++it) {
TRACE("model_generator_bug", tout << "init_model for theory: " << (*it)->get_name() << "\n";);
(*it)->init_model(*this);
}
}
/**
\brief Create the boolean assignment.
*/
void model_generator::mk_bool_model() {
unsigned sz = m_context->get_num_b_internalized();
for (unsigned i = 0; i < sz; i++) {
expr * p = m_context->get_b_internalized(i);
if (is_uninterp_const(p) && m_context->is_relevant(p)) {
SASSERT(m_manager.is_bool(p));
func_decl * d = to_app(p)->get_decl();
lbool val = m_context->get_assignment(p);
expr * v = val == l_true ? m_manager.mk_true() : m_manager.mk_false();
m_model->register_decl(d, v);
}
}
}
/**
\brief Create the mapping root2proc: enode-root -> model_value_proc, and roots.
Store the new model_value_proc at procs.
*/
void model_generator::mk_value_procs(obj_map<enode, model_value_proc *> & root2proc, ptr_vector<enode> & roots,
ptr_vector<model_value_proc> & procs) {
ptr_vector<enode>::const_iterator it = m_context->begin_enodes();
ptr_vector<enode>::const_iterator end = m_context->end_enodes();
for (; it != end; ++it) {
enode * r = *it;
if (r == r->get_root() && m_context->is_relevant(r)) {
roots.push_back(r);
sort * s = m_manager.get_sort(r->get_owner());
model_value_proc * proc = 0;
if (m_manager.is_bool(s)) {
SASSERT(m_context->get_assignment(r) == l_true || m_context->get_assignment(r) == l_false);
if (m_context->get_assignment(r) == l_true)
proc = alloc(expr_wrapper_proc, m_manager.mk_true());
else
proc = alloc(expr_wrapper_proc, m_manager.mk_false());
}
else {
family_id fid = s->get_family_id();
theory * th = m_context->get_theory(fid);
if (th && th->build_models()) {
if (r->get_th_var(th->get_id()) != null_theory_var) {
proc = th->mk_value(r, *this);
}
else {
TRACE("model_bug", tout << "creating fresh value for #" << r->get_owner_id() << "\n";);
proc = alloc(fresh_value_proc, mk_extra_fresh_value(m_manager.get_sort(r->get_owner())));
}
}
else {
proc = mk_model_value(r);
}
}
SASSERT(proc);
procs.push_back(proc);
root2proc.insert(r, proc);
}
}
}
model_value_proc* model_generator::mk_model_value(enode* r) {
SASSERT(r == r->get_root());
expr * n = r->get_owner();
if (!m_manager.is_model_value(n)) {
sort * s = m_manager.get_sort(r->get_owner());
n = m_model->get_fresh_value(s);
CTRACE("model_generator_bug", n == 0,
tout << mk_pp(r->get_owner(), m_manager) << "\nsort:\n" << mk_pp(s, m_manager) << "\n";
tout << "is_finite: " << m_model->is_finite(s) << "\n";);
}
return alloc(expr_wrapper_proc, to_app(n));
}
#define White 0
#define Grey 1
#define Black 2
static int get_color(source2color const & colors, source const & s) {
int color;
if (colors.find(s, color))
return color;
return White;
}
static void set_color(source2color & colors, source const & s, int c) {
colors.insert(s, c);
}
static void visit_child(source const & s, source2color & colors, svector<source> & todo, bool & visited) {
if (get_color(colors, s) == White) {
todo.push_back(s);
visited = false;
}
}
bool model_generator::visit_children(source const & src,
ptr_vector<enode> const & roots,
obj_map<enode, model_value_proc *> const & root2proc,
source2color & colors,
obj_hashtable<sort> & already_traversed,
svector<source> & todo) {
if (src.is_fresh_value()) {
// there is an implicit dependency between a fresh value stub of sort S and the root enodes of sort S that are not associated with fresh values.
sort * s = src.get_value()->get_sort();
if (already_traversed.contains(s))
return true;
bool visited = true;
unsigned sz = roots.size();
for (unsigned i = 0; i < sz; i++) {
enode * r = roots[i];
if (m_manager.get_sort(r->get_owner()) != s)
continue;
SASSERT(r == r->get_root());
model_value_proc * proc = 0;
root2proc.find(r, proc);
SASSERT(proc);
if (proc->is_fresh())
continue; // r is associated with a fresh value...
SASSERT(r == r->get_root());
TRACE("mg_top_sort", tout << "fresh!" << src.get_value()->get_idx() << " -> #" << r->get_owner_id() << " " << mk_pp(m_manager.get_sort(r->get_owner()), m_manager) << "\n";);
visit_child(source(r), colors, todo, visited);
TRACE("mg_top_sort", tout << "visited: " << visited << ", todo.size(): " << todo.size() << "\n";);
}
already_traversed.insert(s);
return visited;
}
SASSERT(!src.is_fresh_value());
enode * n = src.get_enode();
SASSERT(n == n->get_root());
bool visited = true;
model_value_proc * proc = 0;
root2proc.find(n, proc);
SASSERT(proc);
buffer<model_value_dependency> dependencies;
proc->get_dependencies(dependencies);
buffer<model_value_dependency>::const_iterator it = dependencies.begin();
buffer<model_value_dependency>::const_iterator end = dependencies.end();
for (; it != end; ++it) {
model_value_dependency const & dep = *it;
visit_child(dep, colors, todo, visited);
TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << " -> ";
if (dep.is_fresh_value()) tout << "fresh!" << dep.get_value()->get_idx();
else tout << "#" << dep.get_enode()->get_owner_id();
tout << "\n";);
}
return visited;
}
void model_generator::process_source(source const & src,
ptr_vector<enode> const & roots,
obj_map<enode, model_value_proc *> const & root2proc,
source2color & colors,
obj_hashtable<sort> & already_traversed,
svector<source> & todo,
svector<source> & sorted_sources) {
TRACE("mg_top_sort", tout << "process source, is_fresh: " << src.is_fresh_value() << " ";
if (src.is_fresh_value()) tout << "fresh!" << src.get_value()->get_idx();
else tout << "#" << src.get_enode()->get_owner_id();
tout << ", todo.size(): " << todo.size() << "\n";);
int color = get_color(colors, src);
SASSERT(color != Grey);
if (color == Black)
return;
SASSERT(color == White);
todo.push_back(src);
while (!todo.empty()) {
source curr = todo.back();
TRACE("mg_top_sort", tout << "current source, is_fresh: " << curr.is_fresh_value() << " ";
if (curr.is_fresh_value()) tout << "fresh!" << curr.get_value()->get_idx();
else tout << "#" << curr.get_enode()->get_owner_id();
tout << ", todo.size(): " << todo.size() << "\n";);
switch (get_color(colors, curr)) {
case White:
set_color(colors, curr, Grey);
visit_children(curr, roots, root2proc, colors, already_traversed, todo);
break;
case Grey:
SASSERT(visit_children(curr, roots, root2proc, colors, already_traversed, todo));
set_color(colors, curr, Black);
sorted_sources.push_back(curr);
break;
case Black:
todo.pop_back();
break;
default:
UNREACHABLE();
}
}
TRACE("mg_top_sort", tout << "END process_source, todo.size(): " << todo.size() << "\n";);
}
/**
\brief Topological sort of 'sources'. Store result in sorted_sources.
*/
void model_generator::top_sort_sources(ptr_vector<enode> const & roots,
obj_map<enode, model_value_proc *> const & root2proc,
svector<source> & sorted_sources) {
svector<source> todo;
source2color colors;
// The following 'set' of sorts is used to avoid traversing roots looking for enodes of sort S.
// That is, a sort S is in already_traversed, if all enodes of sort S in roots were already traversed.
obj_hashtable<sort> already_traversed;
// topological sort
// traverse all extra fresh values...
unsigned sz = m_extra_fresh_values.size();
for (unsigned i = 0; i < sz; i++) {
extra_fresh_value * f = m_extra_fresh_values[i];
process_source(source(f), roots, root2proc, colors, already_traversed, todo, sorted_sources);
}
// traverse all enodes that are associated with fresh values...
sz = roots.size();
for (unsigned i = 0; i < sz; i++) {
enode * r = roots[i];
model_value_proc * proc = 0;
root2proc.find(r, proc);
SASSERT(proc);
if (!proc->is_fresh())
continue;
process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources);
}
sz = roots.size();
for (unsigned i = 0; i < sz; i++) {
enode * r = roots[i];
process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources);
}
}
void model_generator::mk_values() {
obj_map<enode, model_value_proc *> root2proc;
ptr_vector<enode> roots;
ptr_vector<model_value_proc> procs;
svector<source> sources;
buffer<model_value_dependency> dependencies;
ptr_vector<expr> dependency_values;
mk_value_procs(root2proc, roots, procs);
top_sort_sources(roots, root2proc, sources);
TRACE("sorted_sources",
svector<source>::const_iterator it = sources.begin();
svector<source>::const_iterator end = sources.end();
for (; it != end; ++it) {
source const & curr = *it;
if (curr.is_fresh_value()) {
tout << "fresh!" << curr.get_value()->get_idx() << " " << mk_pp(curr.get_value()->get_sort(), m_manager) << "\n";
}
else {
enode * n = curr.get_enode();
SASSERT(n->get_root() == n);
sort * s = m_manager.get_sort(n->get_owner());
tout << "#" << n->get_owner_id() << " " << mk_pp(s, m_manager);
model_value_proc * proc = 0;
root2proc.find(n, proc);
SASSERT(proc);
tout << " is_fresh: " << proc->is_fresh() << "\n";
}
});
svector<source>::const_iterator it = sources.begin();
svector<source>::const_iterator end = sources.end();
for (; it != end; ++it) {
source const & curr = *it;
if (curr.is_fresh_value()) {
sort * s = curr.get_value()->get_sort();
TRACE("model_fresh_bug", tout << "mk fresh!" << curr.get_value()->get_idx() << " : " << mk_pp(s, m_manager) << "\n";);
expr * val = m_model->get_fresh_value(s);
TRACE("model_fresh_bug", tout << "mk fresh!" << curr.get_value()->get_idx() << " := #" << (val == 0 ? UINT_MAX : val->get_id()) << "\n";);
m_asts.push_back(val);
curr.get_value()->set_value(val);
}
else {
enode * n = curr.get_enode();
SASSERT(n->get_root() == n);
TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << "\n";);
dependencies.reset();
dependency_values.reset();
model_value_proc * proc = 0;
VERIFY(root2proc.find(n, proc));
SASSERT(proc);
proc->get_dependencies(dependencies);
buffer<model_value_dependency>::const_iterator it2 = dependencies.begin();
buffer<model_value_dependency>::const_iterator end2 = dependencies.end();
for (; it2 != end2; ++it2) {
model_value_dependency const & d = *it2;
if (d.is_fresh_value()) {
CTRACE("mg_top_sort", !d.get_value()->get_value(),
tout << "#" << n->get_owner_id() << " -> ";
tout << "fresh!" << d.get_value()->get_idx() << "\n";);
SASSERT(d.get_value()->get_value());
dependency_values.push_back(d.get_value()->get_value());
}
else {
enode * child = d.get_enode();
child = child->get_root();
app * val = 0;
m_root2value.find(child, val);
SASSERT(val);
dependency_values.push_back(val);
}
}
app * val = proc->mk_value(*this, dependency_values);
register_value(val);
m_asts.push_back(val);
m_root2value.insert(n, val);
}
}
std::for_each(procs.begin(), procs.end(), delete_proc<model_value_proc>());
std::for_each(m_extra_fresh_values.begin(), m_extra_fresh_values.end(), delete_proc<extra_fresh_value>());
m_extra_fresh_values.reset();
// send model
ptr_vector<enode>::const_iterator it3 = m_context->begin_enodes();
ptr_vector<enode>::const_iterator end3 = m_context->end_enodes();
for (; it3 != end3; ++it3) {
enode * n = *it3;
if (is_uninterp_const(n->get_owner()) && m_context->is_relevant(n)) {
func_decl * d = n->get_owner()->get_decl();
expr * val = get_value(n);
m_model->register_decl(d, val);
}
}
}
app * model_generator::get_value(enode * n) const {
app * val = 0;
m_root2value.find(n->get_root(), val);
SASSERT(val);
return val;
}
/**
\brief Return true if the interpretation of the function should be included in the model.
*/
bool model_generator::include_func_interp(func_decl * f) const {
return f->get_family_id() == null_family_id;
}
/**
\brief Create (partial) interpretation of function symbols.
The "else" is missing.
*/
void model_generator::mk_func_interps() {
unsigned sz = m_context->get_num_e_internalized();
for (unsigned i = 0; i < sz; i++) {
expr * t = m_context->get_e_internalized(i);
if (!m_context->is_relevant(t))
continue;
enode * n = m_context->get_enode(t);
unsigned num_args = n->get_num_args();
func_decl * f = n->get_decl();
if (num_args > 0 && n->get_cg() == n && include_func_interp(f)) {
ptr_buffer<expr> args;
expr * result = get_value(n);
SASSERT(result);
for (unsigned j = 0; j < num_args; j++) {
app * arg = get_value(n->get_arg(j));
SASSERT(arg);
args.push_back(arg);
}
func_interp * fi = m_model->get_func_interp(f);
if (fi == 0) {
fi = alloc(func_interp, m_manager, f->get_arity());
m_model->register_decl(f, fi);
}
SASSERT(m_model->has_interpretation(f));
SASSERT(m_model->get_func_interp(f) == fi);
// The entry must be new because n->get_cg() == n
TRACE("func_interp_bug",
tout << "insert new entry for:\n" << mk_ismt2_pp(n->get_owner(), m_manager) << "\nargs: ";
for (unsigned i = 0; i < num_args; i++) {
tout << "#" << n->get_arg(i)->get_owner_id() << " ";
}
tout << "\n";
tout << "value: #" << n->get_owner_id() << "\n" << mk_ismt2_pp(result, m_manager) << "\n";);
if (m_context->get_last_search_failure() == smt::THEORY) {
// if the theory solvers are incomplete, then we cannot assume the e-graph is close under congruence
if (fi->get_entry(args.c_ptr()) == 0)
fi->insert_new_entry(args.c_ptr(), result);
}
else {
fi->insert_new_entry(args.c_ptr(), result);
}
}
}
}
extra_fresh_value * model_generator::mk_extra_fresh_value(sort * s) {
SASSERT(s->is_infinite());
extra_fresh_value * r = alloc(extra_fresh_value, s, m_fresh_idx);
m_fresh_idx++;
m_extra_fresh_values.push_back(r);
return r;
}
expr * model_generator::get_some_value(sort * s) {
SASSERT(m_model);
return m_model->get_some_value(s);
}
void model_generator::register_value(expr * val) {
SASSERT(m_model);
m_model->register_value(val);
}
void model_generator::finalize_theory_models() {
ptr_vector<theory>::const_iterator it = m_context->begin_theories();
ptr_vector<theory>::const_iterator end = m_context->end_theories();
for (; it != end; ++it)
(*it)->finalize_model(*this);
}
void model_generator::register_existing_model_values() {
ptr_vector<enode>::const_iterator it = m_context->begin_enodes();
ptr_vector<enode>::const_iterator end = m_context->end_enodes();
for (; it != end; ++it) {
enode * r = *it;
if (r == r->get_root() && m_context->is_relevant(r)) {
expr * n = r->get_owner();
if (m_manager.is_model_value(n)) {
register_value(n);
}
}
}
}
void model_generator::register_factory(value_factory * f) {
m_model->register_factory(f);
}
void model_generator::register_macros() {
unsigned num = m_context->get_num_macros();
TRACE("register_macros", tout << "num. macros: " << num << "\n";);
expr_ref v(m_manager);
for (unsigned i = 0; i < num; i++) {
func_decl * f = m_context->get_macro_interpretation(i, v);
func_interp * fi = alloc(func_interp, m_manager, f->get_arity());
fi->set_else(v);
TRACE("register_macros", tout << f->get_name() << "\n" << mk_pp(v, m_manager) << "\n";);
m_model->register_decl(f, fi);
}
}
/**
\brief Auxiliary functor for method register_indirect_elim_decls.
*/
class mk_interp_proc {
context & m_context;
proto_model & m_model;
public:
mk_interp_proc(context & ctx, proto_model & m):
m_context(ctx),
m_model(m) {
}
void operator()(var * n) {
}
void operator()(app * n) {
if (!is_uninterp(n))
return; // n is interpreted
func_decl * d = n->get_decl();
if (m_model.has_interpretation(d))
return; // declaration already has an interpretation.
if (n->get_num_args() == 0 && m_context.is_subst(n) != 0)
return; // an interpretation will be generated for this variable using the evaluator.
if (n->get_num_args() == 0) {
sort * r = d->get_range();
expr * v = m_model.get_some_value(r);
m_model.register_decl(d, v);
}
else {
func_interp * fi = alloc(func_interp, m_context.get_manager(), d->get_arity());
m_model.register_decl(d, fi);
}
}
void operator()(quantifier * n) {
}
};
/**
\brief Generate an interpretation for variables that were eliminated indirectly.
When a variable is eliminated by substitution and it does not occur anywhere, then
its definition may contain declarations that do not occur anywhere else.
This method will assign an arbitrary interpretation for these declarations.
Example: consider the formula
(= x (f y))
This formula is satisfiable. If the solver is used during preprocessing step,
this formula is reduced to "true", and the substitution set contains the entry (x -> (f y)).
The declarations f and y will not have an interpretation. This method will traverse the
definition of each eliminated variable, and generate an arbitrary interpretations for
declarations that do not have one yet.
*/
void model_generator::register_indirect_elim_decls() {
expr_mark visited;
mk_interp_proc proc(*m_context, *m_model);
ptr_vector<app>::const_iterator it = m_context->begin_subst_vars();
ptr_vector<app>::const_iterator end = m_context->end_subst_vars();
for (; it != end; ++it) {
app * var = *it;
if (var->get_num_args() > 0)
continue;
expr * subst = m_context->get_subst(var);
for_each_expr(proc, visited, subst);
}
}
void model_generator::register_subst_vars() {
ptr_vector<app> ordered_subst_vars;
m_context->get_ordered_subst_vars(ordered_subst_vars);
ptr_vector<app>::const_iterator it = ordered_subst_vars.begin();
ptr_vector<app>::const_iterator end = ordered_subst_vars.end();
for (; it != end; ++it) {
app * var = *it;
TRACE("model_subst_vars", tout << "processing: " << mk_pp(var, m_manager) << "\n";);
if (var->get_num_args() > 0) {
TRACE("model_subst_vars", tout << "not a constant...\n";);
continue;
}
expr * subst = m_context->get_subst(var);
if (subst == 0) {
TRACE("model_subst_vars", tout << "no definition...\n";);
continue;
}
TRACE("model_subst_vars", tout << "definition: " << mk_pp(subst, m_manager) << "\n";);
expr_ref r(m_manager);
m_model->eval(subst, r);
TRACE("model_subst_vars", tout << "result: " << mk_pp(r, m_manager) << "\n";);
m_model->register_decl(var->get_decl(), r);
}
}
proto_model * model_generator::mk_model() {
SASSERT(!m_model);
TRACE("func_interp_bug", m_context->display(tout););
init_model();
register_existing_model_values();
mk_bool_model();
mk_values();
mk_func_interps();
finalize_theory_models();
register_macros();
TRACE("model_subst_vars",
tout << "substitution vars:\n";
ptr_vector<app>::const_iterator it = m_context->begin_subst_vars();
ptr_vector<app>::const_iterator end = m_context->end_subst_vars();
for (; it != end; ++it) {
app * var = *it;
tout << mk_pp(var, m_manager) << "\n";
if (var->get_num_args() > 0)
continue;
expr * subst = m_context->get_subst(var);
if (subst == 0)
continue;
tout << "-> " << mk_bounded_pp(subst, m_manager, 10) << "\n";
});
register_indirect_elim_decls();
register_subst_vars();
return m_model;
}
};

View file

@ -0,0 +1,229 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_model_generator.h
Abstract:
The model generator builds a (partial) model for the ground
formulas in the logical context.
The model finder (smt_model_finder.h) tries to extend the
partial model to satisfy the quantifiers. Main invariant:
the new model still satisfies the ground formulas.
The model checker (smt_model_checker.h) checks whether the (new)
model satisfies the quantifiers. If it doesn't, new instances are
added.
Author:
Leonardo de Moura (leonardo) 2008-10-29.
Revision History:
--*/
#ifndef _SMT_MODEL_GENERATOR_H_
#define _SMT_MODEL_GENERATOR_H_
#include"ast.h"
#include"smt_types.h"
#include"obj_hashtable.h"
#include"map.h"
class value_factory;
class proto_model;
namespace smt {
// -----------------------------
//
// This module builds an interpretation for each relevant expression in the logical context.
//
// 1) The interpretation of boolean constants is their truth value in the logical context.
//
// 2) The interpretation of expressions associated with enodes is built using functors (model_value_proc).
// Theories as arrays and datatypes many need the interpretation of some expressions to be built before the interpretation of others.
// We say this is a dependency. Moreover, some values must be fresh. That is, they should be different
// from all other values associated with enodes of a given sort. For example, the array theory
// uses fresh values to make sure that some array constants are different from each other.
//
// So a dependency for building the interpretation of an enode N can be:
// a) a fresh value (stub) of sort S: it must be built after the interpretation of all enodes of sort S were assigned.
//
// b) an enode N': the interpretation of N' must be built before the interpretation of N.
//
// We say a 'source' is an fresh value or an enode. Note that every dependency is a source,
// but not every source is a dependency.
//
// We use these dependencies to sort (using a topological sort) the sources. The sorted 'sources' specify the
// order the interpretations will be built.
//
// Assumptions regarding dependencies:
//
// - They are acyclic.
//
// - All dependencies are marked as relevant.
//
// - A fresh value stub of sort S depends (implicitly) on all enodes of sort S (that are not associated with fresh values).
// So an enode of sort S may not have a dependency of sort S.
//
// ------------------------------
/**
\brief Stub for extra fresh value.
*/
struct extra_fresh_value {
sort * m_sort;
unsigned m_idx;
expr * m_value;
public:
extra_fresh_value(sort * s, unsigned idx):m_sort(s), m_idx(idx), m_value(0) {}
sort * get_sort() const { return m_sort; }
unsigned get_idx() const { return m_idx; }
void set_value(expr * n) { SASSERT(m_value == 0); m_value = n; }
expr * get_value() const { return m_value; }
};
/**
\brief Theories such as arrays and datatypes may need some values to be already available when
building a value. We say this a dependency. Object of this class are used to track such dependencies.
Example: to build the value (cons 10 nil), the values 10 and nil should be already available.
*/
class model_value_dependency {
bool m_fresh; //!< True if the dependency is a new fresh value;
union {
enode * m_enode; //!< When m_fresh == false, contains an enode depedency.
extra_fresh_value * m_value; //!< When m_fresh == true, contains the sort of the fresh value
};
public:
model_value_dependency():m_fresh(true), m_value(0) {}
model_value_dependency(enode * n):m_fresh(false), m_enode(n->get_root()) {}
model_value_dependency(extra_fresh_value * v):m_fresh(true), m_value(v) {}
bool is_fresh_value() const { return m_fresh; }
enode * get_enode() const { SASSERT(!is_fresh_value()); return m_enode; }
extra_fresh_value * get_value() const { SASSERT(is_fresh_value()); return m_value; }
};
typedef model_value_dependency source;
struct source_hash_proc {
unsigned operator()(source const & s) const { return s.is_fresh_value() ? hash_u_u(17, s.get_value()->get_idx()) : hash_u_u(13, s.get_enode()->get_owner_id()); }
};
struct source_eq_proc {
bool operator()(source const & s1, source const & s2) const {
if (s1.is_fresh_value() != s2.is_fresh_value())
return false;
if (s1.is_fresh_value())
return s1.get_value()->get_idx() == s2.get_value()->get_idx();
else
return s1.get_enode() == s2.get_enode();
}
};
typedef map<source, int, source_hash_proc, source_eq_proc> source2color;
/**
\brief Model value builder. This functor is used to specify the dependencies
needed to build a value, and to build the actual value.
*/
class model_value_proc {
public:
virtual ~model_value_proc() {}
/**
\brief Fill result with the dependencies of this functor.
That is, to invoke mk_value, the dependencies in result must be constructed.
*/
virtual void get_dependencies(buffer<model_value_dependency> & result) {}
/**
\brief The array values has size equal to the size of the argument \c result in get_dependencies.
It contain the values built for the dependencies.
*/
virtual app * mk_value(model_generator & m, ptr_vector<expr> & values) = 0;
/**
\brief Return true if it is associated with a fresh value.
*/
virtual bool is_fresh() const { return false; }
};
/**
\brief Simple model_value_proc. It has no dependencies, and
just returns a given expression.
*/
class expr_wrapper_proc : public model_value_proc {
app * m_value;
public:
expr_wrapper_proc(app * v):m_value(v) {}
virtual app * mk_value(model_generator & m, ptr_vector<expr> & values) { return m_value; }
};
class fresh_value_proc : public model_value_proc {
extra_fresh_value * m_value;
public:
fresh_value_proc(extra_fresh_value * v):m_value(v) {}
virtual void get_dependencies(buffer<model_value_dependency> & result) { result.push_back(m_value); }
virtual app * mk_value(model_generator & m, ptr_vector<expr> & values) { return to_app(values[0]); }
virtual bool is_fresh() const { return true; }
};
/**
\brief Auxiliary class used during model generation.
*/
class model_generator {
ast_manager & m_manager;
context * m_context;
ptr_vector<extra_fresh_value> m_extra_fresh_values;
unsigned m_fresh_idx;
obj_map<enode, app *> m_root2value;
ast_ref_vector m_asts;
proto_model * m_model;
void init_model();
void mk_bool_model();
void mk_value_procs(obj_map<enode, model_value_proc *> & root2proc, ptr_vector<enode> & roots, ptr_vector<model_value_proc> & procs);
void mk_values();
bool include_func_interp(func_decl * f) const;
void mk_func_interps();
void finalize_theory_models();
void display(std::ostream & out);
void register_existing_model_values();
void register_macros();
void register_indirect_elim_decls();
void register_subst_vars();
bool visit_children(source const & src, ptr_vector<enode> const & roots, obj_map<enode, model_value_proc *> const & root2proc,
source2color & colors, obj_hashtable<sort> & already_traversed, svector<source> & todo);
void process_source(source const & src, ptr_vector<enode> const & roots, obj_map<enode, model_value_proc *> const & root2proc,
source2color & colors, obj_hashtable<sort> & already_traversed, svector<source> & todo, svector<source> & sorted_sources);
void top_sort_sources(ptr_vector<enode> const & roots, obj_map<enode, model_value_proc *> const & root2proc, svector<source> & sorted_sources);
public:
model_generator(ast_manager & m);
~model_generator();
void reset();
void set_context(context * c) { SASSERT(m_context == 0); m_context = c; }
void register_factory(value_factory * f);
extra_fresh_value * mk_extra_fresh_value(sort * s);
model_value_proc * mk_model_value(enode* r);
expr * get_some_value(sort * s);
proto_model & get_model() { SASSERT(m_model); return *m_model; }
void register_value(expr * val);
ast_manager & get_manager() { return m_manager; }
proto_model * mk_model();
obj_map<enode, app *> const & get_root2value() const { return m_root2value; }
app * get_value(enode * n) const;
};
};
#endif /* _SMT_MODEL_GENERATOR_H_ */

609
src/smt/smt_quantifier.cpp Normal file
View file

@ -0,0 +1,609 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
smt_quantifier.cpp
Abstract:
Quantifier reasoning support for smt::context.
Author:
Leonardo de Moura (leonardo) 2012-02-16.
Revision History:
--*/
#include"smt_quantifier.h"
#include"smt_context.h"
#include"smt_quantifier_stat.h"
#include"smt_model_finder.h"
#include"smt_model_checker.h"
#include"smt_quick_checker.h"
#include"mam.h"
#include"qi_queue.h"
#include"ast_smt2_pp.h"
namespace smt {
quantifier_manager_plugin * mk_default_plugin();
struct quantifier_manager::imp {
quantifier_manager & m_wrapper;
context & m_context;
front_end_params & m_params;
qi_queue m_qi_queue;
obj_map<quantifier, quantifier_stat *> m_quantifier_stat;
quantifier_stat_gen m_qstat_gen;
ptr_vector<quantifier> m_quantifiers;
scoped_ptr<quantifier_manager_plugin> m_plugin;
unsigned m_num_instances;
imp(quantifier_manager & wrapper, context & ctx, front_end_params & p, quantifier_manager_plugin * plugin):
m_wrapper(wrapper),
m_context(ctx),
m_params(p),
m_qi_queue(m_wrapper, ctx, p, p.m_trace_stream),
m_qstat_gen(ctx.get_manager(), ctx.get_region()),
m_plugin(plugin) {
m_num_instances = 0;
m_qi_queue.setup();
}
quantifier_stat * get_stat(quantifier * q) const {
return m_quantifier_stat.find(q);
}
unsigned get_generation(quantifier * q) const {
return get_stat(q)->get_generation();
}
void add(quantifier * q, unsigned generation) {
quantifier_stat * stat = m_qstat_gen(q, generation);
m_quantifier_stat.insert(q, stat);
m_quantifiers.push_back(q);
m_plugin->add(q);
}
void display_stats(std::ostream & out, quantifier * q) {
quantifier_stat * s = get_stat(q);
unsigned num_instances = s->get_num_instances();
unsigned max_generation = s->get_max_generation();
float max_cost = s->get_max_cost();
if (num_instances > 0) {
out << "[quantifier_instances] ";
out.width(10);
out << q->get_qid().str().c_str() << " : ";
out.width(6);
out << num_instances << " : ";
out.width(3);
out << max_generation << " : " << max_cost << "\n";
}
}
void del(quantifier * q) {
if (m_params.m_qi_profile) {
display_stats(verbose_stream(), q);
}
m_quantifiers.pop_back();
m_quantifier_stat.erase(q);
}
bool empty() const {
return m_quantifiers.empty();
}
bool is_shared(enode * n) const {
return m_plugin->is_shared(n);
}
bool add_instance(quantifier * q, app * pat,
unsigned num_bindings,
enode * const * bindings,
unsigned max_generation,
unsigned min_top_generation,
unsigned max_top_generation,
ptr_vector<enode> & used_enodes) {
max_generation = std::max(max_generation, get_generation(q));
if (m_num_instances > m_params.m_qi_max_instances)
return false;
get_stat(q)->update_max_generation(max_generation);
fingerprint * f = m_context.add_fingerprint(q, q->get_id(), num_bindings, bindings);
if (f) {
if (m_params.m_trace_stream != NULL) {
std::ostream & out = *m_params.m_trace_stream;
out << "[new-match] " << static_cast<void*>(f) << " #" << q->get_id();
for (unsigned i = 0; i < num_bindings; i++) {
// I don't want to use mk_pp because it creates expressions for pretty printing.
// This nasty side-effect may change the behavior of Z3.
out << " #" << bindings[i]->get_owner_id();
}
out << " ;";
ptr_vector<enode>::const_iterator it = used_enodes.begin();
ptr_vector<enode>::const_iterator end = used_enodes.end();
for (; it != end; ++it)
out << " #" << (*it)->get_owner_id();
out << "\n";
}
m_qi_queue.insert(f, pat, max_generation, min_top_generation, max_top_generation); // TODO
m_num_instances++;
return true;
}
return false;
}
void init_search_eh() {
m_num_instances = 0;
ptr_vector<quantifier>::iterator it2 = m_quantifiers.begin();
ptr_vector<quantifier>::iterator end2 = m_quantifiers.end();
for (; it2 != end2; ++it2) {
quantifier * q = *it2;
get_stat(q)->reset_num_instances_curr_search();
}
m_qi_queue.init_search_eh();
m_plugin->init_search_eh();
}
void assign_eh(quantifier * q) {
m_plugin->assign_eh(q);
}
void add_eq_eh(enode * n1, enode * n2) {
m_plugin->add_eq_eh(n1, n2);
}
void relevant_eh(enode * n) {
m_plugin->relevant_eh(n);
}
void restart_eh() {
m_plugin->restart_eh();
}
void push() {
m_plugin->push();
m_qi_queue.push_scope();
}
void pop(unsigned num_scopes) {
m_plugin->pop(num_scopes);
m_qi_queue.pop_scope(num_scopes);
}
bool can_propagate() {
return m_qi_queue.has_work() || m_plugin->can_propagate();
}
void propagate() {
m_plugin->propagate();
m_qi_queue.instantiate();
}
bool quick_check_quantifiers() {
if (m_params.m_qi_quick_checker == MC_NO)
return true;
if (m_quantifiers.empty())
return true;
IF_VERBOSE(10, verbose_stream() << "quick checking quantifiers (unsat)...\n";);
quick_checker mc(m_context);
bool result = true;
ptr_vector<quantifier>::const_iterator it = m_quantifiers.begin();
ptr_vector<quantifier>::const_iterator end = m_quantifiers.end();
for (; it != end; ++it)
if (m_context.is_relevant(*it) && m_context.get_assignment(*it) == l_true && mc.instantiate_unsat(*it))
result = false;
if (m_params.m_qi_quick_checker == MC_UNSAT || !result) {
m_qi_queue.instantiate();
return result;
}
// MC_NO_SAT is too expensive (it creates too many irrelevant instances).
// we should use MBQI=true instead.
IF_VERBOSE(10, verbose_stream() << "quick checking quantifiers (not sat)...\n";);
it = m_quantifiers.begin();
for (; it != end; ++it)
if (m_context.is_relevant(*it) && m_context.get_assignment(*it) == l_true && mc.instantiate_not_sat(*it))
result = false;
m_qi_queue.instantiate();
return result;
}
final_check_status final_check_eh(bool full) {
if (full) {
IF_VERBOSE(100, verbose_stream() << "final check 'quantifiers'...\n";);
final_check_status result = m_qi_queue.final_check_eh() ? FC_DONE : FC_CONTINUE;
final_check_status presult = m_plugin->final_check_eh(full);
if (presult != FC_DONE)
result = presult;
if (m_context.can_propagate())
result = FC_CONTINUE;
if (result == FC_DONE && !m_params.m_qi_lazy_quick_checker && !quick_check_quantifiers())
result = FC_CONTINUE;
return result;
}
else {
return m_plugin->final_check_eh(false);
}
}
check_model_result check_model(proto_model * m, obj_map<enode, app *> const & root2value) {
if (empty())
return SAT;
return m_plugin->check_model(m, root2value);
}
void set_cancel(bool f) {
m_plugin->set_cancel(f);
}
};
quantifier_manager::quantifier_manager(context & ctx, front_end_params & fp, params_ref const & p) {
m_imp = alloc(imp, *this, ctx, fp, mk_default_plugin());
m_imp->m_plugin->set_manager(*this);
}
quantifier_manager::~quantifier_manager() {
dealloc(m_imp);
}
context & quantifier_manager::kernel() const {
return m_imp->m_context;
}
void quantifier_manager::set_plugin(quantifier_manager_plugin * plugin) {
m_imp->m_plugin = plugin;
plugin->set_manager(*this);
}
void quantifier_manager::add(quantifier * q, unsigned generation) {
m_imp->add(q, generation);
}
void quantifier_manager::del(quantifier * q) {
m_imp->del(q);
}
bool quantifier_manager::empty() const {
return m_imp->empty();
}
bool quantifier_manager::is_shared(enode * n) const {
return m_imp->is_shared(n);
}
quantifier_stat * quantifier_manager::get_stat(quantifier * q) const {
return m_imp->get_stat(q);
}
unsigned quantifier_manager::get_generation(quantifier * q) const {
return m_imp->get_generation(q);
}
bool quantifier_manager::add_instance(quantifier * q, app * pat,
unsigned num_bindings,
enode * const * bindings,
unsigned max_generation,
unsigned min_top_generation,
unsigned max_top_generation,
ptr_vector<enode> & used_enodes) {
return m_imp->add_instance(q, pat, num_bindings, bindings, max_generation, min_top_generation, max_generation, used_enodes);
}
bool quantifier_manager::add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation) {
ptr_vector<enode> tmp;
return add_instance(q, 0, num_bindings, bindings, generation, generation, generation, tmp);
}
void quantifier_manager::init_search_eh() {
m_imp->init_search_eh();
}
void quantifier_manager::assign_eh(quantifier * q) {
m_imp->assign_eh(q);
}
void quantifier_manager::add_eq_eh(enode * n1, enode * n2) {
m_imp->add_eq_eh(n1, n2);
}
void quantifier_manager::relevant_eh(enode * n) {
m_imp->relevant_eh(n);
}
final_check_status quantifier_manager::final_check_eh(bool full) {
return m_imp->final_check_eh(full);
}
void quantifier_manager::restart_eh() {
m_imp->restart_eh();
}
bool quantifier_manager::can_propagate() const {
return m_imp->can_propagate();
}
void quantifier_manager::propagate() {
m_imp->propagate();
}
bool quantifier_manager::model_based() const {
return m_imp->m_plugin->model_based();
}
void quantifier_manager::adjust_model(proto_model * m) {
m_imp->m_plugin->adjust_model(m);
}
quantifier_manager::check_model_result quantifier_manager::check_model(proto_model * m, obj_map<enode, app *> const & root2value) {
return m_imp->check_model(m, root2value);
}
void quantifier_manager::push() {
m_imp->push();
}
void quantifier_manager::pop(unsigned num_scopes) {
m_imp->pop(num_scopes);
}
void quantifier_manager::reset() {
#pragma omp critical (quantifier_manager)
{
context & ctx = m_imp->m_context;
front_end_params & p = m_imp->m_params;
quantifier_manager_plugin * plugin = m_imp->m_plugin->mk_fresh();
dealloc(m_imp);
m_imp = alloc(imp, *this, ctx, p, plugin);
plugin->set_manager(*this);
}
}
void quantifier_manager::set_cancel(bool f) {
#pragma omp critical (quantifier_manager)
{
m_imp->set_cancel(f);
}
}
void quantifier_manager::display(std::ostream & out) const {
}
void quantifier_manager::collect_statistics(::statistics & st) const {
m_imp->m_qi_queue.collect_statistics(st);
}
void quantifier_manager::reset_statistics() {
}
void quantifier_manager::display_stats(std::ostream & out, quantifier * q) const {
m_imp->display_stats(out, q);
}
ptr_vector<quantifier>::const_iterator quantifier_manager::begin_quantifiers() const {
return m_imp->m_quantifiers.begin();
}
ptr_vector<quantifier>::const_iterator quantifier_manager::end_quantifiers() const {
return m_imp->m_quantifiers.end();
}
// The default plugin uses E-matching, MBQI and quick-checker
class default_qm_plugin : public quantifier_manager_plugin {
quantifier_manager * m_qm;
front_end_params * m_fparams;
context * m_context;
scoped_ptr<mam> m_mam;
scoped_ptr<mam> m_lazy_mam;
scoped_ptr<model_finder> m_model_finder;
scoped_ptr<model_checker> m_model_checker;
unsigned m_new_enode_qhead;
unsigned m_lazy_matching_idx;
public:
default_qm_plugin():
m_qm(0),
m_context(0),
m_new_enode_qhead(0),
m_lazy_matching_idx(0) {
}
virtual ~default_qm_plugin() {
}
virtual void set_manager(quantifier_manager & qm) {
SASSERT(m_qm == 0);
m_qm = &qm;
m_context = &(qm.kernel());
m_fparams = &(m_context->get_fparams());
ast_manager & m = m_context->get_manager();
m_mam = mk_mam(*m_context, m_fparams->m_trace_stream);
m_lazy_mam = mk_mam(*m_context, m_fparams->m_trace_stream);
m_model_finder = alloc(model_finder, m, m_context->get_simplifier());
m_model_checker = alloc(model_checker, m, *m_fparams, *(m_model_finder.get()));
m_model_finder->set_context(m_context);
m_model_checker->set_qm(qm);
}
virtual quantifier_manager_plugin * mk_fresh() { return alloc(default_qm_plugin); }
virtual bool model_based() const { return m_fparams->m_mbqi; }
virtual void add(quantifier * q) {
if (m_fparams->m_mbqi) {
m_model_finder->register_quantifier(q);
}
}
virtual void del(quantifier * q) {
}
virtual void push() {
m_mam->push_scope();
m_lazy_mam->push_scope();
if (m_fparams->m_mbqi) {
m_model_finder->push_scope();
}
}
virtual void pop(unsigned num_scopes) {
m_mam->pop_scope(num_scopes);
m_lazy_mam->pop_scope(num_scopes);
if (m_fparams->m_mbqi) {
m_model_finder->pop_scope(num_scopes);
}
}
virtual void init_search_eh() {
m_lazy_matching_idx = 0;
if (m_fparams->m_mbqi) {
m_model_finder->init_search_eh();
m_model_checker->init_search_eh();
}
}
virtual void assign_eh(quantifier * q) {
if (m_fparams->m_ematching) {
bool has_unary_pattern = false;
unsigned num_patterns = q->get_num_patterns();
for (unsigned i = 0; i < num_patterns; i++) {
app * mp = to_app(q->get_pattern(i));
if (mp->get_num_args() == 1) {
has_unary_pattern = true;
break;
}
}
unsigned num_eager_multi_patterns = m_fparams->m_qi_max_eager_multipatterns;
if (!has_unary_pattern)
num_eager_multi_patterns++;
for (unsigned i = 0, j = 0; i < num_patterns; i++) {
app * mp = to_app(q->get_pattern(i));
SASSERT(m_context->get_manager().is_pattern(mp));
bool unary = (mp->get_num_args() == 1);
if (!unary && j >= num_eager_multi_patterns) {
TRACE("assign_quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n"
<< "j: " << j << " unary: " << unary << " m_params.m_qi_max_eager_multipatterns: " << m_fparams->m_qi_max_eager_multipatterns
<< " num_eager_multi_patterns: " << num_eager_multi_patterns << "\n";);
m_lazy_mam->add_pattern(q, mp);
}
else {
TRACE("assign_quantifier", tout << "adding:\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n";);
m_mam->add_pattern(q, mp);
}
if (!unary)
j++;
}
}
}
bool use_ematching() const {
return m_fparams->m_ematching && !m_qm->empty();
}
virtual void add_eq_eh(enode * e1, enode * e2) {
if (use_ematching())
m_mam->add_eq_eh(e1, e2);
}
virtual void relevant_eh(enode * e) {
if (use_ematching()) {
m_mam->relevant_eh(e, false);
m_lazy_mam->relevant_eh(e, true);
}
}
virtual bool can_propagate() const {
return m_mam->has_work();
}
virtual void restart_eh() {
if (m_fparams->m_mbqi) {
m_model_finder->restart_eh();
m_model_checker->restart_eh();
}
TRACE("mam_stats", m_mam->display(tout););
}
virtual bool is_shared(enode * n) const {
return (m_mam->is_shared(n) || m_lazy_mam->is_shared(n));
}
virtual void adjust_model(proto_model * m) {
if (m_fparams->m_mbqi) {
m_model_finder->fix_model(m);
}
}
virtual void propagate() {
m_mam->match();
if (!m_context->relevancy() && use_ematching()) {
ptr_vector<enode>::const_iterator it = m_context->begin_enodes();
ptr_vector<enode>::const_iterator end = m_context->end_enodes();
unsigned sz = static_cast<unsigned>(end - it);
if (sz > m_new_enode_qhead) {
m_context->push_trail(value_trail<context, unsigned>(m_new_enode_qhead));
it += m_new_enode_qhead;
while (m_new_enode_qhead < sz) {
enode * e = *it;
m_mam->relevant_eh(e, false);
m_lazy_mam->relevant_eh(e, true);
m_new_enode_qhead++;
it++;
}
}
}
}
virtual quantifier_manager::check_model_result
check_model(proto_model * m, obj_map<enode, app *> const & root2value) {
if (m_fparams->m_mbqi) {
IF_VERBOSE(10, verbose_stream() << "model based quantifier instantiation...\n";);
if (m_model_checker->check(m, root2value)) {
return quantifier_manager::SAT;
}
else if (m_model_checker->has_new_instances()) {
return quantifier_manager::RESTART;
}
}
return quantifier_manager::UNKNOWN;
}
virtual void set_cancel(bool f) {
// TODO: interrupt MAM and MBQI
}
virtual final_check_status final_check_eh(bool full) {
if (!full) {
if (m_fparams->m_qi_lazy_instantiation)
return final_check_quant();
return FC_DONE;
}
else {
return final_check_quant();
}
}
/**
\brief Final check related with quantifiers...
*/
final_check_status final_check_quant() {
if (use_ematching()) {
if (m_lazy_matching_idx < m_fparams->m_qi_max_lazy_multipattern_matching) {
IF_VERBOSE(100, verbose_stream() << "matching delayed multi-patterns... \n";);
m_lazy_mam->rematch();
m_context->push_trail(value_trail<context, unsigned>(m_lazy_matching_idx));
m_lazy_matching_idx++;
}
}
return FC_DONE;
}
};
quantifier_manager_plugin * mk_default_plugin() {
return alloc(default_qm_plugin);
}
};

167
src/smt/smt_quantifier.h Normal file
View file

@ -0,0 +1,167 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
smt_quantifier.h
Abstract:
Quantifier reasoning support for smt::context.
Author:
Leonardo de Moura (leonardo) 2012-02-16.
Revision History:
--*/
#ifndef _SMT_QUANTIFIER_H_
#define _SMT_QUANTIFIER_H_
#include"ast.h"
#include"statistics.h"
#include"params.h"
#include"smt_types.h"
class proto_model;
struct front_end_params;
namespace smt {
class quantifier_manager_plugin;
class quantifier_stat;
class quantifier_manager {
struct imp;
imp * m_imp;
public:
quantifier_manager(context & ctx, front_end_params & fp, params_ref const & p);
~quantifier_manager();
context & kernel() const;
void set_plugin(quantifier_manager_plugin * plugin);
void add(quantifier * q, unsigned generation);
void del(quantifier * q);
bool empty() const;
bool is_shared(enode * n) const;
quantifier_stat * get_stat(quantifier * q) const;
unsigned get_generation(quantifier * q) const;
bool add_instance(quantifier * q, app * pat,
unsigned num_bindings,
enode * const * bindings,
unsigned max_generation,
unsigned min_top_generation,
unsigned max_top_generation,
ptr_vector<enode> & used_enodes);
bool add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation = 0);
void init_search_eh();
void assign_eh(quantifier * q);
void add_eq_eh(enode * n1, enode * n2);
void relevant_eh(enode * n);
final_check_status final_check_eh(bool full);
void restart_eh();
bool can_propagate() const;
void propagate();
enum check_model_result {
SAT, UNKNOWN, RESTART
};
bool model_based() const;
void adjust_model(proto_model * m);
check_model_result check_model(proto_model * m, obj_map<enode, app *> const & root2value);
void push();
void pop(unsigned num_scopes);
void reset();
void set_cancel(bool f);
void display(std::ostream & out) const;
void display_stats(std::ostream & out, quantifier * q) const;
void collect_statistics(::statistics & st) const;
void reset_statistics();
ptr_vector<quantifier>::const_iterator begin_quantifiers() const;
ptr_vector<quantifier>::const_iterator end_quantifiers() const;
};
class quantifier_manager_plugin {
public:
quantifier_manager_plugin() {}
virtual ~quantifier_manager_plugin() {}
virtual void set_manager(quantifier_manager & qm) = 0;
virtual quantifier_manager_plugin * mk_fresh() = 0;
virtual void add(quantifier * q) = 0;
virtual void del(quantifier * q) = 0;
virtual bool is_shared(enode * n) const = 0;
/**
\brief This method is invoked whenever q is assigned to true.
*/
virtual void assign_eh(quantifier * q) = 0;
/**
\brief This method is invoked whenever n1 and n2 are merged into the same equivalence class.
*/
virtual void add_eq_eh(enode * n1, enode * n2) = 0;
/**
\brief This method is invoked whenever n is marked as relevant.
*/
virtual void relevant_eh(enode * n) = 0;
/**
\brief This method is invoked when a new search() is started.
*/
virtual void init_search_eh() = 0;
/**
\brief Final_check event handler.
*/
virtual final_check_status final_check_eh(bool full) = 0;
/**
\brief This method is invoked whenever the solver restarts.
*/
virtual void restart_eh() = 0;
/**
\brief Return true if the quantifier_manager can propagate information
information back into the core.
*/
virtual bool can_propagate() const = 0;
virtual void propagate() = 0;
/**
\brief Return true if the plugin is "model based"
*/
virtual bool model_based() const = 0;
/**
\brief Give a change to the plugin to adjust the interpretation of unintepreted functions.
It can basically change the "else" of each uninterpreted function.
*/
virtual void adjust_model(proto_model * m) = 0;
/**
\brief Core invokes this method to check whether the candidate interpretation
satisfies the quantifiers in the manager.
It also provides a mapping from enodes to their interpretations.
*/
virtual quantifier_manager::check_model_result check_model(proto_model * m, obj_map<enode, app *> const & root2value) = 0;
virtual void push() = 0;
virtual void pop(unsigned num_scopes) = 0;
virtual void set_cancel(bool f) = 0;
};
};
#endif

View file

@ -0,0 +1,66 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_quantifier_instances.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-03-26.
Revision History:
--*/
#ifndef _SMT_QUANTIFIER_INSTANCES_H_
#define _SMT_QUANTIFIER_INSTANCES_H_
namespace smt {
class quantifier_instances;
class quantifier_instance {
quantifier * m_quantifier;
double m_cost;
enode * m_enodes[0];
quantifier_instance(quantifier * q, enode * const * enodes);
friend class quantifier_instances;
public:
quantifier * get_quantifier() const { return m_quantifier; }
unsigned get_num_decls() const { return m_quantifier->get_num_decls(); }
double get_cost() const { return m_cost; }
enode * const * get_enodes() const { return m_enodes; }
bool operator==(quantifier_instance const & other) const;
unsigned hash() const;
};
class quantifier_instances {
struct instance_lt {
ptr_vector<quantifier_instance> const & m_stack;
instance_lt(ptr_vector<quantifier_instance> const & s):
m_stack(s) {
}
bool operator()(int i1, int i2) const {
return m_stack[i1]->get_cost() < m_stack[i2]->get_cost();
}
};
context & m_context;
ast_manager & m_manager;
obj_hashtable<quantifier_instance> m_instances; //!< Set of instances.
ptr_vector<quantifier_instance> m_stack; //!< Stack for backtracking.
heap<instance_lt> m_queue; //!< Instantiation priority queue.
unsigned_vector m_scopes;
};
};
#endif /* _SMT_QUANTIFIER_INSTANCES_H_ */

View file

@ -0,0 +1,115 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_quantifier_stat.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-20.
Revision History:
--*/
#include"smt_quantifier_stat.h"
namespace smt {
quantifier_stat::quantifier_stat(unsigned generation):
m_size(0),
m_depth(0),
m_generation(generation),
m_case_split_factor(1),
m_num_nested_quantifiers(0),
m_num_instances(0),
m_num_instances_curr_search(0),
m_num_instances_curr_branch(0),
m_max_generation(0),
m_max_cost(0.0f) {
}
quantifier_stat_gen::quantifier_stat_gen(ast_manager & m, region & r):
m_manager(m),
m_region(r) {
}
void quantifier_stat_gen::reset() {
m_already_found.reset();
m_todo.reset();
m_case_split_factor = 1;
}
quantifier_stat * quantifier_stat_gen::operator()(quantifier * q, unsigned generation) {
reset();
quantifier_stat * r = new (m_region) quantifier_stat(generation);
m_todo.push_back(entry(q->get_expr()));
while (!m_todo.empty()) {
entry & e = m_todo.back();
expr * n = e.m_expr;
unsigned depth = e.m_depth;
bool depth_only = e.m_depth_only;
m_todo.pop_back();
unsigned old_depth;
if (m_already_found.find(n, old_depth)) {
if (old_depth >= depth)
continue;
depth_only = true;
}
m_already_found.insert(n, depth);
if (depth >= r->m_depth)
r->m_depth = depth;
if (!depth_only) {
r->m_size++;
if (is_quantifier(n))
r->m_num_nested_quantifiers ++;
if (is_app(n) && to_app(n)->get_family_id() == m_manager.get_basic_family_id()) {
unsigned num_args = to_app(n)->get_num_args();
// Remark: I'm approximating the case_split factor.
// I'm also ignoring the case split factor due to theories.
switch (to_app(n)->get_decl_kind()) {
case OP_OR:
if (depth == 0)
m_case_split_factor *= num_args;
else
m_case_split_factor *= (num_args + 1);
break;
case OP_AND:
if (depth > 0)
m_case_split_factor *= (num_args + 1);
break;
case OP_IFF:
if (depth == 0)
m_case_split_factor *= 4;
else
m_case_split_factor *= 9;
break;
case OP_ITE:
if (depth == 0)
m_case_split_factor *= 4;
else
m_case_split_factor *= 9;
break;
default:
break;
}
}
}
if (is_app(n)) {
unsigned j = to_app(n)->get_num_args();
while (j > 0) {
--j;
m_todo.push_back(entry(to_app(n)->get_arg(j), depth + 1, depth_only));
}
}
}
r->m_case_split_factor = m_case_split_factor.get_value();
return r;
}
};

View file

@ -0,0 +1,140 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_quantifier_stat.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-20.
Revision History:
--*/
#ifndef _SMT_QUANTIFIER_STAT_H_
#define _SMT_QUANTIFIER_STAT_H_
#include"ast.h"
#include"obj_hashtable.h"
#include"approx_nat.h"
#include"region.h"
namespace smt {
/**
\brief Store statistics for quantifiers. This information is
used to implement heuristics for quantifier instantiation.
*/
class quantifier_stat {
unsigned m_size;
unsigned m_depth;
unsigned m_generation;
unsigned m_case_split_factor; //!< the product of the size of the clauses created by this quantifier.
unsigned m_num_nested_quantifiers;
unsigned m_num_instances;
unsigned m_num_instances_curr_search;
unsigned m_num_instances_curr_branch; //!< only updated if QI_TRACK_INSTANCES is true
unsigned m_max_generation; //!< max. generation of an instance
float m_max_cost;
friend class quantifier_stat_gen;
quantifier_stat(unsigned generation);
public:
unsigned get_size() const {
return m_size;
}
unsigned get_depth() const {
return m_depth;
}
unsigned get_generation() const {
return m_generation;
}
unsigned get_case_split_factor() const {
return m_case_split_factor;
}
unsigned get_num_nested_quantifiers() const {
return m_num_nested_quantifiers;
}
unsigned get_num_instances() const {
return m_num_instances;
}
unsigned get_num_instances_curr_search() const {
return m_num_instances_curr_search;
}
unsigned & get_num_instances_curr_branch() {
return m_num_instances_curr_branch;
}
void inc_num_instances() {
m_num_instances++;
m_num_instances_curr_search++;
}
void inc_num_instances_curr_branch() {
m_num_instances_curr_branch++;
}
void reset_num_instances_curr_search() {
m_num_instances_curr_search = 0;
}
void update_max_generation(unsigned g) {
if (m_max_generation < g)
m_max_generation = g;
}
unsigned get_max_generation() const {
return m_max_generation;
}
void update_max_cost(float c) {
if (m_max_cost < c)
m_max_cost = c;
}
float get_max_cost() const {
return m_max_cost;
}
};
/**
\brief Functor used to generate quantifier statistics.
*/
class quantifier_stat_gen {
struct entry {
expr * m_expr;
unsigned m_depth:31;
bool m_depth_only:1; //!< track only the depth of this entry.
entry():m_expr(0), m_depth(0), m_depth_only(false) {}
entry(expr * n, unsigned depth = 0, bool depth_only = false):m_expr(n), m_depth(depth), m_depth_only(depth_only) {}
};
ast_manager & m_manager;
region & m_region;
obj_map<expr, unsigned> m_already_found; // expression to the max. depth it was reached.
svector<entry> m_todo;
approx_nat m_case_split_factor;
void reset();
public:
quantifier_stat_gen(ast_manager & m, region & r);
quantifier_stat * operator()(quantifier * q, unsigned generation);
};
};
#endif /* _SMT_QUANTIFIER_STAT_H_ */

View file

@ -0,0 +1,421 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_quick_checker.cpp
Abstract:
Incomplete model checker.
Author:
Leonardo de Moura (leonardo) 2008-06-20.
Revision History:
--*/
#include"smt_context.h"
#include"smt_quick_checker.h"
#include"ast_pp.h"
namespace smt {
quick_checker::collector::collector(context & c):
m_context(c),
m_manager(c.get_manager()),
m_conservative(true) {
}
void quick_checker::collector::init(quantifier * q) {
m_num_vars = q->get_num_decls();
m_already_found.reserve(m_num_vars+1, false);
m_candidates.reserve(m_num_vars+1);
m_tmp_candidates.reserve(m_num_vars+1);
for (unsigned i = 0; i < m_num_vars; i++) {
m_already_found[i] = false;
m_candidates[i].reset();
}
m_cache.reset();
}
/**
\brief Returns true if there is a term (f ... n' ...) in the logical
context such that n' is the i-th argument of f, and n and n' are in the
same equivalence class.
If f == 0 then true is returned (f == 0 means there is no parent to check)
*/
bool quick_checker::collector::check_arg(enode * n, func_decl * f, unsigned i) {
if (!f || !m_conservative)
return true;
enode_vector::const_iterator it = m_context.begin_enodes_of(f);
enode_vector::const_iterator end = m_context.end_enodes_of(f);
for (; it != end; ++it) {
enode * curr = *it;
if (m_context.is_relevant(curr) && curr->is_cgr() && i < curr->get_num_args() && curr->get_arg(i)->get_root() == n->get_root())
return true;
}
return false;
}
void quick_checker::collector::collect_core(app * n, func_decl * p, unsigned i) {
func_decl * f = n->get_decl();
unsigned num_args = n->get_num_args();
for (unsigned j = 0; j < num_args; j++) {
expr * arg = n->get_arg(j);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
if (idx >= m_num_vars)
return;
if (m_already_found[idx] && m_conservative) {
enode_set & s = m_candidates[idx];
enode_set & ns = m_tmp_candidates[idx];
if (s.empty())
continue;
ns.reset();
enode_vector::const_iterator it = m_context.begin_enodes_of(f);
enode_vector::const_iterator end = m_context.end_enodes_of(f);
for (; it != end; ++it) {
enode * curr = *it;
if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) {
enode * arg = curr->get_arg(j)->get_root();
// intersection
if (s.contains(arg))
ns.insert(arg);
}
}
SASSERT(m_conservative);
s.swap(ns);
}
else {
m_already_found[idx] = true;
enode_set & s = m_candidates[idx];
enode_vector::const_iterator it = m_context.begin_enodes_of(f);
enode_vector::const_iterator end = m_context.end_enodes_of(f);
for (; it != end; ++it) {
enode * curr = *it;
if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) {
enode * arg = curr->get_arg(j)->get_root();
s.insert(arg);
}
}
}
}
else {
if (n->get_family_id() != m_manager.get_basic_family_id())
collect(arg, n->get_decl(), j);
else
collect(arg, 0, 0);
}
}
}
void quick_checker::collector::collect(expr * n, func_decl * f, unsigned idx) {
if (is_quantifier(n))
return;
if (is_var(n))
return;
if (is_ground(n))
return;
entry e(n, f, idx);
if (m_cache.contains(e))
return;
m_cache.insert(e);
collect_core(to_app(n), f, idx);
}
void quick_checker::collector::save_result(vector<enode_vector> & candidates) {
candidates.reserve(m_num_vars+1);
for (unsigned i = 0; i < m_num_vars; i++) {
enode_vector & v = candidates[i];
v.reset();
enode_set & s = m_candidates[i];
enode_set::iterator it = s.begin();
enode_set::iterator end = s.end();
for (; it != end; ++it) {
enode * curr = *it;
v.push_back(curr);
}
}
TRACE("collector",
tout << "candidates:\n";
for (unsigned i = 0; i < m_num_vars; i++) {
tout << "var " << i << ":";
enode_vector & v = candidates[i];
enode_vector::iterator it = v.begin();
enode_vector::iterator end = v.end();
for (; it != end; ++it)
tout << " #" << (*it)->get_owner_id();
tout << "\n";
});
}
void quick_checker::collector::operator()(quantifier * q, bool conservative, vector<enode_vector> & candidates) {
flet<bool> l(m_conservative, conservative);
init(q);
TRACE("collector", tout << "model checking: #" << q->get_id() << "\n" << mk_pp(q, m_manager) << "\n";);
collect(q->get_expr(), 0, 0);
save_result(candidates);
}
quick_checker::quick_checker(context & c):
m_context(c),
m_manager(c.get_manager()),
m_simplifier(c.get_simplifier()),
m_collector(c),
m_new_exprs(m_manager) {
}
/**
\brief Instantiate instances unsatisfied by the current model. Return true if new instances were generated.
*/
bool quick_checker::instantiate_unsat(quantifier * q) {
TRACE("quick_checker", tout << "instantiate instances unsatisfied by current model\n" << mk_pp(q, m_manager) << "\n";);
m_candidate_vectors.reset();
m_collector(q, true, m_candidate_vectors);
m_num_bindings = q->get_num_decls();
return process_candidates(q, true);
}
/**
\brief Instantiate instances not satisfied by the current model. Return true if new instances were generated.
*/
bool quick_checker::instantiate_not_sat(quantifier * q) {
TRACE("quick_checker", tout << "instantiate instances not satisfied by current model\n" << mk_pp(q, m_manager) << "\n";);
m_candidate_vectors.reset();
m_collector(q, false, m_candidate_vectors);
m_num_bindings = q->get_num_decls();
return process_candidates(q, false);
}
bool quick_checker::instantiate_not_sat(quantifier * q, unsigned num_candidates, expr * const * candidates) {
// initialize m_candidates using the given set of candidates.
m_candidate_vectors.reset();
m_num_bindings = q->get_num_decls();
m_candidate_vectors.reserve(m_num_bindings+1);
for (unsigned i = 0; i < m_num_bindings; i++) {
m_candidate_vectors[i].reset();
sort * s = q->get_decl_sort(i);
for (unsigned j = 0; j < num_candidates; j++) {
if (m_manager.get_sort(candidates[j]) == s) {
expr * n = candidates[j];
m_context.internalize(n, false);
enode * e = m_context.get_enode(n);
m_candidate_vectors[i].push_back(e);
}
}
}
return process_candidates(q, false);
}
bool quick_checker::process_candidates(quantifier * q, bool unsat) {
ptr_vector<enode> empty_used_enodes;
buffer<unsigned> szs;
buffer<unsigned> it;
for (unsigned i = 0; i < m_num_bindings; i++) {
unsigned sz = m_candidate_vectors[i].size();
if (sz == 0)
return false;
szs.push_back(sz);
it.push_back(0);
}
TRACE("quick_checker_sizes", tout << mk_pp(q, m_manager) << "\n"; for (unsigned i = 0; i < szs.size(); i++) tout << szs[i] << " "; tout << "\n";);
TRACE("quick_checker_candidates",
tout << "candidates:\n";
for (unsigned i = 0; i < m_num_bindings; i++) {
enode_vector & v = m_candidate_vectors[i];
enode_vector::iterator it = v.begin();
enode_vector::iterator end = v.end();
for (; it != end; ++it)
tout << "#" << (*it)->get_owner_id() << " ";
tout << "\n";
});
bool result = false;
m_bindings.reserve(m_num_bindings+1, 0);
do {
for (unsigned i = 0; i < m_num_bindings; i++)
m_bindings[m_num_bindings - i - 1] = m_candidate_vectors[i][it[i]];
if (!m_context.contains_instance(q, m_num_bindings, m_bindings.c_ptr())) {
bool is_candidate = false;
TRACE("quick_checker", tout << "processing bindings:";
for (unsigned i = 0; i < m_num_bindings; i++) tout << " #" << m_bindings[i]->get_owner_id();
tout << "\n";);
if (unsat)
is_candidate = check_quantifier(q, false);
else
is_candidate = !check_quantifier(q, true);
if (is_candidate) {
TRACE("quick_checker", tout << "found new candidate\n";);
TRACE("quick_checker_sizes", tout << "found new candidate\n";
for (unsigned i = 0; i < m_num_bindings; i++) tout << "#" << m_bindings[i]->get_owner_id() << " "; tout << "\n";);
unsigned max_generation = get_max_generation(m_num_bindings, m_bindings.c_ptr());
if (m_context.add_instance(q, 0 /* no pattern was used */, m_num_bindings, m_bindings.c_ptr(), max_generation,
0, // min_top_generation is only available for instances created by the MAM
0, // max_top_generation is only available for instances created by the MAM
empty_used_enodes))
result = true;
}
}
}
while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr()));
return result;
}
bool quick_checker::check_quantifier(quantifier * n, bool is_true) {
bool r = check(n->get_expr(), is_true);
m_new_exprs.reset();
m_check_cache.reset();
m_canonize_cache.reset();
return r;
}
bool quick_checker::all_args(app * a, bool is_true) {
unsigned num_args = a->get_num_args();
for (unsigned i = 0; i < num_args; i++)
if (!check(a->get_arg(i), is_true))
return false;
return true;
}
bool quick_checker::any_arg(app * a, bool is_true) {
unsigned num_args = a->get_num_args();
for (unsigned i = 0; i < num_args; i++)
if (check(a->get_arg(i), is_true))
return true;
return false;
}
bool quick_checker::check_core(expr * n, bool is_true) {
SASSERT(m_manager.is_bool(n));
if (m_context.b_internalized(n) && m_context.is_relevant(n)) {
lbool val = m_context.get_assignment(n);
if (val != l_undef && is_true == (val == l_true))
return true;
else
return false;
}
if (!is_app(n))
return false;
app * a = to_app(n);
if (a->get_family_id() == m_manager.get_basic_family_id()) {
switch (a->get_decl_kind()) {
case OP_TRUE:
return is_true;
case OP_FALSE:
return !is_true;
case OP_NOT:
return check(a->get_arg(0), !is_true);
case OP_OR:
return is_true ? any_arg(a, true) : all_args(a, false);
case OP_AND:
return is_true ? all_args(a, true) : any_arg(a, false);
case OP_IFF:
if (is_true)
return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false));
else
return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true));
case OP_ITE:
if (check(a->get_arg(0), true))
return check(a->get_arg(1), is_true);
else if (check(a->get_arg(0), false))
return check(a->get_arg(2), is_true);
else
return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true);
case OP_EQ:
if (is_true) {
return canonize(a->get_arg(0)) == canonize(a->get_arg(1));
}
else {
expr * lhs = canonize(a->get_arg(0));
expr * rhs = canonize(a->get_arg(1));
if (m_context.e_internalized(lhs) && m_context.is_relevant(lhs) &&
m_context.e_internalized(rhs) && m_context.is_relevant(rhs) &&
m_context.get_enode(lhs)->get_root() != m_context.get_enode(rhs)->get_root())
return true;
return m_manager.are_distinct(lhs, rhs);
}
default:
break;
}
}
expr * new_a = canonize(a);
TRACE("quick_checker_canonizer", tout << "before:\n" << mk_pp(a, m_manager) << "\nafter:\n" << mk_pp(new_a, m_manager) << "\n";);
if (m_context.lit_internalized(new_a) && m_context.is_relevant(new_a)) {
lbool val = m_context.get_assignment(new_a);
if (val != l_undef)
return is_true == (val == l_true);
}
if (is_true && m_manager.is_true(new_a))
return true;
if (!is_true && m_manager.is_false(new_a))
return true;
return false;
}
bool quick_checker::check(expr * n, bool is_true) {
expr_bool_pair p(n, is_true);
bool r;
if (m_check_cache.find(p, r))
return r;
r = check_core(n, is_true);
m_check_cache.insert(p, r);
return r;
}
expr * quick_checker::canonize(expr * n) {
if (is_var(n)) {
unsigned idx = to_var(n)->get_idx();
if (idx >= m_num_bindings)
return n;
// VAR 0 is stored in the last position of m_bindings
return m_bindings[m_num_bindings - idx - 1]->get_root()->get_owner();
}
if (m_context.e_internalized(n))
return m_context.get_enode(n)->get_root()->get_owner();
if (!is_app(n) || to_app(n)->get_num_args() == 0)
return n;
expr * r;
if (m_canonize_cache.find(n, r))
return r;
bool has_arg_enodes = true;
ptr_buffer<expr> new_args;
ptr_buffer<enode> new_arg_enodes;
unsigned num_args = to_app(n)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = canonize(to_app(n)->get_arg(i));
new_args.push_back(arg);
if (m_context.e_internalized(arg))
new_arg_enodes.push_back(m_context.get_enode(arg));
else
has_arg_enodes = false;
}
if (has_arg_enodes) {
enode * e = m_context.get_enode_eq_to(to_app(n)->get_decl(), num_args, new_arg_enodes.c_ptr());
if (e) {
m_canonize_cache.insert(n, e->get_root()->get_owner());
return e->get_root()->get_owner();
}
}
// substitute by values in the model
for (unsigned i = 0; i < num_args; i++) {
expr * arg = new_args[i];
if (m_context.e_internalized(arg)) {
expr_ref new_value(m_manager);
if (m_context.get_value(m_context.get_enode(arg), new_value)) {
new_args[i] = new_value;
m_new_exprs.push_back(new_value);
}
}
}
expr_ref new_expr(m_manager);
m_simplifier.mk_app(to_app(n)->get_decl(), num_args, new_args.c_ptr(), new_expr);
m_new_exprs.push_back(new_expr);
m_canonize_cache.insert(n, new_expr);
return new_expr;
}
};

106
src/smt/smt_quick_checker.h Normal file
View file

@ -0,0 +1,106 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_quick_cheker.h
Abstract:
Incomplete model checker.
Author:
Leonardo de Moura (leonardo) 2008-06-20.
Revision History:
--*/
#ifndef _SMT_QUICK_CHECKER_H_
#define _SMT_QUICK_CHECKER_H_
#include"ast.h"
#include"simplifier.h"
#include"obj_hashtable.h"
namespace smt {
class context;
/**
\brief Simple object for finding quantifier instantiations that are falsified by the current model.
\remark The code for selecting candidates is very naive.
*/
class quick_checker {
typedef obj_hashtable<enode> enode_set;
/**
\brief Functor for collecting candidates for quantifier instantiation.
*/
class collector {
context & m_context;
ast_manager & m_manager;
bool m_conservative;
unsigned m_num_vars;
svector<bool> m_already_found; // mapping from var_idx -> bool
vector<enode_set> m_candidates; // mapping from var_idx -> set of candidates
vector<enode_set> m_tmp_candidates; // auxiliary mapping from var_idx -> set of candidates
struct entry {
expr * m_expr;
func_decl * m_parent;
unsigned m_parent_pos;
entry(expr * n = 0, func_decl * d = 0, unsigned p = 0):m_expr(n), m_parent(d), m_parent_pos(p) {}
unsigned hash() const { return m_parent ? mk_mix(m_expr->get_id(), m_parent->get_id(), m_parent_pos) : m_expr->get_id(); }
bool operator==(entry const & e) const { return m_expr == e.m_expr && m_parent == e.m_parent && m_parent_pos == e.m_parent_pos; }
};
typedef hashtable<entry, obj_hash<entry>, default_eq<entry> > cache;
cache m_cache;
void init(quantifier * q);
bool check_arg(enode * n, func_decl * f, unsigned i);
void collect_core(app * n, func_decl * p, unsigned i);
void collect(expr * n, func_decl * f, unsigned idx);
void save_result(vector<enode_vector> & candidates);
public:
collector(context & c);
void operator()(quantifier * q, bool conservative, vector<enode_vector> & candidates);
};
typedef std::pair<expr *, bool> expr_bool_pair;
typedef pair_hash<obj_ptr_hash<expr>, int_hash> expr_bool_pair_hash;
typedef map<expr_bool_pair, bool, expr_bool_pair_hash, default_eq<expr_bool_pair> > check_cache;
typedef obj_map<expr, expr *> canonize_cache;
context & m_context;
ast_manager & m_manager;
simplifier & m_simplifier;
collector m_collector;
expr_ref_vector m_new_exprs;
vector<enode_vector> m_candidate_vectors;
check_cache m_check_cache;
canonize_cache m_canonize_cache;
unsigned m_num_bindings;
ptr_vector<enode> m_bindings;
bool all_args(app * a, bool is_true);
bool any_arg(app * a, bool is_true);
bool check_core(expr * n, bool is_true);
bool check(expr * n, bool is_true);
bool check_quantifier(quantifier * n, bool is_true);
expr * canonize(expr * n);
bool process_candidates(quantifier * q, bool unsat);
public:
quick_checker(context & c);
bool instantiate_unsat(quantifier * q);
bool instantiate_not_sat(quantifier * q);
bool instantiate_not_sat(quantifier * q, unsigned num_candidates, expr * const * candidates);
};
};
#endif /* _SMT_QUICK_CHECKER_H_ */

669
src/smt/smt_relevancy.cpp Normal file
View file

@ -0,0 +1,669 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_relevancy.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-04.
Revision History:
--*/
#include"smt_context.h"
#include"smt_relevancy.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"ast_smt2_pp.h"
namespace smt {
void relevancy_eh::mark_as_relevant(relevancy_propagator & rp, expr * n) {
rp.mark_as_relevant(n);
}
void relevancy_eh::mark_args_as_relevant(relevancy_propagator & rp, app * n) {
unsigned j = n->get_num_args();
while (j > 0) {
--j;
rp.mark_as_relevant(n->get_arg(j));
}
}
void simple_relevancy_eh::operator()(relevancy_propagator & rp) {
rp.mark_as_relevant(m_target);
}
void pair_relevancy_eh::operator()(relevancy_propagator & rp) {
if (!rp.is_relevant(m_source1))
return;
if (!rp.is_relevant(m_source2))
return;
rp.mark_as_relevant(m_target);
}
class and_relevancy_eh : public relevancy_eh {
app * m_parent;
public:
and_relevancy_eh(app * p):m_parent(p) {}
virtual ~and_relevancy_eh() {}
virtual void operator()(relevancy_propagator & rp);
};
class or_relevancy_eh : public relevancy_eh {
app * m_parent;
public:
or_relevancy_eh(app * p):m_parent(p) {}
virtual ~or_relevancy_eh() {}
virtual void operator()(relevancy_propagator & rp);
};
class ite_relevancy_eh : public relevancy_eh {
app * m_parent;
public:
ite_relevancy_eh(app * p):m_parent(p) {}
virtual ~ite_relevancy_eh() {}
virtual void operator()(relevancy_propagator & rp);
};
class ite_term_relevancy_eh : public relevancy_eh {
app * m_parent;
app * m_then_eq;
app * m_else_eq;
public:
ite_term_relevancy_eh(app * p, app * then_eq, app * else_eq):m_parent(p), m_then_eq(then_eq), m_else_eq(else_eq) {}
virtual ~ite_term_relevancy_eh() {}
virtual void operator()(relevancy_propagator & rp);
};
relevancy_propagator::relevancy_propagator(context & ctx):
m_context(ctx) {
}
bool relevancy_propagator::enabled() const {
return m_context.relevancy();
}
region & relevancy_propagator::get_region() const {
return m_context.get_region();
}
ast_manager & relevancy_propagator::get_manager() const {
return m_context.get_manager();
}
void relevancy_propagator::add_dependency(expr * src, expr * target) {
if (!enabled())
return;
if (is_relevant(src))
mark_as_relevant(target);
else
add_handler(src, mk_relevancy_eh(simple_relevancy_eh(target)));
}
relevancy_eh * relevancy_propagator::mk_or_relevancy_eh(app * n) {
SASSERT(get_manager().is_or(n));
return mk_relevancy_eh(or_relevancy_eh(n));
}
relevancy_eh * relevancy_propagator::mk_and_relevancy_eh(app * n) {
SASSERT(get_manager().is_and(n));
return mk_relevancy_eh(and_relevancy_eh(n));
}
relevancy_eh * relevancy_propagator::mk_ite_relevancy_eh(app * n) {
SASSERT(get_manager().is_ite(n));
return mk_relevancy_eh(ite_relevancy_eh(n));
}
relevancy_eh * relevancy_propagator::mk_term_ite_relevancy_eh(app * c, app * t, app * e) {
return mk_relevancy_eh(ite_term_relevancy_eh(c, t, e));
}
struct relevancy_propagator_imp : public relevancy_propagator {
unsigned m_qhead;
expr_ref_vector m_relevant_exprs;
obj_hashtable<expr> m_is_relevant;
typedef list<relevancy_eh *> relevancy_ehs;
obj_map<expr, relevancy_ehs *> m_relevant_ehs;
obj_map<expr, relevancy_ehs *> m_watches[2];
struct eh_trail {
enum kind { POS_WATCH, NEG_WATCH, HANDLER };
kind m_kind;
expr * m_node;
eh_trail(expr * n):m_kind(HANDLER), m_node(n) {}
eh_trail(expr * n, bool val):m_kind(val ? POS_WATCH : NEG_WATCH), m_node(n) {}
kind get_kind() const { return m_kind; }
expr * get_node() const { return m_node; }
};
svector<eh_trail> m_trail;
struct scope {
unsigned m_relevant_exprs_lim;
unsigned m_trail_lim;
};
svector<scope> m_scopes;
relevancy_propagator_imp(context & ctx):relevancy_propagator(ctx), m_qhead(0), m_relevant_exprs(ctx.get_manager()) {}
virtual ~relevancy_propagator_imp() {
undo_trail(0);
}
relevancy_ehs * get_handlers(expr * n) {
relevancy_ehs * r = 0;
m_relevant_ehs.find(n, r);
SASSERT(m_relevant_ehs.contains(n) || r == 0);
return r;
}
void set_handlers(expr * n, relevancy_ehs * ehs) {
if (ehs == 0)
m_relevant_ehs.erase(n);
else
m_relevant_ehs.insert(n, ehs);
}
relevancy_ehs * get_watches(expr * n, bool val) {
relevancy_ehs * r = 0;
m_watches[val ? 1 : 0].find(n, r);
SASSERT(m_watches[val ? 1 : 0].contains(n) || r == 0);
return r;
}
void set_watches(expr * n, bool val, relevancy_ehs * ehs) {
if (ehs == 0)
m_watches[val ? 1 : 0].erase(n);
else
m_watches[val ? 1 : 0].insert(n, ehs);
}
void push_trail(eh_trail const & t) {
get_manager().inc_ref(t.get_node());
m_trail.push_back(t);
}
virtual void add_handler(expr * source, relevancy_eh * eh) {
if (!enabled())
return;
if (is_relevant_core(source)) {
eh->operator()(*this, source);
}
else {
SASSERT(eh);
push_trail(eh_trail(source));
set_handlers(source, new (get_region()) relevancy_ehs(eh, get_handlers(source)));
}
}
virtual void add_watch(expr * n, bool val, relevancy_eh * eh) {
if (!enabled())
return;
lbool lval = m_context.find_assignment(n);
if (!val)
lval = ~lval;
switch (lval) {
case l_false:
return;
case l_undef:
SASSERT(eh);
push_trail(eh_trail(n, val));
set_watches(n, val, new (get_region()) relevancy_ehs(eh, get_watches(n, val)));
break;
case l_true:
eh->operator()(*this, n, val);
break;
}
}
virtual void add_watch(expr * n, bool val, expr * target) {
if (!enabled())
return;
lbool lval = m_context.find_assignment(n);
if (!val)
lval = ~lval;
switch (lval) {
case l_false:
return;
case l_undef:
add_watch(n, val, mk_relevancy_eh(simple_relevancy_eh(target)));
break;
case l_true:
mark_as_relevant(target); propagate();
break;
}
}
bool is_relevant_core(expr * n) const { return m_is_relevant.contains(n); }
virtual bool is_relevant(expr * n) const {
return !enabled() || is_relevant_core(n);
}
virtual void push() {
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_relevant_exprs_lim = m_relevant_exprs.size();
s.m_trail_lim = m_trail.size();
}
virtual void pop(unsigned num_scopes) {
SASSERT(m_context.get_scope_level() == m_scopes.size());
unsigned lvl = m_scopes.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
scope & s = m_scopes[new_lvl];
unmark_relevant_exprs(s.m_relevant_exprs_lim);
undo_trail(s.m_trail_lim);
m_scopes.shrink(new_lvl);
}
/**
\brief Unmark expressions marked as relevant.
*/
void unmark_relevant_exprs(unsigned old_lim) {
SASSERT(old_lim <= m_relevant_exprs.size());
unsigned i = m_relevant_exprs.size();
while (i != old_lim) {
--i;
expr * n = m_relevant_exprs.get(i);
m_is_relevant.erase(n);
TRACE("propagate_relevancy", tout << "unmarking:\n" << mk_ismt2_pp(n, get_manager()) << "\n";);
}
m_relevant_exprs.shrink(old_lim);
m_qhead = m_relevant_exprs.size();
}
void undo_trail(unsigned old_lim) {
SASSERT(old_lim <= m_trail.size());
ast_manager & m = get_manager();
unsigned i = m_trail.size();
while (i != old_lim) {
--i;
eh_trail & t = m_trail[i];
expr * n = t.get_node();
relevancy_ehs * ehs;
switch (t.get_kind()) {
case eh_trail::POS_WATCH: ehs = get_watches(n, true); SASSERT(ehs); set_watches(n, true, ehs->tail()); break;
case eh_trail::NEG_WATCH: ehs = get_watches(n, false); SASSERT(ehs); set_watches(n, false, ehs->tail()); break;
case eh_trail::HANDLER: ehs = get_handlers(n); SASSERT(ehs); set_handlers(n, ehs->tail()); break;
default: UNREACHABLE(); break;
}
m.dec_ref(n);
}
m_trail.shrink(old_lim);
}
void set_relevant(expr * n) {
m_is_relevant.insert(n);
m_relevant_exprs.push_back(n);
m_context.relevant_eh(n);
}
/**
\brief Mark an expression as relevant and propagate
the relevancy to its descendants.
*/
void mark_and_propagate(expr * n) {
if (!enabled())
return;
if (!is_relevant_core(n)) {
mark_as_relevant(n);
propagate();
}
}
/**
\brief Mark the given expression as relevant if it is not
already marked.
*/
virtual void mark_as_relevant(expr * n) {
if (!enabled())
return;
if (!is_relevant_core(n)) {
enode * e = m_context.find_enode(n);
if (e != 0) {
enode * curr = e;
do {
set_relevant(curr->get_owner());
curr = curr->get_next();
}
while (curr != e);
}
else {
set_relevant(n);
}
}
}
/**
\brief Marks the children of n as relevant.
\pre n is marked as relevant.
*/
void propagate_relevant_app(app * n) {
SASSERT(is_relevant_core(n));
unsigned j = n->get_num_args();
while (j > 0) {
--j;
mark_as_relevant(n->get_arg(j));
}
}
/**
\brief Propagate relevancy for an or-application.
*/
void propagate_relevant_or(app * n) {
SASSERT(get_manager().is_or(n));
lbool val = m_context.find_assignment(n);
// If val is l_undef, then the expression
// is a root, and no boolean variable was created for it.
if (val == l_undef)
val = l_true;
switch (val) {
case l_false:
propagate_relevant_app(n);
break;
case l_undef:
break;
case l_true: {
expr * true_arg = 0;
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = n->get_arg(i);
if (m_context.find_assignment(arg) == l_true) {
if (is_relevant_core(arg))
return;
else if (!true_arg)
true_arg = arg;
}
}
if (true_arg)
mark_as_relevant(true_arg);
break;
} }
}
/**
\brief Propagate relevancy for an and-application.
*/
void propagate_relevant_and(app * n) {
lbool val = m_context.find_assignment(n);
switch (val) {
case l_false: {
expr * false_arg = 0;
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = n->get_arg(i);
if (m_context.find_assignment(arg) == l_false) {
if (is_relevant_core(arg))
return;
else if (!false_arg)
false_arg = arg;
}
}
if (false_arg)
mark_as_relevant(false_arg);
break;
}
case l_undef:
break;
case l_true:
propagate_relevant_app(n);
break;
}
}
/**
\brief Propagate relevancy for an ite-expression.
*/
void propagate_relevant_ite(app * n) {
TRACE("propagate_relevant_ite", tout << "propagating relevancy for #" << n->get_id() << "\n" << mk_pp(n, get_manager()) << "\n";);
mark_as_relevant(n->get_arg(0));
switch (m_context.find_assignment(n->get_arg(0))) {
case l_false:
TRACE("propagate_relevant_ite", tout << "marking as relevant: " << mk_pp(n->get_arg(2), get_manager()) << "\n";);
mark_as_relevant(n->get_arg(2));
break;
case l_undef:
TRACE("propagate_relevant_ite", tout << "ite c is unassigned\n";);
break;
case l_true:
TRACE("propagate_relevant_ite", tout << "marking as relevant: " << mk_pp(n->get_arg(1), get_manager()) << "\n";);
mark_as_relevant(n->get_arg(1));
break;
}
}
/**
\brief Propagate relevancy to the arguments of recently marked
expressions. That is, expressions that are located at positions
[m_qhead, m_relevant_exprs.size()) in the stack of
relevant expressions.
*/
virtual void propagate() {
ast_manager & m = get_manager();
while (m_qhead < m_relevant_exprs.size()) {
expr * n = m_relevant_exprs.get(m_qhead);
TRACE("propagate_relevancy_to_args", tout << "propagating relevancy to args of #" << n->get_id() << "\n";);
TRACE("propagate_relevancy", tout << "marking as relevant:\n" << mk_bounded_pp(n, m) << "\n";);
SASSERT(is_relevant_core(n));
m_qhead++;
if (is_app(n)) {
family_id fid = to_app(n)->get_family_id();
if (fid == m.get_basic_family_id()) {
switch (to_app(n)->get_decl_kind()) {
case OP_OR:
propagate_relevant_or(to_app(n));
break;
case OP_AND:
propagate_relevant_and(to_app(n));
break;
case OP_ITE:
propagate_relevant_ite(to_app(n));
break;
default:
propagate_relevant_app(to_app(n));
break;
}
}
else {
propagate_relevant_app(to_app(n));
}
}
relevancy_ehs * ehs = get_handlers(n);
while (ehs != 0) {
ehs->head()->operator()(*this, n);
ehs = ehs->tail();
}
}
}
virtual bool can_propagate() const {
return m_qhead < m_relevant_exprs.size();
}
virtual void assign_eh(expr * n, bool val) {
if (!enabled())
return;
ast_manager & m = get_manager();
SASSERT(enabled());
if (is_relevant_core(n)) {
if (m.is_or(n))
propagate_relevant_or(to_app(n));
else if (m.is_and(n))
propagate_relevant_and(to_app(n));
}
relevancy_ehs * ehs = get_watches(n, val);
while (ehs != 0) {
ehs->head()->operator()(*this, n, val);
ehs = ehs->tail();
}
}
virtual void display(std::ostream & out) const {
if (enabled() && !m_relevant_exprs.empty()) {
out << "relevant exprs:\n";
for (unsigned i = 0; i < m_relevant_exprs.size(); i++) {
out << "#" << m_relevant_exprs.get(i)->get_id() << " ";
}
out << "\n";
}
}
#ifdef Z3DEBUG
bool check_relevancy_app(app * n) const {
SASSERT(is_relevant(n));
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
CTRACE("relevancy_bug", !is_relevant(n->get_arg(i)), tout << "n: " << mk_ismt2_pp(n, get_manager()) << "\ni: " << i << "\n";);
SASSERT(is_relevant(n->get_arg(i)));
}
return true;
}
virtual bool check_relevancy_or(app * n, bool root) const {
lbool val = root ? l_true : m_context.find_assignment(n);
if (val == l_false)
return check_relevancy_app(n);
if (val == l_true) {
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = n->get_arg(i);
if (m_context.find_assignment(arg) == l_true && is_relevant(arg))
return true;
}
TRACE("check_relevancy", tout << "failed:\n" << mk_ll_pp(n, get_manager()); display(tout););
UNREACHABLE();
}
return true;
}
bool check_relevancy_and(app * n) const {
lbool val = m_context.find_assignment(n);
if (val == l_true)
return check_relevancy_app(n);
if (val == l_false) {
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = n->get_arg(i);
if (m_context.find_assignment(arg) == l_false && is_relevant(arg))
return true;
}
UNREACHABLE();
}
return true;
}
bool check_relevancy_ite(app * n) const {
SASSERT(is_relevant(n->get_arg(0)));
switch (m_context.find_assignment(n->get_arg(0))) {
case l_false:
if (get_manager().is_bool(n)) {
TRACE("ite_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";);
SASSERT(is_relevant(n->get_arg(2)));
}
else {
app_ref eq(get_manager());
eq = m_context.mk_eq_atom(n, n->get_arg(2));
SASSERT(is_relevant(eq.get()));
}
break;
case l_undef:
break;
case l_true:
if (get_manager().is_bool(n)) {
SASSERT(is_relevant(n->get_arg(1)));
}
else {
app_ref eq(get_manager());
eq = m_context.mk_eq_atom(n, n->get_arg(1));
SASSERT(is_relevant(eq.get()));
}
break;
}
return true;
}
bool check_relevancy(expr_ref_vector const & v) const {
SASSERT(!can_propagate());
ast_manager & m = get_manager();
unsigned sz = v.size();
for (unsigned i = 0; i < sz; i++) {
expr * n = v.get(i);
if (is_relevant(n)) {
TRACE("check_relevancy", tout << "checking:\n" << mk_ll_pp(n, get_manager()) << "internalized: " << m_context.find_enode(n) << "\n";);
if (m.is_or(n)) {
SASSERT(check_relevancy_or(to_app(n), false));
}
else if (m.is_and(n)) {
SASSERT(check_relevancy_and(to_app(n)));
}
else if (m.is_ite(n)) {
SASSERT(check_relevancy_ite(to_app(n)));
}
else if (is_app(n)) {
SASSERT(check_relevancy_app(to_app(n)));
}
else {
enode * n0 = m_context.find_enode(n);
if (n0 != 0) {
enode * n = n0->get_next();
while (n0 != n) {
SASSERT(is_relevant(n->get_owner()));
n = n->get_next();
}
}
}
}
}
return true;
}
#endif
};
void and_relevancy_eh::operator()(relevancy_propagator & rp) {
if (rp.is_relevant(m_parent))
static_cast<relevancy_propagator_imp&>(rp).propagate_relevant_and(m_parent);
}
void or_relevancy_eh::operator()(relevancy_propagator & rp) {
if (rp.is_relevant(m_parent))
static_cast<relevancy_propagator_imp&>(rp).propagate_relevant_or(m_parent);
}
void ite_relevancy_eh::operator()(relevancy_propagator & rp) {
if (rp.is_relevant(m_parent)) {
static_cast<relevancy_propagator_imp&>(rp).propagate_relevant_ite(m_parent);
}
}
void ite_term_relevancy_eh::operator()(relevancy_propagator & rp) {
if (!rp.is_relevant(m_parent))
return;
rp.mark_as_relevant(m_parent->get_arg(0));
switch (rp.get_context().get_assignment(m_parent->get_arg(0))) {
case l_false:
TRACE("ite_term_relevancy", tout << "marking else: #" << m_else_eq->get_id() << "\n";);
rp.mark_as_relevant(m_else_eq);
break;
case l_undef:
break;
case l_true:
TRACE("ite_term_relevancy", tout << "marking then: #" << m_then_eq->get_id() << "\n";);
rp.mark_as_relevant(m_then_eq);
break;
}
}
relevancy_propagator * mk_relevancy_propagator(context & ctx) { return alloc(relevancy_propagator_imp, ctx); }
};

204
src/smt/smt_relevancy.h Normal file
View file

@ -0,0 +1,204 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_relevancy.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-04.
Revision History:
--*/
#ifndef _SMT_RELEVANCY_H_
#define _SMT_RELEVANCY_H_
#include"ast.h"
namespace smt {
class context;
class relevancy_propagator;
class relevancy_eh {
protected:
void mark_as_relevant(relevancy_propagator & rp, expr * n);
void mark_args_as_relevant(relevancy_propagator & rp, app * n);
public:
relevancy_eh() {}
virtual ~relevancy_eh() {}
/**
\brief This method is invoked when n is marked as relevant.
*/
virtual void operator()(relevancy_propagator & rp, expr * n) { operator()(rp); }
/**
\brief This method is invoked when atom is assigned to val.
*/
virtual void operator()(relevancy_propagator & rp, expr * atom, bool val) { operator()(rp); }
/**
\brief Fallback for the two previous methods.
*/
virtual void operator()(relevancy_propagator & rp) {}
};
class simple_relevancy_eh : public relevancy_eh {
expr * m_target;
public:
simple_relevancy_eh(expr * t):m_target(t) {}
virtual ~simple_relevancy_eh() {}
virtual void operator()(relevancy_propagator & rp);
};
/**
\brief Propagate relevancy to m_target if both m_source1 and m_source2 are relevant.
*/
class pair_relevancy_eh : public relevancy_eh {
expr * m_source1;
expr * m_source2;
expr * m_target;
public:
pair_relevancy_eh(expr * s1, expr * s2, expr * t):m_source1(s1), m_source2(s2), m_target(t) {}
virtual ~pair_relevancy_eh() {}
virtual void operator()(relevancy_propagator & rp);
};
/**
\brief Relevancy propagator.
The relevancy propagation constraints are specified to the
relevancy propagator using the methods:
- add_handler
- add_watch
This class also provides helper methods for specifying commonly used constraints.
It uses the following API from smt::context
- find_assignment(expr * n)
- find_enode(expr * n)
It notifies smt::context that an expression became relevant by invoking
- relevant_eh(expr * n)
smt::context notifies the relevancy_propagator that a literal was assigned by
invoking assign_eh(n, bool val)
*/
class relevancy_propagator {
protected:
context & m_context;
public:
relevancy_propagator(context & ctx);
virtual ~relevancy_propagator() {}
context & get_context() { return m_context; }
/**
\brief Install an event handler that is invoked whenever n is marked as relevant.
*/
virtual void add_handler(expr * n, relevancy_eh * eh) = 0;
/**
\brief Install an event handler that is invoked whenever n is assigned to the given value.
The relevancy propagator is notified of new assignments by the method assign_eh.
*/
virtual void add_watch(expr * n, bool val, relevancy_eh * eh) = 0;
/**
\brief Install an event handler that just marks target as relevant whenever n is assigned to the given value.
The relevancy propagator is notified of new assignments by the method assign_eh.
*/
virtual void add_watch(expr * n, bool val, expr * target) = 0;
/**
\brief smt::context invokes this method whenever the expression is assigned to true/false
*/
virtual void assign_eh(expr * n, bool val) = 0;
/**
\brief Mark the given expression is relevant.
*/
virtual void mark_as_relevant(expr * n) = 0;
/**
\brief Return true if the given expression is marked as relevant.
*/
virtual bool is_relevant(expr * n) const = 0;
/**
\brief Propagate relevancy using the event handlers
specified by add_handler and add_watch, and the structure
of the expressions already marked as relevant.
*/
virtual void propagate() = 0;
/**
\brief Return true if it can propagate relevancy.
*/
virtual bool can_propagate() const = 0;
/**
\brief Create a backtracking point
*/
virtual void push() = 0;
/**
\brief Backtrack.
*/
virtual void pop(unsigned num_scopes) = 0;
/**
\brief Display relevant expressions.
*/
virtual void display(std::ostream & out) const = 0;
#ifdef Z3DEBUG
virtual bool check_relevancy(expr_ref_vector const & v) const = 0;
virtual bool check_relevancy_or(app * n, bool root) const = 0;
#endif
// --------------------------
//
// Helper method
//
// --------------------------
/**
\brief Return true if relevancy propagation is enabled.
*/
bool enabled() const;
/**
\Brief Return the region allocator for the smt::context that owns this propagator.
*/
region & get_region() const;
/**
\Brief Return the ast_manager for the smt::context that owns this propagator.
*/
ast_manager & get_manager() const;
template<typename Eh>
relevancy_eh * mk_relevancy_eh(Eh const & eh) { return new (get_region()) Eh(eh); }
/**
\brief Creates an event handler that marks target as relevant whenever src is marked
as relevant.
*/
void add_dependency(expr * src, expr * target);
relevancy_eh * mk_or_relevancy_eh(app * n);
relevancy_eh * mk_and_relevancy_eh(app * n);
relevancy_eh * mk_ite_relevancy_eh(app * n);
relevancy_eh * mk_term_ite_relevancy_eh(app * c, app * t, app * e);
};
relevancy_propagator * mk_relevancy_propagator(context & ctx);
};
#endif /* _SMT_RELEVANCY_H_ */

901
src/smt/smt_setup.cpp Normal file
View file

@ -0,0 +1,901 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_setup.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-24.
Revision History:
--*/
#include"smt_context.h"
#include"smt_setup.h"
#include"static_features.h"
#include"theory_arith.h"
#include"theory_dense_diff_logic.h"
#include"theory_diff_logic.h"
#include"theory_array.h"
#include"theory_array_full.h"
#include"theory_bv.h"
#include"theory_datatype.h"
#include"theory_dummy.h"
#include"theory_dl.h"
#include"theory_instgen.h"
#include"theory_seq_empty.h"
namespace smt {
setup::setup(context & c, front_end_params & params):
m_context(c),
m_manager(c.get_manager()),
m_params(params),
m_already_configured(false) {
}
void setup::operator()(config_mode cm) {
SASSERT(m_context.get_scope_level() == 0);
SASSERT(!m_context.already_internalized());
SASSERT(!m_already_configured);
// if (m_params.m_mbqi && m_params.m_model_compact) {
// warning_msg("ignoring MODEL_COMPACT=true because it cannot be used with MBQI=true");
// m_params.m_model_compact = false;
// }
TRACE("setup", tout << "configuring logical context, logic: " << m_logic << "\n";);
m_already_configured = true;
switch (cm) {
case CFG_BASIC: setup_unknown(); break;
case CFG_LOGIC: setup_default(); break;
case CFG_AUTO: setup_auto_config(); break;
}
TRACE("setup", ini_params p; m_params.register_params(p); p.display_params(tout););
}
void setup::setup_default() {
if (m_logic == "QF_UF")
setup_QF_UF();
else if (m_logic == "QF_RDL")
setup_QF_RDL();
else if (m_logic == "QF_IDL")
setup_QF_IDL();
else if (m_logic == "QF_UFIDL")
setup_QF_UFIDL();
else if (m_logic == "QF_LRA")
setup_QF_LRA();
else if (m_logic == "QF_LIA")
setup_QF_LIA();
else if (m_logic == "QF_UFLIA")
setup_QF_UFLIA();
else if (m_logic == "QF_UFLRA")
setup_QF_UFLRA();
else if (m_logic == "QF_AX")
setup_QF_AX();
else if (m_logic == "QF_AUFLIA")
setup_QF_AUFLIA();
else if (m_logic == "QF_BV")
setup_QF_BV();
else if (m_logic == "QF_AUFBV")
setup_QF_AUFBV();
else if (m_logic == "QF_ABV")
setup_QF_AUFBV();
else if (m_logic == "QF_UFBV")
setup_QF_AUFBV();
else if (m_logic == "QF_BVRE")
setup_QF_BVRE();
else if (m_logic == "AUFLIA")
setup_AUFLIA();
else if (m_logic == "AUFLIRA")
setup_AUFLIRA();
else if (m_logic == "AUFNIRA")
setup_AUFNIRA();
else if (m_logic == "AUFLIA+")
setup_AUFLIA();
else if (m_logic == "AUFLIA-")
setup_AUFLIA();
else if (m_logic == "AUFLIRA+")
setup_AUFLIRA();
else if (m_logic == "AUFLIRA-")
setup_AUFLIRA();
else if (m_logic == "AUFNIRA+")
setup_AUFLIRA();
else if (m_logic == "AUFNIRA-")
setup_AUFLIRA();
else if (m_logic == "UFNIA")
setup_UFNIA();
else if (m_logic == "UFLRA")
setup_UFLRA();
else if (m_logic == "LRA")
setup_LRA();
else
setup_unknown();
}
void setup::setup_auto_config() {
static_features st(m_manager);
IF_VERBOSE(100, verbose_stream() << "configuring...\n";);
TRACE("setup", tout << "setup, logic: " << m_logic << "\n";);
// HACK: do not collect features for QF_BV and QF_AUFBV... since they do not use them...
if (m_logic == "QF_BV") {
setup_QF_BV();
}
else if (m_logic == "QF_AUFBV" || m_logic == "QF_ABV" || m_logic == "QF_UFBV") {
setup_QF_AUFBV();
}
else {
IF_VERBOSE(100, verbose_stream() << "collecting features...\n";);
st.collect(m_context.get_num_asserted_formulas(), m_context.get_asserted_formulas());
IF_VERBOSE(1000, st.display_primitive(verbose_stream()););
if (m_logic == "QF_UF")
setup_QF_UF(st);
else if (m_logic == "QF_RDL")
setup_QF_RDL(st);
else if (m_logic == "QF_IDL")
setup_QF_IDL(st);
else if (m_logic == "QF_UFIDL")
setup_QF_UFIDL(st);
else if (m_logic == "QF_LRA")
setup_QF_LRA(st);
else if (m_logic == "QF_LIA")
setup_QF_LIA(st);
else if (m_logic == "QF_UFLIA")
setup_QF_UFLIA(st);
else if (m_logic == "QF_UFLRA")
setup_QF_UFLRA();
else if (m_logic == "QF_AX")
setup_QF_AX(st);
else if (m_logic == "QF_BVRE")
setup_QF_BVRE();
else if (m_logic == "QF_AUFLIA")
setup_QF_AUFLIA(st);
else if (m_logic == "AUFLIA")
setup_AUFLIA(st);
else if (m_logic == "AUFLIRA")
setup_AUFLIRA();
else if (m_logic == "AUFNIRA")
setup_AUFNIRA();
else if (m_logic == "AUFLIA+")
setup_AUFLIA();
else if (m_logic == "AUFLIA-")
setup_AUFLIA();
else if (m_logic == "AUFLIRA+")
setup_AUFLIRA();
else if (m_logic == "AUFLIRA-")
setup_AUFLIRA();
else if (m_logic == "AUFNIRA+")
setup_AUFLIRA();
else if (m_logic == "AUFNIRA-")
setup_AUFLIRA();
else if (m_logic == "UFNIA")
setup_UFNIA();
else if (m_logic == "LRA")
setup_LRA();
else
setup_unknown(st);
}
}
void check_no_arithmetic(static_features const & st, char const * logic) {
if (st.m_num_arith_ineqs > 0 || st.m_num_arith_terms > 0 || st.m_num_arith_eqs > 0)
throw default_exception("Benchmark constains arithmetic, but specified loging does not support it.");
}
void setup::setup_QF_UF() {
m_params.m_relevancy_lvl = 0;
m_params.m_solver = true;
m_params.m_nnf_cnf = false;
}
void setup::setup_QF_BVRE() {
setup_QF_BV();
setup_QF_LIA();
m_context.register_plugin(alloc(smt::theory_seq_empty, m_manager));
}
void setup::setup_QF_UF(static_features const & st) {
check_no_arithmetic(st, "QF_UF");
m_params.m_relevancy_lvl = 0;
m_params.m_solver = true;
m_params.m_nnf_cnf = false;
m_params.m_restart_strategy = RS_LUBY;
m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2;
m_params.m_random_initial_activity = IA_RANDOM;
TRACE("setup",
tout << "st.m_num_theories: " << st.m_num_theories << "\n";
tout << "st.m_num_uninterpreted_functions: " << st.m_num_uninterpreted_functions << "\n";);
}
void setup::setup_QF_RDL() {
m_params.m_relevancy_lvl = 0;
m_params.m_arith_expand_eqs = true;
m_params.m_arith_reflect = false;
m_params.m_solver = true;
m_params.m_arith_propagate_eqs = false;
m_params.m_nnf_cnf = false;
setup_mi_arith();
}
static bool is_dense(static_features const & st) {
return
st.m_num_uninterpreted_constants < 1000 &&
(st.m_num_arith_eqs + st.m_num_arith_ineqs) > st.m_num_uninterpreted_constants * 9;
}
bool is_in_diff_logic(static_features const & st) {
return
st.m_num_arith_eqs == st.m_num_diff_eqs &&
st.m_num_arith_terms == st.m_num_diff_terms &&
st.m_num_arith_ineqs == st.m_num_diff_ineqs;
}
bool is_diff_logic(static_features const & st) {
return
is_in_diff_logic(st) &&
(st.m_num_diff_ineqs > 0 || st.m_num_diff_eqs > 0 || st.m_num_diff_terms > 0)
;
}
void check_no_uninterpreted_functions(static_features const & st, char const * logic) {
if (st.m_num_uninterpreted_functions != 0)
throw default_exception("Benchmark contains uninterpreted function symbols, but specified logic does not support them.");
}
void setup::setup_QF_RDL(static_features & st) {
if (!is_in_diff_logic(st))
throw default_exception("Benchmark is not in QF_RDL (real difference logic).");
if (st.m_has_int)
throw default_exception("Benchmark has integer variables but it is marked as QF_RDL (real difference logic).");
TRACE("setup", tout << "setup_QF_RDL(st)\n";);
check_no_uninterpreted_functions(st, "QF_RDL");
m_params.m_relevancy_lvl = 0;
m_params.m_arith_expand_eqs = true;
m_params.m_arith_reflect = false;
m_params.m_solver = true;
m_params.m_arith_propagate_eqs = false;
m_params.m_nnf_cnf = false;
if (is_dense(st)) {
m_params.m_restart_strategy = RS_GEOMETRIC;
m_params.m_restart_adaptive = false;
m_params.m_phase_selection = PS_CACHING;
}
// The smi theories use fixed size integers instead of rationals.
// They have support for epsilons for modeling strict inequalities, but they
// cannot handle rational numbers.
// It is only safe to use them when the input does not contain rational numbers.
// Moreover, if model construction is enabled, then rational numbers may be needed
// to compute the actual value of epsilon even if the input does not have rational numbers.
// Example: (x < 1) and (x > 0)
if (m_params.m_proof_mode != PGM_DISABLED) {
m_context.register_plugin(alloc(smt::theory_mi_arith_w_proofs, m_manager, m_params));
}
else if (!m_params.m_arith_auto_config_simplex && is_dense(st)) {
if (!st.m_has_rational && !m_params.m_model && st.m_arith_k_sum < rational(INT_MAX / 8))
m_context.register_plugin(alloc(smt::theory_dense_smi, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params));
}
else {
if (m_params.m_arith_auto_config_simplex || st.m_num_uninterpreted_constants > 4 * st.m_num_bool_constants) {
if (!st.m_has_rational && !m_params.m_model && st.m_arith_k_sum < rational(INT_MAX / 8)) {
TRACE("rdl_bug", tout << "using theory_smi_arith\n";);
m_context.register_plugin(alloc(smt::theory_smi_arith, m_manager, m_params));
}
else {
TRACE("rdl_bug", tout << "using theory_mi_arith\n";);
m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params));
}
}
else {
m_params.m_arith_bound_prop = BP_NONE;
m_params.m_arith_propagation_strategy = ARITH_PROP_AGILITY;
m_params.m_arith_add_binary_bounds = true;
if (!st.m_has_rational && !m_params.m_model && st.m_arith_k_sum < rational(INT_MAX / 8))
m_context.register_plugin(alloc(smt::theory_frdl, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_rdl, m_manager, m_params));
}
}
}
void setup::setup_QF_IDL() {
TRACE("setup", tout << "setup_QF_IDL(st)\n";);
m_params.m_relevancy_lvl = 0;
m_params.m_arith_expand_eqs = true;
m_params.m_arith_reflect = false;
m_params.m_solver = true;
m_params.m_arith_propagate_eqs = false;
m_params.m_arith_small_lemma_size = 30;
m_params.m_nnf_cnf = false;
setup_i_arith();
}
void setup::setup_QF_IDL(static_features & st) {
if (!is_in_diff_logic(st))
throw default_exception("Benchmark is not in QF_IDL (integer difference logic).");
if (st.m_has_real)
throw default_exception("Benchmark has real variables but it is marked as QF_IDL (integer difference logic).");
TRACE("setup", tout << "setup QF_IDL, m_arith_k_sum: " << st.m_arith_k_sum << " m_num_diff_terms: " << st.m_num_arith_terms << "\n";
st.display_primitive(tout););
TRACE("setup", tout << "setup_QF_IDL(st)\n";);
check_no_uninterpreted_functions(st, "QF_IDL");
m_params.m_relevancy_lvl = 0;
m_params.m_arith_expand_eqs = true;
m_params.m_arith_reflect = false;
m_params.m_solver = true;
m_params.m_arith_propagate_eqs = false;
m_params.m_arith_small_lemma_size = 30;
m_params.m_nnf_cnf = false;
if (st.m_num_uninterpreted_constants > 5000)
m_params.m_relevancy_lvl = 2;
else if (st.m_cnf && !is_dense(st))
m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2;
else
m_params.m_phase_selection = PS_CACHING;
if (is_dense(st) && st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses) {
m_params.m_restart_adaptive = false;
m_params.m_restart_strategy = RS_GEOMETRIC;
}
if (st.m_cnf && st.m_num_units == st.m_num_clauses) {
// the problem is just a big conjunction... using randomization to deal with crafted benchmarks
m_params.m_random_initial_activity = IA_RANDOM;
}
TRACE("setup",
tout << "RELEVANCY: " << m_params.m_relevancy_lvl << "\n";
tout << "ARITH_EQ_BOUNDS: " << m_params.m_arith_eq_bounds << "\n";);
if (m_params.m_proof_mode != PGM_DISABLED) {
m_context.register_plugin(alloc(smt::theory_mi_arith_w_proofs, m_manager, m_params));
}
else if (!m_params.m_arith_auto_config_simplex && is_dense(st)) {
TRACE("setup", tout << "using dense diff logic...\n";);
m_params.m_phase_selection = PS_CACHING_CONSERVATIVE;
if (st.m_arith_k_sum < rational(INT_MAX / 8))
m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params));
}
else {
if (st.m_arith_k_sum < rational(INT_MAX / 8)) {
TRACE("setup", tout << "using small integer simplex...\n";);
m_context.register_plugin(alloc(smt::theory_si_arith, m_manager, m_params));
}
else {
TRACE("setup", tout << "using big integer simplex...\n";);
m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params));
}
}
}
void setup::setup_QF_UFIDL() {
TRACE("setup", tout << "setup_QF_UFIDL()\n";);
m_params.m_relevancy_lvl = 0;
m_params.m_arith_reflect = false;
m_params.m_solver = true;
m_params.m_nnf_cnf = false;
m_params.m_arith_eq_bounds = true;
m_params.m_phase_selection = PS_ALWAYS_FALSE;
m_params.m_restart_strategy = RS_GEOMETRIC;
m_params.m_restart_factor = 1.5;
m_params.m_restart_adaptive = false;
setup_i_arith();
}
void setup::setup_QF_UFIDL(static_features & st) {
TRACE("setup", tout << "setup_QF_UFIDL(st)\n";);
if (st.m_has_real)
throw default_exception("Benchmark has real variables but it is marked as QF_UFIDL (uninterpreted functions and difference logic).");
m_params.m_relevancy_lvl = 0;
m_params.m_arith_reflect = false;
m_params.m_solver = true;
m_params.m_nnf_cnf = false;
if (st.m_num_uninterpreted_functions == 0) {
m_params.m_arith_expand_eqs = true;
m_params.m_arith_propagate_eqs = false;
if (is_dense(st)) {
m_params.m_arith_small_lemma_size = 128;
m_params.m_lemma_gc_half = true;
m_params.m_restart_strategy = RS_GEOMETRIC;
if (m_params.m_proof_mode != PGM_DISABLED) {
m_context.register_plugin(alloc(smt::theory_mi_arith_w_proofs, m_manager, m_params));
}
else if (st.m_arith_k_sum < rational(INT_MAX / 8))
m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params));
return;
}
}
m_params.m_arith_eq_bounds = true;
m_params.m_phase_selection = PS_ALWAYS_FALSE;
m_params.m_restart_strategy = RS_GEOMETRIC;
m_params.m_restart_factor = 1.5;
m_params.m_restart_adaptive = false;
if (m_params.m_proof_mode != PGM_DISABLED) {
m_context.register_plugin(alloc(smt::theory_mi_arith_w_proofs, m_manager, m_params));
}
else if (st.m_arith_k_sum < rational(INT_MAX / 8))
m_context.register_plugin(alloc(smt::theory_si_arith, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params));
}
void setup::setup_QF_LRA() {
TRACE("setup", tout << "setup_QF_LRA(st)\n";);
m_params.m_relevancy_lvl = 0;
m_params.m_arith_expand_eqs = true;
m_params.m_arith_reflect = false;
m_params.m_arith_propagate_eqs = false;
m_params.m_eliminate_term_ite = true;
m_params.m_solver = true;
m_params.m_nnf_cnf = false;
setup_mi_arith();
}
void setup::setup_QF_LRA(static_features const & st) {
check_no_uninterpreted_functions(st, "QF_LRA");
m_params.m_relevancy_lvl = 0;
m_params.m_arith_expand_eqs = true;
m_params.m_arith_reflect = false;
m_params.m_arith_propagate_eqs = false;
m_params.m_eliminate_term_ite = true;
m_params.m_solver = true;
m_params.m_nnf_cnf = false;
if (numerator(st.m_arith_k_sum) > rational(2000000) && denominator(st.m_arith_k_sum) > rational(500)) {
m_params.m_relevancy_lvl = 2;
m_params.m_relevancy_lemma = false;
}
if (st.m_cnf) {
m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2;
}
else {
m_params.m_restart_strategy = RS_GEOMETRIC;
m_params.m_arith_stronger_lemmas = false;
m_params.m_phase_selection = PS_ALWAYS_FALSE;
m_params.m_restart_adaptive = false;
}
m_params.m_arith_small_lemma_size = 32;
setup_mi_arith();
}
void setup::setup_QF_LIA() {
TRACE("setup", tout << "setup_QF_LIA(st)\n";);
m_params.m_relevancy_lvl = 0;
m_params.m_arith_expand_eqs = true;
m_params.m_arith_reflect = false;
m_params.m_arith_propagate_eqs = false;
m_params.m_nnf_cnf = false;
setup_i_arith();
}
void setup::setup_QF_LIA(static_features const & st) {
check_no_uninterpreted_functions(st, "QF_LIA");
TRACE("setup", tout << "QF_LIA setup\n";);
m_params.m_relevancy_lvl = 0;
m_params.m_arith_expand_eqs = true;
m_params.m_arith_reflect = false;
m_params.m_arith_propagate_eqs = false;
m_params.m_nnf_cnf = false;
if (st.m_max_ite_tree_depth > 50) {
m_params.m_arith_expand_eqs = false;
m_params.m_pull_cheap_ite_trees = true;
m_params.m_arith_propagate_eqs = true;
m_params.m_relevancy_lvl = 2;
m_params.m_relevancy_lemma = false;
}
else if (st.m_num_clauses == st.m_num_units) {
m_params.m_arith_gcd_test = false;
m_params.m_arith_branch_cut_ratio = 4;
m_params.m_relevancy_lvl = 2;
m_params.m_arith_expand_eqs = true;
m_params.m_eliminate_term_ite = true;
// if (st.m_num_exprs < 5000 && st.m_num_ite_terms < 50) { // safeguard to avoid high memory consumption
// TODO: implement analsysis function to decide where lift ite is too expensive.
// m_params.m_lift_ite = LI_FULL;
// }
}
else {
m_params.m_eliminate_term_ite = true;
m_params.m_phase_selection = PS_CACHING;
m_params.m_restart_adaptive = false;
m_params.m_restart_strategy = RS_GEOMETRIC;
m_params.m_restart_factor = 1.5;
}
if (st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses && st.m_cnf && st.m_arith_k_sum > rational(100000)) {
m_params.m_arith_bound_prop = BP_NONE;
m_params.m_arith_stronger_lemmas = false;
}
setup_i_arith();
}
void setup::setup_QF_UFLIA() {
m_params.m_relevancy_lvl = 0;
m_params.m_arith_reflect = false;
m_params.m_solver = true;
m_params.m_nnf_cnf = false;
m_params.m_arith_propagation_threshold = 1000;
setup_i_arith();
}
void setup::setup_QF_UFLIA(static_features & st) {
if (st.m_has_real)
throw default_exception("Benchmark has real variables but it is marked as QF_UFLIA (uninterpreted functions and linear integer arithmetic).");
setup_QF_UFLIA();
}
void setup::setup_QF_UFLRA() {
m_params.m_relevancy_lvl = 0;
m_params.m_arith_reflect = false;
m_params.m_solver = true;
m_params.m_nnf_cnf = false;
setup_mi_arith();
}
void setup::setup_QF_BV() {
m_params.m_relevancy_lvl = 0;
m_params.m_arith_reflect = false;
m_params.m_solver = true;
m_params.m_bv_cc = false;
m_params.m_bb_ext_gates = true;
m_params.m_nnf_cnf = false;
m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params));
}
void setup::setup_QF_AUFBV() {
m_params.m_array_mode = AR_SIMPLE;
m_params.m_relevancy_lvl = 0;
m_params.m_solver = true;
m_params.m_bv_cc = false;
m_params.m_bb_ext_gates = true;
m_params.m_nnf_cnf = false;
m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params));
m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params));
}
void setup::setup_QF_AX() {
m_params.m_array_mode = AR_SIMPLE;
m_params.m_nnf_cnf = false;
m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params));
}
void setup::setup_QF_AX(static_features const & st) {
m_params.m_array_mode = AR_SIMPLE;
m_params.m_nnf_cnf = false;
if (st.m_num_clauses == st.m_num_units) {
m_params.m_relevancy_lvl = 0;
m_params.m_phase_selection = PS_ALWAYS_FALSE;
}
else {
m_params.m_relevancy_lvl = 2;
m_params.m_solver = true;
}
m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params));
}
void setup::setup_QF_AUFLIA() {
TRACE("QF_AUFLIA", tout << "no static features\n";);
m_params.m_array_mode = AR_SIMPLE;
m_params.m_nnf_cnf = false;
m_params.m_relevancy_lvl = 2;
m_params.m_restart_strategy = RS_GEOMETRIC;
m_params.m_restart_factor = 1.5;
m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2;
setup_i_arith();
m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params));
}
void setup::setup_QF_AUFLIA(static_features const & st) {
m_params.m_array_mode = AR_SIMPLE;
if (st.m_has_real)
throw default_exception("Benchmark has real variables but it is marked as QF_AUFLIA (arrays, uninterpreted functions and linear integer arithmetic).");
m_params.m_nnf_cnf = false;
if (st.m_num_clauses == st.m_num_units) {
TRACE("QF_AUFLIA", tout << "using relevancy: 0\n";);
m_params.m_relevancy_lvl = 0;
m_params.m_phase_selection = PS_ALWAYS_FALSE;
}
else {
m_params.m_relevancy_lvl = 0; // it was 2, for some reason 2 doesn't work anymore TODO: investigate
m_params.m_restart_strategy = RS_GEOMETRIC;
m_params.m_restart_factor = 1.5;
m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2;
m_params.m_random_initial_activity = IA_ZERO;
}
// m_params.m_solver = true;
// if (st.m_num_arith_ineqs == st.m_num_diff_ineqs && st.m_num_arith_eqs == st.m_num_diff_eqs && st.m_arith_k_sum < rational(INT_MAX / 8))
// m_context.register_plugin(new smt::theory_si_arith(m_manager, m_params));
// else
setup_i_arith();
m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params));
}
void setup::setup_AUFLIA(bool simple_array) {
TRACE("setup", tout << "AUFLIA\n";);
m_params.m_array_mode = simple_array ? AR_SIMPLE : AR_FULL;
m_params.m_pi_use_database = true;
m_params.m_phase_selection = PS_ALWAYS_FALSE;
m_params.m_restart_strategy = RS_GEOMETRIC;
m_params.m_restart_factor = 1.5;
m_params.m_eliminate_bounds = true;
m_params.m_qi_quick_checker = MC_UNSAT;
m_params.m_propagate_booleans = true;
m_params.m_qi_lazy_threshold = 20;
// m_params.m_qi_max_eager_multipatterns = 10; /// <<< HACK
m_params.m_mbqi = true; // enabling MBQI and MACRO_FINDER by default :-)
// MACRO_FINDER is a horrible for AUFLIA and UFNIA benchmarks (boogie benchmarks in general)
// It destroys the existing patterns.
// m_params.m_macro_finder = true;
//
m_params.m_ng_lift_ite = LI_FULL;
TRACE("setup", tout << "max_eager_multipatterns: " << m_params.m_qi_max_eager_multipatterns << "\n";);
setup_i_arith();
setup_arrays();
}
void setup::setup_AUFLIA(static_features const & st) {
if (st.m_has_real)
throw default_exception("Benchmark has real variables but it is marked as AUFLIA (arrays, uninterpreted functions and linear integer arithmetic).");
m_params.m_qi_eager_threshold = st.m_num_quantifiers_with_patterns == 0 ? 5 : 7;
setup_AUFLIA();
}
void setup::setup_AUFLIRA(bool simple_array) {
m_params.m_array_mode = simple_array ? AR_SIMPLE : AR_FULL;
m_params.m_phase_selection = PS_ALWAYS_FALSE;
m_params.m_eliminate_bounds = true;
m_params.m_qi_quick_checker = MC_UNSAT;
m_params.m_propagate_booleans = true;
m_params.m_qi_eager_threshold = 5;
// Added for MBQI release
m_params.m_qi_lazy_threshold = 20;
//
m_params.m_macro_finder = true;
m_params.m_ng_lift_ite = LI_FULL;
m_params.m_pi_max_multi_patterns = 10; //<< it was used for SMT-COMP
m_params.m_array_lazy_ieq = true;
m_params.m_array_lazy_ieq_delay = 4;
//
m_params.m_mbqi = true; // enabling MBQI by default :-)
//
setup_mi_arith();
setup_arrays();
}
void setup::setup_UFNIA() {
setup_AUFLIA();
}
void setup::setup_UFLRA() {
setup_AUFLIRA();
}
void setup::setup_AUFLIAp() {
setup_AUFLIA();
}
void setup::setup_AUFNIRA() {
setup_AUFLIRA();
}
void setup::setup_LRA() {
m_params.m_quant_elim = true;
// after quantifier elimination, the result is a QF_LRA benchmark
m_params.m_relevancy_lvl = 0;
// m_params.m_arith_expand_eqs = true; << may affect quant_elim
m_params.m_arith_reflect = false;
m_params.m_arith_propagate_eqs = false;
m_params.m_eliminate_term_ite = true;
m_params.m_solver = true;
setup_mi_arith();
}
bool is_arith(static_features const & st) {
return st.m_num_arith_ineqs > 0 || st.m_num_arith_terms > 0 || st.m_num_arith_eqs > 0;
}
void setup::setup_i_arith() {
if (m_params.m_proof_mode != PGM_DISABLED) {
m_context.register_plugin(alloc(smt::theory_mi_arith_w_proofs, m_manager, m_params));
}
else {
m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params));
}
}
void setup::setup_mi_arith() {
if (m_params.m_proof_mode != PGM_DISABLED) {
m_context.register_plugin(alloc(smt::theory_mi_arith_w_proofs, m_manager, m_params));
}
else {
m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params));
}
}
void setup::setup_arith() {
switch(m_params.m_arith_mode) {
case AS_NO_ARITH:
m_context.register_plugin(alloc(smt::theory_dummy, m_manager.get_family_id("arith"), "no arithmetic"));
break;
case AS_DIFF_LOGIC:
if (m_params.m_arith_fixnum) {
if (m_params.m_arith_int_only)
m_context.register_plugin(alloc(smt::theory_fidl, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_frdl, m_manager, m_params));
}
else {
if (m_params.m_arith_int_only)
m_context.register_plugin(alloc(smt::theory_idl, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_rdl, m_manager, m_params));
}
break;
case AS_DENSE_DIFF_LOGIC:
if (m_params.m_arith_fixnum) {
if (m_params.m_arith_int_only)
m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_dense_smi, m_manager, m_params));
}
else {
if (m_params.m_arith_int_only)
m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params));
}
break;
default:
if (m_params.m_proof_mode != PGM_DISABLED) {
m_context.register_plugin(alloc(smt::theory_mi_arith_w_proofs, m_manager, m_params));
}
else if (m_params.m_arith_fixnum) {
if (m_params.m_arith_int_only)
m_context.register_plugin(alloc(smt::theory_si_arith, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_smi_arith, m_manager, m_params));
}
else {
if (m_params.m_arith_int_only)
m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params));
else
m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params));
}
break;
}
}
void setup::setup_bv() {
switch(m_params.m_bv_mode) {
case BS_NO_BV:
m_context.register_plugin(alloc(smt::theory_dummy, m_manager.get_family_id("bv"), "no bit-vector"));
break;
case BS_BLASTER:
m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params));
break;
}
}
void setup::setup_arrays() {
switch(m_params.m_array_mode) {
case AR_NO_ARRAY:
m_context.register_plugin(alloc(smt::theory_dummy, m_manager.get_family_id("array"), "no array"));
break;
case AR_SIMPLE:
m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params));
break;
case AR_MODEL_BASED:
throw default_exception("The model-based array theory solver is deprecated");
break;
case AR_FULL:
m_context.register_plugin(alloc(smt::theory_array_full, m_manager, m_params));
break;
}
}
void setup::setup_datatypes() {
TRACE("datatype", tout << "registering theory datatype...\n";);
m_context.register_plugin(alloc(theory_datatype, m_manager, m_params));
}
void setup::setup_dl() {
m_context.register_plugin(mk_theory_dl(m_manager));
}
void setup::setup_seq() {
m_context.register_plugin(alloc(theory_seq_empty, m_manager));
}
void setup::setup_instgen() {
if (m_params.m_instgen) {
m_context.register_plugin(mk_theory_instgen(m_manager, m_params));
}
}
void setup::setup_unknown() {
setup_arith();
setup_arrays();
setup_bv();
setup_datatypes();
setup_dl();
setup_instgen();
setup_seq();
}
void setup::setup_unknown(static_features & st) {
TRACE("setup", tout << "setup_unknown\n";);
if (st.m_num_quantifiers > 0) {
if (st.m_has_real)
setup_AUFLIRA(false);
else
setup_AUFLIA(false);
setup_datatypes();
setup_bv();
return;
}
TRACE("setup",
tout << "num non UF theories: " << st.num_non_uf_theories() << "\n";
tout << "num theories: " << st.num_theories() << "\n";
tout << "is_diff_logic: " << is_diff_logic(st) << "\n";
tout << "is_arith: " << is_arith(st) << "\n";
tout << "has UF: " << st.has_uf() << "\n";
tout << "has real: " << st.m_has_real << "\n";
tout << "has int: " << st.m_has_int << "\n";);
if (st.num_non_uf_theories() == 0) {
setup_QF_UF(st);
return;
}
if (st.num_theories() == 1 && is_diff_logic(st)) {
if (st.m_has_real && !st.m_has_int)
setup_QF_RDL(st);
else if (!st.m_has_real && st.m_has_int)
setup_QF_IDL(st);
else
setup_unknown();
return;
}
if (st.num_theories() == 2 && st.has_uf() && is_diff_logic(st)) {
if (!st.m_has_real && st.m_has_int)
setup_QF_UFIDL(st);
else
setup_unknown();
return;
}
if (st.num_theories() == 1 && is_arith(st)) {
if (st.m_has_real)
setup_QF_LRA(st);
else
setup_QF_LIA(st);
return;
}
if (st.num_theories() == 2 && st.has_uf() && is_arith(st)) {
if (!st.m_has_real)
setup_QF_UFLIA(st);
else if (!st.m_has_int)
setup_QF_UFLRA();
else
setup_unknown();
return;
}
// TODO QF_BV, QF_AUFBV, QF_AUFLIA
setup_unknown();
}
};

114
src/smt/smt_setup.h Normal file
View file

@ -0,0 +1,114 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_setup.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-24.
Revision History:
--*/
#ifndef _SMT_SETUP_H_
#define _SMT_SETUP_H_
#include"ast.h"
#include"front_end_params.h"
struct static_features;
namespace smt {
enum config_mode {
CFG_BASIC, // install theories based on user options
CFG_LOGIC, // install theories and configure Z3 based on the value of the parameter set-logic.
CFG_AUTO, // install theories based on static features of the input formula
};
class context;
/**
\brief Object used to setup a logical context.
\warning In the current version, we can only setup a logical context at scope level 0,
and before internalizing any formula. Auxiliary temporary contexts are used to avoid this
limitation.
*/
class setup {
context & m_context;
ast_manager & m_manager;
front_end_params & m_params;
symbol m_logic;
bool m_already_configured;
void setup_auto_config();
void setup_default();
//
// setup_<logic>() methods do not depend on static features of the formula. So, they are safe to use
// even in an incremental setting.
//
// setup_<logic>(static_features & st) can only be used if the logical context will perform a single
// check.
//
void setup_QF_UF();
void setup_QF_UF(static_features const & st);
void setup_QF_RDL();
void setup_QF_RDL(static_features & st);
void setup_QF_IDL();
void setup_QF_IDL(static_features & st);
void setup_QF_UFIDL();
void setup_QF_UFIDL(static_features & st);
void setup_QF_LRA();
void setup_QF_LRA(static_features const & st);
void setup_QF_LIA();
void setup_QF_LIA(static_features const & st);
void setup_QF_UFLIA();
void setup_QF_UFLIA(static_features & st);
void setup_QF_UFLRA();
void setup_QF_BV();
void setup_QF_AUFBV();
void setup_QF_AX();
void setup_QF_AX(static_features const & st);
void setup_QF_AUFLIA();
void setup_QF_AUFLIA(static_features const & st);
void setup_LRA();
void setup_AUFLIA(bool simple_array = true);
void setup_AUFLIA(static_features const & st);
void setup_AUFLIRA(bool simple_array = true);
void setup_UFNIA();
void setup_UFLRA();
void setup_AUFLIAp();
void setup_AUFNIRA();
void setup_QF_BVRE();
void setup_unknown();
void setup_unknown(static_features & st);
void setup_arrays();
void setup_datatypes();
void setup_bv();
void setup_arith();
void setup_dl();
void setup_seq();
void setup_instgen();
void setup_i_arith();
void setup_mi_arith();
public:
setup(context & c, front_end_params & params);
void mark_already_configured() { m_already_configured = true; }
bool already_configured() const { return m_already_configured; }
bool set_logic(symbol logic) {
if (already_configured())
return false;
m_logic = logic;
return true;
}
symbol const & get_logic() const { return m_logic; }
void operator()(config_mode cm);
};
};
#endif /* _SMT_SETUP_H_ */

View file

@ -20,6 +20,8 @@ Notes:
--*/
#include"smt_solver_strategy.h"
#include"smt_solver.h"
#include"front_end_params.h"
#include"params2front_end_params.h"
class as_st_solver : public assertion_set_strategy {
scoped_ptr<front_end_params> m_params;

View file

@ -0,0 +1,29 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_statistics.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-21.
Revision History:
--*/
#include<string.h>
#include"smt_statistics.h"
namespace smt {
void statistics::reset() {
memset(this, 0, sizeof(statistics));
}
};

60
src/smt/smt_statistics.h Normal file
View file

@ -0,0 +1,60 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_statistics.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-21.
Revision History:
--*/
#ifndef _SMT_STATISTICS_H_
#define _SMT_STATISTICS_H_
#include<iostream>
namespace smt {
struct statistics {
unsigned m_num_propagations;
unsigned m_num_bin_propagations;
unsigned m_num_conflicts;
unsigned m_num_sat_conflicts;
unsigned m_num_decisions;
unsigned m_num_add_eq;
unsigned m_num_restarts;
unsigned m_num_final_checks;
unsigned m_num_mk_bool_var;
unsigned m_num_del_bool_var;
unsigned m_num_mk_enode;
unsigned m_num_del_enode;
unsigned m_num_mk_clause;
unsigned m_num_del_clause;
unsigned m_num_mk_bin_clause;
unsigned m_num_mk_lits;
unsigned m_num_dyn_ack;
unsigned m_num_del_dyn_ack;
unsigned m_num_interface_eqs;
unsigned m_max_generation;
unsigned m_num_minimized_lits;
unsigned m_num_checks;
statistics() {
reset();
}
void reset();
};
};
#endif /* _SMT_STATISTICS_H_ */

139
src/smt/smt_theory.cpp Normal file
View file

@ -0,0 +1,139 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_theory.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-20.
Revision History:
--*/
#include"smt_context.h"
#include"buffer.h"
#include"ast_ll_pp.h"
namespace smt {
void theory::init(context * ctx) {
SASSERT(m_context == 0);
m_context = ctx;
m_manager = &(ctx->get_manager());
}
void theory::reset_eh() {
m_var2enode.reset();
}
void theory::push_scope_eh() {
SASSERT(m_context);
m_var2enode_lim.push_back(m_var2enode.size());
}
void theory::pop_scope_eh(unsigned num_scopes) {
SASSERT(m_context);
unsigned scope_lvl = m_var2enode_lim.size();
SASSERT(num_scopes <= scope_lvl);
unsigned new_lvl = scope_lvl - num_scopes;
unsigned old_sz = m_var2enode_lim[new_lvl];
m_var2enode.shrink(old_sz);
m_var2enode_lim.shrink(new_lvl);
}
void theory::display_var2enode(std::ostream & out) const {
unsigned sz = m_var2enode.size();
for (unsigned v = 0; v < sz; v++) {
out << "v" << v << " -> #" << m_var2enode[v]->get_owner_id() << "\n";
}
}
void theory::display_app(std::ostream & out, app * n) const {
func_decl * d = n->get_decl();
if (n->get_num_args() == 0) {
out << d->get_name();
display_parameters(out, d->get_num_parameters(), d->get_parameters());
}
else if (n->get_family_id() == get_family_id()) {
out << "(" << d->get_name();
display_parameters(out, d->get_num_parameters(), d->get_parameters());
unsigned num = n->get_num_args();
for (unsigned i = 0; i < num; i++) {
out << " ";
display_app(out, to_app(n->get_arg(i)));
}
out << ")";
}
else {
out << "#" << n->get_id();
}
}
void theory::display_flat_app(std::ostream & out, app * n) const {
func_decl * d = n->get_decl();
if (n->get_num_args() == 0) {
out << d->get_name();
display_parameters(out, d->get_num_parameters(), d->get_parameters());
}
else if (n->get_family_id() == get_family_id()) {
out << "(" << d->get_name();
display_parameters(out, d->get_num_parameters(), d->get_parameters());
ptr_buffer<app> todo;
todo.push_back(n);
while (!todo.empty()) {
n = todo.back();
todo.pop_back();
unsigned num = n->get_num_args();
for (unsigned i = 0; i < num; i++) {
app * arg = to_app(n->get_arg(i));
if (d->is_associative() && arg->get_decl() == d) {
todo.push_back(arg);
}
else {
out << " ";
display_app(out, arg);
}
}
}
out << ")";
}
else {
out << "#" << n->get_id();
}
}
bool theory::is_relevant_and_shared(enode * n) const {
context & ctx = get_context();
return ctx.is_relevant(n) && ctx.is_shared(n);
}
bool theory::assume_eq(enode * n1, enode * n2) {
return get_context().assume_eq(n1, n2);
}
literal theory::mk_eq(expr * a, expr * b, bool gate_ctx) {
context & ctx = get_context();
app * eq = ctx.mk_eq_atom(a, b);
TRACE("mk_var_bug", tout << "mk_eq: " << eq->get_id() << " " << a->get_id() << " " << b->get_id() << "\n";
tout << mk_ll_pp(a, get_manager()) << "\n" << mk_ll_pp(b, get_manager()););
ctx.internalize(eq, gate_ctx);
return ctx.get_literal(eq);
}
theory::theory(family_id fid):
m_id(fid),
m_context(0),
m_manager(0) {
}
theory::~theory() {
}
};

454
src/smt/smt_theory.h Normal file
View file

@ -0,0 +1,454 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_theory.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-20.
Revision History:
--*/
#ifndef _SMT_THEORY_H_
#define _SMT_THEORY_H_
#include"smt_enode.h"
#include"obj_hashtable.h"
#include"statistics.h"
namespace smt {
class model_generator;
class model_value_proc;
class theory {
theory_id m_id;
context * m_context;
ast_manager * m_manager;
enode_vector m_var2enode;
unsigned_vector m_var2enode_lim;
friend class context;
protected:
virtual void init(context * ctx);
/* ---------------------------------------------------
In the logical context, expressions are 'internalized'. That
is, the logical context creates auxiliary data-structures
(e.g., enodes) and attach them to the expressions. The logical
context does not know the internals of each theory. So, during
the internalization process, it notifies the theory (plugin)
whenever it finds an application with a theory function
symbol.
A theory variable created at scope level n must be deleted
when scope level n is backtracked.
The logical context uses the method is_attached_to_var
to decide whether an enode is already associated with a theory
variable or not.
------------------------------------------------------ */
virtual theory_var mk_var(enode * n) {
SASSERT(!is_attached_to_var(n));
theory_var v = m_var2enode.size();
m_var2enode.push_back(n);
return v;
}
public:
/**
\brief Return ture if the given enode is attached to a
variable of the theory.
\remark The result is not equivalent to
n->get_th_var(get_id()) != null_theory_var
A theory variable v may be in the list of variables of n,
but it may be inherited from another enode n' during an
equivalence class merge. That is, get_enode(v) != n.
*/
bool is_attached_to_var(enode const * n) const {
theory_var v = n->get_th_var(get_id());
return v != null_theory_var && get_enode(v) == n;
}
protected:
/**
\brief Return true if the theory uses default internalization:
"the internalization of an application internalizes all arguments".
Theories like arithmetic do not use default internalization.
For example, in the application (+ a (+ b c)), no enode is created
for (+ b c).
*/
virtual bool default_internalizer() const {
return true;
}
/**
\brief This method is invoked by the logical context when
atom is being internalized. The theory may return false if it
does not want to implement the given predicate symbol.
After the execution of this method the given atom must be
associated with a new boolean variable.
*/
virtual bool internalize_atom(app * atom, bool gate_ctx) = 0;
/**
\brief This method is invoked by the logical context
after the given equality atom is internalized.
*/
virtual void internalize_eq_eh(app * atom, bool_var v) {
}
/**
\brief This method is invoked by the logical context when
the term is being internalized. The theory may return false
if it does not want to implement the given function symbol.
After the execution of this method the given term must be
associated with a new enode.
*/
virtual bool internalize_term(app * term) = 0;
/**
\brief Apply (interpreted) sort constraints on the given enode.
*/
virtual void apply_sort_cnstr(enode * n, sort * s) {
}
/**
\brief This method is invoked when a truth value is
assigned to the given boolean variable.
*/
virtual void assign_eh(bool_var v, bool is_true) {
}
/**
\brief Equality propagation (v1 = v2): Core -> Theory
*/
virtual void new_eq_eh(theory_var v1, theory_var v2) = 0;
/**
\brief Return true if the theory does something with the
disequalities implied by the core.
*/
virtual bool use_diseqs() const {
return true;
}
/**
\brief Disequality propagation (v1 /= v2): Core -> Theory
*/
virtual void new_diseq_eh(theory_var v1, theory_var v2) = 0;
/**
\brief This method is invoked when the theory application n
is marked as relevant.
*/
virtual void relevant_eh(app * n) {
}
/**
\brief This method is invoked when a new backtracking point
is created.
*/
virtual void push_scope_eh();
/**
\brief This method is invoked during backtracking.
*/
virtual void pop_scope_eh(unsigned num_scopes);
/**
\brief This method is invoked when the logical context is being
restarted.
*/
virtual void restart_eh() {
}
/**
\brief This method is invoked before the search starts.
*/
virtual void init_search_eh() {
}
/**
\brief This method is invoked when the logical context assigned
a truth value to all boolean variables and no inconsistency was
detected.
*/
virtual final_check_status final_check_eh() {
return FC_DONE;
}
/**
\brief Parametric theories (e.g. Arrays) should implement this method.
See example in context::is_shared
*/
virtual bool is_shared(theory_var v) const {
return false;
}
/**
\brief Return true if the theory has something to propagate
*/
virtual bool can_propagate() {
return false;
}
/**
\brief This method is invoked to give a theory a chance to perform
theory propagation.
*/
virtual void propagate() {
}
/**
\brief This method allows a theory to contribute to
disequality propagation.
*/
virtual justification * why_is_diseq(theory_var v1, theory_var v2) {
return 0;
}
/**
\brief Just releases memory.
*/
virtual void flush_eh() {
}
/**
\brief This method is invoked when the logical context is being reset.
*/
virtual void reset_eh();
// ----------------------------------------------------
//
// Model validation (-vldt flag)
//
// ----------------------------------------------------
virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const {
return true;
}
// ----------------------------------------------------
//
// Conflict resolution event handler
//
// ----------------------------------------------------
public:
/**
\brief This method is invoked when a theory atom is used
during conflict resolution. This allows the theory to bump
the activity of the enodes contained in the given atom.
*/
virtual void conflict_resolution_eh(app * atom, bool_var v) {
}
public:
theory(family_id fid);
virtual ~theory();
virtual void setup() {
}
theory_id get_id() const {
return m_id;
}
family_id get_family_id() const {
return m_id;
}
context & get_context() const {
SASSERT(m_context);
return *m_context;
}
ast_manager & get_manager() const {
SASSERT(m_manager);
return *m_manager;
}
enode * get_enode(theory_var v) const {
SASSERT(v < static_cast<int>(m_var2enode.size()));
return m_var2enode[v];
}
/**
\brief Return the equivalence class representative
of the given theory variable.
*/
theory_var get_representative(theory_var v) const {
SASSERT(v != null_theory_var);
theory_var r = get_enode(v)->get_root()->get_th_var(get_id());
SASSERT(r != null_theory_var);
return r;
}
/**
\brief Return true if the theory variable is the representative
of its equivalence class.
*/
bool is_representative(theory_var v) const {
return get_representative(v) == v;
}
unsigned get_num_vars() const {
return m_var2enode.size();
}
unsigned get_old_num_vars(unsigned num_scopes) const {
return m_var2enode_lim[m_var2enode_lim.size() - num_scopes];
}
virtual void display(std::ostream & out) const {
out << "Theory " << static_cast<int>(get_id()) << " does not have a display method\n";
display_var2enode(out);
}
virtual void display_var2enode(std::ostream & out) const;
virtual void collect_statistics(::statistics & st) const {
}
void display_app(std::ostream & out, app * n) const;
void display_flat_app(std::ostream & out, app * n) const;
void display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_owner()); }
void display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_owner()); }
/**
\brief Assume eqs between variable that are equal with respect to the given table.
Table is a hashtable indexed by the variable value.
table.contains(v) should be true if there is v' in table such that assignment of
v is equal to v'.
This method assumes that class VarValueTable contains the methods:
- void reset()
- theory_var insert_if_not_there(theory_var v)
*/
template<typename VarValueTable>
bool assume_eqs(VarValueTable & table) {
TRACE("assume_eqs", tout << "starting...\n";);
table.reset();
bool result = false;
int num = get_num_vars();
for (theory_var v = 0; v < num; v++) {
enode * n = get_enode(v);
theory_var other = null_theory_var;
TRACE("assume_eqs",
tout << "#" << n->get_owner_id() << " is_relevant_and_shared: " << is_relevant_and_shared(n) << "\n";);
if (n != 0 && is_relevant_and_shared(n)) {
other = table.insert_if_not_there(v);
if (other != v) {
enode * n2 = get_enode(other);
TRACE("assume_eqs", tout << "value(#" << n->get_owner_id() << ") = value(#" << n2->get_owner_id() << ")\n";);
if (assume_eq(n, n2)) {
TRACE("assume_eqs", tout << "new assumed eq\n";);
result = true;
}
}
}
}
return result;
}
/**
\brief When an eq atom n is created during the search, the default behavior is
to make sure that the n->get_arg(0)->get_id() < n->get_arg(1)->get_id().
This may create some redundant atoms, since some theories/families use different
convetions in their simplifiers. For example, arithmetic always force a numeral
to be in the right hand side. So, this method should be redefined if the default
behavior conflicts with a convention used by the theory/family.
*/
virtual app * mk_eq_atom(expr * lhs, expr * rhs) {
if (lhs->get_id() > rhs->get_id())
std::swap(lhs, rhs);
return get_manager().mk_eq(lhs, rhs);
}
literal mk_eq(expr * a, expr * b, bool gate_ctx);
// -----------------------------------
//
// Model generation
//
// -----------------------------------
/**
\brief Return true if theory support model construction
*/
virtual bool build_models() const {
return true;
}
virtual void init_model(model_generator & m) {
}
virtual void finalize_model(model_generator & m) {
}
/**
\brief Return a functor that can build the value (interpretation) for n.
*/
virtual model_value_proc * mk_value(enode * n, model_generator & mg) {
return 0;
}
// -----------------------------------
//
// Model checker
//
// -----------------------------------
virtual bool get_value(enode * n, expr_ref & r) {
return false;
}
virtual char const * get_name() const { return "unknown"; }
// -----------------------------------
//
// Return a fresh new instance of the given theory.
// This function is used to create a fresh context (new_ctx) containing the same theories of the context that owns this theory.
//
// We need the parameter new_ctx because of user_smt_theory :-(
//
// -----------------------------------
virtual theory * mk_fresh(context * new_ctx) = 0;
protected:
// ----------------------------------------------------
//
// Auxiliary methods for assume_eqs
//
// smt::context is not defined at this point.
//
// ----------------------------------------------------
bool is_relevant_and_shared(enode * n) const;
bool assume_eq(enode * n1, enode * n2);
};
};
#endif /* _SMT_THEORY_H_ */

View file

@ -0,0 +1,76 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_theory_var_list.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#ifndef _SMT_THEORY_VAR_LIST_H_
#define _SMT_THEORY_VAR_LIST_H_
#include"smt_types.h"
namespace smt {
class theory_var_list {
int m_th_id:8;
int m_th_var:24;
theory_var_list * m_next;
public:
theory_var_list():
m_th_id(null_theory_id),
m_th_var(null_theory_var),
m_next(0) {
}
theory_var_list(theory_id t, theory_var v, theory_var_list * n = 0):
m_th_id(t),
m_th_var(v),
m_next(n) {
}
theory_id get_th_id() const {
return m_th_id;
}
theory_var get_th_var() const {
return m_th_var;
}
theory_var_list * get_next() const {
return m_next;
}
void set_th_id(theory_id id) {
m_th_id = id;
}
void set_th_var(theory_var v) {
m_th_var = v;
}
void set_next(theory_var_list * next) {
m_next = next;
}
};
// 32 bit machine
COMPILE_TIME_ASSERT(sizeof(expr*) != 4 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int));
// 64 bit machine
COMPILE_TIME_ASSERT(sizeof(expr*) != 8 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int) + /* a structure must be aligned */ sizeof(int));
};
#endif /* _SMT_THEORY_VAR_LIST_H_ */

78
src/smt/smt_types.h Normal file
View file

@ -0,0 +1,78 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_types.h
Abstract:
Basic types for the SMT engine
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#ifndef _SMT_TYPES_H_
#define _SMT_TYPES_H_
#include"list.h"
#include"vector.h"
#include"lbool.h"
class model;
namespace smt {
/**
\brief A boolean variable is just an integer.
*/
typedef int bool_var;
const bool_var null_bool_var = -1;
const bool_var true_bool_var = 0;
const bool_var first_bool_var = 1;
typedef svector<bool_var> bool_var_vector;
typedef family_id theory_id;
const theory_id null_theory_id = null_family_id;
typedef int theory_var;
const theory_var null_theory_var = -1;
class enode;
typedef ptr_vector<enode> enode_vector;
typedef std::pair<enode *, enode *> enode_pair;
class context;
class theory;
class justification;
class model_generator;
enum final_check_status {
FC_DONE,
FC_CONTINUE,
FC_GIVEUP
};
inline std::ostream & operator<<(std::ostream & out, final_check_status st) {
switch (st) {
case FC_DONE: out << "done"; break;
case FC_CONTINUE: out << "continue"; break;
case FC_GIVEUP: out << "giveup"; break;
}
return out;
}
// if defined, then clauses have an extra mask field used to optimize backward subsumption, and backward/forward subsumption resolution.
#define APPROX_LIT_SET
};
#endif /* _SMT_TYPES_H_ */

51
src/smt/solver_plugin.h Normal file
View file

@ -0,0 +1,51 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
solver_plugin.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-30.
Revision History:
--*/
#ifndef _SOLVER_PLUGIN_H_
#define _SOLVER_PLUGIN_H_
#include"ast.h"
/**
\brief Abstract solver used during preprocessing step.
*/
class solver_plugin {
protected:
ast_manager & m_manager;
family_id m_fid;
public:
solver_plugin(symbol const & fname, ast_manager & m):m_manager(m), m_fid(m.get_family_id(fname)) {}
virtual ~solver_plugin() {}
/**
\brief Return true if it is possible to solve lhs = rhs. The
arg. forbidden contains the set of variables that cannot be
used. Store the result (var = subst) in var and subst.
\remark Only simple solvers are supported. That is, the solution set has only one entry.
*/
virtual bool solve(expr * lhs, expr * rhs, expr_mark const & forbidden, app_ref & var, expr_ref & subst) = 0;
family_id get_family_id() const { return m_fid; }
ast_manager & get_manager() { return m_manager; }
};
#endif /* _SOLVER_PLUGIN_H_ */

30
src/smt/theory_arith.cpp Normal file
View file

@ -0,0 +1,30 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_arith.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-04-22.
Revision History:
--*/
#include"theory_arith_def.h"
namespace smt {
template class theory_arith<mi_ext_with_proofs>;
template class theory_arith<mi_ext>;
template class theory_arith<i_ext>;
template class theory_arith<si_ext>;
template class theory_arith<smi_ext>;
};

1222
src/smt/theory_arith.h Normal file

File diff suppressed because it is too large Load diff

1481
src/smt/theory_arith_aux.h Normal file

File diff suppressed because it is too large Load diff

3080
src/smt/theory_arith_core.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,32 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_arith_def.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-04-22.
Revision History:
--*/
#ifndef _THEORY_ARITH_DEF_H_
#define _THEORY_ARITH_DEF_H_
#include"theory_arith.h"
#include"theory_arith_core.h"
#include"theory_arith_aux.h"
#include"theory_arith_inv.h"
#include"theory_arith_pp.h"
#include"theory_arith_int.h"
#include"theory_arith_eq.h"
#include"theory_arith_nl.h"
#endif /* _THEORY_ARITH_DEF_H_ */

351
src/smt/theory_arith_eq.h Normal file
View file

@ -0,0 +1,351 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_arith_eq.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-22.
Revision History:
--*/
#ifndef _THEORY_ARITH_EQ_H_
#define _THEORY_ARITH_EQ_H_
// #define PROFILE_OFFSET_ROW
#ifdef PROFILE_OFFSET_ROW
#include"stopwatch.h"
#undef max
#undef min
#endif
namespace smt {
/**
\brief This method is invoked when a variable was non fixed and become fixed.
*/
template<typename Ext>
void theory_arith<Ext>::fixed_var_eh(theory_var v) {
if (!propagate_eqs())
return;
SASSERT(is_fixed(v));
// WARNINING: it is not safe to use get_value(v) here, since
// get_value(v) may not satisfy v bounds at this point.
CTRACE("arith_bug", !lower_bound(v).is_rational(), display_var(tout, v););
SASSERT(lower_bound(v).is_rational());
numeral const & val = lower_bound(v).get_rational();
value_sort_pair key(val, is_int(v));
theory_var v2;
if (m_fixed_var_table.find(key, v2)) {
if (v2 < static_cast<int>(get_num_vars()) && is_fixed(v2) && lower_bound(v2).get_rational() == val) {
// It only makes sense to propagate equality to the core when v and v2 have the same sort.
// The table m_fixed_var_table is not restored during backtrack. So, it may
// contain invalid (key -> value) pairs. So, we must check whether v2 is really equal to val (previous test) AND it has
// the same sort of v. The following test was missing in a previous version of Z3.
if (!is_equal(v, v2) && is_int(v) == is_int(v2)) {
antecedents& ante = get_antecedents();
//
// v <= k <= v2 => v <= v2
// v >= k >= v2 => v >= v2
//
lower(v)->push_justification(ante, numeral::zero());
upper(v2)->push_justification(ante, numeral::zero());
lower(v2)->push_justification(ante, numeral::zero());
upper(v)->push_justification(ante, numeral::zero());
TRACE("arith_fixed_propagate_eq", tout << "propagate eq: v" << v << " = v" << v2 << "\n";
display_var(tout, v);
display_var(tout, v2););
m_stats.m_fixed_eqs++;
propagate_eq_to_core(v, v2, ante);
}
}
else {
// the original fixed variable v2 was deleted or its bounds were removed
// during backtracking.
m_fixed_var_table.erase(key);
m_fixed_var_table.insert(key, v);
}
}
else {
m_fixed_var_table.insert(key, v);
}
}
/**
\brief Returns true if r is a offset row.
A offset row is a row that can be written as:
x = y + M
where x and y are non fixed variables, and
M is linear polynomials where all variables are fixed,
and M evaluates to k.
When true is returned, x, y and k are stored in the given arguments.
\remark The following rule is used to select x and y.
- if the base variable is not fixed, then x is the base var.
- otherwise x is the smallest var.
*/
template<typename Ext>
bool theory_arith<Ext>::is_offset_row(row const & r, theory_var & x, theory_var & y, numeral & k) const {
#ifdef PROFILE_OFFSET_ROW
static stopwatch timer;
static unsigned total = 0;
static unsigned ok = 0;
timer.start();
total ++;
#endif
// Quick check without using big numbers...
// Check if there are more than 2 unbounded vars.
unsigned bad = 0;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
theory_var v = it->m_var;
if (lower(v) != 0 && upper(v) != 0)
continue;
bad++;
if (bad > 2) {
#ifdef PROFILE_OFFSET_ROW
timer.stop();
#endif
return false;
}
}
}
// Full check using == for big numbers...
x = null_theory_var;
y = null_theory_var;
it = r.begin_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
theory_var v = it->m_var;
if (is_fixed(v))
continue;
if (it->m_coeff.is_one() && x == null_theory_var) {
x = v;
continue;
}
if (it->m_coeff.is_minus_one() && y == null_theory_var) {
y = v;
continue;
}
#ifdef PROFILE_OFFSET_ROW
timer.stop();
#endif
return false;
}
}
if (x == null_theory_var && y == null_theory_var) {
#ifdef PROFILE_OFFSET_ROW
timer.stop();
#endif
return false;
}
k.reset();
it = r.begin_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
theory_var v = it->m_var;
if (v == x || v == y)
continue;
SASSERT(is_fixed(v));
k -= it->m_coeff * lower_bound(v).get_rational();
}
}
#ifdef PROFILE_OFFSET_ROW
timer.stop();
ok++;
if (ok % 100000 == 0) {
TRACE("propagate_cheap_eq",
tout << total << " " << ok << " "
<< static_cast<double>(ok)/static_cast<double>(total)
<< " " << timer.get_seconds() << "\n";
tout.flush(););
}
#endif
if (y == null_theory_var)
return true;
if (x == null_theory_var) {
std::swap(x, y);
k.neg();
SASSERT(x != null_theory_var);
return true;
}
if (!r.get_base_var() == x && x > y) {
std::swap(x, y);
k.neg();
}
return true;
}
/**
\brief Cheap propagation of equalities x_i = x_j, when
x_i = y + k
x_j = y + k
This equalities are detected by maintaining a map:
(y, k) -> row_id when a row is of the form x = y + k
This methods checks whether the given row is an offset row (See is_offset_row),
and uses the map to find new equalities if that is the case.
*/
template<typename Ext>
void theory_arith<Ext>::propagate_cheap_eq(unsigned rid) {
if (!propagate_eqs())
return;
TRACE("propagate_cheap_eq", tout << "checking if row " << rid << " can propagate equality.\n";
display_row_info(tout, rid););
row const & r = m_rows[rid];
theory_var x;
theory_var y;
numeral k;
if (is_offset_row(r, x, y, k)) {
if (y == null_theory_var) {
// x is an implied fixed var at k.
value_sort_pair key(k, is_int(x));
theory_var x2;
if (m_fixed_var_table.find(key, x2) &&
x2 < static_cast<int>(get_num_vars()) &&
is_fixed(x2) &&
lower_bound(x2).get_rational() == k &&
// We must check whether x2 is an integer.
// The table m_fixed_var_table is not restored during backtrack. So, it may
// contain invalid (key -> value) pairs.
// So, we must check whether x2 is really equal to k (previous test)
// AND has the same sort of x.
// The following test was missing in a previous version of Z3.
is_int(x) == is_int(x2) &&
!is_equal(x, x2)) {
antecedents& ante = get_antecedents();
collect_fixed_var_justifications(r, ante);
//
// x1 <= k1 x1 >= k1, x2 <= x1 + k2 x2 >= x1 + k2
//
lower(x2)->push_justification(ante, numeral::zero());
upper(x2)->push_justification(ante, numeral::zero());
m_stats.m_fixed_eqs++;
propagate_eq_to_core(x, x2, ante);
}
}
if (k.is_zero() && y != null_theory_var && !is_equal(x, y) && is_int(x) == is_int(y)) {
// found equality x = y
antecedents& ante = get_antecedents();
collect_fixed_var_justifications(r, ante);
TRACE("propagate_cheap_eq", tout << "propagate eq using x-y=0 row:\n"; display_row_info(tout, r););
m_stats.m_offset_eqs++;
propagate_eq_to_core(x, y, ante);
}
int row_id;
var_offset key(y, k);
if (m_var_offset2row_id.find(key, row_id)) {
row & r2 = m_rows[row_id];
if (r.get_base_var() == r2.get_base_var()) {
// it is the same row.
return;
}
theory_var x2;
theory_var y2;
numeral k2;
if (r2.get_base_var() != null_theory_var && is_offset_row(r2, x2, y2, k2)) {
bool new_eq = false;
#ifdef _TRACE
bool swapped = false;
#endif
if (y == y2 && k == k2) {
new_eq = true;
}
else if (y2 != null_theory_var) {
#ifdef _TRACE
swapped = true;
#endif
std::swap(x2, y2);
k2.neg();
if (y == y2 && k == k2) {
new_eq = true;
}
}
if (new_eq) {
if (!is_equal(x, x2) && is_int(x) == is_int(x2)) {
SASSERT(y == y2 && k == k2);
antecedents& ante = get_antecedents();
collect_fixed_var_justifications(r, ante);
collect_fixed_var_justifications(r2, ante);
TRACE("propagate_cheap_eq", tout << "propagate eq two rows:\n";
tout << "swapped: " << swapped << "\n";
tout << "x : v" << x << "\n";
tout << "x2 : v" << x2 << "\n";
display_row_info(tout, r);
display_row_info(tout, r2););
m_stats.m_offset_eqs++;
propagate_eq_to_core(x, x2, ante);
}
return;
}
}
// the original row was delete or it is not offset row anymore ===> remove it from table
m_var_offset2row_id.erase(key);
}
// add new entry
m_var_offset2row_id.insert(key, rid);
}
}
template<typename Ext>
void theory_arith<Ext>::propagate_eq_to_core(theory_var x, theory_var y, antecedents& antecedents) {
// I doesn't make sense to propagate an equality (to the core) of variables of different sort.
SASSERT(is_int(x) == is_int(y));
// Ignore equality if variables are already known to be equal.
if (is_equal(x, y))
return;
context & ctx = get_context();
region & r = ctx.get_region();
enode * _x = get_enode(x);
enode * _y = get_enode(y);
justification * js =
ctx.mk_justification(
ext_theory_eq_propagation_justification(
get_id(), r,
antecedents.lits().size(), antecedents.lits().c_ptr(),
antecedents.eqs().size(), antecedents.eqs().c_ptr(),
_x, _y,
antecedents.num_params(), antecedents.params("eq-propagate")));
TRACE("propagate_eq_to_core", tout << "detected equality: #" << _x->get_owner_id() << " = #" << _y->get_owner_id() << "\n";
display_var(tout, x);
display_var(tout, y););
ctx.assign_eq(_x, _y, eq_justification(js));
}
};
#endif /* _THEORY_ARITH_EQ_H_ */

1413
src/smt/theory_arith_int.h Normal file

File diff suppressed because it is too large Load diff

207
src/smt/theory_arith_inv.h Normal file
View file

@ -0,0 +1,207 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_arith_inv.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-02.
Revision History:
--*/
#ifndef _THEORY_ARITH_INV_H_
#define _THEORY_ARITH_INV_H_
#include"theory_arith.h"
#include"ast_pp.h"
namespace smt {
#ifdef Z3DEBUG
template<typename Ext>
bool theory_arith<Ext>::check_vector_sizes() const {
SASSERT(m_columns.size() == m_data.size());
SASSERT(m_value.size() == m_data.size());
SASSERT(m_old_value.size() == m_data.size());
SASSERT(m_unassigned_atoms.size() == m_data.size());
SASSERT(m_var_pos.size() == m_data.size());
SASSERT(m_bounds[0].size() == m_data.size());
SASSERT(m_bounds[1].size() == m_data.size());
return true;
}
/**
\brief Check whether all entries of m_var_pos are -1
*/
template<typename Ext>
bool theory_arith<Ext>::check_null_var_pos() const {
svector<int>::const_iterator it = m_var_pos.begin();
svector<int>::const_iterator end = m_var_pos.end();
for (; it != end; ++it) {
SASSERT(*it == -1);
}
return true;
}
/**
\brief Return true if the given row has a variable different from r.m_base_var
that has the given kind.
*/
template<typename Ext>
bool theory_arith<Ext>::has_var_kind(unsigned r_id, var_kind k) const {
row const & r = m_rows[r_id];
theory_var base = r.m_base_var;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead() && get_var_kind(it->m_var) == k && it->m_var != base)
return true;
}
return false;
}
/**
\brief Return true if the given row is well formed.
*/
template<typename Ext>
bool theory_arith<Ext>::wf_row(unsigned r_id) const {
buffer<bool,false,1024> already_found;
already_found.resize(get_num_vars(), false);
row const & r = m_rows[r_id];
if (r.m_base_var != null_theory_var) {
int i = 0;
theory_var s = r.m_base_var;
SASSERT(is_base(s) || is_quasi_base(s));
CTRACE("arith_bug", !(!is_base(s) || (!has_var_kind(r_id, BASE) && !has_var_kind(r_id, QUASI_BASE))),
display_row_info(tout, r_id););
SASSERT(!is_base(s) || (!has_var_kind(r_id, BASE) && !has_var_kind(r_id, QUASI_BASE)));
CTRACE("arith_bug", is_quasi_base(s) && has_var_kind(r_id, QUASI_BASE), display_row_info(tout, r_id););
SASSERT(!is_quasi_base(s) || !has_var_kind(r_id, QUASI_BASE));
SASSERT(r.is_coeff_of(s, numeral::one()));
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it, ++i) {
if (!it->is_dead()) {
CTRACE("row_bug", already_found[it->m_var], display_row_info(tout, r_id););
SASSERT(!already_found[it->m_var]);
already_found[it->m_var] = true;
column const & c = m_columns[it->m_var];
CTRACE("row_bug", it->m_coeff.is_zero(), display_row_info(tout, r_id););
SASSERT(!it->m_coeff.is_zero());
SASSERT(c[it->m_col_idx].m_row_id == static_cast<int>(r_id));
SASSERT(c[it->m_col_idx].m_row_idx == i);
}
}
}
return true;
}
/**
\brief Return true if all rows are well formed.
*/
template<typename Ext>
bool theory_arith<Ext>::wf_rows() const {
unsigned num = m_rows.size();
for (unsigned r_id = 0; r_id < num; r_id++) {
SASSERT(wf_row(r_id));
if (m_rows[r_id].m_base_var == null_theory_var) {
SASSERT(std::find(m_dead_rows.begin(), m_dead_rows.end(), r_id) != m_dead_rows.end());
}
}
return true;
}
/**
\brief Return true if the column associated with v is well formed.
*/
template<typename Ext>
bool theory_arith<Ext>::wf_column(theory_var v) const {
column const & c = m_columns[v];
int i = 0;
typename svector<col_entry>::const_iterator it = c.begin_entries();
typename svector<col_entry>::const_iterator end = c.end_entries();
for (; it != end; ++it, ++i) {
if (!it->is_dead()) {
row const & r = m_rows[it->m_row_id];
CTRACE("wf_column", r.size() == 0, tout << "v" << v << ", it->m_row_id: " << it->m_row_id << "\n"; display_row_info(tout, r); display(tout););
SASSERT(r.size() != 0);
SASSERT(r[it->m_row_idx].m_var == v);
SASSERT(r[it->m_row_idx].m_col_idx == i);
}
}
SASSERT(!is_quasi_base(v) || c.size() == 1);
return true;
}
/**
\brief Return true if all columns are well formed.
*/
template<typename Ext>
bool theory_arith<Ext>::wf_columns() const {
int num = get_num_vars();
for (theory_var v = 0; v < num; v++) {
SASSERT(wf_column(v));
}
return true;
}
/**
\brief Return true if all rows evaluate to zero.
\remark Quasi-base rows don't need to be checked.
*/
template<typename Ext>
bool theory_arith<Ext>::valid_row_assignment() const {
TRACE("valid_row_assignment", display(tout););
typename vector<row>::const_iterator it = m_rows.begin();
typename vector<row>::const_iterator end = m_rows.end();
for (; it != end; ++it) {
if (it->get_base_var() != null_theory_var) {
SASSERT(valid_row_assignment(*it));
}
}
return true;
}
template<typename Ext>
bool theory_arith<Ext>::valid_row_assignment(row const & r) const {
theory_var s = r.get_base_var();
SASSERT(is_base(s) || is_quasi_base(s));
if (s != null_theory_var && is_base(s)) {
inf_numeral sum;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
sum += it->m_coeff * m_value[it->m_var];
}
}
CTRACE("valid_row_assignment_bug", !sum.is_zero(), tout << "checking: "; display_row_info(tout, r););
SASSERT(sum.is_zero());
}
return true;
}
template<typename Ext>
bool theory_arith<Ext>::satisfy_bounds() const {
int num = get_num_vars();
for (theory_var v = 0; v < num; v++) {
CTRACE("bound_bug", below_lower(v) || above_upper(v), display_var(tout, v); display(tout););
SASSERT(!below_lower(v));
SASSERT(!above_upper(v));
}
return true;
}
#endif
};
#endif /* _THEORY_ARITH_INV_H_ */

2406
src/smt/theory_arith_nl.h Normal file

File diff suppressed because it is too large Load diff

516
src/smt/theory_arith_pp.h Normal file
View file

@ -0,0 +1,516 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_arith_pp.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-05.
Revision History:
--*/
#ifndef _THEORY_ARITH_PP_H_
#define _THEORY_ARITH_PP_H_
#include"theory_arith.h"
#include"ast_smt_pp.h"
#include"stats.h"
namespace smt {
template<typename Ext>
void theory_arith<Ext>::collect_statistics(::statistics & st) const {
st.update("arith conflicts", m_stats.m_conflicts);
st.update("add rows", m_stats.m_add_rows);
st.update("pivots", m_stats.m_pivots);
st.update("assert lower", m_stats.m_assert_lower);
st.update("assert upper", m_stats.m_assert_upper);
st.update("assert diseq", m_stats.m_assert_diseq);
st.update("bound prop", m_stats.m_bound_props);
st.update("fixed eqs", m_stats.m_fixed_eqs);
st.update("offset eqs", m_stats.m_offset_eqs);
st.update("gcd tests", m_stats.m_gcd_tests);
st.update("ineq splits", m_stats.m_branches);
st.update("gomory cuts", m_stats.m_gomory_cuts);
st.update("max-min", m_stats.m_max_min);
st.update("grobner", m_stats.m_gb_compute_basis);
st.update("pseudo nonlinear", m_stats.m_nl_linear);
st.update("nonlinear bounds", m_stats.m_nl_bounds);
st.update("nonlinear horner", m_stats.m_nl_cross_nested);
m_arith_eq_adapter.collect_statistics(st);
}
template<typename Ext>
void theory_arith<Ext>::display(std::ostream & out) const {
out << "Theory arithmetic:\n";
display_vars(out);
display_nl_monomials(out);
display_rows(out, true);
display_rows(out, false);
display_atoms(out);
display_asserted_atoms(out);
}
template<typename Ext>
void theory_arith<Ext>::display_nl_monomials(std::ostream & out) const {
if (m_nl_monomials.empty())
return;
out << "non linear monomials:\n";
svector<theory_var>::const_iterator it = m_nl_monomials.begin();
svector<theory_var>::const_iterator end = m_nl_monomials.end();
for (; it != end; ++it)
display_var(out, *it);
}
template<typename Ext>
void theory_arith<Ext>::display_row(std::ostream & out, unsigned r_id, bool compact) const {
out << r_id << " ";
display_row(out, m_rows[r_id], compact);
}
template<typename Ext>
void theory_arith<Ext>::display_row(std::ostream & out, row const & r, bool compact) const {
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
out << "(v" << r.get_base_var() << ") : ";
bool first = true;
for (; it != end; ++it) {
if (!it->is_dead()) {
if (first)
first = false;
else
out << " + ";
theory_var s = it->m_var;
numeral const & c = it->m_coeff;
if (!c.is_one())
out << c << "*";
if (compact) {
out << "v" << s;
if (is_fixed(s)) {
out << ":" << lower(s)->get_value();
}
}
else
display_var_flat_def(out, s);
}
}
out << "\n";
}
template<typename Ext>
void theory_arith<Ext>::display_rows(std::ostream & out, bool compact) const {
if (compact)
out << "rows (compact view):\n";
else
out << "rows (expanded view):\n";
unsigned num = m_rows.size();
for (unsigned r_id = 0; r_id < num; r_id++) {
if (m_rows[r_id].m_base_var != null_theory_var) {
display_row(out, r_id, compact);
}
}
}
template<typename Ext>
void theory_arith<Ext>::display_row_shape(std::ostream & out, row const & r) const {
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
numeral const & c = it->m_coeff;
if (c.is_one())
out << "1";
else if (c.is_minus_one())
out << "-";
else if (c.is_int() && c.to_rational().is_small())
out << "i";
else if (c.is_int() && !c.to_rational().is_small())
out << "I";
else if (c.to_rational().is_small())
out << "r";
else
out << "R";
}
}
out << "\n";
}
template<typename Ext>
bool theory_arith<Ext>::is_one_minus_one_row(row const & r) const {
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
numeral const & c = it->m_coeff;
if (!c.is_one() && !c.is_minus_one())
return false;
}
}
return true;
}
template<typename Ext>
void theory_arith<Ext>::display_rows_shape(std::ostream & out) const {
unsigned num = m_rows.size();
unsigned num_trivial = 0;
for (unsigned r_id = 0; r_id < num; r_id++) {
row const & r = m_rows[r_id];
if (r.m_base_var != null_theory_var) {
if (is_one_minus_one_row(r))
num_trivial++;
else
display_row_shape(out, r);
}
}
out << "num. trivial: " << num_trivial << "\n";
}
template<typename Ext>
void theory_arith<Ext>::display_rows_bignums(std::ostream & out) const {
unsigned num = m_rows.size();
for (unsigned r_id = 0; r_id < num; r_id++) {
row const & r = m_rows[r_id];
if (r.m_base_var != null_theory_var) {
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
numeral const & c = it->m_coeff;
if (c.to_rational().is_big()) {
std::string str = c.to_rational().to_string();
if (str.length() > 48)
out << str << "\n";
}
}
}
}
}
}
template<typename Ext>
void theory_arith<Ext>::display_rows_stats(std::ostream & out) const {
unsigned num_vars = get_num_vars();
unsigned num_rows = 0;
unsigned num_non_zeros = 0;
unsigned num_ones = 0;
unsigned num_minus_ones = 0;
unsigned num_small_ints = 0;
unsigned num_big_ints = 0;
unsigned num_small_rats = 0;
unsigned num_big_rats = 0;
for (unsigned r_id = 0; r_id < m_rows.size(); r_id++) {
row const & r = m_rows[r_id];
if (r.m_base_var != null_theory_var) {
num_rows++;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
numeral const & c = it->m_coeff;
num_non_zeros++;
if (c.is_one())
num_ones++;
else if (c.is_minus_one())
num_minus_ones++;
else if (c.is_int() && c.to_rational().is_small())
num_small_ints++;
else if (c.is_int() && !c.to_rational().is_small())
num_big_ints++;
else if (c.to_rational().is_small())
num_small_rats++;
else
num_big_rats++;
}
}
}
}
out << "A: " << num_rows << " X " << num_vars << "\n";
out << "avg. row: " << num_non_zeros / num_rows << ", num. non zeros: " << num_non_zeros << "\n";
unsigned spc = 6;
out.width(spc);
out << 1 << "|";
out.width(spc);
out << -1 << "|";
out.width(spc);
out << "i";
out << "|";
out.width(spc);
out << "I";
out << "|";
out.width(spc);
out << "r";
out << "|";
out.width(spc);
out << "R";
out << "\n";
out.width(spc);
out << num_ones << "|";
out.width(spc);
out << num_minus_ones << "|";
out.width(spc);
out << num_small_ints;
out << "|";
out.width(spc);
out << num_big_ints;
out << "|";
out.width(spc);
out << num_small_rats;
out << "|";
out.width(spc);
out << num_big_rats;
out << "\n";
}
template<typename Ext>
void theory_arith<Ext>::display_row_info(std::ostream & out, unsigned r_id) const {
out << r_id << " ";
display_row_info(out, m_rows[r_id]);
}
template<typename Ext>
void theory_arith<Ext>::display_row_info(std::ostream & out, row const & r) const {
display_row(out, r, true);
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it)
if (!it->is_dead())
display_var(out, it->m_var);
}
/**
\brief Display row after substituting fixed variables.
*/
template<typename Ext>
void theory_arith<Ext>::display_simplified_row(std::ostream & out, row const & r) const {
bool has_rat_coeff = false;
numeral k;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
out << "(v" << r.get_base_var() << ") : ";
bool first = true;
for (; it != end; ++it) {
if (it->is_dead())
continue;
theory_var v = it->m_var;
numeral const & c = it->m_coeff;
if (is_fixed(v)) {
k += c * lower_bound(v).get_rational();
continue;
}
if (!c.is_int())
has_rat_coeff = true;
if (first)
first = false;
else
out << " + ";
if (!c.is_one())
out << c << "*";
out << "v" << v;
}
if (!k.is_zero()) {
if (!first)
out << " + ";
out << k;
}
out << "\n";
if (has_rat_coeff) {
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it)
if (!it->is_dead() && (is_base(it->m_var) || (!is_fixed(it->m_var) && (lower(it->m_var) || upper(it->m_var)))))
display_var(out, it->m_var);
}
}
template<typename Ext>
void theory_arith<Ext>::display_var(std::ostream & out, theory_var v) const {
out << "v";
out.width(4);
out << std::left << v;
out << " #";
out.width(4);
out << get_enode(v)->get_owner_id();
out << std::right;
out << " lo:";
out.width(10);
if (lower(v)) {
out << lower(v)->get_value();
}
else {
out << "-oo";
}
out << ", up:";
out.width(10);
if (upper(v)) {
out << upper(v)->get_value();
}
else {
out << "oo";
}
out << ", value: ";
out.width(10);
out << get_value(v);
out << ", occs: ";
out.width(4);
out << m_columns[v].size();
out << ", atoms: ";
out.width(4);
out << m_var_occs[v].size();
out << (is_int(v) ? ", int " : ", real");
switch (get_var_kind(v)) {
case NON_BASE:
out << ", non-base ";
break;
case QUASI_BASE:
out << ", quasi-base";
break;
case BASE:
out << ", base ";
break;
}
out << ", shared: " << get_context().is_shared(get_enode(v));
out << ", unassigned: " << m_unassigned_atoms[v];
out << ", rel: " << get_context().is_relevant(get_enode(v));
out << ", def: ";
display_var_flat_def(out, v);
out << "\n";
}
template<typename Ext>
void theory_arith<Ext>::display_vars(std::ostream & out) const {
out << "vars:\n";
int n = get_num_vars();
for (theory_var v = 0; v < n; v++)
display_var(out, v);
}
template<typename Ext>
void theory_arith<Ext>::display_bound(std::ostream & out, bound * b, unsigned indent) const {
for (unsigned i = 0; i < indent; i++) out << " ";
theory_var v = b->get_var();
enode * e = get_enode(v);
out << "v" << v << " #" << e->get_owner_id() << " " << (b->get_bound_kind() == B_LOWER ? ">=" : "<=") << " " << b->get_value() << "\n";
}
template<typename Ext>
void theory_arith<Ext>::display_atoms(std::ostream & out) const {
out << "atoms:\n";
for (unsigned i = 0; i < m_atoms.size(); i++)
display_atom(out, m_atoms[i], false);
}
template<typename Ext>
void theory_arith<Ext>::display_asserted_atoms(std::ostream & out) const {
out << "asserted atoms:\n";
for (unsigned i = 0; i < m_asserted_qhead; i++) {
bound * b = m_asserted_bounds[i];
if (b->is_atom())
display_atom(out, static_cast<atom*>(b), true);
}
if (m_asserted_qhead < m_asserted_bounds.size()) {
out << "delayed atoms:\n";
for (unsigned i = m_asserted_qhead; i < m_asserted_bounds.size(); i++) {
bound * b = m_asserted_bounds[i];
if (b->is_atom())
display_atom(out, static_cast<atom*>(b), true);
}
}
}
template<typename Ext>
void theory_arith<Ext>::display_atom(std::ostream & out, atom * a, bool show_sign) const {
theory_var v = a->get_var();
numeral const & k = a->get_k();
enode * e = get_enode(v);
if (show_sign) {
if (!a->is_true())
out << "not ";
else
out << " ";
}
out << "v";
out.width(3);
out << std::left << v << " #";
out.width(3);
out << e->get_owner_id();
out << std::right;
out << " ";
if (a->get_atom_kind() == A_LOWER)
out << ">=";
else
out << "<=";
out << " ";
out.width(6);
out << k << " ";
display_var_flat_def(out, v);
out << "\n";
}
template<typename Ext>
void theory_arith<Ext>::display_bounds_in_smtlib(std::ostream & out) const {
ast_manager & m = get_manager();
ast_smt_pp pp(m);
pp.set_benchmark_name("lemma");
int n = get_num_vars();
for (theory_var v = 0; v < n; v++) {
expr * n = get_enode(v)->get_owner();
if (is_fixed(v)) {
inf_numeral k_inf = lower_bound(v);
rational k = k_inf.get_rational().to_rational();
expr_ref eq(m);
eq = m.mk_eq(n, m_util.mk_numeral(k, is_int(v)));
pp.add_assumption(eq);
}
else {
if (lower(v) != 0) {
inf_numeral k_inf = lower_bound(v);
rational k = k_inf.get_rational().to_rational();
expr_ref ineq(m);
if (k_inf.get_infinitesimal().is_zero())
ineq = m_util.mk_le(m_util.mk_numeral(k, is_int(v)), n);
else
ineq = m_util.mk_lt(m_util.mk_numeral(k, is_int(v)), n);
pp.add_assumption(ineq);
}
if (upper(v) != 0) {
inf_numeral k_inf = upper_bound(v);
rational k = k_inf.get_rational().to_rational();
expr_ref ineq(m);
if (k_inf.get_infinitesimal().is_zero())
ineq = m_util.mk_le(n, m_util.mk_numeral(k, is_int(v)));
else
ineq = m_util.mk_lt(n, m_util.mk_numeral(k, is_int(v)));
pp.add_assumption(ineq);
}
}
}
pp.display(out, m.mk_true());
}
template<typename Ext>
void theory_arith<Ext>::display_bounds_in_smtlib() const {
char buffer[128];
static int id = 0;
#ifdef _WINDOWS
sprintf_s(buffer, ARRAYSIZE(buffer), "arith_%d.smt", id);
#else
sprintf(buffer, "arith_%d.smt", id);
#endif
std::ofstream out(buffer);
display_bounds_in_smtlib(out);
out.close();
id++;
}
};
#endif /* _THEORY_ARITH_PP_H_ */

479
src/smt/theory_array.cpp Normal file
View file

@ -0,0 +1,479 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_array.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-01.
Revision History:
--*/
#include"smt_context.h"
#include"theory_array.h"
#include"ast_ll_pp.h"
#include"stats.h"
namespace smt {
theory_array::theory_array(ast_manager & m, theory_array_params & params):
theory_array_base(m),
m_params(params),
m_find(*this),
m_trail_stack(*this),
m_final_check_idx(0) {
}
theory_array::~theory_array() {
std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc<var_data>());
m_var_data.reset();
}
void theory_array::init(context * ctx) {
theory_array_base::init(ctx);
if (!ctx->relevancy())
m_params.m_array_laziness = 0;
}
void theory_array::merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) {
// v1 is the new root
TRACE("array", tout << "merging v" << v1 << " v" << v2 << "\n"; display_var(tout, v1););
SASSERT(v1 == find(v1));
var_data * d1 = m_var_data[v1];
var_data * d2 = m_var_data[v2];
if (!d1->m_prop_upward && d2->m_prop_upward)
set_prop_upward(v1);
ptr_vector<enode>::iterator it = d2->m_stores.begin();
ptr_vector<enode>::iterator end = d2->m_stores.end();
for (; it != end; ++it)
add_store(v1, *it);
it = d2->m_parent_stores.begin();
end = d2->m_parent_stores.end();
for (; it != end; ++it)
add_parent_store(v1, *it);
it = d2->m_parent_selects.begin();
end = d2->m_parent_selects.end();
for (; it != end; ++it)
add_parent_select(v1, *it);
TRACE("array", tout << "after merge\n"; display_var(tout, v1););
}
void theory_array::unmerge_eh(theory_var v1, theory_var v2) {
// do nothing
}
theory_var theory_array::mk_var(enode * n) {
theory_var r = theory_array_base::mk_var(n);
theory_var r2 = m_find.mk_var();
SASSERT(r == r2);
SASSERT(r == static_cast<int>(m_var_data.size()));
m_var_data.push_back(alloc(var_data));
var_data * d = m_var_data[r];
TRACE("array", tout << mk_bounded_pp(n->get_owner(), get_manager()) << "\nis_array: " << is_array_sort(n) << ", is_select: " << is_select(n) <<
", is_store: " << is_store(n) << "\n";);
d->m_is_array = is_array_sort(n);
if (d->m_is_array)
register_sort(get_manager().get_sort(n->get_owner()));
d->m_is_select = is_select(n);
if (is_store(n))
d->m_stores.push_back(n);
get_context().attach_th_var(n, this, r);
if (m_params.m_array_laziness <= 1 && is_store(n))
instantiate_axiom1(n);
return r;
}
void theory_array::add_parent_select(theory_var v, enode * s) {
if (m_params.m_array_cg && !s->is_cgr())
return;
SASSERT(is_select(s));
v = find(v);
var_data * d = m_var_data[v];
d->m_parent_selects.push_back(s);
m_trail_stack.push(push_back_trail<theory_array, enode *, false>(d->m_parent_selects));
ptr_vector<enode>::iterator it = d->m_stores.begin();
ptr_vector<enode>::iterator end = d->m_stores.end();
for (; it != end; ++it) {
instantiate_axiom2a(s, *it);
}
if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
it = d->m_parent_stores.begin();
end = d->m_parent_stores.end();
for (; it != end; ++it) {
enode * store = *it;
SASSERT(is_store(store));
if (!m_params.m_array_cg || store->is_cgr())
instantiate_axiom2b(s, store);
}
}
}
void theory_array::add_parent_store(theory_var v, enode * s) {
if (m_params.m_array_cg && !s->is_cgr())
return;
SASSERT(is_store(s));
v = find(v);
var_data * d = m_var_data[v];
d->m_parent_stores.push_back(s);
m_trail_stack.push(push_back_trail<theory_array, enode *, false>(d->m_parent_stores));
if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
ptr_vector<enode>::iterator it = d->m_parent_selects.begin();
ptr_vector<enode>::iterator end = d->m_parent_selects.end();
for (; it != end; ++it)
if (!m_params.m_array_cg || (*it)->is_cgr())
instantiate_axiom2b(*it, s);
}
}
bool theory_array::instantiate_axiom2b_for(theory_var v) {
bool result = false;
var_data * d = m_var_data[v];
ptr_vector<enode>::iterator it = d->m_parent_stores.begin();
ptr_vector<enode>::iterator end = d->m_parent_stores.end();
for (; it != end; ++it) {
ptr_vector<enode>::iterator it2 = d->m_parent_selects.begin();
ptr_vector<enode>::iterator end2 = d->m_parent_selects.end();
for (; it2 != end2; ++it2)
if (instantiate_axiom2b(*it2, *it))
result = true;
}
return result;
}
/**
\brief Mark v for upward propagation. That is, enables the propagation of select(v, i) to store(v,j,k).
*/
void theory_array::set_prop_upward(theory_var v) {
if (m_params.m_array_weak)
return;
v = find(v);
var_data * d = m_var_data[v];
if (!d->m_prop_upward) {
TRACE("array", tout << "#" << v << "\n";);
m_trail_stack.push(reset_flag_trail<theory_array>(d->m_prop_upward));
d->m_prop_upward = true;
if (!m_params.m_array_delay_exp_axiom)
instantiate_axiom2b_for(v);
ptr_vector<enode>::iterator it = d->m_stores.begin();
ptr_vector<enode>::iterator end = d->m_stores.end();
for (; it != end; ++it)
set_prop_upward(*it);
}
}
void theory_array::set_prop_upward(enode * store) {
if (is_store(store)) {
theory_var st_v = store->get_arg(0)->get_th_var(get_id());
set_prop_upward(st_v);
}
}
void theory_array::set_prop_upward(theory_var v, var_data* d) {
unsigned sz = d->m_stores.size();
for (unsigned i = 0; i < sz; ++i) {
set_prop_upward(d->m_stores[i]);
}
}
/**
\brief Return the size of the equivalence class for array terms
that can be expressed as \lambda i : Index . [.. (select a i) ..]
*/
unsigned theory_array::get_lambda_equiv_size(theory_var v, var_data* d) {
return d->m_stores.size();
}
void theory_array::add_store(theory_var v, enode * s) {
if (m_params.m_array_cg && !s->is_cgr())
return;
SASSERT(is_store(s));
v = find(v);
var_data * d = m_var_data[v];
unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d);
if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) {
set_prop_upward(v, d);
}
d->m_stores.push_back(s);
m_trail_stack.push(push_back_trail<theory_array, enode *, false>(d->m_stores));
ptr_vector<enode>::iterator it = d->m_parent_selects.begin();
ptr_vector<enode>::iterator end = d->m_parent_selects.end();
for (; it != end; ++it) {
SASSERT(is_select(*it));
instantiate_axiom2a(*it, s);
}
if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1)
set_prop_upward(s);
}
void theory_array::instantiate_axiom1(enode * store) {
TRACE("array", tout << "axiom 1:\n" << mk_bounded_pp(store->get_owner(), get_manager()) << "\n";);
SASSERT(is_store(store));
m_stats.m_num_axiom1++;
assert_store_axiom1(store);
}
void theory_array::instantiate_axiom2a(enode * select, enode * store) {
TRACE("array", tout << "axiom 2a: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";);
SASSERT(is_select(select));
SASSERT(is_store(store));
if (assert_store_axiom2(store, select))
m_stats.m_num_axiom2a++;
}
bool theory_array::instantiate_axiom2b(enode * select, enode * store) {
TRACE("array_axiom2b", tout << "axiom 2b: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";);
SASSERT(is_select(select));
SASSERT(is_store(store));
if (assert_store_axiom2(store, select)) {
m_stats.m_num_axiom2b++;
return true;
}
return false;
}
void theory_array::instantiate_extensionality(enode * a1, enode * a2) {
TRACE("array", tout << "extensionality: #" << a1->get_owner_id() << " #" << a2->get_owner_id() << "\n";);
SASSERT(is_array_sort(a1));
SASSERT(is_array_sort(a2));
if (m_params.m_array_extensional && assert_extensionality(a1, a2))
m_stats.m_num_extensionality++;
}
bool theory_array::internalize_atom(app * atom, bool) {
return internalize_term(atom);
}
//
// Internalize the term. If it has already been internalized, return false.
//
bool theory_array::internalize_term_core(app * n) {
TRACE("array_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";);
context & ctx = get_context();
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++)
ctx.internalize(n->get_arg(i), false);
if (ctx.e_internalized(n)) {
return false;
}
enode * e = ctx.mk_enode(n, false, false, true);
if (!is_attached_to_var(e))
mk_var(e);
if (get_manager().is_bool(n)) {
bool_var bv = ctx.mk_bool_var(n);
ctx.set_var_theory(bv, get_id());
ctx.set_enode_flag(bv, true);
}
return true;
}
bool theory_array::internalize_term(app * n) {
if (!is_store(n) && !is_select(n)) {
if (!is_array_ext(n))
found_unsupported_op(n);
return false;
}
TRACE("array_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";);
if (!internalize_term_core(n)) {
return true;
}
context & ctx = get_context();
enode * arg0 = ctx.get_enode(n->get_arg(0));
if (!is_attached_to_var(arg0))
mk_var(arg0);
if (m_params.m_array_laziness == 0) {
theory_var v_arg = arg0->get_th_var(get_id());
SASSERT(v_arg != null_theory_var);
if (is_select(n)) {
add_parent_select(v_arg, ctx.get_enode(n));
}
else if (is_store(n)) {
add_parent_store(v_arg, ctx.get_enode(n));
}
}
return true;
}
void theory_array::apply_sort_cnstr(enode * n, sort * s) {
SASSERT(is_array_sort(s));
if (!is_attached_to_var(n))
mk_var(n);
}
void theory_array::new_eq_eh(theory_var v1, theory_var v2) {
m_find.merge(v1, v2);
}
void theory_array::new_diseq_eh(theory_var v1, theory_var v2) {
v1 = find(v1);
v2 = find(v2);
var_data * d1 = m_var_data[v1];
if (d1->m_is_array) {
SASSERT(m_var_data[v2]->m_is_array);
TRACE("ext", tout << "extensionality:\n" << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager(), 5) << "\n" <<
mk_bounded_pp(get_enode(v2)->get_owner(), get_manager(), 5) << "\n";);
instantiate_extensionality(get_enode(v1), get_enode(v2));
}
}
void theory_array::relevant_eh(app * n) {
if (m_params.m_array_laziness == 0)
return;
if (!is_store(n) && !is_select(n))
return;
context & ctx = get_context();
enode * arg = ctx.get_enode(n->get_arg(0));
theory_var v_arg = arg->get_th_var(get_id());
SASSERT(v_arg != null_theory_var);
if (is_select(n)) {
add_parent_select(v_arg, ctx.get_enode(n));
}
else {
SASSERT(is_store(n));
if (m_params.m_array_laziness > 1)
instantiate_axiom1(ctx.get_enode(n));
add_parent_store(v_arg, ctx.get_enode(n));
}
}
void theory_array::push_scope_eh() {
theory_array_base::push_scope_eh();
m_trail_stack.push_scope();
}
void theory_array::pop_scope_eh(unsigned num_scopes) {
m_trail_stack.pop_scope(num_scopes);
unsigned num_old_vars = get_old_num_vars(num_scopes);
std::for_each(m_var_data.begin() + num_old_vars, m_var_data.end(), delete_proc<var_data>());
m_var_data.shrink(num_old_vars);
theory_array_base::pop_scope_eh(num_scopes);
SASSERT(m_find.get_num_vars() == m_var_data.size());
SASSERT(m_find.get_num_vars() == get_num_vars());
}
final_check_status theory_array::final_check_eh() {
m_final_check_idx++;
final_check_status r;
if (m_params.m_array_lazy_ieq) {
// Delay the creation of interface equalities... The
// motivation is too give other theories and quantifier
// instantiation to do something useful during final
// check.
if (m_final_check_idx % m_params.m_array_lazy_ieq_delay != 0) {
assert_delayed_axioms();
r = FC_CONTINUE;
}
else {
if (mk_interface_eqs_at_final_check() == FC_CONTINUE)
r = FC_CONTINUE;
else
r = assert_delayed_axioms();
}
}
else {
if (m_final_check_idx % 2 == 1) {
if (assert_delayed_axioms() == FC_CONTINUE)
r = FC_CONTINUE;
else
r = mk_interface_eqs_at_final_check();
}
else {
if (mk_interface_eqs_at_final_check() == FC_CONTINUE)
r = FC_CONTINUE;
else
r = assert_delayed_axioms();
}
}
TRACE("as_array", tout << "m_found_unsupported_op: " << m_found_unsupported_op << " " << r << "\n";);
if (r == FC_DONE && m_found_unsupported_op)
r = FC_GIVEUP;
return r;
}
final_check_status theory_array::assert_delayed_axioms() {
if (!m_params.m_array_delay_exp_axiom)
return FC_DONE;
final_check_status r = FC_DONE;
unsigned num_vars = get_num_vars();
for (unsigned v = 0; v < num_vars; v++) {
var_data * d = m_var_data[v];
if (d->m_prop_upward && instantiate_axiom2b_for(v))
r = FC_CONTINUE;
}
return r;
}
final_check_status theory_array::mk_interface_eqs_at_final_check() {
unsigned n = mk_interface_eqs();
m_stats.m_num_eq_splits += n;
if (n > 0)
return FC_CONTINUE;
return FC_DONE;
}
void theory_array::reset_eh() {
m_trail_stack.reset();
std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc<var_data>());
m_var_data.reset();
theory_array_base::reset_eh();
}
void theory_array::display(std::ostream & out) const {
out << "Theory array:\n";
unsigned num_vars = get_num_vars();
for (unsigned v = 0; v < num_vars; v++) {
display_var(out, v);
}
}
// TODO: move to another file
void theory_array::display_ids(std::ostream & out, unsigned n, enode * const * v) {
for (unsigned i = 0; i < n; i++) {
if (i > 0) out << " ";
out << "#" << v[i]->get_owner_id();
}
}
void theory_array::display_var(std::ostream & out, theory_var v) const {
var_data const * d = m_var_data[v];
out << "v";
out.width(4);
out << std::left << v;
out << " #";
out.width(4);
out << get_enode(v)->get_owner_id() << " -> #";
out.width(4);
out << get_enode(find(v))->get_owner_id();
out << std::right;
out << " is_array: " << d->m_is_array << " is_select: " << d->m_is_select << " upward: " << d->m_prop_upward;
out << " stores: {";
display_ids(out, d->m_stores.size(), d->m_stores.c_ptr());
out << "} p_stores: {";
display_ids(out, d->m_parent_stores.size(), d->m_parent_stores.c_ptr());
out << "} p_selects: {";
display_ids(out, d->m_parent_selects.size(), d->m_parent_selects.c_ptr());
out << "}";
out << "\n";
}
void theory_array::collect_statistics(::statistics & st) const {
st.update("array ax1", m_stats.m_num_axiom1);
st.update("array ax2", m_stats.m_num_axiom2a);
st.update("array exp ax2", m_stats.m_num_axiom2b);
st.update("array ext ax", m_stats.m_num_extensionality);
st.update("array splits", m_stats.m_num_eq_splits);
}
};

117
src/smt/theory_array.h Normal file
View file

@ -0,0 +1,117 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_array.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-01.
Revision History:
--*/
#ifndef _THEORY_ARRAY_H_
#define _THEORY_ARRAY_H_
#include"theory_array_base.h"
#include"theory_array_params.h"
#include"union_find.h"
namespace smt {
struct theory_array_stats {
unsigned m_num_axiom1, m_num_axiom2a, m_num_axiom2b, m_num_extensionality, m_num_eq_splits;
unsigned m_num_map_axiom, m_num_default_map_axiom;
unsigned m_num_select_const_axiom, m_num_default_store_axiom, m_num_default_const_axiom, m_num_default_as_array_axiom;
unsigned m_num_select_as_array_axiom;
void reset() { memset(this, 0, sizeof(theory_array_stats)); }
theory_array_stats() { reset(); }
};
class theory_array : public theory_array_base {
protected:
typedef trail_stack<theory_array> th_trail_stack;
typedef union_find<theory_array> th_union_find;
struct var_data {
ptr_vector<enode> m_stores;
ptr_vector<enode> m_parent_selects;
ptr_vector<enode> m_parent_stores;
bool m_prop_upward;
bool m_is_array;
bool m_is_select;
var_data():m_prop_upward(false), m_is_array(false), m_is_select(false) {}
};
ptr_vector<var_data> m_var_data;
theory_array_params & m_params;
theory_array_stats m_stats;
th_union_find m_find;
th_trail_stack m_trail_stack;
unsigned m_final_check_idx;
virtual void init(context * ctx);
virtual theory_var mk_var(enode * n);
virtual bool internalize_atom(app * atom, bool gate_ctx);
virtual bool internalize_term(app * term);
virtual void apply_sort_cnstr(enode * n, sort * s);
virtual void new_eq_eh(theory_var v1, theory_var v2);
virtual void new_diseq_eh(theory_var v1, theory_var v2);
virtual void relevant_eh(app * n);
virtual void push_scope_eh();
virtual void pop_scope_eh(unsigned num_scopes);
virtual final_check_status final_check_eh();
virtual void reset_eh();
virtual void init_search_eh() { m_final_check_idx = 0; }
virtual void set_prop_upward(theory_var v);
virtual void set_prop_upward(enode* n);
virtual void set_prop_upward(theory_var v, var_data* d);
virtual unsigned get_lambda_equiv_size(theory_var v, var_data* d);
theory_var find(theory_var v) const { return m_find.find(v); }
bool is_root(theory_var v) const { return m_find.is_root(v); }
virtual void add_parent_select(theory_var v, enode * s);
void add_parent_store(theory_var v, enode * s);
void add_store(theory_var v, enode * s);
bool internalize_term_core(app * term);
void instantiate_axiom2a(enode * select, enode * store);
bool instantiate_axiom2b(enode * select, enode * store);
void instantiate_axiom1(enode * store);
void instantiate_extensionality(enode * a1, enode * a2);
bool instantiate_axiom2b_for(theory_var v);
virtual final_check_status assert_delayed_axioms();
final_check_status mk_interface_eqs_at_final_check();
static void display_ids(std::ostream & out, unsigned n, enode * const * v);
public:
theory_array(ast_manager & m, theory_array_params & params);
virtual ~theory_array();
virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_array, get_manager(), m_params); }
virtual char const * get_name() const { return "array"; }
virtual void display_var(std::ostream & out, theory_var v) const;
virtual void display(std::ostream & out) const;
virtual void collect_statistics(::statistics & st) const;
th_trail_stack & get_trail_stack() { return m_trail_stack; }
virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var);
static void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {}
void unmerge_eh(theory_var v1, theory_var v2);
};
};
#endif /* _THEORY_ARRAY_H_ */

View file

@ -0,0 +1,926 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_array_base.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-02.
Revision History:
--*/
#include"smt_context.h"
#include"theory_array_base.h"
#include"ast_ll_pp.h"
#include"ast_pp.h"
#include"smt_model_generator.h"
#include"func_interp.h"
#include"ast_smt2_pp.h"
namespace smt {
theory_array_base::theory_array_base(ast_manager & m):
theory(m.get_family_id("array")),
m_found_unsupported_op(false)
{
}
void theory_array_base::found_unsupported_op(expr * n) {
TRACE("theory_array_unsup", tout << mk_ll_pp(n, get_manager()) << "\n";);
if (!m_found_unsupported_op) {
get_context().push_trail(value_trail<context, bool>(m_found_unsupported_op));
m_found_unsupported_op = true;
}
}
app * theory_array_base::mk_select(unsigned num_args, expr * const * args) {
app * r = get_manager().mk_app(get_family_id(), OP_SELECT, 0, 0, num_args, args);
TRACE("mk_var_bug", tout << "mk_select: " << r->get_id() << " num_args: " << num_args;
for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_id();
tout << "\n";);
return r;
}
app * theory_array_base::mk_store(unsigned num_args, expr * const * args) {
return get_manager().mk_app(get_family_id(), OP_STORE, 0, 0, num_args, args);
}
app * theory_array_base::mk_default(expr * a) {
sort * s = get_manager().get_sort(a);
unsigned num_params = get_dimension(s);
parameter const* params = s->get_info()->get_parameters();
return get_manager().mk_app(get_family_id(), OP_ARRAY_DEFAULT, num_params, params, 1, & a);
}
unsigned theory_array_base::get_dimension(sort * s) const {
SASSERT(s->is_sort_of(get_family_id(), ARRAY_SORT));
SASSERT(s->get_info()->get_num_parameters() >= 2);
return s->get_info()->get_num_parameters()-1;
}
void theory_array_base::assert_axiom(unsigned num_lits, literal * lits) {
context & ctx = get_context();
TRACE("array_axiom",
tout << "literals:\n";
for (unsigned i = 0; i < num_lits; ++i) {
expr * e = ctx.bool_var2expr(lits[i].var());
if (lits[i].sign())
tout << "not ";
tout << mk_pp(e, get_manager()) << " ";
tout << "\n";
});
ctx.mk_th_axiom(get_id(), num_lits, lits);
}
void theory_array_base::assert_axiom(literal l1, literal l2) {
literal ls[2] = { l1, l2 };
assert_axiom(2, ls);
}
void theory_array_base::assert_axiom(literal l) {
assert_axiom(1, &l);
}
void theory_array_base::assert_store_axiom1_core(enode * e) {
app * n = e->get_owner();
SASSERT(is_store(n));
context & ctx = get_context();
ast_manager & m = get_manager();
ptr_buffer<expr> sel_args;
unsigned num_args = n->get_num_args();
SASSERT(num_args >= 3);
sel_args.push_back(n);
for (unsigned i = 1; i < num_args - 1; ++i) {
sel_args.push_back(to_app(n->get_arg(i)));
}
expr_ref sel(m);
sel = mk_select(sel_args.size(), sel_args.c_ptr());
expr * val = n->get_arg(num_args - 1);
TRACE("array", tout << mk_bounded_pp(sel, m) << " = " << mk_bounded_pp(val, m) << "\n";);
if (m.proofs_enabled()) {
literal l(mk_eq(sel, val, true));
ctx.mark_as_relevant(l);
assert_axiom(l);
}
else {
TRACE("mk_var_bug", tout << "mk_sel: " << sel->get_id() << "\n";);
ctx.internalize(sel, false);
ctx.assign_eq(ctx.get_enode(sel), ctx.get_enode(val), eq_justification::mk_axiom());
ctx.mark_as_relevant(sel.get());
}
}
/**
\brief Assert axiom 2:
FORALL a, i_i, ..., i_n, j_1, ..., j_n
i_1 /= j_1 => select(store(a, i_1, ..., i_n, v), j_1, ..., j_n) = select(a, j_1, ..., j_n)
and
...
and
i_n /= j_n => select(store(a, i_1, ..., i_n, v), j_1, ..., j_n) = select(a, j_1, ..., j_n)
*/
void theory_array_base::assert_store_axiom2_core(enode * store, enode * select) {
TRACE("array", tout << "generating axiom2: #" << store->get_owner_id() << " #" << select->get_owner_id() << "\n";
tout << mk_bounded_pp(store->get_owner(), get_manager()) << "\n" << mk_bounded_pp(select->get_owner(), get_manager()) << "\n";);
SASSERT(is_store(store));
SASSERT(is_select(select));
SASSERT(store->get_num_args() == 1 + select->get_num_args());
ptr_buffer<expr> sel1_args, sel2_args;
context & ctx = get_context();
ast_manager & m = get_manager();
enode * a = store->get_arg(0);
enode * const * is = select->get_args() + 1;
enode * const * js = store->get_args() + 1;
unsigned num_args = select->get_num_args() - 1;
sel1_args.push_back(store->get_owner());
sel2_args.push_back(a->get_owner());
for (unsigned i = 0; i < num_args; i++) {
sel1_args.push_back(is[i]->get_owner());
sel2_args.push_back(is[i]->get_owner());
}
expr_ref sel1(m), sel2(m);
bool init = false;
literal conseq = null_literal;
expr * conseq_expr = 0;
for (unsigned i = 0; i < num_args; i++) {
enode * idx1 = js[i];
enode * idx2 = is[i];
if (idx1->get_root() == idx2->get_root()) {
TRACE("array_bug", tout << "indexes are equal... skipping...\n";);
continue;
}
if (!init) {
sel1 = mk_select(sel1_args.size(), sel1_args.c_ptr());
sel2 = mk_select(sel2_args.size(), sel2_args.c_ptr());
if (sel1 == sel2) {
TRACE("array_bug", tout << "sel1 and sel2 are equal:\n";);
break;
}
init = true;
TRACE("array", tout << mk_bounded_pp(sel1, m) << " " << mk_bounded_pp(sel2, m) << "\n";);
conseq = mk_eq(sel1, sel2, true);
conseq_expr = ctx.bool_var2expr(conseq.var());
}
literal ante = mk_eq(idx1->get_owner(), idx2->get_owner(), true);
ctx.mark_as_relevant(ante);
// ctx.force_phase(ante);
ctx.add_rel_watch(~ante, conseq_expr);
// ctx.mark_as_relevant(conseq_expr);
TRACE("array", tout << "asserting axiom2: " << ante << "\n";);
TRACE("array_map_bug", tout << "axiom2:\n";
tout << mk_ismt2_pp(idx1->get_owner(), m) << "\n=\n" << mk_ismt2_pp(idx2->get_owner(), m);
tout << "\nimplies\n" << mk_ismt2_pp(conseq_expr, m) << "\n";);
assert_axiom(ante, conseq);
}
}
bool theory_array_base::assert_store_axiom2(enode * store, enode * select) {
unsigned num_args = select->get_num_args();
unsigned i = 1;
for (; i < num_args; i++)
if (store->get_arg(i)->get_root() != select->get_arg(i)->get_root())
break;
if (i == num_args)
return false;
if (get_context().add_fingerprint(store, store->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) {
TRACE("array", tout << "adding axiom2 to todo queue\n";);
m_axiom2_todo.push_back(std::make_pair(store, select));
return true;
}
TRACE("array", tout << "axiom already instantiated: #" << store->get_owner_id() << " #" << select->get_owner_id() << "\n";);
return false;
}
func_decl_ref_vector * theory_array_base::register_sort(sort * s_array) {
unsigned dimension = get_dimension(s_array);
func_decl_ref_vector * ext_skolems = 0;
if (!m_sort2skolem.find(s_array, ext_skolems)) {
ast_manager & m = get_manager();
ext_skolems = alloc(func_decl_ref_vector, m);
for (unsigned i = 0; i < dimension; ++i) {
sort * ext_sk_domain[2] = { s_array, s_array };
parameter p(i);
func_decl * ext_sk_decl = m.mk_func_decl(get_id(), OP_ARRAY_EXT_SKOLEM, 1, &p, 2, ext_sk_domain);
ext_skolems->push_back(ext_sk_decl);
}
m_sort2skolem.insert(s_array, ext_skolems);
m_sorts_trail.push_back(s_array);
}
return ext_skolems;
}
bool theory_array_base::value_eq_proc::operator()(enode * n1, enode * n2) const {
SASSERT(n1->get_num_args() == n2->get_num_args());
unsigned n = n1->get_num_args();
// skipping first argument of the select.
for(unsigned i = 1; i < n; i++) {
if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) {
return false;
}
}
return true;
}
/**
\brief Return true if there is a select(v1', i1) and a select(v2', i2) such that:
v1' = v1, v2' = v2, i1 = i2, select(v1', i1) /= select(v2', i2) in the logical context.
*/
bool theory_array_base::already_diseq(enode * v1, enode * v2) {
context & ctx = get_context();
enode * r1 = v1->get_root();
enode * r2 = v2->get_root();
if (r1->get_class_size() > r2->get_class_size()) {
std::swap(r1, r2);
}
m_array_value.reset();
// populate m_array_value if the select(a, i) parent terms of r1
enode_vector::const_iterator it = r1->begin_parents();
enode_vector::const_iterator end = r1->end_parents();
for (; it != end; ++it) {
enode* parent = *it;
if (parent->is_cgr() &&
ctx.is_relevant(parent) &&
is_select(parent->get_owner()) &&
parent->get_arg(0)->get_root() == r1) {
m_array_value.insert(parent);
}
}
// traverse select(a, i) parent terms of r2 trying to find a match.
it = r2->begin_parents();
end = r2->end_parents();
for (; it != end; ++it) {
enode * parent = *it;
enode * other;
if (parent->is_cgr() &&
ctx.is_relevant(parent) &&
is_select(parent->get_owner()) &&
parent->get_arg(0)->get_root() == r2 &&
m_array_value.find(parent, other)) {
if (ctx.is_diseq(parent, other)) {
TRACE("array_ext", tout << "selects are disequal\n";);
return true;
}
}
}
return false;
}
bool theory_array_base::assert_extensionality(enode * n1, enode * n2) {
context & ctx = get_context();
if (n1->get_owner_id() > n2->get_owner_id())
std::swap(n1, n2);
enode * nodes[2] = { n1, n2 };
if (!ctx.add_fingerprint(this, 0, 2, nodes))
return false; // axiom was already instantiated
if (already_diseq(n1, n2))
return false;
m_extensionality_todo.push_back(std::make_pair(n1, n2));
return true;
}
void theory_array_base::assert_extensionality_core(enode * n1, enode * n2) {
app * e1 = n1->get_owner();
app * e2 = n2->get_owner();
context & ctx = get_context();
ast_manager & m = get_manager();
func_decl_ref_vector * funcs = 0;
sort * s = m.get_sort(e1);
if (!m_sort2skolem.find(s, funcs)) {
UNREACHABLE();
return;
}
unsigned dimension = funcs->size();
expr_ref_vector args1(m), args2(m);
args1.push_back(e1);
args2.push_back(e2);
for (unsigned i = 0; i < dimension; i++) {
expr * k = m.mk_app((*funcs)[i].get(), e1, e2);
args1.push_back(k);
args2.push_back(k);
}
expr * sel1 = mk_select(dimension+1, args1.c_ptr());
expr * sel2 = mk_select(dimension+1, args2.c_ptr());
TRACE("ext", tout << mk_bounded_pp(sel1, m) << "\n" << mk_bounded_pp(sel2, m) << "\n";);
literal n1_eq_n2 = mk_eq(e1, e2, true);
literal sel1_eq_sel2 = mk_eq(sel1, sel2, true);
ctx.mark_as_relevant(n1_eq_n2);
ctx.mark_as_relevant(sel1_eq_sel2);
assert_axiom(n1_eq_n2, ~sel1_eq_sel2);
}
bool theory_array_base::can_propagate() {
return !m_axiom1_todo.empty() || !m_axiom2_todo.empty() || !m_extensionality_todo.empty();
}
void theory_array_base::propagate() {
while (can_propagate()) {
for (unsigned i = 0; i < m_axiom1_todo.size(); i++)
assert_store_axiom1_core(m_axiom1_todo[i]);
m_axiom1_todo.reset();
for (unsigned i = 0; i < m_axiom2_todo.size(); i++)
assert_store_axiom2_core(m_axiom2_todo[i].first, m_axiom2_todo[i].second);
m_axiom2_todo.reset();
for (unsigned i = 0; i < m_extensionality_todo.size(); i++)
assert_extensionality_core(m_extensionality_todo[i].first, m_extensionality_todo[i].second);
m_extensionality_todo.reset();
}
}
/**
\brief Return true if v is shared between two different "instances" of the array theory.
It is shared if it is used in more than one role. The possible roles are: array, index, and value.
Example:
(store v i j) <--- v is used as an array
(select A v) <--- v is used as an index
(store A i v) <--- v is used as an value
*/
bool theory_array_base::is_shared(theory_var v) const {
context & ctx = get_context();
enode * n = get_enode(v);
enode * r = n->get_root();
bool is_array = false;
bool is_index = false;
bool is_value = false;
int num_roles = 0;
#define SET_ARRAY(arg) if (arg->get_root() == r && !is_array) { is_array = true; num_roles++; } if (num_roles > 1) return true
#define SET_INDEX(arg) if (arg->get_root() == r && !is_index) { is_index = true; num_roles++; } if (num_roles > 1) return true
#define SET_VALUE(arg) if (arg->get_root() == r && !is_value) { is_value = true; num_roles++; } if (num_roles > 1) return true
enode_vector::const_iterator it = r->begin_parents();
enode_vector::const_iterator end = r->end_parents();
for (; it != end; ++it) {
enode * parent = *it;
if (!ctx.is_relevant(parent))
continue;
unsigned num_args = parent->get_num_args();
if (is_store(parent)) {
SET_ARRAY(parent->get_arg(0));
for (unsigned i = 1; i < num_args - 1; i++) {
SET_INDEX(parent->get_arg(i));
}
SET_VALUE(parent->get_arg(num_args - 1));
}
else if (is_select(parent)) {
SET_ARRAY(parent->get_arg(0));
for (unsigned i = 1; i < num_args; i++) {
SET_INDEX(parent->get_arg(i));
}
}
else if (is_const(parent)) {
SET_VALUE(parent->get_arg(0));
}
}
return false;
}
void theory_array_base::collect_shared_vars(sbuffer<theory_var> & result) {
TRACE("array_shared", tout << "collecting shared vars...\n";);
context & ctx = get_context();
ptr_buffer<enode> to_unmark;
unsigned num_vars = get_num_vars();
for (unsigned i = 0; i < num_vars; i++) {
enode * n = get_enode(i);
if (ctx.is_relevant(n) && ctx.is_shared(n)) {
enode * r = n->get_root();
if (!r->is_marked() && is_array_sort(r)) {
TRACE("array_shared", tout << "new shared var: #" << r->get_owner_id() << "\n";);
r->set_mark();
to_unmark.push_back(r);
theory_var r_th_var = r->get_th_var(get_id());
SASSERT(r_th_var != null_theory_var);
result.push_back(r_th_var);
}
}
}
unmark_enodes(to_unmark.size(), to_unmark.c_ptr());
}
/**
\brief Create interface variables for shared array variables.
Return the number of new interface equalities.
*/
unsigned theory_array_base::mk_interface_eqs() {
context & ctx = get_context();
ast_manager & m = get_manager();
sbuffer<theory_var> roots;
collect_shared_vars(roots);
unsigned result = 0;
sbuffer<theory_var>::iterator it1 = roots.begin();
sbuffer<theory_var>::iterator end1 = roots.end();
for (; it1 != end1; ++it1) {
TRACE("array_bug", tout << "mk_interface_eqs: processing: v" << *it1 << "\n";);
theory_var v1 = *it1;
enode * n1 = get_enode(v1);
sort * s1 = m.get_sort(n1->get_owner());
sbuffer<theory_var>::iterator it2 = it1;
++it2;
for (; it2 != end1; ++it2) {
theory_var v2 = *it2;
enode * n2 = get_enode(v2);
sort * s2 = m.get_sort(n2->get_owner());
if (s1 == s2 && !ctx.is_diseq(n1, n2)) {
app * eq = mk_eq_atom(n1->get_owner(), n2->get_owner());
if (!ctx.b_internalized(eq) || !ctx.is_relevant(eq)) {
result++;
ctx.internalize(eq, true);
ctx.mark_as_relevant(eq);
}
}
}
}
return result;
}
void theory_array_base::push_scope_eh() {
m_scopes.push_back(scope(m_sorts_trail.size()));
theory::push_scope_eh();
}
void theory_array_base::pop_scope_eh(unsigned num_scopes) {
reset_queues();
scope const & s = m_scopes[m_scopes.size() - num_scopes];
restore_sorts(s.m_sorts_trail_lim);
m_scopes.shrink(m_scopes.size()-num_scopes);
theory::pop_scope_eh(num_scopes);
}
void theory_array_base::restore_sorts(unsigned old_size) {
while (m_sorts_trail.size() > old_size) {
sort * s = m_sorts_trail.back();
func_decl_ref_vector * funcs = 0;
if (m_sort2skolem.find(s, funcs)) {
m_sort2skolem.remove(s);
dealloc(funcs);
}
m_sorts_trail.pop_back();
}
}
void theory_array_base::reset_eh() {
reset_queues();
pop_scope_eh(0);
theory::reset_eh();
}
void theory_array_base::reset_queues() {
m_axiom1_todo.reset();
m_axiom2_todo.reset();
m_extensionality_todo.reset();
}
void theory_array_base::set_default(theory_var v, enode* n) {
TRACE("array", tout << "set default: " << v << " " << mk_pp(n->get_owner(), get_manager()) << "\n";);
v = mg_find(v);
if (m_defaults[v] == 0) {
m_defaults[v] = n;
}
}
enode* theory_array_base::get_default(theory_var v) {
return m_defaults[mg_find(v)];
}
theory_var theory_array_base::mg_find(theory_var n) {
if (m_parents[n] < 0) {
return n;
}
theory_var n0 = n;
n = m_parents[n0];
if (m_parents[n] < -1) {
return n;
}
while (m_parents[n] >= 0) {
n = m_parents[n];
}
// compress path.
while (m_parents[n0] >= 0) {
theory_var n1 = m_parents[n0];
m_parents[n0] = n;
n0 = n1;
}
return n;
}
void theory_array_base::mg_merge(theory_var n, theory_var m) {
n = mg_find(n);
m = mg_find(m);
if (n != m) {
SASSERT(m_parents[n] < 0);
SASSERT(m_parents[m] < 0);
if (m_parents[n] > m_parents[m]) {
std::swap(n, m);
}
m_parents[n] += m_parents[m];
m_parents[m] = n;
if (m_defaults[n] == 0) {
m_defaults[n] = m_defaults[m];
}
CTRACE("array", m_defaults[m],
tout << mk_pp(m_defaults[m]->get_root()->get_owner(), get_manager()) << "\n";
tout << mk_pp(m_defaults[n]->get_root()->get_owner(), get_manager()) << "\n";
);
// NB. it may be the case that m_defaults[m] != m_defaults[n]
// when m and n are finite arrays.
}
}
void theory_array_base::init_model(model_generator & m) {
m_factory = alloc(array_factory, get_manager(), m.get_model());
m.register_factory(m_factory);
m_use_unspecified_default = is_unspecified_default_ok();
collect_defaults();
collect_selects();
propagate_selects();
}
/**
\brief It is ok to use an unspecified default value for arrays, when the
logical context does not contain store, default and const terms.
That is, other modules (such as smt_model_finder) may set the default value to an arbitrary value.
*/
bool theory_array_base::is_unspecified_default_ok() const {
context & ctx = get_context();
int num_vars = get_num_vars();
for (theory_var v = 0; v < num_vars; ++v) {
enode * n = get_enode(v);
// If n is not relevant, then it should not be used to set defaults.
if (!ctx.is_relevant(n))
continue;
if (is_store(n) || is_const(n) || is_default(n))
return false;
}
return true;
}
void theory_array_base::collect_defaults() {
int num_vars = get_num_vars();
m_defaults.reset();
m_else_values.reset();
m_parents.reset();
m_parents.resize(num_vars, -1);
m_defaults.resize(num_vars, 0);
m_else_values.resize(num_vars, 0);
if (m_use_unspecified_default)
return;
context & ctx = get_context();
//
// Create equivalence classes for defaults.
//
for (theory_var v = 0; v < num_vars; ++v) {
enode * n = get_enode(v);
// If n is not relevant, then it should not be used to set defaults.
if (!ctx.is_relevant(n))
continue;
theory_var r = get_representative(v);
mg_merge(v, r);
if (is_store(n)) {
theory_var w = n->get_arg(0)->get_th_var(get_id());
SASSERT(w != null_theory_var);
ast_manager& m = get_manager();
mg_merge(v, get_representative(w));
TRACE("array", tout << "merge: " << mk_pp(n->get_owner(), m) << " " << v << " " << w << "\n";);
}
else if (is_const(n)) {
set_default(v, n->get_arg(0));
}
else if (is_default(n)) {
theory_var w = n->get_arg(0)->get_th_var(get_id());
SASSERT(w != null_theory_var);
set_default(w, n);
}
}
}
unsigned theory_array_base::sel_hash::operator()(enode * n) const {
return get_composite_hash<enode *, sel_khasher, sel_chasher>(n, n->get_num_args() - 1, sel_khasher(), sel_chasher());
}
bool theory_array_base::sel_eq::operator()(enode * n1, enode * n2) const {
SASSERT(n1->get_num_args() == n2->get_num_args());
unsigned num_args = n1->get_num_args();
for (unsigned i = 1; i < num_args; i++) {
if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root())
return false;
}
return true;
}
theory_array_base::select_set * theory_array_base::get_select_set(enode * n) {
enode * r = n->get_root();
select_set * set = 0;
m_selects.find(r, set);
if (set == 0) {
set = alloc(select_set);
m_selects.insert(r, set);
m_selects_domain.push_back(r);
m_selects_range.push_back(set);
}
return set;
}
void theory_array_base::collect_selects() {
int num_vars = get_num_vars();
m_selects.reset();
m_selects_domain.reset();
m_selects_range.reset();
for (theory_var v = 0; v < num_vars; ++v) {
enode * r = get_enode(v)->get_root();
if (is_representative(v) && get_context().is_relevant(r)) {
enode_vector::iterator it = r->begin_parents();
enode_vector::iterator end = r->end_parents();
for (; it != end; ++it) {
enode * parent = *it;
if (parent->get_cg() == parent &&
get_context().is_relevant(parent) &&
is_select(parent) &&
parent->get_arg(0)->get_root() == r) {
select_set * s = get_select_set(r);
SASSERT(!s->contains(parent) || (*(s->find(parent)))->get_root() == parent->get_root());
s->insert(parent);
}
}
}
}
}
void theory_array_base::propagate_select_to_store_parents(enode * r, enode * sel, svector<enode_pair> & todo) {
SASSERT(r->get_root() == r);
SASSERT(is_select(sel));
if (!get_context().is_relevant(r)) {
return;
}
ptr_vector<enode>::const_iterator it = r->begin_parents();
ptr_vector<enode>::const_iterator end = r->end_parents();
for (; it != end; ++it) {
enode * parent = *it;
if (get_context().is_relevant(parent) &&
is_store(parent) &&
parent->get_arg(0)->get_root() == r) {
// propagate upward
select_set * parent_sel_set = get_select_set(parent);
enode * parent_root = parent->get_root();
if (parent_sel_set->contains(sel))
continue;
SASSERT(sel->get_num_args() + 1 == parent->get_num_args());
// check whether the sel idx was overwritten by the store
unsigned num_args = sel->get_num_args();
unsigned i = 1;
for (; i < num_args; i++) {
if (sel->get_arg(i)->get_root() != parent->get_arg(i)->get_root())
break;
}
if (i < num_args) {
SASSERT(!parent_sel_set->contains(sel) || (*(parent_sel_set->find(sel)))->get_root() == sel->get_root());
parent_sel_set->insert(sel);
todo.push_back(std::make_pair(parent_root, sel));
}
}
}
}
void theory_array_base::propagate_selects_to_store_parents(enode * r, svector<enode_pair> & todo) {
select_set * sel_set = get_select_set(r);
select_set::iterator it2 = sel_set->begin();
select_set::iterator end2 = sel_set->end();
for (; it2 != end2; ++it2) {
enode * sel = *it2;
SASSERT(is_select(sel));
propagate_select_to_store_parents(r, sel, todo);
}
}
void theory_array_base::propagate_selects() {
svector<enode_pair> todo;
ptr_vector<enode>::const_iterator it = m_selects_domain.begin();
ptr_vector<enode>::const_iterator end = m_selects_domain.end();
for (; it != end; ++it) {
enode * r = *it;
propagate_selects_to_store_parents(r, todo);
}
for (unsigned qhead = 0; qhead < todo.size(); qhead++) {
enode_pair & pair = todo[qhead];
enode * r = pair.first;
enode * sel = pair.second;
propagate_select_to_store_parents(r, sel, todo);
}
}
void theory_array_base::finalize_model(model_generator & m) {
std::for_each(m_selects_range.begin(), m_selects_range.end(), delete_proc<select_set>());
}
class array_value_proc : public model_value_proc {
family_id m_fid;
sort * m_sort;
unsigned m_num_entries;
unsigned m_dim; //!< number of dimensions;
app * m_else;
bool m_unspecified_else;
svector<model_value_dependency> m_dependencies;
public:
array_value_proc(family_id fid, sort * s, extra_fresh_value * v):
m_fid(fid),
m_sort(s),
m_num_entries(0),
m_dim(0),
m_else(0),
m_unspecified_else(false) {
m_dependencies.push_back(model_value_dependency(v));
}
array_value_proc(family_id fid, sort * s, app * else_value):
m_fid(fid),
m_sort(s),
m_num_entries(0),
m_dim(0),
m_else(else_value),
m_unspecified_else(false) {
}
array_value_proc(family_id fid, sort * s, enode * else_value):
m_fid(fid),
m_sort(s),
m_num_entries(0),
m_dim(0),
m_else(0),
m_unspecified_else(false) {
m_dependencies.push_back(model_value_dependency(else_value));
}
array_value_proc(family_id fid, sort * s):
m_fid(fid),
m_sort(s),
m_num_entries(0),
m_dim(0),
m_else(0),
m_unspecified_else(true) {
}
virtual ~array_value_proc() {}
void add_entry(unsigned num_args, enode * const * args, enode * value) {
SASSERT(num_args > 0);
SASSERT(m_dim == 0 || m_dim == num_args);
m_dim = num_args;
m_num_entries ++;
for (unsigned i = 0; i < num_args; i++)
m_dependencies.push_back(model_value_dependency(args[i]));
m_dependencies.push_back(model_value_dependency(value));
}
virtual void get_dependencies(buffer<model_value_dependency> & result) {
result.append(m_dependencies.size(), m_dependencies.c_ptr());
}
virtual app * mk_value(model_generator & mg, ptr_vector<expr> & values) {
// values must have size = m_num_entries * (m_dim + 1) + ((m_else || m_unspecified_else) ? 0 : 1)
// an array value is a lookup table + else_value
// each entry has m_dim indexes that map to a value.
ast_manager & m = mg.get_manager();
SASSERT(values.size() == m_dependencies.size());
SASSERT(values.size() == m_num_entries * (m_dim + 1) + ((m_else || m_unspecified_else) ? 0 : 1));
unsigned arity = get_array_arity(m_sort);
func_decl * f = mk_aux_decl_for_array_sort(m, m_sort);
func_interp * fi = alloc(func_interp, m, arity);
mg.get_model().register_decl(f, fi);
unsigned idx = 0;
if (m_else || m_unspecified_else) {
fi->set_else(m_else);
}
else {
fi->set_else(to_app(values[0]));
idx = 1;
}
ptr_buffer<expr> args;
for (unsigned i = 0; i < m_num_entries; i++) {
args.reset();
// copy indices
for (unsigned j = 0; j < m_dim; j++, idx++)
args.push_back(values[idx]);
expr * result = values[idx];
idx++;
fi->insert_entry(args.c_ptr(), result);
}
parameter p[1] = { parameter(f) };
return m.mk_app(m_fid, OP_AS_ARRAY, 1, p);
}
};
model_value_proc * theory_array_base::mk_value(enode * n, model_generator & m) {
SASSERT(get_context().is_relevant(n));
theory_var v = n->get_th_var(get_id());
SASSERT(v != null_theory_var);
sort * s = get_manager().get_sort(n->get_owner());
enode * else_val_n = get_default(v);
array_value_proc * result = 0;
if (m_use_unspecified_default) {
SASSERT(else_val_n == 0);
result = alloc(array_value_proc, get_id(), s);
}
else {
if (else_val_n != 0) {
SASSERT(get_context().is_relevant(else_val_n));
result = alloc(array_value_proc, get_id(), s, else_val_n);
}
else {
theory_var r = mg_find(v);
void * else_val = m_else_values[r];
// DISABLED. It seems wrong, since different nodes can share the same
// else_val according to the mg class.
// SASSERT(else_val == 0 || get_context().is_relevant(UNTAG(app*, else_val)));
if (else_val == 0) {
sort * range = to_sort(s->get_parameter(s->get_num_parameters() - 1).get_ast());
// IMPORTANT:
// The implementation should not assume a fresh value is created for
// the else_val if the range is finite
if (range->is_infinite())
else_val = TAG(void*, m.mk_extra_fresh_value(range), 1);
else
else_val = TAG(void*, m.get_some_value(range), 0);
m_else_values[r] = else_val;
}
if (GET_TAG(else_val) == 0) {
result = alloc(array_value_proc, get_id(), s, UNTAG(app*, else_val));
}
else {
result = alloc(array_value_proc, get_id(), s, UNTAG(extra_fresh_value*, else_val));
}
}
}
SASSERT(result != 0);
select_set * sel_set = 0;
m_selects.find(n->get_root(), sel_set);
if (sel_set != 0) {
ptr_buffer<enode> args;
select_set::iterator it = sel_set->begin();
select_set::iterator end = sel_set->end();
for (; it != end; ++it) {
enode * select = *it;
args.reset();
unsigned num = select->get_num_args();
for (unsigned j = 1; j < num; ++j)
args.push_back(select->get_arg(j));
SASSERT(get_context().is_relevant(select));
result->add_entry(args.size(), args.c_ptr(), select);
}
}
return result;
}
};

199
src/smt/theory_array_base.h Normal file
View file

@ -0,0 +1,199 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_array_base.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-02.
Revision History:
--*/
#ifndef _THEORY_ARRAY_BASE_H_
#define _THEORY_ARRAY_BASE_H_
#include"smt_theory.h"
#include"array_decl_plugin.h"
#include"array_factory.h"
namespace smt {
class theory_array_base : public theory {
protected:
bool m_found_unsupported_op;
void found_unsupported_op(expr * n);
bool is_store(app const* n) const { return n->is_app_of(get_id(), OP_STORE); }
bool is_map(app const* n) const { return n->is_app_of(get_id(), OP_ARRAY_MAP); }
bool is_select(app const* n) const { return n->is_app_of(get_id(), OP_SELECT); }
bool is_default(app const* n) const { return n->is_app_of(get_id(), OP_ARRAY_DEFAULT); }
bool is_const(app const* n) const { return n->is_app_of(get_id(), OP_CONST_ARRAY); }
bool is_array_ext(app const * n) const { return n->is_app_of(get_id(), OP_ARRAY_EXT_SKOLEM); }
bool is_as_array(app const * n) const { return n->is_app_of(get_id(), OP_AS_ARRAY); }
bool is_array_sort(sort const* s) const { return s->is_sort_of(get_id(), ARRAY_SORT); }
bool is_array_sort(app const* n) const { return is_array_sort(get_manager().get_sort(n)); }
bool is_store(enode const * n) const { return is_store(n->get_owner()); }
bool is_map(enode const* n) const { return is_map(n->get_owner()); }
bool is_select(enode const* n) const { return is_select(n->get_owner()); }
bool is_const(enode const* n) const { return is_const(n->get_owner()); }
bool is_as_array(enode const * n) const { return is_as_array(n->get_owner()); }
bool is_default(enode const* n) const { return is_default(n->get_owner()); }
bool is_array_sort(enode const* n) const { return is_array_sort(n->get_owner()); }
app * mk_select(unsigned num_args, expr * const * args);
app * mk_store(unsigned num_args, expr * const * args);
app * mk_default(expr* a);
unsigned get_dimension(sort* s) const;
ptr_vector<enode> m_axiom1_todo;
svector<std::pair<enode*, enode*> > m_axiom2_todo;
svector<std::pair<enode*, enode*> > m_extensionality_todo;
void assert_axiom(unsigned num_lits, literal * lits);
void assert_axiom(literal l1, literal l2);
void assert_axiom(literal l);
void assert_store_axiom1_core(enode * n);
void assert_store_axiom2_core(enode * store, enode * select);
void assert_store_axiom1(enode * n) { m_axiom1_todo.push_back(n); }
bool assert_store_axiom2(enode * store, enode * select);
void assert_extensionality_core(enode * a1, enode * a2);
bool assert_extensionality(enode * a1, enode * a2);
// --------------------------------------------------
// Array sort -> extensionality skolems
//
// --------------------------------------------------
ptr_vector<sort> m_sorts_trail;
obj_map<sort, func_decl_ref_vector*> m_sort2skolem;
func_decl_ref_vector * register_sort(sort * s_array);
// --------------------------------------------------
// array_value table
//
// Use select(A, i) nodes to represent an assignment for A.
// This structure is used to minimize the number of times the
// extensionality axiom is applied.
//
// --------------------------------------------------
struct value_chasher {
unsigned operator()(enode const * n, unsigned idx) const {
return n->get_arg(idx+1)->get_root()->hash();
}
};
struct value_khasher { unsigned operator()(enode * n) const { return 17; } };
struct value_hash_proc {
unsigned operator()(enode * n) const {
return get_composite_hash<enode *, value_khasher, value_chasher>(n, n->get_num_args() - 1);
}
};
struct value_eq_proc { bool operator()(enode * n1, enode * n2) const; };
typedef ptr_hashtable<enode, value_hash_proc, value_eq_proc> array_value;
array_value m_array_value;
bool already_diseq(enode * v1, enode * v2);
// --------------------------------------------------
// Backtracking
//
//
// --------------------------------------------------
struct scope {
unsigned m_sorts_trail_lim;
scope(unsigned l):m_sorts_trail_lim(l) {}
};
svector<scope> m_scopes;
void restore_sorts(unsigned old_size);
// --------------------------------------------------
// Interface
//
//
// --------------------------------------------------
virtual bool is_shared(theory_var v) const;
void collect_shared_vars(sbuffer<theory_var> & result);
unsigned mk_interface_eqs();
virtual bool can_propagate();
virtual void propagate();
virtual void push_scope_eh();
virtual void pop_scope_eh(unsigned num_scopes);
virtual void reset_eh();
void reset_queues();
// -----------------------------------
//
// Model generation
//
// -----------------------------------
// I need a set of select enodes where select(A,i) = select(B,j) if i->get_root() == j->get_root()
struct sel_khasher {
unsigned operator()(enode const * n) const { return 0; }
};
struct sel_chasher {
unsigned operator()(enode const * n, unsigned idx) const {
return n->get_arg(idx+1)->get_root()->hash();
}
};
struct sel_hash {
unsigned operator()(enode * n) const;
};
struct sel_eq {
bool operator()(enode * n1, enode * n2) const;
};
typedef ptr_hashtable<enode, sel_hash, sel_eq> select_set;
array_factory * m_factory;
ptr_vector<enode> m_defaults; // temporary field for model construction
ptr_vector<void> m_else_values; // tagged pointer: expr or extra_fresh_value
svector<int> m_parents; // temporary field for model construction
obj_map<enode, select_set*> m_selects; // mapping from array -> relevant selects
ptr_vector<enode> m_selects_domain;
ptr_vector<select_set> m_selects_range;
bool m_use_unspecified_default; // temporary field for model construction
theory_var mg_find(theory_var v);
void mg_merge(theory_var n, theory_var m);
void set_default(theory_var v, enode* n);
enode* get_default(theory_var v);
virtual void init_model(model_generator & m);
bool is_unspecified_default_ok() const;
void collect_defaults();
void collect_selects();
void propagate_select_to_store_parents(enode * r, enode * sel, svector<enode_pair> & todo);
void propagate_selects_to_store_parents(enode * r, svector<enode_pair> & todo);
void propagate_selects();
select_set * get_select_set(enode * n);
virtual void finalize_model(model_generator & m);
virtual model_value_proc * mk_value(enode * n, model_generator & m);
public:
theory_array_base(ast_manager & m);
virtual ~theory_array_base() { restore_sorts(0); }
};
};
#endif /* _THEORY_ARRAY_BASE_H_ */

View file

@ -0,0 +1,812 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_array_full.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner 2008-22-10
Revision History:
--*/
#include "smt_context.h"
#include "theory_array_full.h"
#include "ast_ll_pp.h"
#include "ast_pp.h"
#include "ast_smt2_pp.h"
#include "stats.h"
namespace smt {
theory_array_full::theory_array_full(ast_manager & m, theory_array_params & params) :
theory_array(m, params),
m_sort2epsilon(m) {}
theory_array_full::~theory_array_full() {
std::for_each(m_var_data_full.begin(), m_var_data_full.end(), delete_proc<var_data_full>());
m_var_data_full.reset();
}
void theory_array_full::add_map(theory_var v, enode* s) {
if (m_params.m_array_cg && !s->is_cgr()) {
return;
}
SASSERT(is_map(s));
v = find(v);
var_data_full * d_full = m_var_data_full[v];
var_data * d = m_var_data[v];
//
// TODO: defaulting to exhaustive up-propagation.
// instead apply stratified filter.
set_prop_upward(v,d);
d_full->m_maps.push_back(s);
m_trail_stack.push(push_back_trail<theory_array, enode *, false>(d_full->m_maps));
ptr_vector<enode>::iterator it = d->m_parent_selects.begin();
ptr_vector<enode>::iterator end = d->m_parent_selects.end();
for (; it != end; ++it) {
SASSERT(is_select(*it));
instantiate_select_map_axiom(*it, s);
}
set_prop_upward(s);
}
bool theory_array_full::instantiate_axiom_map_for(theory_var v) {
bool result = false;
var_data * d = m_var_data[v];
var_data_full * d_full = m_var_data_full[v];
unsigned num_maps = d_full->m_parent_maps.size();
unsigned num_selects = d->m_parent_selects.size();
for (unsigned i = 0; i < num_maps; ++i) {
for (unsigned j = 0; j < num_selects; ++j) {
if (instantiate_select_map_axiom(d->m_parent_selects[j], d_full->m_parent_maps[i])) {
result = true;
}
}
}
return result;
}
void theory_array_full::add_parent_map(theory_var v, enode* s) {
if (m_params.m_array_cg && !s->is_cgr()) {
return;
}
SASSERT(v != null_theory_var);
SASSERT(is_map(s));
v = find(v);
var_data * d = m_var_data[v];
var_data_full * d_full = m_var_data_full[v];
d_full->m_parent_maps.push_back(s);
m_trail_stack.push(push_back_trail<theory_array, enode *, false>(d_full->m_parent_maps));
if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
ptr_vector<enode>::iterator it = d->m_parent_selects.begin();
ptr_vector<enode>::iterator end = d->m_parent_selects.end();
for (; it != end; ++it) {
if (!m_params.m_array_cg || (*it)->is_cgr()) {
instantiate_select_map_axiom(*it, s);
}
}
}
}
//
// set set_prop_upward on root and recursively on children if necessary.
//
void theory_array_full::set_prop_upward(theory_var v) {
if (m_params.m_array_weak)
return;
v = find(v);
var_data * d = m_var_data[v];
if (!d->m_prop_upward) {
m_trail_stack.push(reset_flag_trail<theory_array>(d->m_prop_upward));
d->m_prop_upward = true;
TRACE("array", tout << "#" << v << "\n";);
if (!m_params.m_array_delay_exp_axiom) {
instantiate_axiom2b_for(v);
instantiate_axiom_map_for(v);
}
var_data_full * d2 = m_var_data_full[v];
ptr_vector<enode>::iterator it = d->m_stores.begin();
ptr_vector<enode>::iterator end = d->m_stores.end();
for (; it != end; ++it) {
set_prop_upward(*it);
}
it = d2->m_maps.begin();
end = d2->m_maps.end();
for (; it != end; ++it) {
set_prop_upward(*it);
}
it = d2->m_consts.begin();
end = d2->m_consts.end();
for (; it != end; ++it) {
set_prop_upward(*it);
}
}
}
//
// call set_prop_upward on array arguments.
//
void theory_array_full::set_prop_upward(enode * n) {
TRACE("array", tout << mk_pp(n->get_owner(), get_manager()) << "\n";);
if (is_store(n)) {
set_prop_upward(n->get_arg(0)->get_th_var(get_id()));
}
else if (is_map(n)) {
for (unsigned i = 0; i < n->get_num_args(); ++i) {
set_prop_upward(n->get_arg(i)->get_th_var(get_id()));
}
}
}
void theory_array_full::set_prop_upward(theory_var v, var_data* d) {
if (m_params.m_array_always_prop_upward || d->m_stores.size() >= 1) {
theory_array::set_prop_upward(v, d);
}
else {
var_data_full * d2 = m_var_data_full[v];
unsigned sz = d2->m_maps.size();
for(unsigned i = 0; i < sz; ++i) {
set_prop_upward(d2->m_maps[i]);
}
}
}
unsigned theory_array_full::get_lambda_equiv_size(theory_var v, var_data* d) {
var_data_full * d2 = m_var_data_full[v];
return d->m_stores.size() + 2*d2->m_consts.size() + 2*d2->m_maps.size();
}
void theory_array_full::add_const(theory_var v, enode* cnst) {
var_data * d = m_var_data[v];
unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d);
if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) {
set_prop_upward(v, d);
}
ptr_vector<enode> & consts = m_var_data_full[v]->m_consts;
m_trail_stack.push(push_back_trail<theory_array, enode *, false>(consts));
consts.push_back(cnst);
instantiate_default_const_axiom(cnst);
ptr_vector<enode>::iterator it = d->m_parent_selects.begin();
ptr_vector<enode>::iterator end = d->m_parent_selects.end();
for (; it != end; ++it) {
SASSERT(is_select(*it));
instantiate_select_const_axiom(*it, cnst);
}
}
void theory_array_full::add_as_array(theory_var v, enode* arr) {
var_data * d = m_var_data[v];
unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d);
if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) {
set_prop_upward(v, d);
}
ptr_vector<enode> & as_arrays = m_var_data_full[v]->m_as_arrays;
m_trail_stack.push(push_back_trail<theory_array, enode *, false>(as_arrays));
as_arrays.push_back(arr);
instantiate_default_as_array_axiom(arr);
ptr_vector<enode>::iterator it = d->m_parent_selects.begin();
ptr_vector<enode>::iterator end = d->m_parent_selects.end();
for (; it != end; ++it) {
SASSERT(is_select(*it));
instantiate_select_as_array_axiom(*it, arr);
}
}
void theory_array_full::reset_eh() {
theory_array::reset_eh();
std::for_each(m_var_data_full.begin(), m_var_data_full.end(), delete_proc<var_data_full>());
m_var_data_full.reset();
}
void theory_array_full::display_var(std::ostream & out, theory_var v) const {
theory_array::display_var(out, v);
var_data_full const * d = m_var_data_full[v];
out << " maps: {";
display_ids(out, d->m_maps.size(), d->m_maps.c_ptr());
out << "} p_parent_maps: {";
display_ids(out, d->m_parent_maps.size(), d->m_parent_maps.c_ptr());
out << "} p_const: {";
display_ids(out, d->m_consts.size(), d->m_consts.c_ptr());
out << "}\n";
}
theory_var theory_array_full::mk_var(enode * n) {
theory_var r = theory_array::mk_var(n);
SASSERT(r == static_cast<int>(m_var_data_full.size()));
m_var_data_full.push_back(alloc(var_data_full));
var_data_full * d = m_var_data_full.back();
if (is_map(n)) {
instantiate_default_map_axiom(n);
d->m_maps.push_back(n);
}
else if (is_const(n)) {
instantiate_default_const_axiom(n);
d->m_consts.push_back(n);
}
else if (is_default(n)) {
// no-op
}
else if (is_as_array(n)) {
instantiate_default_as_array_axiom(n);
d->m_as_arrays.push_back(n);
}
return r;
}
bool theory_array_full::internalize_atom(app * atom, bool) {
return internalize_term(atom);
}
bool theory_array_full::internalize_term(app * n) {
TRACE("array", tout << mk_pp(n, get_manager()) << "\n";);
if (is_store(n) || is_select(n)) {
return theory_array::internalize_term(n);
}
if (!is_const(n) && !is_default(n) && !is_map(n) && !is_as_array(n)) {
if (!is_array_ext(n))
found_unsupported_op(n);
return false;
}
if (!internalize_term_core(n)) {
return true;
}
context & ctx = get_context();
if (is_map(n)) {
for (unsigned i = 0; i < n->get_num_args(); ++i) {
enode* arg = ctx.get_enode(n->get_arg(i));
if (!is_attached_to_var(arg)) {
mk_var(arg);
}
}
}
else if (is_default(n)) {
enode* arg0 = ctx.get_enode(n->get_arg(0));
if (!is_attached_to_var(arg0)) {
mk_var(arg0);
}
}
enode* node = ctx.get_enode(n);
if (!is_attached_to_var(node)) {
mk_var(node);
}
if (is_default(n)) {
enode* arg0 = ctx.get_enode(n->get_arg(0));
theory_var v_arg = arg0->get_th_var(get_id());
add_parent_default(v_arg);
}
else if (is_map(n)) {
for (unsigned i = 0; i < n->get_num_args(); ++i) {
enode* arg = ctx.get_enode(n->get_arg(i));
theory_var v_arg = arg->get_th_var(get_id());
add_parent_map(v_arg, node);
}
instantiate_default_map_axiom(node);
}
else if (is_const(n)) {
instantiate_default_const_axiom(node);
}
else if (is_as_array(n)) {
// The array theory is not a decision procedure
// for as-array.
// Ex: (as-array f) = (as-array g) & f(0) = 0 & g(0) = 1
// There is nothing to propagate the disequality.
// Even if there was, as-array on interpreted
// functions will be incomplete.
// The instantiation operations are still sound to include.
found_unsupported_op(n);
instantiate_default_as_array_axiom(node);
}
return true;
}
void theory_array_full::merge_eh(theory_var v1, theory_var v2, theory_var u, theory_var w) {
theory_array::merge_eh(v1, v2, u, w);
// v1 is the new root
SASSERT(v1 == find(v1));
var_data_full * d2 = m_var_data_full[v2];
ptr_vector<enode>::iterator it, end;
it = d2->m_maps.begin();
end = d2->m_maps.end();
for (; it != end; ++it) {
add_map(v1, *it);
}
it = d2->m_parent_maps.begin();
end = d2->m_parent_maps.end();
for (; it != end; ++it) {
add_parent_map(v1, *it);
}
it = d2->m_consts.begin();
end = d2->m_consts.end();
for (; it != end; ++it) {
add_const(v1, *it);
}
it = d2->m_as_arrays.begin();
end = d2->m_as_arrays.end();
for (; it != end; ++it) {
add_as_array(v1, *it);
}
TRACE("array",
tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n";
tout << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";
tout << "merge in\n"; display_var(tout, v2);
tout << "after merge\n"; display_var(tout, v1););
}
void theory_array_full::add_parent_default(theory_var v) {
SASSERT(v != null_theory_var);
v = find(v);
var_data* d = m_var_data[v];
ptr_vector<enode>::iterator it, end;
it = d->m_stores.begin();
end = d->m_stores.end();
for(; it != end; ++it) {
enode * store = *it;
SASSERT(is_store(store));
instantiate_default_store_axiom(store);
}
if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
it = d->m_parent_stores.begin();
end = d->m_parent_stores.end();
for (; it != end; ++it) {
enode* store = *it;
SASSERT(is_store(store));
if (!m_params.m_array_cg || store->is_cgr()) {
instantiate_default_store_axiom(store);
}
}
}
}
void theory_array_full::add_parent_select(theory_var v, enode * s) {
TRACE("array",
tout << v << " select parent: " << mk_pp(s->get_owner(), get_manager()) << "\n";
display_var(tout, v);
);
theory_array::add_parent_select(v,s);
v = find(v);
var_data_full* d_full = m_var_data_full[v];
var_data* d = m_var_data[v];
ptr_vector<enode>::iterator it = d_full->m_consts.begin();
ptr_vector<enode>::iterator end = d_full->m_consts.end();
for (; it != end; ++it) {
instantiate_select_const_axiom(s, *it);
}
it = d_full->m_maps.begin();
end = d_full->m_maps.end();
for (; it != end; ++it) {
enode* map = *it;
SASSERT(is_map(map));
instantiate_select_map_axiom(s, map);
}
if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
it = d_full->m_parent_maps.begin();
end = d_full->m_parent_maps.end();
for (; it != end; ++it) {
enode* map = *it;
SASSERT(is_map(map));
if (!m_params.m_array_cg || map->is_cgr()) {
instantiate_select_map_axiom(s, map);
}
}
}
}
void theory_array_full::relevant_eh(app* n) {
TRACE("array", tout << mk_pp(n, get_manager()) << "\n";);
theory_array::relevant_eh(n);
if (!is_default(n) && !is_select(n) && !is_map(n) && !is_const(n) && !is_as_array(n)) {
return;
}
context & ctx = get_context();
enode* node = ctx.get_enode(n);
if (is_select(n)) {
enode * arg = ctx.get_enode(n->get_arg(0));
theory_var v = arg->get_th_var(get_id());
SASSERT(v != null_theory_var);
add_parent_select(find(v), node);
}
else if (is_default(n)) {
enode * arg = ctx.get_enode(n->get_arg(0));
theory_var v = arg->get_th_var(get_id());
SASSERT(v != null_theory_var);
add_parent_default(find(v));
}
else if (is_const(n)) {
instantiate_default_const_axiom(node);
}
else if (is_map(n)) {
for (unsigned i = 0; i < n->get_num_args(); ++i) {
enode* arg = ctx.get_enode(n->get_arg(i));
theory_var v_arg = find(arg->get_th_var(get_id()));
add_parent_map(v_arg, node);
set_prop_upward(v_arg);
}
instantiate_default_map_axiom(node);
}
else if (is_as_array(n)) {
instantiate_default_as_array_axiom(node);
}
}
//
// Assert axiom:
// select(map[f](a, ... d), i) = f(select(a,i),...,select(d,i))
//
bool theory_array_full::instantiate_select_map_axiom(enode* sl, enode* mp) {
app* map = mp->get_owner();
app* select = sl->get_owner();
SASSERT(is_map(map));
SASSERT(is_select(select));
SASSERT(map->get_num_args() > 0);
func_decl* f = to_func_decl(map->get_decl()->get_parameter(0).get_ast());
context& ctx = get_context();
ast_manager& m = get_manager();
TRACE("array_map_bug", tout << "invoked instantiate_select_map_axiom\n";
tout << sl->get_owner_id() << " " << mp->get_owner_id() << "\n";
tout << mk_ismt2_pp(sl->get_owner(), m) << "\n" << mk_ismt2_pp(mp->get_owner(), m) << "\n";);
if (!ctx.add_fingerprint(mp, mp->get_owner_id(), sl->get_num_args() - 1, sl->get_args() + 1)) {
return false;
}
TRACE("array_map_bug", tout << "new axiom\n";);
m_stats.m_num_map_axiom++;
TRACE("array",
tout << mk_bounded_pp(mp->get_owner(), get_manager()) << "\n";
tout << mk_bounded_pp(sl->get_owner(), get_manager()) << "\n";);
unsigned num_args = select->get_num_args();
unsigned num_arrays = map->get_num_args();
ptr_buffer<expr> args1, args2;
vector<ptr_vector<expr> > args2l;
args1.push_back(map);
for (unsigned j = 0; j < num_arrays; ++j) {
ptr_vector<expr> arg;
arg.push_back(map->get_arg(j));
args2l.push_back(arg);
}
for (unsigned i = 1; i < num_args; ++i) {
expr* arg = select->get_arg(i);
for (unsigned j = 0; j < num_arrays; ++j) {
args2l[j].push_back(arg);
}
args1.push_back(arg);
}
for (unsigned j = 0; j < num_arrays; ++j) {
expr* sel = mk_select(args2l[j].size(), args2l[j].c_ptr());
args2.push_back(sel);
}
expr_ref sel1(m), sel2(m);
sel1 = mk_select(args1.size(), args1.c_ptr());
m_simp->mk_app(f, args2.size(), args2.c_ptr(), sel2);
ctx.internalize(sel1, false);
ctx.internalize(sel2, false);
TRACE("array_map_bug",
tout << "select-map axiom\n" << mk_ismt2_pp(sel1, m) << "\n=\n" << mk_ismt2_pp(sel2,m) << "\n";);
return try_assign_eq(sel1, sel2);
}
//
//
// Assert axiom:
// default(map[f](a,..,d)) = f(default(a),..,default(d))
//
bool theory_array_full::instantiate_default_map_axiom(enode* mp) {
SASSERT(is_map(mp));
app* map = mp->get_owner();
context& ctx = get_context();
if (!ctx.add_fingerprint(this, 0, 1, &mp)) {
return false;
}
TRACE("array", tout << mk_bounded_pp(map, get_manager()) << "\n";);
m_stats.m_num_default_map_axiom++;
func_decl* f = to_func_decl(map->get_decl()->get_parameter(0).get_ast());
SASSERT(map->get_num_args() == f->get_arity());
ptr_buffer<expr> args2;
for (unsigned i = 0; i < map->get_num_args(); ++i) {
args2.push_back(mk_default(map->get_arg(i)));
}
expr* def1 = mk_default(map);
expr_ref def2(get_manager());
m_simp->mk_app(f, args2.size(), args2.c_ptr(), def2);
ctx.internalize(def1, false);
ctx.internalize(def2, false);
return try_assign_eq(def1, def2);
}
bool theory_array_full::instantiate_default_const_axiom(enode* cnst) {
context& ctx = get_context();
if (!ctx.add_fingerprint(this, 0, 1, &cnst)) {
return false;
}
m_stats.m_num_default_const_axiom++;
SASSERT(is_const(cnst));
TRACE("array", tout << mk_bounded_pp(cnst->get_owner(), get_manager()) << "\n";);
expr* val = cnst->get_arg(0)->get_owner();
expr* def = mk_default(cnst->get_owner());
ctx.internalize(def, false);
return try_assign_eq(val, def);
}
bool theory_array_full::instantiate_default_as_array_axiom(enode* arr) {
context& ctx = get_context();
if (!ctx.add_fingerprint(this, 0, 1, &arr)) {
return false;
}
m_stats.m_num_default_as_array_axiom++;
SASSERT(is_as_array(arr));
TRACE("array", tout << mk_bounded_pp(arr->get_owner(), get_manager()) << "\n";);
expr* def = mk_default(arr->get_owner());
func_decl * f = array_util(get_manager()).get_as_array_func_decl(arr->get_owner());
ptr_vector<expr> args;
for (unsigned i = 0; i < f->get_arity(); ++i) {
args.push_back(mk_epsilon(f->get_domain(i)));
}
expr_ref val(get_manager().mk_app(f, args.size(), args.c_ptr()), get_manager());
ctx.internalize(def, false);
ctx.internalize(val.get(), false);
return try_assign_eq(val.get(), def);
}
bool theory_array_full::has_large_domain(app* array_term) {
SASSERT(is_array_sort(array_term));
sort* s = get_manager().get_sort(array_term);
unsigned dim = get_dimension(s);
parameter const * params = s->get_info()->get_parameters();
rational sz(1);
for (unsigned i = 0; i < dim; ++i) {
SASSERT(params[i].is_ast());
sort* d = to_sort(params[i].get_ast());
if (d->is_infinite() || d->is_very_big()) {
return true;
}
sz *= rational(d->get_num_elements().size(),rational::ui64());
if (sz >= rational(1 << 20)) {
return true;
}
}
return false;
}
//
// Assert axiom:
// select(const v, i_1, ..., i_n) = v
//
bool theory_array_full::instantiate_select_const_axiom(enode* select, enode* cnst) {
SASSERT(is_const(cnst));
SASSERT(is_select(select));
SASSERT(cnst->get_num_args() == 1);
context& ctx = get_context();
unsigned num_args = select->get_num_args();
if (!ctx.add_fingerprint(cnst, cnst->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) {
return false;
}
m_stats.m_num_select_const_axiom++;
ptr_buffer<expr> sel_args;
sel_args.push_back(cnst->get_owner());
for (unsigned short i = 1; i < num_args; ++i) {
sel_args.push_back(select->get_owner()->get_arg(i));
}
expr * sel = mk_select(sel_args.size(), sel_args.c_ptr());
expr * val = cnst->get_owner()->get_arg(0);
TRACE("array", tout << "new select-const axiom...\n";
tout << "const: " << mk_bounded_pp(cnst->get_owner(), get_manager()) << "\n";
tout << "select: " << mk_bounded_pp(select->get_owner(), get_manager()) << "\n";
tout << " sel/const: " << mk_bounded_pp(sel, get_manager()) << "\n";
tout << "value: " << mk_bounded_pp(val, get_manager()) << "\n";
tout << "#" << sel->get_id() << " = #" << val->get_id() << "\n";
);
ctx.internalize(sel, false);
return try_assign_eq(sel,val);
}
//
// Assert axiom:
// select(as-array f, i_1, ..., i_n) = (f i_1 ... i_n)
//
bool theory_array_full::instantiate_select_as_array_axiom(enode* select, enode* arr) {
SASSERT(is_as_array(arr->get_owner()));
SASSERT(is_select(select));
SASSERT(arr->get_num_args() == 0);
context& ctx = get_context();
unsigned num_args = select->get_num_args();
if (!ctx.add_fingerprint(arr, arr->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) {
return false;
}
m_stats.m_num_select_as_array_axiom++;
ptr_buffer<expr> sel_args;
sel_args.push_back(arr->get_owner());
for (unsigned short i = 1; i < num_args; ++i) {
sel_args.push_back(select->get_owner()->get_arg(i));
}
expr * sel = mk_select(sel_args.size(), sel_args.c_ptr());
func_decl * f = array_util(get_manager()).get_as_array_func_decl(arr->get_owner());
expr_ref val(get_manager().mk_app(f, sel_args.size()-1, sel_args.c_ptr()+1), get_manager());
TRACE("array", tout << "new select-as-array axiom...\n";
tout << "as-array: " << mk_bounded_pp(arr->get_owner(), get_manager()) << "\n";
tout << "select: " << mk_bounded_pp(select->get_owner(), get_manager()) << "\n";
tout << " sel/as-array: " << mk_bounded_pp(sel, get_manager()) << "\n";
tout << "value: " << mk_bounded_pp(val.get(), get_manager()) << "\n";
tout << "#" << sel->get_id() << " = #" << val->get_id() << "\n";
);
ctx.internalize(sel, false);
ctx.internalize(val.get(), false);
return try_assign_eq(sel,val);
}
bool theory_array_full::instantiate_default_store_axiom(enode* store) {
SASSERT(is_store(store));
SASSERT(store->get_num_args() >= 3);
app* store_app = store->get_owner();
context& ctx = get_context();
ast_manager& m = get_manager();
if (!ctx.add_fingerprint(this, 0, 1, &store)) {
return false;
}
m_stats.m_num_default_store_axiom++;
app* def1;
app* def2;
TRACE("array", tout << mk_bounded_pp(store_app, m) << "\n";);
if (has_large_domain(store_app)) {
def2 = mk_default(store_app->get_arg(0));
}
else {
//
// let A = store(B, i, v)
//
// Add:
// default(A) = ite(epsilon = i, v, default(B))
//
expr_ref_vector eqs(m);
unsigned num_args = store_app->get_num_args();
for (unsigned i = 1; i + 1 < num_args; ++i) {
sort* srt = m.get_sort(store_app->get_arg(i));
app* ep = mk_epsilon(srt);
eqs.push_back(m.mk_eq(ep, store_app->get_arg(i)));
}
expr_ref eq(m);
simplifier_plugin* p = m_simp->get_plugin(m.get_basic_family_id());
basic_simplifier_plugin* bp = static_cast<basic_simplifier_plugin*>(p);
bp->mk_and(eqs.size(), eqs.c_ptr(), eq);
expr* defA = mk_default(store_app->get_arg(0));
def2 = m.mk_ite(eq, store_app->get_arg(num_args-1), defA);
#if 0
//
// add soft constraints to guide model construction so that
// epsilon agrees with the else case in the model construction.
//
for (unsigned i = 0; i < eqs.size(); ++i) {
// assume_diseq(eqs[i]);
}
#endif
}
def1 = mk_default(store_app);
ctx.internalize(def1, false);
ctx.internalize(def2, false);
return try_assign_eq(def1, def2);
}
app* theory_array_full::mk_epsilon(sort* s) {
app* eps = 0;
if (m_sort2epsilon.find(s, eps)) {
return eps;
}
eps = get_manager().mk_fresh_const("epsilon", s);
m_trail_stack.push(
ast2ast_trail<theory_array, sort, app>(m_sort2epsilon, s, eps));
return eps;
}
final_check_status theory_array_full::assert_delayed_axioms() {
final_check_status r = FC_DONE;
if (!m_params.m_array_delay_exp_axiom) {
r = FC_DONE;
}
else {
r = theory_array::assert_delayed_axioms();
unsigned num_vars = get_num_vars();
for (unsigned v = 0; v < num_vars; v++) {
var_data * d = m_var_data[v];
if (d->m_prop_upward && instantiate_axiom_map_for(v))
r = FC_CONTINUE;
}
}
if (r == FC_DONE && m_found_unsupported_op)
r = FC_GIVEUP;
return r;
}
bool theory_array_full::try_assign_eq(expr* v1, expr* v2) {
context& ctx = get_context();
enode* n1 = ctx.get_enode(v1);
enode* n2 = ctx.get_enode(v2);
if (n1->get_root() == n2->get_root()) {
return false;
}
TRACE("array",
tout << mk_bounded_pp(v1, get_manager()) << "\n==\n"
<< mk_bounded_pp(v2, get_manager()) << "\n";);
#if 0
if (m.proofs_enabled()) {
#endif
literal eq(mk_eq(v1,v2,true));
ctx.mark_as_relevant(eq);
assert_axiom(eq);
#if 0
}
else {
ctx.mark_as_relevant(n1);
ctx.mark_as_relevant(n2);
ctx.assign_eq(n1, n2, eq_justification::mk_axiom());
}
#endif
return true;
}
void theory_array_full::pop_scope_eh(unsigned num_scopes) {
unsigned num_old_vars = get_old_num_vars(num_scopes);
theory_array::pop_scope_eh(num_scopes);
std::for_each(m_var_data_full.begin() + num_old_vars, m_var_data_full.end(), delete_proc<var_data_full>());
m_var_data_full.shrink(num_old_vars);
}
void theory_array_full::collect_statistics(::statistics & st) const {
theory_array::collect_statistics(st);
st.update("array map ax", m_stats.m_num_map_axiom);
st.update("array def const", m_stats.m_num_default_const_axiom);
st.update("array sel const", m_stats.m_num_select_const_axiom);
st.update("array def store", m_stats.m_num_default_store_axiom);
st.update("array def as-array", m_stats.m_num_default_as_array_axiom);
st.update("array sel as-array", m_stats.m_num_select_as_array_axiom);
}
}

109
src/smt/theory_array_full.h Normal file
View file

@ -0,0 +1,109 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_array_full.h
Abstract:
<abstract>
Author:
Nikolaj Bjorner 2008-22-10
Revision History:
--*/
#ifndef _THEORY_ARRAY_FULL_H_
#define _THEORY_ARRAY_FULL_H_
#include"theory_array.h"
#include "simplifier.h"
namespace smt {
class theory_array_full : public theory_array {
struct var_data_full {
ptr_vector<enode> m_maps;
ptr_vector<enode> m_consts;
ptr_vector<enode> m_as_arrays;
ptr_vector<enode> m_parent_maps;
var_data_full() {}
};
ptr_vector<var_data_full> m_var_data_full;
ast2ast_trailmap<sort,app> m_sort2epsilon;
simplifier* m_simp;
protected:
#if 0
virtual final_check_status final_check_eh();
#endif
virtual void reset_eh();
virtual void set_prop_upward(theory_var v);
virtual void set_prop_upward(enode* n);
virtual void set_prop_upward(theory_var v, var_data* d);
virtual unsigned get_lambda_equiv_size(theory_var v, var_data* d);
virtual bool internalize_term(app * term);
virtual bool internalize_atom(app * atom, bool gate_ctx);
virtual void pop_scope_eh(unsigned num_scopes);
virtual theory_var mk_var(enode * n);
virtual void relevant_eh(app * n);
void add_const(theory_var v, enode* c);
void add_map(theory_var v, enode* s);
void add_parent_map(theory_var v, enode* s);
void add_as_array(theory_var v, enode* arr);
virtual void add_parent_select(theory_var v, enode * s);
void add_parent_default(theory_var v);
virtual final_check_status assert_delayed_axioms();
bool instantiate_default_const_axiom(enode* cnst);
bool instantiate_default_store_axiom(enode* store);
bool instantiate_default_map_axiom(enode* map);
bool instantiate_default_as_array_axiom(enode* arr);
bool has_large_domain(app* array_term);
app* mk_epsilon(sort* s);
bool instantiate_select_const_axiom(enode* select, enode* cnst);
bool instantiate_select_as_array_axiom(enode* select, enode* arr);
bool instantiate_select_map_axiom(enode* select, enode* map);
bool instantiate_axiom_map_for(theory_var v);
bool try_assign_eq(expr* n1, expr* n2);
public:
theory_array_full(ast_manager & m, theory_array_params & params);
virtual ~theory_array_full();
virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_array_full, get_manager(), m_params); }
virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var);
virtual void display_var(std::ostream & out, theory_var v) const;
virtual void collect_statistics(::statistics & st) const;
virtual void init(context* ctx) {
// the parent class is theory_array.
// theory::init(ctx);
theory_array::init(ctx);
m_simp = &ctx->get_simplifier();
}
};
};
#endif /* _THEORY_ARRAY_H_ */

1635
src/smt/theory_bv.cpp Normal file

File diff suppressed because it is too large Load diff

282
src/smt/theory_bv.h Normal file
View file

@ -0,0 +1,282 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_bv.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-03.
Revision History:
--*/
#ifndef _THEORY_BV_H_
#define _THEORY_BV_H_
#include"smt_theory.h"
#include"theory_bv_params.h"
#include"bit_blaster.h"
#include"trail.h"
#include"union_find.h"
#include"simplifier.h"
#include"bv_simplifier_plugin.h"
#include"arith_decl_plugin.h"
#include"arith_simplifier_plugin.h"
#include"numeral_factory.h"
namespace smt {
struct theory_bv_stats {
unsigned m_num_diseq_static, m_num_diseq_dynamic, m_num_bit2core, m_num_th2core_eq, m_num_conflicts;
void reset() { memset(this, 0, sizeof(theory_bv_stats)); }
theory_bv_stats() { reset(); }
};
class theory_bv : public theory {
typedef rational numeral;
typedef trail_stack<theory_bv> th_trail_stack;
typedef union_find<theory_bv> th_union_find;
typedef std::pair<theory_var, unsigned> var_pos;
class atom {
public:
virtual ~atom() {}
virtual bool is_bit() const = 0;
};
struct var_pos_occ {
theory_var m_var;
unsigned m_idx;
var_pos_occ * m_next;
var_pos_occ(theory_var v = null_theory_var, unsigned idx = 0, var_pos_occ * next = 0):m_var(v), m_idx(idx), m_next(next) {}
};
struct bit_atom : public atom {
var_pos_occ * m_occs;
bit_atom():m_occs(0) {}
virtual ~bit_atom() {}
virtual bool is_bit() const { return true; }
};
struct le_atom : public atom {
literal m_var;
literal m_def;
le_atom(literal v, literal d):m_var(v), m_def(d) {}
virtual ~le_atom() {}
virtual bool is_bit() const { return false; }
};
/**
\brief Structure used to store the position of a bitvector variable that
contains the true_literal/false_literal.
Remark: the implementation assumes that bitvector variables containing
complementary bits are never merged. I assert a disequality (not (= x y))
whenever x and y contain complementary bits. However, this is too expensive
when the bit is the true_literal or false_literal. The number of disequalities
is too big. To avoid this problem, each equivalence class has a set
of its true_literal and false_literal bits in the form of svector<zero_one_bit>.
Before merging two classes we just check if the merge is valid by traversing these
vectors.
*/
struct zero_one_bit {
theory_var m_owner; //!< variable that owns the bit: useful for backtracking
unsigned m_idx:31;
unsigned m_is_true:1;
zero_one_bit(theory_var v = null_theory_var, unsigned idx = UINT_MAX, bool is_true = false):
m_owner(v), m_idx(idx), m_is_true(is_true) {}
};
typedef svector<zero_one_bit> zero_one_bits;
#ifdef SPARSE_MAP
typedef u_map<atom *> bool_var2atom;
void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.insert(bv, a); }
void erase_bv2a(bool_var bv) { m_bool_var2atom.erase(bv); }
atom * get_bv2a(bool_var bv) const { atom * a; m_bool_var2atom.find(bv, a); return a; }
#else
typedef ptr_vector<atom> bool_var2atom;
void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); }
void erase_bv2a(bool_var bv) { m_bool_var2atom[bv] = 0; }
atom * get_bv2a(bool_var bv) const { return m_bool_var2atom.get(bv, 0); }
#endif
theory_bv_stats m_stats;
theory_bv_params const & m_params;
bv_util m_util;
arith_util m_autil;
simplifier * m_simplifier;
bit_blaster m_bb;
th_trail_stack m_trail_stack;
th_union_find m_find;
vector<literal_vector> m_bits; // per var, the bits of a given variable.
svector<unsigned> m_wpos; // per var, watch position for fixed variable detection.
vector<zero_one_bits> m_zero_one_bits; // per var, see comment in the struct zero_one_bit
bool_var2atom m_bool_var2atom;
typedef svector<theory_var> vars;
typedef std::pair<numeral, unsigned> value_sort_pair;
typedef pair_hash<obj_hash<numeral>, unsigned_hash> value_sort_pair_hash;
typedef map<value_sort_pair, theory_var, value_sort_pair_hash, default_eq<value_sort_pair> > value2var;
value2var m_fixed_var_table;
literal_vector m_tmp_literals;
svector<var_pos> m_prop_queue;
bool m_approximates_large_bvs;
theory_var find(theory_var v) const { return m_find.find(v); }
theory_var next(theory_var v) const { return m_find.next(v); }
bool is_root(theory_var v) const { return m_find.is_root(v); }
unsigned get_bv_size(app const * n) const { return m_util.get_bv_size(n); }
unsigned get_bv_size(enode const * n) const { return m_util.get_bv_size(n->get_owner()); }
unsigned get_bv_size(theory_var v) const { return get_bv_size(get_enode(v)); }
bool is_bv(app const* n) const { return m_util.is_bv_sort(get_manager().get_sort(n)); }
bool is_bv(enode const* n) const { return is_bv(n->get_owner()); }
bool is_bv(theory_var v) const { return is_bv(get_enode(v)); }
region & get_region() { return m_trail_stack.get_region(); }
bool is_numeral(theory_var v) const { return m_util.is_numeral(get_enode(v)->get_owner()); }
app * mk_bit2bool(app * bv, unsigned idx);
void mk_bits(theory_var v);
friend class mk_atom_trail;
void mk_bit2bool(app * n);
void process_args(app * n);
enode * mk_enode(app * n);
theory_var get_var(enode * n);
enode * get_arg(enode * n, unsigned idx);
theory_var get_arg_var(enode * n, unsigned idx);
void get_bits(theory_var v, expr_ref_vector & r);
void get_bits(enode * n, expr_ref_vector & r);
void get_arg_bits(enode * n, unsigned idx, expr_ref_vector & r);
void get_arg_bits(app * n, unsigned idx, expr_ref_vector & r);
friend class add_var_pos_trail;
void simplify_bit(expr * s, expr_ref & r);
void mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx);
friend class register_true_false_bit_trail;
void register_true_false_bit(theory_var v, unsigned idx);
void find_new_diseq_axioms(var_pos_occ * occs, theory_var v, unsigned idx);
void add_bit(theory_var v, literal l);
void init_bits(enode * n, expr_ref_vector const & bits);
void find_wpos(theory_var v);
friend class fixed_eq_justification;
void fixed_var_eh(theory_var v);
bool get_fixed_value(theory_var v, numeral & result) const;
void internalize_num(app * n);
void internalize_add(app * n);
void internalize_mul(app * n);
void internalize_udiv(app * n);
void internalize_sdiv(app * n);
void internalize_urem(app * n);
void internalize_srem(app * n);
void internalize_smod(app * n);
void internalize_shl(app * n);
void internalize_lshr(app * n);
void internalize_ashr(app * n);
void internalize_ext_rotate_left(app * n);
void internalize_ext_rotate_right(app * n);
void internalize_and(app * n);
void internalize_or(app * n);
void internalize_not(app * n);
void internalize_nand(app * n);
void internalize_nor(app * n);
void internalize_xor(app * n);
void internalize_xnor(app * n);
void internalize_concat(app * n);
void internalize_sign_extend(app * n);
void internalize_zero_extend(app * n);
void internalize_extract(app * n);
void internalize_redand(app * n);
void internalize_redor(app * n);
void internalize_comp(app * n);
void internalize_rotate_left(app * n);
void internalize_rotate_right(app * n);
void internalize_bv2int(app* n);
void internalize_int2bv(app* n);
void internalize_mkbv(app* n);
void internalize_umul_no_overflow(app *n);
void internalize_smul_no_overflow(app *n);
void internalize_smul_no_underflow(app *n);
bool approximate_term(app* n);
template<bool Signed>
void internalize_le(app * atom);
bool internalize_xor3(app * n, bool gate_ctx);
bool internalize_carry(app * n, bool gate_ctx);
justification * mk_bit_eq_justification(theory_var v1, theory_var v2, literal consequent, literal antecedent);
void propagate_bits();
void assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc);
void assert_int2bv_axiom(app* n);
void assert_bv2int_axiom(app* n);
arith_simplifier_plugin & arith_simp() const {
SASSERT(m_simplifier != 0);
arith_simplifier_plugin * as = static_cast<arith_simplifier_plugin*>(m_simplifier->get_plugin(m_autil.get_family_id()));
SASSERT(as != 0);
return *as;
}
protected:
virtual void init(context * ctx);
virtual theory_var mk_var(enode * n);
virtual bool internalize_atom(app * atom, bool gate_ctx);
virtual bool internalize_term(app * term);
virtual void apply_sort_cnstr(enode * n, sort * s);
virtual void new_eq_eh(theory_var v1, theory_var v2);
virtual void new_diseq_eh(theory_var v1, theory_var v2);
virtual void expand_diseq(theory_var v1, theory_var v2);
virtual void assign_eh(bool_var v, bool is_true);
virtual void relevant_eh(app * n);
virtual void push_scope_eh();
virtual void pop_scope_eh(unsigned num_scopes);
virtual final_check_status final_check_eh();
virtual void reset_eh();
svector<theory_var> m_merge_aux[2]; //!< auxiliary vector used in merge_zero_one_bits
bool merge_zero_one_bits(theory_var r1, theory_var r2);
// -----------------------------------
//
// Model generation
//
// -----------------------------------
bv_factory * m_factory;
virtual void init_model(model_generator & m);
virtual model_value_proc * mk_value(enode * n, model_generator & mg);
public:
theory_bv(ast_manager & m, theory_bv_params const & params, bit_blaster_params const & bb_params);
virtual ~theory_bv();
virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_bv, get_manager(), m_params, m_bb.get_params()); }
virtual char const * get_name() const { return "bit-vector"; }
th_trail_stack & get_trail_stack() { return m_trail_stack; }
void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2);
void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { SASSERT(check_zero_one_bits(r1)); }
void unmerge_eh(theory_var v1, theory_var v2);
void display_var(std::ostream & out, theory_var v) const;
void display_bit_atom(std::ostream & out, bool_var v, bit_atom const * a) const;
void display_atoms(std::ostream & out) const;
virtual void display(std::ostream & out) const;
virtual void collect_statistics(::statistics & st) const;
bool get_fixed_value(app* x, numeral & result) const;
#ifdef Z3DEBUG
bool check_assignment(theory_var v) const;
bool check_invariant() const;
bool check_zero_one_bits(theory_var v) const;
#endif
};
};
#endif /* _THEORY_BV_H_ */

755
src/smt/theory_datatype.cpp Normal file
View file

@ -0,0 +1,755 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_datatype.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-31.
Revision History:
--*/
#include"smt_context.h"
#include"theory_datatype.h"
#include"smt_model_generator.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"stats.h"
#include"ast_smt2_pp.h"
namespace smt {
class dt_eq_justification : public ext_theory_eq_propagation_justification {
public:
dt_eq_justification(family_id fid, region & r, literal antecedent, enode * lhs, enode * rhs):
ext_theory_eq_propagation_justification(fid, r, 1, &antecedent, 0, 0, lhs, rhs) {
}
// Remark: the assignment must be propagated back to the datatype theory.
virtual theory_id get_from_theory() const { return null_theory_id; }
};
/**
\brief Assert the axiom (antecedent => lhs = rhs)
antecedent may be null_literal
*/
void theory_datatype::assert_eq_axiom(enode * lhs, expr * rhs, literal antecedent) {
ast_manager & m = get_manager();
context & ctx = get_context();
if (m.proofs_enabled()) {
literal l(mk_eq(lhs->get_owner(), rhs, true));
ctx.mark_as_relevant(l);
if (antecedent != null_literal) {
literal lits[2] = {l, ~antecedent};
ctx.mk_th_axiom(get_id(), 2, lits);
}
else {
literal lits[1] = {l};
ctx.mk_th_axiom(get_id(), 1, lits);
}
}
else {
ctx.internalize(rhs, false);
TRACE("datatype", tout << "adding axiom:\n" << mk_pp(lhs->get_owner(), m) << "\n=\n" << mk_pp(rhs, m) << "\n";);
if (antecedent == null_literal) {
ctx.assign_eq(lhs, ctx.get_enode(rhs), eq_justification::mk_axiom());
}
else {
SASSERT(ctx.get_assignment(antecedent) == l_true);
region & r = ctx.get_region();
enode * _rhs = ctx.get_enode(rhs);
justification * js = ctx.mk_justification(dt_eq_justification(get_id(), r, antecedent, lhs, _rhs));
TRACE("datatype", tout << "assigning... #" << lhs->get_owner_id() << " #" << _rhs->get_owner_id() << "\n";
tout << "v" << lhs->get_th_var(get_id()) << " v" << _rhs->get_th_var(get_id()) << "\n";);
TRACE("datatype_detail", display(tout););
ctx.assign_eq(lhs, _rhs, eq_justification(js));
}
}
}
/**
\brief Assert the equality (= n (c (acc_1 n) ... (acc_m n))) where
where acc_i are the accessors of constructor c.
*/
void theory_datatype::assert_is_constructor_axiom(enode * n, func_decl * c, literal antecedent) {
TRACE("datatype_bug", tout << "creating axiom (= n (c (acc_1 n) ... (acc_m n))) for\n" << mk_pp(n->get_owner(), get_manager()) << "\n";);
m_stats.m_assert_cnstr++;
SASSERT(m_util.is_constructor(c));
SASSERT(m_util.is_datatype(get_manager().get_sort(n->get_owner())));
ast_manager & m = get_manager();
ptr_vector<expr> args;
ptr_vector<func_decl> const * accessors = m_util.get_constructor_accessors(c);
SASSERT(c->get_arity() == accessors->size());
ptr_vector<func_decl>::const_iterator it = accessors->begin();
ptr_vector<func_decl>::const_iterator end = accessors->end();
for (; it != end; ++it) {
func_decl * d = *it;
SASSERT(d->get_arity() == 1);
expr * acc = m.mk_app(d, n->get_owner());
args.push_back(acc);
}
expr * mk = m.mk_app(c, args.size(), args.c_ptr());
assert_eq_axiom(n, mk, antecedent);
}
/**
\brief Given a constructor n := (c a_1 ... a_m) assert the axioms
(= (acc_1 n) a_1)
...
(= (acc_m n) a_m)
*/
void theory_datatype::assert_accessor_axioms(enode * n) {
m_stats.m_assert_accessor++;
SASSERT(is_constructor(n));
ast_manager & m = get_manager();
func_decl * d = n->get_decl();
ptr_vector<func_decl> const * accessors = m_util.get_constructor_accessors(d);
SASSERT(n->get_num_args() == accessors->size());
ptr_vector<func_decl>::const_iterator it = accessors->begin();
ptr_vector<func_decl>::const_iterator end = accessors->end();
for (unsigned i = 0; it != end; ++it, ++i) {
func_decl * acc = *it;
app * acc_app = m.mk_app(acc, n->get_owner());
enode * arg = n->get_arg(i);
assert_eq_axiom(arg, acc_app, null_literal);
}
}
/**
\brief Sign a conflict for r := is_mk(a), c := mk(...), not(r), and c == a.
*/
void theory_datatype::sign_recognizer_conflict(enode * c, enode * r) {
SASSERT(is_constructor(c));
SASSERT(is_recognizer(r));
SASSERT(m_util.get_recognizer_constructor(r->get_decl()) == c->get_decl());
SASSERT(c->get_root() == r->get_arg(0)->get_root());
TRACE("recognizer_conflict",
tout << mk_ismt2_pp(c->get_owner(), get_manager()) << "\n" << mk_ismt2_pp(r->get_owner(), get_manager()) << "\n";);
context & ctx = get_context();
literal l(ctx.enode2bool_var(r));
SASSERT(ctx.get_assignment(l) == l_false);
l.neg();
SASSERT(ctx.get_assignment(l) == l_true);
enode_pair p(c, r->get_arg(0));
region & reg = ctx.get_region();
ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), reg, 1, &l, 1, &p)));
}
theory_var theory_datatype::mk_var(enode * n) {
theory_var r = theory::mk_var(n);
theory_var r2 = m_find.mk_var();
SASSERT(r == r2);
SASSERT(r == static_cast<int>(m_var_data.size()));
m_var_data.push_back(alloc(var_data));
var_data * d = m_var_data[r];
if (is_constructor(n)) {
d->m_constructor = n;
get_context().attach_th_var(n, this, r);
assert_accessor_axioms(n);
}
else {
ast_manager & m = get_manager();
context & ctx = get_context();
ctx.attach_th_var(n, this, r);
sort * s = m.get_sort(n->get_owner());
if (m_util.get_datatype_num_constructors(s) == 1) {
func_decl * c = m_util.get_datatype_constructors(s)->get(0);
assert_is_constructor_axiom(n, c, null_literal);
}
else {
if (m_params.m_dt_lazy_splits == 0 || (m_params.m_dt_lazy_splits == 1 && !s->is_infinite()))
mk_split(r);
}
}
return r;
}
bool theory_datatype::internalize_atom(app * atom, bool gate_ctx) {
TRACE("datatype", tout << "internalizing atom:\n" << mk_pp(atom, get_manager()) << "\n";);
return internalize_term(atom);
}
bool theory_datatype::internalize_term(app * term) {
TRACE("datatype", tout << "internalizing term:\n" << mk_pp(term, get_manager()) << "\n";);
context & ctx = get_context();
unsigned num_args = term->get_num_args();
for (unsigned i = 0; i < num_args; i++)
ctx.internalize(term->get_arg(i), false);
// the internalization of the arguments may trigger the internalization of term.
if (ctx.e_internalized(term))
return true;
enode * e = ctx.mk_enode(term, false, get_manager().is_bool(term), true); // possible optimization, the third argument may be set to false, if the term (actually, atom) is not in the context of a gate.
if (get_manager().is_bool(term)) {
bool_var bv = ctx.mk_bool_var(term);
ctx.set_var_theory(bv, get_id());
ctx.set_enode_flag(bv, true);
}
if (is_constructor(term)) {
SASSERT(!is_attached_to_var(e));
// *** We must create a theory variable for each argument that has sort datatype ***
//
// The apply_sort_cnstr method will not create a theory
// variable for an expression N when sort of N has an
// infinite number of elements.
//
// This may create problems during model construction.
// For example, suppose we have
// x1 = cons(v1, x2)
// and x1 and x2 are lists of integers.
// This sort has an infinite number of elements. So, in principle,
// we do not need a theory variable for x2.
// Recall that if an expression is not associated with a
// theory variable, then a fresh value is associated with
// it.
// Moreover, fresh variables of sort S can only be created after the
// interpretation for each (relevant) expression of sort S in the
// logical context is created. Returning to the example,
// to create the interpretation of x1 we need the
// interpretation for x2. So, x2 cannot be a fresh value,
// since it would have to be created after x1.
//
for (unsigned i = 0; i < num_args; i++) {
enode * arg = e->get_arg(i);
sort * s = get_manager().get_sort(arg->get_owner());
if (!m_util.is_datatype(s))
continue;
if (is_attached_to_var(arg))
continue;
mk_var(arg);
}
mk_var(e);
}
else {
SASSERT(is_accessor(term) || is_recognizer(term));
SASSERT(term->get_num_args() == 1);
enode * arg = e->get_arg(0);
if (!is_attached_to_var(arg))
mk_var(arg);
SASSERT(is_attached_to_var(arg));
}
if (is_recognizer(term)) {
enode * arg = e->get_arg(0);
theory_var v = arg->get_th_var(get_id());
SASSERT(v != null_theory_var);
// When relevancy propagation is enabled, the recognizer is only added when it is marked as relevant.
if (!ctx.relevancy())
add_recognizer(v, e);
}
return true;
}
void theory_datatype::apply_sort_cnstr(enode * n, sort * s) {
// Remark: If s is an infinite sort, then it is not necessary to create
// a theory variable.
//
// Actually, when the logical context has quantifiers, it is better to
// disable this optimization.
// Example:
//
// (forall (l list) (a Int) (= (len (cons a l)) (+ (len l) 1)))
// (assert (> (len a) 1)
//
// If the theory variable is not created for 'a', then a wrong model will be generated.
TRACE("datatype", tout << "apply_sort_cnstr: #" << n->get_owner_id() << "\n";);
TRACE("datatype_bug", tout << "apply_sort_cnstr:\n" << mk_pp(n->get_owner(), get_manager()) << "\n";);
if ((get_context().has_quantifiers() || (m_util.is_datatype(s) && !s->is_infinite())) && !is_attached_to_var(n)) {
mk_var(n);
}
}
void theory_datatype::new_eq_eh(theory_var v1, theory_var v2) {
m_find.merge(v1, v2);
}
bool theory_datatype::use_diseqs() const {
return false;
}
void theory_datatype::new_diseq_eh(theory_var v1, theory_var v2) {
UNREACHABLE();
}
void theory_datatype::assign_eh(bool_var v, bool is_true) {
context & ctx = get_context();
enode * n = ctx.bool_var2enode(v);
if (!is_recognizer(n))
return;
TRACE("datatype", tout << "assigning recognizer: #" << n->get_owner_id() << " is_true: " << is_true << "\n"
<< mk_bounded_pp(n->get_owner(), get_manager()) << "\n";);
SASSERT(n->get_num_args() == 1);
enode * arg = n->get_arg(0);
theory_var tv = arg->get_th_var(get_id());
tv = m_find.find(tv);
var_data * d = m_var_data[tv];
func_decl * r = n->get_decl();
func_decl * c = m_util.get_recognizer_constructor(r);
if (is_true) {
SASSERT(tv != null_theory_var);
if (d->m_constructor != 0 && d->m_constructor->get_decl() == c)
return; // do nothing
assert_is_constructor_axiom(arg, c, literal(v));
}
else {
if (d->m_constructor != 0) {
if (d->m_constructor->get_decl() == c) {
// conflict
sign_recognizer_conflict(d->m_constructor, n);
}
}
else {
propagate_recognizer(tv, n);
}
}
}
void theory_datatype::relevant_eh(app * n) {
TRACE("datatype", tout << "relevant_eh: " << mk_bounded_pp(n, get_manager()) << "\n";);
context & ctx = get_context();
SASSERT(ctx.relevancy());
if (is_recognizer(n)) {
TRACE("datatype", tout << "relevant_eh: #" << n->get_id() << "\n" << mk_bounded_pp(n, get_manager()) << "\n";);
SASSERT(ctx.e_internalized(n));
enode * e = ctx.get_enode(n);
theory_var v = e->get_arg(0)->get_th_var(get_id());
SASSERT(v != null_theory_var);
add_recognizer(v, e);
}
}
void theory_datatype::push_scope_eh() {
theory::push_scope_eh();
m_trail_stack.push_scope();
}
void theory_datatype::pop_scope_eh(unsigned num_scopes) {
m_trail_stack.pop_scope(num_scopes);
unsigned num_old_vars = get_old_num_vars(num_scopes);
std::for_each(m_var_data.begin() + num_old_vars, m_var_data.end(), delete_proc<var_data>());
m_var_data.shrink(num_old_vars);
theory::pop_scope_eh(num_scopes);
SASSERT(m_find.get_num_vars() == m_var_data.size());
SASSERT(m_find.get_num_vars() == get_num_vars());
}
final_check_status theory_datatype::final_check_eh() {
int num_vars = get_num_vars();
final_check_status r = FC_DONE;
for (int v = 0; v < num_vars; v++) {
if (v == static_cast<int>(m_find.find(v))) {
enode * node = get_enode(v);
if (occurs_check(node)) {
// conflict was detected...
// return...
return FC_CONTINUE;
}
if (m_params.m_dt_lazy_splits > 0) {
// using lazy case splits...
var_data * d = m_var_data[v];
if (d->m_constructor == 0) {
mk_split(v);
r = FC_CONTINUE;
}
}
}
}
return r;
}
/**
\brief Check if n can be reached starting from n and following equalities and constructors.
For example, occur_check(a1) returns true in the following set of equalities:
a1 = cons(v1, a2)
a2 = cons(v2, a3)
a3 = cons(v3, a1)
*/
bool theory_datatype::occurs_check(enode * n) {
TRACE("datatype", tout << "occurs check: #" << n->get_owner_id() << "\n";);
m_to_unmark.reset();
m_used_eqs.reset();
m_main = n;
bool res = occurs_check_core(m_main);
unmark_enodes(m_to_unmark.size(), m_to_unmark.c_ptr());
if (res) {
context & ctx = get_context();
region & r = ctx.get_region();
ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, 0, m_used_eqs.size(), m_used_eqs.c_ptr())));
TRACE("occurs_check",
tout << "occurs_check: true\n";
svector<enode_pair>::const_iterator it = m_used_eqs.begin();
svector<enode_pair>::const_iterator end = m_used_eqs.end();
for(; it != end; ++it) {
enode_pair const & p = *it;
tout << "eq: #" << p.first->get_owner_id() << " #" << p.second->get_owner_id() << "\n";
tout << mk_bounded_pp(p.first->get_owner(), get_manager()) << " " << mk_bounded_pp(p.second->get_owner(), get_manager()) << "\n";
});
}
return res;
}
/**
\brief Auxiliary method for occurs_check.
TODO: improve performance.
*/
bool theory_datatype::occurs_check_core(enode * app) {
if (app->is_marked())
return false;
m_stats.m_occurs_check++;
app->set_mark();
m_to_unmark.push_back(app);
TRACE("datatype", tout << "occurs check_core: #" << app->get_owner_id() << " #" << m_main->get_owner_id() << "\n";);
theory_var v = app->get_root()->get_th_var(get_id());
if (v != null_theory_var) {
v = m_find.find(v);
var_data * d = m_var_data[v];
if (d->m_constructor) {
if (app != d->m_constructor)
m_used_eqs.push_back(enode_pair(app, d->m_constructor));
unsigned num_args = d->m_constructor->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
enode * arg = d->m_constructor->get_arg(i);
if (arg->get_root() == m_main->get_root()) {
if (arg != m_main)
m_used_eqs.push_back(enode_pair(arg, m_main));
return true;
}
if (m_util.is_datatype(get_manager().get_sort(arg->get_owner())) && occurs_check_core(arg))
return true;
}
if (app != d->m_constructor) {
SASSERT(m_used_eqs.back().first == app);
SASSERT(m_used_eqs.back().second == d->m_constructor);
m_used_eqs.pop_back();
}
}
}
return false;
}
void theory_datatype::reset_eh() {
m_trail_stack.reset();
std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc<var_data>());
m_var_data.reset();
theory::reset_eh();
m_util.reset();
m_stats.reset();
}
bool theory_datatype::is_shared(theory_var v) const {
// In principle, parametric theories such as Array Theory and
// Datatype Theory need to implement this method. However, the datatype theory
// propagates all implied equalities. And, the is_shared method is essentially used
// to create interface equalities. So, it is safe to return false.
return false;
}
theory_datatype::theory_datatype(ast_manager & m, theory_datatype_params & p):
theory(m.get_family_id("datatype")),
m_params(p),
m_util(m),
m_find(*this),
m_trail_stack(*this) {
}
theory_datatype::~theory_datatype() {
std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc<var_data>());
m_var_data.reset();
}
void theory_datatype::display(std::ostream & out) const {
out << "Theory datatype:\n";
unsigned num_vars = get_num_vars();
for (unsigned v = 0; v < num_vars; v++)
display_var(out, v);
}
void theory_datatype::collect_statistics(::statistics & st) const {
st.update("datatype occurs check", m_stats.m_occurs_check);
st.update("datatype splits", m_stats.m_splits);
st.update("datatype constructor ax", m_stats.m_assert_cnstr);
st.update("datatype accessor ax", m_stats.m_assert_accessor);
}
void theory_datatype::display_var(std::ostream & out, theory_var v) const {
var_data * d = m_var_data[v];
out << "v" << v << " #" << get_enode(v)->get_owner_id() << " -> v" << m_find.find(v) << " ";
if (d->m_constructor)
out << mk_bounded_pp(d->m_constructor->get_owner(), get_manager());
else
out << "(null)";
out << "\n";
}
void theory_datatype::init_model(model_generator & m) {
m_factory = alloc(datatype_factory, get_manager(), m.get_model());
m.register_factory(m_factory);
}
class datatype_value_proc : public model_value_proc {
func_decl * m_constructor;
svector<model_value_dependency> m_dependencies;
public:
datatype_value_proc(func_decl * d):m_constructor(d) {}
void add_dependency(enode * n) { m_dependencies.push_back(model_value_dependency(n)); }
virtual ~datatype_value_proc() {}
virtual void get_dependencies(buffer<model_value_dependency> & result) {
result.append(m_dependencies.size(), m_dependencies.c_ptr());
}
virtual app * mk_value(model_generator & mg, ptr_vector<expr> & values) {
SASSERT(values.size() == m_dependencies.size());
return mg.get_manager().mk_app(m_constructor, values.size(), values.c_ptr());
}
};
model_value_proc * theory_datatype::mk_value(enode * n, model_generator & mg) {
theory_var v = n->get_th_var(get_id());
v = m_find.find(v);
SASSERT(v != null_theory_var);
var_data * d = m_var_data[v];
SASSERT(d->m_constructor);
func_decl * c_decl = d->m_constructor->get_decl();
datatype_value_proc * result = alloc(datatype_value_proc, c_decl);
unsigned num = d->m_constructor->get_num_args();
for (unsigned i = 0; i < num; i++)
result->add_dependency(d->m_constructor->get_arg(i));
return result;
}
void theory_datatype::merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) {
// v1 is the new root
TRACE("datatype", tout << "merging v" << v1 << " v" << v2 << "\n";);
SASSERT(v1 == static_cast<int>(m_find.find(v1)));
var_data * d1 = m_var_data[v1];
var_data * d2 = m_var_data[v2];
if (d2->m_constructor != 0) {
context & ctx = get_context();
if (d1->m_constructor != 0 && d1->m_constructor->get_decl() != d2->m_constructor->get_decl()) {
region & r = ctx.get_region();
enode_pair p(d1->m_constructor, d2->m_constructor);
SASSERT(d1->m_constructor->get_root() == d2->m_constructor->get_root());
ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, 0, 1, &p)));
}
if (d1->m_constructor == 0) {
m_trail_stack.push(set_ptr_trail<theory_datatype, enode>(d1->m_constructor));
// check whether there is a recognizer in d1 that conflicts with d2->m_constructor;
if (!d1->m_recognizers.empty()) {
unsigned c_idx = m_util.get_constructor_idx(d2->m_constructor->get_decl());
enode * recognizer = d1->m_recognizers[c_idx];
if (recognizer != 0 && ctx.get_assignment(recognizer) == l_false) {
sign_recognizer_conflict(d2->m_constructor, recognizer);
return;
}
}
d1->m_constructor = d2->m_constructor;
}
}
ptr_vector<enode>::iterator it = d2->m_recognizers.begin();
ptr_vector<enode>::iterator end = d2->m_recognizers.end();
for (; it != end; ++it)
if (*it)
add_recognizer(v1, *it);
}
void theory_datatype::unmerge_eh(theory_var v1, theory_var v2) {
// do nothing
}
void theory_datatype::add_recognizer(theory_var v, enode * recognizer) {
SASSERT(is_recognizer(recognizer));
context & ctx = get_context();
v = m_find.find(v);
var_data * d = m_var_data[v];
sort * s = recognizer->get_decl()->get_domain(0);
if (d->m_recognizers.empty()) {
SASSERT(m_util.is_datatype(s));
d->m_recognizers.resize(m_util.get_datatype_num_constructors(s), 0);
}
SASSERT(d->m_recognizers.size() == m_util.get_datatype_num_constructors(s));
unsigned c_idx = m_util.get_recognizer_constructor_idx(recognizer->get_decl());
if (d->m_recognizers[c_idx] == 0) {
lbool val = ctx.get_assignment(recognizer);
TRACE("datatype", tout << "adding recognizer to v" << v << " rec: #" << recognizer->get_owner_id() << " val: " << val << "\n";);
if (val == l_true) {
// do nothing...
// If recognizer assignment was already processed, then
// d->m_constructor is already set.
// Otherwise, it will be set when assign_eh is invoked.
return;
}
if (val == l_false && d->m_constructor != 0) {
func_decl * c_decl = m_util.get_recognizer_constructor(recognizer->get_decl());
if (d->m_constructor->get_decl() == c_decl) {
// conflict
sign_recognizer_conflict(d->m_constructor, recognizer);
}
return;
}
SASSERT(val == l_undef || (val == l_false && d->m_constructor == 0));
d->m_recognizers[c_idx] = recognizer;
m_trail_stack.push(set_vector_idx_trail<theory_datatype, enode>(d->m_recognizers, c_idx));
if (val == l_false) {
propagate_recognizer(v, recognizer);
}
}
}
/**
\brief Propagate a recognizer assigned to false.
*/
void theory_datatype::propagate_recognizer(theory_var v, enode * recognizer) {
SASSERT(is_recognizer(recognizer));
SASSERT(static_cast<int>(m_find.find(v)) == v);
context & ctx = get_context();
SASSERT(ctx.get_assignment(recognizer) == l_false);
unsigned num_unassigned = 0;
unsigned unassigned_idx = UINT_MAX;
enode * n = get_enode(v);
sort * dt = get_manager().get_sort(n->get_owner());
var_data * d = m_var_data[v];
CTRACE("datatype", d->m_recognizers.empty(), ctx.display(tout););
SASSERT(!d->m_recognizers.empty());
literal_vector lits;
svector<enode_pair> eqs;
ptr_vector<enode>::const_iterator it = d->m_recognizers.begin();
ptr_vector<enode>::const_iterator end = d->m_recognizers.end();
for (unsigned idx = 0; it != end; ++it, ++idx) {
enode * r = *it;
if (r && ctx.get_assignment(r) == l_true)
return; // nothing to be propagated
if (r && ctx.get_assignment(r) == l_false) {
SASSERT(r->get_num_args() == 1);
lits.push_back(literal(ctx.enode2bool_var(r), true));
if (n != r->get_arg(0)) {
// Argument of the current recognizer is not necessarily equal to n.
// This can happen when n and r->get_arg(0) are in the same equivalence class.
// We must add equality as an assumption to the conflict or propagation
SASSERT(n->get_root() == r->get_arg(0)->get_root());
eqs.push_back(enode_pair(n, r->get_arg(0)));
}
continue;
}
if (num_unassigned == 0)
unassigned_idx = idx;
num_unassigned++;
}
if (num_unassigned == 0) {
// conflict
SASSERT(!lits.empty());
region & reg = ctx.get_region();
TRACE("datatype_conflict", tout << mk_ismt2_pp(recognizer->get_owner(), get_manager()) << "\n";
for (unsigned i = 0; i < lits.size(); i++) {
ctx.display_detailed_literal(tout, lits[i]); tout << "\n";
}
for (unsigned i = 0; i < eqs.size(); i++) {
tout << mk_ismt2_pp(eqs[i].first->get_owner(), get_manager()) << " = " << mk_ismt2_pp(eqs[i].second->get_owner(), get_manager()) << "\n";
});
ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), reg, lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr())));
}
else if (num_unassigned == 1) {
// propagate remaining recognizer
SASSERT(!lits.empty());
enode * r = d->m_recognizers[unassigned_idx];
literal consequent;
if (!r) {
ptr_vector<func_decl> const * constructors = m_util.get_datatype_constructors(dt);
func_decl * rec = m_util.get_constructor_recognizer(constructors->get(unassigned_idx));
app * rec_app = get_manager().mk_app(rec, n->get_owner());
ctx.internalize(rec_app, false);
consequent = literal(ctx.get_bool_var(rec_app));
}
else {
consequent = literal(ctx.enode2bool_var(r));
}
ctx.mark_as_relevant(consequent);
region & reg = ctx.get_region();
ctx.assign(consequent,
ctx.mk_justification(ext_theory_propagation_justification(get_id(), reg, lits.size(), lits.c_ptr(),
eqs.size(), eqs.c_ptr(), consequent)));
}
else {
// there are more than 2 unassigned recognizers...
// if eager splits are enabled... create new case split
if (m_params.m_dt_lazy_splits == 0 || (!dt->is_infinite() && m_params.m_dt_lazy_splits == 1))
mk_split(v);
}
}
/**
\brief Create a new case split for v. That is, create the atom (is_mk v) and mark it as relevant.
If first is true, it means that v does not have recognizer yet.
*/
void theory_datatype::mk_split(theory_var v) {
context & ctx = get_context();
ast_manager & m = get_manager();
v = m_find.find(v);
enode * n = get_enode(v);
sort * s = m.get_sort(n->get_owner());
func_decl * non_rec_c = m_util.get_non_rec_constructor(s);
TRACE("datatype_bug", tout << "non_rec_c: " << non_rec_c->get_name() << "\n";);
unsigned non_rec_idx = m_util.get_constructor_idx(non_rec_c);
var_data * d = m_var_data[v];
SASSERT(d->m_constructor == 0);
func_decl * r = 0;
m_stats.m_splits++;
if (d->m_recognizers.empty()) {
r = m_util.get_constructor_recognizer(non_rec_c);
}
else {
enode * recognizer = d->m_recognizers[non_rec_idx];
if (recognizer == 0) {
r = m_util.get_constructor_recognizer(non_rec_c);
}
else if (!ctx.is_relevant(recognizer)) {
ctx.mark_as_relevant(recognizer);
return;
}
else if (ctx.get_assignment(recognizer) != l_false) {
// if is l_true, then we are done
// otherwise wait recognizer to be assigned.
return;
}
else {
// look for a slot of d->m_recognizers that is 0, or it is not marked as relevant and is unassigned.
ptr_vector<enode>::const_iterator it = d->m_recognizers.begin();
ptr_vector<enode>::const_iterator end = d->m_recognizers.end();
for (unsigned idx = 0; it != end; ++it, ++idx) {
enode * curr = *it;
if (curr == 0) {
ptr_vector<func_decl> const * constructors = m_util.get_datatype_constructors(s);
// found empty slot...
r = m_util.get_constructor_recognizer(constructors->get(idx));
break;
}
else if (!ctx.is_relevant(curr)) {
ctx.mark_as_relevant(curr);
return;
}
else if (ctx.get_assignment(curr) != l_false) {
return;
}
}
if (r == 0)
return; // all recognizers are asserted to false... conflict will be detected...
}
}
SASSERT(r != 0);
app * r_app = m.mk_app(r, n->get_owner());
TRACE("datatype", tout << "creating split: " << mk_bounded_pp(r_app, m) << "\n";);
ctx.internalize(r_app, false);
bool_var bv = ctx.get_bool_var(r_app);
ctx.set_true_first_flag(bv);
ctx.mark_as_relevant(bv);
}
};

Some files were not shown because too many files have changed in this diff Show more