mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-03 21:09:11 +00:00 
			
		
		
		
	add Karr linear invariants as transformer
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									3810374cdd
								
							
						
					
					
						commit
						ab73c20757
					
				
					 14 changed files with 830 additions and 56 deletions
				
			
		| 
						 | 
				
			
			@ -45,6 +45,7 @@ Revision History:
 | 
			
		|||
#include"dl_mk_partial_equiv.h"
 | 
			
		||||
#include"dl_mk_bit_blast.h"
 | 
			
		||||
#include"dl_mk_array_blast.h"
 | 
			
		||||
#include"dl_mk_karr_invariants.h"
 | 
			
		||||
#include"datatype_decl_plugin.h"
 | 
			
		||||
#include"expr_abstract.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -223,6 +224,7 @@ namespace datalog {
 | 
			
		|||
        m_rewriter(m),
 | 
			
		||||
        m_var_subst(m),
 | 
			
		||||
        m_rule_manager(*this),
 | 
			
		||||
        m_transf(*this),
 | 
			
		||||
        m_trail(*this),
 | 
			
		||||
        m_pinned(m),
 | 
			
		||||
        m_vars(m),
 | 
			
		||||
| 
						 | 
				
			
			@ -825,23 +827,23 @@ namespace datalog {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    void context::transform_rules(model_converter_ref& mc, proof_converter_ref& pc) {
 | 
			
		||||
        rule_transformer transf(*this);
 | 
			
		||||
        transf.register_plugin(alloc(mk_filter_rules,*this));
 | 
			
		||||
        transf.register_plugin(alloc(mk_simple_joins,*this));
 | 
			
		||||
        m_transf.reset();
 | 
			
		||||
        m_transf.register_plugin(alloc(mk_filter_rules,*this));
 | 
			
		||||
        m_transf.register_plugin(alloc(mk_simple_joins,*this));
 | 
			
		||||
 | 
			
		||||
        if (unbound_compressor()) {
 | 
			
		||||
            transf.register_plugin(alloc(mk_unbound_compressor,*this));
 | 
			
		||||
            m_transf.register_plugin(alloc(mk_unbound_compressor,*this));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (similarity_compressor()) {
 | 
			
		||||
            transf.register_plugin(alloc(mk_similarity_compressor, *this, 
 | 
			
		||||
            m_transf.register_plugin(alloc(mk_similarity_compressor, *this, 
 | 
			
		||||
                                         similarity_compressor_threshold()));
 | 
			
		||||
        }
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_partial_equivalence_transformer, *this));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_partial_equivalence_transformer, *this));
 | 
			
		||||
 | 
			
		||||
        transform_rules(transf, mc, pc);
 | 
			
		||||
        transform_rules(m_transf, mc, pc);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    void context::transform_rules(rule_transformer& transf, model_converter_ref& mc, proof_converter_ref& pc) {
 | 
			
		||||
        SASSERT(m_closed); //we must finish adding rules before we start transforming them
 | 
			
		||||
        TRACE("dl", display_rules(tout););
 | 
			
		||||
| 
						 | 
				
			
			@ -862,32 +864,33 @@ namespace datalog {
 | 
			
		|||
 | 
			
		||||
    void context::apply_default_transformation(model_converter_ref& mc, proof_converter_ref& pc) {
 | 
			
		||||
        ensure_closed();
 | 
			
		||||
        datalog::rule_transformer transf(*this);
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_coi_filter, *this));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this));
 | 
			
		||||
        m_transf.reset();
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this));
 | 
			
		||||
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 35005));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 35000));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34990));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34980));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 35005));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 35000));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34990));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34980));
 | 
			
		||||
 | 
			
		||||
        //and another round of inlining
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34975));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34970));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34960));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34950));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34975));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34970));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34960));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34950));
 | 
			
		||||
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34940));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34930));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34920));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34910));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34900));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34890));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34880));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34940));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34930));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34920));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34910));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34900));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34890));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34880));
 | 
			
		||||
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_bit_blast, *this, 35000));
 | 
			
		||||
        transf.register_plugin(alloc(datalog::mk_array_blast, *this, 36000));
 | 
			
		||||
        transform_rules(transf, mc, pc);
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_bit_blast, *this, 35000));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_array_blast, *this, 36000));
 | 
			
		||||
        m_transf.register_plugin(alloc(datalog::mk_karr_invariants, *this, 36010));
 | 
			
		||||
        transform_rules(m_transf, mc, pc);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void context::collect_params(param_descrs& p) {
 | 
			
		||||
| 
						 | 
				
			
			@ -928,6 +931,7 @@ namespace datalog {
 | 
			
		|||
 | 
			
		||||
    void context::cancel() {
 | 
			
		||||
        m_cancel = true;
 | 
			
		||||
        m_transf.cancel();
 | 
			
		||||
        if (m_pdr.get()) m_pdr->cancel();
 | 
			
		||||
        if (m_bmc.get()) m_bmc->cancel();
 | 
			
		||||
        if (m_rel.get()) m_rel->cancel();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,11 +45,10 @@ Revision History:
 | 
			
		|||
#include"proof_converter.h"
 | 
			
		||||
#include"model2expr.h"
 | 
			
		||||
#include"smt_params.h"
 | 
			
		||||
#include"dl_rule_transformer.h"
 | 
			
		||||
 | 
			
		||||
namespace datalog {
 | 
			
		||||
 | 
			
		||||
    class rule_transformer;
 | 
			
		||||
 | 
			
		||||
    enum execution_result {
 | 
			
		||||
        OK,
 | 
			
		||||
        TIMEOUT,
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +84,7 @@ namespace datalog {
 | 
			
		|||
        th_rewriter        m_rewriter;
 | 
			
		||||
        var_subst          m_var_subst;
 | 
			
		||||
        rule_manager       m_rule_manager;
 | 
			
		||||
        rule_transformer   m_transf;
 | 
			
		||||
 | 
			
		||||
        trail_stack<context> m_trail;
 | 
			
		||||
        ast_ref_vector     m_pinned;
 | 
			
		||||
| 
						 | 
				
			
			@ -314,7 +314,7 @@ namespace datalog {
 | 
			
		|||
        void ensure_opened();
 | 
			
		||||
 | 
			
		||||
        void transform_rules(model_converter_ref& mc, proof_converter_ref& pc);
 | 
			
		||||
        void transform_rules(rule_transformer& trans, model_converter_ref& mc, proof_converter_ref& pc);
 | 
			
		||||
        void transform_rules(rule_transformer& transf, model_converter_ref& mc, proof_converter_ref& pc);
 | 
			
		||||
        void replace_rules(rule_set & rs);
 | 
			
		||||
 | 
			
		||||
        void apply_default_transformation(model_converter_ref& mc, proof_converter_ref& pc);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ namespace datalog {
 | 
			
		|||
 | 
			
		||||
    public:
 | 
			
		||||
        /**
 | 
			
		||||
           \brief Create rule transformer that extracts universal quantifiers (over recursive predicates).
 | 
			
		||||
           \brief Create rule transformer that removes array stores and selects by ackermannization.
 | 
			
		||||
        */
 | 
			
		||||
        mk_array_blast(context & ctx, unsigned priority);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										612
									
								
								src/muz_qe/dl_mk_karr_invariants.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										612
									
								
								src/muz_qe/dl_mk_karr_invariants.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,612 @@
 | 
			
		|||
/*++
 | 
			
		||||
Copyright (c) 2013 Microsoft Corporation
 | 
			
		||||
 | 
			
		||||
Module Name:
 | 
			
		||||
 | 
			
		||||
    dl_mk_karr_invariants.cpp
 | 
			
		||||
 | 
			
		||||
Abstract:
 | 
			
		||||
 | 
			
		||||
    Extract integer linear invariants.
 | 
			
		||||
 | 
			
		||||
    The linear invariants are extracted according to Karr's method.
 | 
			
		||||
    A short description is in 
 | 
			
		||||
    Nikolaj Bjørner, Anca Browne and Zohar Manna. Automatic Generation 
 | 
			
		||||
    of Invariants and Intermediate Assertions, in CP 95.
 | 
			
		||||
 | 
			
		||||
    The algorithm is here adapted to Horn clauses.
 | 
			
		||||
    The idea is to maintain two data-structures for each recursive relation.
 | 
			
		||||
    We call them R and RD
 | 
			
		||||
    - R  - set of linear congruences that are true of R.
 | 
			
		||||
    - RD - the dual basis of of solutions for R.
 | 
			
		||||
 | 
			
		||||
    RD is updated by accumulating basis vectors for solutions 
 | 
			
		||||
    to R (the homogeneous dual of R)
 | 
			
		||||
    R is updated from the inhomogeneous dual of RD.
 | 
			
		||||
 | 
			
		||||
Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner (nbjorner) 2013-03-09
 | 
			
		||||
 | 
			
		||||
Revision History:
 | 
			
		||||
           
 | 
			
		||||
--*/
 | 
			
		||||
 | 
			
		||||
#include"dl_mk_karr_invariants.h"
 | 
			
		||||
 | 
			
		||||
namespace datalog {
 | 
			
		||||
 | 
			
		||||
    mk_karr_invariants::mk_karr_invariants(context & ctx, unsigned priority):
 | 
			
		||||
        rule_transformer::plugin(priority, false),
 | 
			
		||||
        m_ctx(ctx),
 | 
			
		||||
        m(ctx.get_manager()), 
 | 
			
		||||
        rm(ctx.get_rule_manager()),
 | 
			
		||||
        a(m) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mk_karr_invariants::~mk_karr_invariants() {
 | 
			
		||||
        obj_map<func_decl, matrix*>::iterator it = m_constraints.begin(), end = m_constraints.end();
 | 
			
		||||
        for (; it != end; ++it) {
 | 
			
		||||
            dealloc(it->m_value);
 | 
			
		||||
        }
 | 
			
		||||
        it = m_dual_constraints.begin();
 | 
			
		||||
        end = m_dual_constraints.end();
 | 
			
		||||
        for (; it != end; ++it) {
 | 
			
		||||
            dealloc(it->m_value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mk_karr_invariants::matrix& mk_karr_invariants::matrix::operator=(matrix const& other) {
 | 
			
		||||
        reset();
 | 
			
		||||
        append(other);
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void mk_karr_invariants::matrix::display(std::ostream& out) const {
 | 
			
		||||
        for (unsigned i = 0; i < A.size(); ++i) {
 | 
			
		||||
            for (unsigned j = 0; j < A[i].size(); ++j) {
 | 
			
		||||
                out << A[i][j] << " ";
 | 
			
		||||
            }
 | 
			
		||||
            out << (eq[i]?" = ":" >= ") << -b[i] << "\n";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool mk_karr_invariants::is_linear(expr* e, vector<rational>& row, rational& b, rational const& mul) {
 | 
			
		||||
        if (!a.is_int(e)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (is_var(e)) {
 | 
			
		||||
            row[to_var(e)->get_idx()] += mul;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (!is_app(e)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        rational n;
 | 
			
		||||
        if (a.is_numeral(e, n)) {
 | 
			
		||||
            b += mul*n;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (a.is_add(e)) {
 | 
			
		||||
            for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) {
 | 
			
		||||
                if (!is_linear(to_app(e)->get_arg(i), row, b, mul)) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        expr* e1, *e2;
 | 
			
		||||
        if (a.is_sub(e, e1, e2)) {
 | 
			
		||||
            return is_linear(e1, row, b, mul) && is_linear(e2, row, b, -mul);
 | 
			
		||||
        }
 | 
			
		||||
        if (a.is_mul(e, e1, e2) && a.is_numeral(e1, n)) {
 | 
			
		||||
            return is_linear(e2, row, b, mul*n);
 | 
			
		||||
        }
 | 
			
		||||
        if (a.is_mul(e, e1, e2) && a.is_numeral(e2, n)) {
 | 
			
		||||
            return is_linear(e1, row, b, mul*n);
 | 
			
		||||
        }
 | 
			
		||||
        if (a.is_uminus(e, e1)) {
 | 
			
		||||
            return is_linear(e1, row, b, -mul);
 | 
			
		||||
        }
 | 
			
		||||
        return false;        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mk_karr_invariants::matrix* mk_karr_invariants::get_constraints(func_decl* p) {
 | 
			
		||||
        matrix* result = 0;
 | 
			
		||||
        m_constraints.find(p, result);
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mk_karr_invariants::matrix& mk_karr_invariants::get_dual_constraints(func_decl* p) {
 | 
			
		||||
        matrix* result = 0;
 | 
			
		||||
        if (!m_dual_constraints.find(p, result)) {
 | 
			
		||||
            result = alloc(matrix);
 | 
			
		||||
            m_dual_constraints.insert(p, result);
 | 
			
		||||
        }
 | 
			
		||||
        return *result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool mk_karr_invariants::is_eq(expr* e, var*& v, rational& n) {
 | 
			
		||||
        expr* e1, *e2;
 | 
			
		||||
        if (!m.is_eq(e, e1, e2)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (!is_var(e1)) {
 | 
			
		||||
            std::swap(e1, e2);
 | 
			
		||||
        }
 | 
			
		||||
        if (!is_var(e1)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        v = to_var(e1);
 | 
			
		||||
        if (!a.is_numeral(e2, n)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bool mk_karr_invariants::get_transition_relation(rule const& r, matrix& M) {
 | 
			
		||||
        unsigned num_vars = rm.get_var_counter().get_max_var(r)+1;
 | 
			
		||||
        unsigned arity = r.get_decl()->get_arity();
 | 
			
		||||
        unsigned num_columns = arity + num_vars;        
 | 
			
		||||
        unsigned utsz = r.get_uninterpreted_tail_size();
 | 
			
		||||
        unsigned tsz  = r.get_tail_size();
 | 
			
		||||
        M.reset();
 | 
			
		||||
        
 | 
			
		||||
        for (unsigned i = 0; i < utsz; ++i) {
 | 
			
		||||
            matrix const* Mp = get_constraints(r.get_decl(i));
 | 
			
		||||
            if (!Mp) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            TRACE("dl", Mp->display(tout << "Intersect\n"););
 | 
			
		||||
            intersect_matrix(r.get_tail(i), *Mp, num_columns, M);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rational one(1), mone(-1);
 | 
			
		||||
        expr* e1, *e2, *en;
 | 
			
		||||
        var* v, *w;
 | 
			
		||||
        rational n1, n2;
 | 
			
		||||
        expr_ref_vector conjs(m);
 | 
			
		||||
        for (unsigned i = utsz; i < tsz; ++i) {
 | 
			
		||||
            conjs.push_back(r.get_tail(i));
 | 
			
		||||
        }
 | 
			
		||||
        datalog::flatten_and(conjs);
 | 
			
		||||
 | 
			
		||||
        for (unsigned i = 0; i < conjs.size(); ++i) {
 | 
			
		||||
            expr* e = conjs[i].get();
 | 
			
		||||
            rational b(0);
 | 
			
		||||
            vector<rational> row;
 | 
			
		||||
            row.resize(num_columns, rational(0));
 | 
			
		||||
            bool processed = true;
 | 
			
		||||
            if (m.is_eq(e, e1, e2) && is_linear(e1, row, b, one) && is_linear(e2, row, b, mone)) {
 | 
			
		||||
                M.A.push_back(row);
 | 
			
		||||
                M.b.push_back(b);
 | 
			
		||||
                M.eq.push_back(true);
 | 
			
		||||
            }
 | 
			
		||||
            else if ((a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) {
 | 
			
		||||
                M.A.push_back(row);
 | 
			
		||||
                M.b.push_back(b);
 | 
			
		||||
                M.eq.push_back(false);
 | 
			
		||||
            }
 | 
			
		||||
            else if ((a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) {
 | 
			
		||||
                M.A.push_back(row);
 | 
			
		||||
                M.b.push_back(b + rational(1));
 | 
			
		||||
                M.eq.push_back(false);
 | 
			
		||||
            }
 | 
			
		||||
            else if (m.is_not(e, en) && (a.is_lt(en, e2, e1) || a.is_gt(en, e1, e2)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) {
 | 
			
		||||
                M.A.push_back(row);
 | 
			
		||||
                M.b.push_back(b);
 | 
			
		||||
                M.eq.push_back(false);
 | 
			
		||||
            }
 | 
			
		||||
            else if (m.is_not(e, en) && (a.is_le(en, e2, e1) || a.is_ge(en, e1, e2)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) {
 | 
			
		||||
                M.A.push_back(row);
 | 
			
		||||
                M.b.push_back(b + rational(1));
 | 
			
		||||
                M.eq.push_back(false);
 | 
			
		||||
            }
 | 
			
		||||
            else if (m.is_or(e, e1, e2) && is_eq(e1, v, n1) && is_eq(e2, w, n2) && v == w) {
 | 
			
		||||
                if (n1 > n2) {
 | 
			
		||||
                    std::swap(n1, n2);
 | 
			
		||||
                }
 | 
			
		||||
                SASSERT(n1 <= n2);
 | 
			
		||||
                row[v->get_idx()] = rational(1);
 | 
			
		||||
                // v - n1 >= 0
 | 
			
		||||
                M.A.push_back(row);
 | 
			
		||||
                M.b.push_back(-n1);
 | 
			
		||||
                M.eq.push_back(false);
 | 
			
		||||
                // -v + n2 >= 0
 | 
			
		||||
                row[v->get_idx()] = rational(-1);
 | 
			
		||||
                M.A.push_back(row);
 | 
			
		||||
                M.b.push_back(n2);
 | 
			
		||||
                M.eq.push_back(false);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                processed = false;
 | 
			
		||||
            }
 | 
			
		||||
            TRACE("dl", tout << (processed?"+ ":"- ") << mk_pp(e, m) << "\n";);
 | 
			
		||||
        }
 | 
			
		||||
        // intersect with the head predicate.
 | 
			
		||||
        app* head = r.get_head();
 | 
			
		||||
        unsigned sz0 = M.A.size();
 | 
			
		||||
        for (unsigned i = 0; i < arity; ++i) {
 | 
			
		||||
            rational n;
 | 
			
		||||
            expr* arg = head->get_arg(i);
 | 
			
		||||
            if (!a.is_int(arg)) { 
 | 
			
		||||
                // no-op
 | 
			
		||||
            }
 | 
			
		||||
            else if (is_var(arg)) {
 | 
			
		||||
                vector<rational> row;
 | 
			
		||||
                row.resize(num_columns, rational(0));
 | 
			
		||||
                unsigned idx = to_var(arg)->get_idx();
 | 
			
		||||
                row[idx] = rational(-1);
 | 
			
		||||
                row[num_vars + i] = rational(1);
 | 
			
		||||
                M.A.push_back(row);
 | 
			
		||||
                M.b.push_back(rational(0));
 | 
			
		||||
                M.eq.push_back(true);
 | 
			
		||||
            }
 | 
			
		||||
            else if (a.is_numeral(arg, n)) {
 | 
			
		||||
                vector<rational> row;
 | 
			
		||||
                row.resize(num_columns, rational(0));
 | 
			
		||||
                row[num_vars + i] = rational(1);
 | 
			
		||||
                M.A.push_back(row);
 | 
			
		||||
                M.b.push_back(-n);
 | 
			
		||||
                M.eq.push_back(true);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                UNREACHABLE();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (M.A.size() == sz0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        TRACE("dl", M.display(tout << r.get_decl()->get_name() << "\n"););
 | 
			
		||||
        matrix MD;
 | 
			
		||||
        dualizeI(MD, M);
 | 
			
		||||
        M.reset();
 | 
			
		||||
        // project for variables in head.
 | 
			
		||||
        for (unsigned i = 0; i < MD.size(); ++i) {
 | 
			
		||||
            vector<rational> row;
 | 
			
		||||
            row.append(arity, MD.A[i].c_ptr() + num_vars);
 | 
			
		||||
            M.A.push_back(row);
 | 
			
		||||
            M.b.push_back(MD.b[i]);
 | 
			
		||||
            M.eq.push_back(true);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void mk_karr_invariants::intersect_matrix(app* p, matrix const& Mp, unsigned num_columns, matrix& M) {
 | 
			
		||||
        for (unsigned j = 0; j < Mp.size(); ++j) {
 | 
			
		||||
            rational b = Mp.b[j], n;
 | 
			
		||||
            vector<rational> row;
 | 
			
		||||
            row.resize(num_columns, rational(0));
 | 
			
		||||
            for (unsigned i = 0; i < p->get_num_args(); ++i) {
 | 
			
		||||
                expr* arg = p->get_arg(i);
 | 
			
		||||
                if (!a.is_int(arg)) {
 | 
			
		||||
                    // no-op
 | 
			
		||||
                }
 | 
			
		||||
                else if (is_var(arg)) {
 | 
			
		||||
                    unsigned idx = to_var(arg)->get_idx();
 | 
			
		||||
                    row[idx] += Mp.A[j][i];
 | 
			
		||||
                }
 | 
			
		||||
                else if (a.is_numeral(arg, n)) {
 | 
			
		||||
                    b += Mp.A[j][i]*n;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    UNREACHABLE();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            M.A.push_back(row);
 | 
			
		||||
            M.b.push_back(b);
 | 
			
		||||
            M.eq.push_back(Mp.eq[j]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // treat src as a homogeneous matrix.
 | 
			
		||||
    void mk_karr_invariants::dualizeH(matrix& dst, matrix const& src) {
 | 
			
		||||
        dst.reset();
 | 
			
		||||
        if (src.size() == 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        m_hb.reset();
 | 
			
		||||
        for (unsigned i = 0; i < src.size(); ++i) {
 | 
			
		||||
            vector<rational> v(src.A[i]);
 | 
			
		||||
            v.append(src.b[i]);
 | 
			
		||||
            if (src.eq[i]) {
 | 
			
		||||
                m_hb.add_eq(v, rational(0));
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                m_hb.add_ge(v, rational(0));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) {
 | 
			
		||||
            m_hb.set_is_int(i);
 | 
			
		||||
        }
 | 
			
		||||
        lbool is_sat = m_hb.saturate();
 | 
			
		||||
        TRACE("dl", m_hb.display(tout););
 | 
			
		||||
        SASSERT(is_sat == l_true);
 | 
			
		||||
        unsigned basis_size = m_hb.get_basis_size();
 | 
			
		||||
        bool first_initial = true;
 | 
			
		||||
        for (unsigned i = 0; i < basis_size; ++i) {
 | 
			
		||||
            bool is_initial;
 | 
			
		||||
            vector<rational> soln;
 | 
			
		||||
            m_hb.get_basis_solution(i, soln, is_initial);
 | 
			
		||||
            if (!is_initial) {
 | 
			
		||||
                dst.b.push_back(soln.back());
 | 
			
		||||
                dst.eq.push_back(true);
 | 
			
		||||
                soln.pop_back();
 | 
			
		||||
                dst.A.push_back(soln);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // treat src as an inhomegeneous matrix.
 | 
			
		||||
    void mk_karr_invariants::dualizeI(matrix& dst, matrix const& src) {
 | 
			
		||||
        m_hb.reset();
 | 
			
		||||
        for (unsigned i = 0; i < src.size(); ++i) {
 | 
			
		||||
            if (src.eq[i]) {
 | 
			
		||||
                m_hb.add_eq(src.A[i], -src.b[i]);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                m_hb.add_ge(src.A[i], -src.b[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        for (unsigned i = 0; !src.A.empty() && i < src.A[0].size(); ++i) {
 | 
			
		||||
            m_hb.set_is_int(i);
 | 
			
		||||
        }
 | 
			
		||||
        lbool is_sat = m_hb.saturate();
 | 
			
		||||
        TRACE("dl", m_hb.display(tout););
 | 
			
		||||
        SASSERT(is_sat == l_true);
 | 
			
		||||
        dst.reset();
 | 
			
		||||
        unsigned basis_size = m_hb.get_basis_size();
 | 
			
		||||
        bool first_initial = true;
 | 
			
		||||
        for (unsigned i = 0; i < basis_size; ++i) {
 | 
			
		||||
            bool is_initial;
 | 
			
		||||
            vector<rational> soln;
 | 
			
		||||
            m_hb.get_basis_solution(i, soln, is_initial);
 | 
			
		||||
            if (is_initial && first_initial) {
 | 
			
		||||
                dst.A.push_back(soln);
 | 
			
		||||
                dst.b.push_back(rational(1));
 | 
			
		||||
                dst.eq.push_back(true);
 | 
			
		||||
                first_initial = false;
 | 
			
		||||
            }
 | 
			
		||||
            else if (!is_initial) {
 | 
			
		||||
                dst.A.push_back(soln);
 | 
			
		||||
                dst.b.push_back(rational(0));
 | 
			
		||||
                dst.eq.push_back(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void mk_karr_invariants::update_body(rule_set& rules, rule& r){
 | 
			
		||||
        func_decl* p = r.get_decl();        
 | 
			
		||||
        unsigned utsz = r.get_uninterpreted_tail_size();
 | 
			
		||||
        unsigned tsz  = r.get_tail_size();
 | 
			
		||||
        app_ref_vector tail(m);
 | 
			
		||||
        for (unsigned i = 0; i < tsz; ++i) {
 | 
			
		||||
            tail.push_back(r.get_tail(i));
 | 
			
		||||
        }
 | 
			
		||||
        for (unsigned i = 0; i < utsz; ++i) {
 | 
			
		||||
            func_decl* q = r.get_decl(i);            
 | 
			
		||||
            matrix* N = get_constraints(q);
 | 
			
		||||
            if (!N) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            expr_ref zero(m), lhs(m);
 | 
			
		||||
            zero = a.mk_numeral(rational(0), true);
 | 
			
		||||
            for (unsigned j = 0; j < N->size(); ++j) {
 | 
			
		||||
                rational n;
 | 
			
		||||
                SASSERT(N->A[j].size() == q->get_arity());
 | 
			
		||||
                expr_ref_vector sum(m);
 | 
			
		||||
                for (unsigned k = 0; k < N->A[j].size(); ++k) {
 | 
			
		||||
                    n = N->A[j][k];
 | 
			
		||||
                    if (!n.is_zero()) {
 | 
			
		||||
                        expr* arg = r.get_tail(i)->get_arg(k);
 | 
			
		||||
                        sum.push_back(a.mk_mul(a.mk_numeral(n, true), arg));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                n = N->b[j];
 | 
			
		||||
                if (!n.is_zero()) {
 | 
			
		||||
                    sum.push_back(a.mk_numeral(n, true));
 | 
			
		||||
                }               
 | 
			
		||||
                lhs = a.mk_add(sum.size(), sum.c_ptr());
 | 
			
		||||
                if (N->eq[j]) {
 | 
			
		||||
                    tail.push_back(m.mk_eq(lhs, zero));
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    tail.push_back(a.mk_ge(lhs, zero));
 | 
			
		||||
                }
 | 
			
		||||
            }            
 | 
			
		||||
        }
 | 
			
		||||
        rule* new_rule = &r;
 | 
			
		||||
        if (tail.size() != tsz) {
 | 
			
		||||
            new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), 0, r.name());
 | 
			
		||||
        }
 | 
			
		||||
        rules.add_rule(new_rule);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class mk_karr_invariants::add_invariant_model_converter : public model_converter {
 | 
			
		||||
        ast_manager&          m;
 | 
			
		||||
        arith_util            a;
 | 
			
		||||
        func_decl_ref_vector  m_funcs;
 | 
			
		||||
        ptr_vector<matrix>    m_invs;
 | 
			
		||||
    public:
 | 
			
		||||
        
 | 
			
		||||
        add_invariant_model_converter(ast_manager& m): m(m), a(m), m_funcs(m) {}
 | 
			
		||||
 | 
			
		||||
        virtual ~add_invariant_model_converter() {
 | 
			
		||||
            for (unsigned i = 0; i < m_invs.size(); ++i) {
 | 
			
		||||
                dealloc(m_invs[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void add(func_decl* p, matrix& M) {
 | 
			
		||||
            m_funcs.push_back(p);
 | 
			
		||||
            m_invs.push_back(alloc(matrix, M));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        virtual void operator()(model_ref & mr) {
 | 
			
		||||
            for (unsigned i = 0; i < m_funcs.size(); ++i) {
 | 
			
		||||
                func_decl* p = m_funcs[i].get();
 | 
			
		||||
                func_interp* f = mr->get_func_interp(p);
 | 
			
		||||
                expr_ref body(m);                
 | 
			
		||||
                unsigned arity = p->get_arity();
 | 
			
		||||
                SASSERT(0 < arity);
 | 
			
		||||
                if (f) {
 | 
			
		||||
                    matrix const& M = *m_invs[i];
 | 
			
		||||
                    mk_body(M, body);
 | 
			
		||||
                    SASSERT(f->num_entries() == 0);
 | 
			
		||||
                    if (!f->is_partial()) {
 | 
			
		||||
                        body = m.mk_and(f->get_else(), body);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    f = alloc(func_interp, m, arity);
 | 
			
		||||
                    mr->register_decl(p, f);
 | 
			
		||||
                    body = m.mk_false();  // fragile: assume that relation was pruned by being infeasible.
 | 
			
		||||
                }
 | 
			
		||||
                f->set_else(body);
 | 
			
		||||
            }            
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        virtual model_converter * translate(ast_translation & translator) {
 | 
			
		||||
            add_invariant_model_converter* mc = alloc(add_invariant_model_converter, m);
 | 
			
		||||
            for (unsigned i = 0; i < m_funcs.size(); ++i) {
 | 
			
		||||
                mc->add(translator(m_funcs[i].get()), *m_invs[i]);
 | 
			
		||||
            }
 | 
			
		||||
            return mc;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        void mk_body(matrix const& M, expr_ref& body) {
 | 
			
		||||
            expr_ref_vector conj(m);
 | 
			
		||||
            for (unsigned i = 0; i < M.size(); ++i) {
 | 
			
		||||
                mk_body(M.A[i], M.b[i], M.eq[i], conj);
 | 
			
		||||
            }
 | 
			
		||||
            body = m.mk_and(conj.size(), conj.c_ptr());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void mk_body(vector<rational> const& row, rational const& b, bool is_eq, expr_ref_vector& conj) {
 | 
			
		||||
            expr_ref_vector sum(m);
 | 
			
		||||
            expr_ref zero(m), lhs(m);
 | 
			
		||||
            zero = a.mk_numeral(rational(0), true);
 | 
			
		||||
 | 
			
		||||
            for (unsigned i = 0; i < row.size(); ++i) {
 | 
			
		||||
                if (row[i].is_zero()) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                var* var = m.mk_var(i, a.mk_int());
 | 
			
		||||
                if (row[i].is_one()) {
 | 
			
		||||
                    sum.push_back(var);
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (!b.is_zero()) {
 | 
			
		||||
                sum.push_back(a.mk_numeral(b, true));
 | 
			
		||||
            }
 | 
			
		||||
            lhs = a.mk_add(sum.size(), sum.c_ptr());
 | 
			
		||||
            if (is_eq) {
 | 
			
		||||
                conj.push_back(m.mk_eq(lhs, zero));
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                conj.push_back(a.mk_ge(lhs, zero));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void mk_karr_invariants::cancel() {
 | 
			
		||||
        rule_transformer::plugin::cancel();
 | 
			
		||||
        m_hb.set_cancel(true);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    rule_set * mk_karr_invariants::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
 | 
			
		||||
        if (!m_ctx.get_params().karr()) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        rule_set::iterator it = source.begin(), end = source.end();
 | 
			
		||||
        for (; it != end; ++it) {
 | 
			
		||||
            rule const& r = **it;
 | 
			
		||||
            if (r.has_negation()) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        bool change = true, non_empty = false;
 | 
			
		||||
        while (!m_cancel && change) {
 | 
			
		||||
            change = false;
 | 
			
		||||
            it = source.begin();            
 | 
			
		||||
            for (; it != end; ++it) {
 | 
			
		||||
                rule const& r = **it;
 | 
			
		||||
                TRACE("dl", r.display(m_ctx, tout););
 | 
			
		||||
                matrix MD, P;
 | 
			
		||||
                if (!get_transition_relation(r, MD)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                non_empty = true;
 | 
			
		||||
                func_decl* p = r.get_decl();
 | 
			
		||||
                matrix& ND = get_dual_constraints(p);
 | 
			
		||||
                matrix* N  = get_constraints(p);
 | 
			
		||||
                ND.append(MD);
 | 
			
		||||
                dualizeH(P, ND);
 | 
			
		||||
 | 
			
		||||
                TRACE("dl",
 | 
			
		||||
                           MD.display(tout << "MD\n");
 | 
			
		||||
                           P.display(tout << "P\n"););
 | 
			
		||||
 | 
			
		||||
                if (!N) {
 | 
			
		||||
                    change = true;
 | 
			
		||||
                    N = alloc(matrix, P);
 | 
			
		||||
                    m_constraints.insert(p, N);
 | 
			
		||||
                }
 | 
			
		||||
                else if (P.size() != N->size()) {
 | 
			
		||||
                    change = true;
 | 
			
		||||
                    *N = P;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!non_empty) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (m_cancel) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        TRACE("dl",
 | 
			
		||||
                   rule_set::decl2rules::iterator git  = source.begin_grouped_rules();
 | 
			
		||||
                   rule_set::decl2rules::iterator gend = source.end_grouped_rules();
 | 
			
		||||
                   for (; git != gend; ++git) {
 | 
			
		||||
                       func_decl* p = git->m_key;
 | 
			
		||||
                       matrix* M = get_constraints(p);
 | 
			
		||||
                       tout << p->get_name() << "\n";
 | 
			
		||||
                       if (M) {
 | 
			
		||||
                           M->display(tout);
 | 
			
		||||
                       }
 | 
			
		||||
                   });
 | 
			
		||||
        
 | 
			
		||||
        rule_set* rules = alloc(rule_set, m_ctx);
 | 
			
		||||
        it = source.begin();            
 | 
			
		||||
        for (; it != end; ++it) {
 | 
			
		||||
            update_body(*rules, **it);
 | 
			
		||||
        }
 | 
			
		||||
        if (mc) {
 | 
			
		||||
            add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m);
 | 
			
		||||
            rule_set::decl2rules::iterator git  = source.begin_grouped_rules();
 | 
			
		||||
            rule_set::decl2rules::iterator gend = source.end_grouped_rules();
 | 
			
		||||
            for (; git != gend; ++git) {
 | 
			
		||||
                func_decl* p = git->m_key;
 | 
			
		||||
                matrix* M = get_constraints(p);
 | 
			
		||||
                if (M) {
 | 
			
		||||
                    kmc->add(p, *M);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            mc = concat(mc.get(), kmc);
 | 
			
		||||
        }
 | 
			
		||||
        TRACE("dl", rules->display(tout););
 | 
			
		||||
        return rules;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										79
									
								
								src/muz_qe/dl_mk_karr_invariants.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/muz_qe/dl_mk_karr_invariants.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,79 @@
 | 
			
		|||
/*++
 | 
			
		||||
Copyright (c) 2013 Microsoft Corporation
 | 
			
		||||
 | 
			
		||||
Module Name:
 | 
			
		||||
 | 
			
		||||
    dl_mk_karr_invariants.h
 | 
			
		||||
 | 
			
		||||
Abstract:
 | 
			
		||||
 | 
			
		||||
    Extract integer linear invariants.
 | 
			
		||||
 | 
			
		||||
Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner (nbjorner) 2013-03-08
 | 
			
		||||
 | 
			
		||||
Revision History:
 | 
			
		||||
 | 
			
		||||
--*/
 | 
			
		||||
#ifndef _DL_MK_KARR_INVARIANTS_H_
 | 
			
		||||
#define _DL_MK_KARR_INVARIANTS_H_
 | 
			
		||||
 | 
			
		||||
#include"dl_context.h"
 | 
			
		||||
#include"dl_rule_set.h"
 | 
			
		||||
#include"dl_rule_transformer.h"
 | 
			
		||||
#include"arith_decl_plugin.h"
 | 
			
		||||
#include"hilbert_basis.h"
 | 
			
		||||
 | 
			
		||||
namespace datalog {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Rule transformer that strengthens bodies with invariants.
 | 
			
		||||
    */
 | 
			
		||||
    class mk_karr_invariants : public rule_transformer::plugin {
 | 
			
		||||
 | 
			
		||||
        struct matrix {
 | 
			
		||||
            vector<vector<rational> > A;
 | 
			
		||||
            vector<rational>          b;
 | 
			
		||||
            svector<bool>             eq;
 | 
			
		||||
            unsigned size() const { return A.size(); }
 | 
			
		||||
            void reset() { A.reset(); b.reset(); eq.reset(); }
 | 
			
		||||
            matrix& operator=(matrix const& other);
 | 
			
		||||
            void append(matrix const& other) { A.append(other.A); b.append(other.b); eq.append(other.eq); }
 | 
			
		||||
            void display(std::ostream& out) const;
 | 
			
		||||
        };
 | 
			
		||||
        class add_invariant_model_converter;
 | 
			
		||||
 | 
			
		||||
        context&        m_ctx;
 | 
			
		||||
        ast_manager&    m;
 | 
			
		||||
        rule_manager&   rm;
 | 
			
		||||
        arith_util      a;
 | 
			
		||||
        obj_map<func_decl, matrix*> m_constraints;
 | 
			
		||||
        obj_map<func_decl, matrix*> m_dual_constraints;
 | 
			
		||||
        hilbert_basis   m_hb;
 | 
			
		||||
 | 
			
		||||
        bool is_linear(expr* e, vector<rational>& row, rational& b, rational const& mul);
 | 
			
		||||
        bool is_eq(expr* e, var*& v, rational& n);
 | 
			
		||||
        bool get_transition_relation(rule const& r, matrix& M);
 | 
			
		||||
        void intersect_matrix(app* p, matrix const& Mp, unsigned num_columns, matrix& M);
 | 
			
		||||
        matrix* get_constraints(func_decl* p);   
 | 
			
		||||
        matrix& get_dual_constraints(func_decl* p);     
 | 
			
		||||
        void dualizeH(matrix& dst, matrix const& src);
 | 
			
		||||
        void dualizeI(matrix& dst, matrix const& src);
 | 
			
		||||
        void update_body(rule_set& rules, rule& r);
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        mk_karr_invariants(context & ctx, unsigned priority);
 | 
			
		||||
 | 
			
		||||
        virtual ~mk_karr_invariants();
 | 
			
		||||
 | 
			
		||||
        virtual void cancel(); 
 | 
			
		||||
        
 | 
			
		||||
        rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* _DL_MK_KARR_INVARIANTS_H_ */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -927,6 +927,15 @@ namespace datalog {
 | 
			
		|||
        return exist || univ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool rule::has_negation() const {
 | 
			
		||||
        for (unsigned i = 0; i < get_uninterpreted_tail_size(); ++i) {
 | 
			
		||||
            if (is_neg_tail(i)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void rule::get_used_vars(used_vars& used) const {
 | 
			
		||||
        used.process(get_head());
 | 
			
		||||
        unsigned sz = get_tail_size();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -237,6 +237,7 @@ namespace datalog {
 | 
			
		|||
        bool has_uninterpreted_non_predicates(func_decl*& f) const;
 | 
			
		||||
        void has_quantifiers(bool& existential, bool& universal) const;
 | 
			
		||||
        bool has_quantifiers() const;
 | 
			
		||||
        bool has_negation() const;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           \brief Store in d the (direct) dependencies of the given rule.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,40 +16,55 @@ Author:
 | 
			
		|||
Revision History:
 | 
			
		||||
 | 
			
		||||
--*/
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include<typeinfo>
 | 
			
		||||
 | 
			
		||||
#include"dl_context.h"
 | 
			
		||||
 | 
			
		||||
#include"dl_rule_transformer.h"
 | 
			
		||||
 | 
			
		||||
namespace datalog {
 | 
			
		||||
 | 
			
		||||
    rule_transformer::rule_transformer(context & ctx) 
 | 
			
		||||
        : m_context(ctx), m_rule_manager(m_context.get_rule_manager()), m_dirty(false) {
 | 
			
		||||
        : m_context(ctx), m_rule_manager(m_context.get_rule_manager()), m_dirty(false), m_cancel(false) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    rule_transformer::~rule_transformer() {
 | 
			
		||||
        plugin_vector::iterator it=m_plugins.begin();
 | 
			
		||||
        plugin_vector::iterator end=m_plugins.end();
 | 
			
		||||
        reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void rule_transformer::reset() {
 | 
			
		||||
        plugin_vector::iterator it = m_plugins.begin();
 | 
			
		||||
        plugin_vector::iterator end = m_plugins.end();
 | 
			
		||||
        for(; it!=end; ++it) {
 | 
			
		||||
            dealloc(*it);
 | 
			
		||||
        }
 | 
			
		||||
        m_plugins.reset();
 | 
			
		||||
        m_dirty = false;
 | 
			
		||||
        m_cancel = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void rule_transformer::cancel() {
 | 
			
		||||
        m_cancel = true;
 | 
			
		||||
        plugin_vector::iterator it = m_plugins.begin();
 | 
			
		||||
        plugin_vector::iterator end = m_plugins.end();
 | 
			
		||||
        for(; it!=end; ++it) {
 | 
			
		||||
            (*it)->cancel();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct rule_transformer::plugin_comparator {
 | 
			
		||||
        bool operator()(rule_transformer::plugin * p1, rule_transformer::plugin * p2) {
 | 
			
		||||
            return p1->get_priority()>p2->get_priority();
 | 
			
		||||
            return p1->get_priority() > p2->get_priority();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void rule_transformer::ensure_ordered() {
 | 
			
		||||
        if (!m_dirty) {
 | 
			
		||||
            return;
 | 
			
		||||
        if (m_dirty) {
 | 
			
		||||
            std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator());
 | 
			
		||||
            m_dirty = false;
 | 
			
		||||
        }
 | 
			
		||||
        std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator());
 | 
			
		||||
        m_dirty=false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void rule_transformer::register_plugin(plugin * p) {
 | 
			
		||||
| 
						 | 
				
			
			@ -67,9 +82,9 @@ namespace datalog {
 | 
			
		|||
            tout<<"init:\n";
 | 
			
		||||
            rules.display(tout);
 | 
			
		||||
        );
 | 
			
		||||
        plugin_vector::iterator it=m_plugins.begin();
 | 
			
		||||
        plugin_vector::iterator end=m_plugins.end();
 | 
			
		||||
        for(; it!=end; ++it) {
 | 
			
		||||
        plugin_vector::iterator it = m_plugins.begin();
 | 
			
		||||
        plugin_vector::iterator end = m_plugins.end();
 | 
			
		||||
        for(; it!=end && !m_cancel; ++it) {
 | 
			
		||||
            plugin & p = **it;
 | 
			
		||||
 | 
			
		||||
            rule_set * new_rules = p(rules, mc, pc);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,8 @@ Revision History:
 | 
			
		|||
 | 
			
		||||
namespace datalog {
 | 
			
		||||
 | 
			
		||||
    class context;
 | 
			
		||||
 | 
			
		||||
    class rule_transformer {
 | 
			
		||||
    public:
 | 
			
		||||
        class plugin;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,9 +39,10 @@ namespace datalog {
 | 
			
		|||
        typedef svector<plugin*> plugin_vector;
 | 
			
		||||
        struct plugin_comparator;
 | 
			
		||||
 | 
			
		||||
        context & m_context;
 | 
			
		||||
        rule_manager & m_rule_manager;
 | 
			
		||||
        bool m_dirty;
 | 
			
		||||
        context &        m_context;
 | 
			
		||||
        rule_manager &   m_rule_manager;
 | 
			
		||||
        bool             m_dirty;
 | 
			
		||||
        volatile bool    m_cancel;
 | 
			
		||||
        svector<plugin*> m_plugins;
 | 
			
		||||
        
 | 
			
		||||
        void ensure_ordered();
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +50,13 @@ namespace datalog {
 | 
			
		|||
 | 
			
		||||
        rule_transformer(context & ctx);
 | 
			
		||||
        ~rule_transformer();
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           \brief Reset all registered transformers.
 | 
			
		||||
         */
 | 
			
		||||
        void reset();
 | 
			
		||||
 | 
			
		||||
        void cancel();
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
           \brief Add a plugin for rule transformation.
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +82,8 @@ namespace datalog {
 | 
			
		|||
        void attach(rule_transformer & transformer) { m_transformer = &transformer; }
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        volatile bool m_cancel;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           \brief Create a plugin object for rule_transformer.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +91,8 @@ namespace datalog {
 | 
			
		|||
           (higher priority plugins will be applied first).
 | 
			
		||||
        */
 | 
			
		||||
        plugin(unsigned priority, bool can_destratify_negation = false) : m_priority(priority), 
 | 
			
		||||
            m_can_destratify_negation(can_destratify_negation), m_transformer(0) {}
 | 
			
		||||
            m_can_destratify_negation(can_destratify_negation), m_transformer(0), m_cancel(false) {}
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~plugin() {}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -96,8 +109,10 @@ namespace datalog {
 | 
			
		|||
                                      model_converter_ref& mc,
 | 
			
		||||
                                      proof_converter_ref& pc) = 0;
 | 
			
		||||
 | 
			
		||||
        virtual void cancel() { m_cancel = true; }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
        Removes duplicate tails.
 | 
			
		||||
           Removes duplicate tails.
 | 
			
		||||
        */
 | 
			
		||||
        static void remove_duplicate_tails(app_ref_vector& tail, svector<bool>& tail_neg);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,7 @@ def_module_params('fixedpoint',
 | 
			
		|||
                          ('simplify_formulas_pre', BOOL, False, "PDR: simplify derived formulas before inductive propagation"),
 | 
			
		||||
                          ('simplify_formulas_post', BOOL, False, "PDR: simplify derived formulas after inductive propagation"),
 | 
			
		||||
                          ('slice', BOOL, True, "PDR: simplify clause set using slicing"),
 | 
			
		||||
	                  ('karr',  BOOL, False, "Add linear invariants to clauses using Karr's method"),
 | 
			
		||||
                          ('coalesce_rules', BOOL, False, "BMC: coalesce rules"),
 | 
			
		||||
                          ('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"),
 | 
			
		||||
                          ('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -686,6 +686,7 @@ void hilbert_basis::reset() {
 | 
			
		|||
    m_passive2->reset();
 | 
			
		||||
    m_zero.reset();
 | 
			
		||||
    m_index->reset();
 | 
			
		||||
    m_ints.reset();
 | 
			
		||||
    m_cancel = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1425,6 +1425,7 @@ namespace pdr {
 | 
			
		|||
            bool ok = checker.check(pr, side_conditions);
 | 
			
		||||
            if (!ok) {
 | 
			
		||||
                msg << "proof validation failed";
 | 
			
		||||
                IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";);
 | 
			
		||||
                throw default_exception(msg.str());
 | 
			
		||||
            }
 | 
			
		||||
            for (unsigned i = 0; i < side_conditions.size(); ++i) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1437,6 +1438,7 @@ namespace pdr {
 | 
			
		|||
                lbool res = solver.check();
 | 
			
		||||
                if (res != l_false) {
 | 
			
		||||
                    msg << "rule validation failed when checking: " << mk_pp(cond, m);
 | 
			
		||||
                    IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";);
 | 
			
		||||
                    throw default_exception(msg.str());
 | 
			
		||||
                }                                
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -1488,6 +1490,7 @@ namespace pdr {
 | 
			
		|||
                    lbool res = solver.check();
 | 
			
		||||
                    if (res != l_false) {
 | 
			
		||||
                        msg << "rule validation failed when checking: " << mk_pp(tmp, m);
 | 
			
		||||
                        IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";);
 | 
			
		||||
                        throw default_exception(msg.str());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -1595,6 +1598,7 @@ namespace pdr {
 | 
			
		|||
        catch (unknown_exception) {
 | 
			
		||||
            return l_undef;
 | 
			
		||||
        }
 | 
			
		||||
        UNREACHABLE();
 | 
			
		||||
        return l_undef;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,6 @@ Notes:
 | 
			
		|||
#include "for_each_expr.h"
 | 
			
		||||
#include "smt_params.h"
 | 
			
		||||
#include "model.h"
 | 
			
		||||
#include "model_v2_pp.h"
 | 
			
		||||
#include "ref_vector.h"
 | 
			
		||||
#include "rewriter.h"
 | 
			
		||||
#include "rewriter_def.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +41,7 @@ Notes:
 | 
			
		|||
#include "pdr_util.h"
 | 
			
		||||
#include "arith_decl_plugin.h"
 | 
			
		||||
#include "expr_replacer.h"
 | 
			
		||||
#include "model_smt2_pp.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace pdr {
 | 
			
		||||
| 
						 | 
				
			
			@ -510,13 +510,24 @@ namespace pdr {
 | 
			
		|||
            set_x(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void model_evaluator::eval_exprs(expr_ref_vector& es) {
 | 
			
		||||
        model_ref mr(m_model);
 | 
			
		||||
        for (unsigned j = 0; j < es.size(); ++j) {
 | 
			
		||||
            if (m_array.is_as_array(es[j].get())) {
 | 
			
		||||
                es[j] = eval(mr, es[j].get());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bool model_evaluator::extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case) {
 | 
			
		||||
        SASSERT(m_array.is_array(a));
 | 
			
		||||
        
 | 
			
		||||
        TRACE("pdr", tout << mk_pp(a, m) << "\n";);
 | 
			
		||||
        while (m_array.is_store(a)) {
 | 
			
		||||
            expr_ref_vector store(m);
 | 
			
		||||
            store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1);
 | 
			
		||||
            eval_exprs(store);
 | 
			
		||||
            stores.push_back(store);
 | 
			
		||||
            a = to_app(a)->get_arg(0);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -526,7 +537,7 @@ namespace pdr {
 | 
			
		|||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (m_array.is_as_array(a)) {
 | 
			
		||||
        while (m_array.is_as_array(a)) {
 | 
			
		||||
            func_decl* f = m_array.get_as_array_func_decl(to_app(a));
 | 
			
		||||
            func_interp* g = m_model->get_func_interp(f);
 | 
			
		||||
            unsigned sz = g->num_entries();
 | 
			
		||||
| 
						 | 
				
			
			@ -538,20 +549,30 @@ namespace pdr {
 | 
			
		|||
                store.push_back(fe->get_result());
 | 
			
		||||
                for (unsigned j = 0; j < store.size(); ++j) {
 | 
			
		||||
                    if (!is_ground(store[j].get())) {
 | 
			
		||||
                        TRACE("pdr", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";);
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                eval_exprs(store);
 | 
			
		||||
                stores.push_back(store);
 | 
			
		||||
            }        
 | 
			
		||||
            else_case = g->get_else();
 | 
			
		||||
            if (!else_case) {
 | 
			
		||||
                TRACE("pdr", tout << "no else case " << mk_pp(a, m) << "\n";);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            if (!is_ground(else_case)) {
 | 
			
		||||
                TRACE("pdr", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            if (m_array.is_as_array(else_case)) {
 | 
			
		||||
                model_ref mr(m_model);
 | 
			
		||||
                else_case = eval(mr, else_case);
 | 
			
		||||
            }
 | 
			
		||||
            TRACE("pdr", tout << "else case: " << mk_pp(else_case, m) << "\n";);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        TRACE("pdr", tout << "no translation: " << mk_pp(a, m) << "\n";);
 | 
			
		||||
        
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -570,7 +591,8 @@ namespace pdr {
 | 
			
		|||
        }
 | 
			
		||||
        sort* s = m.get_sort(arg1);
 | 
			
		||||
        sort* r = get_array_range(s);
 | 
			
		||||
        if (!r->is_infinite() && !r->is_very_big()) {
 | 
			
		||||
        // give up evaluating finite domain/range arrays
 | 
			
		||||
        if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) {
 | 
			
		||||
            TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
 | 
			
		||||
            set_x(e);
 | 
			
		||||
            return;
 | 
			
		||||
| 
						 | 
				
			
			@ -591,6 +613,9 @@ namespace pdr {
 | 
			
		|||
                      << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";);
 | 
			
		||||
                set_false(e);
 | 
			
		||||
            }
 | 
			
		||||
            else if (m_array.is_array(else1)) {
 | 
			
		||||
                eval_array_eq(e, else1, else2);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
 | 
			
		||||
                set_x(e);
 | 
			
		||||
| 
						 | 
				
			
			@ -614,18 +639,23 @@ namespace pdr {
 | 
			
		|||
            if (w1 == w2) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            else if (m.is_value(w1) && m.is_value(w2)) {
 | 
			
		||||
            if (m.is_value(w1) && m.is_value(w2)) {
 | 
			
		||||
                TRACE("pdr", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; 
 | 
			
		||||
                      tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n";
 | 
			
		||||
                      tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";);
 | 
			
		||||
                set_false(e);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            else if (m_array.is_array(w1)) {
 | 
			
		||||
                eval_array_eq(e, w1, w2);
 | 
			
		||||
                if (is_true(e)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
 | 
			
		||||
                set_x(e);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        set_true(e);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -869,6 +899,7 @@ namespace pdr {
 | 
			
		|||
            }
 | 
			
		||||
            if (is_x(form)) {
 | 
			
		||||
                IF_VERBOSE(0, verbose_stream() << "formula undetermined in model: " << mk_pp(form, m) << "\n";);
 | 
			
		||||
                TRACE("pdr", model_smt2_pp(tout, m, *m_model, 0);); 
 | 
			
		||||
                has_x = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,6 +104,8 @@ namespace pdr {
 | 
			
		|||
        bool check_model(ptr_vector<expr> const & formulas);
 | 
			
		||||
 | 
			
		||||
        bool extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case);
 | 
			
		||||
 | 
			
		||||
        void eval_exprs(expr_ref_vector& es);
 | 
			
		||||
        
 | 
			
		||||
    public:
 | 
			
		||||
        model_evaluator(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue