3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-04 10:20:23 +00:00
z3/src/ast/simplifiers/euf_completion.h

220 lines
8.4 KiB
C++

/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
euf_completion.h
Abstract:
Completion for (conditional) equalities.
This transforms expressions into a normal form by perorming equality saturation modulo
ground equations and E-matching on quantified axioms.
It supports conditional equations in terms of implications.
Author:
Nikolaj Bjorner (nbjorner) 2022-10-30
--*/
#pragma once
#include "util/scoped_vector.h"
#include "util/dlist.h"
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/euf/euf_egraph.h"
#include "ast/euf/euf_mam.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/pattern/pattern_inference.h"
#include "params/smt_params.h"
namespace euf {
class side_condition_solver {
public:
struct solution {
expr* var;
expr_ref term;
expr_ref guard;
};
virtual ~side_condition_solver() = default;
virtual void add_constraint(expr* f, proof*, expr_dependency* d) = 0;
virtual bool is_true(expr* f, proof_ref& pr, expr_dependency*& d) = 0;
virtual void push() = 0;
virtual void pop(unsigned n) = 0;
virtual void solve_for(vector<solution>& sol) = 0;
};
struct binding : public dll_base<binding> {
quantifier* m_q;
app* m_pattern;
unsigned m_max_generation;
unsigned m_min_top_generation;
unsigned m_max_top_generation;
euf::enode* m_nodes[0];
binding(quantifier* q, app* pat, unsigned max_generation, unsigned min_top, unsigned max_top) :
m_q(q),
m_pattern(pat),
m_max_generation(max_generation),
m_min_top_generation(min_top),
m_max_top_generation(max_top) {
}
euf::enode* const* nodes() { return m_nodes; }
euf::enode* operator[](unsigned i) const { return m_nodes[i]; }
unsigned size() const { return m_q->get_num_decls(); }
quantifier* q() const { return m_q; }
bool eq(binding const& other) const {
if (q() != other.q())
return false;
for (unsigned i = size(); i-- > 0; )
if ((*this)[i] != other[i])
return false;
return true;
}
};
struct binding_khasher {
unsigned operator()(binding const* f) const { return f->q()->get_id(); }
};
struct binding_chasher {
unsigned operator()(binding const* f, unsigned idx) const { return f->m_nodes[idx]->hash(); }
};
struct binding_hash_proc {
unsigned operator()(binding const* f) const {
return get_composite_hash<binding*, binding_khasher, binding_chasher>(const_cast<binding*>(f), f->size());
}
};
struct binding_eq_proc {
bool operator()(binding const* a, binding const* b) const { return a->eq(*b); }
};
typedef ptr_hashtable<binding, binding_hash_proc, binding_eq_proc> bindings;
class completion : public dependent_expr_simplifier, public on_binding_callback, public mam_solver {
struct stats {
unsigned m_num_rewrites = 0;
unsigned m_num_instances = 0;
void reset() { memset(this, 0, sizeof(*this)); }
};
struct conditional_rule {
euf::enode_vector m_body;
expr_ref m_head;
expr_ref_vector m_proofs;
expr_dependency* m_dep;
unsigned m_watch_index = 0;
bool m_active = true;
bool m_in_queue = false;
conditional_rule(euf::enode_vector& b, expr_ref& h, expr_ref_vector& prs, expr_dependency* d) :
m_body(b), m_head(h), m_proofs(prs), m_dep(d) {}
};
smt_params m_smt_params;
egraph m_egraph;
scoped_ptr<mam> m_mam;
enode* m_tt, *m_ff;
ptr_vector<expr> m_todo;
enode_vector m_args, m_reps, m_nodes_to_canonize;
expr_ref_vector m_canonical, m_eargs;
proof_ref_vector m_canonical_proofs;
pattern_inference_rw m_infer_patterns;
bindings m_bindings;
scoped_ptr<binding> m_tmp_binding;
unsigned m_tmp_binding_capacity = 0;
expr_dependency_ref_vector m_deps;
obj_map<quantifier, std::pair<proof*, expr_dependency*>> m_q2dep;
vector<std::pair<proof_ref, expr_dependency*>> m_pr_dep;
unsigned m_epoch = 0;
unsigned_vector m_epochs;
th_rewriter m_rewriter;
stats m_stats;
scoped_ptr<side_condition_solver> m_side_condition_solver;
ptr_vector<conditional_rule> m_rules;
bool m_has_new_eq = false;
bool m_should_propagate = false;
unsigned m_max_instantiations = std::numeric_limits<unsigned>::max();
unsigned m_generation = 0;
vector<ptr_vector<conditional_rule>> m_rule_watch;
size_t* to_ptr(size_t i) const { return reinterpret_cast<size_t*>(i); }
unsigned from_ptr(size_t* s) const { return (unsigned)reinterpret_cast<size_t>(s); }
unsigned push_pr_dep(proof* pr, expr_dependency* d);
enode* mk_enode(expr* e);
bool is_new_eq(expr* a, expr* b);
void update_has_new_eq(expr* g);
expr_ref mk_and(expr* a, expr* b);
void add_egraph();
void map_canonical();
void read_egraph();
expr_ref canonize(expr* f, proof_ref& pr, expr_dependency_ref& dep);
expr_ref canonize_fml(expr* f, proof_ref& pr, expr_dependency_ref& dep);
expr* get_canonical(expr* f, proof_ref& pr, expr_dependency_ref& d);
expr* get_canonical(enode* n);
proof* get_canonical_proof(enode* n);
void set_canonical(enode* n, expr* e, proof* pr);
void add_constraint(expr*f, proof* pr, expr_dependency* d);
expr_dependency* explain_eq(enode* a, enode* b);
proof_ref prove_eq(enode* a, enode* b);
proof_ref prove_conflict();
expr_dependency* explain_conflict();
std::pair<proof*, expr_dependency*> get_dependency(quantifier* q) { return m_q2dep.contains(q) ? m_q2dep[q] : std::pair(nullptr, nullptr); }
binding* tmp_binding(quantifier* q, app* pat, euf::enode* const* _binding);
binding* alloc_binding(quantifier* q, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top);
void insert_binding(binding* b);
void apply_binding(binding& b);
void flush_binding_queue();
vector<ptr_vector<binding>> m_queue;
lbool eval_cond(expr* f, proof_ref& pr, expr_dependency*& d);
bool should_stop();
void add_rule(expr* f, proof* pr, expr_dependency* d);
void watch_rule(enode* root, enode* other);
void insert_watch(enode* n, conditional_rule* r);
void propagate_rule(conditional_rule& r);
void propagate_rules();
void propagate_all_rules();
void clear_propagation_queue();
ptr_vector<conditional_rule> m_propagation_queue;
struct push_watch_rule;
struct scoped_generation;
bool is_gt(expr* a, expr* b) const;
public:
completion(ast_manager& m, dependent_expr_state& fmls);
~completion() override;
char const* name() const override { return "euf-completion"; }
void push() override;
void pop(unsigned n) override;
void reduce() override;
void collect_statistics(statistics& st) const override;
void reset_statistics() override { m_stats.reset(); }
void updt_params(params_ref const& p) override;
bool supports_proofs() const override { return true; }
trail_stack& get_trail() override { return m_trail;}
region& get_region() override { return m_trail.get_region(); }
egraph& get_egraph() override { return m_egraph; }
bool is_relevant(enode* n) const override { return true; }
bool resource_limits_exceeded() const override { return m_stats.m_num_instances > m_max_instantiations; }
ast_manager& get_manager() override { return m; }
void on_binding(quantifier* q, app* pat, enode* const* binding, unsigned mg, unsigned ming, unsigned mx) override;
void set_solver(side_condition_solver* s) { m_side_condition_solver = s; }
};
}