3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-22 02:57:50 +00:00

Reorganizing the code

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2012-10-20 22:03:58 -07:00
parent 2b8fb6c718
commit 492484c5aa
125 changed files with 632 additions and 390 deletions

2
src/spc/README Normal file
View file

@ -0,0 +1,2 @@
Superposition Calculus.
This module is currently disabled.

53
src/spc/expr_offset.h Normal file
View file

@ -0,0 +1,53 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
expr_offset.h
Abstract:
Expressions + Offsets.
In order to avoid creating variants of terms, we use a pair (expression, offset),
where offset is just a small integer.
Non ground terms with different offsets are always considered
disequal. For example, (f x):1 is different from (f x):2, and
(f x):1 is unifiable with x:2.
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#ifndef _EXPR_OFFSET_H_
#define _EXPR_OFFSET_H_
#include"ast.h"
class expr_offset {
expr * m_expr;
unsigned m_offset;
public:
expr_offset():m_expr(0), m_offset(0) {}
expr_offset(expr * e, unsigned o):m_expr(e), m_offset(o) {}
expr * get_expr() const { return m_expr; }
unsigned get_offset() const { return m_offset; }
bool operator==(expr_offset const & other) const { return m_expr == other.m_expr && m_offset == other.m_offset; }
bool operator!=(expr_offset const & other) const { return !operator==(other); }
unsigned hash() const {
unsigned a = m_expr->get_id();
unsigned b = m_offset;
unsigned c = 17;
mix(a, b, c);
return c;
}
};
typedef std::pair<expr_offset, expr_offset> expr_offset_pair;
typedef pair_hash<obj_hash<expr_offset>, obj_hash<expr_offset> > expr_offset_pair_hash;
#endif /* _EXPR_OFFSET_H_ */

94
src/spc/expr_offset_map.h Normal file
View file

@ -0,0 +1,94 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
expr_offset_map.h
Abstract:
A generic mapping from (expression, offset) to a value T.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _EXPR_OFFSET_MAP_H_
#define _EXPR_OFFSET_MAP_H_
#include"expr_offset.h"
#include"vector.h"
/**
\brief A mapping from expr_offset to some value of type T.
*/
template<typename T>
class expr_offset_map {
struct data {
T m_data;
unsigned m_timestamp;
data():m_timestamp(0) {}
};
vector<svector<data> > m_map;
unsigned m_timestamp;
public:
expr_offset_map():
m_timestamp(1) {}
bool contains(expr_offset const & n) const {
unsigned off = n.get_offset();
if (off < m_map.size()) {
svector<data> const & v = m_map[off];
unsigned id = n.get_expr()->get_id();
if (id < v.size())
return v[id].m_timestamp == m_timestamp;
}
return false;
}
bool find(expr_offset const & n, T & r) const {
unsigned off = n.get_offset();
if (off < m_map.size()) {
svector<data> const & v = m_map[off];
unsigned id = n.get_expr()->get_id();
if (id < v.size() && v[id].m_timestamp == m_timestamp) {
r = v[id].m_data;
return true;
}
}
return false;
}
void insert(expr_offset const & n, T const & r) {
unsigned off = n.get_offset();
if (off >= m_map.size())
m_map.resize(off+1, svector<data>());
svector<data> & v = m_map[off];
unsigned id = n.get_expr()->get_id();
if (id >= v.size())
v.resize(id+1);
v[id].m_data = r;
v[id].m_timestamp = m_timestamp;
}
void reset() {
m_timestamp++;
if (m_timestamp == UINT_MAX) {
typename vector<svector<data> >::iterator it = m_map.begin();
typename vector<svector<data> >::iterator end = m_map.end();
for (; it != end; ++it) {
svector<data> & v = *it;
typename svector<data>::iterator it2 = v.begin();
typename svector<data>::iterator end2 = v.end();
for (; it2 != end2; ++it2)
it2->m_timestamp = 0;
}
m_timestamp = 1;
}
}
};
#endif /* _EXPR_OFFSET_MAP_H_ */

88
src/spc/expr_stat.cpp Normal file
View file

@ -0,0 +1,88 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
expr_stat.cpp
Abstract:
Expression statistics (symbol count, var count, depth, ...)
All functions in these module assume expressions do not contain
nested quantifiers.
Author:
Leonardo de Moura (leonardo) 2008-02-05.
Revision History:
--*/
#include"for_each_expr.h"
#include"expr_stat.h"
void get_expr_stat(expr * n, expr_stat & r) {
typedef std::pair<expr *, unsigned> pair;
buffer<pair> todo;
todo.push_back(pair(n, 0));
while (!todo.empty()) {
pair & p = todo.back();
n = p.first;
unsigned depth = p.second;
unsigned j;
todo.pop_back();
r.m_sym_count++;
if (depth > r.m_depth)
r.m_depth = depth;
switch (n->get_kind()) {
case AST_APP:
j = to_app(n)->get_num_args();
if (j == 0)
r.m_const_count++;
while (j > 0) {
--j;
todo.push_back(pair(to_app(n)->get_arg(j), depth + 1));
}
break;
case AST_VAR:
if (to_var(n)->get_idx() > r.m_max_var_idx)
r.m_max_var_idx = to_var(n)->get_idx();
r.m_ground = false;
break;
case AST_QUANTIFIER:
todo.push_back(pair(to_quantifier(n)->get_expr(), depth+1));
break;
default:
UNREACHABLE();
}
}
}
unsigned get_symbol_count(expr * n) {
unsigned r = 0;
ptr_buffer<expr> todo;
todo.push_back(n);
while (!todo.empty()) {
n = todo.back();
unsigned j;
todo.pop_back();
r++;
switch (n->get_kind()) {
case AST_APP:
j = to_app(n)->get_num_args();
while (j > 0) {
--j;
todo.push_back(to_app(n)->get_arg(j));
}
break;
case AST_QUANTIFIER:
todo.push_back(to_quantifier(n)->get_expr());
break;
default:
break;
}
}
return r;
}

50
src/spc/expr_stat.h Normal file
View file

@ -0,0 +1,50 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
expr_stat.h
Abstract:
Expression statistics (symbol count, var count, depth, ...)
All functions in these module assume expressions do not contain
nested quantifiers.
Author:
Leonardo de Moura (leonardo) 2008-02-05.
Revision History:
--*/
#ifndef _EXPR_STAT_H_
#define _EXPR_STAT_H_
class expr;
struct expr_stat {
unsigned m_sym_count; // symbol count
unsigned m_depth; // depth
unsigned m_const_count; // constant count
unsigned m_max_var_idx;
bool m_ground;
expr_stat():m_sym_count(0), m_depth(0), m_const_count(0), m_max_var_idx(0), m_ground(true) {}
};
/**
\brief Collect statistics regarding the given expression.
\warning This function traverses the dag as a tree.
*/
void get_expr_stat(expr * n, expr_stat & r);
/**
\brief Return the number of symbols in \c n.
\warning This function traverses the dag as a tree.
*/
unsigned get_symbol_count(expr * n);
#endif /* _EXPR_STAT_H_ */

276
src/spc/kbo.cpp Normal file
View file

@ -0,0 +1,276 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
kbo.cpp
Abstract:
Knuth-Bendix ordering.
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#include"kbo.h"
#include"ast_pp.h"
inline unsigned kbo::f_weight(func_decl * f) const {
// TODO
return 1;
}
inline unsigned kbo::var_weight() const {
return m_var_weight;
}
inline void kbo::reset() {
m_weight_balance = 0;
m_deltas.reset();
m_num_pos = 0;
m_num_neg = 0;
}
/**
\brief Increase the balance of the given variable.
*/
inline void kbo::inc(expr_offset v) {
SASSERT(is_var(v.get_expr()));
int val;
unsigned v_idx = to_var(v.get_expr())->get_idx();
unsigned offset = v.get_offset();
if (m_deltas.find(v_idx, offset, val)) {
if (val == -1)
m_num_neg--;
else if (val == 0)
m_num_pos++;
m_deltas.insert(v_idx, offset, val + 1);
}
else {
m_deltas.insert(v_idx, offset, 1);
m_num_pos ++;
}
}
/**
\brief Decreate the balance of the given variable.
*/
inline void kbo::dec(expr_offset v) {
int val;
unsigned v_idx = to_var(v.get_expr())->get_idx();
unsigned offset = v.get_offset();
if (m_deltas.find(v_idx, offset, val)) {
if (val == 0)
m_num_neg++;
else if (val == 1)
m_num_pos--;
m_deltas.insert(v_idx, offset, val - 1);
}
else {
m_deltas.insert(v_idx, offset, -1);
m_num_neg ++;
}
}
/**
\brief Accumulate the variables and weight balance of t. Return
true if t contains target_var.
*/
template<bool pos>
bool kbo::VWBc(expr_offset t, expr_offset target_var) {
SASSERT(target_var.get_expr() == 0 || is_var(target_var.get_expr()));
svector<expr_offset> & todo = m_vwbc_todo;
expr_offset s;
bool found = false;
unsigned j;
SASSERT(todo.empty());
todo.push_back(t);
while (!todo.empty()) {
t = todo.back();
if (t == target_var)
found = true;
expr * n = t.get_expr();
unsigned offset = t.get_offset();
todo.pop_back();
switch (n->get_kind()) {
case AST_VAR:
if (m_subst && m_subst->find(to_var(n), offset, s))
todo.push_back(s);
else if (pos) {
inc(t);
m_weight_balance += var_weight();
}
else {
dec(t);
m_weight_balance -= var_weight();
}
break;
case AST_APP:
if (pos)
m_weight_balance += f_weight(to_app(n)->get_decl());
else
m_weight_balance -= f_weight(to_app(n)->get_decl());
j = to_app(n)->get_num_args();
while (j > 0) {
--j;
todo.push_back(expr_offset(to_app(n)->get_arg(j), offset));
}
break;
default:
UNREACHABLE();
break;
}
}
return found;
}
template<bool pos>
inline void kbo::VWB(expr_offset t, unsigned idx) {
expr_offset null(0, 0);
app * n = to_app(t.get_expr());
unsigned num = n->get_num_args();
for (; idx < num; idx++)
VWBc<pos>(expr_offset(n->get_arg(idx), t.get_offset()), null);
}
inline bool is_unary_app(expr * n) {
return is_app(n) && to_app(n)->get_num_args() == 1;
}
inline kbo::result kbo::no_neg() const {
return m_num_neg == 0 ? GREATER : UNCOMPARABLE;
}
inline kbo::result kbo::no_pos() const {
return m_num_pos == 0 ? LESSER : UNCOMPARABLE;
}
order::result kbo::compare(expr_offset const & t1, expr_offset const & t2, substitution * s) {
reset();
m_subst = s;
if (t1 == t2)
return EQUAL;
expr * n1 = t1.get_expr();
expr * n2 = t2.get_expr();
// f(s) >_{kbo} f(t) iff s >_{kbo} t
while (is_unary_app(n1) && is_unary_app(n2) && to_app(n1)->get_decl() == to_app(n2)->get_decl()) {
n1 = to_app(n1)->get_arg(0);
n2 = to_app(n2)->get_arg(0);
}
svector<entry> & todo = m_compare_todo;
SASSERT(todo.empty());
todo.push_back(entry(find(expr_offset(n1, t1.get_offset())),
find(expr_offset(n2, t2.get_offset())),
0));
result res = UNKNOWN;
while (!todo.empty()) {
entry & e = todo.back();
expr_offset t1 = e.m_t1;
expr_offset t2 = e.m_t2;
expr * n1 = t1.get_expr();
expr * n2 = t2.get_expr();
TRACE("kbo", tout << "processing with idx: " << e.m_idx << "\n" <<
mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n";
tout << "wb : " << m_weight_balance << "\n";);
SASSERT(!is_quantifier(n1) && !is_quantifier(n2));
bool v1 = is_var(n1);
bool v2 = is_var(n2);
if (v1 && v2) {
todo.pop_back();
inc(t1);
dec(t2);
res = t1 == t2 ? EQUAL : UNCOMPARABLE;
}
else if (v1) {
todo.pop_back();
res = VWBc<false>(t2, t1) ? LESSER : UNCOMPARABLE;
inc(t1);
m_weight_balance += var_weight();
}
else if (v2) {
todo.pop_back();
res = VWBc<true>(t1, t2) ? GREATER : UNCOMPARABLE;
dec(t2);
m_weight_balance -= var_weight();
}
else {
func_decl * f = to_app(n1)->get_decl();
func_decl * g = to_app(n2)->get_decl();
result lex;
if (f != g || to_app(n1)->get_num_args() != to_app(n2)->get_num_args()) {
VWB<true>(t1, 0);
VWB<false>(t2, 0);
lex = UNCOMPARABLE;
}
else {
unsigned & idx = e.m_idx;
// when idx > 0, res contains the result for child (idx - 1)
if (idx > 0 && res != EQUAL) {
VWB<true>(t1, idx);
VWB<false>(t2, idx);
lex = res;
}
else if (idx == to_app(n1)->get_num_args()) {
// all children were visited
lex = EQUAL;
}
else if (idx < to_app(n1)->get_num_args()) {
expr_offset c1 = find(expr_offset(to_app(n1)->get_arg(idx), t1.get_offset()));
expr_offset c2 = find(expr_offset(to_app(n2)->get_arg(idx), t2.get_offset()));
idx++; // move curr entry child idx
entry new_entry(c1, c2, 0);
todo.push_back(new_entry);
continue; // process child before continuing
}
}
todo.pop_back();
m_weight_balance += f_weight(f);
m_weight_balance -= f_weight(g);
if (m_weight_balance > 0)
res = no_neg();
else if (m_weight_balance < 0)
res = no_pos();
else if (f_greater(f, g))
res = no_neg();
else if (f_greater(g, f))
res = no_pos();
else if (f != g)
res = UNCOMPARABLE;
else if (lex == EQUAL)
res = EQUAL;
else if (lex == GREATER)
res = no_neg();
else if (lex == LESSER)
res = no_pos();
else
res = UNCOMPARABLE;
}
TRACE("kbo", tout << "result: " << res << "\n";);
}
return res;
}
bool kbo::greater(expr_offset const & t1, expr_offset const & t2, substitution * s) {
return compare(t1, t2, s) == GREATER;
}
int kbo::compare_ge(expr_offset const & t1, expr_offset const & t2, substitution * s) {
switch (compare(t1, t2, s)) {
case GREATER: return 1;
case EQUAL: return 0;
default: return -1;
}
}

70
src/spc/kbo.h Normal file
View file

@ -0,0 +1,70 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
kbo.h
Abstract:
Knuth-Bendix ordering.
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#ifndef _KBO_H_
#define _KBO_H_
#include"order.h"
class kbo : public order {
struct entry {
expr_offset m_t1;
expr_offset m_t2;
unsigned m_idx;
entry():m_idx(UINT_MAX) {}
entry(expr_offset const & t1, expr_offset const & t2, unsigned idx):
m_t1(t1), m_t2(t2), m_idx(idx) {}
};
unsigned m_var_weight;
int m_weight_balance;
var_offset_map<int> m_deltas;
unsigned m_num_pos;
unsigned m_num_neg;
svector<expr_offset> m_vwbc_todo;
svector<entry> m_compare_todo;
unsigned f_weight(func_decl * f) const;
unsigned var_weight() const;
void reset();
void inc(expr_offset v);
void dec(expr_offset v);
template<bool pos>
bool VWBc(expr_offset t, expr_offset target_var);
template<bool pos>
void VWB(expr_offset t, unsigned idx);
result no_neg() const;
result no_pos() const;
public:
kbo(ast_manager & m, precedence * p, unsigned var_weight = 1):order(m, p), m_var_weight(var_weight) {}
virtual ~kbo() {}
virtual void reserve(unsigned num_offsets, unsigned num_vars) { m_deltas.reserve(num_offsets, num_vars); }
virtual void reserve_offsets(unsigned num_offsets) { m_deltas.reserve_offsets(num_offsets); }
virtual void reserve_vars(unsigned num_vars) { m_deltas.reserve_vars(num_vars); }
virtual result compare(expr_offset const & t1, expr_offset const & t2, substitution * s);
result compare(expr * t1, expr * t2) { return compare(expr_offset(t1, 0), expr_offset(t2, 0), 0); }
virtual bool greater(expr_offset const & t1, expr_offset const & t2, substitution * s);
virtual int compare_ge(expr_offset const & t1, expr_offset const & t2, substitution * s);
};
#endif /* _KBO_H_ */

184
src/spc/lpo.cpp Normal file
View file

@ -0,0 +1,184 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
lpo.h
Abstract:
Lexicographical Path Ordering
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#include"lpo.h"
/**
\brief Check whether the variable in t1 occurs in t2.
*/
bool lpo::occurs(expr_offset const & t1, expr_offset const & t2) {
SASSERT(is_var(t1.get_expr()));
if (is_ground(t2.get_expr()))
return false;
m_todo.reset();
m_todo.push_back(t2);
while (!m_todo.empty()) {
expr_offset t = m_todo.back();
m_todo.pop_back();
t = find(t);
expr * n = t.get_expr();
if (is_ground(n))
continue;
unsigned offset = t.get_offset();
unsigned j;
switch (n->get_kind()) {
case AST_VAR:
if (t == t1)
return true;
break;
case AST_APP:
j = to_app(n)->get_num_args();
while (j > 0) {
--j;
expr * arg = to_app(n)->get_arg(j);
if (!is_ground(arg))
m_todo.push_back(expr_offset(arg, offset));
}
break;
default:
UNREACHABLE();
}
}
return false;
}
inline bool lpo::greater(expr_offset s, expr_offset t, unsigned depth) {
return lpo::compare(s, t, depth) == GREATER;
}
/**
\brief Return true if s >_{lpo} t_i forall children t_i of t.
*/
bool lpo::dominates_args(expr_offset s, expr_offset t, unsigned depth) {
SASSERT(is_app(t.get_expr()));
unsigned num_args = to_app(t.get_expr())->get_num_args();
unsigned off = t.get_offset();
for (unsigned i = 0; i < num_args; i++) {
expr * t_i = to_app(t.get_expr())->get_arg(i);
if (!greater(s, expr_offset(t_i, off), depth+1))
return false;
}
return true;
}
/**
\brief Return true if s_i >=_{lpo} t for some arg s_i of s.
*/
bool lpo::arg_dominates_expr(expr_offset s, expr_offset t, unsigned depth) {
SASSERT(is_app(s.get_expr()));
unsigned num_args = to_app(s.get_expr())->get_num_args();
unsigned off = s.get_offset();
for (unsigned i = 0; i < num_args; i++) {
expr * s_i = to_app(s.get_expr())->get_arg(i);
result r = compare(expr_offset(s_i, off), t, depth+1);
if (r == EQUAL || r == GREATER)
return true;
}
return false;
}
order::result lpo::lex_compare(expr_offset s, expr_offset t, unsigned depth) {
SASSERT(is_app(s.get_expr()));
SASSERT(is_app(t.get_expr()));
app * _s = to_app(s.get_expr());
app * _t = to_app(t.get_expr());
unsigned num_args1 = _s->get_num_args();
unsigned num_args2 = _t->get_num_args();
unsigned num_args = std::min(num_args1, num_args2);
unsigned off1 = s.get_offset();
unsigned off2 = t.get_offset();
result r = EQUAL;
for (unsigned i = 0; i < num_args; i++) {
r = compare(expr_offset(_s->get_arg(i), off1), expr_offset(_t->get_arg(i), off2), depth+1);
if (r != EQUAL)
break;
}
if (r == EQUAL) {
if (num_args1 > num_args2)
return GREATER;
if (num_args1 < num_args2)
return NOT_GTEQ;
}
return r;
}
inline order::result lpo::compare_core(expr_offset s, expr_offset t, unsigned depth) {
s = find(s);
t = find(t);
if (max_depth(depth))
return UNKNOWN;
if (is_var(s.get_expr()))
return s == t ? EQUAL : UNCOMPARABLE;
else if (is_var(t.get_expr()))
return occurs(t, s) ? GREATER : UNCOMPARABLE;
else {
func_decl * f = to_app(s.get_expr())->get_decl();
func_decl * g = to_app(t.get_expr())->get_decl();
if (f_greater(f, g))
return dominates_args(s, t, depth) ? GREATER : NOT_GTEQ;
else if (f != g)
return arg_dominates_expr(s, t, depth) ? GREATER : NOT_GTEQ;
else {
result r = lex_compare(s, t, depth);
if (r == GREATER) {
if (dominates_args(s, t, depth))
return GREATER;
}
else if (r == EQUAL)
return EQUAL;
return to_app(s.get_expr())->get_num_args() > 1 && arg_dominates_expr(s, t, depth) ? GREATER : NOT_GTEQ;
}
}
}
order::result lpo::compare(expr_offset s, expr_offset t, unsigned depth) {
TRACE("lpo", tout << "comparing:\n" << mk_pp(s.get_expr(), m_manager) << "\n" << mk_pp(t.get_expr(), m_manager) << "\n";);
result r = compare_core(s, t, depth);
TRACE("lpo", tout << "result of comparing:\n" << mk_pp(s.get_expr(), m_manager) << "\n" << mk_pp(t.get_expr(), m_manager) << "\nresult: " << r << "\n";);
return r;
}
bool lpo::greater(expr_offset const & t1, expr_offset const & t2, substitution * s) {
m_subst = s;
return greater(t1, t2, static_cast<unsigned>(0));
}
order::result lpo::compare(expr_offset const & t1, expr_offset const & t2, substitution * s) {
m_subst = s;
result r = compare(t1, t2, static_cast<unsigned>(0));
if (r != NOT_GTEQ)
return r;
r = compare(t2, t1, static_cast<unsigned>(0));
if (r == GREATER)
return LESSER;
if (r == UNKNOWN)
return UNKNOWN;
return UNCOMPARABLE;
}
int lpo::compare_ge(expr_offset const & t1, expr_offset const & t2, substitution * s) {
m_subst = s;
result r = compare(t1, t2, static_cast<unsigned>(0));
switch (r) {
case GREATER: return 1;
case EQUAL: return 0;
default: return -1;
}
}

49
src/spc/lpo.h Normal file
View file

@ -0,0 +1,49 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
lpo.h
Abstract:
Lexicographical Path Ordering
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _LPO_H_
#define _LPO_H_
#include"order.h"
#include"vector.h"
#include"map.h"
class lpo : public order {
svector<expr_offset> m_todo;
bool occurs(expr_offset const & t1, expr_offset const & t2);
bool greater(expr_offset s, expr_offset t, unsigned depth);
bool dominates_args(expr_offset s, expr_offset t, unsigned depth);
bool arg_dominates_expr(expr_offset s, expr_offset t, unsigned depth);
result lex_compare(expr_offset s, expr_offset t, unsigned depth);
result compare_core(expr_offset s, expr_offset t, unsigned depth);
result compare(expr_offset s, expr_offset t, unsigned depth);
bool max_depth(unsigned d) { /* TODO */ return false; }
public:
lpo(ast_manager & m, precedence * p):order(m, p) {}
virtual ~lpo() {}
virtual result compare(expr_offset const & t1, expr_offset const & t2, substitution * s);
result compare(expr * t1, expr * t2) { return compare(expr_offset(t1, 0), expr_offset(t2, 0), static_cast<substitution*>(0)); }
virtual bool greater(expr_offset const & t1, expr_offset const & t2, substitution * s);
bool greater(expr_offset const & t1, expr_offset const & t2) { return greater(t1, t2); }
virtual int compare_ge(expr_offset const & t1, expr_offset const & t2, substitution * s);
};
#endif /* _LPO_H_ */

56
src/spc/marker.h Normal file
View file

@ -0,0 +1,56 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
marker.h
Abstract:
Auxiliary object for managing markings
Author:
Leonardo de Moura (leonardo) 2008-02-07.
Revision History:
--*/
#ifndef _MARKER_H_
#define _MARKER_H_
#include"vector.h"
/**
\brief Keep track of all marked objects. Unmark them when the method
unmark or destructor is invoked.
*/
template<typename T>
class marker {
ptr_vector<T> m_to_unmark;
public:
~marker() {
unmark();
}
void mark(T * obj) {
obj->set_mark(true);
m_to_unmark.push_back(obj);
}
bool is_marked(T * obj) const {
return obj->is_marked();
}
void unmark() {
typename ptr_vector<T>::iterator it = m_to_unmark.begin();
typename ptr_vector<T>::iterator end = m_to_unmark.end();
for (; it != end; ++it) {
T * obj = *it;
obj->set_mark(false);
}
m_to_unmark.reset();
}
};
#endif /* _MARKER_H_ */

81
src/spc/matcher.cpp Normal file
View file

@ -0,0 +1,81 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
matcher.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#include"matcher.h"
matcher::matcher(ast_manager & m):
m_manager(m) {
}
bool matcher::operator()(expr * e1, expr * e2, substitution & s) {
reset();
m_subst = &s;
m_todo.push_back(expr_pair(e1, e2));
while (!m_todo.empty()) {
expr_pair const & p = m_todo.back();
// if (m_cache.contains(p)) {
// m_todo.pop_back();
// continue;
// }
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;
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;
}
void matcher::reset() {
// m_cache.reset();
m_todo.reset();
}

64
src/spc/matcher.h Normal file
View file

@ -0,0 +1,64 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
matcher.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _MATCHER_H_
#define _MATCHER_H_
#include"substitution.h"
#include"hashtable.h"
/**
\brief Functor for matching expressions.
*/
class matcher {
typedef std::pair<expr *, expr *> expr_pair;
typedef pair_hash<obj_ptr_hash<expr>, obj_ptr_hash<expr> > expr_pair_hash;
typedef hashtable<expr_pair, expr_pair_hash, default_eq<expr_pair> > cache;
ast_manager & m_manager;
substitution * m_subst;
cache m_cache;
svector<expr_pair> m_todo;
void reset();
public:
matcher(ast_manager & m);
/**
\brief Return true if e2 is an instance of e1.
In case of success (result is true), it will store the substitution that makes e1 equals to e2 into s.
For example:
1) e1 = f(g(x), x), e2 = f(g(h(a)), h(a))
The result is true, and s will contain x -> h(a)
2) e1 = f(a, x) e2 = f(x, a)
The result is false.
3) e1 = f(x, x) e2 = f(y, a)
The result is false
4) e1 = f(x, y) e2 = f(h(z), a)
The result is true, and s contains x->h(z) and y->a
*/
bool operator()(expr * e1, expr * e2, substitution & s);
};
#endif /* _MATCHER_H_ */

View file

@ -0,0 +1,88 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
normalize_vars.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-16.
Revision History:
--*/
#include"normalize_vars.h"
expr * normalize_vars::operator()(expr * n) {
SASSERT(m_todo.empty());
m_todo.push_back(n);
while (!m_todo.empty()) {
n = m_todo.back();
if (m_cache.contains(n)) {
m_todo.pop_back();
continue;
}
if (is_var(n)) {
m_todo.pop_back();
unsigned idx = to_var(n)->get_idx();
var * new_var = m_map.get(idx, 0);
if (new_var == 0) {
new_var = m_manager.mk_var(m_next_var, to_var(n)->get_sort());
m_next_var++;
m_new_vars.push_back(new_var);
m_map.setx(idx, new_var, 0);
}
SASSERT(new_var->get_sort() == to_var(n)->get_sort());
m_cache.insert(n, new_var);
}
else {
SASSERT(is_app(n));
bool visited = true;
unsigned num_args = to_app(n)->get_num_args();
unsigned j = num_args;
while (j > 0) {
--j;
expr * child = to_app(n)->get_arg(j);
if (!m_cache.contains(child)) {
m_todo.push_back(child);
visited = false;
}
}
if (visited) {
m_todo.pop_back();
m_new_children.reset();
bool modified = false;
for (unsigned i = 0; i < num_args; i++) {
expr * child = to_app(n)->get_arg(i);
expr * new_child = 0;
m_cache.find(child, new_child);
SASSERT(new_child);
if (child != new_child)
modified = true;
m_new_children.push_back(new_child);
}
if (!modified)
m_cache.insert(n, n);
else
m_cache.insert(n, m_manager.mk_app(to_app(n)->get_decl(), m_new_children.size(), m_new_children.c_ptr()));
}
}
}
expr * r = 0;
m_cache.find(n, r);
SASSERT(r);
return r;
}
void normalize_vars::reset() {
m_cache.reset();
m_map.reset();
m_new_vars.reset();
m_next_var = 0;
}

47
src/spc/normalize_vars.h Normal file
View file

@ -0,0 +1,47 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
normalize_vars.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-16.
Revision History:
--*/
#ifndef _NORMALIZE_VARS_H_
#define _NORMALIZE_VARS_H_
#include"ast.h"
#include"obj_hashtable.h"
class normalize_vars {
ast_manager & m_manager;
var_ref_vector m_new_vars;
unsigned m_next_var;
ptr_vector<var> m_map;
typedef obj_map<expr, expr *> cache;
cache m_cache;
ptr_vector<expr> m_todo;
ptr_vector<expr> m_new_children;
public:
normalize_vars(ast_manager & m):
m_manager(m),
m_new_vars(m) {
}
expr * operator()(expr * n);
void reset();
unsigned get_num_vars() const { return m_new_vars.size(); }
var * const * get_vars() const { return m_new_vars.c_ptr(); }
};
#endif /* _NORMALIZE_VARS_H_ */

51
src/spc/order.cpp Normal file
View file

@ -0,0 +1,51 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
order.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-15.
Revision History:
--*/
#include"order.h"
bool order::equal(expr_offset const & _t1, expr_offset const & _t2, substitution * s) {
if (_t1 == _t2)
return true;
if (s == 0)
return false;
m_eq_todo.reset();
m_eq_todo.push_back(expr_offset_pair(_t1, _t2));
while (!m_eq_todo.empty()) {
expr_offset_pair const & p = m_eq_todo.back();
expr_offset t1 = find(p.first);
expr_offset t2 = find(p.second);
m_eq_todo.pop_back();
if (t1 == t2)
continue;
expr * n1 = t1.get_expr();
expr * n2 = t2.get_expr();
if (!is_app(n1) || !is_app(n2))
return false;
if (to_app(n1)->get_decl() != to_app(n2)->get_decl())
return false;
if (to_app(n1)->get_num_args() != to_app(n2)->get_num_args())
return false;
unsigned num = to_app(n1)->get_num_args();
for (unsigned i = 0; i < num; i++)
m_eq_todo.push_back(expr_offset_pair(expr_offset(to_app(n1)->get_arg(i), t1.get_offset()),
expr_offset(to_app(n2)->get_arg(i), t2.get_offset())));
}
return true;
}

87
src/spc/order.h Normal file
View file

@ -0,0 +1,87 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
order.h
Abstract:
Abstract class for term orderings.
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#ifndef _ORDER_H_
#define _ORDER_H_
#include"substitution.h"
#include"precedence.h"
#include"trace.h"
class order {
protected:
ast_manager & m_manager;
precedence * m_precedence;
substitution * m_subst;
typedef std::pair<expr_offset, expr_offset> expr_offset_pair;
svector<expr_offset_pair> m_eq_todo;
expr_offset find(expr_offset t) {
while (m_subst && is_var(t.get_expr()) && m_subst->find(to_var(t.get_expr()), t.get_offset(), t))
;
return t;
}
bool f_greater(func_decl * f, func_decl * g) const {
bool r = m_precedence->compare(f, g) > 0;
TRACE("order", tout << f->get_name() << " greater than " << g->get_name() << " == " << r << "\n";);
return r;
}
public:
enum result {
UNKNOWN,
UNCOMPARABLE,
EQUAL,
GREATER,
LESSER,
NOT_GTEQ
};
static bool ok(result r) { return r == EQUAL || r == GREATER || r == LESSER; }
order(ast_manager & m, precedence * p):m_manager(m), m_precedence(p) { SASSERT(p); }
virtual ~order() { dealloc(m_precedence); }
virtual void reserve(unsigned num_offsets, unsigned num_vars) {}
virtual void reserve_offsets(unsigned num_offsets) {}
virtual void reserve_vars(unsigned num_vars) {}
ast_manager & get_manager() { return m_manager; }
virtual result compare(expr_offset const & t1, expr_offset const & t2, substitution * s) = 0;
result compare(expr * t1, expr * t2, unsigned offset, substitution * s) { return compare(expr_offset(t1, offset), expr_offset(t2, offset), s); }
result compare(expr * t1, expr * t2) { return compare(expr_offset(t1, 0), expr_offset(t2, 0), 0); }
virtual bool greater(expr_offset const & t1, expr_offset const & t2, substitution * s) = 0;
bool greater(expr * t1, expr * t2) { return greater(expr_offset(t1,0), expr_offset(t2,0), 0); }
bool greater(expr * t1, expr * t2, substitution * s) { return greater(expr_offset(t1,0), expr_offset(t2,0), s); }
bool greater(expr * t1, expr * t2, unsigned offset, substitution * s) {
return greater(expr_offset(t1, offset), expr_offset(t2, offset), s);
}
/**
\brief Return a value > 0 if t1 is greater than t2, 0 if t1 == t2, and < 0 otherwise (uncomparable, unknown, lesser).
*/
virtual int compare_ge(expr_offset const & t1, expr_offset const & t2, substitution * s) = 0;
/**
\brief Return true if the given terms are equal modulo the given substitution
*/
bool equal(expr_offset const & t1, expr_offset const & t2, substitution * s);
bool equal(expr * t1, expr * t2, unsigned offset = 0, substitution * s = 0) {
return equal(expr_offset(t1, offset), expr_offset(t2, offset), s);
}
};
#endif /* _ORDER_H_ */

191
src/spc/precedence.cpp Normal file
View file

@ -0,0 +1,191 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
precedence.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-08.
Revision History:
--*/
#include"precedence.h"
#include"warning.h"
lex_precedence::lex_precedence(unsigned n, precedence ** ps):
m_precedences(n, ps) {
}
lex_precedence::~lex_precedence() {
std::for_each(m_precedences.begin(), m_precedences.end(), delete_proc<precedence>());
}
int lex_precedence::compare(func_decl * f, func_decl * g) {
int r = 0;
ptr_vector<precedence>::iterator it = m_precedences.begin();
ptr_vector<precedence>::iterator end = m_precedences.end();
for (; it != end; ++it) {
r = (*it)->compare(f, g);
if (r != 0)
return r;
}
return r;
}
inv_precedence::inv_precedence(precedence * p):
m_precedence(p) {
SASSERT(p);
}
inv_precedence::~inv_precedence() {
dealloc(m_precedence);
}
int inv_precedence::compare(func_decl * f, func_decl * g) {
return m_precedence->compare(g, f);
}
int arbitrary_precedence::compare(func_decl * f, func_decl * g) {
return static_cast<int>(f->get_decl_id()) - static_cast<int>(g->get_decl_id());
}
int arity_precedence::compare(func_decl * f, func_decl * g) {
return static_cast<int>(f->get_arity()) - static_cast<int>(g->get_arity());
}
int interpreted_precedence::compare(func_decl * f, func_decl * g) {
return static_cast<int>(f->get_family_id() == null_family_id) - static_cast<int>(g->get_family_id() == null_family_id);
}
inline int ext_precedence::get_func_pos(func_decl * f) {
unsigned id = f->get_decl_id();
return m_cached.get(id, m_undefined);
}
int ext_precedence::compare(func_decl * f, func_decl * g) {
return get_func_pos(f) - get_func_pos(g);
}
ext_precedence::ext_precedence(ast_manager & m, unsigned num_decls, func_decl ** decls):
m_undefined(num_decls),
m_cached_domain(m) {
for (unsigned i = 0; i < num_decls; i++) {
m_cached.setx(decls[i]->get_decl_id(), i, m_undefined);
m_cached_domain.push_back(decls[i]);
}
}
ext_precedence::~ext_precedence() {
}
int abstract_user_precedence::get_decl_pos(decl * d) {
unsigned id = d->get_decl_id();
int pos = m_cached.get(id, -1);
if (pos == -1) {
if (!m_symbol2pos.find(d->get_name(), pos))
pos = m_undefined;
m_cached.setx(id, pos, -1);
SASSERT(pos != -1);
}
return pos;
}
abstract_user_precedence::abstract_user_precedence(ast_manager & m, unsigned num_syms, symbol * syms):
m_undefined(num_syms),
m_cached_domain(m) {
for (unsigned i = 0; i < num_syms; i++)
m_symbol2pos.insert(syms[i], i);
}
abstract_user_precedence::~abstract_user_precedence() {
}
int user_precedence::compare(func_decl * f, func_decl * g) {
return get_decl_pos(f) - get_decl_pos(g);
}
int user_sort_precedence::compare(func_decl * f, func_decl * g) {
return get_decl_pos(f->get_range()) - get_decl_pos(g->get_range());
}
static precedence * mk_default_precedence(ast_manager & m, order_params const & params) {
ptr_buffer<precedence> ps;
if (!params.m_order_precedence.empty())
ps.push_back(alloc(user_precedence, m, params.m_order_precedence.size(), params.m_order_precedence.c_ptr()));
ps.push_back(alloc(interpreted_precedence));
ps.push_back(alloc(arity_precedence));
ps.push_back(alloc(arbitrary_precedence));
return alloc(lex_precedence, ps.size(), ps.c_ptr());
}
static precedence * mk_inv_precedence(bool inv, precedence * p) {
return inv ? alloc(inv_precedence,p) : p;
}
static precedence * mk_lex_precedence(ptr_buffer<precedence> const & ps) {
unsigned sz = ps.size();
if (sz == 0)
return alloc(arbitrary_precedence);
else if (sz == 1)
return ps[0];
else
return alloc(lex_precedence, sz, ps.c_ptr());
}
precedence * mk_precedence(ast_manager & m, order_params const & params) {
if (params.m_order_precedence_gen.empty())
return mk_default_precedence(m, params);
symbol user("user");
symbol definition("definition");
symbol interpreted("interpreted");
symbol frequency("frequency");
symbol arity("arity");
symbol arbitrary("arbitrary");
symbol inv("-");
ptr_buffer<precedence> ps;
svector<symbol>::const_iterator it = params.m_order_precedence_gen.begin();
svector<symbol>::const_iterator end = params.m_order_precedence_gen.end();
bool prev_inv = false;
for (; it != end; ++it) {
symbol curr = *it;
if (curr == user) {
if (params.m_order_precedence.empty())
ps.push_back(mk_inv_precedence(prev_inv, alloc(user_precedence, m, params.m_order_precedence.size(), params.m_order_precedence.c_ptr())));
}
else if (curr == definition) {
warning_msg("definition precedence was not implement yet.");
}
else if (curr == interpreted) {
ps.push_back(mk_inv_precedence(prev_inv, alloc(interpreted_precedence)));
}
else if (curr == frequency) {
warning_msg("frequency precedence was not implement yet.");
}
else if (curr == arity) {
ps.push_back(mk_inv_precedence(prev_inv, alloc(arity_precedence)));
}
else if (curr == arbitrary) {
ps.push_back(mk_inv_precedence(prev_inv, alloc(arbitrary_precedence)));
// it is pointless to continue, arbitrary_precedence is a total order
return mk_lex_precedence(ps);
}
else if (curr == inv) {
prev_inv = true;
}
else {
warning_msg("invalid precedence generator: ignoring atom '%s'.", curr.bare_str());
}
}
return mk_lex_precedence(ps);
}

142
src/spc/precedence.h Normal file
View file

@ -0,0 +1,142 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
precedence.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-08.
Revision History:
--*/
#ifndef _PRECEDENCE_H_
#define _PRECEDENCE_H_
#include"ast.h"
#include"order_params.h"
/**
\brief Abstract functor used to implement an order on function symbols.
*/
class precedence {
public:
virtual ~precedence() {}
virtual int compare(func_decl * f, func_decl * g) = 0;
bool operator()(func_decl * f, func_decl * g) { return compare(f, g) < 0; }
};
/**
\brief Compose different precedence functors using lexicographical order.
*/
class lex_precedence : public precedence {
ptr_vector<precedence> m_precedences;
public:
lex_precedence(unsigned n, precedence ** ps);
virtual ~lex_precedence();
virtual int compare(func_decl * f, func_decl * g);
};
/**
\brief Invert functor
*/
class inv_precedence : public precedence {
precedence * m_precedence;
public:
inv_precedence(precedence * p);
virtual ~inv_precedence();
virtual int compare(func_decl * f, func_decl * g);
};
/**
\brief An arbitrary total order based on the func_decl ids.
*/
class arbitrary_precedence : public precedence {
public:
virtual int compare(func_decl * f, func_decl * g);
};
/**
\brief Precedence based on the arity.
\remark This is not a total order, so it must be combined
with other precedence functors (e.g., arbitrary_precedence).
*/
class arity_precedence : public precedence {
public:
virtual int compare(func_decl * f, func_decl * g);
};
/**
\brief Interpreted function symbols are smaller.
*/
class interpreted_precedence : public precedence {
public:
virtual int compare(func_decl * f, func_decl * g);
};
/**
\brief A precedence given as a sequence of func_decls.
This functor is used to encapsulate automatically/externally generated
precedences.
*/
class ext_precedence : public precedence {
unsigned m_undefined; // position for func_decl's not specified by the user.
int_vector m_cached; // mapping: decl -> int
decl_ref_vector m_cached_domain;
int get_func_pos(func_decl * f);
public:
ext_precedence(ast_manager & m, unsigned num_decls, func_decl ** decls);
virtual ~ext_precedence();
virtual int compare(func_decl * f, func_decl * g);
};
/**
\brief Abstract class for user precedences based on
function or sort symbols.
*/
class abstract_user_precedence : public precedence {
protected:
symbol_table<int> m_symbol2pos;
unsigned m_undefined; // position for symbols not specified by the user.
int_vector m_cached; // mapping: decl -> int
decl_ref_vector m_cached_domain;
int get_decl_pos(decl * d);
public:
abstract_user_precedence(ast_manager & m, unsigned num_syms, symbol * syms);
virtual ~abstract_user_precedence();
};
/**
\brief A user defined precedence given as a sequence of symbols.
\remark User provided precedences are usually not total.
*/
class user_precedence : public abstract_user_precedence {
public:
user_precedence(ast_manager & m, unsigned num_syms, symbol * syms):abstract_user_precedence(m, num_syms, syms) {}
virtual int compare(func_decl * f, func_decl * g);
};
/**
\brief A user defined precedence given as a sequence of sort symbols.
The functions are ordered based on their range sort.
*/
class user_sort_precedence : public abstract_user_precedence {
public:
user_sort_precedence(ast_manager & m, unsigned num_syms, symbol * syms):abstract_user_precedence(m, num_syms, syms) {}
virtual int compare(func_decl * f, func_decl * g);
};
precedence * mk_precedence(ast_manager & m, order_params const & params);
#endif /* _PRECEDENCE_H_ */

155
src/spc/preprocessor.cpp Normal file
View file

@ -0,0 +1,155 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
preprocessor.cpp
Abstract:
Preprocessor
Author:
Leonardo de Moura (leonardo) 2008-01-17.
Revision History:
--*/
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"preprocessor.h"
#include"for_each_expr.h"
#include"num_occurs.h"
preprocessor::preprocessor(ast_manager & m, defined_names & d, simplifier & s, preprocessor_params & p):
m_params(p),
m_manager(m),
m_simp(s),
m_nnf(m, d, p),
m_cnf(m, d, p),
m_der(m),
m_push_app_ite(s, p.m_lift_ite == LI_CONSERVATIVE),
m_cnf_todo(m),
m_cnf_todo_prs(m),
m_push_todo(m),
m_push_todo_prs(m) {
switch (m_params.m_cnf_mode) {
case CNF_QUANT:
if (m_params.m_nnf_mode == NNF_SKOLEM)
m_params.m_nnf_mode = NNF_QUANT;
break;
case CNF_OPPORTUNISTIC:
if (m_params.m_nnf_mode == NNF_SKOLEM)
m_params.m_nnf_mode = NNF_QUANT;
break;
case CNF_FULL:
m_params.m_nnf_mode = NNF_FULL;
break;
default:
break;
}
}
#ifdef _TRACE
struct num_occurs_pp {
ast_manager & m_manager;
std::ostream & m_out;
num_occurs m_occurs;
num_occurs_pp(ast_manager & m, std::ostream & out, expr * root):
m_manager(m),
m_out(out) {
m_occurs(root);
}
void operator()(var * n) {}
void operator()(app * n) {
unsigned val = m_occurs.get_num_occs(n);
if (val > 1 && m_manager.is_bool(n))
m_out << "#" << n->get_id() << " -> " << val << " " << n->get_ref_count() << "\n";
}
void operator()(quantifier * n) {}
};
#endif
void preprocessor::operator()(expr * e, proof * in_pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
m_cnf_todo.reset();
m_cnf_todo_prs.reset();
expr_ref r1(m_manager);
proof_ref pr1(m_manager);
m_simp(e, r1, pr1);
in_pr = m_manager.mk_modus_ponens(in_pr, pr1);
expr_ref r2(m_manager);
proof_ref pr2(m_manager);
m_nnf(r1, m_cnf_todo, m_cnf_todo_prs, r2, pr2);
in_pr = m_manager.mk_modus_ponens(in_pr, pr2);
TRACE("preprocessor", tout << mk_ll_pp(r2, m_manager);
num_occurs_pp proc(m_manager, tout, r2);
for_each_expr(proc, r2););
m_cnf_todo.push_back(r2);
m_cnf_todo_prs.push_back(in_pr);
unsigned sz = m_cnf_todo.size();
for (unsigned i = 0; i < sz; i++) {
m_push_todo.reset();
m_push_todo_prs.reset();
expr * e = m_cnf_todo.get(i);
if (m_params.m_lift_ite != LI_NONE) {
m_push_app_ite(e, r1, pr1);
}
else {
r1 = e;
pr1 = 0;
}
TRACE("preprocessor", tout << mk_ll_pp(r1, m_manager););
expr_ref aux(r1, m_manager);
m_simp(aux, r1, pr2);
pr1 = m_manager.mk_transitivity(pr1, pr2);
TRACE("preprocessor", tout << mk_ll_pp(r1, m_manager););
aux = r1;
m_der(aux, r1, pr2);
pr1 = m_manager.mk_transitivity(pr1, pr2);
TRACE("preprocessor", tout << mk_ll_pp(r1, m_manager););
if (m_manager.proofs_enabled())
in_pr = m_manager.mk_modus_ponens(m_cnf_todo_prs.get(i), pr1);
else
in_pr = 0;
aux = r1;
m_cnf(aux, m_push_todo, m_push_todo_prs, r1, pr1);
m_push_todo.push_back(r1);
TRACE("preprocessor", tout << mk_ll_pp(r1, m_manager););
if (m_manager.proofs_enabled()) {
in_pr = m_manager.mk_modus_ponens(in_pr, pr1);
m_push_todo_prs.push_back(in_pr);
}
unsigned sz2 = m_push_todo.size();
for (unsigned j = 0; j < sz2; j++) {
expr * e = m_push_todo.get(j);
m_simp(e, r1, pr1);
TRACE("preprocessor", tout << mk_ll_pp(r1, m_manager););
if (m_manager.proofs_enabled())
in_pr = m_manager.mk_modus_ponens(m_push_todo_prs.get(j), pr1);
else
in_pr = 0;
push_assertion(m_manager, r1, in_pr, result, result_prs);
}
}
}

51
src/spc/preprocessor.h Normal file
View file

@ -0,0 +1,51 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
preprocessor.h
Abstract:
Preprocess AST before adding them to the logical context
Author:
Leonardo de Moura (leonardo) 2008-01-17.
Revision History:
--*/
#ifndef _PREPROCESSOR_H_
#define _PREPROCESSOR_H_
#include"preprocessor_params.h"
#include"simplifier.h"
#include"pattern_inference.h"
#include"nnf.h"
#include"cnf.h"
#include"der.h"
#include"push_app_ite.h"
/**
\brief Functor used to preprocess expressions before adding them to
the logical context.
*/
class preprocessor {
preprocessor_params & m_params;
ast_manager & m_manager;
simplifier & m_simp;
nnf m_nnf;
cnf m_cnf;
der_star m_der;
push_app_ite m_push_app_ite;
expr_ref_vector m_cnf_todo;
proof_ref_vector m_cnf_todo_prs;
expr_ref_vector m_push_todo;
proof_ref_vector m_push_todo_prs;
public:
preprocessor(ast_manager & m, defined_names & d, simplifier & s, preprocessor_params & p);
void operator()(expr * e, proof * in_pr, expr_ref_vector & result, proof_ref_vector & result_prs);
};
#endif /* _PREPROCESSOR_H_ */

107
src/spc/sparse_use_list.h Normal file
View file

@ -0,0 +1,107 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
sparse_use_list.h
Abstract:
Sparse use list index.
Author:
Leonardo de Moura (leonardo) 2008-02-13.
Revision History:
--*/
#ifndef _SPARSE_USE_LIST_H_
#define _SPARSE_USE_LIST_H_
#include"ast.h"
#include"obj_hashtable.h"
/**
\brief (Generic sparse) use-list data-structure.
*/
template<typename T, typename Set>
class sparse_use_list {
typedef obj_map<T, Set *> use_list;
use_list m_use_list;
public:
typedef typename Set::iterator iterator;
sparse_use_list() {}
~sparse_use_list() {
reset();
}
void insert(typename Set::data const & parent, T * child) {
Set * parents = 0;
if (!m_use_list.find(child, parents)) {
parents = alloc(Set);
m_use_list.insert(child, parents);
}
SASSERT(parents);
parents->insert(parent);
}
/**
\brief Return 0 if child did not contain any parents.
Return 1, if child does not have more parents after
removing parent.
Return 2 otherwise.
*/
unsigned erase(typename Set::data const & parent, T * child) {
Set * parents = 0;
if (m_use_list.find(child, parents)) {
parents->erase(parent);
if (parents->empty()) {
dealloc(parents);
m_use_list.erase(child);
return 1;
}
return 2;
}
return 0;
}
void reset() {
typename use_list::iterator it = m_use_list.begin();
typename use_list::iterator end = m_use_list.end();
for (; it != end; ++it)
dealloc(it->m_value);
m_use_list.reset();
}
Set * get_parents(T * e) {
Set * parents = 0;
m_use_list.find(e, parents);
return parents;
}
iterator begin(T * e) {
Set * parents = 0;
m_use_list.find(e, parents);
SASSERT(parents);
return parents->begin();
}
iterator end(T * e) {
Set * parents = 0;
m_use_list.find(e, parents);
SASSERT(parents);
return parents->end();
}
bool empty(T * e) const {
Set * parents = 0;
if (m_use_list.find(e, parents))
return parents->empty();
return true;
}
};
#endif /* _SPARSE_USE_LIST_H_ */

View file

@ -0,0 +1,170 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_asserted_literals.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-14.
Revision History:
--*/
#include"spc_asserted_literals.h"
#include"ast_pp.h"
namespace spc {
asserted_literals::asserted_literals(ast_manager & m):
m_manager(m),
m_subst(m),
m_tmp_eq1(2),
m_tmp_eq2(2) {
for (unsigned i = 0; i < 2; i++) {
m_st[i] = alloc(substitution_tree, m_manager);
m_expr2clause[i] = alloc(expr2clause);
}
m_subst.reserve_offsets(3);
}
asserted_literals::~asserted_literals() {
for (unsigned i = 0; i < 2; i++) {
dealloc(m_st[i]);
dealloc(m_expr2clause[i]);
}
}
void asserted_literals::insert(clause * cls) {
if (cls->get_num_literals() == 1) {
TRACE("asserted_literals", tout << "inserting clause into asserted_literals index:\n";
cls->display(tout, m_manager); tout << "\n";);
literal const & l = cls->get_literal(0);
unsigned neg = static_cast<unsigned>(l.sign());
expr * atom = l.atom();
m_st[neg]->insert(to_app(atom));
m_expr2clause[neg]->insert(atom, cls);
m_subst.reserve_vars(m_st[neg]->get_approx_num_regs());
}
}
void asserted_literals::erase(clause * cls) {
if (cls->get_num_literals() == 1) {
literal const & l = cls->get_literal(0);
unsigned neg = static_cast<unsigned>(l.sign());
expr * atom = l.atom();
m_expr2clause[neg]->erase(atom);
m_st[neg]->erase(to_app(atom));
}
}
void asserted_literals::reset() {
for (unsigned i = 0; i < 2; i++) {
m_st[i]->reset();
m_expr2clause[i]->reset();
}
}
struct asserted_literals_visitor : public st_visitor {
expr * m_target;
asserted_literals_visitor(substitution & s):st_visitor(s), m_target(0) {}
virtual bool operator()(expr * e) {
m_target = e;
return false; // stop
}
};
/**
\brief Return an unit clause that is a generalization
of the given literal.
Return 0 if such clause does not exist.
*/
clause * asserted_literals::gen(expr * atom, bool n) {
if (is_app(atom)) {
TRACE("asserted_literals", tout << "checking if there is generalizer for: " << n << "\n" <<
mk_pp(atom, m_manager) << "\n";);
unsigned neg = static_cast<unsigned>(n);
m_subst.reset_subst();
asserted_literals_visitor visitor(m_subst);
TRACE("asserted_literals_bug", tout << "query: " << mk_pp(atom, m_manager) << "\n"; m_st[neg]->display(tout);
m_subst.display(tout););
m_st[neg]->gen(to_app(atom), visitor);
if (visitor.m_target != 0) {
clause * cls = 0;
m_expr2clause[neg]->find(visitor.m_target, cls);
SASSERT(cls);
return cls;
}
if (m_manager.is_eq(atom)) {
m_subst.reset();
m_tmp_eq1.copy_swapping_args(to_app(atom));
m_st[neg]->gen(m_tmp_eq1.get_app(), visitor);
if (visitor.m_target != 0) {
clause * cls = 0;
m_expr2clause[neg]->find(visitor.m_target, cls);
SASSERT(cls);
return cls;
}
}
}
return 0;
}
/**
\brief Return an unit clause that is a generalization
of the equality (= lhs rhs)
Return 0 if such clause does not exist.
*/
clause * asserted_literals::gen_eq(expr * lhs, expr * rhs) {
expr * args[2] = { lhs, rhs };
func_decl_ref eq_decl(m_manager.mk_func_decl(m_manager.get_basic_family_id(), OP_EQ, 0, 0, 2, args), m_manager);
m_tmp_eq2.set_decl(eq_decl);
m_tmp_eq2.set_arg(0, lhs);
m_tmp_eq2.set_arg(1, rhs);
return gen(m_tmp_eq2.get_app(), false);
}
/**
\brief Return a unit equality clause (= s t) that (eq) subsumes (= lhs rhs).
That is, lhs and rhs have the form u[s'] and u[t'] and there is
a substitution sigma s.t. sigma(s) = s' and sigma(t) = t'.
Return 0 if such clause does not exist.
*/
clause * asserted_literals::subsumes(expr * lhs, expr * rhs) {
while (true) {
TRACE("eq_subsumption", tout << "eq_subsumption loop:\n" << mk_pp(lhs, m_manager) << "\n" <<
mk_pp(rhs, m_manager) << "\n";);
clause * subsumer = gen_eq(lhs, rhs);
if (subsumer)
return subsumer;
if (!is_app(lhs) || !is_app(rhs) ||
to_app(lhs)->get_decl() != to_app(rhs)->get_decl() ||
to_app(lhs)->get_num_args() != to_app(rhs)->get_num_args())
return 0;
expr * d1 = 0;
expr * d2 = 0;
unsigned num_args = to_app(lhs)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * c1 = to_app(lhs)->get_arg(i);
expr * c2 = to_app(rhs)->get_arg(i);
if (c1 != c2) {
if (d1)
return 0;
d1 = c1;
d2 = c2;
}
}
SASSERT(d1);
lhs = d1;
rhs = d2;
}
return 0;
}
};

View file

@ -0,0 +1,69 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_asserted_literals.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-14.
Revision History:
--*/
#ifndef _SPC_ASSERTED_LITERALS_H_
#define _SPC_ASSERTED_LITERALS_H_
#include"spc_clause.h"
#include"substitution_tree.h"
#include"obj_hashtable.h"
namespace spc {
/**
\brief Index for the asserted literals in the logical context.
This index is used to implement forward unit subsumption,
equality subsumption, positive simplify-reflect, and
negative simplify-reflect.
*/
class asserted_literals {
protected:
typedef obj_map<expr, clause*> expr2clause;
ast_manager & m_manager;
substitution_tree * m_st[2];
expr2clause * m_expr2clause[2];
substitution m_subst;
tmp_app m_tmp_eq1;
tmp_app m_tmp_eq2;
public:
asserted_literals(ast_manager & m);
~asserted_literals();
void insert(clause * cls);
void erase(clause * cls);
void reset();
void reserve_vars(unsigned num_vars) { m_subst.reserve_vars(num_vars); }
clause * gen(literal const & l) {
return gen(l.atom(), l.sign());
}
clause * gen(expr * atom, bool neg);
clause * gen_eq(expr * lhs, expr * rhs);
clause * subsumes(expr * lhs, expr * rhs);
bool has_pos_literals() const { return !m_st[0]->empty(); }
bool has_neg_literals() const { return !m_st[1]->empty(); }
bool has_literals() const { return has_pos_literals() || has_neg_literals(); }
};
};
#endif /* _SPC_ASSERTED_LITERALS_H_ */

287
src/spc/spc_clause.cpp Normal file
View file

@ -0,0 +1,287 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_clause.cpp
Abstract:
Superposition Calculus Clause
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#include"spc_clause.h"
#include"splay_tree_def.h"
template class splay_tree<spc::clause *, spc::clause::compare>;
namespace spc {
clause::clause(ast_manager & m, unsigned num_lits, literal * lits, justification * p, unsigned scope_lvl):
m_id(UINT_MAX),
m_time(UINT_MAX),
m_scope_lvl(scope_lvl),
m_bidx(UINT_MAX),
m_processed(false),
m_indexed(false),
m_has_sel_lit(false),
m_justification(p) {
set_fields(num_lits, lits);
m_num_lits_capacity = m_num_lits[0] + m_num_lits[1];
memcpy(m_lits, lits, sizeof(literal) * get_num_literals());
for (unsigned i = 0; i < num_lits; i++)
m.inc_ref(m_lits[i].atom());
m_justification->inc_ref();
m_justification->set_owner(this);
sort_literals();
}
clause * clause::mk(ast_manager & m, unsigned num_lits, literal * lits, justification * p, unsigned scope_lvl) {
void * mem = m.get_allocator().allocate(sizeof(clause) + num_lits * sizeof(literal));
return new (mem) clause(m, num_lits, lits, p, scope_lvl);
}
void clause::init(unsigned id, unsigned time) {
SASSERT(m_id == UINT_MAX);
SASSERT(m_time == UINT_MAX);
m_id = id;
m_time = time;
m_proof_depth = 0;
justification_stat j_stat;
get_justification_stat(m_justification, j_stat);
m_proof_depth = j_stat.m_proof_depth;
if (j_stat.m_max_scope_lvl > m_scope_lvl)
m_scope_lvl = j_stat.m_max_scope_lvl;
update_parents(j_stat.m_parent_clauses);
}
void clause::update_parents(ptr_buffer<clause> & parents) {
ptr_buffer<clause>::iterator it = parents.begin();
ptr_buffer<clause>::iterator end = parents.end();
for (; it != end; ++it) {
clause * parent = *it;
parent->add_child(this);
}
}
void clause::deallocate(ast_manager & m) {
justification_stat j_stat;
get_justification_stat(get_justification(), j_stat);
ptr_buffer<clause>::iterator it = j_stat.m_parent_clauses.begin();
ptr_buffer<clause>::iterator end = j_stat.m_parent_clauses.end();
for (; it != end; ++it) {
clause * parent = *it;
parent->del_child(this);
}
dec_ref(get_justification(), m);
unsigned num_lits = get_num_literals();
for (unsigned i = 0; i < num_lits; i++)
m.dec_ref(get_literal(i).atom());
unsigned capacity = get_num_literals_capacity();
this->~clause();
m.get_allocator().deallocate(sizeof(clause) + capacity * sizeof(literal), this);
}
void clause::select_literal(unsigned idx) {
SASSERT(idx < get_num_literals());
m_lits[idx].set_selected(true);
m_has_sel_lit = true;
}
/**
\brief Return true if l is maximal in the clause, given a substitution s.
s(l) is considered maximal if there is no literal l' in the clause such s(l') is greater
than s(l).
*/
bool clause::is_maximal(order & o, literal const & l, unsigned offset, substitution * s) const {
unsigned num_lits = get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l_prime = m_lits[i];
if (l != l_prime && greater(o, l_prime, l, offset, s))
return false;
}
return true;
}
/**
\brief Return true if l is a maximal selected literal in the clause, given a substitution s.
s(l) is considered maximal selected literal if there is no
selected literal l' in the clause such s(l') is greater than s(l).
*/
bool clause::is_sel_maximal(order & o, literal const & l, unsigned offset, substitution * s) const {
if (!l.is_selected())
return false;
unsigned num_lits = get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l_prime = m_lits[i];
if (l != l_prime && l_prime.is_selected() && greater(o, l_prime, l, offset, s))
return false;
}
return true;
}
/**
\brief Return true if l is eligible for resolution.
*/
bool clause::is_eligible_for_resolution(order & o, literal const & l, unsigned offset, substitution * s) const {
if (has_sel_lit())
return is_sel_maximal(o, l, offset, s);
else
return is_maximal(o, l, offset, s);
}
/**
\brief Return true if l is eligible for paramodulation.
*/
bool clause::is_eligible_for_paramodulation(order & o, literal const & l, unsigned offset, substitution * s) const {
return !has_sel_lit() && is_maximal(o, l, offset, s);
}
/**
\brief Try to orient literals.
*/
void clause::try_to_orient_literals(order & o) {
o.reserve_vars(get_num_vars());
unsigned num_lits = get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal & l = m_lits[i];
l.try_to_orient(o);
}
}
void clause::set_fields(unsigned num_lits, literal * lits) {
clause_stat c_stat;
get_clause_stat(num_lits, lits, c_stat);
m_num_vars = c_stat.m_max_var_idx + 1;
m_sym_count = c_stat.m_sym_count;
m_const_count = c_stat.m_const_count;
m_depth = c_stat.m_depth;
m_num_lits[0] = c_stat.m_num_lits[0];
m_num_lits[1] = c_stat.m_num_lits[1];
m_ground = c_stat.m_ground;
}
struct lit_lt {
bool operator()(literal const & l1, literal const & l2) const {
if (l1.is_ground() > l2.is_ground())
return true;
if (l1.is_ground() != l2.is_ground())
return false;
if (l1.get_approx_depth() > l2.get_approx_depth())
return true;
if (l1.get_approx_depth() != l2.get_approx_depth())
return false;
if (l1.get_approx_sym_count() > l2.get_approx_sym_count())
return true;
if (l1.get_approx_sym_count() != l2.get_approx_sym_count())
return false;
if (l1.get_approx_const_count() > l2.get_approx_const_count())
return true;
if (l1.get_approx_const_count() != l2.get_approx_const_count())
return false;
return l1.get_id() < l2.get_id();
}
};
/**
\brief Sort literals to improve the performance of subsumption tests.
*/
void clause::sort_literals() {
DEBUG_CODE({
unsigned num_lits = get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
SASSERT(m_lits[i].has_stats());
}
});
std::sort(m_lits, m_lits + get_num_literals(), lit_lt());
}
/**
\brief Replace clause literal with the given literals.
Use the given justification to justify the new clause.
*/
void clause::update_lits(ast_manager & m, unsigned num_lits, literal * lits, justification * j) {
unsigned old_num_lits = get_num_literals();
SASSERT(num_lits <= old_num_lits);
for (unsigned i = 0; i < num_lits; i++)
m.inc_ref(lits[i].atom());
for (unsigned i = 0; i < old_num_lits; i++)
m.dec_ref(m_lits[i].atom());
for (unsigned i = 0; i < num_lits; i++)
m_lits[i] = lits[i];
set_fields(num_lits, m_lits);
SASSERT(get_num_literals() == num_lits);
j->inc_ref();
m_justification->set_owner(0); // release ownership
dec_ref(m_justification, m);
m_justification = j;
m_justification->set_owner(this);
sort_literals();
justification_stat j_stat;
get_justification_stat(m_justification, j_stat);
m_proof_depth = j_stat.m_proof_depth;
SASSERT(m_scope_lvl == j_stat.m_max_scope_lvl);
update_parents(j_stat.m_parent_clauses);
}
void clause::display(std::ostream & out, ast_manager & m, bool detailed) {
if (get_num_literals() == 0) {
out << "empty-clause";
return;
}
out << "#" << m_id << ": (clause ";
spc::display(out, get_num_literals(), m_lits, m, detailed);
out << ")";
if (m_processed)
out << "*";
}
void get_clause_stat(unsigned num_lits, literal * lits, clause_stat & stat) {
for (unsigned i = 0; i < num_lits; i++) {
literal_stat c;
lits[i].get_stat(c);
stat.m_sym_count += c.m_sym_count;
stat.m_depth = std::max(stat.m_depth, c.m_depth);
stat.m_max_var_idx = std::max(stat.m_max_var_idx, c.m_max_var_idx);
stat.m_const_count += c.m_const_count;
stat.m_ground &= c.m_ground;
stat.m_num_lits[static_cast<unsigned>(lits[i].sign())]++;
}
}
};

152
src/spc/spc_clause.h Normal file
View file

@ -0,0 +1,152 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_clause.h
Abstract:
Superposition Calculus Clause
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _SPC_CLAUSE_H_
#define _SPC_CLAUSE_H_
#include"ast.h"
#include"splay_tree.h"
#include"use_list.h"
#include"spc_literal.h"
#include"spc_justification.h"
#include"use_list.h"
namespace spc {
class context;
/**
\brief Superposition Calculus clause.
*/
class clause {
struct compare {
// ignoring potential overflow/underflow
int operator()(clause * c1, clause * c2) const {
return static_cast<int>(c1->get_id()) - static_cast<int>(c2->get_id());
}
};
public:
typedef splay_tree<clause *, compare> set;
private:
unsigned m_id; // clause unique id
unsigned m_time; // how old is the clause.
unsigned m_num_vars; // approx. number of variables (i.e., max_var_id + 1)
unsigned m_sym_count; // number of symbols
unsigned m_const_count; // number of constants
unsigned m_depth; // depth (i.e., max depth of a literal)
unsigned m_proof_depth;
unsigned m_scope_lvl; // which scope level owns the clause
unsigned m_num_lits[2]; // number of positive [0] and negative [1] literals.
unsigned m_num_lits_capacity; // some of the clause literals can be simplified and removed, this field contains the original number of literals (used for GC).
unsigned m_bidx; // position on the backtracking stack
bool m_ground:1;
bool m_processed:1;
bool m_indexed:1;
bool m_has_sel_lit:1;
justification * m_justification;
set m_children;
literal m_lits[0];
friend class context;
void set_fields(unsigned num_lits, literal * lits);
unsigned get_bidx() const { return m_bidx; }
void init(unsigned idx, unsigned time);
void update_parents(ptr_buffer<clause> & parents);
void set_bidx(unsigned idx) { SASSERT(m_bidx == UINT_MAX); m_bidx = idx; }
void add_child(clause * c) { m_children.insert(c); }
void del_child(clause * c) { m_children.erase(c); }
void set_processed(bool f) { m_processed = f; }
void set_indexed(bool f) { m_indexed = f; }
void sort_literals();
/**
\brief Release ownership of the justification.
*/
justification * release_justification() { justification * r = m_justification; m_justification = 0; return r; }
clause(ast_manager & m, unsigned num_lits, literal * lits, justification * p, unsigned scope_lvl);
public:
static clause * mk(ast_manager & m, unsigned num_lits, literal * lits, justification * p, unsigned scope_lvl);
void deallocate(ast_manager & m);
unsigned get_id() const { SASSERT(m_id != UINT_MAX); return m_id; }
unsigned get_time() const { return m_time; }
unsigned get_symbol_count() const { return m_sym_count; }
unsigned get_proof_depth() const { return m_proof_depth; }
unsigned get_num_literals() const { return m_num_lits[0] + m_num_lits[1]; }
unsigned get_num_literals_capacity() const { return m_num_lits_capacity; }
unsigned get_num_pos_literals() const { return m_num_lits[0]; }
unsigned get_num_neg_literals() const { return m_num_lits[1]; }
unsigned get_depth() const { return m_depth; }
unsigned get_const_count() const { return m_const_count; }
unsigned get_scope_lvl() const { return m_scope_lvl; }
unsigned get_num_vars() const { return m_num_vars; }
bool empty() const { return m_num_lits[0] == 0 && m_num_lits[1] == 0; }
literal const & get_literal(unsigned idx) const { return m_lits[idx]; }
literal & get_literal(unsigned idx) { return m_lits[idx]; }
literal * get_literals() const { return const_cast<literal*>(m_lits); }
justification * get_justification() const { return m_justification; }
bool is_processed() const { return m_processed; }
bool is_indexed() const { return m_indexed; }
bool is_ground() const { return m_ground; }
void select_literal(unsigned idx);
bool is_maximal(order & o, literal const & l, unsigned offset = 0, substitution * s = 0) const;
bool is_sel_maximal(order & o, literal const & l, unsigned offset = 0, substitution * s = 0) const ;
bool is_eligible_for_resolution(order & o, literal const & l, unsigned offset = 0, substitution * s = 0) const;
bool is_eligible_for_paramodulation(order & o, literal const & l, unsigned offset = 0, substitution * s = 0) const;
bool has_sel_lit() const { return m_has_sel_lit; }
void try_to_orient_literals(order & o);
void update_lits(ast_manager & m, unsigned num_lits, literal * lits, justification * j);
void display(std::ostream & out, ast_manager & m, bool detailed = false);
unsigned hash() const { return m_id; }
};
typedef ptr_vector<clause> clause_vector;
/**
\brief Clause Statistics (used to build clauses, subsumption, etc).
*/
struct clause_stat : public expr_stat {
unsigned m_num_lits[2];
clause_stat() {
m_num_lits[0] = 0;
m_num_lits[1] = 0;
}
};
/**
\brief Compute the statistics for a clause with num_lits
literals lits, and store the results in stat.
*/
void get_clause_stat(unsigned num_lits, literal * lits, clause_stat & stat);
/**
\brief A mapping from clause-id's to clauses
*/
class id2clause {
ptr_vector<clause> m_clauses;
public:
void insert(clause * c) { return m_clauses.setx(c->get_id(), c, 0); }
void erase(clause * c) { unsigned id = c->get_id(); if (id < m_clauses.size()) m_clauses[id] = 0; }
clause * operator()(unsigned id) const { return m_clauses.get(id, 0); }
};
};
#endif /* _SPC_CLAUSE_H_ */

View file

@ -0,0 +1,58 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_clause_pos_set.h
Abstract:
A set of pairs (clause, index).
Author:
Leonardo de Moura (leonardo) 2008-02-16.
Revision History:
--*/
#ifndef _SPC_CLAUSE_POS_SET_H_
#define _SPC_CLAUSE_POS_SET_H_
#include"hashtable.h"
namespace spc {
typedef std::pair<clause *, unsigned> clause_pos_pair;
class clause_pos_entry {
clause_pos_pair m_data;
public:
typedef clause_pos_pair data;
clause_pos_entry() { m_data.first = 0; }
unsigned get_hash() const { return m_data.first->get_id(); }
bool is_free() const { return m_data.first == 0; }
bool is_deleted() const { return m_data.first == reinterpret_cast<clause *>(1); }
bool is_used() const {
return m_data.first != reinterpret_cast<clause *>(0) && m_data.first != reinterpret_cast<clause *>(1);
}
clause_pos_pair const & get_data() const { return m_data; }
clause_pos_pair & get_data() { return m_data; }
void set_data(clause_pos_pair const & d) {
SASSERT(d.first != 0 && d.first != reinterpret_cast<clause*>(1));
m_data = d;
}
void set_hash(unsigned h) { SASSERT(m_data.first->get_id() == h); }
void mark_as_deleted() { m_data.first = reinterpret_cast<clause *>(1); }
void mark_as_free() { m_data.first = 0; }
};
struct clause_pos_pair_hash {
unsigned operator()(clause_pos_pair const & p) const { return p.first->get_id(); }
};
typedef core_hashtable<clause_pos_entry, clause_pos_pair_hash, default_eq<clause_pos_pair> > clause_pos_set;
};
#endif /* _SPC_CLAUSE_POS_SET_H_ */

View file

@ -0,0 +1,121 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_clause_selection.cpp
Abstract:
Superposition Calculus Clause Selection
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#include"spc_clause_selection.h"
namespace spc {
const unsigned default_heap_size = 1024;
clause_selection::clause_selection(unsigned num_heaps, clause_eval * const * fs, unsigned * slot_size):
m_curr_slot(0),
m_counter(0),
m_fs(num_heaps, fs) {
SASSERT(num_heaps > 0);
for (unsigned i = 0; i < num_heaps; i++) {
m_heaps.push_back(alloc(heap<lt>, default_heap_size, lt(m_id2clause, *(fs[i]))));
SASSERT(slot_size[i] > 0);
m_slot_size.push_back(slot_size[i]);
}
}
clause_selection::~clause_selection() {
std::for_each(m_heaps.begin(), m_heaps.end(), delete_proc<heap<lt> >());
std::for_each(m_fs.begin(), m_fs.end(), delete_proc<clause_eval>());
}
void clause_selection::reserve(unsigned cid) {
unsigned capacity = m_heaps[0]->get_bounds();
if (cid >= capacity) {
unsigned new_capacity = 2 * cid + 1;
SASSERT(cid < new_capacity);
ptr_vector<heap<lt> >::iterator it = m_heaps.begin();
ptr_vector<heap<lt> >::iterator end = m_heaps.end();
for (; it != end; ++it) {
heap<lt> * h = *it;
h->reserve(new_capacity);;
}
}
}
void clause_selection::reset() {
ptr_vector<heap<lt> >::iterator it = m_heaps.begin();
ptr_vector<heap<lt> >::iterator end = m_heaps.end();
for (; it != end; ++it) {
heap<lt> * h = *it;
h->reset();
}
}
void clause_selection::insert(clause * c) {
reserve(c->get_id());
m_id2clause.insert(c);
ptr_vector<heap<lt> >::iterator it = m_heaps.begin();
ptr_vector<heap<lt> >::iterator end = m_heaps.end();
for (; it != end; ++it) {
heap<lt> * h = *it;
h->insert(c->get_id());
}
}
void clause_selection::erase(clause * c) {
// remark: it is not necessary to remove c from m_id2clause
ptr_vector<heap<lt> >::iterator it = m_heaps.begin();
ptr_vector<heap<lt> >::iterator end = m_heaps.end();
SASSERT(it != end);
if (!(*it)->contains(c->get_id()))
return;
for (; it != end; ++it) {
heap<lt> * h = *it;
h->erase(c->get_id());
}
}
bool clause_selection::empty() const {
ptr_vector<heap<lt> >::const_iterator it = m_heaps.begin();
ptr_vector<heap<lt> >::const_iterator end = m_heaps.end();
for (; it != end; ++it)
if (!(*it)->empty())
return false;
return true;
}
clause * clause_selection::get_best() {
heap<lt> * h = m_heaps[m_curr_slot];
if (h->empty())
return 0;
unsigned cid = m_heaps[m_curr_slot]->erase_min();
clause * c = m_id2clause(cid);
SASSERT(c);
// remove clause from the other heaps
unsigned num_heaps = m_heaps.size();
for (unsigned i = 0; i < num_heaps; i++) {
if (m_curr_slot != i)
m_heaps[i]->erase(cid);
}
// remark: it is not necessary to remove c from m_id2clause
m_counter++;
if (m_counter >= m_slot_size[m_curr_slot]) {
m_counter = 0;
m_curr_slot++;
if (m_curr_slot >= m_slot_size.size())
m_curr_slot = 0;
}
return c;
}
};

View file

@ -0,0 +1,85 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_clause_selection.h
Abstract:
Superposition Calculus Clause Selection
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _SPC_CLAUSE_SELECTION_H_
#define _SPC_CLAUSE_SELECTION_H_
#include"spc_clause.h"
#include"heap.h"
namespace spc {
/**
\brief Abstract functor for evaluating how 'good' a clause is.
Smaller values mean better clauses.
*/
struct clause_eval {
virtual ~clause_eval() {}
virtual unsigned operator()(clause * c) const = 0;
};
/**
\brief Clause selection heuristic. It supports different priority queues.
*/
class clause_selection {
class lt {
id2clause & m_id2clause;
clause_eval & m_func;
public:
lt(id2clause & m, clause_eval & f):
m_id2clause(m), m_func(f) {}
bool operator()(int cidx1, int cidx2) const {
return m_func(m_id2clause(cidx1)) < m_func(m_id2clause(cidx2));
}
};
id2clause m_id2clause;
ptr_vector<heap<lt> > m_heaps;
unsigned_vector m_slot_size;
unsigned m_curr_slot;
unsigned m_counter;
ptr_vector<clause_eval> m_fs;
void reserve(unsigned cid);
public:
clause_selection(unsigned num_heaps, clause_eval * const * fs, unsigned * slots);
~clause_selection();
void insert(clause * c);
void erase(clause * c);
bool empty() const;
void reset();
clause * get_best();
};
struct symbol_count_clause_eval : public clause_eval {
virtual ~symbol_count_clause_eval() {}
virtual unsigned operator()(clause * c) const { return c->get_symbol_count(); }
};
struct time_clause_eval : public clause_eval {
virtual ~time_clause_eval() {}
virtual unsigned operator()(clause * c) const { return c->get_time(); }
};
struct proof_depth_clause_eval : public clause_eval {
virtual ~proof_depth_clause_eval() {}
virtual unsigned operator()(clause * c) const { return c->get_proof_depth(); }
};
};
#endif /* _SPC_CLAUSE_SELECTION_H_ */

504
src/spc/spc_context.cpp Normal file
View file

@ -0,0 +1,504 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_context.cpp
Abstract:
Superposition Calculus Engine
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#include"spc_context.h"
#include"buffer.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"ast_smt2_pp.h"
#include"warning.h"
namespace spc {
context::context(ast_manager & m, order & o, clause_selection & cs, literal_selection & ls, simplifier & s, spc_params & params):
m_manager(m),
m_params(params),
m_alloc(m.get_allocator()),
m_order(o),
m_cls_sel(cs),
m_lit_sel(ls),
m_simplifier(s),
m_time(0),
m_scope_lvl(0),
m_sem_taut(m),
m_asserted_literals(m),
m_rewriter(m, s, m_order, m_asserted_literals),
m_der(m),
m_subsumption(m, m_asserted_literals, params),
m_eq_resolution(m, m_order, m_stats),
m_factoring(m, m_order, m_stats),
m_superposition(m, m_order, m_stats),
m_unsat(0) {
m_order.reserve_offsets(3);
}
context::~context() {
reset();
}
void context::reset() {
m_cls_sel.reset();
m_time = 0;
m_scope_lvl = 0;
if (m_unsat)
m_unsat = 0;
for (unsigned i = 0; i <= m_scope_lvl; i++) {
del_clauses(i);
if (i < m_clauses_to_unfreeze.size())
m_clauses_to_unfreeze[i].reset();
}
m_asserted_literals.reset();
m_rewriter.reset();
m_subsumption.reset();
m_superposition.reset();
}
/**
\brief Insert the given clause into the indexes of processed clauses.
*/
void context::insert_index(clause * cls) {
TRACE("insert_index", tout << "indexing clause, num_vars: " << cls->get_num_vars() << "\n";
cls->display(tout, m_manager); tout << "\n";);
m_order.reserve_vars(cls->get_num_vars());
m_lit_sel(cls);
m_asserted_literals.insert(cls);
m_rewriter.insert(cls);
m_subsumption.insert(cls);
m_superposition.insert(cls);
cls->set_indexed(true);
}
void context::erase_index(clause * cls) {
if (cls->is_indexed()) {
m_asserted_literals.erase(cls);
m_rewriter.erase(cls);
m_subsumption.erase(cls);
m_superposition.erase(cls);
cls->set_indexed(false);
}
}
void context::set_conflict(clause * cls) {
SASSERT(cls->get_num_literals() == 0);
m_unsat = cls;
if (m_params.m_spc_trace) {
cls->display(std::cout, m_manager); std::cout << " ";
cls->get_justification()->display(std::cout);
std::cout << "\n";
std::cout.flush();
}
}
void context::del_clause(clause * cls) {
TRACE("context", tout << "deleting clause:\n"; cls->display(tout, m_manager); tout << "\n";);
m_stats.m_num_del_clause++;
erase_index(cls);
if (!cls->is_processed())
m_cls_sel.erase(cls);
unsigned scope_lvl = cls->get_scope_lvl();
unsigned bidx = cls->get_bidx();
m_clauses_to_delete[scope_lvl][bidx] = 0;
cls->deallocate(m_manager);
}
void context::freeze_clause_until(clause * cls, unsigned scope_lvl) {
if (cls->get_scope_lvl() >= scope_lvl) {
del_clause(cls);
return;
}
TRACE("context", tout << "freezing clause until: " << scope_lvl << ":\n"; cls->display(tout, m_manager); tout << "\n";);
if (scope_lvl >= m_clauses_to_unfreeze.size())
m_clauses_to_unfreeze.resize(scope_lvl+1, clause_vector());
erase_index(cls);
cls->set_processed(false);
m_clauses_to_unfreeze[scope_lvl].push_back(cls);
}
void context::unfreeze_clause(clause * cls) {
TRACE("context", tout << "unfreezing clausel: "; cls->display(tout, m_manager); tout << "\n";);
SASSERT(!cls->is_processed());
m_cls_sel.insert(cls);
}
void context::init_clause(clause * cls) {
m_stats.m_num_mk_clause++;
cls->init(m_cls_id_gen.mk(), m_time);
m_time++;
unsigned scope_lvl = cls->get_scope_lvl();
if (scope_lvl >= m_clauses_to_delete.size())
m_clauses_to_delete.resize(scope_lvl+1, clause_vector());
clause_vector & cv = m_clauses_to_delete[scope_lvl];
unsigned bidx = cv.size();
cv.push_back(cls);
cls->set_bidx(bidx);
}
clause * context::mk_clause(unsigned num_lits, literal * lits, justification * p, unsigned scope_lvl) {
clause * cls = clause::mk(m_manager, num_lits, lits, p, scope_lvl);
init_clause(cls);
return cls;
}
void context::assert_expr(expr * n, proof * p, unsigned scope_lvl) {
TRACE("spc_assert_expr", tout << mk_ismt2_pp(n, m_manager) << "\n";);
SASSERT(scope_lvl <= m_scope_lvl);
justification_ref ref(m_manager);
ref = justification_proof_wrapper::mk(p, m_manager);
assert_expr(n, ref, scope_lvl);
}
void invalid_clause(expr * n) {
warning_msg("ignoring formula containing an universally quantified boolean variable.");
}
void context::assert_expr(expr * n, justification * p, unsigned scope_lvl) {
SASSERT(scope_lvl <= m_scope_lvl);
buffer<literal> lits;
if (is_forall(n))
n = to_quantifier(n)->get_expr();
if (m_manager.is_or(n)) {
unsigned num = to_app(n)->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * c = to_app(n)->get_arg(i);
bool is_neg = m_manager.is_not(c);
if (is_var(c) || (is_neg && is_var(to_app(c)->get_arg(0)))) {
invalid_clause(n);
return;
}
if (is_neg)
lits.push_back(literal(to_app(c)->get_arg(0), true));
else
lits.push_back(literal(c, false));
}
}
else if (m_manager.is_false(n)) {
// skip
}
else if (m_manager.is_not(n)) {
if (is_var(to_app(n)->get_arg(0))) {
invalid_clause(n);
return;
}
lits.push_back(literal(to_app(n)->get_arg(0), true));
}
else {
if (is_var(n)) {
invalid_clause(n);
return;
}
lits.push_back(literal(n, false));
}
if (trivial(lits.size(), lits.c_ptr()))
return;
clause * cls = mk_clause(lits.size(), lits.c_ptr(), p, scope_lvl);
m_cls_sel.insert(cls);
if (cls->get_num_literals() == 0)
set_conflict(cls);
}
/**
\brief Return true if the given clause (set of literals) is trivial.
That is, it contains the literal s = s or complementary literals.
*/
bool context::trivial(unsigned num_lits, literal * lits) {
SASSERT(m_found_literals.empty());
for (unsigned i = 0; i < num_lits; i++) {
literal l = lits[i];
if (m_found_literals.contains_neg(l) || l.is_true(m_manager)) {
m_found_literals.reset();
m_stats.m_num_trivial++;
return true;
}
m_found_literals.insert(l);
}
m_found_literals.reset();
return false;
}
bool context::trivial(clause * cls) {
return trivial(cls->get_num_literals(), cls->get_literals());
}
/**
\brief Simplify the given clause using the set of processed clauses.
Return the simplified clause.
*/
clause * context::simplify(clause * cls) {
clause * old_cls = cls;
m_der(cls);
cls = m_rewriter(old_cls);
if (cls != old_cls) {
// freeze old clause until simplified clause is deleted.
freeze_clause_until(old_cls, cls->get_scope_lvl());
init_clause(cls);
m_stats.m_num_simplified++;
}
m_der(cls);
return cls;
}
/**
\brief Use the given clause to simplify the set of processed clauses.
\remark: processed clauses that can be simplified, are moved to the
set of unprocessed clauses.
*/
void context::simplify_processed(clause * cls) {
// TODO
}
/**
\brief Return true if the clause is redundant.
*/
bool context::redundant(clause * cls) {
int r_scope_lvl = -1;
if (trivial(cls)) {
TRACE("redundant", tout << "clause is trivial:\n"; cls->display(tout, m_manager); tout << "\n";);
r_scope_lvl = 0;
}
else if (m_sem_taut(cls->get_num_literals(), cls->get_literals())) {
TRACE("redundant", tout << "clause is a semantic tautology:\n"; cls->display(tout, m_manager); tout << "\n";);
r_scope_lvl = 0;
}
else {
clause * subsumer = m_subsumption.forward(cls);
if (subsumer != 0) {
TRACE("redundant", tout << "clause was subsumed: "; cls->display(tout, m_manager);
tout << "\nsubsumer:\n"; subsumer->display(tout, m_manager); tout << "\n";);
r_scope_lvl = subsumer->get_scope_lvl();
m_stats.m_num_subsumed++;
}
}
if (r_scope_lvl >= 0) {
m_stats.m_num_redundant++;
TRACE("spc_saturate", tout << "clause is redundant until level: " << r_scope_lvl << " ...\n";);
freeze_clause_until(cls, r_scope_lvl);
return true;
}
return false;
}
/**
\brief Process a newly generated clause.
*/
void context::process_new_clause(clause * cls) {
if (cls) {
SASSERT(cls->get_justification() != 0);
init_clause(cls);
if (trivial(cls)) {
del_clause(cls);
return;
}
cls = simplify(cls);
if (trivial(cls)) {
del_clause(cls);
return;
}
// if (!redundant(cls)) {
m_cls_sel.insert(cls);
if (cls->get_num_literals() == 0)
set_conflict(cls);
// }
}
}
/**
\brief Apply superposition (left&right), resolution, (equality) factoring, and equality resolution
with the given clause and the set of processed clauses.
*/
void context::generate(clause * cls) {
m_new_clauses.reset();
m_eq_resolution(cls, m_new_clauses);
m_factoring(cls, m_new_clauses);
m_superposition(cls, m_new_clauses);
ptr_vector<clause>::iterator it = m_new_clauses.begin();
ptr_vector<clause>::iterator end = m_new_clauses.end();
for (; it != end; ++it) {
TRACE("spc_generate", tout << "new generated clause:\n"; (*it)->display(tout, m_manager); tout << "\n";);
process_new_clause(*it);
}
}
void context::saturate(unsigned threshold) {
if (inconsistent())
return;
TRACE("spc_saturate", tout << "initial state:\n"; display(tout););
unsigned i = 0;
ptr_buffer<clause> to_simplify;
while (i < threshold && !processed_all()) {
i++;
m_stats.m_num_processed++;
clause * cls = m_cls_sel.get_best();
if (m_params.m_spc_trace) {
cls->display(std::cout, m_manager); std::cout << " ";
cls->get_justification()->display(std::cout);
std::cout << "\n";
std::cout.flush();
}
cls->set_processed(true);
TRACE("spc_saturate", tout << "get best: "; cls->display(tout, m_manager); tout << "\n";);
cls = simplify(cls);
TRACE("spc_saturate", tout << "clause after simplification: "; cls->display(tout, m_manager); tout << "\n";);
if (redundant(cls))
continue;
if (cls->empty()) {
set_conflict(cls);
break;
}
cls->try_to_orient_literals(m_order);
simplify_processed(cls);
insert_index(cls);
generate(cls);
if (inconsistent())
break;
}
TRACE("spc_saturate", tout << "final state:\n"; display(tout););
#if 0
IF_VERBOSE(10000,
display(std::cout););
display_statistics(std::cout);
if (m_unsat && m_manager.fine_grain_proofs()) {
std::cout << mk_ll_pp(m_unsat->get_justification()->get_proof(), m_manager);
}
#endif
}
void context::push_scope() {
m_scope_lvl++;
m_time_trail.push_back(m_time);
}
void context::del_clauses(unsigned scope_lvl) {
if (scope_lvl < m_clauses_to_delete.size()) {
clause_vector & cv = m_clauses_to_delete[m_scope_lvl];
clause_vector::iterator it = cv.begin();
clause_vector::iterator end = cv.end();
for (; it != end; ++it) {
clause * cls = *it;
if (cls)
del_clause(cls);
}
cv.reset();
}
}
void context::unfreeze_clauses(unsigned scope_lvl) {
if (scope_lvl < m_clauses_to_unfreeze.size()) {
clause_vector & cv = m_clauses_to_unfreeze[m_scope_lvl];
clause_vector::iterator it = cv.begin();
clause_vector::iterator end = cv.end();
for (; it != end; ++it)
unfreeze_clause(*it);
cv.reset();
}
}
void context::pop_scope(unsigned num_scopes) {
SASSERT(num_scopes >= m_scope_lvl);
unsigned new_lvl = m_scope_lvl - num_scopes;
m_time = m_time_trail[new_lvl];
m_time_trail.shrink(new_lvl);
if (m_unsat && new_lvl < m_unsat->get_scope_lvl())
m_unsat = 0;
while (m_scope_lvl > new_lvl) {
del_clauses(m_scope_lvl);
unfreeze_clauses(m_scope_lvl);
m_scope_lvl --;
}
}
void context::display(std::ostream & out, vector<clause_vector> const & cvs, unsigned scope_lvl, bool frozen) const {
if (scope_lvl < cvs.size()) {
bool first = true;
clause_vector const & cv = cvs[scope_lvl];
clause_vector::const_iterator it = cv.begin();
clause_vector::const_iterator end = cv.end();
for (; it != end; ++it) {
clause * cls = *it;
if (cls) {
if (first) {
out << "level " << scope_lvl << ":\n";
first = false;
}
cls->display(out, m_manager);
if (frozen)
out << " [frozen]";
out << "\n";
}
}
}
}
void context::display(std::ostream & out) const {
for (unsigned i = 0; i <= m_scope_lvl; i++) {
display(out, m_clauses_to_delete, i, false);
display(out, m_clauses_to_unfreeze, i, true);
}
}
void context::display_statistics(std::ostream & out) const {
m_stats.display(out);
}
/**
Generate new clauses
5) Object equality resolution 1
(R or X = i)
==>
sigma(R)
sigma = { X -> j }
where i and j are distinct objects
sigma(X = i) is not smaller or equal than any other literal in the clause
6) Object equality resolution 2
(R or X = Y)
==>
sigma(R)
sigma = { X -> i, Y -> j }
For every pair of distinct objects i and j
sigma(X = Y) is not smaller or equal than any other literal in the clause
*/
};

122
src/spc/spc_context.h Normal file
View file

@ -0,0 +1,122 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_context.h
Abstract:
Superposition Calculus Engine
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _SPC_CONTEXT_H_
#define _SPC_CONTEXT_H_
#include"spc_params.h"
#include"spc_clause.h"
#include"spc_clause_selection.h"
#include"spc_literal_selection.h"
#include"spc_semantic_tautology.h"
#include"spc_rewriter.h"
#include"spc_asserted_literals.h"
#include"spc_subsumption.h"
#include"spc_eq_resolution.h"
#include"spc_factoring.h"
#include"spc_superposition.h"
#include"spc_statistics.h"
#include"spc_der.h"
#include"substitution_tree.h"
#include"order.h"
namespace spc {
/**
\brief Logical context of the superposition calculus engine.
*/
class context {
public:
statistics m_stats;
protected:
typedef clause::set clause_set;
ast_manager & m_manager;
spc_params & m_params;
small_object_allocator & m_alloc;
order & m_order;
clause_selection & m_cls_sel;
literal_selection & m_lit_sel;
simplifier & m_simplifier;
unsigned m_time;
unsigned m_scope_lvl;
id_gen m_cls_id_gen;
found_literals m_found_literals;
semantic_tautology m_sem_taut;
asserted_literals m_asserted_literals;
rewriter m_rewriter;
der m_der;
subsumption m_subsumption;
eq_resolution m_eq_resolution;
factoring m_factoring;
superposition m_superposition;
vector<clause_vector> m_clauses_to_unfreeze;
vector<clause_vector> m_clauses_to_delete;
unsigned_vector m_time_trail;
clause * m_unsat;
ptr_vector<clause> m_new_clauses;
void insert_index(clause * cls);
void erase_index(clause * cls);
void init_clause(clause * cls);
clause * mk_clause(unsigned num_lits, literal * lits, justification * p, unsigned scope_lvl);
void del_clause(clause * cls);
void del_clauses(unsigned scope_lvl);
void freeze_clause_until(clause * cls, unsigned scope_lvl);
void unfreeze_clause(clause * cls);
void unfreeze_clauses(unsigned scope_lvl);
bool trivial(unsigned num_lits, literal * lits);
bool trivial(clause * cls);
clause * simplify(clause * cls);
void simplify_processed(clause * cls);
bool redundant(clause * cls);
void generate(clause * cls);
void process_new_clause(clause * cls);
void display(std::ostream & out, vector<clause_vector> const & cvs, unsigned scope_lvl, bool frozen) const;
void set_conflict(clause * cls);
public:
context(ast_manager & m, order & o, clause_selection & cs, literal_selection & ls, simplifier & s, spc_params & params);
~context();
simplifier & get_simplifier() { return m_simplifier; }
order & get_order() { return m_order; }
ast_manager & get_manager() { return m_manager; }
unsigned get_scope_lvl() const { return m_scope_lvl; }
void assert_expr(expr * n, proof * p, unsigned scope_lvl = 0);
void assert_expr(expr * n, justification * p, unsigned scope_lvl = 0);
void saturate(unsigned threshold);
bool inconsistent() const { return m_unsat != 0; }
bool processed_all() const { return m_cls_sel.empty(); }
void push_scope();
void pop_scope(unsigned num_scopes);
void reset();
void display(std::ostream & out) const;
void display_statistics(std::ostream & out) const;
};
};
#endif /* _SPC_CONTEXT_H_ */

135
src/spc/spc_decl_plugin.cpp Normal file
View file

@ -0,0 +1,135 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_decl_plugin.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-12.
Revision History:
--*/
#include"spc_decl_plugin.h"
std::ostream & operator<<(std::ostream & out, spc_op_kind k) {
switch (k) {
case PR_DEMODULATION: out << "demod"; break;
case PR_SPC_REWRITE: out << "rewrite"; break;
case PR_SPC_RESOLUTION: out << "res"; break;
case PR_SUPERPOSITION: out << "sup"; break;
case PR_EQUALITY_RESOLUTION: out << "eq_res"; break;
case PR_FACTORING: out << "fact"; break;
case PR_SPC_DER: out << "der"; break;
case PR_SPC_ASSERTED: out << "asserted"; break;
default: out << "unknown"; break;
}
return out;
}
spc_decl_plugin::spc_decl_plugin() :
m_demodulation("demod"),
m_spc_rewrite("sp-rw"),
m_spc_resolution("sp-res"),
m_superposition("sp"),
m_equality_resolution("eq-res"),
m_factoring("fact"),
m_spc_der("spc-der") {
}
spc_decl_plugin::~spc_decl_plugin() {
}
sort * spc_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) {
UNREACHABLE();
return 0;
}
func_decl * spc_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) {
#define MK_PROOF(SYM) m_manager->mk_func_decl(SYM, arity, domain, m_manager->mk_proof_sort(), func_decl_info(m_family_id, k))
SASSERT(num_parameters == 0);
switch (k) {
/*
#1: (forall (x) (= t[x] s[x]))
[demod #1] (= t[a] s[a])
*/
case PR_DEMODULATION: return MK_PROOF(m_demodulation);
/*
Justifies a rewriting (simplification step) in the superposition engine.
It has n+1 antecedents. The first antecedent is the clause being simplified.
The other antecedents are demodulators.
The consequent is the simplied clause.
*/
case PR_SPC_REWRITE: return MK_PROOF(m_spc_rewrite);
/*
Resolution proof:
#1: (or C l)
#2: (or D (not l'))
[sp-res #1 #2]: sigma(or C D)
where sigma is the mgu of l and l'
*/
case PR_SPC_RESOLUTION: return MK_PROOF(m_spc_resolution);
/*
Superposition proof:
#1: (or (= s t) R)
#2: D[u]
[sp #1 #2]: sigma(or R D[t])
where sigma is the mgu(u, s)
*/
case PR_SUPERPOSITION: return MK_PROOF(m_superposition);
/*
Equality resolution proof:
#1: (or (not (= s t)) R)
[eq-res #1]: sigma R
where sigma is the mgu of s and t.
*/
case PR_EQUALITY_RESOLUTION: return MK_PROOF(m_equality_resolution);
/*
Proof object for factoring and equality-factoring:
#1: (or P[t] P[s] R)
[fact #1]: sigma(or P[t] R)
where sigma is the mgu(t,s)
#1: (or (= s t) (= u v) R)
[fact #1]: sigma(or (not (= t v)) (= u v) R)
where sigma = mgu(s, u)
*/
case PR_FACTORING: return MK_PROOF(m_factoring);
/*
Proof object for destructive equality resolution:
#1: (or (not (= x t)) C[x])
[spc-der #1]: C[t]
t does not contain x.
Several variables may be eliminated simultaneously.
*/
case PR_SPC_DER: return MK_PROOF(m_spc_der);
default:
UNREACHABLE();
return 0;
}
}

61
src/spc/spc_decl_plugin.h Normal file
View file

@ -0,0 +1,61 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_decl_plugin.h
Abstract:
Proof declarations for Superposition Calculus Engine.
Author:
Leonardo de Moura (leonardo) 2008-02-12.
Revision History:
--*/
#ifndef _SPC_DECL_PLUGIN_H_
#define _SPC_DECL_PLUGIN_H_
#include"ast.h"
enum spc_op_kind {
PR_DEMODULATION,
PR_SPC_REWRITE,
PR_SPC_RESOLUTION,
PR_SUPERPOSITION,
PR_EQUALITY_RESOLUTION,
PR_FACTORING,
PR_SPC_DER,
PR_SPC_ASSERTED,
PR_SPC_LAST_ID
};
std::ostream & operator<<(std::ostream & out, spc_op_kind k);
class spc_decl_plugin : public decl_plugin {
symbol m_demodulation;
symbol m_spc_rewrite;
symbol m_spc_resolution;
symbol m_superposition;
symbol m_equality_resolution;
symbol m_factoring;
symbol m_spc_der;
public:
spc_decl_plugin();
virtual ~spc_decl_plugin();
virtual decl_plugin * mk_fresh() { return alloc(spc_decl_plugin); }
virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters);
virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range);
};
#endif /* _SPC_DECL_PLUGIN_H_ */

80
src/spc/spc_der.cpp Normal file
View file

@ -0,0 +1,80 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_der.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-17.
Revision History:
--*/
#include"spc_der.h"
#include"occurs.h"
namespace spc {
der::der(ast_manager & m):
m_manager(m),
m_subst(m),
m_spc_fid(m.get_family_id("spc")) {
m_subst.reserve_offsets(1);
}
void der::apply(clause * cls, unsigned j, expr * lhs, expr * rhs) {
TRACE("der", tout << "applying der at: " << j << "\n"; cls->display(tout, m_manager); tout << "\n";);
m_subst.reserve_vars(cls->get_num_vars());
m_subst.reset();
m_subst.insert(expr_offset(lhs, 0), expr_offset(rhs, 0));
literal_buffer new_lits(m_manager);
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
if (i != j) {
literal const & l = cls->get_literal(i);
expr_ref new_atom(m_manager);
m_subst.apply(l.atom(), new_atom);
new_lits.push_back(literal(new_atom, l.sign()));
}
}
justification * js = mk_der_justification(m_manager, m_spc_fid, cls->get_justification(), new_lits.size(), new_lits.c_ptr());
cls->update_lits(m_manager, new_lits.size(), new_lits.c_ptr(), js);
}
bool der::apply(clause * cls) {
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l = cls->get_literal(i);
if (l.sign() && m_manager.is_eq(l.atom())) {
expr * lhs = l.lhs();
expr * rhs = l.rhs();
if (is_var(lhs) && !occurs(lhs, rhs)) {
apply(cls, i, lhs, rhs);
return true;
}
else if (is_var(rhs) && !occurs(rhs, lhs)) {
apply(cls, i, rhs, lhs);
return true;
}
}
}
return false;
}
/**
\brief Clause cls is destructively updated.
*/
void der::operator()(clause * cls) {
while(apply(cls))
;
}
};

52
src/spc/spc_der.h Normal file
View file

@ -0,0 +1,52 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_der.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-17.
Revision History:
--*/
#ifndef _SPC_DER_H_
#define _SPC_DER_H_
#include"spc_clause.h"
namespace spc {
/**
\brief Functor for applying destructive equality resolution.
This is similar to the Functor in der.h, but this one applies
the simplification on clauses instead of ast's.
x != s or R
==>
sigma(R)
where
sigma = mgu(x, s)
*/
class der {
ast_manager & m_manager;
substitution m_subst;
unsigned_vector m_to_keep;
family_id m_spc_fid;
void apply(clause * cls, unsigned j, expr * lhs, expr * rhs);
bool apply(clause * cls);
public:
der(ast_manager & m);
void operator()(clause * cls);
};
};
#endif /* _SPC_DER_H_ */

View file

@ -0,0 +1,44 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_eq_resolution.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-14.
Revision History:
--*/
#include"spc_eq_resolution.h"
namespace spc {
/**
\brief Apply equality resolution rule on the given clause.
Store the produced clauses in new_clauses.
*/
void eq_resolution::operator()(clause * cls, ptr_vector<clause> & new_clauses) {
m_subst.reserve_vars(cls->get_num_vars());
unsigned num = cls->get_num_literals();
for (unsigned i = 0; i < num; i++) {
literal const & l = cls->get_literal(i);
expr * atom = l.atom();
if (l.sign() && m_manager.is_eq(atom)) {
expr * lhs = to_app(atom)->get_arg(0);
expr * rhs = to_app(atom)->get_arg(1);
m_subst.reset();
if (m_unifier(lhs, rhs, m_subst, false) && cls->is_eligible_for_resolution(m_order, l, 0, &m_subst)) {
m_stats.m_num_eq_resolution++;
new_clauses.push_back(mk_result(cls, i));
}
}
}
}
};

View file

@ -0,0 +1,50 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_eq_resolution.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-14.
Revision History:
--*/
#ifndef _SPC_EQ_RESOLUTION_H_
#define _SPC_EQ_RESOLUTION_H_
#include"spc_unary_inference.h"
#include"spc_statistics.h"
namespace spc {
/**
\brief Functor for applying equality resolution.
s != t or R
==>
sigma(R)
*/
class eq_resolution : public unary_inference {
protected:
statistics & m_stats;
family_id m_spc_fid;
virtual justification * mk_justification(justification * parent, unsigned num_lits, literal * new_lits) {
return mk_eq_res_justification(m_manager, m_spc_fid, parent, num_lits, new_lits);
}
public:
eq_resolution(ast_manager & m, order & ord, statistics & stats):unary_inference(m, ord), m_stats(stats), m_spc_fid(m.get_family_id("spc")) {}
virtual ~eq_resolution() {}
void operator()(clause * cls, ptr_vector<clause> & new_clauses);
};
};
#endif /* _SPC_EQ_RESOLUTION_H_ */

156
src/spc/spc_factoring.cpp Normal file
View file

@ -0,0 +1,156 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_factoring.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-14.
Revision History:
--*/
#include"spc_factoring.h"
namespace spc {
/**
\brief Create a new clause by removing literal at position j, apply substitution m_subst,
and adding a disequality lhs != rhs.
*/
clause * factoring::mk_eq_fact_result(clause * cls, unsigned j, expr * lhs, expr * rhs) {
sbuffer<literal> new_literals;
expr_ref new_eq(m_manager.mk_eq(lhs, rhs), m_manager);
expr_ref new_eq_after_subst(m_manager);
m_subst.apply(new_eq, new_eq_after_subst);
new_literals.push_back(literal(new_eq_after_subst, true));
unsigned num = cls->get_num_literals();
for (unsigned i = 0; i < num; i++) {
if (i != j) {
literal const & l = cls->get_literal(i);
expr_ref new_atom(m_manager);
m_subst.apply(l.atom(), new_atom);
new_literals.push_back(literal(new_atom, l.sign()));
}
}
justification * js = mk_factoring_justification(m_manager, m_spc_fid, cls->get_justification(), new_literals.size(),
new_literals.c_ptr());
clause * new_cls = clause::mk(m_manager, new_literals.size(), new_literals.c_ptr(), js, cls->get_scope_lvl());
m_stats.m_num_eq_factoring++;
return new_cls;
}
/**
\brief Try to apply equality factoring using the eq literal stored at position j.
Assume lhs and rhs are the left hand side of this equality (they may be swapped).
*/
void factoring::try_eq_factoring(clause * cls, unsigned j, expr * lhs, expr * rhs, ptr_vector<clause> & new_clauses) {
literal const & l1 = cls->get_literal(j);
sort * s = m_manager.get_sort(lhs);
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l2 = cls->get_literal(i);
if (i == j)
continue;
if (l2.sign())
continue;
expr * atom = l2.atom();
if (!m_manager.is_eq(atom))
continue;
expr * lhs2 = to_app(atom)->get_arg(0);
if (m_manager.get_sort(lhs2) != s)
continue;
expr * rhs2 = to_app(atom)->get_arg(1);
m_subst.reset();
if (m_unifier(lhs, lhs2, m_subst, false) &&
(l1.is_oriented() || !m_order.greater(rhs, lhs, &m_subst)) &&
cls->is_eligible_for_paramodulation(m_order, l1, 0, &m_subst)) {
new_clauses.push_back(mk_eq_fact_result(cls, j, rhs, rhs2));
}
m_subst.reset();
if (m_unifier(lhs, rhs2, m_subst, false) &&
(l1.is_oriented() || !m_order.greater(rhs, lhs, &m_subst)) &&
cls->is_eligible_for_paramodulation(m_order, l1, 0, &m_subst)) {
new_clauses.push_back(mk_eq_fact_result(cls, j, rhs, lhs2));
}
}
}
/**
\brief Try to apply equality factoring using the eq literal stored at position i.
*/
void factoring::try_eq_factoring(clause * cls, unsigned i, ptr_vector<clause> & new_clauses) {
if (cls->get_num_pos_literals() <= 1)
return;
literal const & l = cls->get_literal(i);
app * eq = to_app(l.atom());
expr * lhs = eq->get_arg(0);
expr * rhs = eq->get_arg(1);
if (l.is_oriented()) {
if (!l.is_left())
std::swap(lhs, rhs);
try_eq_factoring(cls, i, lhs, rhs, new_clauses);
}
else {
try_eq_factoring(cls, i, lhs, rhs, new_clauses);
try_eq_factoring(cls, i, rhs, lhs, new_clauses);
}
}
/**
\brief Try to apply (ordering) factoring rule.
*/
void factoring::try_factoring(clause * cls, unsigned j, ptr_vector<clause> & new_clauses) {
literal const & l1 = cls->get_literal(j);
if (l1.sign() && cls->get_num_neg_literals() <= 1)
return;
if (!l1.sign() && cls->get_num_pos_literals() <= 1)
return;
unsigned num = cls->get_num_literals();
for (unsigned i = 0; i < num; i++) {
if (i == j)
continue;
literal const & l2 = cls->get_literal(i);
if (l1.sign() != l2.sign())
continue;
m_subst.reset();
if (m_unifier(l1.atom(), l2.atom(), m_subst, false) &&
cls->is_eligible_for_resolution(m_order, l1, 0, &m_subst)) {
new_clauses.push_back(mk_result(cls, i));
m_stats.m_num_factoring++;
}
}
}
/**
\brief Apply factoring rule on the given clause.
Store the produced clauses into new_clauses.
*/
void factoring::operator()(clause * cls, ptr_vector<clause> & new_clauses) {
if (cls->get_num_pos_literals() <= 1 && cls->get_num_neg_literals() <= 1)
return;
m_subst.reserve_vars(cls->get_num_vars());
unsigned num = cls->get_num_literals();
for (unsigned i = 0; i < num; i++) {
literal const & l = cls->get_literal(i);
expr * atom = l.atom();
// remark: if the clause has selected literals then the literal will not be eligible
// for paramodulation and eq_resolution will not be applied.
if (!l.sign() && m_manager.is_eq(atom) && !cls->has_sel_lit())
try_eq_factoring(cls, i, new_clauses);
if (l.is_selected() || !cls->has_sel_lit())
try_factoring(cls, i, new_clauses);
}
}
};

66
src/spc/spc_factoring.h Normal file
View file

@ -0,0 +1,66 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_factoring.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-14.
Revision History:
--*/
#ifndef _SPC_FACTORING_H_
#define _SPC_FACTORING_H_
#include"spc_unary_inference.h"
#include"spc_statistics.h"
namespace spc {
/**
\brief Functor for applying factoring.
- Equality Factoring
s = t or u = v or R
==>
sigma(t != v or u = v or R)
sigma = mgu(s, u)
sigma(s) not greater than sigma(t)
sigma(s = t) is eligible for paramodulation.
- Factoring
P(t) or P(s) or R
==>
sigma(P(t) or R)
sigma = mgu(t,s)
sigma(P(t)) is eligible for resolution.
*/
class factoring : public unary_inference {
protected:
statistics & m_stats;
family_id m_spc_fid;
virtual justification * mk_justification(justification * parent, unsigned num_lits, literal * new_lits) {
return mk_factoring_justification(m_manager, m_spc_fid, parent, num_lits, new_lits);
}
clause * mk_eq_fact_result(clause * cls, unsigned j, expr * lhs, expr * rhs);
void try_eq_factoring(clause * cls, unsigned j, expr * lhs, expr * rhs, ptr_vector<clause> & new_clauses);
void try_eq_factoring(clause * cls, unsigned i, ptr_vector<clause> & new_clauses);
void try_factoring(clause * cls, unsigned j, ptr_vector<clause> & new_clauses);
public:
factoring(ast_manager & m, order & ord, statistics & stats):unary_inference(m, ord), m_stats(stats), m_spc_fid(m.get_family_id("spc")) {}
virtual ~factoring() {}
void operator()(clause * cls, ptr_vector<clause> & new_clauses);
};
};
#endif /* _SPC_FACTORING_H_ */

View file

@ -0,0 +1,184 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_justification.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-08.
Revision History:
--*/
#include"spc_justification.h"
#include"spc_clause.h"
#include"marker.h"
namespace spc {
void get_justification_stat(justification * p, justification_stat & stat) {
// Remark: justification objects that are not associated
// with clauses may be shared. That is, they may be parent of
// several different justification objects.
marker<justification> m;
ptr_buffer<justification> todo;
todo.push_back(p);
while (!todo.empty()) {
justification * p = todo.back();
todo.pop_back();
if (!m.is_marked(p)) {
m.mark(p);
clause * cls = p->get_clause();
if (cls) {
if (cls->get_proof_depth() > stat.m_proof_depth)
stat.m_proof_depth = cls->get_proof_depth();
if (cls->get_scope_lvl() > stat.m_max_scope_lvl)
stat.m_max_scope_lvl = cls->get_scope_lvl();
stat.m_parent_clauses.push_back(cls);
}
else {
p->get_parents(todo);
}
}
}
}
void justification::display(std::ostream & out) {
out << "[" << get_rule_id();
ptr_buffer<justification> ps;
get_parents(ps);
unsigned sz = ps.size();
for (unsigned i = 0; i < sz; i++) {
out << " ";
justification * js = ps[i];
clause * cls = js->get_clause();
if (cls)
out << "#" << cls->get_id();
else
js->display(out);
}
out << "]";
}
justification * justification_proof_wrapper::mk(proof * p, ast_manager & m) {
void * mem = m.get_allocator().allocate(sizeof(justification_proof_wrapper));
return new (mem) justification_proof_wrapper(p, m);
}
proof * justification_proof_wrapper::get_proof() const {
return m_proof;
}
unsigned justification_proof_wrapper::del_eh(ast_manager & m) {
m.dec_ref(m_proof);
return sizeof(justification_proof_wrapper);
}
void dec_ref(justification * p, ast_manager & m) {
if (p->dec_ref()) {
ptr_buffer<justification> to_delete;
ptr_buffer<justification> parents;
to_delete.push_back(p);
while (!to_delete.empty()) {
justification * p = to_delete.back();
to_delete.pop_back();
SASSERT(p->get_ref_count() == 0);
parents.reset();
p->get_parents(parents);
ptr_buffer<justification>::iterator it = parents.begin();
ptr_buffer<justification>::iterator end = parents.end();
for (; it != end; ++it) {
justification * parent = *it;
if (parent->dec_ref())
to_delete.push_back(parent);
}
unsigned sz = p->del_eh(m);
p->~justification();
m.get_allocator().deallocate(sz, p);
}
}
}
/**
\brief Return a proof for a new clause formed by the literals lits[0] ... lits[num_lits - 1].
This clause was produced using a main clause C, where the proof of C is \c main_pr,
and the auxiliary proofs auxs[0] ... aux[num_auxs-1].
\remark If fine_grain_proofs() is false, then 0 is returned.
*/
proof * mk_proof(ast_manager & m, family_id spc_fid, spc_op_kind pid, unsigned num_lits, literal * lits, proof * main_pr,
unsigned num_auxs, proof * const * auxs) {
if (m.fine_grain_proofs()) {
expr * new_fact_body = mk_or(m, num_lits, lits);
SASSERT(main_pr);
SASSERT(m.has_fact(main_pr));
expr * fact = m.get_fact(main_pr);
expr * new_fact = 0;
if (is_quantifier(fact))
new_fact = m.update_quantifier(to_quantifier(fact), new_fact_body);
else
new_fact = new_fact_body;
ptr_buffer<expr> args;
args.push_back(main_pr);
args.append(num_auxs, (expr**) auxs);
args.push_back(new_fact);
return m.mk_app(spc_fid, pid, args.size(), args.c_ptr());
}
return 0;
}
justification * rewrite_justification::mk(ast_manager & m, justification * head,
unsigned num_demodulators, justification * const * demodulators, proof * pr) {
void * mem = m.get_allocator().allocate(get_obj_size(num_demodulators, m.fine_grain_proofs()));
return new (mem) rewrite_justification(m, head, num_demodulators, demodulators, pr);
}
rewrite_justification::rewrite_justification(ast_manager & m, justification * head,
unsigned num_demodulators, justification * const * demodulators, proof * pr):
m_num_demodulators(num_demodulators) {
SASSERT(m.fine_grain_proofs() == (pr != 0));
m_fields[0] = head;
head->inc_ref();
for (unsigned i = 0; i < num_demodulators; i++) {
m_fields[i+1] = demodulators[i];
demodulators[i]->inc_ref();
}
if (m.fine_grain_proofs()) {
SASSERT(pr);
m_fields[num_demodulators+1] = pr;
m.inc_ref(pr);
}
}
void rewrite_justification::get_parents(ptr_buffer<justification> & parents) {
unsigned num_parents = m_num_demodulators+1;
for (unsigned i = 0; i < num_parents; i++)
parents.push_back(reinterpret_cast<justification*>(m_fields[i]));
}
proof * rewrite_justification::get_proof() const {
return reinterpret_cast<proof*>(m_fields[m_num_demodulators+1]);
}
unsigned rewrite_justification::del_eh(ast_manager & m) {
if (m.fine_grain_proofs()) {
m.dec_ref(reinterpret_cast<proof*>(m_fields[m_num_demodulators+1]));
return get_obj_size(m_num_demodulators, true);
}
return get_obj_size(m_num_demodulators, false);
}
proof * mk_rewrite_proof(ast_manager & m, family_id spc_fid, unsigned num_lits, literal * lits, proof * main_pr,
unsigned num_auxs, proof * const * auxs) {
return mk_proof(m, spc_fid, PR_SPC_REWRITE, num_lits, lits, main_pr, num_auxs, auxs);
}
};

337
src/spc/spc_justification.h Normal file
View file

@ -0,0 +1,337 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_justification.h
Abstract:
Proof-like objects for tracking dependencies in the superposition
calculus engine, and generating proofs.
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _SPC_JUSTIFICATION_H_
#define _SPC_JUSTIFICATION_H_
#include"ast.h"
#include"spc_literal.h"
#include"spc_decl_plugin.h"
namespace spc {
class clause;
/**
\brief Proof-like object use to track dependencies and produce
proofs.
\remark All justification objects must be allocated using the
small_object_allocator in ast_manager.
*/
class justification {
clause * m_owner;
unsigned m_ref_count:30;
unsigned m_mark:1;
unsigned m_assumption:1;
friend class clause;
void set_owner(clause * cls) { m_owner = cls; }
public:
justification(bool assumption = false):
m_owner(0),
m_ref_count(0),
m_mark(false),
m_assumption(assumption) {
}
virtual ~justification() {}
void inc_ref() {
m_ref_count++;
}
bool dec_ref() {
SASSERT(m_ref_count > 0);
m_ref_count--;
return m_ref_count == 0;
}
unsigned get_ref_count() {
return m_ref_count;
}
void set_mark(bool f) { m_mark = f; }
bool is_marked() const { return m_mark; }
/**
\brief Return the clause justified by this object.
\remark for some justification objects that clause is
supressed. Example: intermediate steps.
*/
clause * get_clause() { return m_owner; }
/**
\brief Return the expr justified by this object.
This method returns a non null value only when
proof generation is enabled.
*/
virtual expr * get_expr(ast_manager & m) { return 0; }
/**
\brief Return a non-zero value if the justification
is wrapping a proof object.
*/
virtual proof * get_proof() const { return 0; }
/**
\brief Return the parent justifications.
*/
virtual void get_parents(ptr_buffer<justification> & parents) {}
/**
\brief Return the name of the rule used.
*/
virtual spc_op_kind get_rule_id() = 0;
/**
\brief Return true if the justification is an external assumption.
*/
bool assumption() const { return m_assumption; }
void display(std::ostream & out);
/**
\brief This method is invoked before the object is deleted.
Return the amount of memory consumed by this object.
*/
virtual unsigned del_eh(ast_manager & m) = 0;
};
struct justification_stat {
unsigned m_proof_depth;
unsigned m_max_scope_lvl;
ptr_buffer<clause> m_parent_clauses;
justification_stat():
m_proof_depth(0),
m_max_scope_lvl(0) {
}
};
void get_justification_stat(justification * p, justification_stat & stat);
void dec_ref(justification * p, ast_manager & m);
/**
\brief Smart pointer for justification objects.
*/
class justification_ref {
justification * m_obj;
ast_manager & m_manager;
void inc_ref() { if (m_obj) m_obj->inc_ref(); }
void dec_ref() { if (m_obj) spc::dec_ref(m_obj, m_manager); }
public:
justification_ref(ast_manager & m):m_obj(0), m_manager(m) {}
justification_ref(justification * j, ast_manager & m):
m_obj(j), m_manager(m) {
inc_ref();
}
~justification_ref() {
dec_ref();
}
operator justification*() const { return m_obj; }
operator bool() const { return m_obj != 0; }
bool operator!() const { return m_obj == 0; }
justification * operator->() const { return m_obj; }
justification const & operator*() const { return *m_obj; }
justification_ref & operator=(justification * n) {
if (n)
n->inc_ref();
dec_ref();
m_obj = n;
return *this;
}
justification_ref & operator=(justification_ref & n) {
SASSERT(&m_manager == &n.m_manager);
n.inc_ref();
dec_ref();
m_obj = n.m_obj;
return *this;
}
};
class justification_proof_wrapper : public justification {
proof * m_proof;
justification_proof_wrapper(proof * p, ast_manager & m):m_proof(p) { m.inc_ref(m_proof); }
public:
static justification * mk(proof * p, ast_manager & m);
virtual ~justification_proof_wrapper() {}
virtual proof * get_proof() const;
virtual spc_op_kind get_rule_id() { return PR_SPC_ASSERTED; };
virtual unsigned del_eh(ast_manager & m);
};
proof * mk_proof(ast_manager & m, family_id spc_fid, spc_op_kind pid, unsigned num_lits, literal * lits, proof * main_pr, unsigned num_auxs,
proof * const * auxs);
/**
\brief Justification for rewriting steps: demodulation, duplicate literal deletion, resolved literal deletion.
*/
class rewrite_justification : public justification {
unsigned m_num_demodulators;
void * m_fields[0];
static unsigned get_obj_size(unsigned num_demodulators, bool fine_grain) {
return sizeof(rewrite_justification) + (num_demodulators + (fine_grain ? 2 : 1)) * sizeof(void *);
}
rewrite_justification(ast_manager & m, justification * head,
unsigned num_demodulators, justification * const * demodulators, proof * pr);
public:
static justification * mk(ast_manager & m, justification * head,
unsigned num_demodulators, justification * const * demodulators, proof * pr = 0);
virtual ~rewrite_justification() {}
virtual proof * get_proof() const;
virtual spc_op_kind get_rule_id() { return PR_SPC_REWRITE; }
virtual void get_parents(ptr_buffer<justification> & parents);
virtual unsigned del_eh(ast_manager & m);
};
proof * mk_rewrite_proof(ast_manager & m, family_id spc_fid, unsigned num_lits, literal * lits, proof * main_pr, unsigned num_auxs,
proof * const * auxs);
template<spc_op_kind Kind>
class unary_justification : public justification {
protected:
justification * m_parent;
proof * m_proof;
unary_justification(ast_manager & m, justification * p, proof * pr):
m_parent(p),
m_proof(pr) {
p->inc_ref();
SASSERT(m.fine_grain_proofs() == (pr != 0));
if (m.fine_grain_proofs())
m.inc_ref(pr);
}
public:
virtual proof * get_proof() const {
return m_proof;
}
virtual void get_parents(ptr_buffer<justification> & parents) {
parents.push_back(m_parent);
}
virtual unsigned del_eh(ast_manager & m) {
if (m.fine_grain_proofs())
m.dec_ref(m_proof);
return sizeof(unary_justification);
}
virtual spc_op_kind get_rule_id() {
return Kind;
}
static justification * mk(ast_manager & m, family_id spc_fid, justification * p, unsigned num_lits, literal * lits) {
proof * pr = 0;
if (m.fine_grain_proofs())
pr = mk_proof(m, spc_fid, Kind, num_lits, lits, p->get_proof(), 0, 0);
void * mem = m.get_allocator().allocate(sizeof(unary_justification));
return new (mem) unary_justification(m, p, pr);
}
};
inline justification * mk_eq_res_justification(ast_manager & m, family_id spc_fid, justification * p, unsigned num_lits, literal * lits) {
return unary_justification<PR_EQUALITY_RESOLUTION>::mk(m, spc_fid, p, num_lits, lits);
}
inline justification * mk_factoring_justification(ast_manager & m, family_id spc_fid, justification * p, unsigned num_lits, literal * lits) {
return unary_justification<PR_FACTORING>::mk(m, spc_fid, p, num_lits, lits);
}
inline justification * mk_der_justification(ast_manager & m, family_id spc_fid, justification * p, unsigned num_lits, literal * lits) {
return unary_justification<PR_SPC_DER>::mk(m, spc_fid, p, num_lits, lits);
}
template<spc_op_kind Kind>
class binary_justification : public justification {
protected:
justification * m_parent1;
justification * m_parent2;
proof * m_proof;
binary_justification(ast_manager & m, justification * p1, justification * p2, proof * pr):
m_parent1(p1),
m_parent2(p2),
m_proof(pr) {
p1->inc_ref();
p2->inc_ref();
SASSERT(m.fine_grain_proofs() == (pr != 0));
if (m.fine_grain_proofs())
m.inc_ref(pr);
}
public:
virtual proof * get_proof() const {
return m_proof;
}
virtual void get_parents(ptr_buffer<justification> & parents) {
parents.push_back(m_parent1);
parents.push_back(m_parent2);
}
virtual unsigned del_eh(ast_manager & m) {
if (m.fine_grain_proofs())
m.dec_ref(m_proof);
return sizeof(binary_justification);
}
virtual spc_op_kind get_rule_id() {
return Kind;
}
static justification * mk(ast_manager & m, family_id spc_fid, justification * p1, justification * p2, unsigned num_lits, literal * lits,
unsigned num_vars, var * const * vars) {
proof * pr = 0;
if (m.fine_grain_proofs()) {
ptr_buffer<sort> sorts;
sbuffer<symbol> names;
for (unsigned i = 0; i < num_vars; i++) {
sorts.push_back(vars[num_vars - i - 1]->get_sort());
names.push_back(symbol(num_vars - i - 1));
}
expr * body = mk_or(m, num_lits, lits);
expr * new_fact = 0;
if (num_vars == 0)
new_fact = body;
else
new_fact = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), body);
pr = m.mk_app(spc_fid, Kind, p1->get_proof(), p2->get_proof(), new_fact);
}
void * mem = m.get_allocator().allocate(sizeof(binary_justification));
return new (mem) binary_justification(m, p1, p2, pr);
}
};
inline justification * mk_superposition_justification(ast_manager & m, family_id spc_fid, justification * p1, justification * p2,
unsigned num_lits, literal * lits, unsigned num_vars, var * const * vars) {
return binary_justification<PR_SUPERPOSITION>::mk(m, spc_fid, p1, p2, num_lits, lits, num_vars, vars);
}
inline justification * mk_resolution_justification(ast_manager & m, family_id spc_fid, justification * p1, justification * p2,
unsigned num_lits, literal * lits, unsigned num_vars, var * const * vars) {
return binary_justification<PR_SPC_RESOLUTION>::mk(m, spc_fid, p1, p2, num_lits, lits, num_vars, vars);
}
};
#endif /* _SPC_JUSTIFICATION_H_ */

432
src/spc/spc_literal.cpp Normal file
View file

@ -0,0 +1,432 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_literal.cpp
Abstract:
Superposition Calculus literal
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#include"spc_literal.h"
#include"ast_pp.h"
namespace spc {
void literal::try_to_orient(order & o) {
ast_manager & m = o.get_manager();
if (!m_sign && m.is_eq(m_atom)) {
expr * lhs = to_app(m_atom)->get_arg(0);
expr * rhs = to_app(m_atom)->get_arg(1);
TRACE("spc_orient", tout << "trying to orient:\n" << mk_pp(lhs, m) << "\n" << mk_pp(rhs, m) << "\n";);
switch (o.compare(lhs, rhs)) {
case order::GREATER:
m_oriented = true;
m_left = true;
TRACE("spc_orient", tout << "greater\n";);
return;
case order::LESSER:
m_oriented = true;
m_left = false;
TRACE("spc_orient", tout << "smaller\n";);
return;
default:
return;
}
}
}
void literal::get_stat(literal_stat & stat) {
get_expr_stat(m_atom, stat);
m_stats = true;
m_ground = stat.m_ground;
m_sym_count = stat.m_sym_count > SYM_COUNT_MAX ? SYM_COUNT_MAX : stat.m_sym_count;
m_depth = stat.m_depth > DEPTH_MAX ? DEPTH_MAX : stat.m_depth;
m_const_count = stat.m_const_count > CONST_COUNT_MAX ? CONST_COUNT_MAX : stat.m_const_count;
}
expr * literal::to_expr(ast_manager & m) const {
if (is_true(m))
return m.mk_true();
else if (is_false(m))
return m.mk_false();
else if (m_sign)
return m.mk_not(m_atom);
else
return m_atom;
}
void literal::display(std::ostream & out, ast_manager & m, bool detailed) const {
pp_params p;
p.m_pp_single_line = true;
if (m_sign)
out << "(not ";
if (m_oriented) {
expr * lhs = to_app(m_atom)->get_arg(0);
expr * rhs = to_app(m_atom)->get_arg(1);
if (!m_left)
std::swap(lhs, rhs);
out << "(-> ";
ast_pp(out, lhs, m, p);
out << " ";
ast_pp(out, rhs, m, p);
out << ")";
}
else {
ast_pp(out, m_atom, m, p);
}
if (m_sign)
out << ")";
if (detailed && m_stats) {
out << "[" << m_ground << ", " << m_depth << ", " << m_sym_count << ", " << m_const_count << "]";
}
if (m_selected)
out << "$";
if (m_p_indexed)
out << "!";
if (m_r_indexed)
out << "@";
}
void display(std::ostream & out, unsigned num_lists, literal * lits, ast_manager & m, bool detailed) {
for (unsigned i = 0; i < num_lists; i++) {
if (i > 0) out << " ";
lits[i].display(out, m, detailed);
}
}
/**
\brief Given an eq literal store in lhs and rhs the left and right hand sides. If they can be oriented
given the substitution s, then return true, and make lhs the maximal one.
*/
bool can_orient(order & o, literal const & l, unsigned offset, substitution * s, expr * & lhs, expr * & rhs) {
SASSERT(o.get_manager().is_eq(l.atom()));
lhs = l.lhs();
rhs = l.rhs();
if (l.is_oriented()) {
if (!l.is_left())
std::swap(lhs, rhs);
return true;
}
else {
order::result comp = o.compare(lhs, rhs, offset, s);
if (comp == order::GREATER)
return true;
else if (comp == order::LESSER) {
std::swap(lhs, rhs);
return true;
}
return false;
}
}
/**
\brief Compare literal signs. Negative sign is bigger than the positive one.
*/
inline order::result compare_signs(bool sign1, bool sign2) {
if (sign1 && !sign2)
return order::GREATER;
else if (!sign1 && sign2)
return order::LESSER;
else
return order::EQUAL;
}
/**
\brief Compare two literals (modulo a substitution) using the given term ordering.
*/
order::result compare(order & o, literal const & l1, literal const & l2, unsigned offset, substitution * s) {
ast_manager & m = o.get_manager();
expr * n1 = l1.atom();
expr * n2 = l2.atom();
bool is_eq1 = m.is_eq(n1);
bool is_eq2 = m.is_eq(n2);
if (is_eq1 && is_eq2) {
expr * lhs1 = 0;
expr * rhs1 = 0;
expr * lhs2 = 0;
expr * rhs2 = 0;
bool oriented1 = can_orient(o, l1, offset, s, lhs1, rhs1);
bool oriented2 = can_orient(o, l2, offset, s, lhs2, rhs2);
if (oriented1) {
// equation 1 can be oriented
if (oriented2) {
// equation 2 can be oriented
// both equations are oriented
SASSERT(oriented1);
SASSERT(oriented2);
order::result r = o.compare(lhs1, lhs2, offset, s);
if (r == order::EQUAL) {
if (l1.pos()) {
if (l2.pos())
return o.compare(rhs1, rhs2, offset, s);
else
return order::LESSER;
}
else {
if (l2.pos())
return order::GREATER;
else
return o.compare(rhs1, rhs2, offset, s);
}
}
return r;
}
else {
// equation 2 cannot be oriented
SASSERT(oriented1);
SASSERT(!oriented2);
SASSERT(o.compare(lhs1, rhs1, offset, s) == order::GREATER);
if (o.equal(lhs1, lhs2, offset, s)) {
order::result r = o.compare(rhs1, rhs2, offset, s);
if (r == order::EQUAL)
return compare_signs(l1.sign(), l2.sign());
return r;
}
if (o.equal(lhs1, rhs2, offset, s)) {
order::result r = o.compare(rhs1, lhs2, offset, s);
if (r == order::EQUAL)
return compare_signs(l1.sign(), l2.sign());
return r;
}
order::result lhs1_lhs2 = o.compare(lhs1, lhs2, offset, s);
order::result lhs1_rhs2 = o.compare(lhs1, rhs2, offset, s);
if (lhs1_lhs2 == lhs1_rhs2)
return lhs1_lhs2;
order::result rhs1_rhs2 = o.compare(rhs1, rhs2, offset, s);
if (lhs1_lhs2 == rhs1_rhs2)
return lhs1_lhs2;
if (lhs1_rhs2 == order::LESSER && rhs1_rhs2 == order::LESSER)
return order::LESSER;
order::result rhs1_lhs2 = o.compare(rhs1, lhs2, offset, s);
if (lhs1_lhs2 == order::LESSER && rhs1_lhs2 == order::LESSER)
return order::LESSER;
return order::UNCOMPARABLE;
}
}
else {
// equation 1 cannot be oriented
if (oriented2) {
SASSERT(!oriented1);
SASSERT(oriented2);
// equation 2 can be oriented
if (o.equal(lhs1, lhs2, offset, s)) {
order::result r = o.compare(rhs1, rhs2, offset, s);
if (r == order::EQUAL)
return compare_signs(l1.sign(), l2.sign());
return r;
}
if (o.equal(rhs1, lhs2, offset, s)) {
order::result r = o.compare(lhs1, rhs2, offset, s);
if (r == order::EQUAL)
return compare_signs(l1.sign(), l2.sign());
return r;
}
order::result lhs1_lhs2 = o.compare(lhs1, lhs2, offset, s);
order::result rhs1_lhs2 = o.compare(rhs1, lhs2, offset, s);
if (lhs1_lhs2 == rhs1_lhs2)
return lhs1_lhs2;
order::result rhs1_rhs2 = o.compare(rhs1, rhs2, offset, s);
if (lhs1_lhs2 == rhs1_rhs2)
return lhs1_lhs2;
if (rhs1_lhs2 == order::GREATER && rhs1_rhs2 == order::GREATER)
return order::GREATER;
order::result lhs1_rhs2 = o.compare(lhs1, rhs2, offset, s);
if (lhs1_lhs2 == order::GREATER && lhs1_rhs2 == order::GREATER)
return order::GREATER;
return order::UNCOMPARABLE;
}
else {
SASSERT(!oriented1);
SASSERT(!oriented2);
if (o.equal(lhs1, lhs2, offset, s)) {
order::result r = o.compare(rhs1, rhs2, offset, s);
if (r == order::EQUAL)
return compare_signs(l1.sign(), l2.sign());
return r;
}
if (o.equal(rhs1, lhs2, offset, s)) {
order::result r = o.compare(lhs1, rhs2, offset, s);
if (r == order::EQUAL)
return compare_signs(l1.sign(), l2.sign());
return r;
}
if (o.equal(lhs1, rhs2, offset, s)) {
order::result r = o.compare(rhs1, lhs2, offset, s);
if (r == order::EQUAL)
return compare_signs(l1.sign(), l2.sign());
return r;
}
if (o.equal(rhs1, rhs2, offset, s)) {
order::result r = o.compare(lhs1, lhs2, offset, s);
if (r == order::EQUAL)
return compare_signs(l1.sign(), l2.sign());
return r;
}
order::result r;
order::result aux;
switch (o.compare(lhs1, lhs2, offset, s)) {
case order::GREATER:
r = o.compare(lhs1, rhs2, offset, s);
if (r == order::GREATER)
return order::GREATER;
aux = o.compare(rhs1, rhs2, offset, s);
if (aux == order::GREATER)
return order::GREATER;
if (r == order::LESSER && aux == order::LESSER)
return order::LESSER;
SASSERT(r != order::EQUAL);
SASSERT(aux != order::EQUAL);
return order::UNCOMPARABLE;
case order::LESSER:
r = o.compare(rhs1, lhs2, offset, s);
if (r == order::LESSER)
return order::LESSER;
aux = o.compare(rhs1, rhs2, offset, s);
if (aux == order::LESSER)
return order::LESSER;
if (r == order::GREATER && aux == order::GREATER)
return order::GREATER;
SASSERT(r != order::EQUAL);
SASSERT(aux != order::EQUAL);
return order::UNCOMPARABLE;
case order::EQUAL:
UNREACHABLE();
return order::UNKNOWN;
default:
switch (o.compare(lhs1, rhs2, offset, s)) {
case order::GREATER:
if (o.compare(rhs1, lhs2, offset, s) == order::GREATER)
return order::GREATER;
return order::UNCOMPARABLE;
case order::LESSER:
if (o.compare(rhs1, lhs2, offset, s) == order::LESSER ||
o.compare(rhs1, rhs2, offset, s) == order::LESSER)
return order::LESSER;
return order::UNCOMPARABLE;
case order::EQUAL:
UNREACHABLE();
return order::UNKNOWN;
default:
if (o.compare(rhs1, lhs2, offset, s) == order::GREATER &&
o.compare(rhs1, rhs2, offset, s) == order::GREATER)
return order::GREATER;
return order::UNCOMPARABLE;
}
}
}
}
}
else if (is_eq1) {
expr * lhs1 = l1.lhs();
expr * rhs1 = l1.rhs();
if (l1.is_oriented() && !l1.is_left())
std::swap(lhs1, rhs1);
order::result r = o.compare(lhs1, n2, offset, s);
if (!l1.is_oriented() || r != order::GREATER) {
order::result r2 = o.compare(rhs1, n2, offset, s);
if (r2 == order::GREATER)
return order::GREATER;
else if (r != r2)
return order::UNCOMPARABLE;
}
return r;
}
else if (is_eq2) {
expr * lhs2 = l2.lhs();
expr * rhs2 = l2.rhs();
if (l2.is_oriented() && !l2.is_left())
std::swap(lhs2, rhs2);
order::result r = o.compare(n1, lhs2, offset, s);
if (!l1.is_oriented() || r != order::LESSER) {
order::result r2 = o.compare(n1, rhs2, offset, s);
if (r2 == order::LESSER)
return order::LESSER;
else if (r != r2)
return order::UNCOMPARABLE;
}
return r;
}
else {
order::result r = o.compare(n1, n2, offset, s);
if (r == order::EQUAL)
return compare_signs(l1.sign(), l2.sign());
return r;
}
}
bool greater(order & o, literal const & l1, literal const & l2, unsigned offset, substitution * s) {
order::result r = compare(o, l1, l2, offset, s);
TRACE("literal_order", ast_manager & m = o.get_manager();
tout << "comparing ";
l1.display(tout, m);
tout << " ";
l2.display(tout, m);
tout << " : " << r << "\n";);
return r == order::GREATER;
}
void found_literals::insert(literal const & l) {
unsigned id = l.get_id();
m_marks.reserve(id+1);
if (!m_marks.get(id)) {
m_marks.set(id);
m_lit_ids.push_back(id);
}
}
bool found_literals::contains(literal const & l) const {
unsigned id = l.get_id();
return id < m_marks.size() && m_marks.get(id);
}
bool found_literals::contains_neg(literal const & l) const {
unsigned id = l.get_neg_id();
return id < m_marks.size() && m_marks.get(id);
}
void found_literals::reset() {
unsigned_vector::iterator it = m_lit_ids.begin();
unsigned_vector::iterator end = m_lit_ids.end();
for (; it != end; ++it)
m_marks.unset(*it);
m_lit_ids.reset();
}
void literal_buffer::reset() {
buffer<literal>::iterator it = m_lits.begin();
buffer<literal>::iterator end = m_lits.end();
for (; it != end; ++it)
m_manager.dec_ref(it->atom());
m_lits.reset();
}
expr * mk_or(ast_manager & m, unsigned num_lists, literal * lits) {
if (num_lists == 0)
return m.mk_false();
else if (num_lists == 1)
return lits[0].to_expr(m);
else {
ptr_buffer<expr> new_exprs;
for (unsigned i = 0; i < num_lists; i++)
new_exprs.push_back(lits[i].to_expr(m));
return m.mk_or(new_exprs.size(), new_exprs.c_ptr());
}
}
};

212
src/spc/spc_literal.h Normal file
View file

@ -0,0 +1,212 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_literal.h
Abstract:
Superposition Calculus literal
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _SPC_LITERAL_H_
#define _SPC_LITERAL_H_
#include"ast.h"
#include"order.h"
#include"expr_stat.h"
namespace spc {
typedef expr_stat literal_stat;
#define DEPTH_NUM_BITS 4
#define DEPTH_MAX ((1 << DEPTH_NUM_BITS) - 1)
#define CONST_COUNT_NUM_BITS 4
#define CONST_COUNT_MAX ((1 << CONST_COUNT_NUM_BITS) - 1)
#define SYM_COUNT_NUM_BITS 16
#define SYM_COUNT_MAX ((1 << SYM_COUNT_NUM_BITS) - 1)
/**
\brief Superposition Calculus literal.
*/
class literal {
expr * m_atom;
unsigned m_sign:1; // true if a negative literal.
unsigned m_oriented:1; // true if it is an oriented equality.
unsigned m_left:1; // true if the largest term is on the left-hand-side of the equality (only meaningful if m_oriented == true).
unsigned m_selected:1; // true if it is a selected literal.
unsigned m_stats:1; // true if the following fields were initialized.
unsigned m_ground:1; // true if it is a ground literal
unsigned m_p_indexed:1; // true if the literal was inserted into the p (paramodulation) superposition index.
unsigned m_r_indexed:1; // true if the literal was inserted into the r (resolution) superposition index.
unsigned m_depth:DEPTH_NUM_BITS; // approx. depth
unsigned m_const_count:CONST_COUNT_NUM_BITS; // approx. number of constants
unsigned m_sym_count:SYM_COUNT_NUM_BITS; // approx. size
friend class clause;
void set_selected(bool f) {
m_selected = f;
}
public:
literal():
m_atom(0),
m_sign(false),
m_oriented(false),
m_left(false),
m_selected(false),
m_stats(false),
m_ground(false),
m_p_indexed(false),
m_r_indexed(false),
m_depth(0),
m_const_count(0),
m_sym_count(0) {
}
literal(expr * atom, bool sign = false):
m_atom(atom),
m_sign(sign),
m_oriented(false),
m_left(false),
m_selected(false),
m_stats(false),
m_ground(false),
m_p_indexed(false),
m_r_indexed(false),
m_depth(0),
m_const_count(0),
m_sym_count(0) {
}
bool sign() const { return m_sign; }
bool pos() const { return !m_sign; }
bool neg() const { return m_sign; }
bool is_oriented() const { return m_oriented; }
bool is_left() const { return m_left; }
bool is_selected() const { return m_selected; }
expr * atom() const { return m_atom; }
expr * lhs() const { return to_app(m_atom)->get_arg(0); }
expr * rhs() const { return to_app(m_atom)->get_arg(1); }
unsigned get_id() const { return m_sign ? (to_app(m_atom)->get_id() << 1) + 1 : (to_app(m_atom)->get_id() << 1); }
unsigned get_neg_id() const { return m_sign ? (to_app(m_atom)->get_id() << 1) : (to_app(m_atom)->get_id() << 1) + 1; }
bool operator==(literal const & other) const { return m_atom == other.m_atom && m_sign == other.m_sign; }
bool operator!=(literal const & other) const { return !operator==(other); }
void set_p_indexed(bool f) { m_p_indexed = f; }
void set_r_indexed(bool f) { m_r_indexed = f; }
bool is_p_indexed() const { return m_p_indexed; }
bool is_r_indexed() const { return m_r_indexed; }
void try_to_orient(order & o);
bool is_true(ast_manager & m) const {
return
(!m_sign && m.is_true(m_atom)) ||
(!m_sign && m.is_eq(m_atom) && to_app(m_atom)->get_arg(0) == to_app(m_atom)->get_arg(1)) ||
(m_sign && m.is_false(m_atom));
}
bool is_false(ast_manager & m) const {
return
(m_sign && m.is_true(m_atom)) ||
(m_sign && m.is_eq(m_atom) && to_app(m_atom)->get_arg(0) == to_app(m_atom)->get_arg(1)) ||
(!m_sign && m.is_false(m_atom));
}
expr * to_expr(ast_manager & m) const;
/**
\brief Collect literal statistics
*/
void get_stat(literal_stat & stat);
void init_stat() { literal_stat st; get_stat(st); }
bool has_stats() const { return m_stats; }
bool is_ground() const { SASSERT(m_stats); return m_ground; }
unsigned get_approx_depth() const { SASSERT(m_stats); return m_depth; }
unsigned get_approx_const_count() const { SASSERT(m_stats); return m_const_count; }
unsigned get_approx_sym_count() const { SASSERT(m_stats); return m_sym_count; }
void display(std::ostream & out, ast_manager & m, bool detailed = false) const;
};
COMPILE_TIME_ASSERT(sizeof(expr*) != 4 || sizeof(literal) == sizeof(expr *) + sizeof(unsigned)); // 32 bit machine
COMPILE_TIME_ASSERT(sizeof(expr*) != 8 || sizeof(literal) == sizeof(expr *) + sizeof(unsigned) + /* a structure must be aligned */ sizeof(unsigned)); // 64 bit machine
void display(std::ostream & out, unsigned num_lists, literal * lits, ast_manager & m, bool detailed = false);
order::result compare(order & o, literal const & l1, literal const & l2, unsigned offset = 0, substitution * s = 0);
bool greater(order & o, literal const & l1, literal const & l2, unsigned offset = 0, substitution * s = 0);
bool is_maximal(order & o, unsigned num_lists, literal * lits, literal const & l, unsigned offset = 0, substitution * s = 0);
bool is_sel_maximal(order & o, unsigned num_lists, literal * lits, literal const & l, unsigned offset = 0, substitution * s = 0);
/**
\brief Set of found literals.
This object is used to implement duplicate literal elimination, ans syntatic tautology.
*/
class found_literals {
bit_vector m_marks;
unsigned_vector m_lit_ids;
public:
/**
\brief Insert the given literal into the set.
*/
void insert(literal const & l);
/**
\brief Return true if the set contains \c l.
*/
bool contains(literal const & l) const;
/**
\brief Return true if the set contains the negation of \c l.
*/
bool contains_neg(literal const & l) const;
bool empty() const { return m_lit_ids.empty(); }
/**
\brief Remove all literals from the set.
*/
void reset();
};
class literal_buffer {
ast_manager & m_manager;
buffer<literal> m_lits;
public:
literal_buffer(ast_manager & m):
m_manager(m) {
}
~literal_buffer() {
reset();
}
void push_back(literal const & l) {
m_manager.inc_ref(l.atom());
m_lits.push_back(l);
}
void reset();
unsigned size() const {
return m_lits.size();
}
literal * c_ptr() const {
return m_lits.c_ptr();
}
};
expr * mk_or(ast_manager & m, unsigned num_lists, literal * lits);
};
#endif /* _SPC_LITERAL_H_ */

View file

@ -0,0 +1,107 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_literal_selection.cpp
Abstract:
Superposition Calculus Literal Selection
Author:
Leonardo de Moura (leonardo) 2008-02-05.
Revision History:
--*/
#include"spc_literal_selection.h"
#include"expr_stat.h"
namespace spc {
void diff_literal_selection::operator()(clause * cls) {
bool found = false;
unsigned target = UINT_MAX;
unsigned best_count = 0;
unsigned num = cls->get_num_literals();
for (unsigned i = 0; i < num; i++) {
literal & l = cls->get_literal(i);
if (l.sign()) {
unsigned count;
if (m_manager.is_eq(l.atom())) {
unsigned c1 = get_symbol_count(to_app(l.atom())->get_arg(0));
unsigned c2 = get_symbol_count(to_app(l.atom())->get_arg(1));
count = c1 >= c2 ? c1 - c2 : c2 - c1;
}
else {
count = get_symbol_count(l.atom());
}
if (count > best_count) {
found = true;
target = i;
best_count = count;
}
}
}
if (found)
cls->select_literal(target);
}
void complex_literal_selection::operator()(clause * cls) {
// look for x != y
unsigned num = cls->get_num_literals();
for (unsigned i = 0; i < num; i++) {
literal & l = cls->get_literal(i);
if (l.sign() && m_manager.is_eq(l.atom()) && is_var(to_app(l.atom())->get_arg(0)) && is_var(to_app(l.atom())->get_arg(1))) {
cls->select_literal(i);
return;
}
}
// look for min ground neg literal
bool found = false;
unsigned target = UINT_MAX;
unsigned best_count = UINT_MAX;
for (unsigned i = 0; i < num; i++) {
literal & l = cls->get_literal(i);
if (l.sign() && is_ground(l.atom())) {
unsigned count = get_symbol_count(l.atom());
if (count < best_count) {
found = true;
target = i;
best_count = count;
}
}
}
if (found) {
cls->select_literal(target);
return;
}
diff_literal_selection::operator()(cls);
}
void max_no_selection::operator()(clause * cls) {
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l1 = cls->get_literal(i);
if (!l1.sign()) {
unsigned j = 0;
for (; j < num_lits; j++) {
if (i != j) {
literal const & l2 = cls->get_literal(j);
if (!greater(m_order, l1, l2))
break;
}
}
if (j == num_lits)
return; // clause has maximal positive literal.
}
}
diff_literal_selection::operator()(cls);
}
};

View file

@ -0,0 +1,95 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_literal_selection.h
Abstract:
Superposition Calculus Literal Selection
Author:
Leonardo de Moura (leonardo) 2008-02-05.
Revision History:
--*/
#ifndef _SPC_LITERAL_SELECTION_H_
#define _SPC_LITERAL_SELECTION_H_
#include"spc_clause.h"
#include"order.h"
namespace spc {
/**
\brief Abstract functor for literal selection.
*/
class literal_selection {
public:
virtual ~literal_selection() {}
/**
\brief Updates the selected status flag of the literals of the given clause.
*/
virtual void operator()(clause * cls) = 0;
};
/**
\brief Never selects a literal. This strategy is supposed to be good for planning problems
of TPTP.
*/
class no_literal_selection : public literal_selection {
public:
virtual void operator()(clause * cls) {}
};
/**
\brief Selects a negative literal l with the largest V(l)
where V is defined as:
- difference in symbol count for the left-right hand sides of equalities, .
- symbol count for other predicates
*/
class diff_literal_selection : public literal_selection {
protected:
ast_manager & m_manager;
public:
diff_literal_selection(ast_manager & m):m_manager(m) {}
virtual void operator()(clause * cls);
};
/**
\brief Selects a negative literal using the following algo:
- if there is x != y, select it.
- else if there is negative ground literal, select the smallest one.
- else if use the approach in diff_literal_selection.
*/
class complex_literal_selection : public diff_literal_selection {
public:
complex_literal_selection(ast_manager & m):diff_literal_selection(m) {}
virtual void operator()(clause * cls);
};
/**
\brief Similar to diff_literal_selection, but a literal
is not selected if the clause contains a positive literal
greater than all other literals.
*/
class max_no_selection : public diff_literal_selection {
order & m_order;
public:
max_no_selection(order & o):diff_literal_selection(o.get_manager()), m_order(o) {}
virtual void operator()(clause * cls);
};
};
#endif /* _SPC_LITERAL_SELECTION_H_ */

132
src/spc/spc_prover.cpp Normal file
View file

@ -0,0 +1,132 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_prover.cpp
Abstract:
Stand-alone SPC prover (it is mainly for debugging purposes).
Author:
Leonardo de Moura (leonardo) 2008-02-08.
Revision History:
--*/
#include"spc_prover.h"
#include"spc_decl_plugin.h"
#include"for_each_expr.h"
namespace spc {
prover::prover(ast_manager & m, front_end_params & params):
m_manager(m),
m_params(params),
m_simplifier(m),
m_defined_names(m),
m_preprocessor(m, m_defined_names, m_simplifier, params),
m_order(0),
m_cls_sel(0),
m_lit_sel(0),
m_context(0),
m_exprs(m),
m_expr_proofs(m),
m_has_theories(false) {
family_id fid = m_manager.get_family_id("spc");
if (!m_manager.has_plugin(fid))
m_manager.register_plugin(fid, alloc(spc_decl_plugin));
// This piece of code shows why the old model for passing parameters is broken.
// spc::prover must overwrite some parameters, but this modification affects other
// components. :-(
// TODO: move everything to the new params_ref object
params.m_nnf_mode = NNF_FULL;
params.m_cnf_mode = CNF_FULL;
params.m_lift_ite = LI_CONSERVATIVE;
basic_simplifier_plugin * basic = alloc(basic_simplifier_plugin, m_manager);
m_simplifier.register_plugin(basic);
m_simplifier.register_plugin(alloc(arith_simplifier_plugin, m_manager, *basic, params));
}
prover::~prover() {
if (m_context) {
dealloc(m_context);
dealloc(m_lit_sel);
dealloc(m_cls_sel);
dealloc(m_order);
}
}
void prover::init() {
if (m_context)
return;
precedence * p = mk_precedence(m_manager, m_params);
// TODO use params to configure the following functors.
m_order = alloc(kbo, m_manager, p);
clause_eval * evals[2] = { alloc(symbol_count_clause_eval), alloc(time_clause_eval) };
unsigned slots[2] = { 10, 1 };
m_cls_sel = alloc(clause_selection, 2, evals, slots);
m_lit_sel = alloc(max_no_selection, *m_order);
// m_lit_sel = new complex_literal_selection(m_manager);
// m_lit_sel = new diff_literal_selection(m_manager);
// m_lit_sel = new no_literal_selection(); // new diff_literal_selection(m_manager);
// END TODO
m_context = alloc(context, m_manager, *m_order, *m_cls_sel, *m_lit_sel, m_simplifier, m_params);
}
struct has_theories_proc {
ast_manager & m_manager;
has_theories_proc(ast_manager & m):m_manager(m) {}
struct found {};
void operator()(var * n) {}
void operator()(app * n) { if (!m_manager.is_builtin_family_id(n->get_family_id())) throw found(); }
void operator()(quantifier * n) {}
};
bool has_theories(ast_manager & m, expr * e) {
has_theories_proc p(m);
try {
for_each_expr(p, e);
}
catch (has_theories_proc::found) {
return true;
}
return false;
}
void prover::assert_expr(expr * e) {
if (!m_has_theories && has_theories(m_manager, e))
m_has_theories = true;
TRACE("spc_assert", tout << mk_pp(e, m_manager) << "\nhas_theories: " << m_has_theories << "\n";);
m_preprocessor(e, m_manager.mk_asserted(e), m_exprs, m_expr_proofs);
}
lbool prover::check() {
init();
unsigned sz = m_exprs.size();
for (unsigned i = 0; i < sz; i++) {
expr * curr = m_exprs.get(i);
proof * p = m_manager.proofs_enabled() ? m_expr_proofs.get(i) : m_manager.mk_undef_proof();
m_context->assert_expr(curr, p);
}
m_exprs.reset();
m_expr_proofs.reset();
m_context->saturate(m_params.m_spc_num_iterations);
if (m_context->inconsistent())
return l_false;
else if (m_context->processed_all())
return m_has_theories ? l_undef : l_true;
else
return l_undef;
}
};

59
src/spc/spc_prover.h Normal file
View file

@ -0,0 +1,59 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_prover.h
Abstract:
Stand-alone SPC prover (it is mainly for debugging purposes).
Author:
Leonardo de Moura (leonardo) 2008-02-08.
Revision History:
--*/
#ifndef _SPC_PROVER_H_
#define _SPC_PROVER_H_
#include"spc_context.h"
#include"front_end_params.h"
#include"kbo.h"
#include"lpo.h"
#include"basic_simplifier_plugin.h"
#include"arith_simplifier_plugin.h"
#include"preprocessor.h"
#include"defined_names.h"
#include"lbool.h"
namespace spc {
class prover {
ast_manager & m_manager;
front_end_params & m_params;
simplifier m_simplifier;
defined_names m_defined_names;
preprocessor m_preprocessor;
order * m_order;
clause_selection * m_cls_sel;
literal_selection * m_lit_sel;
context * m_context;
expr_ref_vector m_exprs;
proof_ref_vector m_expr_proofs;
bool m_has_theories;
void init();
public:
prover(ast_manager & m, front_end_params & params);
~prover();
void assert_expr(expr * e);
lbool check();
void display_statistics(std::ostream & out) const { if (m_context) m_context->display_statistics(out); }
};
};
#endif /* _SPC_PROVER_H_ */

269
src/spc/spc_rewriter.cpp Normal file
View file

@ -0,0 +1,269 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_rewrite.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-12.
Revision History:
--*/
#include"spc_rewriter.h"
#include"spc_decl_plugin.h"
#include"ast_pp.h"
namespace spc {
rewriter::rewriter(ast_manager & m, simplifier & simp, order & ord, asserted_literals & al):
simplifier(m),
m_asserted_literals(al),
m_order(ord),
m_spc_fid(m.get_family_id("spc")),
m_subst(m),
m_st(m),
m_visitor(m_order, m_subst, m_cls_use_list) {
reserve_offsets(3);
borrow_plugins(simp);
}
rewriter::~rewriter() {
release_plugins();
}
inline void rewriter::reserve_vars(unsigned num_vars) {
m_subst.reserve_vars(num_vars);
m_order.reserve_vars(num_vars);
m_asserted_literals.reserve_vars(num_vars);
}
void rewriter::reserve_offsets(unsigned num_offsets) {
m_subst.reserve_offsets(num_offsets);
m_order.reserve_offsets(num_offsets);
}
inline bool rewriter::demodulator(clause * cls) const {
if (cls->get_num_literals() != 1)
return false;
literal const & l = cls->get_literal(0);
return !l.sign() && m_manager.is_eq(l.atom());
}
inline void rewriter::insert(clause * cls, expr * source) {
if (!is_var(source)) {
TRACE("rewriter_detail", tout << "inserting into rewriter index:\n"; cls->display(tout, m_manager); tout << "\n";);
flush_cache();
m_st.insert(to_app(source));
m_cls_use_list.insert(cls, source);
}
}
void rewriter::insert(clause * cls) {
if (demodulator(cls)) {
reserve_vars(cls->get_num_vars());
literal const & l = cls->get_literal(0);
app * eq = to_app(l.atom());
if (l.is_oriented()) {
expr * source = l.is_left() ? eq->get_arg(0) : eq->get_arg(1);
insert(cls, source);
}
else {
insert(cls, eq->get_arg(0));
insert(cls, eq->get_arg(1));
}
}
}
inline void rewriter::erase(clause * cls, expr * source) {
if (!is_var(source)) {
flush_cache();
m_cls_use_list.erase(cls, source);
if (m_cls_use_list.empty(source))
m_st.erase(to_app(source));
}
}
void rewriter::erase(clause * cls) {
if (demodulator(cls)) {
literal const & l = cls->get_literal(0);
app * eq = to_app(l.atom());
if (l.is_oriented()) {
expr * source = l.is_left() ? eq->get_arg(0) : eq->get_arg(1);
erase(cls, source);
}
else {
erase(cls, eq->get_arg(0));
erase(cls, eq->get_arg(1));
}
}
}
bool rewriter::visitor::operator()(expr * e) {
if (m_cls_use_list.empty(e))
return true; // continue;
clause_use_list::iterator it = m_cls_use_list.begin(e);
clause_use_list::iterator end = m_cls_use_list.end(e);
for (; it != end; ++it) {
m_clause = *it;
SASSERT(m_clause->get_num_literals() == 1);
literal & l = m_clause->get_literal(0);
expr * atom = l.atom();
SASSERT(!l.sign() && m_manager.is_eq(atom));
SASSERT(to_app(atom)->get_arg(0) == e || to_app(atom)->get_arg(1) == e);
m_source = to_app(atom)->get_arg(0);
m_target = to_app(atom)->get_arg(1);
if (m_source != e)
std::swap(m_source, m_target);
SASSERT(m_source == e);
TRACE("rewriter", tout << "found generalization:\n" << mk_pp(m_source, m_manager) << "\n" <<
mk_pp(m_target, m_manager) << "\nsubstitution\n";
m_subst.display(tout); tout << "m_subst: " << &m_subst << "\n";
tout << "checking ordering constraints...\n";);
if (l.is_oriented() || m_order.greater(expr_offset(m_source, 1), expr_offset(m_target, 1), &m_subst)) {
m_found = true;
return false; // stop
}
TRACE("rewriter", tout << "failed ordering constraints...\n";);
}
return true; // continue
}
void rewriter::save_justification(justification * j) {
if (std::find(m_justifications.begin(), m_justifications.end(), j) == m_justifications.end())
m_justifications.push_back(j);
}
proof * rewriter::mk_demodulation_proof(expr * old_expr, expr * new_expr, proof * parent) {
if (m_manager.fine_grain_proofs()) {
SASSERT(parent);
return m_manager.mk_app(m_spc_fid, PR_DEMODULATION, parent, m_manager.mk_eq(old_expr, new_expr));
}
return 0;
}
void rewriter::reset() {
m_st.reset();
m_cls_use_list.reset();
}
void rewriter::reduce_literal(literal const & l, literal & l_r, proof * & l_pr) {
if (m_st.empty()) {
l_r = l;
l_pr = 0;
return;
}
expr * atom = l.atom();
expr * r;
proof * r_pr;
m_proofs.reset();
while (true) {
reduce_core(atom);
get_cached(atom, r, r_pr);
if (m_manager.fine_grain_proofs() && r_pr)
m_proofs.push_back(r_pr);
if (atom == r)
break;
atom = r;
}
l_r = literal(atom, l.sign());
if (m_manager.fine_grain_proofs())
l_pr = m_proofs.empty() ? 0 : m_manager.mk_transitivity(m_proofs.size(), m_proofs.c_ptr());
}
clause * rewriter::operator()(clause * cls) {
reserve_vars(cls->get_num_vars());
SASSERT(m_found_literals.empty());
m_justifications.reset();
m_max_scope_lvl = cls->get_scope_lvl();
literal_buffer new_literals(m_manager);
proof_ref_buffer new_proofs(m_manager);
bool changed = false;
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal & l = cls->get_literal(i);
literal l_r;
proof * l_pr = 0;
reduce_literal(l, l_r, l_pr);
if (l != l_r) {
changed = true;
}
if (!l_r.is_false(m_manager) && !m_found_literals.contains(l_r)) {
m_found_literals.insert(l_r);
// apply simplify reflect rules
expr * atom = l_r.atom();
clause * unit = 0;
TRACE("rewriter", tout << "adding literal: " << mk_pp(atom, m_manager) << "\n";);
if (l_r.sign()) {
if (m_manager.is_eq(atom))
unit = m_asserted_literals.subsumes(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1));
else
unit = m_asserted_literals.gen(atom, false);
}
else {
// check if there is a generalization of the negation of the current literal.
unit = m_asserted_literals.gen(atom, true);
}
if (unit) {
// new literal was resolved
justification * j = unit->get_justification();
m_justifications.push_back(j);
changed = true;
}
else {
// keep new literal
new_literals.push_back(l_r);
}
}
else {
// removed duplicate or resolved literal.
changed = true;
}
if (m_manager.fine_grain_proofs() && l_pr != 0) {
new_proofs.push_back(l_pr);
}
}
m_found_literals.reset();
if (!changed) {
m_found_literals.reset();
return cls;
}
proof * new_pr = mk_rewrite_proof(m_manager, m_spc_fid, new_literals.size(), new_literals.c_ptr(), cls->get_justification()->get_proof(),
new_proofs.size(), new_proofs.c_ptr());
justification * new_j = rewrite_justification::mk(m_manager, cls->get_justification(), m_justifications.size(), m_justifications.c_ptr(), new_pr);
if (m_max_scope_lvl == cls->get_scope_lvl()) {
// peform destructive update
cls->update_lits(m_manager, new_literals.size(), new_literals.c_ptr(), new_j);
return cls;
}
else {
SASSERT(m_max_scope_lvl > cls->get_scope_lvl());
// create new clause
// the old clause will be frozen
return clause::mk(m_manager, new_literals.size(), new_literals.c_ptr(), new_j, m_max_scope_lvl);
}
}
};

122
src/spc/spc_rewriter.h Normal file
View file

@ -0,0 +1,122 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_rewriter.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-11.
Revision History:
--*/
#ifndef _SPC_REWRITER_H_
#define _SPC_REWRITER_H_
#include"simplifier.h"
#include"order.h"
#include"substitution_tree.h"
#include"spc_clause.h"
#include"spc_asserted_literals.h"
#include"sparse_use_list.h"
namespace spc {
/**
\brief Apply rewriting steps using demodulation rule:
C[s] ==> C[sigma(r)]
when
l = r is a known equality (demodulator)
sigma(l) = s
sigma(l) > sigma(r)
It also applies the following rules:
- Duplicate literal deletion
- Resolved literal deletion
- Positive simplify reflect
s = t, (u[p <- sigma(s)] != u[p <- sigma(t)] or R)
==>
R
- Negative simplify reflect
s != t (sigma(s = t) or R)
===>
R
*/
class rewriter : public simplifier {
protected:
typedef sparse_use_list<expr, ptr_vector<clause> > clause_use_list;
asserted_literals & m_asserted_literals;
order & m_order;
family_id m_spc_fid;
substitution m_subst;
substitution_tree m_st; // index for potential demodulators left-hand-side
clause_use_list m_cls_use_list; // index for demodulators left-hand-side to equation.
found_literals m_found_literals;
ptr_vector<justification> m_justifications;
struct visitor : public st_visitor {
ast_manager & m_manager;
order & m_order;
clause_use_list & m_cls_use_list;
bool m_found;
clause * m_clause;
expr * m_source;
expr * m_target;
visitor(order & ord, substitution & subst, clause_use_list & ul):
st_visitor(subst), m_manager(ord.get_manager()), m_order(ord), m_cls_use_list(ul) {
}
virtual bool operator()(expr * e);
};
unsigned m_max_scope_lvl; // maximal scope level used during rewrite.
visitor m_visitor;
proof * mk_demodulation_proof(expr * old_expr, expr * new_expr, proof * parent);
bool demodulator(clause * cls) const;
void insert(clause * cls, expr * source);
void erase(clause * cls, expr * source);
void reserve_vars(unsigned num_vars);
void reserve_offsets(unsigned num_offsets);
void save_justification(justification * j);
void reduce_literal(literal const & l, literal & l_r, proof * & l_pr);
public:
rewriter(ast_manager & m, simplifier & s, order & ord, asserted_literals & al);
virtual ~rewriter();
/**
\brief Insert clause into rewriter indexes
*/
void insert(clause * cls);
/**
\brief Remove clause from rewriter indexes
*/
void erase(clause * cls);
clause * operator()(clause * cls);
void reset();
};
};
#endif /* _SPC_REWRITER_H_ */

View file

@ -0,0 +1,234 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_semantic_tautology.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-11.
Revision History:
--*/
#include"spc_semantic_tautology.h"
#include"ast_pp.h"
namespace spc {
expr * find(expr2expr & f, expr * n) {
#ifdef _TRACE
expr * _n = n;
#endif
ptr_buffer<expr> path;
expr * next;
while (f.find(n, next)) {
path.push_back(n);
n = next;
}
ptr_buffer<expr>::iterator it = path.begin();
ptr_buffer<expr>::iterator end = path.end();
for (; it != end; ++it) {
expr * prev = *it;
f.insert(prev, n);
}
SASSERT(n);
TRACE("semantic_tautology_detail", tout << "find(#" << _n->get_id() << ") = #" << n->get_id() << "\n";);
return n;
}
semantic_tautology::semantic_tautology(ast_manager & m):
m_manager(m),
m_cg_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, cg_hash(m_manager, m_find), cg_eq(m_find)) {
}
unsigned semantic_tautology::cg_hash::operator()(app * n) const {
TRACE("semantic_tautology_detail", tout << "hash code of:\n" << mk_pp(n, m_manager) << "\n";);
unsigned r = get_composite_hash<app *, k_hash, c_hash>(n, n->get_num_args(), m_k_hash, m_c_hash);
TRACE("semantic_tautology_detail", tout << "result: " << r << "\n";);
return r;
}
bool semantic_tautology::cg_eq::operator()(app * n1, app * n2) const {
if (n1->get_decl() != n2->get_decl() || n1->get_num_args() != n2->get_num_args())
return false;
unsigned num_args = n1->get_num_args();
for (unsigned i = 0; i < num_args; i++)
if (spc::find(m_find, n1->get_arg(i)) != spc::find(m_find, n2->get_arg(i)))
return false;
return true;
}
bool semantic_tautology::is_target(unsigned num_lits, literal * lits) {
bool has_diseq = false;
bool has_non_diseq = false;
for (unsigned i = 0; i < num_lits; i++) {
literal const & l = lits[i];
if (l.sign() && m_manager.is_eq(l.atom()))
has_diseq = true;
else
has_non_diseq = true;
}
return has_diseq && has_non_diseq;
}
void semantic_tautology::reset() {
m_region.reset();
m_init_todo.reset();
m_todo.reset();
m_already_found.reset();
m_use_list.reset();
m_find.reset();
m_size.reset();
m_cg_table.reset();
}
void semantic_tautology::update_use_list(app * parent, expr * child) {
list<app*> * use_list = 0;
m_use_list.find(child, use_list);
use_list = new (m_region) list<app*>(parent, use_list);
m_use_list.insert(child, use_list);
}
inline void semantic_tautology::push_init_core(expr * n) {
if (is_app(n) && to_app(n)->get_num_args() > 0)
m_init_todo.push_back(to_app(n));
}
inline void semantic_tautology::push_init(expr * atom) {
if (m_manager.is_eq(atom)) {
push_init_core(to_app(atom)->get_arg(0));
push_init_core(to_app(atom)->get_arg(1));
}
else
push_init_core(atom);
}
void semantic_tautology::init_use_list() {
while (!m_init_todo.empty()) {
app * n = m_init_todo.back();
m_init_todo.pop_back();
if (!m_already_found.contains(n)) {
unsigned num_args = n->get_num_args();
SASSERT(num_args > 0);
m_cg_table.insert(n);
m_already_found.insert(n);
for (unsigned i = 0; i < num_args; i++) {
expr * c = n->get_arg(i);
update_use_list(n, c);
push_init_core(c);
}
}
}
}
void semantic_tautology::init(unsigned num_lits, literal * lits) {
reset();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l = lits[i];
expr * atom = l.atom();
push_init(atom);
if (l.sign() && m_manager.is_eq(atom))
m_todo.push_back(expr_pair(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1)));
}
init_use_list();
}
void semantic_tautology::remove_parents(expr * n1) {
list<app*> * use_list = 0;
m_use_list.find(n1, use_list);
while (use_list) {
TRACE("semantic_tautology", tout << "removing parent from cg_table:\n" << mk_pp(use_list->head(), m_manager) << "\n";);
m_cg_table.erase(use_list->head());
use_list = use_list->tail();
}
}
void semantic_tautology::restore_parents(expr * n1, expr * n2) {
list<app*> * use_list = 0;
m_use_list.find(n1, use_list);
while (use_list) {
app * parent = use_list->head();
app * other = 0;
if (m_cg_table.find(parent, other)) {
TRACE("semantic_tautology", tout << "new congruence:\n" << mk_pp(parent, m_manager) << "\n" << mk_pp(other, m_manager) << "\n";);
if (parent != other)
m_todo.push_back(expr_pair(parent, other));
}
else {
TRACE("semantic_tautology", tout << "restoring parent to cg_table:\n" << mk_pp(parent, m_manager) << "\n";);
m_cg_table.insert(parent);
update_use_list(parent, n2);
}
use_list = use_list->tail();
}
}
void semantic_tautology::assert_eq(expr * n1, expr * n2) {
n1 = find(n1);
n2 = find(n2);
if (n1 == n2)
return;
TRACE("semantic_tautology", tout << "processing equality:\n" << mk_pp(n1, m_manager) << " " << n1->get_id() << "\n" <<
mk_pp(n2, m_manager) << " " << n2->get_id() << "\n";);
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(n1, sz1);
m_size.find(n2, sz2);
if (sz1 > sz2)
std::swap(n1, n2);
remove_parents(n1);
TRACE("semantic_tautology", tout << "merging equivalence classes\n";);
m_find.insert(n1, n2);
m_size.insert(n2, sz1 + sz2);
restore_parents(n1, n2);
}
void semantic_tautology::process_eqs() {
while (!m_todo.empty()) {
expr_pair const & p = m_todo.back();
expr * lhs = p.first;
expr * rhs = p.second;
m_todo.pop_back();
assert_eq(lhs, rhs);
}
}
bool semantic_tautology::contains_complement(unsigned num_lits, literal * lits, unsigned i, bool sign, expr * atom) {
atom = find(atom);
for (unsigned j = i + 1; j < num_lits; j++) {
literal const & l = lits[j];
if (l.sign() != sign && find(l.atom()) == atom)
return true;
}
return false;
}
bool semantic_tautology::is_tautology(unsigned num_lits, literal * lits) {
for (unsigned i = 0; i < num_lits; i++) {
literal const & l = lits[i];
expr * atom = l.atom();
if (!l.sign() && m_manager.is_eq(atom) && find(to_app(atom)->get_arg(0)) == find(to_app(atom)->get_arg(1)))
return true;
if (!m_manager.is_eq(atom) && contains_complement(num_lits, lits, i, l.sign(), atom))
return true;
}
return false;
}
bool semantic_tautology::operator()(unsigned num_lits, literal * lits) {
if (!is_target(num_lits, lits))
return false;
init(num_lits, lits);
process_eqs();
bool r = is_tautology(num_lits, lits);
TRACE("semantic_tautology", display(tout, num_lits, lits, m_manager); tout << "\nis semantic tautology: " << r << "\n";);
return r;
}
};

View file

@ -0,0 +1,114 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_semantic_tautology.h
Abstract:
Semantic tautology detection
Author:
Leonardo de Moura (leonardo) 2008-02-11.
Revision History:
--*/
#ifndef _SPC_SEMANTIC_TAUTOLOGY_H_
#define _SPC_SEMANTIC_TAUTOLOGY_H_
#include"spc_literal.h"
#include"list.h"
#include"obj_hashtable.h"
#include"map.h"
namespace spc {
typedef obj_map<expr, expr *> expr2expr;
expr * find(expr2expr & f, expr * e);
/**
\brief Functor for detecting semantic tautology.
A clause C is a semantic tautology if it has the following form:
s_1 != t_1 or ... or s_n != t_n or s = t or R
sigma(s_1 = t_1), ..., sigma(s_n t_n) |= sigma(s = t)
where sigma maps variables to constants.
*/
class semantic_tautology {
typedef std::pair<expr *, expr *> expr_pair;
typedef obj_hashtable<expr> already_found;
typedef expr2expr find_map;
typedef obj_map<expr, list<app*> *> use_list;
typedef obj_map<expr, unsigned> size_map;
struct k_hash {
unsigned operator()(app * n) const { return n->get_decl()->get_id(); }
};
struct c_hash {
find_map & m_find;
c_hash(find_map & f):m_find(f) {}
unsigned operator()(app * n, unsigned i) const {
unsigned id = spc::find(m_find, n->get_arg(i))->get_id();
TRACE("semantic_tautology_detail", tout << "child(" << i << ") = #" << id << "\n";);
return id;
}
};
struct cg_hash {
ast_manager & m_manager;
k_hash m_k_hash;
c_hash m_c_hash;
cg_hash(ast_manager & m, find_map & f):m_manager(m), m_c_hash(f) {}
unsigned operator()(app * n) const;
};
struct cg_eq {
find_map & m_find;
cg_eq(find_map & f):m_find(f) {}
bool operator()(app * n1, app * n2) const;
};
typedef ptr_hashtable<app, cg_hash, cg_eq> cg_table;
ast_manager & m_manager;
region m_region;
ptr_vector<app> m_init_todo;
svector<expr_pair> m_todo;
already_found m_already_found;
use_list m_use_list;
find_map m_find;
size_map m_size;
cg_table m_cg_table;
bool is_target(unsigned num_lits, literal * lits);
void reset();
void update_use_list(app * parent, expr * child);
void push_init_core(expr * n);
void push_init(expr * atom);
void init_use_list();
void init(unsigned num_lits, literal * lits);
expr * find(expr * n) { return spc::find(m_find, n); }
void remove_parents(expr * n1);
void restore_parents(expr * n1, expr * n2);
void assert_eq(expr * n1, expr * n2);
void process_eqs();
bool contains_complement(unsigned num_lits, literal * lits, unsigned i, bool sign, expr * atom);
bool is_tautology(unsigned num_lits, literal * lits);
public:
semantic_tautology(ast_manager & m);
bool operator()(unsigned num_lits, literal * lits);
};
};
#endif /* _SPC_SEMANTIC_TAUTOLOGY_H_ */

View file

@ -0,0 +1,54 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_statistics.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-17.
Revision History:
--*/
#include"spc_statistics.h"
namespace spc {
void statistics::reset() {
m_num_mk_clause = 0;
m_num_del_clause = 0;
m_num_processed = 0;
m_num_superposition = 0;
m_num_resolution = 0;
m_num_eq_factoring = 0;
m_num_factoring = 0;
m_num_eq_resolution = 0;
m_num_trivial = 0;
m_num_simplified = 0;
m_num_subsumed = 0;
m_num_redundant = 0;
}
void statistics::display(std::ostream & out) const {
out << "num. mk. clause: " << m_num_mk_clause << "\n";
out << "num. del. clause: " << m_num_del_clause << "\n";
out << "num. processed: " << m_num_processed << "\n";
out << "num. superposition: " << m_num_superposition << "\n";
out << "num. resolution: " << m_num_resolution << "\n";
out << "num. eq. factoring: " << m_num_eq_factoring << "\n";
out << "num. factoring: " << m_num_factoring << "\n";
out << "num. eq. resol.: " << m_num_eq_resolution << "\n";
out << "num. simplified: " << m_num_simplified << "\n";
out << "num. redundant: " << m_num_redundant << "\n";
out << " num. trivial: " << m_num_trivial << "\n";
out << " num. subsumed: " << m_num_subsumed << "\n";
}
};

49
src/spc/spc_statistics.h Normal file
View file

@ -0,0 +1,49 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_statistics.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-17.
Revision History:
--*/
#ifndef _SPC_STATISTICS_H_
#define _SPC_STATISTICS_H_
#include<iostream>
namespace spc {
struct statistics {
unsigned m_num_mk_clause;
unsigned m_num_del_clause;
unsigned m_num_processed;
unsigned m_num_superposition;
unsigned m_num_resolution;
unsigned m_num_eq_factoring;
unsigned m_num_factoring;
unsigned m_num_eq_resolution;
unsigned m_num_trivial;
unsigned m_num_simplified;
unsigned m_num_subsumed;
unsigned m_num_redundant;
statistics() {
reset();
}
void reset();
void display(std::ostream & out) const;
};
};
#endif /* _SPC_STATISTICS_H_ */

698
src/spc/spc_subsumption.cpp Normal file
View file

@ -0,0 +1,698 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_subsumption.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-13.
Revision History:
--*/
#include"spc_subsumption.h"
#include"fvi_def.h"
#include"ast_pp.h"
namespace spc {
/**
\brief Return true if literal l2 is an instance of l1.
When ResetSubst == true, m_subst is reset before trying to match l1 and l2.
When ResetSubst == false, it is assumed that m_subst.push_scope was invoked
before invoking match_literal.
*/
template<bool ResetSubst>
bool subsumption::match_literal(literal const & l1, literal const & l2) {
if (l1.sign() == l2.sign()) {
expr * atom1 = l1.atom();
expr * atom2 = l2.atom();
bool is_eq1 = m_manager.is_eq(atom1);
bool is_eq2 = m_manager.is_eq(atom2);
if (is_eq1 && is_eq2) {
expr * lhs1 = to_app(atom1)->get_arg(0);
expr * rhs1 = to_app(atom1)->get_arg(1);
expr * lhs2 = to_app(atom2)->get_arg(0);
expr * rhs2 = to_app(atom2)->get_arg(1);
if (ResetSubst)
m_subst.reset_subst();
if (m_matcher(lhs1, lhs2, m_subst) && m_matcher(rhs1, rhs2, m_subst))
return true;
if (ResetSubst)
m_subst.reset_subst();
else {
// I'm assuming push_scope was invoked before executing match_literal
// So, pop_scope is equivalent to a local reset.
m_subst.pop_scope();
m_subst.push_scope();
}
return (m_matcher(lhs1, rhs2, m_subst) && m_matcher(rhs1, lhs2, m_subst));
}
else if (!is_eq1 && !is_eq2) {
if (ResetSubst)
m_subst.reset_subst();
return m_matcher(atom1, atom2, m_subst);
}
}
return false;
}
/**
\brief Return true if for every literal l1 in lits1 there is a
literal l2 in lits2 such that l2 is an instance of l1.
*/
bool subsumption::can_subsume(unsigned num_lits1, literal * lits1, unsigned num_lits2, literal * lits2) {
for (unsigned i = 0; i < num_lits1; i++) {
literal const & l1 = lits1[i];
unsigned j = 0;
for (; j < num_lits2; j++) {
literal const & l2 = lits2[j];
if (match_literal<true>(l1, l2))
break;
}
if (j == num_lits2)
return false;
}
return true;
}
/**
\brief Return true if cls1 can subsume cls2. It performs a series of quick checks.
*/
bool subsumption::quick_check(clause * cls1, clause * cls2) {
return
cls1->get_symbol_count() <= cls2->get_symbol_count() &&
cls1->get_const_count() <= cls2->get_const_count() &&
cls1->get_depth() <= cls2->get_depth() &&
cls1->get_num_pos_literals() <= cls2->get_num_pos_literals() &&
cls1->get_num_neg_literals() <= cls2->get_num_neg_literals() &&
(!cls1->is_ground() || cls2->is_ground());
}
/**
\brief Return true if the set of literals lits1 subsumes the set of literals lits2.
*/
bool subsumption::subsumes_core(unsigned num_lits1, literal * lits1, unsigned num_lits2, literal * lits2) {
enum state {
INVOKE, DECIDE, BACKTRACK, RETURN
};
if (num_lits1 == 0)
return true;
m_stack.reset();
m_subst.reset();
m_stack.push_back(assoc(0, 0));
state st = DECIDE;
unsigned i;
assoc * top;
unsigned counter = 0;
#ifdef _TRACE
unsigned opt = 0;
unsigned nopt = 0;
#endif
while (true && counter < 5000000) {
counter++;
switch (st) {
case INVOKE:
SASSERT(!m_stack.empty());
i = m_stack.back().first + 1;
if (i >= num_lits1) {
TRACE("subsumption", tout << "subsumption result: YES.\n";);
TRACE_CODE({
if (counter > 10000) {
TRACE("subsumption_perf",
tout << "subsumption succeeded: " << counter << " " << opt << " " << nopt << "\n";
tout << "literals1:\n"; display(tout, num_lits1, lits1, m_manager); tout << "\n";
tout << "literals2:\n"; display(tout, num_lits2, lits2, m_manager); tout << "\n";);
}
});
return true;
}
else {
m_stack.push_back(assoc(i, 0));
st = DECIDE;
}
break;
case DECIDE:
top = &(m_stack.back());
m_subst.push_scope();
if (match_literal<false>(lits1[top->first], lits2[top->second]))
st = INVOKE;
else
st = BACKTRACK;
break;
case BACKTRACK:
top = &(m_stack.back());
top->second++;
m_subst.pop_scope();
if (top->second >= num_lits2)
st = RETURN;
else
st = DECIDE;
break;
case RETURN:
top = &(m_stack.back());
m_stack.pop_back();
if (m_stack.empty()) {
// no more alternatives
TRACE("subsumption", tout << "subsumption result: NO\n";);
TRACE_CODE({
if (counter > 10000) {
TRACE("subsumption_perf",
tout << "subsumption failed: " << counter << " " << opt << " " << nopt << "\n";
tout << "literals1:\n"; display(tout, num_lits1, lits1, m_manager); tout << "\n";
tout << "literals2:\n"; display(tout, num_lits2, lits2, m_manager); tout << "\n";);
}
});
return false;
}
if (m_subst.top_scope_has_bindings()) {
TRACE_CODE(nopt++;);
st = BACKTRACK;
}
else {
TRACE_CODE(opt++;);
#ifdef Z3DEBUG
unsigned num_bindings = m_subst.get_num_bindings();
#endif
m_subst.pop_scope();
SASSERT(num_bindings == m_subst.get_num_bindings());
st = RETURN;
}
break;
}
}
return false;
}
/**
\brief Return true if the set of ground literals lits1 subsumes the set of ground literals lits2.
*/
bool subsumption::ground_subsumes_core(unsigned num_lits1, literal * lits1, unsigned num_lits2, literal * lits2) {
for (unsigned i = 0; i < num_lits1; i++) {
literal const & l1 = lits1[i];
unsigned j = 0;
for (; j < num_lits2; j++) {
literal const & l2 = lits2[j];
if (l1 == l2)
break;
}
if (j == num_lits2)
return false;
}
return true;
}
/**
\brief Return true if the literal l1 subsumes the set of literals lits2.
*/
bool subsumption::subsumes_core(literal const & l1, unsigned num_lits2, literal * lits2) {
for (unsigned i = 0; i < num_lits2; i++) {
if (match_literal<true>(l1, lits2[i]))
return true;
}
return false;
}
subsumption::subsumption(ast_manager & m, asserted_literals & al, spc_params & params):
m_manager(m),
m_params(params),
m_asserted_literals(al),
m_subst(m),
m_matcher(m),
m_found_decls(m),
m_index(0),
m_num_processed_clauses(0),
m_opt_threshold(params.m_initial_subsumption_index_opt) {
m_subst.reserve_offsets(1);
init_indexes();
}
subsumption::~subsumption() {
if (m_index)
dealloc(m_index);
}
/**
\brief Return true if cls1 subsumes cls2
*/
bool subsumption::operator()(clause * cls1, clause * cls2) {
TRACE("subsumption_detail", tout << "checking if:\n"; cls1->display(tout, m_manager); tout << "\nsubsumes\n";
cls2->display(tout, m_manager); tout << "\n";);
if (!quick_check(cls1, cls2)) {
TRACE("subsumption_detail", tout << "failed quick check\n";);
return false;
}
m_subst.reserve_vars(std::max(cls1->get_num_vars(), cls2->get_num_vars()));
unsigned num_lits1 = cls1->get_num_literals();
unsigned num_lits2 = cls2->get_num_literals();
literal * lits1 = cls1->get_literals();
literal * lits2 = cls2->get_literals();
if (cls1->is_ground() && cls2->is_ground())
return ground_subsumes_core(num_lits1, lits1, num_lits2, lits2);
if (num_lits1 == 1)
return subsumes_core(lits1[0], num_lits2, lits2);
// TODO: REMOVE true below... using it for debugging purposes.
if (true || cls1->get_num_neg_literals() >= 3 || cls1->get_num_pos_literals() >= 3)
if (!can_subsume(num_lits1, lits1, num_lits2, lits2)) {
TRACE("subsumption_detail", tout << "failed can_subsume\n";);
return false;
}
return subsumes_core(num_lits1, lits1, num_lits2, lits2);
}
/**
\brief Update the set of function symbols found in the clause being inserted into the index,
and the set of clauses found since the index started to be built.
Return true if the function symbol should be tracked.
*/
bool subsumption::mark_func_decl(func_decl * f) {
if (m_refining_index) {
if (!m_cls_found_decl_set.contains(f)) {
// update set of func_decls in the curr clause
m_cls_found_decl_set.insert(f);
m_cls_found_decls.push_back(f);
// update global set of founf func_decls
unsigned id = f->get_decl_id();
m_found_decl_set.reserve(id+1);
if (!m_found_decl_set.get(id)) {
m_found_decl_set.set(id);
m_found_decls.push_back(f);
}
}
return true;
}
else {
unsigned id = f->get_decl_id();
// if func_decl was not found yet, then ignore it.
if (id < m_found_decl_set.size() && m_found_decl_set.get(id)) {
if (!m_cls_found_decl_set.contains(f)) {
// update set of func_decls in the curr clause
m_cls_found_decl_set.insert(f);
m_cls_found_decls.push_back(f);
}
return true;
}
return false;
}
}
/**
\brief Increment the number of occurrences of a function symbol in the clause being
inserted into the index.
*/
void subsumption::inc_f_count(func_decl * f, bool neg) {
decl2nat & f_count = m_f_count[static_cast<unsigned>(neg)];
unsigned val;
if (f_count.find(f, val)) {
f_count.insert(f, val + 1);
}
else {
f_count.insert(f, 1);
}
}
/**
\brief Update the min/max num. of occurrences of func symbol in a clause.
*/
void subsumption::update_min_max(func_decl * f) {
for (unsigned is_neg = 0; is_neg < 1; is_neg++) {
decl2nat & f_count = m_f_count[is_neg];
decl2nat & f_min = m_f_min[is_neg];
decl2nat & f_max = m_f_max[is_neg];
unsigned count;
if (f_count.find(f, count)) {
unsigned old_count;
if (!f_min.find(f, old_count) || old_count > count) {
f_min.insert(f, count);
}
if (!f_max.find(f, old_count) || old_count < count) {
f_max.insert(f, count);
}
}
}
}
/**
\brief Compute the number of occurences of function symbols in
a clause.
*/
void subsumption::update_neg_pos_func_counts(clause * cls) {
m_f_count[0].reset();
m_f_count[1].reset();
m_cls_found_decl_set.reset();
m_cls_found_decls.reset();
ptr_buffer<expr> todo;
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l = cls->get_literal(i);
bool is_neg = l.sign();
expr * n = l.atom();
todo.push_back(n);
while (!todo.empty()) {
n = todo.back();
todo.pop_back();
if (is_app(n)) {
func_decl * f = to_app(n)->get_decl();
if (fvi_candidate(f) && mark_func_decl(f))
inc_f_count(f, is_neg);
unsigned num = to_app(n)->get_num_args();
for (unsigned i = 0; i < num; i++)
todo.push_back(to_app(n)->get_arg(i));
}
}
}
if (m_refining_index) {
ptr_vector<func_decl>::iterator it = m_cls_found_decls.begin();
ptr_vector<func_decl>::iterator end = m_cls_found_decls.end();
for (; it != end; ++it) {
func_decl * f = *it;
update_min_max(f);
unsigned val;
if (m_f_freq.find(f, val))
m_f_freq.insert(f, val + 1);
else
m_f_freq.insert(f, 1);
}
}
}
/**
\brief Store in m_feature_vector the value for the features of cls.
*/
void subsumption::compute_features(clause * cls, unsigned * fvector) {
unsigned num = m_features.size();
for (unsigned i = 0; i < num; i++) {
feature & f = m_features[i];
switch (f.m_kind) {
case F_GROUND:
fvector[i] = cls->is_ground();
break;
case F_NUM_POS_LITS:
fvector[i] = cls->get_num_pos_literals();
break;
case F_NUM_NEG_LITS:
fvector[i] = cls->get_num_neg_literals();
break;
case F_DEPTH:
fvector[i] = cls->get_depth();
break;
case F_CONST_COUNT:
fvector[i] = cls->get_const_count();
break;
case F_SYM_COUNT:
fvector[i] = cls->get_symbol_count();
break;
case F_NUM_NEG_FUNCS: {
unsigned val;
if (m_f_count[1].find(f.m_decl, val))
fvector[i] = val;
else
fvector[i] = 0;
break;
}
case F_NUM_POS_FUNCS: {
unsigned val;
if (m_f_count[0].find(f.m_decl, val))
fvector[i] = val;
else
fvector[i] = 0;
break;
}
default:
UNREACHABLE();
}
}
TRACE("subsumption_features",
tout << "features of: "; cls->display(tout, m_manager); tout << "\n";
for (unsigned i = 0; i < num; i++) {
tout << fvector[i] << " ";
}
tout << "\n";);
}
/**
\brief Initialise indexes for forward/backward subsumption.
*/
void subsumption::init_indexes() {
// index for forward/backward subsumption
// start with simple set of features
m_features.push_back(feature(F_GROUND));
m_features.push_back(feature(F_NUM_POS_LITS));
m_features.push_back(feature(F_NUM_NEG_LITS));
m_features.push_back(feature(F_DEPTH));
m_features.push_back(feature(F_CONST_COUNT));
m_features.push_back(feature(F_SYM_COUNT));
m_index = alloc(index, m_features.size(), to_feature_vector(*this));
}
unsigned subsumption::get_value_range(func_decl * f, bool neg) const {
unsigned i = static_cast<unsigned>(neg);
unsigned min;
unsigned max;
if (!m_f_min[i].find(f, min))
min = 0;
if (!m_f_max[i].find(f, max))
max = 0;
SASSERT(min <= max);
return max - min;
}
inline unsigned subsumption::get_value_range(func_decl * f) const {
return std::max(get_value_range(f, false), get_value_range(f, true));
}
bool subsumption::f_lt::operator()(func_decl * f1, func_decl * f2) const {
unsigned vrange1 = m_owner.get_value_range(f1);
unsigned vrange2 = m_owner.get_value_range(f2);
if (vrange1 < vrange2)
return true;
if (vrange1 == vrange2)
return f1->get_id() < f2->get_id();
return false;
}
/**
\brief Optimize the index for (non unit) forward subsumption and
backward subsumption.
*/
void subsumption::optimize_feature_index() {
ptr_vector<clause> clauses;
m_index->collect(clauses);
dealloc(m_index);
m_features.reset();
ptr_vector<func_decl> targets;
unsigned sz = m_found_decls.size();
for (unsigned i = 0; i < sz; i++) {
func_decl * f = m_found_decls.get(i);
unsigned val;
if (m_f_freq.find(f, val) && val > m_params.m_min_func_freq_subsumption_index && get_value_range(f) > 0)
targets.push_back(f);
}
f_lt lt(*this);
std::sort(targets.begin(), targets.end(), lt);
m_features.push_back(feature(F_GROUND));
m_features.push_back(feature(F_NUM_POS_LITS));
m_features.push_back(feature(F_NUM_NEG_LITS));
m_features.push_back(feature(F_DEPTH));
ptr_vector<func_decl>::iterator it = targets.begin();
ptr_vector<func_decl>::iterator end = targets.end();
for (; it != end; ++it) {
func_decl * f = *it;
if (get_value_range(f, false) > 1)
m_features.push_back(feature(f, false));
if (get_value_range(f, true) > 1)
m_features.push_back(feature(f, true));
if (m_features.size() > m_params.m_max_subsumption_index_features)
break;
}
m_features.push_back(feature(F_CONST_COUNT));
m_features.push_back(feature(F_SYM_COUNT));
m_index = alloc(index, m_features.size(), to_feature_vector(*this));
m_num_processed_clauses = 0;
unsigned new_threshold = static_cast<unsigned>(m_opt_threshold * m_params.m_factor_subsumption_index_opt);
if (new_threshold > m_opt_threshold)
m_opt_threshold = new_threshold;
}
/**
\brief Insert cls into the indexes used for forward/backward subsumption.
*/
void subsumption::insert(clause * cls) {
TRACE("subsumption", tout << "adding clause to subsumption index: " << cls << "\n"; cls->display(tout, m_manager); tout << "\n";);
unsigned num_lits = cls->get_num_literals();
if (num_lits > 1 || m_params.m_backward_subsumption) {
m_index->insert(cls);
SASSERT(m_index->contains(cls));
m_num_processed_clauses++;
if (m_num_processed_clauses > m_opt_threshold)
optimize_feature_index();
}
}
/**
\brief Remove cls from the indexes used for forward/backward subsumption.
*/
void subsumption::erase(clause * cls) {
TRACE("subsumption", tout << "removing clause from subsumption index:" << cls << "\n"; cls->display(tout, m_manager); tout << "\n";
tout << "num lits.: " << cls->get_num_literals() << ", backward_sub: " << m_params.m_backward_subsumption << "\n";);
unsigned num_lits = cls->get_num_literals();
if (num_lits > 1 || m_params.m_backward_subsumption)
m_index->erase(cls);
}
/**
\brief Reset the indexes used for forward/backward subsumption.
*/
void subsumption::reset() {
if (m_index)
m_index->reset();
m_num_processed_clauses = 0;
m_opt_threshold = m_params.m_initial_subsumption_index_opt;
}
/**
\brief Return an unit clause C in the index that subsumes cls.
Return 0 if such clause does not exist.
*/
clause * subsumption::unit_forward(clause * cls) {
if (!m_asserted_literals.has_literals())
return 0;
m_asserted_literals.reserve_vars(cls->get_num_vars());
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l = cls->get_literal(i);
clause * subsumer = m_asserted_literals.gen(l);
if (subsumer)
return subsumer;
}
return 0;
}
struct non_unit_subsumption_visitor {
subsumption & m_owner;
clause * m_new_clause;
clause * m_subsumer;
non_unit_subsumption_visitor(subsumption & owner, clause * new_clause):
m_owner(owner),
m_new_clause(new_clause),
m_subsumer(0) {
}
bool operator()(clause * candidate) {
TRACE("subsumption_index", tout << "considering candidate:\n"; candidate->display(tout, m_owner.get_manager()); tout << "\n";);
if (candidate->get_num_literals() > 1 && m_owner(candidate, m_new_clause)) {
m_subsumer = candidate;
return false; // stop subsumer was found
}
return true; // continue;
}
};
/**
\brief Return a non unit clause C in the index that subsumes cls.
Return 0 if such clause does not exist.
*/
clause * subsumption::non_unit_forward(clause * cls) {
non_unit_subsumption_visitor visitor(*this, cls);
m_index->visit(cls, visitor, true);
return visitor.m_subsumer;
}
/**
\brief Return a unit equality clause (= s t) that (eq) subsumes cls.
That is, cls contains a literal (= u[s'] u[t']) and there is
a substitution sigma s.t. sigma(s) = s' and sigma(t) = t'.
Return 0 if such clause does not exist.
*/
clause * subsumption::eq_subsumption(clause * cls) {
if (!m_asserted_literals.has_pos_literals())
return 0;
m_asserted_literals.reserve_vars(cls->get_num_vars());
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l = cls->get_literal(i);
expr * atom = l.atom();
if (!l.sign() && m_manager.is_eq(atom)) {
expr * lhs = to_app(atom)->get_arg(0);
expr * rhs = to_app(atom)->get_arg(1);
clause * subsumer = m_asserted_literals.subsumes(lhs, rhs);
if (subsumer) {
TRACE("eq_subsumption", tout << "equality subsumption:\n"; cls->display(tout, m_manager);
tout << "\nis subsumed by:\n"; subsumer->display(tout, m_manager); tout << "\n";);
return subsumer;
}
}
}
return 0;
}
/**
\brief Return a clause C in the index (i.e., insert(C) was invoked) that subsumes cls.
Return 0 if such clause does not exist.
*/
clause * subsumption::forward(clause * cls) {
TRACE("subsumption", tout << "trying forward subsumption:\n"; cls->display(tout, m_manager); tout << "\n";);
clause * subsumer = unit_forward(cls);
if (subsumer)
return subsumer;
subsumer = non_unit_forward(cls);
if (subsumer)
return subsumer;
if (m_params.m_equality_subsumption)
return eq_subsumption(cls);
return 0;
}
struct backward_subsumption_visitor {
subsumption & m_owner;
clause * m_new_clause;
ptr_buffer<clause> & m_result;
backward_subsumption_visitor(subsumption & owner, clause * new_clause, ptr_buffer<clause> & result):
m_owner(owner),
m_new_clause(new_clause),
m_result(result) {
}
bool operator()(clause * candidate) {
if (m_owner(m_new_clause, candidate))
m_result.push_back(candidate);
return true; // always continue in backward subsumption
}
};
/**
\brief Store in result the set of clauses in the index that are subsumes by cls.
*/
void subsumption::backward(clause * cls, ptr_buffer<clause> & result) {
if (m_params.m_backward_subsumption) {
backward_subsumption_visitor visitor(*this, cls, result);
m_index->visit(cls, visitor, false);
}
}
};

156
src/spc/spc_subsumption.h Normal file
View file

@ -0,0 +1,156 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_subsumption.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-13.
Revision History:
--*/
#ifndef _SPC_SUBSUMPTION_H_
#define _SPC_SUBSUMPTION_H_
#include"spc_asserted_literals.h"
#include"matcher.h"
#include"fvi.h"
#include"spc_params.h"
#include"obj_hashtable.h"
namespace spc {
class subsumption {
ast_manager & m_manager;
spc_params & m_params;
asserted_literals & m_asserted_literals;
substitution m_subst;
matcher m_matcher;
// A pair representing the association between l1 and l2 where
// first is the position of l1 in lits1 and second the position of l2 in
// lits2.
typedef std::pair<unsigned, unsigned> assoc;
typedef vector<assoc> stack;
stack m_stack;
template<bool ResetSubst>
bool match_literal(literal const & l1, literal const & l2);
bool can_subsume(unsigned num_lits1, literal * lits1, unsigned num_lits2, literal * lits2);
bool quick_check(clause * cls1, clause * cls2);
bool subsumes_core(unsigned num_lits1, literal * lits1, unsigned num_lits2, literal * lits2);
bool subsumes_core(literal const & l1, unsigned num_lits2, literal * lits2);
bool ground_subsumes_core(unsigned num_lits1, literal * lits1, unsigned num_lits2, literal * lits2);
enum feature_kind {
F_GROUND,
F_NUM_POS_LITS,
F_NUM_NEG_LITS,
F_DEPTH,
F_CONST_COUNT,
F_SYM_COUNT,
F_NUM_NEG_FUNCS,
F_NUM_POS_FUNCS
};
struct feature {
feature_kind m_kind;
func_decl * m_decl;
feature(feature_kind k = F_GROUND):m_kind(k) {}
feature(func_decl * decl, bool neg):m_kind(neg ? F_NUM_NEG_FUNCS : F_NUM_POS_FUNCS), m_decl(decl) {}
};
vector<feature> m_features;
bit_vector m_found_decl_set;
func_decl_ref_vector m_found_decls; // domain of m_found_decl_set;
/**
\brief Return true if the function symbol is considered for feature vector indexing.
*/
bool fvi_candidate(func_decl * f) {
return f->get_family_id() == null_family_id || f->get_arity() > 0;
}
typedef obj_hashtable<func_decl> found_func_decl_set;
found_func_decl_set m_cls_found_decl_set; // temporary set used to track the func_decl's found in a clause
ptr_vector<func_decl> m_cls_found_decls;
bool mark_func_decl(func_decl * f);
typedef obj_map<func_decl, unsigned> decl2nat;
bool m_refining_index; // if true keep collecting data to refine index.
decl2nat m_f_count[2]; // temporary field used to track the num. of occurs. of function symbols in neg/pos literals.
decl2nat m_f_min[2];
decl2nat m_f_max[2];
decl2nat m_f_freq;
void inc_f_count(func_decl * f, bool neg);
void update_min_max(func_decl * f);
void update_neg_pos_func_counts(clause * cls);
void compute_features(clause * cls, unsigned * fvector);
struct to_feature_vector;
friend struct to_feature_vector;
struct to_feature_vector {
subsumption & m_owner;
to_feature_vector(subsumption & o):m_owner(o) {}
void operator()(clause * cls, unsigned * fvector) {
m_owner.compute_features(cls, fvector);
}
};
typedef fvi<clause, to_feature_vector, obj_ptr_hash<clause>, ptr_eq<clause> > index;
index * m_index;
unsigned m_num_processed_clauses;
unsigned m_opt_threshold;
void init_indexes();
struct f_lt;
friend struct f_lt;
unsigned get_value_range(func_decl * f, bool neg) const;
unsigned get_value_range(func_decl * f) const;
struct f_lt {
subsumption & m_owner;
f_lt(subsumption & o):m_owner(o) {}
bool operator()(func_decl * f1, func_decl * f2) const;
};
void optimize_feature_index();
clause * unit_forward(clause * cls);
clause * non_unit_forward(clause * cls);
clause * eq_subsumption(expr * lhs, expr * rhs);
clause * eq_subsumption(clause * cls);
public:
subsumption(ast_manager & m, asserted_literals & al, spc_params & params);
~subsumption();
bool operator()(clause * cls1, clause * cls2);
void insert(clause * cls);
void erase(clause * cls);
void reset();
clause * forward(clause * cls);
void backward(clause * cls, ptr_buffer<clause> & result);
ast_manager & get_manager() { return m_manager; }
};
};
#endif /* _SPC_SUBSUMPTION_H_ */

View file

@ -0,0 +1,531 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_superposition.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-15.
Revision History:
--*/
#include"spc_superposition.h"
#include"ast_pp.h"
namespace spc {
superposition::superposition(ast_manager & m, order & o, statistics & s):
m_manager(m),
m_order(o),
m_stats(s),
m_subst(m),
m_p(m),
m_r(m),
m_normalize_vars(m),
m_spc_fid(m.get_family_id("spc")) {
m_subst.reserve_offsets(3);
m_deltas[0] = 0;
m_deltas[1] = 0;
}
superposition::~superposition() {
}
void superposition::insert_p(clause * cls, expr * lhs, unsigned i) {
m_p.insert(lhs);
m_subst.reserve_vars(m_p.get_approx_num_regs());
m_p2clause_set.insert(clause_pos_pair(cls, i), lhs);
}
void superposition::insert_p(clause * cls, literal & l, unsigned i) {
l.set_p_indexed(true);
expr * atom = l.atom();
if (!m_manager.is_eq(atom))
return;
if (l.is_oriented())
insert_p(cls, l.is_left() ? l.lhs() : l.rhs(), i);
else {
insert_p(cls, l.lhs(), i);
insert_p(cls, l.rhs(), i);
}
}
void superposition::insert_r(clause * cls, expr * n, unsigned i, bool lhs) {
if (is_app(n)) {
unsigned idx = (i << 1) | static_cast<unsigned>(lhs);
clause_pos_pair new_pair(cls, idx);
SASSERT(m_todo.empty());
m_todo.push_back(to_app(n));
while (!m_todo.empty()) {
app * n = m_todo.back();
m_todo.pop_back();
clause_pos_set * s = m_r2clause_set.get_parents(n);
if (s == 0 || !s->contains(new_pair)) {
m_r.insert(n);
m_r2clause_set.insert(new_pair, n);
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * c = n->get_arg(i);
if (is_app(c))
m_todo.push_back(to_app(c));
}
}
}
}
}
void superposition::insert_r(clause * cls, literal & l, unsigned i) {
l.set_r_indexed(true);
expr * atom = l.atom();
if (m_manager.is_eq(atom)) {
expr * lhs = l.lhs();
expr * rhs = l.rhs();
if (l.is_oriented()) {
bool left = true;
if (!l.is_left()) {
left = false;
std::swap(lhs, rhs);
}
insert_r(cls, lhs, i, left);
}
else {
insert_r(cls, lhs, i, true);
insert_r(cls, rhs, i, false);
}
}
else {
insert_r(cls, atom, i, false);
}
m_subst.reserve_vars(m_r.get_approx_num_regs());
}
void superposition::insert(clause * cls) {
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal & l = cls->get_literal(i);
if (l.is_p_indexed() || cls->is_eligible_for_paramodulation(m_order, l)) {
if (!l.sign() && m_manager.is_eq(l.atom()))
insert_p(cls, l, i);
insert_r(cls, l, i);
}
else if (l.is_r_indexed() || cls->is_eligible_for_resolution(m_order, l)) {
insert_r(cls, l, i);
}
}
TRACE("superposition_detail",
tout << "adding clause: "; cls->display(tout, m_manager); tout << "\n";
tout << "p index:\n";
m_p.display(tout);
tout << "r index:\n";
m_r.display(tout););
}
void superposition::erase_p(clause * cls, expr * lhs, unsigned i) {
m_p2clause_set.erase(clause_pos_pair(cls, i), lhs);
if (m_p2clause_set.empty(lhs))
m_p.erase(lhs);
}
void superposition::erase_p(clause * cls, literal & l, unsigned i) {
expr * atom = l.atom();
if (!m_manager.is_eq(atom))
return;
if (l.is_oriented())
erase_p(cls, l.is_left() ? l.lhs() : l.rhs(), i);
else {
erase_p(cls, l.lhs(), i);
erase_p(cls, l.rhs(), i);
}
}
void superposition::erase_r(clause * cls, literal & l, unsigned i) {
clause_pos_pair pair(cls, i);
expr * atom = l.atom();
SASSERT(is_app(atom));
SASSERT(m_todo.empty());
m_todo.push_back(to_app(atom));
while (!m_todo.empty()) {
app * n = m_todo.back();
m_todo.pop_back();
switch (m_r2clause_set.erase(pair, n)) {
case 0: // pair is not a parent of n
break;
case 1: // pair is the last parent of n
m_r.erase(n);
default:
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * c = n->get_arg(i);
if (is_app(c))
m_todo.push_back(to_app(c));
}
}
}
}
void superposition::erase(clause * cls) {
unsigned num_lits = cls->get_num_literals();
for (unsigned i = 0; i < num_lits; i++) {
literal & l = cls->get_literal(i);
if (l.is_p_indexed())
erase_p(cls, l, i);
if (l.is_r_indexed())
erase_r(cls, l, i);
}
}
void superposition::reset() {
m_p.reset();
m_p2clause_set.reset();
m_r.reset();
m_r2clause_set.reset();
}
/**
\brief Copy to result the literals of s except literal at position idx. Apply the substitution m_subst,
assuming that the variables of s are in the variable bank offset. The deltas for each bank are
stored in m_deltas.
*/
void superposition::copy_literals(clause * s, unsigned idx, unsigned offset, literal_buffer & result) {
unsigned num_lits = s->get_num_literals();
for (unsigned i = 0; i < num_lits; i++)
if (i != idx) {
literal const & l = s->get_literal(i);
expr_ref new_atom(m_manager);
m_subst.apply(2, m_deltas, expr_offset(l.atom(), offset), new_atom);
TRACE("superposition_copy", tout << "i: " << i << ", idx: " << idx << ", offset: " << offset << "\natom:\n";
tout << mk_pp(l.atom(), m_manager) << "\nnew_atom:\n" << mk_pp(new_atom, m_manager) << "\n";);
result.push_back(literal(new_atom, l.sign()));
}
}
void superposition::normalize_literals(unsigned num_lits, literal * lits, literal_buffer & result) {
m_normalize_vars.reset();
for (unsigned i = 0; i < num_lits; i++) {
literal const & l = lits[i];
result.push_back(literal(m_normalize_vars(l.atom()), l.sign()));
}
}
void superposition::mk_sp_clause(unsigned num_lits, literal * lits, justification * p1, justification * p2) {
literal_buffer new_literals(m_manager);
normalize_literals(num_lits, lits, new_literals);
justification * js = mk_superposition_justification(m_manager, m_spc_fid, p1, p2,
new_literals.size(), new_literals.c_ptr(),
m_normalize_vars.get_num_vars(), m_normalize_vars.get_vars());
clause * new_cls = clause::mk(m_manager, new_literals.size(), new_literals.c_ptr(), js, 0);
m_new_clauses->push_back(new_cls);
TRACE("superposition", tout << "new superposition clause:\n"; new_cls->display(tout, m_manager); tout << "\n";);
m_stats.m_num_superposition++;
}
void superposition::mk_res_clause(unsigned num_lits, literal * lits, justification * p1, justification * p2) {
literal_buffer new_literals(m_manager);
normalize_literals(num_lits, lits, new_literals);
justification * js = mk_resolution_justification(m_manager, m_spc_fid, p1, p2,
new_literals.size(), new_literals.c_ptr(),
m_normalize_vars.get_num_vars(), m_normalize_vars.get_vars());
clause * new_cls = clause::mk(m_manager, new_literals.size(), new_literals.c_ptr(), js, 0);
m_new_clauses->push_back(new_cls);
TRACE("superposition", tout << "new resolution clause:\n"; new_cls->display(tout, m_manager); tout << "\n";);
m_stats.m_num_resolution++;
}
/**
\brief Given the equation (= lhs rhs) of the clause being
added, try to apply resolution where the clause being added
is the main clause in the superposition rule.
*/
void superposition::try_superposition_main(expr * lhs, expr * rhs) {
m_lhs = lhs;
m_rhs = rhs;
m_subst.reset_subst();
TRACE("spc_superposition", tout << "try_superposition_main, lhs:\n" << mk_pp(m_lhs, m_manager) << "\nrhs:\n" << mk_pp(m_rhs, m_manager) << "\n";
tout << "substitution:\n"; m_subst.display(tout););
r_visitor v(*this, m_subst);
m_r.unify(lhs, v);
}
/**
\brief Try to apply superposition rule using the clause
being added (m_clause) as main clause, and its literal m_lit
as the equation.
*/
void superposition::try_superposition_main() {
expr * lhs = m_lit->lhs();
expr * rhs = m_lit->rhs();
TRACE("spc_superposition", tout << "trying superposition:\n" << mk_pp(lhs, m_manager) << "\n" << mk_pp(rhs, m_manager) << "\nis_oriented: " << m_lit->is_oriented() << "\n";);
if (m_lit->is_oriented()) {
if (!m_lit->is_left())
std::swap(lhs, rhs);
try_superposition_main(lhs, rhs);
}
else {
try_superposition_main(lhs, rhs);
try_superposition_main(rhs, lhs);
}
}
void superposition::found_r(expr * r) {
TRACE("spc_superposition", tout << "found_r:\n" << mk_pp(r, m_manager) << "\n";
tout << "substitution:\n"; m_subst.display(tout););
if (m_r2clause_set.empty(r))
return;
TRACE("spc_superposition", tout << "r2clause is not empty.\n";);
if (!m_lit->is_oriented() && m_order.greater(m_rhs, m_lhs, &m_subst))
return;
TRACE("spc_superposition", tout << "order restriction was met.\n";);
if (!m_clause->is_eligible_for_paramodulation(m_order, *m_lit, 0, &m_subst))
return;
TRACE("spc_superposition", tout << "literal is eligible for paramodulation.\n";);
r2clause_set::iterator it = m_r2clause_set.begin(r);
r2clause_set::iterator end = m_r2clause_set.end(r);
for (; it != end; ++it) {
clause_pos_pair & p = *it;
clause * aux_cls = p.first;
unsigned aux_idx = p.second >> 1;
//
// The following optimization is incorrect (if statement).
// For example, it prevents the Z3 from proving the trivial benchmark
// c = X,
// a != b
// using the order a < b < c
//
// To prove, this example we need to generate the clause Y = X by applying superposition of c = X on itself.
// We can see that by renaming the first clause to c = Y, and then, substituting c in the original by Y.
//
// Actually, this optimization is correct when the set of variables in m_lhs is a superset of the set of variables in m_rhs,
// because in this case, the new literal will be equivalent to true. In the example above, this is not the case,
// since m_lhs does not contain any variable, and m_rhs contains one.
//
//
// if (r == m_lhs && m_clause == aux_cls && m_idx == aux_idx)
// continue;
//
bool in_lhs = (p.second & 1) != 0;
TRACE("spc_superposition", tout << "aux_cls:\n"; aux_cls->display(tout, m_manager); tout << "\naux_idx: " << aux_cls << ", in_lhs: " << in_lhs << "\n";);
literal & aux_lit = aux_cls->get_literal(aux_idx);
if (!aux_cls->is_eligible_for_resolution(m_order, aux_lit, 1, &m_subst))
continue;
literal_buffer new_literals(m_manager);
m_subst.reset_cache();
if (m_manager.is_eq(aux_lit.atom())) {
expr * lhs = aux_lit.lhs();
expr * rhs = aux_lit.rhs();
TRACE("spc_superposition", tout << "aux_lit lhs:\n" << mk_pp(lhs, m_manager) << "\nrhs:\n" << mk_pp(rhs, m_manager) << "\n";);
if (!in_lhs)
std::swap(lhs, rhs);
if (!aux_lit.is_oriented() && m_order.greater(rhs, lhs, 1, &m_subst)) {
TRACE("spc_superposition", tout << "failed order constraint.\n";);
continue;
}
expr_ref new_lhs(m_manager), new_rhs(m_manager);
m_subst.apply(2, m_deltas, expr_offset(lhs, 1), expr_offset(r, 1), expr_offset(m_rhs, 0), new_lhs);
m_subst.apply(2, m_deltas, expr_offset(rhs, 1), new_rhs);
TRACE("spc_superposition", tout << "aux_lit new_lhs:\n" << mk_pp(new_lhs, m_manager) << "\nnew_rhs:\n" << mk_pp(new_rhs, m_manager) << "\n";);
expr * new_eq = m_manager.mk_eq(new_lhs, new_rhs);
new_literals.push_back(literal(new_eq, aux_lit.sign()));
}
else {
expr_ref new_atom(m_manager);
m_subst.apply(2, m_deltas, expr_offset(aux_lit.atom(), 1), new_atom);
new_literals.push_back(literal(new_atom, aux_lit.sign()));
}
copy_literals(m_clause, m_idx, 0, new_literals);
copy_literals(aux_cls, aux_idx, 1, new_literals);
TRACE("superposition", tout << "found r target: " << mk_pp(r, m_manager) << " for \n" <<
mk_pp(m_lhs, m_manager) << "\nmain clause: "; m_clause->display(tout, m_manager);
tout << "\naux clause: "; aux_cls->display(tout, m_manager); tout << "\nat pos: " <<
aux_idx << "\n";);
mk_sp_clause(new_literals.size(), new_literals.c_ptr(), m_clause->get_justification(), aux_cls->get_justification());
}
}
/**
\brief Try to apply superposition rule using the clause
being added (m_clause) as the aux clause, and its literal m_lit
as the target.
*/
void superposition::try_superposition_aux() {
TRACE("superposition_aux", tout << "superposition aux:\n"; m_clause->display(tout, m_manager);
tout << "\nusing literal: " << m_idx << "\n";);
if (m_manager.is_eq(m_lit->atom())) {
expr * lhs = m_lit->lhs();
expr * rhs = m_lit->rhs();
if (m_lit->is_oriented()) {
if (!m_lit->is_left())
std::swap(lhs, rhs);
try_superposition_aux(lhs, rhs);
}
else {
try_superposition_aux(lhs, rhs);
try_superposition_aux(rhs, lhs);
}
}
else {
try_superposition_aux(m_lit->atom(), 0);
}
}
/**
\brief Use the clause being added as the auxiliary clause in the superposition rule.
*/
void superposition::try_superposition_aux(expr * lhs, expr * rhs) {
TRACE("superposition_aux", tout << "try_superposition_aux\n" << mk_pp(lhs, m_manager) << "\n" << mk_pp(rhs, m_manager) << "\n";);
if (is_var(lhs))
return;
m_lhs = lhs;
m_rhs = rhs;
SASSERT(m_todo.empty());
m_todo.push_back(to_app(lhs));
while (!m_todo.empty()) {
m_target = m_todo.back();
m_todo.pop_back();
m_subst.reset_subst();
p_visitor v(*this, m_subst);
TRACE("superposition_aux", tout << "trying to find unifier for:\n" << mk_pp(m_target, m_manager) << "\n";);
m_p.unify(m_target, v);
unsigned j = m_target->get_num_args();
while (j > 0) {
--j;
expr * arg = m_target->get_arg(j);
if (is_app(arg))
m_todo.push_back(to_app(arg));
}
}
}
void superposition::found_p(expr * p) {
TRACE("superposition_found_p", tout << "found p:\n" << mk_pp(p, m_manager) << "\n";);
if (m_p2clause_set.empty(p)) {
TRACE("superposition_found_p", tout << "clause set is empty.\n";);
return;
}
if (m_rhs && !m_lit->is_oriented() && m_order.greater(m_rhs, m_lhs, &m_subst)) {
TRACE("superposition_found_p", tout << "aux clause failed not rhs > lhs constraint.\n";);
return;
}
if (!m_clause->is_eligible_for_resolution(m_order, *m_lit, 0, &m_subst)) {
TRACE("superposition_found_p", tout << "aux literal is not eligible for resolution.\n";);
return;
}
p2clause_set::iterator it = m_p2clause_set.begin(p);
p2clause_set::iterator end = m_p2clause_set.end(p);
for (; it != end; ++it) {
clause_pos_pair & pair = *it;
clause * main_cls = pair.first;
TRACE("superposition_found_p", tout << "p clause:\n"; main_cls->display(tout, m_manager); tout << "\n";);
unsigned lit_idx = pair.second;
if (p == m_lhs && m_clause == main_cls && m_idx == lit_idx)
continue;
literal const & main_lit = main_cls->get_literal(lit_idx);
SASSERT(m_manager.is_eq(main_lit.atom()));
expr * lhs = main_lit.lhs();
expr * rhs = main_lit.rhs();
if (rhs == p)
std::swap(lhs, rhs);
SASSERT(lhs == p);
TRACE("superposition_found_p", tout << "lhs: " << mk_pp(lhs, m_manager) << "\nrhs: " << mk_pp(rhs, m_manager) << "\n";);
if (!main_lit.is_oriented() && m_order.greater(rhs, lhs, 1, &m_subst))
continue;
if (!main_cls->is_eligible_for_paramodulation(m_order, main_lit, 1, &m_subst))
continue;
literal_buffer new_literals(m_manager);
m_subst.reset_cache();
TRACE("superposition_found_p", tout << "creating new_lhs\n";);
expr_ref new_lhs(m_manager);
m_subst.apply(2, m_deltas, expr_offset(m_lhs, 0), expr_offset(m_target, 0), expr_offset(rhs, 1), new_lhs);
// FIX: m_subst.reset_cache();
TRACE("superposition_found_p", tout << "new_lhs: " << mk_pp(new_lhs, m_manager) << "\n";
m_subst.display(tout););
expr * new_atom = 0;
if (m_rhs) {
TRACE("superposition_found_p", tout << "creating new_rhs\n";);
expr_ref new_rhs(m_manager);
m_subst.apply(2, m_deltas, expr_offset(m_rhs, 0), new_rhs);
TRACE("superposition_found_p", tout << "new_rhs: " << mk_pp(new_rhs, m_manager) << "\n";);
new_atom = m_manager.mk_eq(new_lhs, new_rhs);
}
else
new_atom = new_lhs;
TRACE("superposition_found_p", tout << "new_atom: " << mk_pp(new_atom, m_manager) << "\n"; m_subst.display(tout););
new_literals.push_back(literal(new_atom, m_lit->sign()));
TRACE("superposition_found_p", tout << "copying literals\n";);
copy_literals(main_cls, lit_idx, 1, new_literals);
copy_literals(m_clause, m_idx, 0, new_literals);
TRACE("superposition", tout << "found p target: " << mk_pp(p, m_manager) << " for \n" <<
mk_pp(m_lhs, m_manager) << "\nmain clause: "; main_cls->display(tout, m_manager);
tout << "\naux clause: "; m_clause->display(tout, m_manager); tout << "\n";);
mk_sp_clause(new_literals.size(), new_literals.c_ptr(), main_cls->get_justification(), m_clause->get_justification());
}
}
/**
\brief Try to apply resolution rule using the clause being added (m_clause).
*/
void superposition::try_resolution() {
m_subst.reset_subst();
res_visitor v(*this, m_subst);
m_r.unify(m_lit->atom(), v);
}
void superposition::found_res(expr * r) {
if (m_r2clause_set.empty(r))
return;
if (!m_clause->is_eligible_for_resolution(m_order, *m_lit, 0, &m_subst))
return;
r2clause_set::iterator it = m_r2clause_set.begin(r);
r2clause_set::iterator end = m_r2clause_set.end(r);
for (; it != end; ++it) {
clause_pos_pair & pair = *it;
clause * aux_cls = pair.first;
unsigned aux_idx = pair.second >> 1;
literal const & aux_lit = aux_cls->get_literal(aux_idx);
if (aux_lit.sign() == m_lit->sign())
continue;
if (aux_lit.atom() != r)
continue;
if (!aux_cls->is_eligible_for_resolution(m_order, aux_lit, 1, &m_subst))
continue;
literal_buffer new_literals(m_manager);
m_subst.reset_cache();
copy_literals(m_clause, m_idx, 0, new_literals);
copy_literals(aux_cls, aux_idx, 1, new_literals);
mk_res_clause(new_literals.size(), new_literals.c_ptr(), m_clause->get_justification(), aux_cls->get_justification());
}
}
void superposition::operator()(clause * cls, ptr_vector<clause> & new_clauses) {
m_subst.reserve_vars(cls->get_num_vars());
m_clause = cls;
m_new_clauses = &new_clauses;
SASSERT(m_deltas[0] == 0);
m_deltas[1] = m_clause->get_num_vars();
unsigned num_lits = cls->get_num_literals();
for (m_idx = 0; m_idx < num_lits; m_idx++) {
m_lit = &(cls->get_literal(m_idx));
bool is_eq = m_manager.is_eq(m_lit->atom());
if (!m_lit->sign() && m_lit->is_p_indexed() && is_eq)
try_superposition_main();
if (m_lit->is_r_indexed()) {
try_superposition_aux();
if (!is_eq)
try_resolution();
}
}
}
};

147
src/spc/spc_superposition.h Normal file
View file

@ -0,0 +1,147 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_superposition.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-15.
Revision History:
--*/
#ifndef _SPC_SUPERPOSITION_H_
#define _SPC_SUPERPOSITION_H_
#include"spc_clause.h"
#include"spc_clause_pos_set.h"
#include"substitution_tree.h"
#include"obj_hashtable.h"
#include"sparse_use_list.h"
#include"normalize_vars.h"
#include"spc_statistics.h"
namespace spc {
/**
\brief Functor for applying the superposition right/left rules.
- Superposition Left
s = t or S, u != v or R
==>
sigma(u[p<-t] != v or S or R)
sigma is the mgu(u|p, s)
sigma(s) not greater than sigma(t)
sigma(u) not greater than sigma(v)
sigma(s = t) is eligible for paramodulation
sigma(u != v) is eligible for resolution
u|p is not a variable
- Superposition Right
s = t or S, u = v or R
==>
sigma(u[p<-t] != v or S or R)
Same restrictions of Superposition Left
This functor also applied binary resolution rule.
We say the left clause is the main clause in the superposition.
*/
class superposition {
ast_manager & m_manager;
order & m_order;
statistics & m_stats;
substitution m_subst;
// indexes for left clause
substitution_tree m_p; // potential left hand sides for superposition
typedef sparse_use_list<expr, svector<clause_pos_pair> > p2clause_set;
p2clause_set m_p2clause_set;
void insert_p(clause * cls, expr * lhs, unsigned i);
void insert_p(clause * cls, literal & l, unsigned i);
void erase_p(clause * cls, expr * lhs, unsigned i);
void erase_p(clause * cls, literal & l, unsigned i);
// indexes for right clause
substitution_tree m_r; // potential targets for superposition
typedef sparse_use_list<expr, clause_pos_set> r2clause_set;
r2clause_set m_r2clause_set;
ptr_vector<app> m_todo;
void insert_r(clause * cls, expr * n, unsigned i, bool lhs);
void insert_r(clause * cls, literal & l, unsigned i);
void erase_r(clause * cls, literal & l, unsigned i);
normalize_vars m_normalize_vars;
// temporary fields...
ptr_vector<clause> * m_new_clauses;
clause * m_clause;
literal * m_lit;
expr * m_lhs;
expr * m_rhs;
app * m_target;
unsigned m_idx;
unsigned m_deltas[2];
family_id m_spc_fid;
void normalize_literals(unsigned num_lits, literal * lits, literal_buffer & result);
void copy_literals(clause * s, unsigned idx, unsigned offset, literal_buffer & result);
void mk_sp_clause(unsigned num_lits, literal * lits, justification * p1, justification * p2);
void mk_res_clause(unsigned num_lits, literal * lits, justification * p1, justification * p2);
void try_superposition_main(expr * lhs, expr * rhs);
void try_superposition_main();
void found_r(expr * r);
void try_superposition_aux(expr * lhs, expr * rhs);
void try_superposition_aux();
void found_p(expr * p);
void try_resolution();
void found_res(expr * r);
friend struct r_visitor;
struct r_visitor : public st_visitor {
superposition & m_owner;
r_visitor(superposition & o, substitution & s):st_visitor(s), m_owner(o) {}
virtual bool operator()(expr * e) { m_owner.found_r(e); return true; /* continue */ }
};
friend struct p_visitor;
struct p_visitor : public st_visitor {
superposition & m_owner;
p_visitor(superposition & o, substitution & s):st_visitor(s), m_owner(o) {}
virtual bool operator()(expr * e) { m_owner.found_p(e); return true; /* continue */ }
};
friend struct res_visitor;
struct res_visitor : public st_visitor {
superposition & m_owner;
res_visitor(superposition & o, substitution & s):st_visitor(s), m_owner(o) {}
virtual bool operator()(expr * e) { m_owner.found_res(e); return true; /* continue */ }
};
public:
superposition(ast_manager & m, order & o, statistics & stats);
~superposition();
void insert(clause * cls);
void erase(clause * cls);
void reset();
void operator()(clause * cls, ptr_vector<clause> & new_clauses);
};
};
#endif /* _SPC_SUPERPOSITION_H_ */

View file

@ -0,0 +1,52 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_unary_inference.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-14.
Revision History:
--*/
#include"spc_unary_inference.h"
namespace spc {
unary_inference::unary_inference(ast_manager & m, order & ord):
m_manager(m),
m_order(ord),
m_subst(m),
m_unifier(m) {
m_subst.reserve_offsets(1);
}
/**
\brief Create the result clause. The literal at position j of \c cls in removed,
and the substitution m_subst is applied to the resultant clause.
*/
clause * unary_inference::mk_result(clause * cls, unsigned j) {
sbuffer<literal> new_literals;
unsigned num = cls->get_num_literals();
for (unsigned i = 0; i < num; i++) {
if (i != j) {
literal const & l = cls->get_literal(i);
expr_ref new_atom(m_manager);
m_subst.apply(l.atom(), new_atom);
new_literals.push_back(literal(new_atom, l.sign()));
}
}
justification * js = mk_justification(cls->get_justification(), new_literals.size(), new_literals.c_ptr());
clause * new_cls = clause::mk(m_manager, new_literals.size(), new_literals.c_ptr(), js, cls->get_scope_lvl());
return new_cls;
}
};

View file

@ -0,0 +1,48 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
spc_unary_inference.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-14.
Revision History:
--*/
#ifndef _SPC_UNARY_INFERENCE_H_
#define _SPC_UNARY_INFERENCE_H_
#include"spc_clause.h"
#include"unifier.h"
namespace spc {
/**
\brief Superclass for eq_resolution and factoring.
*/
class unary_inference {
protected:
ast_manager & m_manager;
order & m_order;
substitution m_subst;
unifier m_unifier;
clause * mk_result(clause * cls, unsigned j);
virtual justification * mk_justification(justification * parent, unsigned num_lits, literal * new_lits) = 0;
public:
unary_inference(ast_manager & m, order & ord);
virtual ~unary_inference() {}
};
};
#endif /* _SPC_UNARY_INFERENCE_H_ */

180
src/spc/splay_tree.h Normal file
View file

@ -0,0 +1,180 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
splay_tree.h
Abstract:
Splay trees
Author:
Leonardo de Moura (leonardo) 2008-01-31.
Revision History:
--*/
#ifndef _SPLAY_TREE_H_
#define _SPLAY_TREE_H_
#include"util.h"
#include"buffer.h"
template<typename Key, typename Compare>
class splay_tree : private Compare {
struct cell {
Key m_key;
cell * m_left;
cell * m_right;
cell():m_left(0), m_right(0) {}
cell(Key const & k, cell * l = 0, cell * r = 0):
m_key(k), m_left(l), m_right(r) {}
};
cell * m_root;
int compare(Key const & k1, Key const & k2) const { return Compare::operator()(k1, k2); }
cell * splay(cell * c, Key const & k);
void display_core(std::ostream & out, cell * c) const {
if (c) {
out << "(" << c->m_key << " ";
display_core(out, c->m_left);
out << " ";
display_core(out, c->m_right);
out << ")";
}
else
out << "null";
}
public:
splay_tree(Compare const & c = Compare()):
Compare(c),
m_root(0) {}
~splay_tree() {
m_root = 0;
}
void insert(Key const & k);
bool find(Key const & k, Key & r) const;
void erase(Key const & k);
void reset();
bool empty() const { return m_root == 0; }
bool singleton() const { return m_root != 0 && m_root->m_left == 0 && m_root->m_right == 0; }
/**
\brief Visit nodes in the splay tree in ascending order.
The Visitor functor should provide the following methods:
- bool visit_left(Key const & k)
return true if the left child should be visited
- bool visit_right(Key const & k)
return true if the right child should be visited
- void operator()(Key const & k)
do something with the key.
*/
template<typename Visitor>
void visit_core(Visitor & v) {
typedef std::pair<cell *, bool> entry;
if (m_root) {
buffer<entry> todo;
todo.push_back(entry(m_root, false));
while (!todo.empty()) {
entry & curr = todo.back();
cell * c = curr.first;
if (!curr.second) {
curr.second = true;
if (c->m_left && v.visit_left(c->m_key))
todo.push_back(entry(c->m_left, false));
}
else {
v(c->m_key);
todo.pop_back();
if (c->m_right && v.visit_right(c->m_key))
todo.push_back(entry(c->m_right, false));
}
}
}
}
template<typename Visitor>
struct all_visitor_wrapper {
Visitor & m_visitor;
all_visitor_wrapper(Visitor & v):m_visitor(v) {}
bool visit_right(Key const & k) { return true; }
bool visit_left(Key const & k) { return true; }
void operator()(Key const & k) { m_visitor.operator()(k); }
};
/**
\brief Visit all nodes in the splay tree in ascending order.
- void operator()(Key const & k)
do something with the key pair.
*/
template<typename Visitor>
void visit(Visitor & v) {
all_visitor_wrapper<Visitor> w(v);
visit_core(w);
}
template<typename Visitor, bool LE>
struct visitor_wrapper {
Visitor & m_visitor;
splay_tree & m_tree;
Key m_key;
visitor_wrapper(Visitor & v, splay_tree & t, Key const & k):m_visitor(v), m_tree(t), m_key(k) {}
bool visit_left(Key const & k) {
return LE || m_tree.compare(k, m_key) > 0;
}
bool visit_right(Key const & k) {
return !LE || m_tree.compare(k, m_key) < 0;
}
void operator()(Key const & k) {
if ((LE && m_tree.compare(k, m_key) <= 0) ||
(!LE && m_tree.compare(k, m_key) >= 0))
m_visitor.operator()(k);
}
};
/**
\brief Visit all nodes with keys less than or equal to k.
- void operator()(Key const & k)
do something with the key.
*/
template<typename Visitor>
void visit_le(Visitor & v, Key const & k) {
visitor_wrapper<Visitor, true> w(v, *this, k);
visit_core(w);
}
/**
\brief Visit all nodes with keys greater than or equal to k.
- void operator()(Key const & k)
do something with the key.
*/
template<typename Visitor>
void visit_ge(Visitor & v, Key const & k) {
visitor_wrapper<Visitor, false> w(v, *this, k);
visit_core(w);
}
void display(std::ostream & out) const {
display_core(out, m_root);
}
};
#endif

152
src/spc/splay_tree_def.h Normal file
View file

@ -0,0 +1,152 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
splay_tree_def.h
Abstract:
Splay trees
Author:
Leonardo de Moura (leonardo) 2008-01-31.
Revision History:
--*/
#ifndef _SPLAY_TREE_DEF_H_
#define _SPLAY_TREE_DEF_H_
#include"splay_tree.h"
template<typename Key, typename Compare>
typename splay_tree<Key, Compare>::cell * splay_tree<Key, Compare>::splay(cell * root, Key const & k) {
if (!root)
return 0;
cell aux;
cell * tmp;
cell * left = &aux;
cell * right = &aux;
cell * t = root;
while (true) {
int r = compare(k, t->m_key);
if (r < 0) {
if (!t->m_left)
break;
if (compare(k, t->m_left->m_key) < 0) {
tmp = t->m_left;
t->m_left = tmp->m_right;
tmp->m_right = t;
t = tmp;
if (!t->m_left)
break;
}
right->m_left = t;
right = t;
t = t->m_left;
}
else if (r > 0) {
if (!t->m_right)
break;
if (compare(k, t->m_right->m_key) > 0) {
tmp = t->m_right;
t->m_right = tmp->m_left;
tmp->m_left = t;
t = tmp;
if (!t->m_right)
break;
}
left->m_right = t;
left = t;
t = t->m_right;
}
else
break;
}
left->m_right = t->m_left;
right->m_left = t->m_right;
t->m_left = aux.m_right;
t->m_right = aux.m_left;
return t;
}
template<typename Key, typename Compare>
void splay_tree<Key, Compare>::insert(Key const & k) {
if (!m_root)
m_root = alloc(cell, k);
else {
m_root = splay(m_root, k);
int r = compare(k, m_root->m_key);
if (r < 0) {
cell * new_cell = alloc(cell, k, m_root->m_left, m_root);
m_root->m_left = 0;
m_root = new_cell;
}
else if (r > 0) {
cell * new_cell = alloc(cell, k, m_root, m_root->m_right);
m_root->m_right = 0;
m_root = new_cell;
}
else
m_root->m_key = k;
}
}
template<typename Key, typename Compare>
bool splay_tree<Key, Compare>::find(Key const & k, Key & r) const {
if (m_root) {
splay_tree<Key, Compare> * _this = const_cast<splay_tree<Key, Compare> *>(this);
_this->m_root = _this->splay(m_root, k);
if (compare(k, m_root->m_key) == 0) {
r = m_root->m_key;
return true;
}
}
return false;
}
template<typename Key, typename Compare>
void splay_tree<Key, Compare>::erase(Key const & k) {
if (m_root) {
m_root = splay(m_root, k);
if (compare(k, m_root->m_key) == 0) {
cell * to_delete = m_root;
if (m_root->m_left) {
cell * aux = splay(m_root->m_left, k);
SASSERT(!aux->m_right);
aux->m_right = m_root->m_right;
m_root = aux;
}
else
m_root = m_root->m_right;
dealloc(to_delete);
}
}
}
template<typename Key, typename Compare>
void splay_tree<Key, Compare>::reset() {
ptr_buffer<cell> todo;
if (m_root)
todo.push_back(m_root);
while (!todo.empty()) {
cell * c = todo.back();
todo.pop_back();
if (c->m_left)
todo.push_back(c->m_left);
if (c->m_right)
todo.push_back(c->m_right);
dealloc(c);
}
m_root = 0;
}
#endif /* _SPLAY_TREE_DEF_H_ */

114
src/spc/splay_tree_map.h Normal file
View file

@ -0,0 +1,114 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
splay_tree_map.h
Abstract:
A mapping as a splay tree.
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _SPLAY_TREE_MAP_H_
#define _SPLAY_TREE_MAP_H_
#include"splay_tree.h"
template<typename Key, typename Data, typename Compare>
class splay_tree_map {
typedef std::pair<Key, Data> entry;
struct entry_compare : private Compare {
entry_compare(Compare const & c):Compare(c) {}
int operator()(entry const & e1, entry const & e2) const {
return Compare::operator()(e1.first, e2.first);
}
};
typedef splay_tree<entry, entry_compare> tree;
tree m_tree;
template<typename Visitor>
struct core_visitor_wrapper {
Visitor & m_visitor;
core_visitor_wrapper(Visitor & v):m_visitor(v) {}
bool visit_right(entry const & k) { return m_visitor.visit_right(k.first); }
bool visit_left(entry const & k) { return m_visitor.visit_left(k.first); }
void operator()(entry const & k) { m_visitor.operator()(k.first, k.second); }
};
template<typename Visitor>
struct visitor_wrapper {
Visitor & m_visitor;
visitor_wrapper(Visitor & v):m_visitor(v) {}
void operator()(entry const & k) { m_visitor.operator()(k.first, k.second); }
};
public:
splay_tree_map(Compare const & c = Compare()):
m_tree(entry_compare(c)) {}
void insert(Key const & k, Data const & d) {
m_tree.insert(entry(k, d));
}
bool find(Key const & k, Data & r) const {
entry e(k, r);
if (m_tree.find(e, e)) {
r = e.second;
return true;
}
return false;
}
void erase(Key const & k) {
entry e;
e.first = k;
m_tree.erase(e);
}
void reset() { m_tree.reset(); }
bool empty() const { return m_tree.empty(); }
void display(std::ostream & out) const { m_tree.display(out); }
template<typename Visitor>
void visit_core(Visitor & v) {
core_visitor_wrapper<Visitor> w(v);
m_tree.visit_core(w);
}
template<typename Visitor>
void visit(Visitor & v) {
visitor_wrapper<Visitor> w(v);
m_tree.visit(w);
}
template<typename Visitor>
void visit_le(Visitor & v, Key const & k) {
visitor_wrapper<Visitor> w(v);
entry e;
e.first = k;
m_tree.visit_le(w, e);
}
template<typename Visitor>
void visit_ge(Visitor & v, Key const & k) {
visitor_wrapper<Visitor> w(v);
entry e;
e.first = k;
m_tree.visit_ge(w, e);
}
};
#endif /* _SPLAY_TREE_MAP_H_ */

286
src/spc/substitution.cpp Normal file
View file

@ -0,0 +1,286 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution.cpp
Abstract:
A substitution, that is, a mapping from (variable, offset) to (expr, offset).
We use offsets in order to avoid creating variants of terms.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#include"substitution.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
substitution::substitution(ast_manager & m):
m_manager(m),
m_new_exprs(m) {
}
void substitution::reset() {
reset_subst();
reset_cache();
}
void substitution::reset_subst() {
m_subst.reset();
m_vars.reset();
m_scopes.reset();
}
void substitution::reset_cache() {
TRACE("subst_bug", tout << "substitution::reset_cache\n";
for (unsigned i = 0; i < m_new_exprs.size(); i++) { tout << mk_pp(m_new_exprs.get(i), m_manager) << "\nref_count: " << m_new_exprs.get(i)->get_ref_count() << "\n"; });
m_apply_cache.reset();
m_new_exprs.reset();
}
void substitution::pop_scope(unsigned num_scopes) {
unsigned lvl = m_scopes.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
unsigned old_sz = m_scopes[new_lvl];
unsigned curr_sz = m_vars.size();
SASSERT(old_sz <= curr_sz);
for (unsigned i = old_sz; i < curr_sz; i++) {
var_offset & curr = m_vars[i];
m_subst.erase(curr.first, curr.second);
}
m_vars.shrink(old_sz);
m_scopes.shrink(new_lvl);
}
inline void substitution::apply_visit(expr_offset const & n, bool & visited) {
if (!m_apply_cache.contains(n)) {
m_todo.push_back(n);
visited = false;
}
}
void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n,
expr_offset const & s, expr_offset const & t, expr_ref & result) {
TRACE("subst_bug", tout << "BEGIN substitution::apply\n";);
// It is incorrect to cache results between different calls if we are applying a substitution
// modulo a substitution s -> t.
if (s != expr_offset(0,0))
reset_cache();
unsigned j;
expr * e;
unsigned off;
expr_offset n1;
bool visited;
unsigned num_args;
ptr_buffer<expr> new_args;
m_todo.push_back(n);
while (!m_todo.empty()) {
expr_offset n = m_todo.back();
TRACE("subst_bug", tout << "n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << "\n";);
if (m_apply_cache.contains(n)) {
m_todo.pop_back();
continue;
}
expr_offset n_prime = n == s ? t : n;
TRACE("subst_bug", tout << "n_prime: " << mk_pp(n_prime.get_expr(), m_manager) << " : " << n_prime.get_offset() << "\n";);
visited = true;
e = n_prime.get_expr();
off = n_prime.get_offset();
switch (e->get_kind()) {
case AST_VAR:
if (find(to_var(e)->get_idx(), off, n1)) {
apply_visit(n1, visited);
TRACE("subst_bug", tout << "visited: " << visited << ", n1: " << mk_pp(n1.get_expr(), m_manager) << " : " << n1.get_offset() << "\n";);
if (visited) {
m_todo.pop_back();
expr * new_expr;
m_apply_cache.find(n1, new_expr);
m_apply_cache.insert(n, new_expr);
TRACE("subst_bug", tout << "1. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(new_expr, m_manager) << "\n";);
}
}
else {
m_todo.pop_back();
SASSERT(off < num_actual_offsets);
unsigned delta = deltas[off];
expr * new_expr = e;
if (delta > 0) {
new_expr = m_manager.mk_var(to_var(e)->get_idx() + delta, to_var(e)->get_sort());
m_new_exprs.push_back(new_expr);
}
m_apply_cache.insert(n, new_expr);
TRACE("subst_bug", tout << "2. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(new_expr, m_manager) << "\n";);
}
break;
case AST_APP:
num_args = to_app(e)->get_num_args();
j = num_args;
while (j > 0) {
--j;
apply_visit(expr_offset(to_app(e)->get_arg(j), off), visited);
}
if (visited) {
m_todo.pop_back();
new_args.reset();
bool has_new_args = false;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(e)->get_arg(i);
expr * new_arg;
m_apply_cache.find(expr_offset(arg, off), new_arg);
new_args.push_back(new_arg);
if (arg != new_arg)
has_new_args = true;
}
if (!has_new_args) {
m_apply_cache.insert(n, e);
TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(e, m_manager) << "\n";);
}
else {
expr * new_expr = m_manager.mk_app(to_app(e)->get_decl(), new_args.size(), new_args.c_ptr());
m_new_exprs.push_back(new_expr);
m_apply_cache.insert(n, new_expr);
TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(new_expr, m_manager) << "\n";);
}
}
break;
default:
UNREACHABLE();
}
}
SASSERT(m_apply_cache.contains(n));
m_apply_cache.find(n, e);
m_new_exprs.push_back(e);
result = e;
if (s != expr_offset(0,0))
reset_cache();
TRACE("subst_bug", tout << "END substitution::apply\nresult:\n" << mk_pp(e, m_manager) << "\nref_count: " << e->get_ref_count() << "\n";);
}
inline substitution::color substitution::get_color(expr_offset const & p) const {
color c;
if (m_color.find(p, c))
return c;
return White;
}
inline void substitution::set_color(expr_offset const & p, color c) {
m_color.insert(p, c);
}
inline void substitution::visit(expr_offset const & p, bool & visited) {
if (get_color(p) != Black) {
m_todo.push_back(p);
visited = false;
}
}
bool substitution::visit_children(expr_offset const & p) {
bool visited = true;
expr * n = p.get_expr();
unsigned off;
expr_offset p1;
unsigned j;
switch (n->get_kind()) {
case AST_VAR:
if (find(p, p1) && p != p1)
visit(p1, visited);
break;
case AST_APP:
off = p.get_offset();
j = to_app(n)->get_num_args();
while (j > 0) {
--j;
visit(expr_offset(to_app(n)->get_arg(j), off), visited);
}
break;
default:
UNREACHABLE();
}
return visited;
}
bool substitution::acyclic(expr_offset p) {
if (get_color(p) == Black)
return true;
m_todo.reset();
m_todo.push_back(p);
while (!m_todo.empty()) {
expr_offset p = m_todo.back();
switch (get_color(p)) {
case Black:
m_todo.pop_back();
break;
case White:
set_color(p, Grey);
if (visit_children(p)) {
set_color(p, Black);
SASSERT(m_todo.back() == p);
m_todo.pop_back();
}
break;
case Grey:
if (!visit_children(p))
return false;
set_color(p, Black);
SASSERT(m_todo.back() == p);
m_todo.pop_back();
break;
}
}
return true;
}
bool substitution::acyclic() {
m_color.reset();
expr_offset r;
svector<var_offset>::iterator it = m_vars.begin();
svector<var_offset>::iterator end = m_vars.end();
for (; it != end; ++it) {
m_subst.find(it->first, it->second, r);
if (!acyclic(r))
return false;
}
return true;
}
void substitution::display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas) {
reset_cache();
for (unsigned i = 0; i < num_actual_offsets; i++)
for (unsigned j = 0; j < m_subst.vars_capacity(); j++) {
expr_offset r;
if (find(j, i, r)) {
expr_ref tmp(m_manager);
apply(num_actual_offsets, deltas, r, tmp);
out << "VAR " << j << ":" << i << " -->\n" << mk_pp(tmp, m_manager) << "\n";
}
}
}
void substitution::display(std::ostream & out) {
for (unsigned i = 0; i < m_subst.offsets_capacity(); i++)
for (unsigned j = 0; j < m_subst.vars_capacity(); j++) {
expr_offset r;
if (find(j, i, r))
out << "VAR " << j << ":" << i << " --> " << r.get_offset() << "\n" << mk_pp(r.get_expr(), m_manager) << "\n";
}
}

202
src/spc/substitution.h Normal file
View file

@ -0,0 +1,202 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution.h
Abstract:
A substitution, that is, a mapping from (variable, offset) to (expr, offset).
We use offsets in order to avoid creating variants of terms.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _SUBSTITUTION_H_
#define _SUBSTITUTION_H_
#include"expr_offset_map.h"
#include"var_offset_map.h"
#include"ast_pp.h"
/**
\brief A mapping from (variable,offset) to expr_offset.
*/
class substitution {
ast_manager & m_manager;
var_offset_map<expr_offset> m_subst;
// field for backtracking
typedef std::pair<unsigned, unsigned> var_offset;
svector<var_offset> m_vars;
unsigned_vector m_scopes;
// fields for applying substitutions
svector<expr_offset> m_todo;
expr_offset_map<expr *> m_apply_cache;
expr_ref_vector m_new_exprs;
// fields for checking for cycles
enum color { White, Grey, Black };
expr_offset_map<color> m_color;
#ifdef Z3DEBUG
unsigned m_max_offset_since_reset;
#endif
void apply_visit(expr_offset const & n, bool & visited);
color get_color(expr_offset const & p) const;
void set_color(expr_offset const & p, color c);
void visit(expr_offset const & p, bool & visited);
bool visit_children(expr_offset const & p);
bool acyclic(expr_offset p);
public:
substitution(ast_manager & m);
ast_manager & get_manager() const { return m_manager; }
// -----------------------------------
//
// Reserve memory for the given number of
// offsets and variables.
//
// -----------------------------------
void reserve(unsigned num_offsets, unsigned num_vars) { m_subst.reserve(num_offsets, num_vars); }
void reserve_offsets(unsigned num_offsets) { m_subst.reserve_offsets(num_offsets); }
void reserve_vars(unsigned num_vars) { m_subst.reserve_vars(num_vars); }
// -----------------------------------
//
// Reset functions
//
// -----------------------------------
// reset everything
void reset();
// reset only the mapping from variables to expressions
void reset_subst();
// reset only the substitution application cache
void reset_cache();
// -----------------------------------
//
// Backtracking
//
// -----------------------------------
void push_scope() { m_scopes.push_back(m_vars.size()); }
void pop_scope(unsigned num_scopes = 1);
unsigned get_scope_lvl() { return m_scopes.size(); }
bool top_scope_has_bindings() const { return m_scopes.empty() ? !m_vars.empty() : m_scopes.back() < m_vars.size(); }
unsigned get_num_bindings() const { return m_vars.size(); }
// -----------------------------------
//
// Cycle detection
//
// -----------------------------------
bool acyclic();
// -----------------------------------
//
// Insertion & Lookup
//
// get_binding supplies a way to inspect the substitution.
//
// -----------------------------------
void insert(unsigned v_idx, unsigned offset, expr_offset const & t) {
TRACE("subst_insert", tout << "inserting: #" << v_idx << ":" << offset << " --> " << mk_pp(t.get_expr(), m_manager)
<< ":" << t.get_offset() << "\n";);
m_vars.push_back(var_offset(v_idx, offset));
m_subst.insert(v_idx, offset, t);
}
void insert(var * v, unsigned offset, expr_offset const & t) { insert(v->get_idx(), offset, t); }
void insert(expr_offset v, expr_offset const & t) {
SASSERT(is_var(v.get_expr()));
insert(to_var(v.get_expr()), v.get_offset(), t);
}
bool find(unsigned v_idx, unsigned offset, expr_offset & r) const { return m_subst.find(v_idx, offset, r); }
bool find(var * v, unsigned offset, expr_offset & r) const { return find(v->get_idx(), offset, r); }
bool find(expr_offset v, expr_offset & r) const {
SASSERT(is_var(v.get_expr()));
return find(to_var(v.get_expr()), v.get_offset(), r);
}
void get_binding(unsigned binding_num, var_offset& var, expr_offset& r) {
var = m_vars[binding_num];
VERIFY(m_subst.find(var.first, var.second, r));
}
bool contains(var * v, unsigned offset) { expr_offset r; return find(v, offset, r); }
// -----------------------------------
//
// Application
//
// -----------------------------------
/**
\brief Apply the current substitution to the given
expression+offset. The result is an expression.
The argument num_actual_offsets is the maximum offset used in a
insert method since the last reset.
The argument deltas is an array of size num_actual_offsets. It contains
the variable delta for each offset. A free variable x:i in an expression offset t:j is mapped
to the variable x+delta[i].
*/
void apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n, expr_ref & result) {
apply(num_actual_offsets, deltas, n, expr_offset(0, 0), expr_offset(0, 0), result);
}
/**
\brief Similar to the previous method, but occurrences of s in n are substituted by t.
If s != expr_offset(0,0), then the cache is reset before and after the execution of this procedure.
*/
void apply(unsigned num_actual_offsets, unsigned const* deltas, expr_offset const & n, expr_offset const & s, expr_offset const & t, expr_ref & result);
void apply(expr * n, expr_ref & result) {
unsigned deltas[1] = { 0 };
apply(1, deltas, expr_offset(n, 0), result);
}
// -----------------------------------
//
// Debugging
//
// -----------------------------------
/**
\brief Dump the current substitution (for debugging purposes).
*/
void display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas);
/**
\brief Dump the current substitution without normalizing expressions (for debugging purposes).
*/
void display(std::ostream & out);
// -----------------------------------
//
// Compare terms modulo a substitution
//
// -----------------------------------
bool compare(expr_offset t1, expr_offset t2);
};
#endif

View file

@ -0,0 +1,899 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution_tree.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-04.
Revision History:
--*/
#include"substitution_tree.h"
#include"ast_pp.h"
#include"ast_smt2_pp.h"
/**
\brief Return the next available register.
*/
unsigned substitution_tree::next_reg() {
while(true) {
unsigned curr = m_next_reg;
if (curr > m_max_reg)
m_max_reg = curr;
m_next_reg++;
if (curr >= m_used_regs.size() || !m_used_regs.get(curr))
return curr;
}
}
inline void substitution_tree::push(svector<subst> & sv, subst const & s) {
sv.push_back(s);
m_manager.inc_ref(s.first);
m_manager.inc_ref(s.second);
}
inline expr * substitution_tree::get_reg_value(unsigned ridx) {
return m_registers.get(ridx, 0);
}
inline void substitution_tree::set_reg_value(unsigned ridx, expr * e) {
m_registers.setx(ridx, e, 0);
}
inline void substitution_tree::erase_reg_from_todo(unsigned ridx) {
SASSERT(m_registers[ridx]);
m_registers[ridx] = 0;
SASSERT(m_todo.contains(ridx));
m_todo.erase(ridx);
}
/**
\brief Linearize the expressions in the registers stored in m_todo.
Store the result in \c result.
Example:
m_todo = { 3, 4 }
m_registers[3] = (f (g a))
m_registers[4] = b
next_regs are 5 6 7
result:
#3 -> (f #5); #4 -> b; #5 -> (g #6); #6 -> a
*/
void substitution_tree::linearize(svector<subst> & result) {
ptr_buffer<expr> new_args;
for (unsigned i = 0; i < m_todo.size(); i++) {
unsigned ireg_idx = m_todo[i];
expr * n = get_reg_value(ireg_idx);
var * ireg = m_manager.mk_var(ireg_idx, m_manager.get_sort(n));
if (is_var(n))
push(result, subst(ireg, n));
else {
SASSERT(is_app(n));
app * new_app;
unsigned num = to_app(n)->get_num_args();
if (num == 0)
new_app = to_app(n);
else {
for (unsigned j = 0; j < num; j++) {
unsigned oreg = next_reg();
set_reg_value(oreg, to_app(n)->get_arg(j));
m_todo.push_back(oreg);
sort * s = m_manager.get_sort(get_reg_value(oreg));
new_args.push_back(m_manager.mk_var(oreg, s));
}
new_app = m_manager.mk_app(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr());
new_args.reset();
}
push(result, subst(ireg, new_app));
}
}
}
/**
\brief Process the pair in := (f t_1 ... t_n) and out := (f r_1 ... r_n),
where r_i's are variables (register ids), and t_i's are arbitrary expressions.
The r_i's are added to the m_todo list, and m_registers[r_i] is assigned to t_i.
If save_set_registers == true, then r_i's are stored in m_to_reset.
*/
void substitution_tree::process_args(app * in, app * out) {
CTRACE("subst_tree_bug", in->get_num_args() != out->get_num_args(), tout << mk_ismt2_pp(in, m_manager) << "\n"
<< mk_ismt2_pp(out, m_manager) << "\n";);
unsigned num = out->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * in_arg = in->get_arg(i);
expr * out_arg = out->get_arg(i);
SASSERT(is_var(out_arg));
unsigned oreg = to_var(out_arg)->get_idx();
set_reg_value(oreg, in_arg);
m_todo.push_back(oreg);
}
}
/**
\brief Reset registers in m_todo at [old_size, m_todo.size())
*/
void substitution_tree::reset_registers(unsigned old_size) {
SASSERT(m_todo.size() >= old_size);
unsigned_vector::iterator it2 = m_todo.begin() + old_size;
unsigned_vector::iterator end2 = m_todo.end();
for (; it2 != end2; ++it2)
m_registers[*it2] = 0;
m_todo.shrink(old_size);
}
/**
\brief Return a measure on how compatible sv and the expressions to be processed are.
*/
unsigned substitution_tree::get_compatibility_measure(svector<subst> const & sv) {
unsigned old_size = m_todo.size();
unsigned measure = 0;
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
if (is_var(out)) {
if (out == in)
measure += 1;
}
else {
SASSERT(is_app(out));
if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) {
measure += 2;
process_args(to_app(in), to_app(out));
}
}
}
reset_registers(old_size);
return measure;
}
/**
\brief Find the child of r that is most compatible with the expressions stored
in the registers in m_todo.
Return 0 if none of the children has any compatible substitution entry.
*/
substitution_tree::node * substitution_tree::find_best_child(node * r) {
SASSERT(!r->m_leaf);
#ifdef Z3DEBUG
unsigned todo_size = m_todo.size();
#endif
node * best_child = 0;
unsigned max_measure = 0;
node * curr_child = r->m_first_child;
while (curr_child) {
unsigned measure = get_compatibility_measure(curr_child->m_subst);
if (measure > max_measure) {
best_child = curr_child;
max_measure = measure;
}
curr_child = curr_child->m_next_sibling;
}
SASSERT(todo_size == m_todo.size());
return best_child;
}
/**
\brief Reset datastructures used to insert/erase elements from the substitution tree.
*/
void substitution_tree::reset_compiler() {
m_todo.reset();
m_used_regs.reset();
m_next_reg = 1; // register 0 is reserved for input.
DEBUG_CODE({
ptr_vector<expr>::iterator it = m_registers.begin();
ptr_vector<expr>::iterator end = m_registers.end();
for (; it != end; ++it) {
SASSERT(*it == 0);
}
});
}
/**
\brief Create a node with the linearization for all registers in todo.
Attach new_expr to it.
*/
substitution_tree::node * substitution_tree::mk_node_for(expr * new_expr) {
node * n = alloc(node, true);
linearize(n->m_subst);
n->m_expr = new_expr;
m_manager.inc_ref(new_expr);
return n;
}
/**
\brief Mark register ridx as used.
*/
void substitution_tree::mark_used_reg(unsigned ridx) {
if (ridx >= m_used_regs.size())
m_used_regs.resize(ridx+1);
m_used_regs.set(ridx);
}
/**
\brief Mark (m_used_regs) all registers used in \c sv.
*/
void substitution_tree::mark_used_regs(svector<subst> const & sv) {
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
mark_used_reg(s.first->get_idx());
if (is_app(s.second)) {
unsigned num_args = to_app(s.second)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(s.second)->get_arg(i);
SASSERT(is_var(arg));
mark_used_reg(to_var(arg)->get_idx());
}
}
}
}
/**
\brief Insert a new expression in the substitution tree.
*/
void substitution_tree::insert(expr * new_expr) {
if (is_app(new_expr)) {
insert(to_app(new_expr));
}
else {
SASSERT(is_var(new_expr));
sort * s = to_var(new_expr)->get_sort();
unsigned id = s->get_decl_id();
if (id >= m_vars.size())
m_vars.resize(id+1, 0);
if (m_vars[id] == 0)
m_vars[id] = alloc(var_ref_vector, m_manager);
var_ref_vector * v = m_vars[id];
if (!v->contains(to_var(new_expr)))
v->push_back(to_var(new_expr));
}
}
/**
\brief Insert a new application in the substitution tree.
*/
void substitution_tree::insert(app * new_expr) {
reset_compiler();
set_reg_value(0, new_expr);
m_todo.push_back(0);
func_decl * d = new_expr->get_decl();
unsigned id = d->get_decl_id();
if (id >= m_roots.size())
m_roots.resize(id+1, 0);
if (!m_roots[id]) {
// there is no tree for the function symbol heading new_expr
m_roots[id] = mk_node_for(new_expr);
reset_registers(0);
m_size++;
return;
}
node * r = m_roots[id];
while (true) {
m_compatible.reset();
m_incompatible.reset();
svector<subst> & sv = r->m_subst;
// separate sv in the set of compatible & incompatible instructions
svector<subst>::iterator it = sv.begin();
svector<subst>::iterator end = sv.end();
for (; it != end; ++it) {
subst & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
SASSERT(is_var(out) || is_app(out));
if (is_var(out)) {
if (out == in) {
erase_reg_from_todo(ireg);
m_compatible.push_back(s);
}
else {
m_incompatible.push_back(s);
}
}
else {
if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) {
erase_reg_from_todo(ireg);
m_compatible.push_back(s);
process_args(to_app(in), to_app(out));
}
else {
m_incompatible.push_back(s);
}
}
}
// process m_compatible & m_incompatible
if (m_incompatible.empty()) {
if (m_todo.empty()) {
// nothing else to process
// new_expr is already in the substitution tree
SASSERT(r->m_leaf && r->m_expr == new_expr);
reset_registers(0);
return;
}
else {
mark_used_regs(r->m_subst);
node * best_child = find_best_child(r);
if (best_child == 0) {
// there is no compatible child
node * n = mk_node_for(new_expr);
n->m_next_sibling = r->m_first_child;
r->m_first_child = n;
reset_registers(0);
m_size++;
return;
}
else {
// continue with best_child
r = best_child;
}
}
}
else {
SASSERT(!m_compatible.empty());
SASSERT(!m_incompatible.empty());
mark_used_regs(m_compatible);
r->m_subst.swap(m_compatible);
node * n = mk_node_for(new_expr);
node * incomp = alloc(node, r->m_leaf);
incomp->m_subst.swap(m_incompatible);
if (r->m_leaf) {
incomp->m_expr = r->m_expr;
r->m_leaf = false;
}
else
incomp->m_first_child = r->m_first_child;
incomp->m_next_sibling = n;
SASSERT(!r->m_leaf);
r->m_first_child = incomp;
reset_registers(0);
m_size++;
return;
}
}
}
/**
\brief Return true if sv is fully compatible with the expressions in the registers in m_todo.
*/
bool substitution_tree::is_fully_compatible(svector<subst> const & sv) {
unsigned old_size = m_todo.size();
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
if (is_var(out)) {
if (out != in) {
reset_registers(old_size);
return false;
}
}
else {
if (!in || !is_app(in) || to_app(in)->get_decl() != to_app(out)->get_decl()) {
reset_registers(old_size);
return false;
}
process_args(to_app(in), to_app(out));
}
}
reset_registers(old_size);
return true;
}
/**
\brief Return a child of r that is fully compatible with the expressions in the registers in m_todo.
*/
bool substitution_tree::find_fully_compatible_child(node * r, node * & prev, node * & child) {
SASSERT(!r->m_leaf);
prev = 0;
child = r->m_first_child;
while (child) {
if (is_fully_compatible(child->m_subst))
return true;
prev = child;
child = child->m_next_sibling;
}
return false;
}
inline bool substitution_tree::at_least_3_children(node * r) {
return !r->m_leaf && r->m_first_child->m_next_sibling && r->m_first_child->m_next_sibling->m_next_sibling;
}
/**
\brief Remove expression from the substitution tree.
Do nothing, if n is not in the tree.
*/
void substitution_tree::erase(expr * e) {
if (is_app(e))
erase(to_app(e));
else {
SASSERT(is_var(e));
sort * s = to_var(e)->get_sort();
unsigned id = s->get_decl_id();
if (id >= m_vars.size() || m_vars[id] == 0)
return;
var_ref_vector * v = m_vars[id];
v->erase(to_var(e));
}
}
/**
\brief Remove application from the substitution tree.
Do nothing, if n is not in the tree.
*/
void substitution_tree::erase(app * e) {
func_decl * d = e->get_decl();
unsigned id = d->get_decl_id();
if (id >= m_roots.size() || !m_roots[id])
return;
reset_compiler();
set_reg_value(0, e);
m_todo.push_back(0);
node * r = m_roots[id];
node * parent = 0;
node * prev = 0;
while (true) {
svector<subst> & sv = r->m_subst;
svector<subst>::iterator it = sv.begin();
svector<subst>::iterator end = sv.end();
for (; it != end; ++it) {
subst & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
SASSERT(is_var(out) || is_app(out));
if (is_var(out)) {
if (out != in) {
reset_registers(0);
return; // node is not in the substitution tree
}
erase_reg_from_todo(ireg);
}
else {
if (!in || !is_app(in) || to_app(out)->get_decl() != to_app(in)->get_decl()) {
reset_registers(0);
return; // node is not in the substitution tree
}
erase_reg_from_todo(ireg);
process_args(to_app(in), to_app(out));
}
}
if (m_todo.empty()) {
reset_registers(0);
SASSERT(r->m_expr == e);
if (parent == 0) {
delete_node(r);
m_roots[id] = 0;
}
else if (at_least_3_children(parent)) {
if (prev == 0)
parent->m_first_child = r->m_next_sibling;
else
prev->m_next_sibling = r->m_next_sibling;
delete_node(r);
}
else {
SASSERT(parent->m_first_child && parent->m_first_child->m_next_sibling && !parent->m_first_child->m_next_sibling->m_next_sibling);
node * other_child = prev ? prev : r->m_next_sibling;
SASSERT(other_child);
parent->m_subst.append(other_child->m_subst);
parent->m_leaf = other_child->m_leaf;
if (other_child->m_leaf)
parent->m_expr = other_child->m_expr;
else
parent->m_first_child = other_child->m_first_child;
delete_node(r);
dealloc(other_child); // Remark: I didn't use delete_node since its resources were sent to parent.
}
m_size --;
return;
}
else {
parent = r;
if (!find_fully_compatible_child(r, prev, r)) {
// node is not in the substitution tree
reset_registers(0);
return;
}
// continue with fully compatible child
}
}
}
void substitution_tree::delete_node(node * n) {
ptr_buffer<node> todo;
SASSERT(todo.empty());
todo.push_back(n);
while (!todo.empty()) {
node * n = todo.back();
todo.pop_back();
svector<subst>::iterator it2 = n->m_subst.begin();
svector<subst>::iterator end2 = n->m_subst.end();
for (; it2 != end2; ++it2) {
m_manager.dec_ref(it2->first);
m_manager.dec_ref(it2->second);
}
if (n->m_leaf)
m_manager.dec_ref(n->m_expr);
else {
node * c = n->m_first_child;
while (c) {
todo.push_back(c);
c = c->m_next_sibling;
}
}
dealloc(n);
}
}
void substitution_tree::reset() {
ptr_vector<node>::iterator it = m_roots.begin();
ptr_vector<node>::iterator end = m_roots.end();
for (; it != end; ++it) {
if (*it)
delete_node(*it);
}
m_roots.reset();
std::for_each(m_vars.begin(), m_vars.end(), delete_proc<var_ref_vector>());
m_vars.reset();
m_size = 0;
}
void substitution_tree::display(std::ostream & out, subst const & s) const {
out << "r!" << s.first->get_idx() << " -> ";
if (is_app(s.second)) {
unsigned num = to_app(s.second)->get_num_args();
if (num == 0)
out << to_app(s.second)->get_decl()->get_name();
else {
out << "(" << to_app(s.second)->get_decl()->get_name();
for (unsigned i = 0; i < num; i++)
out << " r!" << to_var(to_app(s.second)->get_arg(i))->get_idx();
out << ")";
}
}
else {
out << mk_pp(s.second, m_manager);
}
}
void substitution_tree::display(std::ostream & out, svector<subst> const & sv) const {
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (bool first = true; it != end; ++it, first = false) {
subst const & s = *it;
if (!first)
out << "; ";
display(out, s);
}
}
void substitution_tree::display(std::ostream & out, node * n, unsigned delta) const {
for (unsigned i = 0; i < delta; i++)
out << " ";
display(out, n->m_subst);
if (n->m_leaf) {
pp_params p;
p.m_pp_single_line = true;
out << " ==> ";
ast_pp(out, n->m_expr, m_manager, p);
out << "\n";
}
else {
out << "\n";
node * c = n->m_first_child;
while (c) {
display(out, c, delta+1);
c = c->m_next_sibling;
}
}
}
bool substitution_tree::backtrack() {
while (!m_bstack.empty()) {
TRACE("st", tout << "backtracking...\n";);
m_subst->pop_scope();
node * n = m_bstack.back();
if (n->m_next_sibling) {
m_bstack.back() = n->m_next_sibling;
return true;
}
m_bstack.pop_back();
}
return false;
}
inline expr_offset substitution_tree::find(expr_offset p) {
TRACE("substitution_tree_bug", tout << "find...\n";);
while (is_var(p.get_expr())) {
TRACE("substitution_tree_bug", tout << mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";);
if (!m_subst->find(to_var(p.get_expr()), p.get_offset(), p))
return p;
}
return p;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::bind_var(var * v, unsigned offset, expr_offset const & p) {
TRACE("st", tout << "bind_var: " << mk_pp(v, m_manager) << " " << offset << "\n" <<
mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";);
if (Mode == STV_INST && offset == m_st_offset) {
SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset);
if (is_var(p.get_expr()) && p.get_offset() == m_in_offset) {
m_subst->insert(p, expr_offset(v, offset));
return true;
}
return false;
}
if (Mode == STV_GEN && offset == m_in_offset) {
SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset);
if (is_var(p.get_expr()) && p.get_offset() == m_st_offset) {
m_subst->insert(p, expr_offset(v, offset));
return true;
}
return false;
}
m_subst->insert(v, offset, p);
TRACE("st_bug", tout << "substitution updated\n"; m_subst->display(tout););
return true;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::unify_match(expr_offset p1, expr_offset p2) {
svector<entry> & todo = m_visit_todo;
todo.reset();
todo.push_back(entry(p1, p2));
while (!todo.empty()) {
entry const & e = todo.back();
p1 = find(e.first);
p2 = find(e.second);
todo.pop_back();
if (p1 != p2) {
expr * n1 = p1.get_expr();
expr * n2 = p2.get_expr();
SASSERT(!is_quantifier(n1));
SASSERT(!is_quantifier(n2));
bool v1 = is_var(n1);
bool v2 = is_var(n2);
TRACE("st",
tout << "n1: " << mk_pp(n1, m_manager) << " " << p1.get_offset() << "\n";
tout << "n2: " << mk_pp(n2, m_manager) << " " << p2.get_offset() << "\n";);
if (v1 && v2) {
if (p2.get_offset() == m_reg_offset)
std::swap(p1, p2);
if (!bind_var<Mode>(to_var(p1.get_expr()), p1.get_offset(), p2))
return false;
}
else if (v1) {
if (!bind_var<Mode>(to_var(n1), p1.get_offset(), p2))
return false;
}
else if (v2) {
if (!bind_var<Mode>(to_var(n2), p2.get_offset(), p1))
return false;
}
else {
app * a1 = to_app(n1);
app * a2 = to_app(n2);
unsigned off1 = p1.get_offset();
unsigned off2 = p2.get_offset();
if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args())
return false;
unsigned j = a1->get_num_args();
while (j > 0) {
--j;
entry new_e(expr_offset(a1->get_arg(j), off1),
expr_offset(a2->get_arg(j), off2));
todo.push_back(new_e);
}
}
}
}
return true;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::visit_vars(expr * e, st_visitor & st) {
if (m_vars.empty())
return true; // continue
sort * s = m_manager.get_sort(e);
unsigned s_id = s->get_decl_id();
if (s_id < m_vars.size()) {
var_ref_vector * v = m_vars[s_id];
if (v && !v->empty()) {
unsigned sz = v->size();
for (unsigned i = 0; i < sz; i++) {
var * curr = v->get(i);
m_subst->push_scope();
if (unify_match<Mode>(expr_offset(curr, m_st_offset), expr_offset(e, m_in_offset))) {
if (Mode != STV_UNIF || m_subst->acyclic()) {
if (!st(curr)) {
m_subst->pop_scope();
return false; // stop
}
}
}
m_subst->pop_scope();
}
}
}
return true; // continue
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::visit(svector<subst> const & sv) {
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
TRACE("st", tout << "processing subst:\n"; display(tout, s); tout << "\n";);
var * rin = s.first;
expr * out = s.second;
expr_offset p1(rin, m_reg_offset);
expr_offset p2(out, is_var(out) ? m_st_offset : m_reg_offset);
if (!unify_match<Mode>(p1, p2))
return false;
}
return true;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::visit(expr * e, st_visitor & st, node * r) {
m_bstack.reset();
m_bstack.push_back(r);
m_subst->push_scope();
m_subst->insert(static_cast<unsigned>(0), m_reg_offset, expr_offset(e, m_in_offset));
while (true) {
node * n = m_bstack.back();
TRACE("st", tout << "push scope...\n";);
m_subst->push_scope();
TRACE("st", tout << "processing node:\n"; display(tout, n->m_subst); tout << "\n";);
if (visit<Mode>(n->m_subst)) {
if (n->m_leaf) {
// if searching for unifiers and the substitution is cyclic, then backtrack.
if (Mode == STV_UNIF && !m_subst->acyclic()) {
if (!backtrack())
break;
}
else {
TRACE("st_bug", tout << "found match:\n"; m_subst->display(tout); tout << "m_subst: " << m_subst << "\n";);
if (!st(n->m_expr))
return false;
if (!backtrack())
break;
}
}
else {
m_bstack.push_back(n->m_first_child);
}
}
else if (!backtrack())
break;
}
while (!m_bstack.empty()) {
m_subst->pop_scope();
m_bstack.pop_back();
}
m_subst->pop_scope();
return true;
}
template<substitution_tree::st_visit_mode Mode>
void substitution_tree::visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
m_in_offset = in_offset;
m_st_offset = st_offset;
m_reg_offset = reg_offset;
m_subst = &(st.get_substitution());
m_subst->reserve_vars(get_approx_num_regs());
if (visit_vars<Mode>(e, st)) {
if (is_app(e)) {
func_decl * d = to_app(e)->get_decl();
unsigned id = d->get_decl_id();
node * r = m_roots.get(id, 0);
if (r)
visit<Mode>(e, st, r);
}
else {
SASSERT(is_var(e));
ptr_vector<node>::iterator it = m_roots.begin();
ptr_vector<node>::iterator end = m_roots.end();
for (; it != end; ++it) {
node * r = *it;
if (r != 0) {
var * v = r->m_subst[0].first;
if (v->get_sort() == to_var(e)->get_sort())
if (!visit<Mode>(e, st, r))
break;
}
}
}
}
}
void substitution_tree::unify(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
visit<STV_UNIF>(e, v, in_offset, st_offset, reg_offset);
}
void substitution_tree::inst(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
visit<STV_INST>(e, v, in_offset, st_offset, reg_offset);
}
void substitution_tree::gen(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
visit<STV_GEN>(e, v, in_offset, st_offset, reg_offset);
}
void substitution_tree::display(std::ostream & out) const {
out << "substitution tree:\n";
ptr_vector<node>::const_iterator it = m_roots.begin();
ptr_vector<node>::const_iterator end = m_roots.end();
for (; it != end; ++it)
if (*it)
display(out, *it, 0);
bool found_var = false;
ptr_vector<var_ref_vector>::const_iterator it2 = m_vars.begin();
ptr_vector<var_ref_vector>::const_iterator end2 = m_vars.end();
for (; it2 != end2; ++it2) {
var_ref_vector * v = *it2;
if (v == 0)
continue; // m_vars may contain null pointers. See substitution_tree::insert.
unsigned num = v->size();
for (unsigned i = 0; i < num; i++) {
if (!found_var) {
found_var = true;
out << "vars: ";
}
out << mk_pp(v->get(i), m_manager) << " ";
}
}
if (found_var)
out << "\n";
}
substitution_tree::substitution_tree(ast_manager & m):
m_manager(m),
m_max_reg(0),
m_size(0) {
}
substitution_tree::~substitution_tree() {
reset();
}

148
src/spc/substitution_tree.h Normal file
View file

@ -0,0 +1,148 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution_tree.h
Abstract:
Substitution Trees
Author:
Leonardo de Moura (leonardo) 2008-02-03.
Revision History:
--*/
#ifndef _SUBSTITUTION_TREE_H_
#define _SUBSTITUTION_TREE_H_
#include"ast.h"
#include"substitution.h"
/**
\brief Substitution tree visitor.
*/
class st_visitor {
protected:
substitution & m_subst;
public:
st_visitor(substitution & s):m_subst(s) {}
virtual ~st_visitor() {}
substitution & get_substitution() { return m_subst; }
virtual bool operator()(expr * e) { return true; }
};
/**
\brief Substitution tree term index.
*/
class substitution_tree {
typedef std::pair<var *, expr *> subst;
struct node {
bool m_leaf;
svector<subst> m_subst;
node * m_next_sibling;
union {
node * m_first_child;
expr * m_expr;
};
node(bool leaf):m_leaf(leaf), m_next_sibling(0), m_first_child(0) {}
};
ast_manager & m_manager;
ptr_vector<node> m_roots;
unsigned m_max_reg;
ptr_vector<expr> m_registers;
unsigned m_size;
ptr_vector<var_ref_vector> m_vars; // mapping from decl_id to var_ref_vector
// Compilation time fields
unsigned m_next_reg;
bit_vector m_used_regs;
unsigned_vector m_todo;
svector<subst> m_compatible;
svector<subst> m_incompatible;
// Execution time fields
substitution * m_subst;
ptr_vector<node> m_bstack;
unsigned m_in_offset;
unsigned m_st_offset;
unsigned m_reg_offset;
typedef std::pair<expr_offset, expr_offset> entry;
svector<entry> m_visit_todo;
unsigned next_reg();
void push(svector<subst> & sv, subst const & s);
expr * get_reg_value(unsigned ridx);
void set_reg_value(unsigned ridx, expr * e);
void erase_reg_from_todo(unsigned ridx);
void linearize(svector<subst> & result);
void process_args(app * in, app * out);
void reset_registers(unsigned old_size);
unsigned get_compatibility_measure(svector<subst> const & sv);
node * find_best_child(node * r);
void reset_compiler();
node * mk_node_for(expr * new_expr);
void mark_used_reg(unsigned ridx);
void mark_used_regs(svector<subst> const & sv);
bool is_fully_compatible(svector<subst> const & sv);
bool find_fully_compatible_child(node * r, node * & prev, node * & child);
static bool at_least_3_children(node * r);
void delete_node(node * n);
void display(std::ostream & out, subst const & s) const;
void display(std::ostream & out, svector<subst> const & sv) const;
void display(std::ostream & out, node * n, unsigned delta) const;
enum st_visit_mode {
STV_UNIF,
STV_INST,
STV_GEN
};
expr_offset find(expr_offset p);
bool backtrack();
template<st_visit_mode Mode>
bool bind_var(var * v, unsigned offset, expr_offset const & p);
template<st_visit_mode Mode>
bool unify_match(expr_offset p1, expr_offset p2);
template<substitution_tree::st_visit_mode Mode>
bool visit_vars(expr * e, st_visitor & st);
template<st_visit_mode Mode>
bool visit(svector<subst> const & s);
template<st_visit_mode Mode>
bool visit(expr * e, st_visitor & st, node * r);
template<st_visit_mode Mode>
void visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset);
public:
substitution_tree(ast_manager & m);
~substitution_tree();
void insert(app * n);
void insert(expr * n);
void erase(app * n);
void erase(expr * n);
void reset();
bool empty() const { return m_size == 0; }
void unify(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2);
void inst(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2);
void gen(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2);
unsigned get_approx_num_regs() const { return m_max_reg + 1; }
void display(std::ostream & out) const;
};
#endif /* _SUBSTITUTION_TREE_H_ */

183
src/spc/unifier.cpp Normal file
View file

@ -0,0 +1,183 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
unifier.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#include"unifier.h"
#include"ast_pp.h"
void unifier::reset(unsigned num_offsets) {
m_todo.reset();
m_find.reset();
m_size.reset();
}
/**
\brief Find with path compression.
*/
expr_offset unifier::find(expr_offset p) {
buffer<expr_offset> path;
expr_offset next;
while (m_find.find(p, next)) {
path.push_back(p);
p = next;
}
buffer<expr_offset>::iterator it = path.begin();
buffer<expr_offset>::iterator end = path.end();
for (; it != end; ++it) {
expr_offset & prev = *it;
m_find.insert(prev, p);
}
return p;
}
void unifier::save_var(expr_offset const & p, expr_offset const & t) {
expr * n = p.get_expr();
if (is_var(n)) {
unsigned off = p.get_offset();
m_subst->insert(to_var(n)->get_idx(), off, t);
}
}
/**
\brief Merge the equivalence classes of n1 and n2. n2 will be the
root of the resultant equivalence class.
*/
void unifier::union1(expr_offset const & n1, expr_offset const & n2) {
DEBUG_CODE({
expr_offset f;
SASSERT(!m_find.find(n1, f));
SASSERT(!m_find.find(n2, f));
});
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(n1, sz1);
m_size.find(n2, sz2);
m_find.insert(n1, n2);
m_size.insert(n2, sz1 + sz2);
save_var(n1, n2);
}
/**
\brief Merge the equivalence classes of n1 and n2. The root of the
resultant equivalence class is the one with more elements.
*/
void unifier::union2(expr_offset n1, expr_offset n2) {
DEBUG_CODE({
expr_offset f;
SASSERT(!m_find.find(n1, f));
SASSERT(!m_find.find(n2, f));
});
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(n1, sz1);
m_size.find(n2, sz2);
if (sz1 > sz2)
std::swap(n1, n2);
m_find.insert(n1, n2);
m_size.insert(n2, sz1 + sz2);
save_var(n1, n2);
}
bool unifier::unify_core(expr_offset p1, expr_offset p2) {
entry e(p1, p2);
m_todo.push_back(e);
while (!m_todo.empty()) {
entry const & e = m_todo.back();
p1 = find(e.first);
p2 = find(e.second);
m_todo.pop_back();
if (p1 != p2) {
expr * n1 = p1.get_expr();
expr * n2 = p2.get_expr();
SASSERT(!is_quantifier(n1));
SASSERT(!is_quantifier(n2));
bool v1 = is_var(n1);
bool v2 = is_var(n2);
if (v1 && v2) {
union2(p1, p2);
}
else if (v1) {
union1(p1, p2);
}
else if (v2) {
union1(p2, p1);
}
else {
app * a1 = to_app(n1);
app * a2 = to_app(n2);
unsigned off1 = p1.get_offset();
unsigned off2 = p2.get_offset();
if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args())
return false;
union2(p1, p2);
unsigned j = a1->get_num_args();
while (j > 0) {
--j;
entry new_e(expr_offset(a1->get_arg(j), off1),
expr_offset(a2->get_arg(j), off2));
m_todo.push_back(new_e);
}
}
}
}
return true;
}
bool unifier::operator()(unsigned num_exprs, expr ** es, substitution & s, bool use_offsets) {
SASSERT(num_exprs > 0);
unsigned num_offsets = use_offsets ? num_exprs : 1;
reset(num_offsets);
m_subst = &s;
#if 1
TRACE("unifier", for (unsigned i = 0; i < num_exprs; ++i) tout << mk_pp(es[i], m_manager) << "\n";);
for (unsigned i = s.get_num_bindings(); i > 0; ) {
--i;
std::pair<unsigned,unsigned> bound;
expr_offset root, child;
s.get_binding(i, bound, root);
TRACE("unifier", tout << bound.first << " |-> " << mk_pp(root.get_expr(), m_manager) << "\n";);
if (is_var(root.get_expr())) {
var* v = m_manager.mk_var(bound.first,to_var(root.get_expr())->get_sort());
child = expr_offset(v, bound.second);
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(child, sz1);
m_size.find(root, sz2);
m_find.insert(child, root);
m_size.insert(root, sz1 + sz2);
}
}
#endif
for (unsigned i = 0; i < num_exprs - 1; i++) {
if (!unify_core(expr_offset(es[i], use_offsets ? i : 0),
expr_offset(es[i+1], use_offsets ? i + 1 : 0))) {
m_last_call_succeeded = false;
return m_last_call_succeeded;
}
}
m_last_call_succeeded = m_subst->acyclic();
return m_last_call_succeeded;
}
bool unifier::operator()(expr * e1, expr * e2, substitution & s, bool use_offsets) {
expr * es[2] = { e1, e2 };
return operator()(2, es, s, use_offsets);
}

70
src/spc/unifier.h Normal file
View file

@ -0,0 +1,70 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
unifier.h
Abstract:
Quasi-linear unification.
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#ifndef _UNIFIER_H_
#define _UNIFIER_H_
#include"ast.h"
#include"substitution.h"
/**
\brief Functor for unifying expressions.
It implements a quasi-linear unification algorithm.
It has support for two different variable banks: left and right.
That is, variable i in left bank is considered different from
variable i in the right bank. This feature allows us to avoid
unnecessary variable renaming.
*/
class unifier {
typedef std::pair<expr_offset, expr_offset> entry;
ast_manager & m_manager;
substitution * m_subst;
svector<entry> m_todo;
expr_offset_map<expr_offset> m_find;
expr_offset_map<unsigned> m_size;
bool m_last_call_succeeded;
expr_offset find(expr_offset n);
void save_var(expr_offset const & p, expr_offset const & t);
void union1(expr_offset const & n1, expr_offset const & n2);
void union2(expr_offset n1, expr_offset n2);
void reset(unsigned num_offsets);
bool unify_core(expr_offset p1, expr_offset p2);
public:
unifier(ast_manager & m):m_manager(m), m_last_call_succeeded(false) {}
/**
\brief Unify the given expressions. Return true if succeeded,
and store the result in the given substitution.
If use_offsets is true, then the variables in the given expressions are assumed to be
in different banks.
*/
bool operator()(unsigned num_exprs, expr ** es, substitution & s, bool use_offsets = true);
bool operator()(expr * e1, expr * e2, substitution & s, bool use_offsets = true);
};
#endif /* _UNIFIER_H_ */

90
src/spc/use_list.cpp Normal file
View file

@ -0,0 +1,90 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
use_list.cpp
Abstract:
Use list term index.
Author:
Leonardo de Moura (leonardo) 2008-02-04.
Revision History:
--*/
#include"use_list.h"
void app_use_list::inc_ref(app * n) {
if (n->get_num_args() == 0)
return; // ignore constants
unsigned id = n->get_id();
unsigned c = m_ref_counter.get(id, 0);
m_ref_counter.setx(id, c+1, 0);
if (c == 0)
m_todo.push_back(n);
}
void app_use_list::dec_ref(app * n) {
if (n->get_num_args() == 0)
return; // ignore constants
unsigned id = n->get_id();
SASSERT(m_ref_counter[id] > 0);
m_ref_counter[id]--;
if (m_ref_counter[id] == 0)
m_todo.push_back(n);
}
void app_use_list::insert(expr * n) {
if (is_var(n))
return; // nothing to index
SASSERT(m_todo.empty());
inc_ref(to_app(n));
while (!m_todo.empty()) {
app * n = m_todo.back();
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * c = n->get_arg(i);
if (is_var(c)) {
if (!m_ignore_vars)
use_list<app*>::insert(n, c);
}
else {
SASSERT(is_app(c));
use_list<app*>::insert(n, c);
inc_ref(to_app(c));
}
}
}
}
void app_use_list::erase(expr * n) {
if (is_var(n))
return; // nothing to index
SASSERT(m_todo.empty());
dec_ref(to_app(n));
while (!m_todo.empty()) {
app * n = m_todo.back();
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * c = n->get_arg(i);
if (is_var(c)) {
if (!m_ignore_vars)
use_list<app*>::erase(n, c);
}
else {
SASSERT(is_app(c));
use_list<app*>::erase(n, c);
dec_ref(to_app(c));
}
}
}
}
void app_use_list::reset() {
use_list<app*>::reset();
m_ref_counter.reset();
}

120
src/spc/use_list.h Normal file
View file

@ -0,0 +1,120 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
use_list.h
Abstract:
Use list expression index.
Author:
Leonardo de Moura (leonardo) 2008-02-04.
Revision History:
--*/
#ifndef _USE_LIST_H_
#define _USE_LIST_H_
#include"ast.h"
#include"vector.h"
/**
\brief Generic use-list data-structure.
*/
template<typename T>
class use_list {
typedef vector<T> set;
vector<set> m_use_list;
public:
typedef typename set::const_iterator iterator;
use_list() {}
void insert(T const & parent, expr * child) {
unsigned id = child->get_id();
if (id >= m_use_list.size())
m_use_list.resize(id+1, set());
set & s = m_use_list[id];
s.push_back(parent);
}
void erase(T const & parent, expr * child) {
unsigned id = child->get_id();
if (id >= m_use_list.size())
return;
set & s = m_use_list[id];
s.erase(parent);
}
void reset() {
m_use_list.reset();
}
iterator begin(expr * e) const {
unsigned id = e->get_id();
if (id >= m_use_list.size())
return 0;
return m_use_list[id].begin();
}
iterator end(expr * e) const {
unsigned id = e->get_id();
if (id >= m_use_list.size())
return 0;
return m_use_list[id].end();
}
bool empty(expr * e) const {
unsigned id = e->get_id();
if (id >= m_use_list.size())
return true;
return m_use_list[id].empty();
}
};
/**
\brief Index for tracking the uses of an expression. It is a
mapping from expressions to expressions. For example, consider the
term (f a (g a)), the constant a is used by f and g applications.
\remark The expressions inserted in this index should not contain
quantifiers.
\warning This index will not increase the reference counter of the
indexed expressions.
*/
class app_use_list : use_list<app*> {
bool m_ignore_vars; //!< when true, variables are not indexed
unsigned_vector m_ref_counter;
ptr_vector<app> m_todo;
void inc_ref(app * n);
void dec_ref(app * n);
public:
/**
\brief If ignore_vars = true, then the index will not track
the use of variables.
*/
app_use_list(bool ignore_vars = true):
m_ignore_vars(ignore_vars) {
}
/**
\brief Update the use list of all direct/indirect children of n.
*/
void insert(expr * n);
/**
\brief Remove n (and its unreachable direct/indirect children) from the index.
*/
void erase(expr * n);
void reset();
};
#endif /* _USE_LIST_H_ */

114
src/spc/var_offset_map.h Normal file
View file

@ -0,0 +1,114 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
var_offset_map.h
Abstract:
A generic mapping from (var, offset) to a value T.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _VAR_OFFSET_MAP_H_
#define _VAR_OFFSET_MAP_H_
#include"ast.h"
#include"vector.h"
/**
\brief A mapping from variable-id + offset to some value of type T.
*/
template<typename T>
class var_offset_map {
protected:
struct data {
T m_data;
unsigned m_timestamp;
data():m_timestamp(0) {}
};
svector<data> m_map;
unsigned m_num_offsets;
unsigned m_num_vars;
unsigned m_timestamp;
public:
var_offset_map():
m_num_offsets(0),
m_num_vars(0),
m_timestamp(1) {
}
void reset() {
m_timestamp++;
if (m_timestamp == UINT_MAX) {
typename svector<data>::iterator it = m_map.begin();
typename svector<data>::iterator end = m_map.end();
for (; it != end; ++it)
it->m_timestamp = 0;
m_timestamp = 1;
}
}
unsigned offsets_capacity() const { return m_num_offsets; }
unsigned vars_capacity() const { return m_num_vars; }
void reserve(unsigned num_offsets, unsigned num_vars) {
if (num_offsets > m_num_offsets || num_vars > m_num_vars) {
unsigned sz = num_offsets * num_vars;
m_map.resize(sz);
m_num_vars = num_vars;
m_num_offsets = num_offsets;
}
reset();
}
void reserve_offsets(unsigned num_offsets) { reserve(num_offsets, m_num_vars); }
void reserve_vars(unsigned num_vars) { reserve(m_num_offsets, num_vars); }
void insert(unsigned v_idx, unsigned offset, T const & t) {
SASSERT(v_idx < m_num_vars);
SASSERT(offset < m_num_offsets);
unsigned idx = v_idx + offset * m_num_vars;
SASSERT(idx < m_map.size());
data & d = m_map[idx];
d.m_data = t;
d.m_timestamp = m_timestamp;
}
void insert(var * v, unsigned offset, T const & t) { insert(v->get_idx(), offset, t); }
bool find(unsigned v_idx, unsigned offset, T & r) const {
SASSERT(v_idx < m_num_vars);
SASSERT(offset < m_num_offsets);
unsigned idx = v_idx + offset * m_num_vars;
data const & d = m_map[idx];
SASSERT(d.m_timestamp <= m_timestamp);
if (d.m_timestamp == m_timestamp) {
r = d.m_data;
return true;
}
return false;
}
bool find(var * v, unsigned offset, T & r) const { return find(v->get_idx(), offset, r); }
void erase(unsigned v_idx, unsigned offset) {
SASSERT(v_idx < m_num_vars);
SASSERT(offset < m_num_offsets);
unsigned idx = v_idx + offset * m_num_vars;
m_map[idx].m_timestamp = 0;
}
};
#endif /* _VAR_OFFSET_MAP_H_ */