/*++ Copyright (c) 2011 Microsoft Corporation Module Name: api_interp.cpp Abstract: API for interpolation Author: Ken McMillan Revision History: --*/ #include #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_tactic.h" #include"api_solver.h" #include"api_model.h" #include"api_stats.h" #include"api_ast_vector.h" #include"tactic2solver.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"scoped_timer.h" #include"smt_strategic_solver.h" #include"smt_solver.h" #include"smt_implied_equalities.h" #include"iz3interp.h" #include"iz3profiling.h" #include"iz3hash.h" #include"iz3pp.h" #include"iz3checker.h" #ifndef WIN32 using namespace stl_ext; #endif typedef interpolation_options_struct *Z3_interpolation_options; extern "C" { Z3_context Z3_mk_interpolation_context(Z3_config cfg){ if(!cfg) cfg = Z3_mk_config(); Z3_set_param_value(cfg, "PROOF", "true"); Z3_set_param_value(cfg, "MODEL", "true"); // Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); // Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); return ctx; } void Z3_interpolate_proof(Z3_context ctx, Z3_ast proof, int num, Z3_ast *cnsts, unsigned *parents, Z3_params options, Z3_ast *interps, int num_theory, Z3_ast *theory ){ if(num > 1){ // if we have interpolants to compute ptr_vector pre_cnsts_vec(num); // get constraints in a vector for(int i = 0; i < num; i++){ ast *a = to_ast(cnsts[i]); pre_cnsts_vec[i] = a; } ::vector pre_parents_vec; // get parents in a vector if(parents){ pre_parents_vec.resize(num); for(int i = 0; i < num; i++) pre_parents_vec[i] = parents[i]; } ptr_vector theory_vec; // get background theory in a vector if(theory){ theory_vec.resize(num_theory); for(int i = 0; i < num_theory; i++) theory_vec[i] = to_ast(theory[i]); } ptr_vector interpolants(num-1); // make space for result ast_manager &_m = mk_c(ctx)->m(); iz3interpolate(_m, to_ast(proof), pre_cnsts_vec, pre_parents_vec, interpolants, theory_vec, 0); // ignore params for now FIXME // copy result back for(unsigned i = 0; i < interpolants.size(); i++){ mk_c(ctx)->save_ast_trail(interpolants[i]); interps[i] = of_ast(interpolants[i]); _m.dec_ref(interpolants[i]); } } } Z3_lbool Z3_interpolate(Z3_context ctx, int num, Z3_ast *cnsts, unsigned *parents, Z3_params options, Z3_ast *interps, Z3_model *model, Z3_literals *labels, int incremental, int num_theory, Z3_ast *theory ){ profiling::timer_start("Solve"); if(!incremental){ profiling::timer_start("Z3 assert"); Z3_push(ctx); // so we can rewind later for(int i = 0; i < num; i++) Z3_assert_cnstr(ctx,cnsts[i]); // assert all the constraints if(theory){ for(int i = 0; i < num_theory; i++) Z3_assert_cnstr(ctx,theory[i]); } profiling::timer_stop("Z3 assert"); } // Get a proof of unsat Z3_ast proof; Z3_lbool result; profiling::timer_start("Z3 solving"); result = Z3_check_assumptions(ctx, 0, 0, model, &proof, 0, 0); profiling::timer_stop("Z3 solving"); switch (result) { case Z3_L_FALSE: Z3_interpolate_proof(ctx, proof, num, cnsts, parents, options, interps, num_theory, theory); if(!incremental) for(int i = 0; i < num-1; i++) Z3_persist_ast(ctx,interps[i],1); break; case Z3_L_UNDEF: if(labels) *labels = Z3_get_relevant_labels(ctx); break; case Z3_L_TRUE: if(labels) *labels = Z3_get_relevant_labels(ctx); break; } profiling::timer_start("Z3 pop"); if(!incremental) Z3_pop(ctx,1); profiling::timer_stop("Z3 pop"); profiling::timer_stop("Solve"); return result; } static std::ostringstream itp_err; int Z3_check_interpolant(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, Z3_ast *itp, const char **error, int num_theory, Z3_ast *theory){ ast_manager &_m = mk_c(ctx)->m(); itp_err.clear(); // need a solver -- make one here, but how? params_ref p = params_ref::get_empty(); //FIXME scoped_ptr sf(mk_smt_solver_factory()); scoped_ptr sp((*(sf))(_m, p, false, true, false, symbol("AUFLIA"))); ptr_vector cnsts_vec(num); // get constraints in a vector for(int i = 0; i < num; i++){ ast *a = to_ast(cnsts[i]); cnsts_vec[i] = a; } ptr_vector itp_vec(num); // get interpolants in a vector for(int i = 0; i < num-1; i++){ ast *a = to_ast(itp[i]); itp_vec[i] = a; } ::vector parents_vec; // get parents in a vector if(parents){ parents_vec.resize(num); for(int i = 0; i < num; i++) parents_vec[i] = parents[i]; } ptr_vector theory_vec; // get background theory in a vector if(theory){ theory_vec.resize(num_theory); for(int i = 0; i < num_theory; i++) theory_vec[i] = to_ast(theory[i]); } bool res = iz3check(_m, sp.get(), itp_err, cnsts_vec, parents_vec, itp_vec, theory_vec); *error = res ? 0 : itp_err.str().c_str(); return res; } static std::string Z3_profile_string; Z3_string Z3_interpolation_profile(Z3_context ctx){ std::ostringstream f; profiling::print(f); Z3_profile_string = f.str(); return Z3_profile_string.c_str(); } Z3_interpolation_options Z3_mk_interpolation_options(){ return (Z3_interpolation_options) new interpolation_options_struct; } void Z3_del_interpolation_options(Z3_interpolation_options opts){ delete opts; } void Z3_set_interpolation_option(Z3_interpolation_options opts, Z3_string name, Z3_string value){ opts->map[name] = value; } }; static void tokenize(const std::string &str, std::vector &tokens){ for(unsigned i = 0; i < str.size();){ if(str[i] == ' '){i++; continue;} unsigned beg = i; while(i < str.size() && str[i] != ' ')i++; if(i > beg) tokens.push_back(str.substr(beg,i-beg)); } } static void get_file_params(const char *filename, hash_map ¶ms){ std::ifstream f(filename); if(f){ std::string first_line; std::getline(f,first_line); // std::cout << "first line: '" << first_line << "'" << std::endl; if(first_line.size() >= 2 && first_line[0] == ';' && first_line[1] == '!'){ std::vector tokens; tokenize(first_line.substr(2,first_line.size()-2),tokens); for(unsigned i = 0; i < tokens.size(); i++){ std::string &tok = tokens[i]; int eqpos = tok.find('='); if(eqpos >= 0 && eqpos < (int)tok.size()){ std::string left = tok.substr(0,eqpos); std::string right = tok.substr(eqpos+1,tok.size()-eqpos-1); params[left] = right; } } } f.close(); } } extern "C" { #if 0 static void iZ3_write_seq(Z3_context ctx, int num, Z3_ast *cnsts, const char *filename, int num_theory, Z3_ast *theory){ int num_fmlas = num+num_theory; std::vector fmlas(num_fmlas); if(num_theory) std::copy(theory,theory+num_theory,fmlas.begin()); for(int i = 0; i < num_theory; i++) fmlas[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),fmlas[i]); std::copy(cnsts,cnsts+num,fmlas.begin()+num_theory); Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"none","AUFLIA","unknown","",num_fmlas-1,&fmlas[0],fmlas[num_fmlas-1]); std::ofstream f(filename); if(num_theory) f << ";! THEORY=" << num_theory << "\n"; f << smt; f.close(); } void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, const char *filename, int num_theory, Z3_ast *theory){ if(!parents){ iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); return; } std::vector tcnsts(num); hash_map syms; for(int j = 0; j < num - 1; j++){ std::ostringstream oss; oss << "$P" << j; std::string name = oss.str(); Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); syms[j] = symbol; tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); } tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); for(int j = num-2; j >= 0; j--){ int parent = parents[j]; // assert(parent >= 0 && parent < num); tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); } iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); } #else static Z3_ast and_vec(Z3_context ctx,std::vector &c){ return (c.size() > 1) ? Z3_mk_and(ctx,c.size(),&c[0]) : c[0]; } static Z3_ast parents_vector_to_tree(Z3_context ctx, int num, Z3_ast *cnsts, int *parents){ Z3_ast res; if(!parents){ res = Z3_mk_interp(ctx,cnsts[0]); for(int i = 1; i < num-1; i++){ Z3_ast bar[2] = {res,cnsts[i]}; res = Z3_mk_interp(ctx,Z3_mk_and(ctx,2,bar)); } if(num > 1){ Z3_ast bar[2] = {res,cnsts[num-1]}; res = Z3_mk_and(ctx,2,bar); } } else { std::vector > chs(num); for(int i = 0; i < num-1; i++){ std::vector &c = chs[i]; c.push_back(cnsts[i]); Z3_ast foo = Z3_mk_interp(ctx,and_vec(ctx,c)); chs[parents[i]].push_back(foo); } { std::vector &c = chs[num-1]; c.push_back(cnsts[num-1]); res = and_vec(ctx,c); } } Z3_inc_ref(ctx,res); return res; } void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, const char *filename, int num_theory, Z3_ast *theory){ std::ofstream f(filename); if(num > 0){ ptr_vector cnsts_vec(num); // get constraints in a vector for(int i = 0; i < num; i++){ expr *a = to_expr(cnsts[i]); cnsts_vec[i] = a; } Z3_ast tree = parents_vector_to_tree(ctx,num,cnsts,parents); iz3pp(mk_c(ctx)->m(),cnsts_vec,to_expr(tree),f); Z3_dec_ref(ctx,tree); } f.close(); #if 0 if(!parents){ iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); return; } std::vector tcnsts(num); hash_map syms; for(int j = 0; j < num - 1; j++){ std::ostringstream oss; oss << "$P" << j; std::string name = oss.str(); Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); syms[j] = symbol; tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); } tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); for(int j = num-2; j >= 0; j--){ int parent = parents[j]; // assert(parent >= 0 && parent < num); tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); } iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); #endif } #endif static std::vector read_cnsts; static std::vector read_parents; static std::ostringstream read_error; static std::string read_msg; static std::vector read_theory; static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, std::vector &assertions){ read_error.clear(); try { std::string foo(filename); if(foo.size() >= 5 && foo.substr(foo.size()-5) == ".smt2"){ Z3_ast ass = Z3_parse_smtlib2_file(ctx, filename, 0, 0, 0, 0, 0, 0); Z3_app app = Z3_to_app(ctx,ass); int nconjs = Z3_get_app_num_args(ctx,app); assertions.resize(nconjs); for(int k = 0; k < nconjs; k++) assertions[k] = Z3_get_app_arg(ctx,app,k); } else { Z3_parse_smtlib_file(ctx, filename, 0, 0, 0, 0, 0, 0); int numa = Z3_get_smtlib_num_assumptions(ctx); int numf = Z3_get_smtlib_num_formulas(ctx); int num = numa + numf; assertions.resize(num); for(int j = 0; j < num; j++){ if(j < numa) assertions[j] = Z3_get_smtlib_assumption(ctx,j); else assertions[j] = Z3_get_smtlib_formula(ctx,j-numa); } } } catch(...) { read_error << "SMTLIB parse error: " << Z3_get_smtlib_error(ctx); read_msg = read_error.str(); *error = read_msg.c_str(); return false; } Z3_set_error_handler(ctx, 0); return true; } int Z3_read_interpolation_problem(Z3_context ctx, int *_num, Z3_ast **cnsts, int **parents, const char *filename, const char **error, int *ret_num_theory, Z3_ast **theory ){ hash_map file_params; get_file_params(filename,file_params); int num_theory = 0; if(file_params.find("THEORY") != file_params.end()) num_theory = atoi(file_params["THEORY"].c_str()); std::vector assertions; if(!iZ3_parse(ctx,filename,error,assertions)) return false; if(num_theory > (int)assertions.size()) num_theory = assertions.size(); int num = assertions.size() - num_theory; read_cnsts.resize(num); read_parents.resize(num); read_theory.resize(num_theory); for(int j = 0; j < num_theory; j++) read_theory[j] = assertions[j]; for(int j = 0; j < num; j++) read_cnsts[j] = assertions[j+num_theory]; if(ret_num_theory) *ret_num_theory = num_theory; if(theory) *theory = &read_theory[0]; if(!parents){ *_num = num; *cnsts = &read_cnsts[0]; return true; } for(int j = 0; j < num; j++) read_parents[j] = SHRT_MAX; hash_map pred_map; for(int j = 0; j < num; j++){ Z3_ast lhs = 0, rhs = read_cnsts[j]; if(Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx,Z3_to_app(ctx,rhs))) == Z3_OP_IMPLIES){ Z3_app app1 = Z3_to_app(ctx,rhs); Z3_ast lhs1 = Z3_get_app_arg(ctx,app1,0); Z3_ast rhs1 = Z3_get_app_arg(ctx,app1,1); if(Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx,Z3_to_app(ctx,lhs1))) == Z3_OP_AND){ Z3_app app2 = Z3_to_app(ctx,lhs1); int nconjs = Z3_get_app_num_args(ctx,app2); for(int k = nconjs - 1; k >= 0; --k) rhs1 = Z3_mk_implies(ctx,Z3_get_app_arg(ctx,app2,k),rhs1); rhs = rhs1; } } while(1){ Z3_app app = Z3_to_app(ctx,rhs); Z3_func_decl func = Z3_get_app_decl(ctx,app); Z3_decl_kind dk = Z3_get_decl_kind(ctx,func); if(dk == Z3_OP_IMPLIES){ if(lhs){ Z3_ast child = lhs; if(pred_map.find(child) == pred_map.end()){ read_error << "formula " << j+1 << ": unknown: " << Z3_ast_to_string(ctx,child); goto fail; } int child_num = pred_map[child]; if(read_parents[child_num] != SHRT_MAX){ read_error << "formula " << j+1 << ": multiple reference: " << Z3_ast_to_string(ctx,child); goto fail; } read_parents[child_num] = j; } lhs = Z3_get_app_arg(ctx,app,0); rhs = Z3_get_app_arg(ctx,app,1); } else { if(!lhs){ read_error << "formula " << j+1 << ": should be (implies {children} fmla parent)"; goto fail; } read_cnsts[j] = lhs; Z3_ast name = rhs; if(pred_map.find(name) != pred_map.end()){ read_error << "formula " << j+1 << ": duplicate symbol"; goto fail; } pred_map[name] = j; break; } } } for(int j = 0; j < num-1; j++) if(read_parents[j] == SHRT_MIN){ read_error << "formula " << j+1 << ": unreferenced"; goto fail; } *_num = num; *cnsts = &read_cnsts[0]; *parents = &read_parents[0]; return true; fail: read_msg = read_error.str(); *error = read_msg.c_str(); return false; } }