mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	add basic built-in consequence finding
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									b7de813c63
								
							
						
					
					
						commit
						14f29e7265
					
				
					 6 changed files with 200 additions and 13 deletions
				
			
		| 
						 | 
				
			
			@ -413,4 +413,42 @@ extern "C" {
 | 
			
		|||
        Z3_CATCH_RETURN(Z3_L_UNDEF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Z3_lbool Z3_API Z3_solver_get_consequences(Z3_context c, 
 | 
			
		||||
                                        Z3_solver s,
 | 
			
		||||
                                        Z3_ast_vector assumptions,
 | 
			
		||||
                                        Z3_ast_vector variables,
 | 
			
		||||
                                        Z3_ast_vector consequences) {
 | 
			
		||||
        Z3_TRY;
 | 
			
		||||
        LOG_Z3_solver_get_consequences(c, s, assumptions, variables, consequences);
 | 
			
		||||
        ast_manager& m = mk_c(c)->m();
 | 
			
		||||
        RESET_ERROR_CODE();
 | 
			
		||||
        CHECK_SEARCHING(c);
 | 
			
		||||
        init_solver(c, s);
 | 
			
		||||
        expr_ref_vector _assumptions(m), _consequences(m), _variables(m);
 | 
			
		||||
        ast_ref_vector const& __assumptions = to_ast_vector_ref(assumptions);
 | 
			
		||||
        unsigned sz = __assumptions.size();
 | 
			
		||||
        for (unsigned i = 0; i < sz; ++i) {
 | 
			
		||||
            if (!is_expr(__assumptions[i])) {
 | 
			
		||||
                SET_ERROR_CODE(Z3_INVALID_USAGE);
 | 
			
		||||
                return Z3_L_UNDEF;
 | 
			
		||||
            }
 | 
			
		||||
            _assumptions.push_back(to_expr(__assumptions[i]));
 | 
			
		||||
        }
 | 
			
		||||
        ast_ref_vector const& __variables = to_ast_vector_ref(variables);
 | 
			
		||||
        sz = __variables.size();
 | 
			
		||||
        for (unsigned i = 0; i < sz; ++i) {
 | 
			
		||||
            if (!is_expr(__variables[i])) {
 | 
			
		||||
                SET_ERROR_CODE(Z3_INVALID_USAGE);
 | 
			
		||||
                return Z3_L_UNDEF;
 | 
			
		||||
            }
 | 
			
		||||
            _variables.push_back(to_expr(__variables[i]));
 | 
			
		||||
        }
 | 
			
		||||
        lbool result = to_solver_ref(s)->get_consequences(_assumptions, _variables, _consequences);
 | 
			
		||||
        for (unsigned i = 0; i < _consequences.size(); ++i) {
 | 
			
		||||
            to_ast_vector_ref(consequences).push_back(_consequences[i].get());
 | 
			
		||||
        }
 | 
			
		||||
        return static_cast<Z3_lbool>(result); 
 | 
			
		||||
        Z3_CATCH_RETURN(Z3_L_UNDEF);        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@ Notes:
 | 
			
		|||
--*/
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics.Contracts;
 | 
			
		||||
 | 
			
		||||
namespace Microsoft.Z3
 | 
			
		||||
| 
						 | 
				
			
			@ -212,12 +213,34 @@ namespace Microsoft.Z3
 | 
			
		|||
                r = (Z3_lbool)Native.Z3_solver_check(Context.nCtx, NativeObject);
 | 
			
		||||
            else
 | 
			
		||||
                r = (Z3_lbool)Native.Z3_solver_check_assumptions(Context.nCtx, NativeObject, (uint)assumptions.Length, AST.ArrayToNative(assumptions));
 | 
			
		||||
            switch (r)
 | 
			
		||||
            {
 | 
			
		||||
                case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE;
 | 
			
		||||
                case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE;
 | 
			
		||||
                default: return Status.UNKNOWN;
 | 
			
		||||
            return lboolToStatus(r);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Retrieve fixed assignments to the set of variables in the form of consequences.
 | 
			
		||||
        /// Each consequence is an implication of the form 
 | 
			
		||||
        ///
 | 
			
		||||
        ///       relevant-assumptions Implies variable = value
 | 
			
		||||
        /// 
 | 
			
		||||
        /// where the relevant assumptions is a subset of the assumptions that are passed in
 | 
			
		||||
        /// and the equality on the right side of the implication indicates how a variable
 | 
			
		||||
        /// is fixed.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// <seealso cref="Model"/>
 | 
			
		||||
        /// <seealso cref="UnsatCore"/>
 | 
			
		||||
        /// <seealso cref="Proof"/>    
 | 
			
		||||
        /// </remarks>    
 | 
			
		||||
        public Status Consequences(IEnumerable<BoolExpr> assumptions, IEnumerable<Expr> variables, out BoolExpr[] consequences) 
 | 
			
		||||
        {
 | 
			
		||||
            ASTVector result = new ASTVector(Context);
 | 
			
		||||
            ASTVector asms = new ASTVector(Context);
 | 
			
		||||
            ASTVector vars = new ASTVector(Context);
 | 
			
		||||
            foreach (var asm in assumptions) asms.Push(asm);
 | 
			
		||||
            foreach (var v in variables) vars.Push(v);
 | 
			
		||||
            Z3_lbool r = (Z3_lbool)Native.Z3_solver_get_consequences(Context.nCtx, NativeObject, asms.NativeObject, vars.NativeObject, result.NativeObject);
 | 
			
		||||
            consequences = result.ToBoolExprArray();
 | 
			
		||||
            return lboolToStatus(r);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
| 
						 | 
				
			
			@ -355,6 +378,17 @@ namespace Microsoft.Z3
 | 
			
		|||
            Context.Solver_DRQ.Add(o);
 | 
			
		||||
            base.DecRef(o);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Status lboolToStatus(Z3_lbool r) 
 | 
			
		||||
        {
 | 
			
		||||
            switch (r)
 | 
			
		||||
            {
 | 
			
		||||
                case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE;
 | 
			
		||||
                case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE;
 | 
			
		||||
                default: return Status.UNKNOWN;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6123,6 +6123,28 @@ class Solver(Z3PPObject):
 | 
			
		|||
        """
 | 
			
		||||
        return AstVector(Z3_solver_get_unsat_core(self.ctx.ref(), self.solver), self.ctx)
 | 
			
		||||
 | 
			
		||||
    def consequences(self, assumptions, variables):
 | 
			
		||||
        """Determine fixed values for the variables based on the solver state and assumptions.
 | 
			
		||||
        documentation TBD
 | 
			
		||||
        """
 | 
			
		||||
        if isinstance(assumptions, list):
 | 
			
		||||
            _asms = AstVector(None, self.ctx)
 | 
			
		||||
            for a in assumptions:
 | 
			
		||||
                _asms.push(a)
 | 
			
		||||
            assumptions = _asms
 | 
			
		||||
        if isinstance(variables, list):
 | 
			
		||||
            _vars = AstVector(None, self.ctx)
 | 
			
		||||
            for a in variables:
 | 
			
		||||
                _vars.push(a)
 | 
			
		||||
            variables = _vars            
 | 
			
		||||
        _z3_assert(isinstance(assumptions, AstVector), "ast vector expected")
 | 
			
		||||
        _z3_assert(isinstance(variables, AstVector), "ast vector expected")
 | 
			
		||||
        consequences = AstVector(None, self.ctx)
 | 
			
		||||
        r = Z3_solver_get_consequences(self.ctx.ref(), self.solver, assumptions.vector, variables.vector, consequences.vector)
 | 
			
		||||
        sz = len(consequences)
 | 
			
		||||
        consequences = [ consequences[i] for i in range(sz) ]
 | 
			
		||||
        return CheckSatResult(r), consequences
 | 
			
		||||
    
 | 
			
		||||
    def proof(self):
 | 
			
		||||
        """Return a proof for the last `check()`. Proof construction must be enabled."""
 | 
			
		||||
        return _to_expr_ref(Z3_solver_get_proof(self.ctx.ref(), self.solver), self.ctx)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5887,6 +5887,17 @@ extern "C" {
 | 
			
		|||
                                              Z3_ast const terms[],
 | 
			
		||||
                                              unsigned class_ids[]);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief retrieve consequences from solver that determine values of the supplied function symbols.
 | 
			
		||||
       
 | 
			
		||||
       def_API('Z3_solver_get_consequences', INT, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(AST_VECTOR), _in(AST_VECTOR)))
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    Z3_lbool Z3_API Z3_solver_get_consequences(Z3_context c, 
 | 
			
		||||
                                               Z3_solver s,
 | 
			
		||||
                                               Z3_ast_vector assumptions,
 | 
			
		||||
                                               Z3_ast_vector variables,
 | 
			
		||||
                                               Z3_ast_vector consequences);
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Retrieve the model for the last #Z3_solver_check or #Z3_solver_check_assumptions
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,8 @@ Notes:
 | 
			
		|||
 | 
			
		||||
--*/
 | 
			
		||||
#include"solver.h"
 | 
			
		||||
#include"model_evaluator.h"
 | 
			
		||||
#include"ast_util.h"
 | 
			
		||||
 | 
			
		||||
unsigned solver::get_num_assertions() const {
 | 
			
		||||
    NOT_IMPLEMENTED_YET();
 | 
			
		||||
| 
						 | 
				
			
			@ -38,3 +40,82 @@ void solver::get_assertions(expr_ref_vector& fmls) const {
 | 
			
		|||
        fmls.push_back(get_assertion(i));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct scoped_assumption_push {
 | 
			
		||||
    expr_ref_vector& m_vec;
 | 
			
		||||
    scoped_assumption_push(expr_ref_vector& v, expr* e): m_vec(v) { v.push_back(e); }
 | 
			
		||||
    ~scoped_assumption_push() { m_vec.pop_back(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
lbool solver::get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) {
 | 
			
		||||
    ast_manager& m = asms.get_manager();
 | 
			
		||||
    lbool is_sat = check_sat(asms);
 | 
			
		||||
    if (is_sat != l_true) {
 | 
			
		||||
        return is_sat;
 | 
			
		||||
    }
 | 
			
		||||
    model_ref model;
 | 
			
		||||
    get_model(model);
 | 
			
		||||
    expr_ref tmp(m), nlit(m), lit(m), val(m);
 | 
			
		||||
    expr_ref_vector asms1(asms);
 | 
			
		||||
    model_evaluator eval(*model.get());
 | 
			
		||||
    unsigned k = 0;
 | 
			
		||||
    for (unsigned i = 0; i < vars.size(); ++i) {
 | 
			
		||||
        expr_ref_vector core(m);
 | 
			
		||||
        tmp = vars[i];
 | 
			
		||||
        val = eval(tmp);
 | 
			
		||||
        if (!m.is_value(val)) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (m.is_bool(tmp) && is_uninterp_const(tmp)) {
 | 
			
		||||
            if (m.is_true(val)) {
 | 
			
		||||
                nlit = m.mk_not(tmp);
 | 
			
		||||
                lit = tmp;
 | 
			
		||||
            }
 | 
			
		||||
            else if (m.is_false(val)) {
 | 
			
		||||
                nlit = tmp;
 | 
			
		||||
                lit = m.mk_not(tmp);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            scoped_assumption_push _scoped_push(asms1, nlit);
 | 
			
		||||
            is_sat = check_sat(asms1);
 | 
			
		||||
            switch (is_sat) {
 | 
			
		||||
            case l_undef: 
 | 
			
		||||
                return is_sat;
 | 
			
		||||
            case l_true:
 | 
			
		||||
                break;
 | 
			
		||||
            case l_false:
 | 
			
		||||
                get_unsat_core(core);
 | 
			
		||||
                k = 0;
 | 
			
		||||
                for (unsigned j = 0; j < core.size(); ++j) {
 | 
			
		||||
                    if (core[j].get() != nlit) {
 | 
			
		||||
                        core[k] = core[j].get();
 | 
			
		||||
                        ++k;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                core.resize(k);
 | 
			
		||||
                consequences.push_back(m.mk_implies(mk_and(core), lit));
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            lit = m.mk_eq(tmp, val);
 | 
			
		||||
            nlit = m.mk_not(lit);
 | 
			
		||||
            scoped_push _scoped_push(*this);
 | 
			
		||||
            assert_expr(nlit);
 | 
			
		||||
            is_sat = check_sat(asms);            
 | 
			
		||||
            switch (is_sat) {
 | 
			
		||||
            case l_undef: 
 | 
			
		||||
                return is_sat;
 | 
			
		||||
            case l_true:
 | 
			
		||||
                break;
 | 
			
		||||
            case l_false:
 | 
			
		||||
                get_unsat_core(core);
 | 
			
		||||
                consequences.push_back(m.mk_implies(mk_and(core), lit));
 | 
			
		||||
                break;
 | 
			
		||||
            }            
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return l_true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -150,12 +150,13 @@ public:
 | 
			
		|||
    virtual expr * get_assumption(unsigned idx) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    \brief under assumptions, asms, retrieve set of consequences that fix values for expressions that can be 
 | 
			
		||||
      built from fns. For functions that take 0 arguments, we require that the function returns all consequences
 | 
			
		||||
      that mention these functions. The consequences are clauses whose first literal constrain one of the 
 | 
			
		||||
      functions from fns and the other literals are negations of literals from asms.
 | 
			
		||||
    \brief under assumptions, asms, retrieve set of consequences that 
 | 
			
		||||
    fix values for expressions that can be built from vars. 
 | 
			
		||||
    The consequences are clauses whose first literal constrain one of the 
 | 
			
		||||
    functions from vars and the other literals are negations of literals from asms.
 | 
			
		||||
    */
 | 
			
		||||
    //virtual lbool get_consequences(expr_ref_vector const& asms, func_ref_vector const& fns, expr_ref_vector& consequences);
 | 
			
		||||
    
 | 
			
		||||
    virtual lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Display the content of this solver.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue