3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-05 17:14:07 +00:00
z3/src/api/api_interp.cpp
Nikolaj Bjorner 0ac80fc042 have parser produce ast-vector instead of single ast
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2017-06-01 21:21:05 -07:00

746 lines
26 KiB
C++

/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
api_interp.cpp
Abstract:
API for interpolation
Author:
Ken McMillan
Revision History:
--*/
#include<sstream>
#include<vector>
#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"
#include"scoped_proof.h"
using namespace stl_ext;
// WARNING: don't make a hash_map with this if the range type
// has a destructor: you'll get an address dependency!!!
namespace stl_ext {
template <>
class hash < Z3_ast > {
public:
size_t operator()(const Z3_ast p) const {
return (size_t)p;
}
};
}
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);
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<ast> 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<int> 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<ast> 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<ast> 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]);
}
}
}
static std::ostringstream itp_err;
int Z3_check_interpolant(Z3_context ctx,
unsigned num,
Z3_ast *cnsts,
unsigned *parents,
Z3_ast *itp,
Z3_string *error,
unsigned 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<solver_factory> sf(mk_smt_solver_factory());
scoped_ptr<solver> sp((*(sf))(_m, p, false, true, false, symbol("AUFLIA")));
ptr_vector<ast> cnsts_vec(num); // get constraints in a vector
for (unsigned i = 0; i < num; i++){
ast *a = to_ast(cnsts[i]);
cnsts_vec[i] = a;
}
ptr_vector<ast> itp_vec(num); // get interpolants in a vector
for (unsigned i = 0; i < num - 1; i++){
ast *a = to_ast(itp[i]);
itp_vec[i] = a;
}
::vector<int> parents_vec; // get parents in a vector
if (parents){
parents_vec.resize(num);
for (unsigned i = 0; i < num; i++)
parents_vec[i] = parents[i];
}
ptr_vector<ast> theory_vec; // get background theory in a vector
if (theory){
theory_vec.resize(num_theory);
for (unsigned 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;
}
Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p){
Z3_TRY;
LOG_Z3_get_interpolant(c, pf, pat, p);
RESET_ERROR_CODE();
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(v);
ast *_pf = to_ast(pf);
ast *_pat = to_ast(pat);
ptr_vector<ast> interp;
ptr_vector<ast> cnsts; // to throw away
ast_manager &_m = mk_c(c)->m();
iz3interpolate(_m,
_pf,
cnsts,
_pat,
interp,
(interpolation_options_struct *)0 // ignore params for now
);
// copy result back
for (unsigned i = 0; i < interp.size(); i++){
v->m_ast_vector.push_back(interp[i]);
_m.dec_ref(interp[i]);
}
RETURN_Z3(of_ast_vector(v));
Z3_CATCH_RETURN(0);
}
Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, Z3_ast pat, Z3_params p, Z3_ast_vector *out_interp, Z3_model *model){
Z3_TRY;
LOG_Z3_compute_interpolant(c, pat, p, out_interp, model);
RESET_ERROR_CODE();
// params_ref &_p = to_params(p)->m_params;
params_ref _p;
_p.set_bool("proof", true); // this is currently useless
scoped_proof_mode spm(mk_c(c)->m(), PGM_FINE);
scoped_ptr<solver_factory> sf = mk_smt_solver_factory();
scoped_ptr<solver> m_solver((*sf)(mk_c(c)->m(), _p, true, true, true, ::symbol::null));
m_solver.get()->updt_params(_p); // why do we have to do this?
// some boilerplate stolen from Z3_solver_check
unsigned timeout = p?to_params(p)->m_params.get_uint("timeout", mk_c(c)->get_timeout()):UINT_MAX;
unsigned rlimit = p?to_params(p)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()):0;
bool use_ctrl_c = p?to_params(p)->m_params.get_bool("ctrl_c", false): false;
cancel_eh<reslimit> eh(mk_c(c)->m().limit());
api::context::set_interruptable si(*(mk_c(c)), eh);
ast *_pat = to_ast(pat);
ptr_vector<ast> interp;
ptr_vector<ast> cnsts; // to throw away
ast_manager &_m = mk_c(c)->m();
model_ref m;
lbool _status;
{
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
scoped_timer timer(timeout, &eh);
scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit);
try {
_status = iz3interpolate(_m,
*(m_solver.get()),
_pat,
cnsts,
interp,
m,
0 // ignore params for now
);
}
catch (z3_exception & ex) {
mk_c(c)->handle_exception(ex);
RETURN_Z3_compute_interpolant Z3_L_UNDEF;
}
}
for (unsigned i = 0; i < cnsts.size(); i++)
_m.dec_ref(cnsts[i]);
Z3_lbool status = of_lbool(_status);
Z3_ast_vector_ref *v = 0;
*model = 0;
if (_status == l_false){
// copy result back
v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(v);
for (unsigned i = 0; i < interp.size(); i++){
v->m_ast_vector.push_back(interp[i]);
_m.dec_ref(interp[i]);
}
}
else {
model_ref mr;
m_solver.get()->get_model(mr);
if(mr.get()){
Z3_model_ref *tmp_val = alloc(Z3_model_ref, *mk_c(c));
tmp_val->m_model = mr.get();
mk_c(c)->save_object(tmp_val);
*model = of_model(tmp_val);
}
}
*out_interp = of_ast_vector(v);
RETURN_Z3_compute_interpolant status;
Z3_CATCH_RETURN(Z3_L_UNDEF);
}
};
static void tokenize(const std::string &str, std::vector<std::string> &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<std::string, std::string> &params){
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<std::string> tokens;
tokenize(first_line.substr(2, first_line.size() - 2), tokens);
for (unsigned i = 0; i < tokens.size(); i++){
std::string &tok = tokens[i];
size_t eqpos = tok.find('=');
if (eqpos != std::string::npos){
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<Z3_ast> 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, unsigned *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<Z3_ast> tcnsts(num);
hash_map<int,Z3_ast> 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, svector<Z3_ast> &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, unsigned *parents){
Z3_ast res;
if (!parents){
res = Z3_mk_interpolant(ctx, cnsts[0]);
for (int i = 1; i < num - 1; i++){
Z3_ast bar[2] = { res, cnsts[i] };
res = Z3_mk_interpolant(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<svector<Z3_ast> > chs(num);
for (int i = 0; i < num - 1; i++){
svector<Z3_ast> &c = chs[i];
c.push_back(cnsts[i]);
Z3_ast foo = Z3_mk_interpolant(ctx, and_vec(ctx, c));
chs[parents[i]].push_back(foo);
}
{
svector<Z3_ast> &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, unsigned num, Z3_ast *cnsts, unsigned *parents, const char *filename, unsigned num_theory, Z3_ast *theory){
std::ofstream f(filename);
if (num > 0){
#if 0
// Suggested shorthand:
ptr_vector<expr> cnsts_vec;
cnsts_vec.append(num, to_exprs(cnsts));
cnsts_vec.append(num_theory, to_exprs(theory));
#endif
ptr_vector<expr> cnsts_vec(num); // get constraints in a vector
for (unsigned i = 0; i < num; i++){
expr *a = to_expr(cnsts[i]);
cnsts_vec[i] = a;
}
for (unsigned i = 0; i < num_theory; i++){
expr *a = to_expr(theory[i]);
cnsts_vec.push_back(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<Z3_ast> tcnsts(num);
hash_map<int,Z3_ast> 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<Z3_ast> read_cnsts;
static std::vector<unsigned> read_parents;
static std::ostringstream read_error;
static std::string read_msg;
static std::vector<Z3_ast> read_theory;
static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, svector<Z3_ast> &assertions){
read_error.clear();
try {
std::string foo(filename);
if (foo.size() >= 5 && foo.substr(foo.size() - 5) == ".smt2"){
Z3_ast_vector assrts = Z3_parse_smtlib2_file(ctx, filename, 0, 0, 0, 0, 0, 0);
Z3_ast_vector_inc_ref(ctx, assrts);
unsigned nconjs = Z3_ast_vector_size(ctx, assrts);
assertions.resize(nconjs);
for (unsigned k = 0; k < nconjs; k++)
assertions[k] = Z3_ast_vector_get(ctx, assrts, k);
// Z3_ast_vector_dec_ref(ctx, assrts) is unsafe
}
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, unsigned *_num, Z3_ast *cnsts[], unsigned *parents[], const char *filename, Z3_string_ptr error, unsigned *ret_num_theory, Z3_ast *theory[]){
hash_map<std::string, std::string> file_params;
get_file_params(filename, file_params);
unsigned num_theory = 0;
if (file_params.find("THEORY") != file_params.end())
num_theory = atoi(file_params["THEORY"].c_str());
svector<Z3_ast> assertions;
if (!iZ3_parse(ctx, filename, error, assertions))
return false;
if (num_theory > assertions.size())
num_theory = assertions.size();
unsigned num = assertions.size() - num_theory;
read_cnsts.resize(num);
read_parents.resize(num);
read_theory.resize(num_theory);
for (unsigned j = 0; j < num_theory; j++)
read_theory[j] = assertions[j];
for (unsigned 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 (unsigned j = 0; j < num; j++)
read_parents[j] = SHRT_MAX;
hash_map<Z3_ast, int> pred_map;
for (unsigned 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 (unsigned j = 0; j < num - 1; j++)
if (read_parents[j] == SHRT_MAX){
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;
}
}
#if 0
/** Constant reprepresenting a root of a formula tree for tree interpolation */
#define IZ3_ROOT SHRT_MAX
/** This function uses Z3 to determine satisfiability of a set of
constraints. If UNSAT, an interpolant is returned, based on the
refutation generated by Z3. If SAT, a model is returned.
If "parents" is non-null, computes a tree interpolant. The tree is
defined by the array "parents". This array maps each formula in
the tree to its parent, where formulas are indicated by their
integer index in "cnsts". The parent of formula n must have index
greater than n. The last formula is the root of the tree. Its
parent entry should be the constant IZ3_ROOT.
If "parents" is null, computes a sequence interpolant.
\param ctx The Z3 context. Must be generated by iz3_mk_context
\param num The number of constraints in the sequence
\param cnsts Array of constraints (AST's in context ctx)
\param parents The parents vector defining the tree structure
\param options Interpolation options (may be NULL)
\param interps Array to return interpolants (size at least num-1, may be NULL)
\param model Returns a Z3 model if constraints SAT (may be NULL)
\param labels Returns relevant labels if SAT (may be NULL)
\param incremental
VERY IMPORTANT: All the Z3 formulas in cnsts must be in Z3
context ctx. The model and interpolants returned are also
in this context.
The return code is as in Z3_check_assumptions, that is,
Z3_L_FALSE = constraints UNSAT (interpolants returned)
Z3_L_TRUE = constraints SAT (model returned)
Z3_L_UNDEF = Z3 produced no result, or interpolation not possible
Currently, this function supports integer and boolean variables,
as well as arrays over these types, with linear arithmetic,
uninterpreted functions and quantifiers over integers (that is
AUFLIA). Interpolants are produced in AULIA. However, some
uses of array operations may cause quantifiers to appear in the
interpolants even when there are no quantifiers in the input formulas.
Although quantifiers may appear in the input formulas, Z3 may give up in
this case, returning Z3_L_UNDEF.
If "incremental" is true, cnsts must contain exactly the set of
formulas that are currently asserted in the context. If false,
there must be no formulas currently asserted in the context.
Setting "incremental" to true makes it posisble to incrementally
add and remove constraints from the context until the context
becomes UNSAT, at which point an interpolant is computed. Caution
must be used, however. Before popping the context, if you wish to
keep the interolant formulas, you *must* preserve them by using
Z3_persist_ast. Also, if you want to simplify the interpolant
formulas using Z3_simplify, you must first pop all of the
assertions in the context (or use a different context). Otherwise,
the formulas will be simplified *relative* to these constraints,
which is almost certainly not what you want.
Current limitations on tree interpolants. In a tree interpolation
problem, each constant (0-ary function symbol) must occur only
along one path from root to leaf. Function symbols (of arity > 0)
are considered to have global scope (i.e., may appear in any
interpolant formula).
def_API('Z3_interpolate', BOOL, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(PARAMS), _out_array(1, AST), _out(MODEL), _out(LITERALS), _in(UINT), _in(UINT), _in_array(9, AST)))
*/
Z3_lbool Z3_API Z3_interpolate(Z3_context ctx,
unsigned num,
Z3_ast *cnsts,
unsigned *parents,
Z3_params options,
Z3_ast *interps,
Z3_model *model,
Z3_literals *labels,
unsigned incremental,
unsigned num_theory,
Z3_ast *theory);
#endif