mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-26 09:24:36 +00:00 
			
		
		
		
	euf
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									96587bf708
								
							
						
					
					
						commit
						65e6d942ac
					
				
					 23 changed files with 1338 additions and 39 deletions
				
			
		
							
								
								
									
										9
									
								
								src/ast/euf/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/ast/euf/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| z3_add_component(euf | ||||
|   SOURCES | ||||
|     euf_enode.cpp | ||||
|     euf_etable.cpp | ||||
|     euf_egraph.cpp | ||||
|   COMPONENT_DEPENDENCIES | ||||
|     ast | ||||
|     util | ||||
| ) | ||||
							
								
								
									
										302
									
								
								src/ast/euf/euf_egraph.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								src/ast/euf/euf_egraph.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,302 @@ | |||
| /*++
 | ||||
| Copyright (c) 2020 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     euf_egraph.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     E-graph layer | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2020-08-23 | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #include "ast/euf/euf_egraph.h" | ||||
| 
 | ||||
| namespace euf { | ||||
| 
 | ||||
|     void egraph::undo_eq(enode* r1, enode* n1, unsigned r2_num_parents) { | ||||
|         enode* r2 = r1->get_root(); | ||||
|         r2->dec_class_size(r1->class_size()); | ||||
|         std::swap(r1->m_next, r2->m_next); | ||||
|         auto begin = r2->begin_parents() + r2_num_parents, end = r2->end_parents(); | ||||
|         for (auto it = begin; it != end; ++it)  | ||||
|             m_table.erase(*it); | ||||
|         for (enode* c : enode_class(r1))  | ||||
|             c->m_root = r1; | ||||
|         for (auto it = begin; it != end; ++it)  | ||||
|             m_table.insert(*it); | ||||
|         r2->m_parents.shrink(r2_num_parents); | ||||
|         unmerge_justification(n1); | ||||
|     } | ||||
| 
 | ||||
|     enode* egraph::mk_enode(expr* f, enode * const* args) { | ||||
|         unsigned num_args = is_app(f) ? to_app(f)->get_num_args() : 0; | ||||
|         enode* n = enode::mk(m_region, f, num_args, args); | ||||
|         m_nodes.push_back(n); | ||||
|         m_exprs.push_back(f); | ||||
|         return n; | ||||
|     } | ||||
| 
 | ||||
|     void egraph::reinsert(enode* n) { | ||||
|         unsigned num_parents = n->m_parents.size(); | ||||
|         for (unsigned i = 0; i < num_parents; ++i) { | ||||
|             enode* p = n->m_parents[i]; | ||||
|             if (is_equality(p)) { | ||||
|                 reinsert_equality(p); | ||||
|             } | ||||
|             else { | ||||
|                 auto rc = m_table.insert(p); | ||||
|                 merge(rc.first, p, justification::congruence(rc.second)); | ||||
|                 if (inconsistent()) | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void egraph::reinsert_equality(enode* p) { | ||||
|         if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root())  | ||||
|             m_new_eqs.push_back(p); | ||||
|     } | ||||
| 
 | ||||
|     bool egraph::is_equality(enode* p) const { | ||||
|         return m.is_eq(p->get_owner()); | ||||
|     } | ||||
| 
 | ||||
|     void egraph::dedup_equalities() { | ||||
|         unsigned j = 0; | ||||
|         for (enode* p : m_new_eqs) { | ||||
|             if (!p->is_marked1()) | ||||
|                 m_new_eqs[j++] = p; | ||||
|             p->mark1(); | ||||
|         } | ||||
|         for (enode* p : m_new_eqs) | ||||
|             p->unmark1(); | ||||
|         m_new_eqs.shrink(j);         | ||||
|     } | ||||
| 
 | ||||
|     void egraph::force_push() { | ||||
|         for (; m_num_scopes > 0; --m_num_scopes) { | ||||
|             scope s; | ||||
|             s.m_inconsistent = m_inconsistent; | ||||
|             s.m_num_eqs = m_eqs.size(); | ||||
|             s.m_num_nodes = m_nodes.size(); | ||||
|             m_scopes.push_back(s); | ||||
|             m_region.push_scope(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void egraph::update_children(enode* n) { | ||||
|         for (enode* child : enode_args(n))  | ||||
|             child->get_root()->add_parent(n); | ||||
|         n->set_update_children();             | ||||
|     } | ||||
| 
 | ||||
|     enode* egraph::mk(expr* f, enode *const* args) { | ||||
|         SASSERT(!find(f)); | ||||
|         force_push(); | ||||
|         enode *n = mk_enode(f, args); | ||||
|         SASSERT(n->class_size() == 1); | ||||
|         m_expr2enode.setx(f->get_id(), n, nullptr); | ||||
|         if (n->num_args() == 0 && m.is_unique_value(f)) | ||||
|             n->mark_interpreted(); | ||||
|         if (n->num_args() == 0)  | ||||
|             return n; | ||||
|         if (is_equality(n)) { | ||||
|             update_children(n); | ||||
|             return n; | ||||
|         } | ||||
|         enode_bool_pair p = m_table.insert(n); | ||||
|         enode* r = p.first; | ||||
|         if (r == n) { | ||||
|             update_children(n); | ||||
|         } | ||||
|         else { | ||||
|             SASSERT(r->get_owner() != n->get_owner()); | ||||
|             merge_justification(n, r, justification::congruence(p.second)); | ||||
|             std::swap(n->m_next, r->m_next); | ||||
|             n->m_root = r; | ||||
|             r->inc_class_size(n->class_size()); | ||||
|             push_eq(n, n, r->num_parents()); | ||||
|         } | ||||
|         return n; | ||||
|     } | ||||
| 
 | ||||
|     void egraph::pop(unsigned num_scopes) { | ||||
|         if (num_scopes <= m_num_scopes) { | ||||
|             m_num_scopes -= num_scopes; | ||||
|             return; | ||||
|         } | ||||
|         num_scopes -= m_num_scopes; | ||||
|         unsigned old_lim = m_scopes.size() - num_scopes; | ||||
|         scope s = m_scopes[old_lim]; | ||||
|         for (unsigned i = m_eqs.size(); i-- > s.m_num_eqs; ) { | ||||
|             auto const& p = m_eqs[i]; | ||||
|             undo_eq(p.r1, p.n1, p.r2_num_parents); | ||||
|         }         | ||||
|         for (unsigned i = m_nodes.size(); i-- > s.m_num_nodes; ) { | ||||
|             enode* n = m_nodes[i]; | ||||
|             if (n->num_args() > 1) | ||||
|                 m_table.erase(n); | ||||
|             m_expr2enode[n->get_owner_id()] = nullptr; | ||||
|             n->~enode(); | ||||
|         } | ||||
|         m_inconsistent = s.m_inconsistent; | ||||
|         m_eqs.shrink(s.m_num_eqs); | ||||
|         m_nodes.shrink(s.m_num_nodes); | ||||
|         m_exprs.shrink(s.m_num_nodes); | ||||
|         m_scopes.shrink(old_lim);         | ||||
|         m_region.pop_scope(num_scopes);   | ||||
|     } | ||||
| 
 | ||||
|     void egraph::merge(enode* n1, enode* n2, justification j) { | ||||
|         SASSERT(m.get_sort(n1->get_owner()) == m.get_sort(n2->get_owner())); | ||||
|         TRACE("euf", tout << n1->get_owner_id() << " == " << n2->get_owner_id() << "\n";); | ||||
|         force_push(); | ||||
|         enode* r1 = n1->get_root(); | ||||
|         enode* r2 = n2->get_root(); | ||||
|         if (r1 == r2) | ||||
|             return; | ||||
|         if (r1->interpreted() && r2->interpreted()) { | ||||
|             set_conflict(r1, r2, j); | ||||
|             return; | ||||
|         } | ||||
|         if ((r1->class_size() > r2->class_size() && !r1->interpreted()) || r2->interpreted()) { | ||||
|             std::swap(r1, r2); | ||||
|             std::swap(n1, n2); | ||||
|         } | ||||
|         for (enode* p : enode_parents(n1))  | ||||
|             m_table.erase(p);             | ||||
|         for (enode* p : enode_parents(n2))  | ||||
|             m_table.erase(p);             | ||||
|         push_eq(r1, n1, r2->num_parents()); | ||||
|         merge_justification(n1, n2, j); | ||||
|         for (enode* c : enode_class(n1))  | ||||
|             c->m_root = r2; | ||||
|         std::swap(r1->m_next, r2->m_next); | ||||
|         r2->inc_class_size(r1->class_size());    | ||||
|         r2->m_parents.append(r1->m_parents); | ||||
|         m_worklist.push_back(r2); | ||||
|     } | ||||
| 
 | ||||
|     void egraph::propagate() { | ||||
|         m_new_eqs.reset(); | ||||
|         SASSERT(m_num_scopes == 0 || m_worklist.empty()); | ||||
|         unsigned head = 0, tail = m_worklist.size(); | ||||
|         while (head < tail && m.limit().inc() && !inconsistent()) { | ||||
|             TRACE("euf", tout << "iterate: " << head << " " << tail << "\n";); | ||||
|             for (unsigned i = head; i < tail && !inconsistent(); ++i) { | ||||
|                 enode* n = m_worklist[i]->get_root(); | ||||
|                 if (!n->is_marked1()) { | ||||
|                     n->mark1(); | ||||
|                     m_worklist[i] = n; | ||||
|                     reinsert(n); | ||||
|                 } | ||||
|             } | ||||
|             for (unsigned i = head; i < tail; ++i)  | ||||
|                 m_worklist[i]->unmark1(); | ||||
|             head = tail; | ||||
|             tail = m_worklist.size(); | ||||
|         } | ||||
|         m_worklist.reset(); | ||||
|         dedup_equalities(); | ||||
|     } | ||||
| 
 | ||||
|     void egraph::set_conflict(enode* n1, enode* n2, justification j) { | ||||
|         if (m_inconsistent) | ||||
|             return; | ||||
|         m_inconsistent = true; | ||||
|         m_n1 = n1; | ||||
|         m_n2 = n2; | ||||
|         m_justification = j; | ||||
|     } | ||||
| 
 | ||||
|     void egraph::merge_justification(enode* n1, enode* n2, justification j) { | ||||
|         SASSERT(n1->reaches(n1->get_root())); | ||||
|         n1->reverse_justification(); | ||||
|         n1->m_target = n2; | ||||
|         n1->m_justification = j; | ||||
|         SASSERT(n1->get_root()->reaches(n1)); | ||||
|     } | ||||
| 
 | ||||
|     void egraph::unmerge_justification(enode* n1) { | ||||
|         // r1 -> ..  -> n1 -> n2 -> ... -> r2
 | ||||
|         // where n2 = n1->m_target
 | ||||
|         SASSERT(n1->get_root()->reaches(n1)); | ||||
|         n1->m_target        = nullptr; | ||||
|         n1->m_justification = justification::axiom(); | ||||
|         n1->get_root()->reverse_justification(); | ||||
|         // ---------------
 | ||||
|         // n1 -> ... -> r1
 | ||||
|         // n2 -> ... -> r2
 | ||||
|         SASSERT(n1->reaches(n1->get_root())); | ||||
|         SASSERT(n1->get_root()->m_target == nullptr); | ||||
|     } | ||||
| 
 | ||||
|     template <typename T> | ||||
|     void egraph::explain(ptr_vector<T>& justifications) { | ||||
|         SASSERT(m_inconsistent); | ||||
|         SASSERT(m_todo.empty()); | ||||
|         m_todo.push_back(m_n1); | ||||
|         m_todo.push_back(m_n2); | ||||
|         auto push_congruence = [&](enode* p, enode* q) { | ||||
|             SASSERT(p->get_decl() == q->get_decl()); | ||||
|             for (enode* arg : enode_args(p)) | ||||
|                 m_todo.push_back(arg); | ||||
|             for (enode* arg : enode_args(q)) | ||||
|                 m_todo.push_back(arg); | ||||
|         }; | ||||
|         auto explain_node = [&](enode* n) { | ||||
|             if (!n->m_target) | ||||
|                 return; | ||||
|             if (n->is_marked1()) | ||||
|                 return; | ||||
|             n->mark1(); | ||||
|             if (n->m_justification.is_external()) | ||||
|                 justifications.push_back(n->m_justification.ext<T>()); | ||||
|             else if (n->m_justification.is_congruence())  | ||||
|                 push_congruence(n, n->m_target); | ||||
|             n = n->m_target; | ||||
|             if (!n->is_marked1()) | ||||
|                 m_todo.push_back(n); | ||||
|         }; | ||||
|         if (m_justification.is_external()) | ||||
|             justifications.push_back(m_justification.ext<T>()); | ||||
|         for (unsigned i = 0; i < m_todo.size(); ++i)  | ||||
|             explain_node(m_todo[i]); | ||||
|         for (enode* n : m_todo)  | ||||
|             n->unmark1(); | ||||
|     } | ||||
| 
 | ||||
|     void egraph::invariant() { | ||||
|         for (enode* n : m_nodes) | ||||
|             n->invariant(); | ||||
|     } | ||||
| 
 | ||||
|     std::ostream& egraph::display(std::ostream& out) const { | ||||
|         m_table.display(out); | ||||
|         for (enode* n : m_nodes) { | ||||
|             out << std::setw(5) | ||||
|                 << n->get_owner_id() << " := "; | ||||
|             out << n->get_root()->get_owner_id() << " "; | ||||
|             expr* f = n->get_owner(); | ||||
|             if (is_app(f))  | ||||
|                 out << to_app(f)->get_decl()->get_name() << " "; | ||||
|             else if (is_quantifier(f))  | ||||
|                 out << "q "; | ||||
|             else  | ||||
|                 out << "v "; | ||||
|             for (enode* arg : enode_args(n))  | ||||
|                 out << arg->get_owner_id() << " ";             | ||||
|             out << std::setw(20) << " parents: "; | ||||
|             for (enode* p : enode_parents(n))  | ||||
|                 out << p->get_owner_id() << " "; | ||||
|             out << "\n";             | ||||
|         } | ||||
|         return out; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										109
									
								
								src/ast/euf/euf_egraph.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/ast/euf/euf_egraph.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,109 @@ | |||
| /*++
 | ||||
| Copyright (c) 2020 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     euf_egraph.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     E-graph layer | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2020-08-23 | ||||
| 
 | ||||
| Notes: | ||||
|     | ||||
|     It relies on | ||||
|     - data structures form the (legacy) SMT solver. | ||||
|       - it still uses eager path compression. | ||||
|     - delayed congruence table reconstruction from egg.  | ||||
|       - it does not deduplicate parents. | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "ast/euf/euf_enode.h" | ||||
| #include "ast/euf/euf_etable.h" | ||||
| 
 | ||||
| namespace euf { | ||||
| 
 | ||||
|     struct add_eq_record { | ||||
|         enode* r1; | ||||
|         enode* n1; | ||||
|         unsigned r2_num_parents; | ||||
|         add_eq_record(enode* r1, enode* n1, unsigned r2_num_parents): | ||||
|             r1(r1), n1(n1), r2_num_parents(r2_num_parents) {} | ||||
|     }; | ||||
|      | ||||
|     class egraph {         | ||||
|         struct scope { | ||||
|             bool     m_inconsistent; | ||||
|             unsigned m_num_eqs; | ||||
|             unsigned m_num_nodes; | ||||
|         }; | ||||
|         ast_manager&           m; | ||||
|         region                 m_region; | ||||
|         enode_vector           m_worklist; | ||||
|         etable                 m_table; | ||||
|         svector<add_eq_record> m_eqs; | ||||
|         svector<scope>         m_scopes; | ||||
|         enode_vector           m_expr2enode; | ||||
|         enode_vector           m_nodes; | ||||
|         expr_ref_vector        m_exprs; | ||||
|         unsigned               m_num_scopes { 0 }; | ||||
|         bool                   m_inconsistent { false }; | ||||
|         enode                  *m_n1 { nullptr }; | ||||
|         enode                  *m_n2 { nullptr }; | ||||
|         justification          m_justification; | ||||
|         enode_vector           m_new_eqs; | ||||
|         enode_vector           m_todo; | ||||
| 
 | ||||
|         void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) { | ||||
|             m_eqs.push_back(add_eq_record(r1, n1, r2_num_parents)); | ||||
|         } | ||||
|         void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents); | ||||
|         enode* mk_enode(expr* f, enode * const* args); | ||||
|         void reinsert(enode* n); | ||||
|         void force_push(); | ||||
|         void set_conflict(enode* n1, enode* n2, justification j); | ||||
|         void merge(enode* n1, enode* n2, justification j); | ||||
|         void merge_justification(enode* n1, enode* n2, justification j); | ||||
|         void unmerge_justification(enode* n1); | ||||
|         void dedup_equalities(); | ||||
|         bool is_equality(enode* n) const; | ||||
|         void reinsert_equality(enode* p); | ||||
|         void update_children(enode* n); | ||||
|     public: | ||||
|         egraph(ast_manager& m): m(m), m_table(m), m_exprs(m) {} | ||||
|         enode* find(expr* f) { return m_expr2enode.get(f->get_id(), nullptr); } | ||||
|         enode* mk(expr* f, enode *const* args); | ||||
|         void push() { ++m_num_scopes; } | ||||
|         void pop(unsigned num_scopes); | ||||
|         /**
 | ||||
|            \brief merge nodes, all effects are deferred to the propagation step. | ||||
|          */ | ||||
|         void merge(enode* n1, enode* n2, void* reason) { merge(n1, n2, justification::external(reason)); }         | ||||
| 
 | ||||
|         /**
 | ||||
|            \brief propagate set of merges.  | ||||
|            This call may detect an inconsistency. Then inconsistent() is true. | ||||
|            Use then explain() to extract an explanation for the conflict. | ||||
| 
 | ||||
|            It may also infer new implied equalities, when the roots of the  | ||||
|            equated nodes are merged. Use then new_eqs() to extract the vector | ||||
|            of new equalities. | ||||
|         */ | ||||
|         void propagate(); | ||||
|         bool inconsistent() const { return m_inconsistent; } | ||||
|         enode_vector const& new_eqs() const { return m_new_eqs; } | ||||
|         template <typename T> | ||||
|         void explain(ptr_vector<T>& justifications); | ||||
| 
 | ||||
|         void invariant(); | ||||
|         std::ostream& display(std::ostream& out) const;         | ||||
|     }; | ||||
| 
 | ||||
|     inline std::ostream& operator<<(std::ostream& out, egraph const& g) { return g.display(out); } | ||||
| } | ||||
							
								
								
									
										101
									
								
								src/ast/euf/euf_enode.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/ast/euf/euf_enode.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,101 @@ | |||
| /*++
 | ||||
| Copyright (c) 2020 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     euf_enode.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     enode layer | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2020-08-23 | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #include "ast/euf/euf_enode.h" | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| namespace euf { | ||||
| 
 | ||||
|     void enode::invariant() { | ||||
|         unsigned class_size = 0; | ||||
|         bool     found_root = false; | ||||
|         bool     found_this = false;         | ||||
|         for (enode* c : enode_class(this)) { | ||||
|             VERIFY(c->m_root == m_root); | ||||
|             found_root |= c == m_root; | ||||
|             found_this |= c == this; | ||||
|         } | ||||
|         VERIFY(found_root); | ||||
|         VERIFY(found_this); | ||||
|         VERIFY(this != m_root || class_size == m_class_size); | ||||
|         if (this == m_root) { | ||||
|             for (enode* p : enode_parents(this)) { | ||||
|                 bool found = false; | ||||
|                 for (enode* arg : enode_args(p)) { | ||||
|                     found |= arg->get_root() == this; | ||||
|                 } | ||||
|                 VERIFY(found); | ||||
|             } | ||||
|             for (enode* c : enode_class(this)) { | ||||
|                 if (c == this) | ||||
|                     continue; | ||||
|                 for (enode* p : enode_parents(c)) { | ||||
|                     bool found = false; | ||||
|                     for (enode* q : enode_parents(this)) { | ||||
|                         found |= p->congruent(q); | ||||
|                     } | ||||
|                     VERIFY(found); | ||||
|                 } | ||||
|             } | ||||
|         }         | ||||
|     } | ||||
| 
 | ||||
|     bool enode::congruent(enode* n) const { | ||||
|         if (get_decl() != n->get_decl())  | ||||
|             return false; | ||||
|         if (num_args() != n->num_args()) | ||||
|             return false; | ||||
|         SASSERT(!m_commutative || num_args() == 2); | ||||
|         if (m_commutative && | ||||
|             get_arg(0)->get_root() == n->get_arg(1)->get_root() && | ||||
|             get_arg(1)->get_root() == n->get_arg(0)->get_root()) | ||||
|             return true; | ||||
|         for (unsigned i = num_args(); i-- > 0; )  | ||||
|             if (get_arg(i)->get_root() != n->get_arg(i)->get_root()) | ||||
|                 return false; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     bool enode::reaches(enode* n) const { | ||||
|         enode const* r = this; | ||||
|         while (r) { | ||||
|             if (r == n) | ||||
|                 return true; | ||||
|             r = r->m_target; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     void enode::reverse_justification() { | ||||
|         enode* curr = m_target; | ||||
|         enode* prev = this; | ||||
|         justification js = m_justification; | ||||
|         prev->m_target = nullptr; | ||||
|         prev->m_justification = justification::axiom(); | ||||
|         while (curr != nullptr) { | ||||
| 
 | ||||
|             enode* new_curr = curr->m_target; | ||||
|             justification new_js = curr->m_justification; | ||||
|             curr->m_target = prev; | ||||
|             curr->m_justification = js; | ||||
|             prev = curr; | ||||
|             js = new_js; | ||||
|             curr = new_curr; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										154
									
								
								src/ast/euf/euf_enode.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/ast/euf/euf_enode.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,154 @@ | |||
| /*++
 | ||||
| Copyright (c) 2020 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     euf_enode.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     enode layer | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2020-08-23 | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #include "util/vector.h" | ||||
| #include "ast/ast.h" | ||||
| #include "ast/euf/euf_justification.h" | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| namespace euf { | ||||
| 
 | ||||
|     class enode; | ||||
| 
 | ||||
|     typedef ptr_vector<enode> enode_vector; | ||||
|     typedef std::pair<enode*,enode*> enode_pair; | ||||
|     typedef svector<enode_pair> enode_pair_vector; | ||||
| 
 | ||||
|     class enode { | ||||
|         expr*         m_owner; | ||||
|         bool          m_mark1 { false }; | ||||
|         bool          m_mark2 { false }; | ||||
|         bool          m_commutative { false }; | ||||
|         bool          m_update_children { false }; | ||||
|         bool          m_interpreted { false }; | ||||
|         unsigned      m_class_size { 1 }; | ||||
|         unsigned      m_table_id { UINT_MAX }; | ||||
|         enode_vector  m_parents; | ||||
|         enode*        m_next; | ||||
|         enode*        m_root; | ||||
|         enode*        m_target { nullptr }; | ||||
|         justification m_justification; | ||||
|         enode*        m_args[0];         | ||||
| 
 | ||||
|         friend class enode_args; | ||||
|         friend class enode_parents; | ||||
|         friend class enode_class; | ||||
|         friend class etable; | ||||
|         friend class egraph; | ||||
| 
 | ||||
|         static unsigned get_enode_size(unsigned num_args) { | ||||
|             return sizeof(enode) + num_args * sizeof(enode*); | ||||
|         } | ||||
| 
 | ||||
|         static enode* mk(region& r, expr* f, unsigned num_args, enode* const* args) { | ||||
|             void* mem = r.allocate(get_enode_size(num_args)); | ||||
|             enode* n = new (mem) enode(); | ||||
|             n->m_owner = f; | ||||
|             n->m_next = n; | ||||
|             n->m_root = n; | ||||
|             n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative(); | ||||
|             for (unsigned i = 0; i < num_args; ++i) { | ||||
|                 n->m_args[i] = args[i]; | ||||
|             } | ||||
|             return n; | ||||
|         } | ||||
|          | ||||
|         void set_update_children() { m_update_children = true; } | ||||
|     public: | ||||
|         ~enode() {  | ||||
|             SASSERT(m_root == this);  | ||||
|             SASSERT(class_size() == 1);  | ||||
|             if (m_update_children) { | ||||
|                 for (unsigned i = 0; i < num_args(); ++i) { | ||||
|                     SASSERT(m_args[i]->get_root()->m_parents.back() == this); | ||||
|                     m_args[i]->get_root()->m_parents.pop_back(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         enode* const* args() const { return m_args; } | ||||
|         unsigned num_args() const { return is_app(m_owner) ? to_app(m_owner)->get_num_args() : 0; }                 | ||||
|         unsigned num_parents() const { return m_parents.size(); } | ||||
|         bool interpreted() const { return m_interpreted; } | ||||
|         void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; } | ||||
| 
 | ||||
|         enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }         | ||||
|         unsigned hash() const { return m_owner->hash(); } | ||||
|         func_decl* get_decl() const { return is_app(m_owner) ? to_app(m_owner)->get_decl() : nullptr; }  | ||||
|         unsigned get_table_id() const { return m_table_id; } | ||||
|         void     set_table_id(unsigned t) { m_table_id = t; } | ||||
| 
 | ||||
|         void mark1() { m_mark1 = true; } | ||||
|         void unmark1() { m_mark1 = false; } | ||||
|         bool is_marked1() { return m_mark1; } | ||||
|         void add_parent(enode* p) { m_parents.push_back(p); } | ||||
|         unsigned class_size() const { return m_class_size; } | ||||
|         enode* get_root() const { return m_root; } | ||||
|         expr*  get_owner() const { return m_owner; } | ||||
|         unsigned get_owner_id() const { return m_owner->get_id(); } | ||||
|         void inc_class_size(unsigned n) { m_class_size += n; } | ||||
|         void dec_class_size(unsigned n) { m_class_size -= n; } | ||||
| 
 | ||||
|         void reverse_justification(); | ||||
|         bool reaches(enode* n) const; | ||||
| 
 | ||||
|         enode* const* begin_parents() const { return m_parents.begin(); } | ||||
|         enode* const* end_parents() const { return m_parents.end(); } | ||||
| 
 | ||||
|         void invariant(); | ||||
|         bool congruent(enode* n) const; | ||||
|     }; | ||||
| 
 | ||||
|     class enode_args { | ||||
|         enode& n; | ||||
|     public: | ||||
|         enode_args(enode& _n):n(_n) {} | ||||
|         enode_args(enode* _n):n(*_n) {} | ||||
|         enode* const* begin() const { return n.m_args; } | ||||
|         enode* const* end()   const { return n.m_args + n.num_args(); } | ||||
|     }; | ||||
| 
 | ||||
|     class enode_parents { | ||||
|         enode const& n; | ||||
|     public: | ||||
|         enode_parents(enode const& _n):n(_n) {} | ||||
|         enode_parents(enode const* _n):n(*_n) {} | ||||
|         enode* const* begin() const { return n.m_parents.begin(); } | ||||
|         enode* const* end()   const { return n.m_parents.end(); } | ||||
|     }; | ||||
| 
 | ||||
|     class enode_class { | ||||
|         enode & n; | ||||
|     public: | ||||
|         class iterator { | ||||
|             enode* m_first; | ||||
|             enode* m_last; | ||||
|         public: | ||||
|             iterator(enode* n, enode* m): m_first(n), m_last(m) {}  | ||||
|             enode* operator*() { return m_first; } | ||||
|             iterator& operator++() { if (!m_last) m_last = m_first; m_first = m_first->m_next; return *this; } | ||||
|             iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } | ||||
|             bool operator==(iterator const& other) const { return m_last == other.m_last && m_first == other.m_first; } | ||||
|             bool operator!=(iterator const& other) const { return !(*this == other); }             | ||||
|         }; | ||||
|         enode_class(enode & _n):n(_n) {} | ||||
|         enode_class(enode * _n):n(*_n) {} | ||||
|         iterator begin() const { return iterator(&n, nullptr); } | ||||
|         iterator end() const { return iterator(&n, &n); } | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										252
									
								
								src/ast/euf/euf_etable.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								src/ast/euf/euf_etable.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,252 @@ | |||
| /*++
 | ||||
| Copyright (c) 2006 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     euf_etable.cpp | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Leonardo de Moura (leonardo) 2008-02-19. | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
|     Ported from smt_cg_table.cpp | ||||
| 
 | ||||
| --*/ | ||||
| #include "ast/euf/euf_etable.h" | ||||
| #include "ast/ast_pp.h" | ||||
| #include "ast/ast_ll_pp.h" | ||||
| 
 | ||||
| namespace euf { | ||||
| 
 | ||||
|     // one table per func_decl implementation
 | ||||
|     unsigned etable::cg_hash::operator()(enode * n) const { | ||||
|         SASSERT(n->get_decl()->is_flat_associative() || n->num_args() >= 3); | ||||
|         unsigned a, b, c; | ||||
|         a = b = 0x9e3779b9; | ||||
|         c = 11;     | ||||
|          | ||||
|         unsigned i = n->num_args(); | ||||
|         while (i >= 3) { | ||||
|             i--; | ||||
|             a += n->get_arg(i)->get_root()->hash(); | ||||
|             i--; | ||||
|             b += n->get_arg(i)->get_root()->hash(); | ||||
|             i--; | ||||
|             c += n->get_arg(i)->get_root()->hash(); | ||||
|             mix(a, b, c); | ||||
|         } | ||||
|          | ||||
|         switch (i) { | ||||
|         case 2: | ||||
|             b += n->get_arg(1)->get_root()->hash(); | ||||
|             Z3_fallthrough; | ||||
|         case 1: | ||||
|             c += n->get_arg(0)->get_root()->hash(); | ||||
|         } | ||||
|         mix(a, b, c); | ||||
|         return c; | ||||
|     } | ||||
| 
 | ||||
|     bool etable::cg_eq::operator()(enode * n1, enode * n2) const { | ||||
|         SASSERT(n1->get_decl() == n2->get_decl()); | ||||
|         unsigned num = n1->num_args(); | ||||
|         if (num != n2->num_args()) { | ||||
|             return false; | ||||
|         } | ||||
|         for (unsigned i = 0; i < num; i++)  | ||||
|             if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) | ||||
|                 return false; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     etable::etable(ast_manager & m): | ||||
|         m_manager(m) { | ||||
|     } | ||||
| 
 | ||||
|     etable::~etable() { | ||||
|         reset(); | ||||
|     } | ||||
| 
 | ||||
|     void * etable::mk_table_for(func_decl * d) { | ||||
|         void * r; | ||||
|         SASSERT(d->get_arity() >= 1); | ||||
|         switch (d->get_arity()) { | ||||
|         case 1: | ||||
|             r = TAG(void*, alloc(unary_table), UNARY); | ||||
|             SASSERT(GET_TAG(r) == UNARY); | ||||
|             return r; | ||||
|         case 2: | ||||
|             if (d->is_flat_associative()) { | ||||
|                 // applications of declarations that are flat-assoc (e.g., +) may have many arguments.
 | ||||
|                 r = TAG(void*, alloc(table), NARY); | ||||
|                 SASSERT(GET_TAG(r) == NARY); | ||||
|                 return r; | ||||
|             } | ||||
|             else if (d->is_commutative()) { | ||||
|                 r = TAG(void*, alloc(comm_table, cg_comm_hash(), cg_comm_eq(m_commutativity)), BINARY_COMM); | ||||
|                 SASSERT(GET_TAG(r) == BINARY_COMM); | ||||
|                 return r; | ||||
|             } | ||||
|             else { | ||||
|                 r = TAG(void*, alloc(binary_table), BINARY); | ||||
|                 SASSERT(GET_TAG(r) == BINARY); | ||||
|                 return r; | ||||
|             } | ||||
|         default:  | ||||
|             r = TAG(void*, alloc(table), NARY); | ||||
|             SASSERT(GET_TAG(r) == NARY); | ||||
|             return r; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     unsigned etable::set_table_id(enode * n) { | ||||
|         func_decl * f = n->get_decl(); | ||||
|         unsigned tid; | ||||
|         if (!m_func_decl2id.find(f, tid)) { | ||||
|             tid = m_tables.size(); | ||||
|             m_func_decl2id.insert(f, tid); | ||||
|             m_manager.inc_ref(f); | ||||
|             SASSERT(tid <= m_tables.size()); | ||||
|             m_tables.push_back(mk_table_for(f)); | ||||
|         } | ||||
|         SASSERT(tid < m_tables.size()); | ||||
|         n->set_table_id(tid); | ||||
|         DEBUG_CODE({ | ||||
|             unsigned tid_prime; | ||||
|             SASSERT(m_func_decl2id.find(n->get_decl(), tid_prime) && tid == tid_prime); | ||||
|         }); | ||||
|         return tid; | ||||
|     } | ||||
|      | ||||
|     void etable::reset() { | ||||
|         for (void* t : m_tables) { | ||||
|             switch (GET_TAG(t)) { | ||||
|             case UNARY: | ||||
|                 dealloc(UNTAG(unary_table*, t)); | ||||
|                 break; | ||||
|             case BINARY: | ||||
|                 dealloc(UNTAG(binary_table*, t)); | ||||
|                 break; | ||||
|             case BINARY_COMM: | ||||
|                 dealloc(UNTAG(comm_table*, t)); | ||||
|                 break; | ||||
|             case NARY: | ||||
|                 dealloc(UNTAG(table*, t)); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         m_tables.reset(); | ||||
|         for (auto const& kv : m_func_decl2id) { | ||||
|             m_manager.dec_ref(kv.m_key); | ||||
|         } | ||||
|         m_func_decl2id.reset(); | ||||
|     } | ||||
| 
 | ||||
|     void etable::display(std::ostream & out) const { | ||||
|         for (auto const& kv : m_func_decl2id) { | ||||
|             void * t = m_tables[kv.m_value]; | ||||
|             out << mk_pp(kv.m_key, m_manager) << ": "; | ||||
|             switch (GET_TAG(t)) { | ||||
|             case UNARY:  | ||||
|                 display_unary(out, t); | ||||
|                 break;             | ||||
|             case BINARY:  | ||||
|                 display_binary(out, t); | ||||
|                 break; | ||||
|             case BINARY_COMM:  | ||||
|                 display_binary_comm(out, t); | ||||
|                 break;             | ||||
|             case NARY:  | ||||
|                 display_nary(out, t); | ||||
|                 break; | ||||
|             } | ||||
|         }         | ||||
|     } | ||||
| 
 | ||||
|     void etable::display_binary(std::ostream& out, void* t) const { | ||||
|         binary_table* tb = UNTAG(binary_table*, t); | ||||
|         out << "b "; | ||||
|         for (enode* n : *tb) { | ||||
|             out << n->get_owner_id() << " " << cg_binary_hash()(n) << " "; | ||||
|         } | ||||
|         out << "\n"; | ||||
|     } | ||||
|      | ||||
|     void etable::display_binary_comm(std::ostream& out, void* t) const { | ||||
|         comm_table* tb = UNTAG(comm_table*, t); | ||||
|         out << "bc "; | ||||
|         for (enode* n : *tb) { | ||||
|             out << n->get_owner_id() << " "; | ||||
|         } | ||||
|         out << "\n"; | ||||
|     } | ||||
|      | ||||
|     void etable::display_unary(std::ostream& out, void* t) const { | ||||
|         unary_table* tb = UNTAG(unary_table*, t); | ||||
|         out << "un "; | ||||
|         for (enode* n : *tb) { | ||||
|             out << n->get_owner_id() << " "; | ||||
|         } | ||||
|         out << "\n"; | ||||
|     } | ||||
|      | ||||
|     void etable::display_nary(std::ostream& out, void* t) const { | ||||
|         table* tb = UNTAG(table*, t); | ||||
|         out << "nary "; | ||||
|         for (enode* n : *tb) { | ||||
|             out << n->get_owner_id() << " "; | ||||
|         } | ||||
|         out << "\n"; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     enode_bool_pair etable::insert(enode * n) { | ||||
|         // it doesn't make sense to insert a constant.
 | ||||
|         SASSERT(n->num_args() > 0); | ||||
|         SASSERT(!m_manager.is_and(n->get_owner())); | ||||
|         SASSERT(!m_manager.is_or(n->get_owner())); | ||||
|         enode * n_prime; | ||||
|         void * t = get_table(n);  | ||||
|         switch (static_cast<table_kind>(GET_TAG(t))) { | ||||
|         case UNARY: | ||||
|             n_prime = UNTAG(unary_table*, t)->insert_if_not_there(n); | ||||
|             return enode_bool_pair(n_prime, false); | ||||
|         case BINARY: | ||||
|             n_prime = UNTAG(binary_table*, t)->insert_if_not_there(n); | ||||
|             TRACE("euf", tout << "insert: " << n->get_owner_id() << " " << cg_binary_hash()(n) << " inserted: " << (n == n_prime) << " " << n_prime->get_owner_id() << "\n"; | ||||
|                   display_binary(tout, t); tout << "contains_ptr: " << contains_ptr(n) << "\n";);  | ||||
|             return enode_bool_pair(n_prime, false); | ||||
|         case BINARY_COMM: | ||||
|             m_commutativity = false; | ||||
|             n_prime = UNTAG(comm_table*, t)->insert_if_not_there(n); | ||||
|             return enode_bool_pair(n_prime, m_commutativity); | ||||
|         default: | ||||
|             n_prime = UNTAG(table*, t)->insert_if_not_there(n); | ||||
|             return enode_bool_pair(n_prime, false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void etable::erase(enode * n) { | ||||
|         SASSERT(n->num_args() > 0); | ||||
|         void * t = get_table(n);  | ||||
|         switch (static_cast<table_kind>(GET_TAG(t))) { | ||||
|         case UNARY: | ||||
|             UNTAG(unary_table*, t)->erase(n); | ||||
|             break; | ||||
|         case BINARY: | ||||
|             TRACE("euf", tout << "erase: " << n->get_owner_id() << " " << cg_binary_hash()(n) << " contains: " << contains_ptr(n) << "\n";); | ||||
|             UNTAG(binary_table*, t)->erase(n); | ||||
|             break; | ||||
|         case BINARY_COMM: | ||||
|             UNTAG(comm_table*, t)->erase(n); | ||||
|             break; | ||||
|         default: | ||||
|             UNTAG(table*, t)->erase(n); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
							
								
								
									
										217
									
								
								src/ast/euf/euf_etable.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/ast/euf/euf_etable.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,217 @@ | |||
| /*++
 | ||||
| Copyright (c) 2006 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     euf_etable.h | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Leonardo de Moura (leonardo) 2008-02-19. | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
|     copied from smt_cg_table | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ast/euf/euf_enode.h" | ||||
| #include "util/hashtable.h" | ||||
| #include "util/chashtable.h" | ||||
| 
 | ||||
| namespace euf { | ||||
| 
 | ||||
|     typedef std::pair<enode *, bool> enode_bool_pair; | ||||
|      | ||||
|     // one table per function symbol
 | ||||
| 
 | ||||
|     /**
 | ||||
|        \brief Congruence table. | ||||
|     */ | ||||
|     class etable { | ||||
|         struct cg_unary_hash { | ||||
|             unsigned operator()(enode * n) const { | ||||
|                 SASSERT(n->num_args() == 1); | ||||
|                 return n->get_arg(0)->get_root()->hash(); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         struct cg_unary_eq { | ||||
|             bool operator()(enode * n1, enode * n2) const { | ||||
|                 SASSERT(n1->num_args() == 1); | ||||
|                 SASSERT(n2->num_args() == 1); | ||||
|                 SASSERT(n1->get_decl() == n2->get_decl()); | ||||
|                 return n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root(); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         typedef chashtable<enode *, cg_unary_hash, cg_unary_eq> unary_table; | ||||
|          | ||||
|         struct cg_binary_hash { | ||||
|             unsigned operator()(enode * n) const { | ||||
|                 SASSERT(n->num_args() == 2); | ||||
|                 return combine_hash(n->get_arg(0)->get_root()->hash(), n->get_arg(1)->get_root()->hash()); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         struct cg_binary_eq { | ||||
|             bool operator()(enode * n1, enode * n2) const { | ||||
|                 SASSERT(n1->num_args() == 2); | ||||
|                 SASSERT(n2->num_args() == 2); | ||||
|                 SASSERT(n1->get_decl() == n2->get_decl()); | ||||
|                 return  | ||||
|                     n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root() && | ||||
|                     n1->get_arg(1)->get_root() == n2->get_arg(1)->get_root(); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         typedef chashtable<enode*, cg_binary_hash, cg_binary_eq> binary_table; | ||||
|          | ||||
|         struct cg_comm_hash { | ||||
|             unsigned operator()(enode * n) const { | ||||
|                 SASSERT(n->num_args() == 2); | ||||
|                 unsigned h1 = n->get_arg(0)->get_root()->hash(); | ||||
|                 unsigned h2 = n->get_arg(1)->get_root()->hash(); | ||||
|                 if (h1 > h2) | ||||
|                     std::swap(h1, h2); | ||||
|                 return hash_u((h1 << 16) | (h2 & 0xFFFF)); | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|         struct cg_comm_eq { | ||||
|             bool & m_commutativity; | ||||
|             cg_comm_eq(bool & c):m_commutativity(c) {} | ||||
|             bool operator()(enode * n1, enode * n2) const { | ||||
|                 SASSERT(n1->num_args() == 2); | ||||
|                 SASSERT(n2->num_args() == 2); | ||||
|                 SASSERT(n1->get_decl() == n2->get_decl()); | ||||
|                 enode * c1_1 = n1->get_arg(0)->get_root(); | ||||
|                 enode * c1_2 = n1->get_arg(1)->get_root(); | ||||
|                 enode * c2_1 = n2->get_arg(0)->get_root(); | ||||
|                 enode * c2_2 = n2->get_arg(1)->get_root(); | ||||
|                 if (c1_1 == c2_1 && c1_2 == c2_2) { | ||||
|                     return true; | ||||
|                 } | ||||
|                 if (c1_1 == c2_2 && c1_2 == c2_1) { | ||||
|                     m_commutativity = true; | ||||
|                     return true; | ||||
|                 } | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         typedef chashtable<enode*, cg_comm_hash, cg_comm_eq> comm_table; | ||||
| 
 | ||||
|         struct cg_hash { | ||||
|             unsigned operator()(enode * n) const; | ||||
|         }; | ||||
| 
 | ||||
|         struct cg_eq { | ||||
|             bool operator()(enode * n1, enode * n2) const; | ||||
|         }; | ||||
| 
 | ||||
|         typedef chashtable<enode*, cg_hash, cg_eq> table; | ||||
| 
 | ||||
|         ast_manager &                 m_manager; | ||||
|         bool                          m_commutativity; //!< true if the last found congruence used commutativity
 | ||||
|         ptr_vector<void>              m_tables; | ||||
|         obj_map<func_decl, unsigned>  m_func_decl2id; | ||||
| 
 | ||||
|         enum table_kind { | ||||
|             UNARY, | ||||
|             BINARY, | ||||
|             BINARY_COMM, | ||||
|             NARY | ||||
|         }; | ||||
| 
 | ||||
|         void * mk_table_for(func_decl * d); | ||||
|         unsigned set_table_id(enode * n); | ||||
|          | ||||
|         void * get_table(enode * n) { | ||||
|             unsigned tid = n->get_table_id(); | ||||
|             if (tid == UINT_MAX) | ||||
|                 tid = set_table_id(n); | ||||
|             SASSERT(tid < m_tables.size()); | ||||
|             return m_tables[tid]; | ||||
|         } | ||||
| 
 | ||||
|         void display_binary(std::ostream& out, void* t) const; | ||||
|         void display_binary_comm(std::ostream& out, void* t) const; | ||||
|         void display_unary(std::ostream& out, void* t) const; | ||||
|         void display_nary(std::ostream& out, void* t) const; | ||||
| 
 | ||||
|     public: | ||||
|         etable(ast_manager & m); | ||||
| 
 | ||||
|         ~etable(); | ||||
| 
 | ||||
|         /**
 | ||||
|            \brief Try to insert n into the table. If the table already | ||||
|            contains an element n' congruent to n, then do nothing and | ||||
|            return n' and a boolean indicating whether n and n' are congruence | ||||
|            modulo commutativity, otherwise insert n and return (n,false). | ||||
|         */ | ||||
|         enode_bool_pair insert(enode * n); | ||||
| 
 | ||||
|         void erase(enode * n); | ||||
| 
 | ||||
|         bool contains(enode * n) const { | ||||
|             SASSERT(n->num_args() > 0); | ||||
|             void * t = const_cast<etable*>(this)->get_table(n);  | ||||
|             switch (static_cast<table_kind>(GET_TAG(t))) { | ||||
|             case UNARY: | ||||
|                 return UNTAG(unary_table*, t)->contains(n); | ||||
|             case BINARY: | ||||
|                 return UNTAG(binary_table*, t)->contains(n); | ||||
|             case BINARY_COMM: | ||||
|                 return UNTAG(comm_table*, t)->contains(n); | ||||
|             default: | ||||
|                 return UNTAG(table*, t)->contains(n); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         enode * find(enode * n) const { | ||||
|             SASSERT(n->num_args() > 0); | ||||
|             enode * r = nullptr; | ||||
|             void * t = const_cast<etable*>(this)->get_table(n);  | ||||
|             switch (static_cast<table_kind>(GET_TAG(t))) { | ||||
|             case UNARY: | ||||
|                 return UNTAG(unary_table*, t)->find(n, r) ? r : nullptr; | ||||
|             case BINARY: | ||||
|                 return UNTAG(binary_table*, t)->find(n, r) ? r : nullptr; | ||||
|             case BINARY_COMM: | ||||
|                 return UNTAG(comm_table*, t)->find(n, r) ? r : nullptr; | ||||
|             default: | ||||
|                 return UNTAG(table*, t)->find(n, r) ? r : nullptr; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         bool contains_ptr(enode * n) const { | ||||
|             enode * r; | ||||
|             SASSERT(n->num_args() > 0); | ||||
|             void * t = const_cast<etable*>(this)->get_table(n);  | ||||
|             switch (static_cast<table_kind>(GET_TAG(t))) { | ||||
|             case UNARY: | ||||
|                 return UNTAG(unary_table*, t)->find(n, r) && n == r; | ||||
|             case BINARY: | ||||
|                 return UNTAG(binary_table*, t)->find(n, r) && n == r; | ||||
|             case BINARY_COMM: | ||||
|                 return UNTAG(comm_table*, t)->find(n, r) && n == r; | ||||
|             default: | ||||
|                 return UNTAG(table*, t)->find(n, r) && n == r; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         void reset(); | ||||
| 
 | ||||
|         void display(std::ostream & out) const; | ||||
| 
 | ||||
|     }; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										59
									
								
								src/ast/euf/euf_justification.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/ast/euf/euf_justification.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| /*++
 | ||||
| Copyright (c) 2020 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     euf_justification.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     justification structure for euf | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2020-08-23 | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| namespace euf { | ||||
| 
 | ||||
|     class justification { | ||||
|         enum kind_t { | ||||
|             axiom_t,  | ||||
|             congruence_t,  | ||||
|             external_t | ||||
|         }; | ||||
|         kind_t m_kind; | ||||
|         bool   m_comm; | ||||
|         void*  m_external; | ||||
|         justification(bool comm): | ||||
|             m_kind(congruence_t), | ||||
|             m_comm(comm), | ||||
|             m_external(nullptr) | ||||
|         {} | ||||
| 
 | ||||
|         justification(void* ext): | ||||
|             m_kind(external_t), | ||||
|             m_comm(false), | ||||
|             m_external(ext) | ||||
|         {} | ||||
| 
 | ||||
|     public: | ||||
|         justification(): | ||||
|             m_kind(axiom_t), | ||||
|             m_comm(false), | ||||
|             m_external(nullptr) | ||||
|         {} | ||||
| 
 | ||||
|         static justification axiom() { return justification(); } | ||||
|         static justification congruence(bool c) { return justification(c); } | ||||
|         static justification external(void* ext) { return justification(ext); } | ||||
|          | ||||
|         bool   is_external() const { return m_kind == external_t; } | ||||
|         bool   is_congruence() const { return m_kind == congruence_t; } | ||||
|         template <typename T> | ||||
|         T*  ext() const { SASSERT(is_external()); return static_cast<T*>(m_external); }             | ||||
|     }; | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue