mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32:28 +00:00 
			
		
		
		
	gc-ing definitions leads to unsoundness when they are not replayed. Instead of attempting to replay definitions theory internalization is irredundant by default. This is also the old solver behavior where TH_LEMMA is essentially never used, but is valid for top-level theory lemmas.
		
			
				
	
	
		
			227 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			227 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*++
 | |
| Copyright (c) 2020 Microsoft Corporation
 | |
| 
 | |
| Module Name:
 | |
| 
 | |
|     euf_ackerman.cpp
 | |
| 
 | |
| Abstract:
 | |
| 
 | |
|     Ackerman reduction plugin for EUF
 | |
| 
 | |
| Author:
 | |
| 
 | |
|     Nikolaj Bjorner (nbjorner) 2020-08-28
 | |
| 
 | |
| --*/
 | |
| 
 | |
| #include "sat/smt/euf_solver.h"
 | |
| #include "sat/smt/euf_ackerman.h"
 | |
| 
 | |
| namespace euf {
 | |
| 
 | |
|     ackerman::ackerman(solver& ctx, ast_manager& m): ctx(ctx), m(m) {
 | |
|         new_tmp();
 | |
|     }
 | |
| 
 | |
|     ackerman::~ackerman() {
 | |
|         reset();
 | |
|         dealloc(m_tmp_inference);
 | |
|     }
 | |
| 
 | |
|     void ackerman::reset() {
 | |
|         for (inference* inf : m_table) {
 | |
|             m.dec_ref(inf->a);
 | |
|             m.dec_ref(inf->b);
 | |
|             m.dec_ref(inf->c);
 | |
|             dealloc(inf);
 | |
|         }
 | |
|         m_table.reset();
 | |
|         m_queue = nullptr;        
 | |
|     }
 | |
| 
 | |
|     void ackerman::insert(expr* a, expr* b, expr* lca) {
 | |
|         if (a->get_id() > b->get_id())
 | |
|             std::swap(a, b);
 | |
|         inference& inf = *m_tmp_inference;
 | |
|         inf.a = a;
 | |
|         inf.b = b;
 | |
|         inf.c = lca;
 | |
|         inf.is_cc = false;
 | |
|         inf.m_count = 0;
 | |
|         insert();
 | |
|     }
 | |
| 
 | |
|     void ackerman::insert(app* a, app* b) {
 | |
|         if (a->get_id() > b->get_id())
 | |
|             std::swap(a, b);
 | |
|         inference& inf = *m_tmp_inference;
 | |
|         inf.a = a;
 | |
|         inf.b = b;
 | |
|         inf.c = nullptr;
 | |
|         inf.is_cc = true;
 | |
|         inf.m_count = 0;
 | |
|         insert();
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void ackerman::insert() {
 | |
|         inference* inf = m_tmp_inference;
 | |
|         inference* other = m_table.insert_if_not_there(inf);
 | |
|         if (other == inf) {
 | |
|             m.inc_ref(inf->a);
 | |
|             m.inc_ref(inf->b);
 | |
|             m.inc_ref(inf->c);
 | |
|             new_tmp();        
 | |
|         }
 | |
|         other->m_count++;    
 | |
|         inference::push_to_front(m_queue, other);
 | |
|     }
 | |
| 
 | |
|     void ackerman::remove(inference* inf) {
 | |
|         inference::remove_from(m_queue, inf);
 | |
|         m_table.erase(inf);
 | |
|         m.dec_ref(inf->a);
 | |
|         m.dec_ref(inf->b);
 | |
|         m.dec_ref(inf->c);
 | |
|         dealloc(inf);
 | |
|     }
 | |
| 
 | |
|     void ackerman::new_tmp() {
 | |
|         m_tmp_inference = alloc(inference);
 | |
|         m_tmp_inference->init(m_tmp_inference);
 | |
|     }
 | |
| 
 | |
|     bool ackerman::enable_cc(app* a, app* b) {
 | |
|         if (!ctx.enable_ackerman_axioms(a))
 | |
|             return false;
 | |
|         if (!ctx.enable_ackerman_axioms(b))
 | |
|             return false;
 | |
|         for (expr* arg : *a)
 | |
|             if (!ctx.enable_ackerman_axioms(arg))
 | |
|                 return false;
 | |
|         for (expr* arg : *b)
 | |
|             if (!ctx.enable_ackerman_axioms(arg))
 | |
|                 return false;
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool ackerman::enable_eq(expr* a, expr* b, expr* c) {
 | |
|         return ctx.enable_ackerman_axioms(a) && 
 | |
|             ctx.enable_ackerman_axioms(b) && 
 | |
|             ctx.enable_ackerman_axioms(c);           
 | |
|     }
 | |
| 
 | |
|     void ackerman::cg_conflict_eh(expr * n1, expr * n2) {
 | |
|         if (!is_app(n1) || !is_app(n2))
 | |
|             return;
 | |
|         if (!ctx.enable_ackerman_axioms(n1))
 | |
|             return;
 | |
|         SASSERT(!ctx.m_drating);
 | |
|         app* a = to_app(n1);
 | |
|         app* b = to_app(n2);
 | |
|         if (a->get_decl() != b->get_decl() || a->get_num_args() != b->get_num_args())
 | |
|             return;
 | |
|         if (!enable_cc(a, b))
 | |
|             return;
 | |
|         TRACE("ack", tout << "conflict eh: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";);
 | |
|         insert(a, b);
 | |
|         gc();
 | |
|     }
 | |
| 
 | |
|     void ackerman::used_eq_eh(expr* a, expr* b, expr* c) {
 | |
|         if (a == b || a == c || b == c)
 | |
|             return;
 | |
|         if (ctx.m_drating)
 | |
|             return;
 | |
|         if (!enable_eq(a, b, c))
 | |
|             return;
 | |
|         TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";);
 | |
|         insert(a, b, c);
 | |
|         gc();
 | |
|     }
 | |
|         
 | |
|     void ackerman::used_cc_eh(app* a, app* b) {
 | |
|         if (ctx.m_drating)
 | |
|             return;
 | |
|         TRACE("ack", tout << "used cc: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";);
 | |
|         SASSERT(a->get_decl() == b->get_decl());
 | |
|         SASSERT(a->get_num_args() == b->get_num_args());
 | |
|         if (!enable_cc(a, b))
 | |
|             return;
 | |
|         insert(a, b);
 | |
|         gc();
 | |
|     }
 | |
| 
 | |
|     void ackerman::gc() {
 | |
|         m_num_propagations_since_last_gc++;
 | |
|         if (m_num_propagations_since_last_gc <= ctx.m_config.m_dack_gc) 
 | |
|             return;
 | |
|         m_num_propagations_since_last_gc = 0;
 | |
|         
 | |
|         while (m_table.size() > m_gc_threshold) 
 | |
|             remove(m_queue->prev());
 | |
|     
 | |
|         m_gc_threshold *= 110;
 | |
|         m_gc_threshold /= 100;
 | |
|         m_gc_threshold++;
 | |
|     }
 | |
| 
 | |
|     void ackerman::propagate() {
 | |
|         SASSERT(ctx.s().at_base_lvl());
 | |
|         auto* n = m_queue;
 | |
|         inference* k = nullptr;
 | |
|         unsigned num_prop = static_cast<unsigned>(ctx.s().get_stats().m_conflict * ctx.m_config.m_dack_factor);
 | |
|         num_prop = std::min(num_prop, m_table.size());
 | |
|         for (unsigned i = 0; i < num_prop; ++i, n = k) {
 | |
|             k = n->next();
 | |
|             if (n->m_count < ctx.m_config.m_dack_threshold) 
 | |
|                 continue;
 | |
|             if (n->m_count >= m_high_watermark && num_prop < m_table.size())
 | |
|                 ++num_prop;
 | |
|             if (n->is_cc) 
 | |
|                 add_cc(n->a, n->b);
 | |
|             else 
 | |
|                 add_eq(n->a, n->b, n->c);       
 | |
|             ++ctx.m_stats.m_ackerman;
 | |
|             remove(n);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void ackerman::add_cc(expr* _a, expr* _b) {     
 | |
|         app* a = to_app(_a);
 | |
|         app* b = to_app(_b);
 | |
|         TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << "\n";);
 | |
|         sat::literal_vector lits;
 | |
|         unsigned sz = a->get_num_args();        
 | |
|         
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             expr* ai = a->get_arg(i);
 | |
|             expr* bi = b->get_arg(i);
 | |
|             if (ai != bi) {
 | |
|                 expr_ref eq = ctx.mk_eq(ai, bi);
 | |
|                 lits.push_back(~ctx.mk_literal(eq));
 | |
|             }
 | |
|         }
 | |
|         expr_ref eq = ctx.mk_eq(a, b);
 | |
|         lits.push_back(ctx.mk_literal(eq));
 | |
|         th_proof_hint* ph = ctx.mk_cc_proof_hint(lits, a, b);
 | |
|         ctx.s().mk_clause(lits, sat::status::th(true, m.get_basic_family_id(), ph));                        
 | |
|     }
 | |
| 
 | |
|     void ackerman::add_eq(expr* a, expr* b, expr* c) {
 | |
|         if (a == c || b == c)
 | |
|             return;
 | |
|         sat::literal lits[3];
 | |
|         expr_ref eq1(ctx.mk_eq(a, c), m);
 | |
|         expr_ref eq2(ctx.mk_eq(b, c), m);
 | |
|         expr_ref eq3(ctx.mk_eq(a, b), m);
 | |
|         TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";);
 | |
|         lits[0] = ~ctx.mk_literal(eq1);
 | |
|         lits[1] = ~ctx.mk_literal(eq2);
 | |
|         lits[2] = ctx.mk_literal(eq3);
 | |
|         th_proof_hint* ph = ctx.mk_tc_proof_hint(lits);
 | |
|         ctx.s().add_clause(3, lits, sat::status::th(true, m.get_basic_family_id(), ph));        
 | |
|     }
 | |
| 
 | |
| }
 |