3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

Anti-unification of two ground expressions

This commit is contained in:
Arie Gurfinkel 2018-05-27 09:34:49 -07:00
parent aa8dac2583
commit af57db0413
2 changed files with 80 additions and 190 deletions

View file

@ -103,190 +103,73 @@ struct var_abs_rewriter : public default_rewriter_cfg {
};
/*
* construct m_g, which is a generalization of t, where every constant
* is replaced by a variable for any variable in m_g, remember the
* substitution to get back t and save it in m_substitutions
*/
anti_unifier::anti_unifier(expr* t, ast_manager& man) : m(man), m_pinned(m), m_g(m)
{
m_pinned.push_back(t);
obj_map<expr, expr*> substitution;
anti_unifier::anti_unifier(ast_manager &manager) : m(manager), m_pinned(m) {}
var_abs_rewriter var_abs_cfg(m, substitution);
rewriter_tpl<var_abs_rewriter> var_abs_rw (m, false, var_abs_cfg);
var_abs_rw (t, m_g);
m_substitutions.push_back(substitution); //TODO: refactor into vector, remove k
void anti_unifier::reset() {
m_subs.reset();
m_cache.reset();
m_todo.reset();
m_pinned.reset();
}
/* traverses m_g and t in parallel. if they only differ in constants
* (i.e. m_g contains a variable, where t contains a constant), then
* add the substitutions, which need to be applied to m_g to get t, to
* m_substitutions.
*/
bool anti_unifier::add_term(expr* t) {
m_pinned.push_back(t);
void anti_unifier::operator()(expr *e1, expr *e2, expr_ref &res,
substitution &s1, substitution &s2) {
ptr_vector<expr> todo;
ptr_vector<expr> todo2;
todo.push_back(m_g);
todo2.push_back(t);
reset();
if (e1 == e2) {res = e1; s1.reset(); s2.reset(); return;}
ast_mark visited;
m_todo.push_back(expr_pair(e1, e2));
while (!m_todo.empty()) {
const expr_pair &p = m_todo.back();
SASSERT(is_app(p.first));
SASSERT(is_app(p.second));
arith_util util(m);
app * n1 = to_app(p.first);
app * n2 = to_app(p.second);
obj_map<expr, expr*> substitution;
while (!todo.empty()) {
expr* current = todo.back();
todo.pop_back();
expr* current2 = todo2.back();
todo2.pop_back();
if (!visited.is_marked(current)) {
visited.mark(current, true);
if (is_var(current)) {
// TODO: for now we don't allow variables in the terms we want to antiunify
SASSERT(m_substitutions[0].contains(current));
if (util.is_numeral(current2)) {
substitution.insert(current, current2);
}
else {return false;}
}
else {
SASSERT(is_app(current));
if (is_app(current2) &&
to_app(current)->get_decl() == to_app(current2)->get_decl() &&
to_app(current)->get_num_args() == to_app(current2)->get_num_args()) {
// TODO: what to do for numerals here? E.g. if we
// have 1 and 2, do they have the same decl or are
// the decls already different?
SASSERT (!util.is_numeral(current) || current == current2);
for (unsigned i = 0, num_args = to_app(current)->get_num_args();
i < num_args; ++i) {
todo.push_back(to_app(current)->get_arg(i));
todo2.push_back(to_app(current2)->get_arg(i));
}
}
else {
return false;
}
}
}
}
// we now know that the terms can be anti-unified, so add the cached substitution
m_substitutions.push_back(substitution);
return true;
}
/*
* returns m_g, where additionally any variable, which has only equal
* substitutions, is substituted with that substitution
*/
void anti_unifier::finalize() {
ptr_vector<expr> todo;
todo.push_back(m_g);
ast_mark visited;
obj_map<expr, expr*> generalization;
arith_util util(m);
// post-order traversel which ignores constants and handles them
// directly when the enclosing term of the constant is handled
while (!todo.empty()) {
expr* current = todo.back();
SASSERT(is_app(current));
// if we haven't already visited current
if (!visited.is_marked(current)) {
bool existsUnvisitedParent = false;
for (unsigned i = 0, sz = to_app(current)->get_num_args(); i < sz; ++i) {
expr* argument = to_app(current)->get_arg(i);
if (!is_var(argument)) {
SASSERT(is_app(argument));
// if we haven't visited the current parent yet
if(!visited.is_marked(argument)) {
// add it to the stack
todo.push_back(argument);
existsUnvisitedParent = true;
}
}
}
// if we already visited all parents, we can visit current too
if (!existsUnvisitedParent) {
visited.mark(current, true);
todo.pop_back();
ptr_buffer<expr> arg_list;
for (unsigned i = 0, num_args = to_app(current)->get_num_args();
i < num_args; ++i) {
expr* argument = to_app(current)->get_arg(i);
if (is_var(argument)) {
// compute whether there are different
// substitutions for argument
bool containsDifferentSubstitutions = false;
for (unsigned i=0, sz = m_substitutions.size(); i+1 < sz; ++i) {
SASSERT(m_substitutions[i].contains(argument));
SASSERT(m_substitutions[i+1].contains(argument));
// TODO: how to check equality?
if (m_substitutions[i][argument] !=
m_substitutions[i+1][argument])
{
containsDifferentSubstitutions = true;
break;
}
}
// if yes, use the variable
if (containsDifferentSubstitutions) {
arg_list.push_back(argument);
}
// otherwise use the concrete value instead
// and remove the substitutions
else
{
arg_list.push_back(m_substitutions[0][argument]);
for (unsigned i=0, sz = m_substitutions.size(); i < sz; ++i) {
SASSERT(m_substitutions[i].contains(argument));
m_substitutions[i].remove(argument);
}
}
}
else {
SASSERT(generalization.contains(argument));
arg_list.push_back(generalization[argument]);
}
}
SASSERT(to_app(current)->get_num_args() == arg_list.size());
expr_ref application(m.mk_app(to_app(current)->get_decl(),
to_app(current)->get_num_args(),
arg_list.c_ptr()), m);
m_pinned.push_back(application);
generalization.insert(current, application);
}
unsigned num_arg1 = n1->get_num_args();
unsigned num_arg2 = n2->get_num_args();
if (n1->get_decl() != n2->get_decl() || num_arg1 != num_arg2) {
expr_ref v(m);
v = m.mk_var(m_subs.size(), get_sort(n1));
m_pinned.push_back(v);
m_subs.push_back(expr_pair(n1, n2));
m_cache.insert(n1, n2, v);
}
else {
todo.pop_back();
expr *tmp;
unsigned todo_sz = m_todo.size();
ptr_buffer<expr> kids;
for (unsigned i = 0; i < num_arg1; ++i) {
expr *arg1 = n1->get_arg(i);
expr *arg2 = n2->get_arg(i);
if (arg1 == arg2) {kids.push_back(arg1);}
else if (m_cache.find(arg1, arg2, tmp)) {kids.push_back(tmp);}
else {m_todo.push_back(expr_pair(arg1, arg2));}
}
if (m_todo.size() > todo_sz) {continue;}
expr_ref u(m);
u = m.mk_app(n1->get_decl(), kids.size(), kids.c_ptr());
m_pinned.push_back(u);
m_cache.insert(n1, n2, u);
}
}
m_g = generalization[m_g];
expr *r;
VERIFY(m_cache.find(e1, e2, r));
res = r;
// create substitutions
s1.reserve(2, m_subs.size());
s2.reserve(2, m_subs.size());
for (unsigned i = 0, sz = m_subs.size(); i < sz; ++i) {
expr_pair p = m_subs.get(i);
s1.insert(i, 0, expr_offset(p.first, 1));
s2.insert(i, 0, expr_offset(p.second, 1));
}
}
@ -319,6 +202,8 @@ public:
*/
bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m,
expr_ref& result) {
NOT_IMPLEMENTED_YET();
#if 0
arith_util util(m);
SASSERT(au.get_num_substitutions() > 0);
@ -412,6 +297,7 @@ bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m,
result = expr_ref(m.mk_exists(vars.size(), sorts.c_ptr(), names.c_ptr(), body),m);
return true;
#endif
}
bool naive_convex_closure::get_range(vector<unsigned int>& v,

View file

@ -22,32 +22,36 @@ Revision History:
#define _SPACER_ANTIUNIFY_H_
#include "ast/ast.h"
#include "ast/substitution/substitution.h"
#include "util/obj_pair_hashtable.h"
namespace spacer {
/**
\brief Anti-unifier for ground expressions
*/
class anti_unifier
{
public:
anti_unifier(expr* t, ast_manager& m);
~anti_unifier() {}
typedef std::pair<expr *, expr *> expr_pair;
typedef pair_hash<obj_ptr_hash<expr>, obj_ptr_hash<expr> > expr_pair_hash;
typedef obj_pair_map<expr, expr, expr*> cache_ty;
bool add_term(expr* t);
void finalize();
expr* get_generalization() {return m_g;}
unsigned get_num_substitutions() {return m_substitutions.size();}
obj_map<expr, expr*> get_substitution(unsigned index){
SASSERT(index < m_substitutions.size());
return m_substitutions[index];
}
private:
ast_manager& m;
// tracking all created expressions
ast_manager &m;
expr_ref_vector m_pinned;
expr_ref m_g;
svector<expr_pair> m_todo;
cache_ty m_cache;
svector<expr_pair> m_subs;
vector<obj_map<expr, expr*>> m_substitutions;
public:
anti_unifier(ast_manager& m);
void reset();
/**
\brief Computes anti-unifier of two ground expressions. Returns
the anti-unifier and the corresponding substitutions
*/
void operator() (expr *e1, expr *e2, expr_ref &res,
substitution &s1, substitution &s2);
};
class naive_convex_closure