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:
parent
4722fdfca5
commit
add684d8e9
377 changed files with 204 additions and 62 deletions
316
src/smt/arith_eq_adapter.cpp
Normal file
316
src/smt/arith_eq_adapter.cpp
Normal 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";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
97
src/smt/arith_eq_adapter.h
Normal file
97
src/smt/arith_eq_adapter.h
Normal 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
632
src/smt/arith_eq_solver.cpp
Normal 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
108
src/smt/arith_eq_solver.h
Normal 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_ */
|
104
src/smt/arith_solver_plugin.cpp
Normal file
104
src/smt/arith_solver_plugin.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
34
src/smt/arith_solver_plugin.h
Normal file
34
src/smt/arith_solver_plugin.h
Normal 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_ */
|
||||
|
1504
src/smt/asserted_formulas.cpp
Normal file
1504
src/smt/asserted_formulas.cpp
Normal file
File diff suppressed because it is too large
Load diff
204
src/smt/asserted_formulas.h
Normal file
204
src/smt/asserted_formulas.h
Normal 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_ */
|
||||
|
79
src/smt/cached_var_subst.cpp
Normal file
79
src/smt/cached_var_subst.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
53
src/smt/cached_var_subst.h
Normal file
53
src/smt/cached_var_subst.h
Normal 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
104
src/smt/cost_evaluator.cpp
Normal 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
43
src/smt/cost_evaluator.h
Normal 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
316
src/smt/database.h
Normal 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
314
src/smt/database.smt
Normal 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
906
src/smt/demodulator.cpp
Normal 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
265
src/smt/demodulator.h
Normal 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
524
src/smt/dyn_ack.cpp
Normal 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
137
src/smt/dyn_ack.h
Normal 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_ */
|
||||
|
753
src/smt/expr_context_simplifier.cpp
Normal file
753
src/smt/expr_context_simplifier.cpp
Normal 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);
|
||||
}
|
86
src/smt/expr_context_simplifier.h
Normal file
86
src/smt/expr_context_simplifier.h
Normal 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
156
src/smt/fingerprints.cpp
Normal 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
85
src/smt/fingerprints.h
Normal 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
3987
src/smt/mam.cpp
Normal file
File diff suppressed because it is too large
Load diff
74
src/smt/mam.h
Normal file
74
src/smt/mam.h
Normal 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
484
src/smt/qi_queue.cpp
Normal 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
105
src/smt/qi_queue.h
Normal 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_ */
|
||||
|
127
src/smt/smt_almost_cg_table.cpp
Normal file
127
src/smt/smt_almost_cg_table.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
72
src/smt/smt_almost_cg_table.h
Normal file
72
src/smt/smt_almost_cg_table.h
Normal 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_ */
|
||||
|
100
src/smt/smt_b_justification.h
Normal file
100
src/smt/smt_b_justification.h
Normal 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
128
src/smt/smt_bool_var_data.h
Normal 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_ */
|
||||
|
1124
src/smt/smt_case_split_queue.cpp
Normal file
1124
src/smt/smt_case_split_queue.cpp
Normal file
File diff suppressed because it is too large
Load diff
55
src/smt/smt_case_split_queue.h
Normal file
55
src/smt/smt_case_split_queue.h
Normal 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
250
src/smt/smt_cg_table.cpp
Normal 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
354
src/smt/smt_cg_table.h
Normal 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
188
src/smt/smt_checker.cpp
Normal 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
57
src/smt/smt_checker.h
Normal 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
118
src/smt/smt_clause.cpp
Normal 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
258
src/smt/smt_clause.h
Normal 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_ */
|
||||
|
1430
src/smt/smt_conflict_resolution.cpp
Normal file
1430
src/smt/smt_conflict_resolution.cpp
Normal file
File diff suppressed because it is too large
Load diff
278
src/smt/smt_conflict_resolution.h
Normal file
278
src/smt/smt_conflict_resolution.h
Normal 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
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
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
403
src/smt/smt_context_inv.cpp
Normal 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
621
src/smt/smt_context_pp.cpp
Normal 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";
|
||||
}
|
||||
|
||||
};
|
||||
|
159
src/smt/smt_context_stat.cpp
Normal file
159
src/smt/smt_context_stat.cpp
Normal 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
453
src/smt/smt_enode.cpp
Normal 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
382
src/smt/smt_enode.h
Normal 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_ */
|
||||
|
85
src/smt/smt_eq_justification.h
Normal file
85
src/smt/smt_eq_justification.h
Normal 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
40
src/smt/smt_failure.h
Normal 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
|
298
src/smt/smt_for_each_relevant_expr.cpp
Normal file
298
src/smt/smt_for_each_relevant_expr.cpp
Normal 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
|
||||
}
|
||||
|
||||
};
|
114
src/smt/smt_for_each_relevant_expr.h
Normal file
114
src/smt/smt_for_each_relevant_expr.h
Normal 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
1700
src/smt/smt_internalizer.cpp
Normal file
File diff suppressed because it is too large
Load diff
419
src/smt/smt_justification.cpp
Normal file
419
src/smt/smt_justification.cpp
Normal 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
412
src/smt/smt_justification.h
Normal 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
100
src/smt/smt_literal.cpp
Normal 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
122
src/smt/smt_literal.h
Normal 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_ */
|
||||
|
419
src/smt/smt_model_checker.cpp
Normal file
419
src/smt/smt_model_checker.cpp
Normal 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";);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
98
src/smt/smt_model_checker.h
Normal file
98
src/smt/smt_model_checker.h
Normal 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
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
125
src/smt/smt_model_finder.h
Normal 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
|
631
src/smt/smt_model_generator.cpp
Normal file
631
src/smt/smt_model_generator.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
229
src/smt/smt_model_generator.h
Normal file
229
src/smt/smt_model_generator.h
Normal 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
609
src/smt/smt_quantifier.cpp
Normal 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
167
src/smt/smt_quantifier.h
Normal 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
|
66
src/smt/smt_quantifier_instances.h
Normal file
66
src/smt/smt_quantifier_instances.h
Normal 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_ */
|
||||
|
115
src/smt/smt_quantifier_stat.cpp
Normal file
115
src/smt/smt_quantifier_stat.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
140
src/smt/smt_quantifier_stat.h
Normal file
140
src/smt/smt_quantifier_stat.h
Normal 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_ */
|
||||
|
421
src/smt/smt_quick_checker.cpp
Normal file
421
src/smt/smt_quick_checker.cpp
Normal 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
106
src/smt/smt_quick_checker.h
Normal 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
669
src/smt/smt_relevancy.cpp
Normal 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
204
src/smt/smt_relevancy.h
Normal 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
901
src/smt/smt_setup.cpp
Normal 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
114
src/smt/smt_setup.h
Normal 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_ */
|
||||
|
|
@ -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;
|
||||
|
|
29
src/smt/smt_statistics.cpp
Normal file
29
src/smt/smt_statistics.cpp
Normal 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
60
src/smt/smt_statistics.h
Normal 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
139
src/smt/smt_theory.cpp
Normal 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
454
src/smt/smt_theory.h
Normal 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_ */
|
||||
|
76
src/smt/smt_theory_var_list.h
Normal file
76
src/smt/smt_theory_var_list.h
Normal 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
78
src/smt/smt_types.h
Normal 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
51
src/smt/solver_plugin.h
Normal 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
30
src/smt/theory_arith.cpp
Normal 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
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
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
3080
src/smt/theory_arith_core.h
Normal file
File diff suppressed because it is too large
Load diff
32
src/smt/theory_arith_def.h
Normal file
32
src/smt/theory_arith_def.h
Normal 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
351
src/smt/theory_arith_eq.h
Normal 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
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
207
src/smt/theory_arith_inv.h
Normal 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
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
516
src/smt/theory_arith_pp.h
Normal 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
479
src/smt/theory_array.cpp
Normal 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
117
src/smt/theory_array.h
Normal 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_ */
|
||||
|
926
src/smt/theory_array_base.cpp
Normal file
926
src/smt/theory_array_base.cpp
Normal 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
199
src/smt/theory_array_base.h
Normal 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_ */
|
||||
|
812
src/smt/theory_array_full.cpp
Normal file
812
src/smt/theory_array_full.cpp
Normal 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
109
src/smt/theory_array_full.h
Normal 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
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
282
src/smt/theory_bv.h
Normal 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
755
src/smt/theory_datatype.cpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue