3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-11 03:33:35 +00:00
z3/lib/spc_superposition.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

532 lines
22 KiB
C++

/*++
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();
}
}
}
};