bool Z3SolverImpl::internalRunSolver( const Query &query, const std::vector<const Array *> *objects, std::vector<std::vector<unsigned char> > *values, bool &hasSolution) { TimerStatIncrementer t(stats::queryTime); // TODO: Does making a new solver for each query have a performance // impact vs making one global solver and using push and pop? // TODO: is the "simple_solver" the right solver to use for // best performance? Z3_solver theSolver = Z3_mk_simple_solver(builder->ctx); Z3_solver_inc_ref(builder->ctx, theSolver); Z3_solver_set_params(builder->ctx, theSolver, solverParameters); runStatusCode = SOLVER_RUN_STATUS_FAILURE; for (ConstraintManager::const_iterator it = query.constraints.begin(), ie = query.constraints.end(); it != ie; ++it) { Z3_solver_assert(builder->ctx, theSolver, builder->construct(*it)); } ++stats::queries; if (objects) ++stats::queryCounterexamples; Z3ASTHandle z3QueryExpr = Z3ASTHandle(builder->construct(query.expr), builder->ctx); // KLEE Queries are validity queries i.e. // ∀ X Constraints(X) → query(X) // but Z3 works in terms of satisfiability so instead we ask the // negation of the equivalent i.e. // ∃ X Constraints(X) ∧ ¬ query(X) Z3_solver_assert( builder->ctx, theSolver, Z3ASTHandle(Z3_mk_not(builder->ctx, z3QueryExpr), builder->ctx)); ::Z3_lbool satisfiable = Z3_solver_check(builder->ctx, theSolver); runStatusCode = handleSolverResponse(theSolver, satisfiable, objects, values, hasSolution); Z3_solver_dec_ref(builder->ctx, theSolver); // Clear the builder's cache to prevent memory usage exploding. // By using ``autoClearConstructCache=false`` and clearning now // we allow Z3_ast expressions to be shared from an entire // ``Query`` rather than only sharing within a single call to // ``builder->construct()``. builder->clearConstructCache(); if (runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE || runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE) { if (hasSolution) { ++stats::queriesInvalid; } else { ++stats::queriesValid; } return true; // success } return false; // failed }
int main(){ Z3_context context; Z3_solver solver; Z3_sort bvsort1; Z3_sort bvsort4; Z3_sort memsort; Z3_ast x_ast,y_ast,z_ast,u_ast,v_ast,w_ast,test_ast; Z3_model model; Z3_config config = Z3_mk_config(); Z3_set_param_value(config,"model","true"); context = Z3_mk_context_rc(config); Z3_set_error_handler(context,error_handler); solver = Z3_mk_solver(context); Z3_solver_inc_ref(context,solver); bvsort1 = Z3_mk_bv_sort(context,8); bvsort4 = Z3_mk_bv_sort(context,32); memsort = Z3_mk_array_sort(context,bvsort4,bvsort1); y_ast = Z3_mk_const(context,Z3_mk_string_symbol(context,"mem"),memsort); Z3_inc_ref(context,y_ast); u_ast = Z3_mk_unsigned_int64(context,13,bvsort4); Z3_inc_ref(context,u_ast); v_ast = Z3_mk_select(context,y_ast,u_ast); Z3_inc_ref(context,v_ast); z_ast = Z3_mk_unsigned_int64(context,7,bvsort1); Z3_inc_ref(context,z_ast); test_ast = Z3_mk_eq(context,v_ast,z_ast); Z3_inc_ref(context,test_ast); Z3_solver_assert(context,solver,test_ast); w_ast = Z3_mk_const(context,Z3_mk_string_symbol(context,"w"),bvsort1); y_ast = Z3_mk_store(context,y_ast,u_ast,w_ast); Z3_inc_ref(context,y_ast); v_ast = Z3_mk_select(context,y_ast,u_ast); Z3_inc_ref(context,v_ast); z_ast = Z3_mk_unsigned_int64(context,2,bvsort1); Z3_inc_ref(context,z_ast); test_ast = Z3_mk_eq(context,v_ast,z_ast); Z3_inc_ref(context,test_ast); Z3_solver_assert(context,solver,test_ast); Z3_solver_check(context,solver); model = Z3_solver_get_model(context,solver); fprintf(stderr,"%s\n",Z3_model_to_string(context,model)); fprintf(stderr,"%s\n",Z3_simplify_get_help(context)); return 0; }
static void tst_get_implied_equalities1() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); Z3_sort int_ty = Z3_mk_int_sort(ctx); Z3_ast a = mk_int_var(ctx,"a"); Z3_ast b = mk_int_var(ctx,"b"); Z3_ast c = mk_int_var(ctx,"c"); Z3_ast d = mk_int_var(ctx,"d"); Z3_func_decl f = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx,"f"), 1, &int_ty, int_ty); Z3_ast fa = Z3_mk_app(ctx, f, 1, &a); Z3_ast fb = Z3_mk_app(ctx, f, 1, &b); Z3_ast fc = Z3_mk_app(ctx, f, 1, &c); unsigned const num_terms = 7; unsigned i; Z3_ast terms[7] = { a, b, c, d, fa, fb, fc }; unsigned class_ids[7] = { 0, 0, 0, 0, 0, 0, 0 }; Z3_solver solver = Z3_mk_simple_solver(ctx); Z3_solver_inc_ref(ctx, solver); Z3_solver_assert(ctx, solver, Z3_mk_eq(ctx, a, b)); Z3_solver_assert(ctx, solver, Z3_mk_eq(ctx, b, d)); Z3_solver_assert(ctx, solver, Z3_mk_le(ctx, fa, fc)); Z3_solver_assert(ctx, solver, Z3_mk_le(ctx, fc, d)); Z3_get_implied_equalities(ctx, solver, num_terms, terms, class_ids); for (i = 0; i < num_terms; ++i) { printf("Class %s |-> %d\n", Z3_ast_to_string(ctx, terms[i]), class_ids[i]); } SASSERT(class_ids[1] == class_ids[0]); SASSERT(class_ids[2] != class_ids[0]); SASSERT(class_ids[3] == class_ids[0]); SASSERT(class_ids[4] != class_ids[0]); SASSERT(class_ids[5] != class_ids[0]); SASSERT(class_ids[6] != class_ids[0]); SASSERT(class_ids[4] == class_ids[5]); printf("asserting b <= f(a)\n"); Z3_solver_assert(ctx, solver, Z3_mk_le(ctx, b, fa)); Z3_get_implied_equalities(ctx, solver, num_terms, terms, class_ids); for (i = 0; i < num_terms; ++i) { printf("Class %s |-> %d\n", Z3_ast_to_string(ctx, terms[i]), class_ids[i]); } SASSERT(class_ids[1] == class_ids[0]); SASSERT(class_ids[2] != class_ids[0]); SASSERT(class_ids[3] == class_ids[0]); SASSERT(class_ids[4] == class_ids[0]); SASSERT(class_ids[5] == class_ids[0]); SASSERT(class_ids[6] == class_ids[0]); Z3_solver_dec_ref(ctx, solver); /* delete logical context */ Z3_del_context(ctx); }
/** \brief Assert hard constraints stored in the given array. */ void assert_hard_constraints(Z3_context ctx, Z3_solver s, unsigned num_cnstrs, Z3_ast * cnstrs) { unsigned i; for (i = 0; i < num_cnstrs; i++) { Z3_solver_assert(ctx, s, cnstrs[i]); } }
/* (declare-fun p_name (E3 E3 ...) Bool) */ Z3_func_decl gen_pred(z3_wrapper *z3, const char *name, const pred *pred) { Z3_symbol symb = Z3_mk_string_symbol(z3->ctx, name); Z3_sort domain[pred->arity]; for(size_t i = 0; i < pred->arity; ++i) { domain[i] = z3->Ek_sort; } Z3_sort range = Z3_mk_bool_sort(z3->ctx); Z3_func_decl p = Z3_mk_func_decl(z3->ctx, symb, pred->arity, domain, range); /* create assertions */ for(size_t xs = 0; xs < int_pow(K, pred->arity); ++xs) { /* represent `xs` in the K-ary form, * with digits[0] being the highest digit. */ uint32_t digits[pred->arity]; get_K_digits(digits, pred->arity, xs); Z3_ast args[pred->arity]; for(size_t i = 0; i < pred->arity; ++i) { args[i] = z3->Ek_consts[digits[i]]; } Z3_ast phi = Z3_mk_app(z3->ctx, p, pred->arity, args); /* if the predicate equals to false on `xs` */ if(!pred_compute(pred, xs)) { phi = Z3_mk_not(z3->ctx, phi); } /* printf("\n%s\n", Z3_ast_to_string(z3->ctx, phi)); */ Z3_solver_assert(z3->ctx, z3->solver, phi); } return p; }
/** \brief Small test for the at-most-one constraint. */ void tst_at_most_one() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_ast k1 = mk_bool_var(ctx, "k1"); Z3_ast k2 = mk_bool_var(ctx, "k2"); Z3_ast k3 = mk_bool_var(ctx, "k3"); Z3_ast k4 = mk_bool_var(ctx, "k4"); Z3_ast k5 = mk_bool_var(ctx, "k5"); Z3_ast k6 = mk_bool_var(ctx, "k6"); Z3_ast args1[5] = { k1, k2, k3, k4, k5 }; Z3_ast args2[3] = { k4, k5, k6 }; Z3_model m = 0; Z3_lbool result; printf("testing at-most-one constraint\n"); assert_at_most_one(ctx, s, 5, args1); assert_at_most_one(ctx, s, 3, args2); printf("it must be sat...\n"); result = Z3_solver_check(ctx, s); if (result != Z3_L_TRUE) error("BUG"); m = Z3_solver_get_model(ctx, s); Z3_model_inc_ref(ctx, m); printf("model:\n%s\n", Z3_model_to_string(ctx, m)); Z3_model_dec_ref(ctx, m); Z3_solver_assert(ctx, s, mk_binary_or(ctx, k2, k3)); Z3_solver_assert(ctx, s, mk_binary_or(ctx, k1, k6)); printf("it must be sat...\n"); result = Z3_solver_check(ctx, s); if (result != Z3_L_TRUE) error("BUG"); m = Z3_solver_get_model(ctx, s); Z3_model_inc_ref(ctx, m); printf("model:\n%s\n", Z3_model_to_string(ctx, m)); Z3_solver_assert(ctx, s, mk_binary_or(ctx, k4, k5)); printf("it must be unsat...\n"); result = Z3_solver_check(ctx, s); if (result != Z3_L_FALSE) error("BUG"); Z3_model_dec_ref(ctx, m); Z3_solver_dec_ref(ctx, s); Z3_del_context(ctx); }
/** \brief Assert soft constraints stored in the given array. This function will assert each soft-constraint C_i as (C_i or k_i) where k_i is a fresh boolean variable. It will also return an array containing these fresh variables. */ Z3_ast * assert_soft_constraints(Z3_context ctx, Z3_solver s, unsigned num_cnstrs, Z3_ast * cnstrs) { unsigned i; Z3_ast * aux_vars; aux_vars = mk_fresh_bool_var_array(ctx, num_cnstrs); for (i = 0; i < num_cnstrs; i++) { Z3_ast assumption = cnstrs[i]; Z3_solver_assert(ctx, s, mk_binary_or(ctx, assumption, aux_vars[i])); } return aux_vars; }
/** \brief Implement one step of the Fu&Malik algorithm. See fu_malik_maxsat function for more details. Input: soft constraints + aux-vars (aka answer literals) Output: done/not-done when not done return updated set of soft-constraints and aux-vars. - if SAT --> terminates - if UNSAT * compute unsat core * add blocking variable to soft-constraints in the core - replace soft-constraint with the one with the blocking variable - we should also add an aux-var - replace aux-var with a new one * add at-most-one constraint with blocking */ int fu_malik_maxsat_step(Z3_context ctx, Z3_solver s, unsigned num_soft_cnstrs, Z3_ast * soft_cnstrs, Z3_ast * aux_vars) { // create assumptions Z3_ast * assumptions = (Z3_ast*) malloc(sizeof(Z3_ast) * num_soft_cnstrs); Z3_lbool is_sat; Z3_ast_vector core; unsigned core_size; unsigned i = 0; unsigned k = 0; Z3_ast * block_vars; for (i = 0; i < num_soft_cnstrs; i++) { // Recall that we asserted (soft_cnstrs[i] \/ aux_vars[i]) // So using (NOT aux_vars[i]) as an assumption we are actually forcing the soft_cnstrs[i] to be considered. assumptions[i] = Z3_mk_not(ctx, aux_vars[i]); } is_sat = Z3_solver_check_assumptions(ctx, s, num_soft_cnstrs, assumptions); if (is_sat != Z3_L_FALSE) { return 1; // done } else { core = Z3_solver_get_unsat_core(ctx, s); Z3_ast_vector_inc_ref(ctx, core); core_size = Z3_ast_vector_size(ctx, core); block_vars = (Z3_ast*) malloc(sizeof(Z3_ast) * core_size); k = 0; // update soft-constraints and aux_vars for (i = 0; i < num_soft_cnstrs; i++) { unsigned j; // check whether assumption[i] is in the core or not for (j = 0; j < core_size; j++) { if (assumptions[i] == Z3_ast_vector_get(ctx, core, j)) break; } if (j < core_size) { // assumption[i] is in the unsat core... so soft_cnstrs[i] is in the unsat core Z3_ast block_var = mk_fresh_bool_var(ctx); Z3_ast new_aux_var = mk_fresh_bool_var(ctx); soft_cnstrs[i] = mk_binary_or(ctx, soft_cnstrs[i], block_var); aux_vars[i] = new_aux_var; block_vars[k] = block_var; k++; // Add new constraint containing the block variable. // Note that we are using the new auxiliary variable to be able to use it as an assumption. Z3_solver_assert(ctx, s, mk_binary_or(ctx, soft_cnstrs[i], new_aux_var)); } } assert_at_most_one(ctx, s, k, block_vars); Z3_ast_vector_dec_ref(ctx, core); return 0; // not done. } }
/** \brief Given an integer val encoded in n bits (boolean variables), assert the constraint that val <= k. */ void assert_le_k(Z3_context ctx, Z3_solver s, unsigned n, Z3_ast * val, unsigned k) { Z3_ast i1, i2, not_val, out; unsigned idx; not_val = Z3_mk_not(ctx, val[0]); if (get_bit(k, 0)) out = Z3_mk_true(ctx); else out = not_val; for (idx = 1; idx < n; idx++) { not_val = Z3_mk_not(ctx, val[idx]); if (get_bit(k, idx)) { i1 = not_val; i2 = out; } else { i1 = Z3_mk_false(ctx); i2 = Z3_mk_false(ctx); } out = mk_ternary_or(ctx, i1, i2, mk_binary_and(ctx, not_val, out)); } // printf("at-most-k:\n%s\n", Z3_ast_to_string(ctx, out)); Z3_solver_assert(ctx, s, out); }
void gen_preserve(z3_wrapper *z3, int not_preserve, Z3_func_decl fun, uint32_t fun_arity, Z3_func_decl pred, uint32_t pred_arity) { /* create bound variables */ Z3_ast bound_vars[fun_arity][pred_arity]; size_t idx = fun_arity * pred_arity - 1; for(int i = 0; i < fun_arity; ++i) { for(int j = 0; j < pred_arity; ++j) { bound_vars[i][j] = Z3_mk_bound(z3->ctx, idx, z3->Ek_sort); --idx; } } /* create left-hand side of implication */ Z3_ast t1; if(fun_arity == 0) { t1 = Z3_mk_true(z3->ctx); } else { /* fun_arity >= 1*/ Z3_ast and_args[fun_arity]; for(int i = 0; i < fun_arity; ++i) { Z3_ast pred_args[pred_arity]; for(int j = 0; j < pred_arity; ++j) { pred_args[j] = bound_vars[i][j]; } and_args[i] = Z3_mk_app(z3->ctx, pred, pred_arity, pred_args); } t1 = Z3_mk_and(z3->ctx, fun_arity, and_args); } /* create right-hand side of implication */ Z3_ast pred_args[pred_arity]; for(int j = 0; j < pred_arity; ++j) { Z3_ast fun_args[fun_arity]; for(int i = 0; i < fun_arity; ++i) { fun_args[i] = bound_vars[i][j]; } pred_args[j] = Z3_mk_app(z3->ctx, fun, fun_arity, fun_args); } Z3_ast t2 = Z3_mk_app(z3->ctx, pred, pred_arity, pred_args); /* create implication */ Z3_ast body = Z3_mk_implies(z3->ctx, t1, t2); /* create quantified formula phi */ size_t num_decls = fun_arity * pred_arity; Z3_sort sorts[num_decls]; for(int i = 0; i < num_decls; ++i) { sorts[i] = z3->Ek_sort; } Z3_symbol decl_names[num_decls]; idx = fun_arity * pred_arity - 1; for(int i = 0; i < fun_arity; ++i) { for(int j = 0; j < pred_arity; ++j) { char s[16]; sprintf(s, "x%d_%d ", i, j); decl_names[idx] = Z3_mk_string_symbol(z3->ctx, s); --idx; } } Z3_ast phi = Z3_mk_forall(z3->ctx, 0, 0, NULL, num_decls, sorts, decl_names, body); if(not_preserve) { phi = Z3_mk_not(z3->ctx, phi); } /* printf("\n%s\n", Z3_ast_to_string(z3->ctx, phi)); */ Z3_solver_assert(z3->ctx, z3->solver, phi); }
bool Z3SolverImpl::internalRunSolver( const Query &query, const std::vector<const Array *> *objects, std::vector<std::vector<unsigned char> > *values, bool &hasSolution) { TimerStatIncrementer t(stats::queryTime); // NOTE: Z3 will switch to using a slower solver internally if push/pop are // used so for now it is likely that creating a new solver each time is the // right way to go until Z3 changes its behaviour. // // TODO: Investigate using a custom tactic as described in // https://github.com/klee/klee/issues/653 Z3_solver theSolver = Z3_mk_solver(builder->ctx); Z3_solver_inc_ref(builder->ctx, theSolver); Z3_solver_set_params(builder->ctx, theSolver, solverParameters); runStatusCode = SOLVER_RUN_STATUS_FAILURE; for (ConstraintManager::const_iterator it = query.constraints.begin(), ie = query.constraints.end(); it != ie; ++it) { Z3_solver_assert(builder->ctx, theSolver, builder->construct(*it)); } ++stats::queries; if (objects) ++stats::queryCounterexamples; Z3ASTHandle z3QueryExpr = Z3ASTHandle(builder->construct(query.expr), builder->ctx); // KLEE Queries are validity queries i.e. // ∀ X Constraints(X) → query(X) // but Z3 works in terms of satisfiability so instead we ask the // negation of the equivalent i.e. // ∃ X Constraints(X) ∧ ¬ query(X) Z3_solver_assert( builder->ctx, theSolver, Z3ASTHandle(Z3_mk_not(builder->ctx, z3QueryExpr), builder->ctx)); if (dumpedQueriesFile) { *dumpedQueriesFile << "; start Z3 query\n"; *dumpedQueriesFile << Z3_solver_to_string(builder->ctx, theSolver); *dumpedQueriesFile << "(check-sat)\n"; *dumpedQueriesFile << "(reset)\n"; *dumpedQueriesFile << "; end Z3 query\n\n"; dumpedQueriesFile->flush(); } ::Z3_lbool satisfiable = Z3_solver_check(builder->ctx, theSolver); runStatusCode = handleSolverResponse(theSolver, satisfiable, objects, values, hasSolution); Z3_solver_dec_ref(builder->ctx, theSolver); // Clear the builder's cache to prevent memory usage exploding. // By using ``autoClearConstructCache=false`` and clearning now // we allow Z3_ast expressions to be shared from an entire // ``Query`` rather than only sharing within a single call to // ``builder->construct()``. builder->clearConstructCache(); if (runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE || runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE) { if (hasSolution) { ++stats::queriesInvalid; } else { ++stats::queriesValid; } return true; // success } return false; // failed }