3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-10 19:27:06 +00:00

Merge branch 'opt' of https://git01.codeplex.com/z3 into opt

This commit is contained in:
Nikolaj Bjorner 2014-01-06 16:23:50 -08:00
commit 1f7c994e43
95 changed files with 24076 additions and 414 deletions

452
examples/interp/iz3.cpp Executable file
View file

@ -0,0 +1,452 @@
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <vector>
#include "z3.h"
int usage(const char **argv){
std::cerr << "usage: " << argv[0] << " [options] file.smt" << std::endl;
std::cerr << std::endl;
std::cerr << "options:" << std::endl;
std::cerr << " -t,--tree tree interpolation" << std::endl;
std::cerr << " -c,--check check result" << std::endl;
std::cerr << " -p,--profile profile execution" << std::endl;
std::cerr << " -w,--weak weak interpolants" << std::endl;
std::cerr << " -f,--flat ouput flat formulas" << std::endl;
std::cerr << " -o <file> ouput to SMT-LIB file" << std::endl;
std::cerr << " -a,--anon anonymize" << std::endl;
std::cerr << " -s,--simple simple proof mode" << std::endl;
std::cerr << std::endl;
return 1;
}
int main(int argc, const char **argv) {
bool tree_mode = false;
bool check_mode = false;
bool profile_mode = false;
bool incremental_mode = false;
std::string output_file;
bool flat_mode = false;
bool anonymize = false;
bool write = false;
Z3_config cfg = Z3_mk_config();
// Z3_interpolation_options options = Z3_mk_interpolation_options();
Z3_params options = 0;
/* Parse the command line */
int argn = 1;
while(argn < argc-1){
std::string flag = argv[argn];
if(flag[0] == '-'){
if(flag == "-t" || flag == "--tree")
tree_mode = true;
else if(flag == "-c" || flag == "--check")
check_mode = true;
else if(flag == "-p" || flag == "--profile")
profile_mode = true;
#if 0
else if(flag == "-w" || flag == "--weak")
Z3_set_interpolation_option(options,"weak","1");
else if(flag == "--secondary")
Z3_set_interpolation_option(options,"secondary","1");
#endif
else if(flag == "-i" || flag == "--incremental")
incremental_mode = true;
else if(flag == "-o"){
argn++;
if(argn >= argc) return usage(argv);
output_file = argv[argn];
}
else if(flag == "-f" || flag == "--flat")
flat_mode = true;
else if(flag == "-a" || flag == "--anon")
anonymize = true;
else if(flag == "-w" || flag == "--write")
write = true;
else if(flag == "-s" || flag == "--simple")
Z3_set_param_value(cfg,"PREPROCESS","false");
else
return usage(argv);
}
argn++;
}
if(argn != argc-1)
return usage(argv);
const char *filename = argv[argn];
/* Create a Z3 context to contain formulas */
Z3_context ctx = Z3_mk_interpolation_context(cfg);
if(write || anonymize)
Z3_set_ast_print_mode(ctx,Z3_PRINT_SMTLIB2_COMPLIANT);
else if(!flat_mode)
Z3_set_ast_print_mode(ctx,Z3_PRINT_SMTLIB_COMPLIANT);
/* Read an interpolation problem */
int num;
Z3_ast *constraints;
int *parents = 0;
const char *error;
bool ok;
int num_theory;
Z3_ast *theory;
ok = Z3_read_interpolation_problem(ctx, &num, &constraints, tree_mode ? &parents : 0, filename, &error, &num_theory, &theory);
/* If parse failed, print the error message */
if(!ok){
std::cerr << error << "\n";
return 1;
}
/* if we get only one formula, and it is a conjunction, split it into conjuncts. */
if(!tree_mode && num == 1){
Z3_app app = Z3_to_app(ctx,constraints[0]);
Z3_func_decl func = Z3_get_app_decl(ctx,app);
Z3_decl_kind dk = Z3_get_decl_kind(ctx,func);
if(dk == Z3_OP_AND){
int nconjs = Z3_get_app_num_args(ctx,app);
if(nconjs > 1){
std::cout << "Splitting formula into " << nconjs << " conjuncts...\n";
num = nconjs;
constraints = new Z3_ast[num];
for(int k = 0; k < num; k++)
constraints[k] = Z3_get_app_arg(ctx,app,k);
}
}
}
/* Write out anonymized version. */
if(write || anonymize){
#if 0
Z3_anonymize_ast_vector(ctx,num,constraints);
#endif
std::string ofn = output_file.empty() ? "iz3out.smt2" : output_file;
Z3_write_interpolation_problem(ctx, num, constraints, parents, ofn.c_str(), num_theory, theory);
std::cout << "anonymized problem written to " << ofn << "\n";
exit(0);
}
/* Compute an interpolant, or get a model. */
Z3_ast *interpolants = (Z3_ast *)malloc((num-1) * sizeof(Z3_ast));
Z3_model model = 0;
Z3_lbool result;
if(!incremental_mode){
/* In non-incremental mode, we just pass the constraints. */
result = Z3_interpolate(ctx, num, constraints, (unsigned int *)parents, options, interpolants, &model, 0, false, num_theory, theory);
}
else {
/* This is a somewhat useless demonstration of incremental mode.
Here, we assert the constraints in the context, then pass them to
iZ3 in an array, so iZ3 knows the sequence. Note it's safe to pass
"true", even though we haven't techically asserted if. */
Z3_push(ctx);
std::vector<Z3_ast> asserted(num);
/* We start with nothing asserted. */
for(int i = 0; i < num; i++)
asserted[i] = Z3_mk_true(ctx);
/* Now we assert the constrints one at a time until UNSAT. */
for(int i = 0; i < num; i++){
asserted[i] = constraints[i];
Z3_assert_cnstr(ctx,constraints[i]); // assert one constraint
result = Z3_interpolate(ctx, num, &asserted[0], (unsigned int *)parents, options, interpolants, &model, 0, true, 0, 0);
if(result == Z3_L_FALSE){
for(unsigned j = 0; j < num-1; j++)
/* Since we want the interpolant formulas to survive a "pop", we
"persist" them here. */
Z3_persist_ast(ctx,interpolants[j],1);
break;
}
}
Z3_pop(ctx,1);
}
switch (result) {
/* If UNSAT, print the interpolants */
case Z3_L_FALSE:
printf("unsat\n");
if(output_file.empty()){
printf("interpolant:\n");
for(int i = 0; i < num-1; i++)
printf("%s\n", Z3_ast_to_string(ctx, interpolants[i]));
}
else {
#if 0
Z3_write_interpolation_problem(ctx,num-1,interpolants,0,output_file.c_str());
printf("interpolant written to %s\n",output_file.c_str());
#endif
}
#if 1
if(check_mode){
std::cout << "Checking interpolant...\n";
bool chk;
chk = Z3_check_interpolant(ctx,num,constraints,parents,interpolants,&error,num_theory,theory);
if(chk)
std::cout << "Interpolant is correct\n";
else {
std::cout << "Interpolant is incorrect\n";
std::cout << error;
return 1;
}
}
#endif
break;
case Z3_L_UNDEF:
printf("fail\n");
break;
case Z3_L_TRUE:
printf("sat\n");
printf("model:\n%s\n", Z3_model_to_string(ctx, model));
break;
}
if(profile_mode)
std::cout << Z3_interpolation_profile(ctx);
/* Delete the model if there is one */
if (model)
Z3_del_model(ctx, model);
/* Delete logical context. */
Z3_del_context(ctx);
free(interpolants);
return 0;
}
#if 0
int test(){
int i;
/* Create a Z3 context to contain formulas */
Z3_config cfg = Z3_mk_config();
Z3_context ctx = iz3_mk_context(cfg);
int num = 2;
Z3_ast *constraints = (Z3_ast *)malloc(num * sizeof(Z3_ast));
#if 1
Z3_sort arr = Z3_mk_array_sort(ctx,Z3_mk_int_sort(ctx),Z3_mk_bool_sort(ctx));
Z3_symbol as = Z3_mk_string_symbol(ctx, "a");
Z3_symbol bs = Z3_mk_string_symbol(ctx, "b");
Z3_symbol xs = Z3_mk_string_symbol(ctx, "x");
Z3_ast a = Z3_mk_const(ctx,as,arr);
Z3_ast b = Z3_mk_const(ctx,bs,arr);
Z3_ast x = Z3_mk_const(ctx,xs,Z3_mk_int_sort(ctx));
Z3_ast c1 = Z3_mk_eq(ctx,a,Z3_mk_store(ctx,b,x,Z3_mk_true(ctx)));
Z3_ast c2 = Z3_mk_not(ctx,Z3_mk_select(ctx,a,x));
#else
Z3_symbol xs = Z3_mk_string_symbol(ctx, "x");
Z3_ast x = Z3_mk_const(ctx,xs,Z3_mk_bool_sort(ctx));
Z3_ast c1 = Z3_mk_eq(ctx,x,Z3_mk_true(ctx));
Z3_ast c2 = Z3_mk_eq(ctx,x,Z3_mk_false(ctx));
#endif
constraints[0] = c1;
constraints[1] = c2;
/* print out the result for grins. */
// Z3_string smtout = Z3_benchmark_to_smtlib_string (ctx, "foo", "QFLIA", "sat", "", num, constraints, Z3_mk_true(ctx));
// Z3_string smtout = Z3_ast_to_string(ctx,constraints[0]);
// Z3_string smtout = Z3_context_to_string(ctx);
// puts(smtout);
iz3_print(ctx,num,constraints,"iZ3temp.smt");
/* Make room for interpolants. */
Z3_ast *interpolants = (Z3_ast *)malloc((num-1) * sizeof(Z3_ast));
/* Make room for the model. */
Z3_model model = 0;
/* Call the prover */
Z3_lbool result = iz3_interpolate(ctx, num, constraints, interpolants, &model);
switch (result) {
/* If UNSAT, print the interpolants */
case Z3_L_FALSE:
printf("unsat, interpolants:\n");
for(i = 0; i < num-1; i++)
printf("%s\n", Z3_ast_to_string(ctx, interpolants[i]));
break;
case Z3_L_UNDEF:
printf("fail\n");
break;
case Z3_L_TRUE:
printf("sat\n");
printf("model:\n%s\n", Z3_model_to_string(ctx, model));
break;
}
/* Delete the model if there is one */
if (model)
Z3_del_model(ctx, model);
/* Delete logical context (note, we call iz3_del_context, not
Z3_del_context */
iz3_del_context(ctx);
return 1;
}
struct z3_error {
Z3_error_code c;
z3_error(Z3_error_code _c) : c(_c) {}
};
extern "C" {
static void throw_z3_error(Z3_error_code c){
throw z3_error(c);
}
}
int main(int argc, const char **argv) {
/* Create a Z3 context to contain formulas */
Z3_config cfg = Z3_mk_config();
Z3_context ctx = iz3_mk_context(cfg);
Z3_set_error_handler(ctx, throw_z3_error);
/* Make some constraints, by parsing an smtlib formatted file given as arg 1 */
try {
Z3_parse_smtlib_file(ctx, argv[1], 0, 0, 0, 0, 0, 0);
}
catch(const z3_error &err){
std::cerr << "Z3 error: " << Z3_get_error_msg(err.c) << "\n";
std::cerr << Z3_get_smtlib_error(ctx) << "\n";
return(1);
}
/* Get the constraints from the parser. */
int num = Z3_get_smtlib_num_formulas(ctx);
if(num == 0){
std::cerr << "iZ3 error: File contains no formulas.\n";
return 1;
}
Z3_ast *constraints = (Z3_ast *)malloc(num * sizeof(Z3_ast));
int i;
for (i = 0; i < num; i++)
constraints[i] = Z3_get_smtlib_formula(ctx, i);
/* if we get only one formula, and it is a conjunction, split it into conjuncts. */
if(num == 1){
Z3_app app = Z3_to_app(ctx,constraints[0]);
Z3_func_decl func = Z3_get_app_decl(ctx,app);
Z3_decl_kind dk = Z3_get_decl_kind(ctx,func);
if(dk == Z3_OP_AND){
int nconjs = Z3_get_app_num_args(ctx,app);
if(nconjs > 1){
std::cout << "Splitting formula into " << nconjs << " conjuncts...\n";
num = nconjs;
constraints = new Z3_ast[num];
for(int k = 0; k < num; k++)
constraints[k] = Z3_get_app_arg(ctx,app,k);
}
}
}
/* print out the result for grins. */
// Z3_string smtout = Z3_benchmark_to_smtlib_string (ctx, "foo", "QFLIA", "sat", "", num, constraints, Z3_mk_true(ctx));
// Z3_string smtout = Z3_ast_to_string(ctx,constraints[0]);
// Z3_string smtout = Z3_context_to_string(ctx);
// puts(smtout);
// iz3_print(ctx,num,constraints,"iZ3temp.smt");
/* Make room for interpolants. */
Z3_ast *interpolants = (Z3_ast *)malloc((num-1) * sizeof(Z3_ast));
/* Make room for the model. */
Z3_model model = 0;
/* Call the prover */
Z3_lbool result = iz3_interpolate(ctx, num, constraints, interpolants, &model);
switch (result) {
/* If UNSAT, print the interpolants */
case Z3_L_FALSE:
printf("unsat, interpolants:\n");
for(i = 0; i < num-1; i++)
printf("%s\n", Z3_ast_to_string(ctx, interpolants[i]));
std::cout << "Checking interpolants...\n";
const char *error;
if(iZ3_check_interpolant(ctx, num, constraints, 0, interpolants, &error))
std::cout << "Interpolant is correct\n";
else {
std::cout << "Interpolant is incorrect\n";
std::cout << error << "\n";
}
break;
case Z3_L_UNDEF:
printf("fail\n");
break;
case Z3_L_TRUE:
printf("sat\n");
printf("model:\n%s\n", Z3_model_to_string(ctx, model));
break;
}
/* Delete the model if there is one */
if (model)
Z3_del_model(ctx, model);
/* Delete logical context (note, we call iz3_del_context, not
Z3_del_context */
iz3_del_context(ctx);
return 0;
}
#endif

View file

@ -34,7 +34,8 @@ def init_project_def():
add_lib('subpaving_tactic', ['core_tactics', 'subpaving'], 'math/subpaving/tactic')
add_lib('aig_tactic', ['tactic'], 'tactic/aig')
add_lib('solver', ['model', 'tactic'])
add_lib('cmd_context', ['solver', 'rewriter'])
add_lib('interp', ['solver'])
add_lib('cmd_context', ['solver', 'rewriter', 'interp'])
add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'arith_tactics'], 'cmd_context/extra_cmds')
add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2')
add_lib('proof_checker', ['rewriter'], 'ast/proof_checker')
@ -55,6 +56,7 @@ def init_project_def():
add_lib('smt_tactic', ['smt'], 'smt/tactic')
add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls')
add_lib('qe', ['smt','sat'], 'qe')
add_lib('duality', ['smt', 'interp', 'qe'])
add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base')
add_lib('transforms', ['muz', 'hilbert'], 'muz/transforms')
add_lib('rel', ['muz', 'transforms'], 'muz/rel')
@ -62,14 +64,15 @@ def init_project_def():
add_lib('clp', ['muz', 'transforms'], 'muz/clp')
add_lib('tab', ['muz', 'transforms'], 'muz/tab')
add_lib('bmc', ['muz', 'transforms'], 'muz/bmc')
add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc'], 'muz/fp')
add_lib('duality_intf', ['muz', 'transforms', 'duality'], 'muz/duality')
add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf'], 'muz/fp')
add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics')
add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv')
add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio')
add_lib('smtparser', ['portfolio'], 'parsers/smt')
add_lib('opt', ['smt', 'smtlogic_tactics'], 'opt')
API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h']
add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure','opt'],
add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure','interp','opt'],
includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files)
add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3')
add_exe('test', ['api', 'fuzzing'], exe_name='test-z3', install=False)
@ -84,6 +87,7 @@ def init_project_def():
set_z3py_dir('api/python')
# Examples
add_cpp_example('cpp_example', 'c++')
add_cpp_example('iz3', 'interp')
add_cpp_example('z3_tptp', 'tptp')
add_c_example('c_example', 'c')
add_c_example('maxsat')

View file

@ -29,6 +29,7 @@ CXX=getenv("CXX", None)
CC=getenv("CC", None)
CPPFLAGS=getenv("CPPFLAGS", "")
CXXFLAGS=getenv("CXXFLAGS", "")
EXAMP_DEBUG_FLAG=''
LDFLAGS=getenv("LDFLAGS", "")
JNI_HOME=getenv("JNI_HOME", None)
@ -70,6 +71,8 @@ VER_BUILD=None
VER_REVISION=None
PREFIX=os.path.split(os.path.split(os.path.split(PYTHON_PACKAGE_DIR)[0])[0])[0]
GMP=False
FOCI2=False
FOCI2LIB=''
VS_PAR=False
VS_PAR_NUM=8
GPROF=False
@ -198,6 +201,14 @@ def test_gmp(cc):
t.commit()
return exec_compiler_cmd([cc, CPPFLAGS, 'tstgmp.cpp', LDFLAGS, '-lgmp']) == 0
def test_foci2(cc,foci2lib):
if is_verbose():
print("Testing FOCI2...")
t = TempFile('tstfoci2.cpp')
t.add('#include<foci2.h>\nint main() { foci2 *f = foci2::create("lia"); return 0; }\n')
t.commit()
return exec_compiler_cmd([cc, CPPFLAGS, '-Isrc/interp', 'tstfoci2.cpp', LDFLAGS, foci2lib]) == 0
def test_openmp(cc):
if is_verbose():
print("Testing OpenMP...")
@ -443,6 +454,7 @@ def display_help(exit_code):
if not IS_WINDOWS:
print(" -g, --gmp use GMP.")
print(" --gprof enable gprof")
print(" -f <path> --foci2=<path> use foci2 library at path")
print("")
print("Some influential environment variables:")
if not IS_WINDOWS:
@ -458,18 +470,19 @@ def display_help(exit_code):
# Parse configuration option for mk_make script
def parse_options():
global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM
global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH
global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP, FOCI2, FOCI2LIB, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH
try:
options, remainder = getopt.gnu_getopt(sys.argv[1:],
'b:dsxhmcvtnp:gj',
'b:df:sxhmcvtnp:gj',
['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj',
'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'java', 'parallel=', 'gprof',
'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'foci2=', 'java', 'parallel=', 'gprof',
'githash='])
except:
print("ERROR: Invalid command line option")
display_help(1)
for opt, arg in options:
print('opt = %s, arg = %s' % (opt, arg))
if opt in ('-b', '--build'):
if arg == 'src':
raise MKException('The src directory should not be used to host the Makefile')
@ -507,6 +520,9 @@ def parse_options():
VS_PAR_NUM = int(arg)
elif opt in ('-g', '--gmp'):
GMP = True
elif opt in ('-f', '--foci2'):
FOCI2 = True
FOCI2LIB = arg
elif opt in ('-j', '--java'):
JAVA_ENABLED = True
elif opt == '--gprof':
@ -665,6 +681,10 @@ class Component:
self.src_dir = os.path.join(SRC_DIR, path)
self.to_src_dir = os.path.join(REV_BUILD_DIR, self.src_dir)
def get_link_name(self):
return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)'
# Find fname in the include paths for the given component.
# ownerfile is only used for creating error messages.
# That is, we were looking for fname when processing ownerfile
@ -880,7 +900,7 @@ class ExeComponent(Component):
out.write(obj)
for dep in deps:
c_dep = get_component(dep)
out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name))
out.write(' ' + c_dep.get_link_name())
out.write('\n')
out.write('\t$(LINK) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % exefile)
for obj in objs:
@ -888,7 +908,8 @@ class ExeComponent(Component):
out.write(obj)
for dep in deps:
c_dep = get_component(dep)
out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name))
out.write(' ' + c_dep.get_link_name())
out.write(' ' + FOCI2LIB)
out.write(' $(LINK_EXTRA_FLAGS)\n')
out.write('%s: %s\n\n' % (self.name, exefile))
@ -957,6 +978,12 @@ class DLLComponent(Component):
self.install = install
self.static = static
def get_link_name(self):
if self.static:
return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)'
else:
return self.name + '$(SO_EXT)'
def mk_makefile(self, out):
Component.mk_makefile(self, out)
# generate rule for (SO_EXT)
@ -979,7 +1006,7 @@ class DLLComponent(Component):
for dep in deps:
if not dep in self.reexports:
c_dep = get_component(dep)
out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name))
out.write(' ' + c_dep.get_link_name())
out.write('\n')
out.write('\t$(LINK) $(SLINK_OUT_FLAG)%s $(SLINK_FLAGS)' % dllfile)
for obj in objs:
@ -988,7 +1015,8 @@ class DLLComponent(Component):
for dep in deps:
if not dep in self.reexports:
c_dep = get_component(dep)
out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name))
out.write(' ' + c_dep.get_link_name())
out.write(' ' + FOCI2LIB)
out.write(' $(SLINK_EXTRA_FLAGS)')
if IS_WINDOWS:
out.write(' /DEF:%s.def' % os.path.join(self.to_src_dir, self.name))
@ -1230,7 +1258,7 @@ class CppExampleComponent(ExampleComponent):
out.write(' ')
out.write(os.path.join(self.to_ex_dir, cppfile))
out.write('\n')
out.write('\t%s $(OS_DEFINES) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), exefile))
out.write('\t%s $(OS_DEFINES) $(EXAMP_DEBUG_FLAG) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), exefile))
# Add include dir components
out.write(' -I%s' % get_component(API_COMPONENT).to_src_dir)
out.write(' -I%s' % get_component(CPP_COMPONENT).to_src_dir)
@ -1450,7 +1478,7 @@ def mk_config():
print('JNI Bindings: %s' % JNI_HOME)
print('Java Compiler: %s' % JAVAC)
else:
global CXX, CC, GMP, CPPFLAGS, CXXFLAGS, LDFLAGS
global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG
OS_DEFINES = ""
ARITH = "internal"
check_ar()
@ -1468,6 +1496,14 @@ def mk_config():
SLIBEXTRAFLAGS = '%s -lgmp' % SLIBEXTRAFLAGS
else:
CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS
if FOCI2:
if test_foci2(CXX,FOCI2LIB):
LDFLAGS = '%s %s' % (LDFLAGS,FOCI2LIB)
SLIBEXTRAFLAGS = '%s %s' % (SLIBEXTRAFLAGS,FOCI2LIB)
CPPFLAGS = '%s -D_FOCI2' % CPPFLAGS
else:
print "FAILED\n"
FOCI2 = False
if GIT_HASH:
CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH)
CXXFLAGS = '%s -c' % CXXFLAGS
@ -1480,6 +1516,7 @@ def mk_config():
CXXFLAGS = '%s -D_NO_OMP_' % CXXFLAGS
if DEBUG_MODE:
CXXFLAGS = '%s -g -Wall' % CXXFLAGS
EXAMP_DEBUG_FLAG = '-g'
else:
if GPROF:
CXXFLAGS = '%s -O3 -D _EXTERNAL_RELEASE' % CXXFLAGS
@ -1526,6 +1563,7 @@ def mk_config():
config.write('CC=%s\n' % CC)
config.write('CXX=%s\n' % CXX)
config.write('CXXFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS))
config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG)
config.write('CXX_OUT_FLAG=-o \n')
config.write('OBJ_EXT=.o\n')
config.write('LIB_EXT=.a\n')

View file

@ -209,6 +209,7 @@ extern "C" {
MK_BINARY(Z3_mk_xor, mk_c(c)->get_basic_fid(), OP_XOR, SKIP);
MK_NARY(Z3_mk_and, mk_c(c)->get_basic_fid(), OP_AND, SKIP);
MK_NARY(Z3_mk_or, mk_c(c)->get_basic_fid(), OP_OR, SKIP);
MK_UNARY(Z3_mk_interp, mk_c(c)->get_basic_fid(), OP_INTERP, SKIP);
Z3_ast mk_ite_core(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3) {
expr * result = mk_c(c)->m().mk_ite(to_expr(t1), to_expr(t2), to_expr(t3));
@ -923,6 +924,7 @@ extern "C" {
case OP_NOT: return Z3_OP_NOT;
case OP_IMPLIES: return Z3_OP_IMPLIES;
case OP_OEQ: return Z3_OP_OEQ;
case OP_INTERP: return Z3_OP_INTERP;
case PR_UNDEF: return Z3_OP_PR_UNDEF;
case PR_TRUE: return Z3_OP_PR_TRUE;

622
src/api/api_interp.cpp Normal file
View file

@ -0,0 +1,622 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
api_interp.cpp
Abstract:
API for interpolation
Author:
Ken McMillan
Revision History:
--*/
#include<iostream>
#include<sstream>
#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
#ifndef WIN32
// 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;
}
};
}
#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<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]);
}
}
}
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<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(int 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(int 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(int 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(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<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 >= 0 && eqpos < 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<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, 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<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, 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<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_interp(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, 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<expr> 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);
for(int i = 0; i < num_theory; i++){
expr *a = to_expr(theory[i]);
cnsts_vec.push_back(a);
}
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<int> 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 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<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_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;
}
}

View file

@ -36,4 +36,3 @@ using System.Security.Permissions;
// [assembly: AssemblyVersion("4.2.0.0")]
[assembly: AssemblyVersion("4.3.2.0")]
[assembly: AssemblyFileVersion("4.3.2.0")]

View file

@ -35,7 +35,7 @@ public class BitVecNum extends BitVecExpr
{
Native.LongPtr res = new Native.LongPtr();
if (Native.getNumeralInt64(getContext().nCtx(), getNativeObject(), res) ^ true)
throw new Z3Exception("Numeral is not an int64");
throw new Z3Exception("Numeral is not a long");
return res.value;
}

View file

@ -6424,7 +6424,7 @@ class Tactic:
_z3_assert(isinstance(goal, Goal) or isinstance(goal, BoolRef), "Z3 Goal or Boolean expressions expected")
goal = _to_goal(goal)
if len(arguments) > 0 or len(keywords) > 0:
p = args2params(arguments, keywords, a.ctx)
p = args2params(arguments, keywords, self.ctx)
return ApplyResult(Z3_tactic_apply_ex(self.ctx.ref(), self.tactic, goal.goal, p.params), self.ctx)
else:
return ApplyResult(Z3_tactic_apply(self.ctx.ref(), self.tactic, goal.goal), self.ctx)

View file

@ -251,6 +251,8 @@ typedef enum
- Z3_OP_OEQ Binary equivalence modulo namings. This binary predicate is used in proof terms.
It captures equisatisfiability and equivalence modulo renamings.
- Z3_OP_INTERP Marks a sub-formula for interpolation.
- Z3_OP_ANUM Arithmetic numeral.
- Z3_OP_AGNUM Arithmetic algebraic numeral. Algebraic numbers are used to represent irrational numbers in Z3.
@ -901,6 +903,7 @@ typedef enum {
Z3_OP_NOT,
Z3_OP_IMPLIES,
Z3_OP_OEQ,
Z3_OP_INTERP,
// Arithmetic
Z3_OP_ANUM = 0x200,
@ -2121,6 +2124,16 @@ END_MLAPI_EXCLUDE
*/
Z3_ast Z3_API Z3_mk_not(__in Z3_context c, __in Z3_ast a);
/**
\brief \mlh mk_interp c a \endmlh
Create an AST node marking a formula position for interpolation.
The node \c a must have Boolean sort.
def_API('Z3_mk_interp', AST, (_in(CONTEXT), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_interp(__in Z3_context c, __in Z3_ast a);
/**
\brief \mlh mk_ite c t1 t2 t2 \endmlh
Create an AST node representing an if-then-else: <tt>ite(t1, t2,
@ -7863,6 +7876,211 @@ END_MLAPI_EXCLUDE
/*@}*/
/**
@name Interpolation
*/
/*@{*/
/** \brief This function generates a Z3 context suitable for generation of
interpolants. Formulas can be generated as abstract syntx trees in
this context using the Z3 C API.
Interpolants are also generated as AST's in this context.
If cfg is non-null, it will be used as the base configuration
for the Z3 context. This makes it possible to set Z3 options
to be used during interpolation. This feature should be used
with some caution however, as it may be that certain Z3 options
are incompatible with interpolation.
def_API('Z3_mk_interpolation_context', CONTEXT, (_in(CONFIG),))
*/
Z3_context Z3_API Z3_mk_interpolation_context(__in Z3_config cfg);
/** 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 AUFLIA. 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).
*/
Z3_lbool Z3_API Z3_interpolate(__in Z3_context ctx,
__in int num,
__in_ecount(num) Z3_ast *cnsts,
__in_ecount(num) unsigned *parents,
__in Z3_params options,
__out_ecount(num-1) Z3_ast *interps,
__out Z3_model *model,
__out Z3_literals *labels,
__in int incremental,
__in int num_theory,
__in_ecount(num_theory) Z3_ast *theory);
/** Return a string summarizing cumulative time used for
interpolation. This string is purely for entertainment purposes
and has no semantics.
\param ctx The context (currently ignored)
def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),))
*/
Z3_string Z3_API Z3_interpolation_profile(__in Z3_context ctx);
/**
\brief Read an interpolation problem from file.
\param ctx The Z3 context. This resets the error handler of ctx.
\param filename The file name to read.
\param num Returns length of sequence.
\param cnsts Returns sequence of formulas (do not free)
\param parents Returns the parents vector (or NULL for sequence)
\param error Returns an error message in case of failure (do not free the string)
Returns true on success.
File formats: Currently two formats are supported, based on
SMT-LIB2. For sequence interpolants, the sequence of constraints is
represented by the sequence of "assert" commands in the file.
For tree interpolants, one symbol of type bool is associated to
each vertex of the tree. For each vertex v there is an "assert"
of the form:
(implies (and c1 ... cn f) v)
where c1 .. cn are the children of v (which must precede v in the file)
and f is the formula assiciated to node v. The last formula in the
file is the root vertex, and is represented by the predicate "false".
A solution to a tree interpolation problem can be thought of as a
valuation of the vertices that makes all the implications true
where each value is represented using the common symbols between
the formulas in the subtree and the remainder of the formulas.
*/
int Z3_API Z3_read_interpolation_problem(__in Z3_context ctx,
__out int *num,
__out_ecount(*num) Z3_ast **cnsts,
__out_ecount(*num) int **parents,
__in const char *filename,
__out const char **error,
__out int *num_theory,
__out_ecount(*num_theory) Z3_ast **theory);
/** Check the correctness of an interpolant. The Z3 context must
have no constraints asserted when this call is made. That means
that after interpolating, you must first fully pop the Z3
context before calling this. See Z3_interpolate for meaning of parameters.
\param ctx The Z3 context. Must be generated by Z3_mk_interpolation_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 (or NULL for sequence)
\param interps The interpolant to check
\param error Returns an error message if interpolant incorrect (do not free the string)
Return value is Z3_L_TRUE if interpolant is verified, Z3_L_FALSE if
incorrect, and Z3_L_UNDEF if unknown.
*/
int Z3_API Z3_check_interpolant(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, Z3_ast *interps, const char **error,
int num_theory, Z3_ast *theory);
/** Write an interpolation problem to file suitable for reading with
Z3_read_interpolation_problem. The output file is a sequence
of SMT-LIB2 format commands, suitable for reading with command-line Z3
or other interpolating solvers.
\param ctx The Z3 context. Must be generated by z3_mk_interpolation_context
\param num The number of constraints in the sequence
\param cnsts Array of constraints
\param parents The parents vector (or NULL for sequence)
\param filename The file name to write
*/
void Z3_API Z3_write_interpolation_problem(Z3_context ctx,
int num,
Z3_ast *cnsts,
int *parents,
const char *filename,
int num_theory,
Z3_ast *theory);
#endif

View file

@ -640,6 +640,7 @@ basic_decl_plugin::basic_decl_plugin():
m_iff_decl(0),
m_xor_decl(0),
m_not_decl(0),
m_interp_decl(0),
m_implies_decl(0),
m_proof_sort(0),
@ -863,6 +864,7 @@ void basic_decl_plugin::set_manager(ast_manager * m, family_id id) {
m_iff_decl = mk_bool_op_decl("iff", OP_IFF, 2, false, true, false, false, true);
m_xor_decl = mk_bool_op_decl("xor", OP_XOR, 2, true, true);
m_not_decl = mk_bool_op_decl("not", OP_NOT, 1);
m_interp_decl = mk_bool_op_decl("interp", OP_INTERP, 1);
m_implies_decl = mk_implies_decl();
m_proof_sort = m->mk_sort(symbol("Proof"), sort_info(id, PROOF_SORT));
@ -887,6 +889,7 @@ void basic_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol co
op_names.push_back(builtin_name("or", OP_OR));
op_names.push_back(builtin_name("xor", OP_XOR));
op_names.push_back(builtin_name("not", OP_NOT));
op_names.push_back(builtin_name("interp", OP_INTERP));
op_names.push_back(builtin_name("=>", OP_IMPLIES));
if (logic == symbol::null) {
// user friendly aliases
@ -898,6 +901,7 @@ void basic_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol co
op_names.push_back(builtin_name("||", OP_OR));
op_names.push_back(builtin_name("equals", OP_EQ));
op_names.push_back(builtin_name("equiv", OP_IFF));
op_names.push_back(builtin_name("@@", OP_INTERP));
}
}
@ -918,6 +922,7 @@ void basic_decl_plugin::finalize() {
DEC_REF(m_and_decl);
DEC_REF(m_or_decl);
DEC_REF(m_not_decl);
DEC_REF(m_interp_decl);
DEC_REF(m_iff_decl);
DEC_REF(m_xor_decl);
DEC_REF(m_implies_decl);
@ -1016,6 +1021,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
case OP_AND: return m_and_decl;
case OP_OR: return m_or_decl;
case OP_NOT: return m_not_decl;
case OP_INTERP: return m_interp_decl;
case OP_IFF: return m_iff_decl;
case OP_IMPLIES: return m_implies_decl;
case OP_XOR: return m_xor_decl;
@ -1051,6 +1057,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
case OP_AND: return m_and_decl;
case OP_OR: return m_or_decl;
case OP_NOT: return m_not_decl;
case OP_INTERP: return m_interp_decl;
case OP_IFF: return m_iff_decl;
case OP_IMPLIES: return m_implies_decl;
case OP_XOR: return m_xor_decl;
@ -3146,4 +3153,14 @@ void scoped_mark::pop_scope(unsigned num_scopes) {
}
}
// Added by KLM for use in GDB
// show an expr_ref on stdout
void prexpr(expr_ref &e){
std::cout << mk_pp(e.get(), e.get_manager()) << std::endl;
}
void ast_manager::show_id_gen(){
std::cout << "id_gen: " << m_expr_id_gen.show_hash() << " " << m_decl_id_gen.show_hash() << "\n";
}

View file

@ -1006,7 +1006,7 @@ enum basic_sort_kind {
};
enum basic_op_kind {
OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP,
OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, OP_INTERP, LAST_BASIC_OP,
PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO,
PR_DISTRIBUTIVITY, PR_AND_ELIM, PR_NOT_OR_ELIM, PR_REWRITE, PR_REWRITE_STAR, PR_PULL_QUANT,
@ -1028,6 +1028,7 @@ protected:
func_decl * m_iff_decl;
func_decl * m_xor_decl;
func_decl * m_not_decl;
func_decl * m_interp_decl;
func_decl * m_implies_decl;
ptr_vector<func_decl> m_eq_decls; // cached eqs
ptr_vector<func_decl> m_ite_decls; // cached ites
@ -1417,6 +1418,8 @@ protected:
public:
typedef expr_dependency_array_manager::ref expr_dependency_array;
void show_id_gen();
protected:
small_object_allocator m_alloc;
family_manager m_family_manager;
@ -2000,6 +2003,7 @@ public:
app * mk_distinct_expanded(unsigned num_args, expr * const * args);
app * mk_true() { return m_true; }
app * mk_false() { return m_false; }
app * mk_interp(expr * arg) { return mk_app(m_basic_family_id, OP_INTERP, arg); }
func_decl* mk_and_decl() {
sort* domain[2] = { m_bool_sort, m_bool_sort };

View file

@ -475,6 +475,7 @@ void float_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol co
op_names.push_back(builtin_name("plusInfinity", OP_FLOAT_PLUS_INF));
op_names.push_back(builtin_name("minusInfinity", OP_FLOAT_MINUS_INF));
op_names.push_back(builtin_name("NaN", OP_FLOAT_NAN));
op_names.push_back(builtin_name("roundNearestTiesToEven", OP_RM_NEAREST_TIES_TO_EVEN));
op_names.push_back(builtin_name("roundNearestTiesToAway", OP_RM_NEAREST_TIES_TO_AWAY));
op_names.push_back(builtin_name("roundTowardPositive", OP_RM_TOWARD_POSITIVE));
@ -486,7 +487,7 @@ void float_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol co
op_names.push_back(builtin_name("/", OP_FLOAT_DIV));
op_names.push_back(builtin_name("*", OP_FLOAT_MUL));
op_names.push_back(builtin_name("abs", OP_FLOAT_ABS));
op_names.push_back(builtin_name("abs", OP_FLOAT_ABS));
op_names.push_back(builtin_name("remainder", OP_FLOAT_REM));
op_names.push_back(builtin_name("fusedMA", OP_FLOAT_FUSED_MA));
op_names.push_back(builtin_name("squareRoot", OP_FLOAT_SQRT));
@ -515,6 +516,49 @@ void float_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol co
if (m_bv_plugin)
op_names.push_back(builtin_name("asIEEEBV", OP_TO_IEEE_BV));
// We also support draft version 3
op_names.push_back(builtin_name("fp", OP_TO_FLOAT));
op_names.push_back(builtin_name("RNE", OP_RM_NEAREST_TIES_TO_EVEN));
op_names.push_back(builtin_name("RNA", OP_RM_NEAREST_TIES_TO_AWAY));
op_names.push_back(builtin_name("RTP", OP_RM_TOWARD_POSITIVE));
op_names.push_back(builtin_name("RTN", OP_RM_TOWARD_NEGATIVE));
op_names.push_back(builtin_name("RTZ", OP_RM_TOWARD_ZERO));
op_names.push_back(builtin_name("fp.abs", OP_FLOAT_ABS));
op_names.push_back(builtin_name("fp.neg", OP_FLOAT_UMINUS));
op_names.push_back(builtin_name("fp.add", OP_FLOAT_ADD));
op_names.push_back(builtin_name("fp.sub", OP_FLOAT_SUB));
op_names.push_back(builtin_name("fp.mul", OP_FLOAT_MUL));
op_names.push_back(builtin_name("fp.div", OP_FLOAT_DIV));
op_names.push_back(builtin_name("fp.fma", OP_FLOAT_FUSED_MA));
op_names.push_back(builtin_name("fp.sqrt", OP_FLOAT_SQRT));
op_names.push_back(builtin_name("fp.rem", OP_FLOAT_REM));
op_names.push_back(builtin_name("fp.eq", OP_FLOAT_EQ));
op_names.push_back(builtin_name("fp.leq", OP_FLOAT_LE));
op_names.push_back(builtin_name("fp.lt", OP_FLOAT_LT));
op_names.push_back(builtin_name("fp.geq", OP_FLOAT_GE));
op_names.push_back(builtin_name("fp.gt", OP_FLOAT_GT));
op_names.push_back(builtin_name("fp.isNormal", OP_FLOAT_IS_NORMAL));
op_names.push_back(builtin_name("fp.isSubnormal", OP_FLOAT_IS_SUBNORMAL));
op_names.push_back(builtin_name("fp.isZero", OP_FLOAT_IS_ZERO));
op_names.push_back(builtin_name("fp.isInfinite", OP_FLOAT_IS_INF));
op_names.push_back(builtin_name("fp.isNaN", OP_FLOAT_IS_NAN));
op_names.push_back(builtin_name("fp.min", OP_FLOAT_MIN));
op_names.push_back(builtin_name("fp.max", OP_FLOAT_MAX));
op_names.push_back(builtin_name("fp.convert", OP_TO_FLOAT));
if (m_bv_plugin) {
// op_names.push_back(builtin_name("fp.fromBv", OP_TO_FLOAT));
// op_names.push_back(builtin_name("fp.fromUBv", OP_TO_FLOAT));
// op_names.push_back(builtin_name("fp.fromSBv", OP_TO_FLOAT));
// op_names.push_back(builtin_name("fp.toUBv", OP_TO_IEEE_BV));
// op_names.push_back(builtin_name("fp.toSBv", OP_TO_IEEE_BV));
}
op_names.push_back(builtin_name("fp.fromReal", OP_TO_FLOAT));
// op_names.push_back(builtin_name("fp.toReal", ?));
}
void float_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) {

View file

@ -479,7 +479,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
// otherwise t2 is also a quantifier.
return true;
}
UNREACHABLE();
IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";);
return false;
}
case PR_DER: {
@ -488,13 +488,12 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
match_fact(p, fact) &&
match_iff(fact.get(), t1, t2) &&
match_quantifier(t1, is_forall, decls1, body1) &&
is_forall &&
match_or(body1.get(), terms1)) {
is_forall) {
// TBD: check that terms are set of equalities.
// t2 is an instance of a predicate in terms1
return true;
}
UNREACHABLE();
}
IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";);
return false;
}
case PR_HYPOTHESIS: {
@ -832,7 +831,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
}
else {
IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" <<
mk_pp(lit1, m) << "\n" << mk_pp(lit2, m) << "\n";);
mk_pp(lit1, m) << "\n" << mk_pp(lit2, m) << "\n" << mk_pp(p, m) << "\n";);
}
fmls[i] = premise1;
}

View file

@ -68,7 +68,7 @@ bool array_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * co
set_reduce_invoked();
if (m_presimp)
return false;
#if _DEBUG
#if Z3DEBUG
for (unsigned i = 0; i < num_args && i < f->get_arity(); ++i) {
SASSERT(m_manager.get_sort(args[i]) == f->get_domain(i));
}

View file

@ -240,6 +240,8 @@ protected:
symbol m_produce_unsat_cores;
symbol m_produce_models;
symbol m_produce_assignments;
symbol m_produce_interpolants;
symbol m_check_interpolants;
symbol m_regular_output_channel;
symbol m_diagnostic_output_channel;
symbol m_random_seed;
@ -253,7 +255,9 @@ protected:
return
s == m_print_success || s == m_print_warning || s == m_expand_definitions ||
s == m_interactive_mode || s == m_produce_proofs || s == m_produce_unsat_cores ||
s == m_produce_models || s == m_produce_assignments || s == m_regular_output_channel || s == m_diagnostic_output_channel ||
s == m_produce_models || s == m_produce_assignments || s == m_produce_interpolants ||
s == m_check_interpolants ||
s == m_regular_output_channel || s == m_diagnostic_output_channel ||
s == m_random_seed || s == m_verbosity || s == m_global_decls;
}
@ -270,6 +274,8 @@ public:
m_produce_unsat_cores(":produce-unsat-cores"),
m_produce_models(":produce-models"),
m_produce_assignments(":produce-assignments"),
m_produce_interpolants(":produce-interpolants"),
m_check_interpolants(":check-interpolants"),
m_regular_output_channel(":regular-output-channel"),
m_diagnostic_output_channel(":diagnostic-output-channel"),
m_random_seed(":random-seed"),
@ -337,6 +343,13 @@ class set_option_cmd : public set_get_option_cmd {
check_not_initialized(ctx, m_produce_proofs);
ctx.set_produce_proofs(to_bool(value));
}
else if (m_option == m_produce_interpolants) {
check_not_initialized(ctx, m_produce_interpolants);
ctx.set_produce_interpolants(to_bool(value));
}
else if (m_option == m_check_interpolants) {
ctx.set_check_interpolants(to_bool(value));
}
else if (m_option == m_produce_unsat_cores) {
check_not_initialized(ctx, m_produce_unsat_cores);
ctx.set_produce_unsat_cores(to_bool(value));
@ -485,6 +498,9 @@ public:
else if (opt == m_produce_proofs) {
print_bool(ctx, ctx.produce_proofs());
}
else if (opt == m_produce_interpolants) {
print_bool(ctx, ctx.produce_interpolants());
}
else if (opt == m_produce_unsat_cores) {
print_bool(ctx, ctx.produce_unsat_cores());
}

View file

@ -39,6 +39,7 @@ Notes:
#include"model_evaluator.h"
#include"for_each_expr.h"
#include"scoped_timer.h"
#include"interpolant_cmds.h"
func_decls::func_decls(ast_manager & m, func_decl * f):
m_decls(TAG(func_decl*, f, 0)) {
@ -324,6 +325,7 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l):
install_basic_cmds(*this);
install_ext_basic_cmds(*this);
install_core_tactic_cmds(*this);
install_interpolant_cmds(*this);
SASSERT(m != 0 || !has_manager());
if (m)
init_external_manager();
@ -381,6 +383,19 @@ void cmd_context::set_produce_proofs(bool f) {
m_params.m_proof = f;
}
void cmd_context::set_produce_interpolants(bool f) {
// can only be set before initialization
// FIXME currently synonym for produce_proofs
// also sets the default solver to be simple smt
SASSERT(!has_manager());
m_params.m_proof = f;
// set_solver_factory(mk_smt_solver_factory());
}
void cmd_context::set_check_interpolants(bool f) {
m_params.m_check_interpolants = f;
}
bool cmd_context::produce_models() const {
return m_params.m_model;
}
@ -389,6 +404,15 @@ bool cmd_context::produce_proofs() const {
return m_params.m_proof;
}
bool cmd_context::produce_interpolants() const {
// FIXME currently synonym for produce_proofs
return m_params.m_proof;
}
bool cmd_context::check_interpolants() const {
return m_params.m_check_interpolants;
}
bool cmd_context::produce_unsat_cores() const {
return m_params.m_unsat_core;
}
@ -1458,11 +1482,27 @@ void cmd_context::validate_model() {
}
}
// FIXME: really interpolants_enabled ought to be a parameter to the solver factory,
// but this is a global change, so for now, we use an alternate solver factory
// for interpolation
void cmd_context::mk_solver() {
bool proofs_enabled, models_enabled, unsat_core_enabled;
params_ref p;
m_params.get_solver_params(m(), p, proofs_enabled, models_enabled, unsat_core_enabled);
m_solver = (*m_solver_factory)(m(), p, proofs_enabled, models_enabled, unsat_core_enabled, m_logic);
if(produce_interpolants()){
SASSERT(m_interpolating_solver_factory);
m_solver = (*m_interpolating_solver_factory)(m(), p, true /* must have proofs */, models_enabled, unsat_core_enabled, m_logic);
}
else
m_solver = (*m_solver_factory)(m(), p, proofs_enabled, models_enabled, unsat_core_enabled, m_logic);
}
void cmd_context::set_interpolating_solver_factory(solver_factory * f) {
SASSERT(!has_manager());
m_interpolating_solver_factory = f;
}
void cmd_context::set_solver_factory(solver_factory * f) {

View file

@ -186,6 +186,7 @@ protected:
svector<scope> m_scopes;
scoped_ptr<solver_factory> m_solver_factory;
scoped_ptr<solver_factory> m_interpolating_solver_factory;
ref<solver> m_solver;
ref<check_sat_result> m_check_sat_result;
@ -252,6 +253,8 @@ public:
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
context_params & params() { return m_params; }
solver_factory &get_solver_factory() { return *m_solver_factory; }
solver_factory &get_interpolating_solver_factory() { return *m_interpolating_solver_factory; }
void global_params_updated(); // this method should be invoked when global (and module) params are updated.
bool set_logic(symbol const & s);
bool has_logic() const { return m_logic != symbol::null; }
@ -274,12 +277,16 @@ public:
void set_random_seed(unsigned s) { m_random_seed = s; }
bool produce_models() const;
bool produce_proofs() const;
bool produce_interpolants() const;
bool check_interpolants() const;
bool produce_unsat_cores() const;
bool well_sorted_check_enabled() const;
bool validate_model_enabled() const;
void set_produce_models(bool flag);
void set_produce_unsat_cores(bool flag);
void set_produce_proofs(bool flag);
void set_produce_interpolants(bool flag);
void set_check_interpolants(bool flag);
bool produce_assignments() const { return m_produce_assignments; }
void set_produce_assignments(bool flag) { m_produce_assignments = flag; }
void set_status(status st) { m_status = st; }
@ -293,6 +300,7 @@ public:
sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast<cmd_context*>(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; }
void set_solver_factory(solver_factory * s);
void set_interpolating_solver_factory(solver_factory * s);
void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; }
check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); }
check_sat_state cs_state() const;

View file

@ -61,6 +61,9 @@ void context_params::set(char const * param, char const * value) {
else if (p == "proof") {
set_bool(m_proof, param, value);
}
else if (p == "check_interpolants") {
set_bool(m_check_interpolants, param, value);
}
else if (p == "model") {
set_bool(m_model, param, value);
}
@ -96,6 +99,7 @@ void context_params::updt_params(params_ref const & p) {
m_well_sorted_check = p.get_bool("type_check", p.get_bool("well_sorted_check", true));
m_auto_config = p.get_bool("auto_config", true);
m_proof = p.get_bool("proof", false);
m_check_interpolants = p.get_bool("check_interpolants", false);
m_model = p.get_bool("model", true);
m_model_validate = p.get_bool("model_validate", false);
m_trace = p.get_bool("trace", false);
@ -111,6 +115,7 @@ void context_params::collect_param_descrs(param_descrs & d) {
d.insert("type_check", CPK_BOOL, "type checker (alias for well_sorted_check)", "true");
d.insert("auto_config", CPK_BOOL, "use heuristics to automatically select solver and configure it", "true");
d.insert("proof", CPK_BOOL, "proof generation, it must be enabled when the Z3 context is created", "false");
d.insert("check_interpolants", CPK_BOOL, "check correctness of interpolants", "false");
d.insert("model", CPK_BOOL, "model generation for solvers, this parameter can be overwritten when creating a solver", "true");
d.insert("model_validate", CPK_BOOL, "validate models produced by solvers", "false");
d.insert("trace", CPK_BOOL, "trace generation for VCC", "false");

View file

@ -29,6 +29,8 @@ class context_params {
public:
bool m_auto_config;
bool m_proof;
bool m_interpolants;
bool m_check_interpolants;
bool m_debug_ref_count;
bool m_trace;
std::string m_trace_file_name;

View file

@ -0,0 +1,272 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
interpolant_cmds.cpp
Abstract:
Commands for interpolation.
Author:
Leonardo (leonardo) 2011-12-23
Notes:
--*/
#include<sstream>
#include"cmd_context.h"
#include"cmd_util.h"
#include"scoped_timer.h"
#include"scoped_ctrl_c.h"
#include"cancel_eh.h"
#include"ast_pp.h"
#include"ast_smt_pp.h"
#include"ast_smt2_pp.h"
#include"parametric_cmd.h"
#include"mpq.h"
#include"expr2var.h"
#include"pp.h"
#include"pp_params.hpp"
#include"iz3interp.h"
#include"iz3checker.h"
#include"iz3profiling.h"
#include"interp_params.hpp"
static void show_interpolant_and_maybe_check(cmd_context & ctx,
ptr_vector<ast> &cnsts,
expr *t,
ptr_vector<ast> &interps,
params_ref &m_params,
bool check)
{
if (m_params.get_bool("som", false))
m_params.set_bool("flat", true);
th_rewriter s(ctx.m(), m_params);
for(unsigned i = 0; i < interps.size(); i++){
expr_ref r(ctx.m());
proof_ref pr(ctx.m());
s(to_expr(interps[i]),r,pr);
ctx.regular_stream() << mk_pp(r.get(), ctx.m()) << std::endl;
#if 0
ast_smt_pp pp(ctx.m());
pp.set_logic(ctx.get_logic().str().c_str());
pp.display_smt2(ctx.regular_stream(), to_expr(interps[i]));
ctx.regular_stream() << std::endl;
#endif
}
s.cleanup();
// verify, for the paranoid...
if(check || ctx.check_interpolants()){
std::ostringstream err;
ast_manager &_m = ctx.m();
// need a solver -- make one here FIXME is this right?
bool proofs_enabled, models_enabled, unsat_core_enabled;
params_ref p;
ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled);
scoped_ptr<solver> sp = (ctx.get_solver_factory())(_m, p, false, true, false, ctx.get_logic());
if(iz3check(_m,sp.get(),err,cnsts,t,interps))
ctx.regular_stream() << "correct\n";
else
ctx.regular_stream() << "incorrect: " << err.str().c_str() << "\n";
}
for(unsigned i = 0; i < interps.size(); i++){
ctx.m().dec_ref(interps[i]);
}
interp_params itp_params(m_params);
if(itp_params.profile())
profiling::print(ctx.regular_stream());
}
static void check_can_interpolate(cmd_context & ctx){
if (!ctx.produce_interpolants())
throw cmd_exception("interpolation is not enabled, use command (set-option :produce-interpolants true)");
}
static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check) {
check_can_interpolate(ctx);
// get the proof, if there is one
if (!ctx.has_manager() ||
ctx.cs_state() != cmd_context::css_unsat)
throw cmd_exception("proof is not available");
expr_ref pr(ctx.m());
pr = ctx.get_check_sat_result()->get_proof();
if (pr == 0)
throw cmd_exception("proof is not available");
// get the assertions from the context
ptr_vector<expr>::const_iterator it = ctx.begin_assertions();
ptr_vector<expr>::const_iterator end = ctx.end_assertions();
ptr_vector<ast> cnsts(end - it);
for (int i = 0; it != end; ++it, ++i)
cnsts[i] = *it;
// compute an interpolant
ptr_vector<ast> interps;
try {
iz3interpolate(ctx.m(),pr.get(),cnsts,t,interps,0);
}
catch (iz3_bad_tree &) {
throw cmd_exception("interpolation pattern contains non-asserted formula");
}
catch (iz3_incompleteness &) {
throw cmd_exception("incompleteness in interpolator");
}
show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check);
}
static void get_interpolant(cmd_context & ctx, expr * t, params_ref &m_params) {
get_interpolant_and_maybe_check(ctx,t,m_params,false);
}
static void get_and_check_interpolant(cmd_context & ctx, params_ref &m_params, expr * t) {
get_interpolant_and_maybe_check(ctx,t,m_params,true);
}
static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check){
// create a fresh solver suitable for interpolation
bool proofs_enabled, models_enabled, unsat_core_enabled;
params_ref p;
ast_manager &_m = ctx.m();
// TODO: the following is a HACK to enable proofs in the old smt solver
// When we stop using that solver, this hack can be removed
_m.toggle_proof_mode(PGM_FINE);
ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled);
p.set_bool("proof", true);
scoped_ptr<solver> sp = (ctx.get_interpolating_solver_factory())(_m, p, true, models_enabled, false, ctx.get_logic());
ptr_vector<ast> cnsts;
ptr_vector<ast> interps;
model_ref m;
// compute an interpolant
lbool res;
try {
res = iz3interpolate(_m, *sp.get(), t, cnsts, interps, m, 0);
}
catch (iz3_incompleteness &) {
throw cmd_exception("incompleteness in interpolator");
}
switch(res){
case l_false:
ctx.regular_stream() << "unsat\n";
show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check);
break;
case l_true:
ctx.regular_stream() << "sat\n";
// TODO: how to return the model to the context, if it exists?
break;
case l_undef:
ctx.regular_stream() << "unknown\n";
// TODO: how to return the model to the context, if it exists?
break;
}
for(unsigned i = 0; i < cnsts.size(); i++)
ctx.m().dec_ref(cnsts[i]);
}
static expr *make_tree(cmd_context & ctx, const ptr_vector<expr> &exprs){
if(exprs.size() == 0)
throw cmd_exception("not enough arguments");
expr *foo = exprs[0];
for(unsigned i = 1; i < exprs.size(); i++){
foo = ctx.m().mk_and(ctx.m().mk_interp(foo),exprs[i]);
}
return foo;
}
static void get_interpolant(cmd_context & ctx, const ptr_vector<expr> &exprs, params_ref &m_params) {
expr *foo = make_tree(ctx,exprs);
ctx.m().inc_ref(foo);
get_interpolant(ctx,foo,m_params);
ctx.m().dec_ref(foo);
}
static void compute_interpolant(cmd_context & ctx, const ptr_vector<expr> &exprs, params_ref &m_params) {
expr *foo = make_tree(ctx, exprs);
ctx.m().inc_ref(foo);
compute_interpolant_and_maybe_check(ctx,foo,m_params,false);
ctx.m().dec_ref(foo);
}
// UNARY_CMD(get_interpolant_cmd, "get-interpolant", "<fmla>", "get interpolant for marked positions in fmla", CPK_EXPR, expr *, get_interpolant(ctx, arg););
// UNARY_CMD(get_and_check_interpolant_cmd, "get-and-check-interpolant", "<fmla>", "get and check interpolant for marked positions in fmla", CPK_EXPR, expr *, get_and_check_interpolant(ctx, arg););
class get_interpolant_cmd : public parametric_cmd {
protected:
ptr_vector<expr> m_targets;
public:
get_interpolant_cmd(char const * name = "get-interpolant"):parametric_cmd(name) {}
virtual char const * get_usage() const { return "<fmla>+"; }
virtual char const * get_main_descr() const {
return "get interpolant for formulas";
}
virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) {
}
virtual void prepare(cmd_context & ctx) {
parametric_cmd::prepare(ctx);
m_targets.resize(0);
}
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const {
return CPK_EXPR;
}
virtual void set_next_arg(cmd_context & ctx, expr * arg) {
m_targets.push_back(arg);
}
virtual void execute(cmd_context & ctx) {
get_interpolant(ctx,m_targets,m_params);
}
};
class compute_interpolant_cmd : public get_interpolant_cmd {
public:
compute_interpolant_cmd(char const * name = "compute-interpolant"):get_interpolant_cmd(name) {}
virtual void execute(cmd_context & ctx) {
compute_interpolant(ctx,m_targets,m_params);
}
};
void install_interpolant_cmds(cmd_context & ctx) {
ctx.insert(alloc(get_interpolant_cmd));
ctx.insert(alloc(compute_interpolant_cmd));
// ctx.insert(alloc(get_and_check_interpolant_cmd));
}

View file

@ -0,0 +1,24 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
interpolant_cmds.h
Abstract:
Commands for interpolation.
Author:
Leonardo (leonardo) 2011-12-23
Notes:
--*/
#ifndef _INTERPOLANT_CMDS_H_
#define _INTERPOLANT_CMDS_H_
class cmd_context;
void install_interpolant_cmds(cmd_context & ctx);
#endif

1044
src/duality/duality.h Normal file

File diff suppressed because it is too large Load diff

169
src/duality/duality_hash.h Executable file
View file

@ -0,0 +1,169 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3hash.h
Abstract:
Wrapper for stl hash tables
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
// pull in the headers for has_map and hash_set
// these live in non-standard places
#ifndef IZ3_HASH_H
#define IZ3_HASH_H
//#define USE_UNORDERED_MAP
#ifdef USE_UNORDERED_MAP
#define stl_ext std
#define hash_space std
#include <unordered_map>
#include <unordered_set>
#define hash_map unordered_map
#define hash_set unordered_set
#else
#if __GNUC__ >= 3
#undef __DEPRECATED
#define stl_ext __gnu_cxx
#define hash_space stl_ext
#include <ext/hash_map>
#include <ext/hash_set>
#else
#ifdef WIN32
#define stl_ext stdext
#define hash_space std
#include <hash_map>
#include <hash_set>
#else
#define stl_ext std
#define hash_space std
#include <hash_map>
#include <hash_set>
#endif
#endif
#endif
#include <string>
// stupid STL doesn't include hash function for class string
#ifndef WIN32
namespace stl_ext {
template <>
class hash<std::string> {
stl_ext::hash<char *> H;
public:
size_t operator()(const std::string &s) const {
return H(s.c_str());
}
};
}
#endif
namespace hash_space {
template <>
class hash<std::pair<int,int> > {
public:
size_t operator()(const std::pair<int,int> &p) const {
return p.first + p.second;
}
};
}
#ifdef WIN32
template <> inline
size_t stdext::hash_value<std::pair<int,int> >(const std::pair<int,int>& p)
{ // hash _Keyval to size_t value one-to-one
return p.first + p.second;
}
#endif
namespace hash_space {
template <class T>
class hash<std::pair<T *, T *> > {
public:
size_t operator()(const std::pair<T *,T *> &p) const {
return (size_t)p.first + (size_t)p.second;
}
};
}
#if 0
template <class T> inline
size_t stdext::hash_value<std::pair<T *, T *> >(const std::pair<T *, T *>& p)
{ // hash _Keyval to size_t value one-to-one
return (size_t)p.first + (size_t)p.second;
}
#endif
#ifdef WIN32
namespace std {
template <>
class less<std::pair<int,int> > {
public:
bool operator()(const pair<int,int> &x, const pair<int,int> &y) const {
return x.first < y.first || x.first == y.first && x.second < y.second;
}
};
}
namespace std {
template <class T>
class less<std::pair<T *,T *> > {
public:
bool operator()(const pair<T *,T *> &x, const pair<T *,T *> &y) const {
return (size_t)x.first < (size_t)y.first || (size_t)x.first == (size_t)y.first && (size_t)x.second < (size_t)y.second;
}
};
}
#endif
#ifndef WIN32
namespace stl_ext {
template <class T>
class hash<T *> {
public:
size_t operator()(const T *p) const {
return (size_t) p;
}
};
}
#endif
#ifdef WIN32
template <class K, class T>
class hash_map : public stl_ext::hash_map<K,T,stl_ext::hash_compare<K,std::less<K> > > {};
template <class K>
class hash_set : public stl_ext::hash_set<K,stl_ext::hash_compare<K,std::less<K> > > {};
#endif
#endif

126
src/duality/duality_profiling.cpp Executable file
View file

@ -0,0 +1,126 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
duality_profiling.cpp
Abstract:
collection performance information for duality
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include <map>
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include "duality_wrapper.h"
namespace Duality {
void show_time(){
output_time(std::cout,current_time());
std::cout << "\n";
}
typedef std::map<const char*, struct node> nmap;
struct node {
std::string name;
clock_t time;
clock_t start_time;
nmap sub;
struct node *parent;
node();
} top;
node::node(){
time = 0;
parent = 0;
}
struct node *current;
struct init {
init(){
top.name = "TOTAL";
current = &top;
}
} initializer;
struct time_entry {
clock_t t;
time_entry(){t = 0;};
void add(clock_t incr){t += incr;}
};
struct ltstr
{
bool operator()(const char* s1, const char* s2) const
{
return strcmp(s1, s2) < 0;
}
};
typedef std::map<const char*, time_entry, ltstr> tmap;
static std::ostream *pfs;
void print_node(node &top, int indent, tmap &totals){
for(int i = 0; i < indent; i++) (*pfs) << " ";
(*pfs) << top.name;
int dots = 70 - 2 * indent - top.name.size();
for(int i = 0; i <dots; i++) (*pfs) << ".";
output_time(*pfs, top.time);
(*pfs) << std::endl;
if(indent != 0)totals[top.name.c_str()].add(top.time);
for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++)
print_node(it->second,indent+1,totals);
}
void print_profile(std::ostream &os) {
pfs = &os;
top.time = 0;
for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++)
top.time += it->second.time;
tmap totals;
print_node(top,0,totals);
(*pfs) << "TOTALS:" << std::endl;
for(tmap::iterator it = totals.begin(); it != totals.end(); it++){
(*pfs) << (it->first) << " ";
output_time(*pfs, it->second.t);
(*pfs) << std::endl;
}
}
void timer_start(const char *name){
node &child = current->sub[name];
if(child.name.empty()){ // a new node
child.parent = current;
child.name = name;
}
child.start_time = current_time();
current = &child;
}
void timer_stop(const char *name){
if(current->name != name || !current->parent){
std::cerr << "imbalanced timer_start and timer_stop";
exit(1);
}
current->time += (current_time() - current->start_time);
current = current->parent;
}
}

38
src/duality/duality_profiling.h Executable file
View file

@ -0,0 +1,38 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
duality_profiling.h
Abstract:
collection performance information for duality
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef DUALITYPROFILING_H
#define DUALITYPROFILING_H
#include <ostream>
namespace Duality {
/** Start a timer with given name */
void timer_start(const char *);
/** Stop a timer with given name */
void timer_stop(const char *);
/** Print out timings */
void print_profile(std::ostream &s);
/** Show the current time. */
void show_time();
}
#endif

2894
src/duality/duality_rpfp.cpp Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,677 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
wrapper.cpp
Abstract:
wrap various objects in the style expected by duality
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include "duality_wrapper.h"
#include <iostream>
#include "smt_solver.h"
#include "iz3interp.h"
#include "statistics.h"
#include "expr_abstract.h"
#include "stopwatch.h"
#include "model_smt2_pp.h"
#include "qe_lite.h"
namespace Duality {
solver::solver(Duality::context& c, bool extensional) : object(c), the_model(c) {
params_ref p;
p.set_bool("proof", true); // this is currently useless
p.set_bool("model", true);
p.set_bool("unsat_core", true);
p.set_bool("mbqi",true);
p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants
p.set_uint("mbqi.max_iterations",1); // use mbqi for quantifiers in interpolants
if(true || extensional)
p.set_bool("array.extensional",true);
scoped_ptr<solver_factory> sf = mk_smt_solver_factory();
m_solver = (*sf)(m(), p, true, true, true, ::symbol::null);
m_solver->updt_params(p); // why do we have to do this?
canceled = false;
}
expr context::constant(const std::string &name, const sort &ty){
symbol s = str_symbol(name.c_str());
return cook(m().mk_const(m().mk_const_decl(s, ty)));
}
expr context::make(decl_kind op, int n, ::expr **args){
switch(op) {
case True: return mki(m_basic_fid,OP_TRUE,n,args);
case False: return mki(m_basic_fid,OP_FALSE,n,args);
case Equal: return mki(m_basic_fid,OP_EQ,n,args);
case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args);
case Ite: return mki(m_basic_fid,OP_ITE,n,args);
case And: return mki(m_basic_fid,OP_AND,n,args);
case Or: return mki(m_basic_fid,OP_OR,n,args);
case Iff: return mki(m_basic_fid,OP_IFF,n,args);
case Xor: return mki(m_basic_fid,OP_XOR,n,args);
case Not: return mki(m_basic_fid,OP_NOT,n,args);
case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args);
case Oeq: return mki(m_basic_fid,OP_OEQ,n,args);
case Interp: return mki(m_basic_fid,OP_INTERP,n,args);
case Leq: return mki(m_arith_fid,OP_LE,n,args);
case Geq: return mki(m_arith_fid,OP_GE,n,args);
case Lt: return mki(m_arith_fid,OP_LT,n,args);
case Gt: return mki(m_arith_fid,OP_GT,n,args);
case Plus: return mki(m_arith_fid,OP_ADD,n,args);
case Sub: return mki(m_arith_fid,OP_SUB,n,args);
case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args);
case Times: return mki(m_arith_fid,OP_MUL,n,args);
case Div: return mki(m_arith_fid,OP_DIV,n,args);
case Idiv: return mki(m_arith_fid,OP_IDIV,n,args);
case Rem: return mki(m_arith_fid,OP_REM,n,args);
case Mod: return mki(m_arith_fid,OP_MOD,n,args);
case Power: return mki(m_arith_fid,OP_POWER,n,args);
case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args);
case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args);
case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args);
case Store: return mki(m_array_fid,OP_STORE,n,args);
case Select: return mki(m_array_fid,OP_SELECT,n,args);
case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args);
case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args);
case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args);
case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args);
case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args);
case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args);
case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args);
case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args);
case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args);
default:
assert(0);
return expr(*this);
}
}
expr context::mki(family_id fid, ::decl_kind dk, int n, ::expr **args){
return cook(m().mk_app(fid, dk, 0, 0, n, (::expr **)args));
}
expr context::make(decl_kind op, const std::vector<expr> &args){
static std::vector< ::expr*> a(10);
if(a.size() < args.size())
a.resize(args.size());
for(unsigned i = 0; i < args.size(); i++)
a[i] = to_expr(args[i].raw());
return make(op,args.size(), args.size() ? &a[0] : 0);
}
expr context::make(decl_kind op){
return make(op,0,0);
}
expr context::make(decl_kind op, const expr &arg0){
::expr *a = to_expr(arg0.raw());
return make(op,1,&a);
}
expr context::make(decl_kind op, const expr &arg0, const expr &arg1){
::expr *args[2];
args[0] = to_expr(arg0.raw());
args[1] = to_expr(arg1.raw());
return make(op,2,args);
}
expr context::make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2){
::expr *args[3];
args[0] = to_expr(arg0.raw());
args[1] = to_expr(arg1.raw());
args[2] = to_expr(arg2.raw());
return make(op,3,args);
}
expr context::make_quant(decl_kind op, const std::vector<expr> &bvs, const expr &body){
if(bvs.size() == 0) return body;
std::vector< ::expr *> foo(bvs.size());
std::vector< ::symbol> names;
std::vector< ::sort *> types;
std::vector< ::expr *> bound_asts;
unsigned num_bound = bvs.size();
for (unsigned i = 0; i < num_bound; ++i) {
app* a = to_app(bvs[i].raw());
::symbol s(to_app(a)->get_decl()->get_name());
names.push_back(s);
types.push_back(m().get_sort(a));
bound_asts.push_back(a);
}
expr_ref abs_body(m());
expr_abstract(m(), 0, num_bound, &bound_asts[0], to_expr(body.raw()), abs_body);
expr_ref result(m());
result = m().mk_quantifier(
op == Forall,
names.size(), &types[0], &names[0], abs_body.get(),
0,
::symbol(),
::symbol(),
0, 0,
0, 0
);
return cook(result.get());
}
expr context::make_quant(decl_kind op, const std::vector<sort> &_sorts, const std::vector<symbol> &_names, const expr &body){
if(_sorts.size() == 0) return body;
std::vector< ::symbol> names;
std::vector< ::sort *> types;
std::vector< ::expr *> bound_asts;
unsigned num_bound = _sorts.size();
for (unsigned i = 0; i < num_bound; ++i) {
names.push_back(_names[i]);
types.push_back(to_sort(_sorts[i].raw()));
}
expr_ref result(m());
result = m().mk_quantifier(
op == Forall,
names.size(), &types[0], &names[0], to_expr(body.raw()),
0,
::symbol(),
::symbol(),
0, 0,
0, 0
);
return cook(result.get());
}
decl_kind func_decl::get_decl_kind() const {
return ctx().get_decl_kind(*this);
}
decl_kind context::get_decl_kind(const func_decl &t){
::func_decl *d = to_func_decl(t.raw());
if (null_family_id == d->get_family_id())
return Uninterpreted;
// return (opr)d->get_decl_kind();
if (m_basic_fid == d->get_family_id()) {
switch(d->get_decl_kind()) {
case OP_TRUE: return True;
case OP_FALSE: return False;
case OP_EQ: return Equal;
case OP_DISTINCT: return Distinct;
case OP_ITE: return Ite;
case OP_AND: return And;
case OP_OR: return Or;
case OP_IFF: return Iff;
case OP_XOR: return Xor;
case OP_NOT: return Not;
case OP_IMPLIES: return Implies;
case OP_OEQ: return Oeq;
case OP_INTERP: return Interp;
default:
return OtherBasic;
}
}
if (m_arith_fid == d->get_family_id()) {
switch(d->get_decl_kind()) {
case OP_LE: return Leq;
case OP_GE: return Geq;
case OP_LT: return Lt;
case OP_GT: return Gt;
case OP_ADD: return Plus;
case OP_SUB: return Sub;
case OP_UMINUS: return Uminus;
case OP_MUL: return Times;
case OP_DIV: return Div;
case OP_IDIV: return Idiv;
case OP_REM: return Rem;
case OP_MOD: return Mod;
case OP_POWER: return Power;
case OP_TO_REAL: return ToReal;
case OP_TO_INT: return ToInt;
case OP_IS_INT: return IsInt;
default:
return OtherArith;
}
}
if (m_array_fid == d->get_family_id()) {
switch(d->get_decl_kind()) {
case OP_STORE: return Store;
case OP_SELECT: return Select;
case OP_CONST_ARRAY: return ConstArray;
case OP_ARRAY_DEFAULT: return ArrayDefault;
case OP_ARRAY_MAP: return ArrayMap;
case OP_SET_UNION: return SetUnion;
case OP_SET_INTERSECT: return SetIntersect;
case OP_SET_DIFFERENCE: return SetDifference;
case OP_SET_COMPLEMENT: return SetComplement;
case OP_SET_SUBSET: return SetSubSet;
case OP_AS_ARRAY: return AsArray;
default:
return OtherArray;
}
}
return Other;
}
sort_kind context::get_sort_kind(const sort &s){
family_id fid = to_sort(s.raw())->get_family_id();
::decl_kind k = to_sort(s.raw())->get_decl_kind();
if (m().is_uninterp(to_sort(s.raw()))) {
return UninterpretedSort;
}
else if (fid == m_basic_fid && k == BOOL_SORT) {
return BoolSort;
}
else if (fid == m_arith_fid && k == INT_SORT) {
return IntSort;
}
else if (fid == m_arith_fid && k == REAL_SORT) {
return RealSort;
}
else if (fid == m_array_fid && k == ARRAY_SORT) {
return ArraySort;
}
else {
return UnknownSort;
}
}
expr func_decl::operator()(unsigned n, expr const * args) const {
std::vector< ::expr *> _args(n);
for(unsigned i = 0; i < n; i++)
_args[i] = to_expr(args[i].raw());
return ctx().cook(m().mk_app(to_func_decl(raw()),n,&_args[0]));
}
int solver::get_num_decisions(){
::statistics st;
m_solver->collect_statistics(st);
std::ostringstream ss;
st.display(ss);
std::string stats = ss.str();
int pos = stats.find("decisions:");
if(pos < 0) return 0; // for some reason, decisions are not reported if there are none
pos += 10;
int end = stats.find('\n',pos);
std::string val = stats.substr(pos,end-pos);
return atoi(val.c_str());
}
void context::print_expr(std::ostream &s, const ast &e){
s << mk_pp(e.raw(), m());
}
expr expr::simplify(const params &_p) const {
::expr * a = to_expr(raw());
params_ref p = _p.get();
th_rewriter m_rw(m(), p);
expr_ref result(m());
m_rw(a, result);
return ctx().cook(result);
}
expr expr::simplify() const {
params p;
return simplify(p);
}
expr expr::qe_lite() const {
::qe_lite qe(m());
expr_ref result(to_expr(raw()),m());
proof_ref pf(m());
qe(result,pf);
return ctx().cook(result);
}
expr clone_quantifier(const expr &q, const expr &b){
return q.ctx().cook(q.m().update_quantifier(to_quantifier(q.raw()), to_expr(b.raw())));
}
expr clone_quantifier(const expr &q, const expr &b, const std::vector<expr> &patterns){
quantifier *thing = to_quantifier(q.raw());
bool is_forall = thing->is_forall();
unsigned num_patterns = patterns.size();
std::vector< ::expr *> _patterns(num_patterns);
for(unsigned i = 0; i < num_patterns; i++)
_patterns[i] = to_expr(patterns[i].raw());
return q.ctx().cook(q.m().update_quantifier(thing, is_forall, num_patterns, &_patterns[0], to_expr(b.raw())));
}
void expr::get_patterns(std::vector<expr> &pats) const {
quantifier *thing = to_quantifier(raw());
unsigned num_patterns = thing->get_num_patterns();
:: expr * const *it = thing->get_patterns();
pats.resize(num_patterns);
for(unsigned i = 0; i < num_patterns; i++)
pats[i] = expr(ctx(),it[i]);
}
func_decl context::fresh_func_decl(char const * prefix, const std::vector<sort> &domain, sort const & range){
std::vector < ::sort * > _domain(domain.size());
for(unsigned i = 0; i < domain.size(); i++)
_domain[i] = to_sort(domain[i].raw());
::func_decl* d = m().mk_fresh_func_decl(prefix,
_domain.size(),
&_domain[0],
to_sort(range.raw()));
return func_decl(*this,d);
}
func_decl context::fresh_func_decl(char const * prefix, sort const & range){
::func_decl* d = m().mk_fresh_func_decl(prefix,
0,
0,
to_sort(range.raw()));
return func_decl(*this,d);
}
#if 0
lbool interpolating_solver::interpolate(
const std::vector<expr> &assumptions,
std::vector<expr> &interpolants,
model &model,
Z3_literals &labels,
bool incremental)
{
Z3_model _model = 0;
Z3_literals _labels = 0;
Z3_lbool lb;
std::vector<Z3_ast> _assumptions(assumptions.size());
std::vector<Z3_ast> _interpolants(assumptions.size()-1);
for(unsigned i = 0; i < assumptions.size(); i++)
_assumptions[i] = assumptions[i];
std::vector<Z3_ast> _theory(theory.size());
for(unsigned i = 0; i < theory.size(); i++)
_theory[i] = theory[i];
lb = Z3_interpolate(
ctx(),
_assumptions.size(),
&_assumptions[0],
0,
0,
&_interpolants[0],
&_model,
&_labels,
incremental,
_theory.size(),
&_theory[0]
);
if(lb == Z3_L_FALSE){
interpolants.resize(_interpolants.size());
for (unsigned i = 0; i < _interpolants.size(); ++i) {
interpolants[i] = expr(ctx(),_interpolants[i]);
}
}
if (_model) {
model = iz3wrapper::model(ctx(), _model);
}
if(_labels){
labels = _labels;
}
return lb;
}
#endif
static int linearize_assumptions(int num,
TermTree *assumptions,
std::vector<std::vector <expr> > &linear_assumptions,
std::vector<int> &parents){
for(unsigned i = 0; i < assumptions->getChildren().size(); i++)
num = linearize_assumptions(num, assumptions->getChildren()[i], linear_assumptions, parents);
// linear_assumptions[num].push_back(assumptions->getTerm());
for(unsigned i = 0; i < assumptions->getChildren().size(); i++)
parents[assumptions->getChildren()[i]->getNumber()] = num;
parents[num] = SHRT_MAX; // in case we have no parent
linear_assumptions[num].push_back(assumptions->getTerm());
std::vector<expr> &ts = assumptions->getTerms();
for(unsigned i = 0; i < ts.size(); i++)
linear_assumptions[num].push_back(ts[i]);
return num + 1;
}
static int unlinearize_interpolants(int num,
TermTree* assumptions,
const std::vector<expr> &interpolant,
TermTree * &tree_interpolant)
{
std::vector<TermTree *> chs(assumptions->getChildren().size());
for(unsigned i = 0; i < assumptions->getChildren().size(); i++)
num = unlinearize_interpolants(num, assumptions->getChildren()[i], interpolant,chs[i]);
expr f;
if(num < (int)interpolant.size()) // last interpolant is missing, presumed false
f = interpolant[num];
tree_interpolant = new TermTree(f,chs);
return num + 1;
}
lbool interpolating_solver::interpolate_tree(TermTree *assumptions,
TermTree *&interpolant,
model &model,
literals &labels,
bool incremental
)
{
int size = assumptions->number(0);
std::vector<std::vector<expr> > linear_assumptions(size);
std::vector<int> parents(size);
linearize_assumptions(0,assumptions,linear_assumptions,parents);
ptr_vector< ::ast> _interpolants(size-1);
vector<ptr_vector< ::ast> >_assumptions(size);
for(int i = 0; i < size; i++)
for(unsigned j = 0; j < linear_assumptions[i].size(); j++)
_assumptions[i].push_back(linear_assumptions[i][j]);
::vector<int> _parents; _parents.resize(parents.size());
for(unsigned i = 0; i < parents.size(); i++)
_parents[i] = parents[i];
ptr_vector< ::ast> _theory(theory.size());
for(unsigned i = 0; i < theory.size(); i++)
_theory[i] = theory[i];
if(!incremental){
push();
for(unsigned i = 0; i < linear_assumptions.size(); i++)
for(unsigned j = 0; j < linear_assumptions[i].size(); j++)
add(linear_assumptions[i][j]);
}
check_result res = check();
if(res == unsat){
interpolation_options_struct opts;
if(weak_mode)
opts.set("weak","1");
::ast *proof = m_solver->get_proof();
iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts);
std::vector<expr> linearized_interpolants(_interpolants.size());
for(unsigned i = 0; i < _interpolants.size(); i++)
linearized_interpolants[i] = expr(ctx(),_interpolants[i]);
// since iz3interpolant returns interpolants with one ref count, we decrement here
for(unsigned i = 0; i < _interpolants.size(); i++)
m().dec_ref(_interpolants[i]);
unlinearize_interpolants(0,assumptions,linearized_interpolants,interpolant);
interpolant->setTerm(ctx().bool_val(false));
}
model_ref _m;
m_solver->get_model(_m);
model = Duality::model(ctx(),_m.get());
#if 0
if(_labels){
labels = _labels;
}
#endif
if(!incremental)
pop();
return (res == unsat) ? l_false : ((res == sat) ? l_true : l_undef);
}
void interpolating_solver::SetWeakInterpolants(bool weak){
weak_mode = weak;
}
void interpolating_solver::SetPrintToFile(const std::string &filename){
print_filename = filename;
}
void interpolating_solver::AssertInterpolationAxiom(const expr & t){
add(t);
theory.push_back(t);
}
void interpolating_solver::RemoveInterpolationAxiom(const expr & t){
// theory.remove(t);
}
const char *interpolating_solver::profile(){
// return Z3_interpolation_profile(ctx());
return "";
}
static void get_assumptions_rec(stl_ext::hash_set<ast> &memo, const proof &pf, std::vector<expr> &assumps){
if(memo.find(pf) != memo.end())return;
memo.insert(pf);
pfrule dk = pf.rule();
if(dk == PR_ASSERTED){
expr con = pf.conc();
assumps.push_back(con);
}
else {
unsigned nprems = pf.num_prems();
for(unsigned i = 0; i < nprems; i++){
proof arg = pf.prem(i);
get_assumptions_rec(memo,arg,assumps);
}
}
}
void proof::get_assumptions(std::vector<expr> &assumps){
stl_ext::hash_set<ast> memo;
get_assumptions_rec(memo,*this,assumps);
}
void ast::show() const{
std::cout << mk_pp(raw(), m()) << std::endl;
}
void model::show() const {
model_smt2_pp(std::cout, m(), *m_model, 0);
std::cout << std::endl;
}
void model::show_hash() const {
std::ostringstream ss;
model_smt2_pp(ss, m(), *m_model, 0);
hash_space::hash<std::string> hasher;
unsigned h = hasher(ss.str());
std::cout << "model hash: " << h << "\n";
}
void solver::show() {
unsigned n = m_solver->get_num_assertions();
if(!n)
return;
ast_smt_pp pp(m());
for (unsigned i = 0; i < n-1; ++i)
pp.add_assumption(m_solver->get_assertion(i));
pp.display_smt2(std::cout, m_solver->get_assertion(n-1));
}
void solver::show_assertion_ids() {
#if 0
unsigned n = m_solver->get_num_assertions();
std::cerr << "assertion ids: ";
for (unsigned i = 0; i < n-1; ++i)
std::cerr << " " << m_solver->get_assertion(i)->get_id();
std::cerr << "\n";
#else
unsigned n = m_solver->get_num_assertions();
std::cerr << "assertion ids hash: ";
unsigned h = 0;
for (unsigned i = 0; i < n-1; ++i)
h += m_solver->get_assertion(i)->get_id();
std::cerr << h << "\n";
#endif
}
void include_ast_show(ast &a){
a.show();
}
void include_model_show(model &a){
a.show();
}
void show_ast(::ast *a, ast_manager &m) {
std::cout << mk_pp(a, m) << std::endl;
}
bool expr::is_label (bool &pos,std::vector<symbol> &names) const {
buffer< ::symbol> _names;
bool res = m().is_label(to_expr(raw()),pos,_names);
if(res)
for(unsigned i = 0; i < _names.size(); i++)
names.push_back(symbol(ctx(),_names[i]));
return res;
}
double current_time()
{
static stopwatch sw;
static bool started = false;
if(!started){
sw.start();
started = true;
}
return sw.get_current_seconds();
}
}

1430
src/duality/duality_wrapper.h Executable file

File diff suppressed because it is too large Load diff

75
src/interp/foci2.h Executable file
View file

@ -0,0 +1,75 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
foci2.h
Abstract:
An interface class for foci2.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef FOCI2_H
#define FOCI2_H
#include <vector>
#include <string>
#ifdef WIN32
#define FOCI2_EXPORT __declspec(dllexport)
#else
#define FOCI2_EXPORT __attribute__ ((visibility ("default")))
#endif
class foci2 {
public:
virtual ~foci2(){}
typedef int ast;
typedef int symb;
/** Built-in operators */
enum ops {
And = 0, Or, Not, Iff, Ite, Equal, Plus, Times, Floor, Leq, Div, Bool, Int, Array, Tsym, Fsym, Forall, Exists, Distinct, LastOp
};
virtual symb mk_func(const std::string &s) = 0;
virtual symb mk_pred(const std::string &s) = 0;
virtual ast mk_op(ops op, const std::vector<ast> args) = 0;
virtual ast mk_op(ops op, ast) = 0;
virtual ast mk_op(ops op, ast, ast) = 0;
virtual ast mk_op(ops op, ast, ast, ast) = 0;
virtual ast mk_int(const std::string &) = 0;
virtual ast mk_rat(const std::string &) = 0;
virtual ast mk_true() = 0;
virtual ast mk_false() = 0;
virtual ast mk_app(symb,const std::vector<ast> args) = 0;
virtual bool get_func(ast, symb &) = 0;
virtual bool get_pred(ast, symb &) = 0;
virtual bool get_op(ast, ops &) = 0;
virtual bool get_true(ast id) = 0;
virtual bool get_false(ast id) = 0;
virtual bool get_int(ast id, std::string &res) = 0;
virtual bool get_rat(ast id, std::string &res) = 0;
virtual const std::string &get_symb(symb) = 0;
virtual int get_num_args(ast) = 0;
virtual ast get_arg(ast, int) = 0;
virtual void show_ast(ast) = 0;
virtual bool interpolate(const std::vector<ast> &frames, std::vector<ast> &itps, std::vector<int> parents) = 0;
FOCI2_EXPORT static foci2 *create(const std::string &);
};
#endif

View file

@ -0,0 +1,25 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
foci2.cpp
Abstract:
Fake foci2, to be replaced
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include "foci2.h"
FOCI2_EXPORT foci2 *foci2::create(const std::string &){
return 0;
}

75
src/interp/foci2stub/foci2.h Executable file
View file

@ -0,0 +1,75 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
foci2.h
Abstract:
An interface class for foci2.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef FOCI2_H
#define FOCI2_H
#include <vector>
#include <string>
#ifdef WIN32
#define FOCI2_EXPORT __declspec(dllexport)
#else
#define FOCI2_EXPORT __attribute__ ((visibility ("default")))
#endif
class foci2 {
public:
virtual ~foci2(){}
typedef int ast;
typedef int symb;
/** Built-in operators */
enum ops {
And = 0, Or, Not, Iff, Ite, Equal, Plus, Times, Floor, Leq, Div, Bool, Int, Array, Tsym, Fsym, Forall, Exists, Distinct, LastOp
};
virtual symb mk_func(const std::string &s) = 0;
virtual symb mk_pred(const std::string &s) = 0;
virtual ast mk_op(ops op, const std::vector<ast> args) = 0;
virtual ast mk_op(ops op, ast) = 0;
virtual ast mk_op(ops op, ast, ast) = 0;
virtual ast mk_op(ops op, ast, ast, ast) = 0;
virtual ast mk_int(const std::string &) = 0;
virtual ast mk_rat(const std::string &) = 0;
virtual ast mk_true() = 0;
virtual ast mk_false() = 0;
virtual ast mk_app(symb,const std::vector<ast> args) = 0;
virtual bool get_func(ast, symb &) = 0;
virtual bool get_pred(ast, symb &) = 0;
virtual bool get_op(ast, ops &) = 0;
virtual bool get_true(ast id) = 0;
virtual bool get_false(ast id) = 0;
virtual bool get_int(ast id, std::string &res) = 0;
virtual bool get_rat(ast id, std::string &res) = 0;
virtual const std::string &get_symb(symb) = 0;
virtual int get_num_args(ast) = 0;
virtual ast get_arg(ast, int) = 0;
virtual void show_ast(ast) = 0;
virtual bool interpolate(const std::vector<ast> &frames, std::vector<ast> &itps, std::vector<int> parents) = 0;
FOCI2_EXPORT static foci2 *create(const std::string &);
};
#endif

352
src/interp/iz3base.cpp Executable file
View file

@ -0,0 +1,352 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3base.cpp
Abstract:
Base class for interpolators. Includes an AST manager and a scoping
object as bases.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include "iz3base.h"
#include <stdio.h>
#include <fstream>
#include <iostream>
#include <ostream>
#include "solver.h"
#include "../smt/smt_solver.h"
#ifndef WIN32
using namespace stl_ext;
#endif
iz3base::range &iz3base::ast_range(ast t){
return ast_ranges_hash[t].rng;
}
iz3base::range &iz3base::sym_range(symb d){
return sym_range_hash[d];
}
void iz3base::add_frame_range(int frame, ast t){
range &rng = ast_range(t);
if(!in_range(frame,rng)){
range_add(frame,rng);
for(int i = 0, n = num_args(t); i < n; ++i)
add_frame_range(frame,arg(t,i));
if(op(t) == Uninterpreted)
range_add(frame,sym_range(sym(t)));
}
}
#if 1
iz3base::range &iz3base::ast_scope(ast t){
ranges &rngs = ast_ranges_hash[t];
range &rng = rngs.scp;
if(!rngs.scope_computed){ // not computed yet
rng = range_full();
for(int i = 0, n = num_args(t); i < n; ++i)
rng = range_glb(rng,ast_scope(arg(t,i)));
if(op(t) == Uninterpreted)
if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global
rng = range_glb(rng,sym_range(sym(t)));
rngs.scope_computed = true;
}
return rng;
}
#else
iz3base::range &iz3base::ast_scope(ast t){
ranges &rngs = ast_ranges_hash[t];
if(rngs.scope_computed) return rngs.scp;
range rng = range_full();
for(int i = 0, n = num_args(t); i < n; ++i)
rng = range_glb(rng,ast_scope(arg(t,i)));
if(op(t) == Uninterpreted)
if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global
rng = range_glb(rng,sym_range(sym(t)));
rngs = ast_ranges_hash[t];
rngs.scope_computed = true;
rngs.scp = rng;
return rngs.scp;
}
#endif
void iz3base::print(const std::string &filename){
ast t = make(And,cnsts);
std::ofstream f(filename.c_str());
print_sat_problem(f,t);
f.close();
}
void iz3base::gather_conjuncts_rec(ast n, std::vector<ast> &conjuncts, stl_ext::hash_set<ast> &memo){
if(memo.find(n) == memo.end()){
memo.insert(n);
if(op(n) == And){
int nargs = num_args(n);
for(int i = 0; i < nargs; i++)
gather_conjuncts_rec(arg(n,i),conjuncts,memo);
}
else
conjuncts.push_back(n);
}
}
void iz3base::gather_conjuncts(ast n, std::vector<ast> &conjuncts){
hash_set<ast> memo;
gather_conjuncts_rec(n,conjuncts,memo);
}
bool iz3base::is_literal(ast n){
if(is_not(n))n = arg(n,0);
if(is_true(n) || is_false(n)) return false;
if(op(n) == And) return false;
return true;
}
iz3base::ast iz3base::simplify_and(std::vector<ast> &conjuncts){
hash_set<ast> memo;
for(unsigned i = 0; i < conjuncts.size(); i++){
if(is_false(conjuncts[i]))
return conjuncts[i];
if(is_true(conjuncts[i]) || memo.find(conjuncts[i]) != memo.end()){
std::swap(conjuncts[i],conjuncts.back());
conjuncts.pop_back();
}
else if(memo.find(mk_not(conjuncts[i])) != memo.end())
return mk_false(); // contradiction!
else
memo.insert(conjuncts[i]);
}
if(conjuncts.empty())return mk_true();
return make(And,conjuncts);
}
iz3base::ast iz3base::simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map<ast,ast> &memo, int depth){
if(is_not(n))return mk_not(simplify_with_lit_rec(mk_not(n),lit,memo,depth));
if(n == lit) return mk_true();
ast not_lit = mk_not(lit);
if(n == not_lit) return mk_false();
if(op(n) != And || depth <= 0) return n;
std::pair<ast,ast> foo(n,ast());
std::pair<hash_map<ast,ast>::iterator,bool> bar = memo.insert(foo);
ast &res = bar.first->second;
if(!bar.second) return res;
int nargs = num_args(n);
std::vector<ast> args(nargs);
for(int i = 0; i < nargs; i++)
args[i] = simplify_with_lit_rec(arg(n,i),lit,memo,depth-1);
res = simplify_and(args);
return res;
}
iz3base::ast iz3base::simplify_with_lit(ast n, ast lit){
hash_map<ast,ast> memo;
return simplify_with_lit_rec(n,lit,memo,1);
}
iz3base::ast iz3base::simplify(ast n){
if(is_not(n)) return mk_not(simplify(mk_not(n)));
std::pair<ast,ast> memo_obj(n,ast());
std::pair<hash_map<ast,ast>::iterator,bool> memo = simplify_memo.insert(memo_obj);
ast &res = memo.first->second;
if(!memo.second) return res;
switch(op(n)){
case And: {
std::vector<ast> conjuncts;
gather_conjuncts(n,conjuncts);
for(unsigned i = 0; i < conjuncts.size(); i++)
conjuncts[i] = simplify(conjuncts[i]);
#if 0
for(unsigned i = 0; i < conjuncts.size(); i++)
if(is_literal(conjuncts[i]))
for(unsigned j = 0; j < conjuncts.size(); j++)
if(j != i)
conjuncts[j] = simplify_with_lit(conjuncts[j],conjuncts[i]);
#endif
res = simplify_and(conjuncts);
}
break;
case Equal: {
ast x = arg(n,0);
ast y = arg(n,1);
if(ast_id(x) > ast_id(y))
std::swap(x,y);
res = make(Equal,x,y);
break;
}
default:
res = n;
}
return res;
}
void iz3base::initialize(const std::vector<ast> &_parts, const std::vector<int> &_parents, const std::vector<ast> &_theory){
cnsts = _parts;
theory = _theory;
for(unsigned i = 0; i < cnsts.size(); i++)
add_frame_range(i, cnsts[i]);
for(unsigned i = 0; i < _theory.size(); i++){
add_frame_range(SHRT_MIN, _theory[i]);
add_frame_range(SHRT_MAX, _theory[i]);
}
for(unsigned i = 0; i < cnsts.size(); i++)
frame_map[cnsts[i]] = i;
for(unsigned i = 0; i < theory.size(); i++)
frame_map[theory[i]] = INT_MAX;
}
void iz3base::initialize(const std::vector<std::vector<ast> > &_parts, const std::vector<int> &_parents, const std::vector<ast> &_theory){
cnsts.resize(_parts.size());
theory = _theory;
for(unsigned i = 0; i < _parts.size(); i++)
for(unsigned j = 0; j < _parts[i].size(); j++){
cnsts[i] = make(And,_parts[i]);
add_frame_range(i, _parts[i][j]);
frame_map[_parts[i][j]] = i;
}
for(unsigned i = 0; i < _theory.size(); i++){
add_frame_range(SHRT_MIN, _theory[i]);
add_frame_range(SHRT_MAX, _theory[i]);
frame_map[theory[i]] = INT_MAX;
}
}
void iz3base::check_interp(const std::vector<ast> &itps, std::vector<ast> &theory){
#if 0
Z3_config config = Z3_mk_config();
Z3_context vctx = Z3_mk_context(config);
int frames = cnsts.size();
std::vector<Z3_ast> foocnsts(cnsts);
for(unsigned i = 0; i < frames; i++)
foocnsts[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),cnsts[i]);
Z3_write_interpolation_problem(ctx,frames,&foocnsts[0],0, "temp_lemma.smt", theory.size(), &theory[0]);
int vframes,*vparents;
Z3_ast *vcnsts;
const char *verror;
bool ok = Z3_read_interpolation_problem(vctx,&vframes,&vcnsts,0,"temp_lemma.smt",&verror);
assert(ok);
std::vector<Z3_ast> vvcnsts(vframes);
std::copy(vcnsts,vcnsts+vframes,vvcnsts.begin());
std::vector<Z3_ast> vitps(itps.size());
for(unsigned i = 0; i < itps.size(); i++)
vitps[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),itps[i]);
Z3_write_interpolation_problem(ctx,itps.size(),&vitps[0],0,"temp_interp.smt");
int iframes,*iparents;
Z3_ast *icnsts;
const char *ierror;
ok = Z3_read_interpolation_problem(vctx,&iframes,&icnsts,0,"temp_interp.smt",&ierror);
assert(ok);
const char *error = 0;
bool iok = Z3_check_interpolant(vctx, frames, &vvcnsts[0], parents.size() ? &parents[0] : 0, icnsts, &error);
assert(iok);
#endif
}
bool iz3base::is_sat(const std::vector<ast> &q, ast &_proof){
params_ref p;
p.set_bool("proof", true); // this is currently useless
p.set_bool("model", true);
p.set_bool("unsat_core", true);
scoped_ptr<solver_factory> sf = mk_smt_solver_factory();
::solver *m_solver = (*sf)(m(), p, true, true, true, ::symbol::null);
::solver &s = *m_solver;
for(unsigned i = 0; i < q.size(); i++)
s.assert_expr(to_expr(q[i].raw()));
lbool res = s.check_sat(0,0);
if(res == l_false){
::ast *proof = s.get_proof();
_proof = cook(proof);
}
dealloc(m_solver);
return res != l_false;
}
void iz3base::find_children(const stl_ext::hash_set<ast> &cnsts_set,
const ast &tree,
std::vector<ast> &cnsts,
std::vector<int> &parents,
std::vector<ast> &conjuncts,
std::vector<int> &children,
std::vector<int> &pos_map,
bool merge
){
std::vector<int> my_children;
std::vector<ast> my_conjuncts;
if(op(tree) == Interp){ // if we've hit an interpolation position...
find_children(cnsts_set,arg(tree,0),cnsts,parents,my_conjuncts,my_children,pos_map,merge);
if(my_conjuncts.empty())
my_conjuncts.push_back(mk_true()); // need at least one conjunct
int root = cnsts.size() + my_conjuncts.size() - 1;
for(unsigned i = 0; i < my_conjuncts.size(); i++){
parents.push_back(root);
cnsts.push_back(my_conjuncts[i]);
}
for(unsigned i = 0; i < my_children.size(); i++)
parents[my_children[i]] = root;
children.push_back(root);
pos_map.push_back(root);
}
else {
if(op(tree) == And){
int nargs = num_args(tree);
for(int i = 0; i < nargs; i++)
find_children(cnsts_set,arg(tree,i),cnsts,parents,my_conjuncts,my_children,pos_map,merge);
}
if(cnsts_set.find(tree) != cnsts_set.end()){
if(merge && !my_conjuncts.empty())
my_conjuncts.back() = mk_and(my_conjuncts.back(),tree);
else
my_conjuncts.push_back(tree);
}
for(unsigned i = 0; i < my_children.size(); i++)
children.push_back(my_children[i]);
for(unsigned i = 0; i < my_conjuncts.size(); i++)
conjuncts.push_back(my_conjuncts[i]);
}
}
void iz3base::to_parents_vec_representation(const std::vector<ast> &_cnsts,
const ast &tree,
std::vector<ast> &cnsts,
std::vector<int> &parents,
std::vector<ast> &theory,
std::vector<int> &pos_map,
bool merge
){
std::vector<int> my_children;
std::vector<ast> my_conjuncts;
hash_set<ast> cnsts_set;
for(unsigned i = 0; i < _cnsts.size(); i++)
cnsts_set.insert(_cnsts[i]);
ast _tree = (op(tree) != Interp) ? make(Interp,tree) : tree;
find_children(cnsts_set,_tree,cnsts,parents,my_conjuncts,my_children,pos_map,merge);
if(op(tree) != Interp) pos_map.pop_back();
parents[parents.size()-1] = SHRT_MAX;
// rest of the constraints are the background theory
hash_set<ast> used_set;
for(unsigned i = 0; i < cnsts.size(); i++)
used_set.insert(cnsts[i]);
for(unsigned i = 0; i < _cnsts.size(); i++)
if(used_set.find(_cnsts[i]) == used_set.end())
theory.push_back(_cnsts[i]);
}

195
src/interp/iz3base.h Executable file
View file

@ -0,0 +1,195 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3base.h
Abstract:
Base class for interpolators. Includes an AST manager and a scoping
object as bases.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3BASE_H
#define IZ3BASE_H
#include "iz3mgr.h"
#include "iz3scopes.h"
namespace hash_space {
template <>
class hash<func_decl *> {
public:
size_t operator()(func_decl * const &s) const {
return (size_t) s;
}
};
}
/* Base class for interpolators. Includes an AST manager and a scoping
object as bases. */
class iz3base : public iz3mgr, public scopes {
public:
/** Get the range in which an expression occurs. This is the
smallest subtree containing all occurrences of the
expression. */
range &ast_range(ast);
/** Get the scope of an expression. This is the set of tree nodes in
which all of the expression's symbols are in scope. */
range &ast_scope(ast);
/** Get the range of a symbol. This is the smallest subtree containing
all occurrences of the symbol. */
range &sym_range(symb);
/** Is an expression local (in scope in some frame)? */
bool is_local(ast node){
return !range_is_empty(ast_scope(node));
}
/** Simplify an expression */
ast simplify(ast);
/** Constructor */
iz3base(ast_manager &_m_manager,
const std::vector<ast> &_cnsts,
const std::vector<int> &_parents,
const std::vector<ast> &_theory)
: iz3mgr(_m_manager), scopes(_parents) {
initialize(_cnsts,_parents,_theory);
weak = false;
}
iz3base(const iz3mgr& other,
const std::vector<ast> &_cnsts,
const std::vector<int> &_parents,
const std::vector<ast> &_theory)
: iz3mgr(other), scopes(_parents) {
initialize(_cnsts,_parents,_theory);
weak = false;
}
iz3base(const iz3mgr& other,
const std::vector<std::vector<ast> > &_cnsts,
const std::vector<int> &_parents,
const std::vector<ast> &_theory)
: iz3mgr(other), scopes(_parents) {
initialize(_cnsts,_parents,_theory);
weak = false;
}
iz3base(const iz3mgr& other)
: iz3mgr(other), scopes() {
weak = false;
}
/* Set our options */
void set_option(const std::string &name, const std::string &value){
if(name == "weak" && value == "1") weak = true;
}
/* Are we doing weak interpolants? */
bool weak_mode(){return weak;}
/** Print interpolation problem to an SMTLIB format file */
void print(const std::string &filename);
/** Check correctness of a solutino to this problem. */
void check_interp(const std::vector<ast> &itps, std::vector<ast> &theory);
/** For convenience -- is this formula SAT? */
bool is_sat(const std::vector<ast> &consts, ast &_proof);
/** Interpolator for clauses, to be implemented */
virtual void interpolate_clause(std::vector<ast> &lits, std::vector<ast> &itps){
throw "no interpolator";
}
ast get_proof_check_assump(range &rng){
std::vector<ast> cs(theory);
cs.push_back(cnsts[rng.hi]);
return make(And,cs);
}
int frame_of_assertion(const ast &ass){
stl_ext::hash_map<ast,int>::iterator it = frame_map.find(ass);
if(it == frame_map.end())
throw "unknown assertion";
return it->second;
}
void to_parents_vec_representation(const std::vector<ast> &_cnsts,
const ast &tree,
std::vector<ast> &cnsts,
std::vector<int> &parents,
std::vector<ast> &theory,
std::vector<int> &pos_map,
bool merge = false
);
protected:
std::vector<ast> cnsts;
std::vector<ast> theory;
private:
struct ranges {
range rng;
range scp;
bool scope_computed;
ranges(){scope_computed = false;}
};
stl_ext::hash_map<symb,range> sym_range_hash;
stl_ext::hash_map<ast,ranges> ast_ranges_hash;
stl_ext::hash_map<ast,ast> simplify_memo;
stl_ext::hash_map<ast,int> frame_map; // map assertions to frames
int frames; // number of frames
protected:
void add_frame_range(int frame, ast t);
private:
void initialize(const std::vector<ast> &_parts, const std::vector<int> &_parents, const std::vector<ast> &_theory);
void initialize(const std::vector<std::vector<ast> > &_parts, const std::vector<int> &_parents, const std::vector<ast> &_theory);
bool is_literal(ast n);
void gather_conjuncts_rec(ast n, std::vector<ast> &conjuncts, stl_ext::hash_set<ast> &memo);
void gather_conjuncts(ast n, std::vector<ast> &conjuncts);
ast simplify_and(std::vector<ast> &conjuncts);
ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map<ast,ast> &memo, int depth);
ast simplify_with_lit(ast n, ast lit);
void find_children(const stl_ext::hash_set<ast> &cnsts_set,
const ast &tree,
std::vector<ast> &cnsts,
std::vector<int> &parents,
std::vector<ast> &conjuncts,
std::vector<int> &children,
std::vector<int> &pos_map,
bool merge
);
bool weak;
};
#endif

222
src/interp/iz3checker.cpp Executable file
View file

@ -0,0 +1,222 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3checker.cpp
Abstract:
check correctness of interpolant
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include "iz3base.h"
#include "iz3checker.h"
#include <algorithm>
#include <stdio.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <set>
#include <iterator>
#ifndef WIN32
using namespace stl_ext;
#endif
struct iz3checker : iz3base {
/* HACK: for tree interpolants, we assume that uninterpreted functions
are global. This is because in the current state of the tree interpolation
code, symbols that appear in sibling sub-trees have to be global, and
we have no way to eliminate such function symbols. When tree interpoaltion is
fixed, we can tree function symbols the same as constant symbols. */
bool is_tree;
void support(const ast &t, std::set<std::string> &res, hash_set<ast> &memo){
if(memo.find(t) != memo.end()) return;
memo.insert(t);
int nargs = num_args(t);
for(int i = 0; i < nargs; i++)
support(arg(t,i),res,memo);
switch(op(t)){
case Uninterpreted:
if(nargs == 0 || !is_tree) {
std::string name = string_of_symbol(sym(t));
res.insert(name);
}
break;
case Forall:
case Exists:
support(get_quantifier_body(t),res,memo);
break;
default:;
}
}
bool check(solver *s, std::ostream &err,
const std::vector<ast> &cnsts,
const std::vector<int> &parents,
const std::vector<ast> &itp,
const std::vector<ast> &theory){
is_tree = !parents.empty();
int num = cnsts.size();
std::vector<std::vector<int> > children(num);
for(int i = 0; i < num-1; i++){
if(parents.size())
children[parents[i]].push_back(i);
else
children[i+1].push_back(i);
}
for(int i = 0; i < num; i++){
s->push();
for(unsigned j = 0; j < theory.size(); j++)
s->assert_expr(to_expr(theory[j].raw()));
s->assert_expr(to_expr(cnsts[i].raw()));
std::vector<int> &cs = children[i];
for(unsigned j = 0; j < cs.size(); j++)
s->assert_expr(to_expr(itp[cs[j]].raw()));
if(i != num-1)
s->assert_expr(to_expr(mk_not(itp[i]).raw()));
lbool result = s->check_sat(0,0);
if(result != l_false){
err << "interpolant " << i << " is incorrect";
s->pop(1);
for(unsigned j = 0; j < theory.size(); j++)
s->assert_expr(to_expr(theory[j].raw()));
for(unsigned j = 0; j < cnsts.size(); j++)
if(in_range(j,range_downward(i)))
s->assert_expr(to_expr(cnsts[j].raw()));
if(i != num-1)
s->assert_expr(to_expr(mk_not(itp[i]).raw()));
lbool result = s->check_sat(0,0);
if(result != l_false)
err << "interpolant " << i << " is not implied by its downeard closurn";
return false;
}
s->pop(1);
}
std::vector<std::set<std::string> > supports(num);
for(int i = 0; i < num; i++){
hash_set<ast> memo;
support(cnsts[i],supports[i],memo);
}
for(int i = 0; i < num-1; i++){
std::vector<bool> Bside(num);
for(int j = num-1; j >= 0; j--)
Bside[j] = j != i;
for(int j = num-1; j >= 0; j--)
if(!Bside[j]){
std::vector<int> &cs = children[i];
for(unsigned k = 0; k < cs.size(); k++)
Bside[cs[k]] = false;
}
std::set<std::string> Asup, Bsup,common,Isup,bad;
for(int j = num-1; j >= 0; j--){
std::set<std::string> &side = Bside[j] ? Bsup : Asup;
side.insert(supports[j].begin(),supports[j].end());
}
std::set_intersection(Asup.begin(),Asup.end(),Bsup.begin(),Bsup.end(),std::inserter(common,common.begin()));
{
hash_set<ast> tmemo;
for(unsigned j = 0; j < theory.size(); j++)
support(theory[j],common,tmemo); // all theory symbols allowed in interps
}
hash_set<ast> memo;
support(itp[i],Isup,memo);
std::set_difference(Isup.begin(),Isup.end(),common.begin(),common.end(),std::inserter(bad,bad.begin()));
if(!bad.empty()){
err << "bad symbols in interpolant " << i << ":";
std::copy(bad.begin(),bad.end(),std::ostream_iterator<std::string>(err,","));
return false;
}
}
return true;
}
bool check(solver *s, std::ostream &err,
const std::vector<ast> &_cnsts,
const ast &tree,
const std::vector<ast> &itp){
std::vector<int> pos_map;
// convert to the parents vector representation
to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map);
//use the parents vector representation to compute interpolant
return check(s,err,cnsts,parents,itp,theory);
}
iz3checker(ast_manager &_m)
: iz3base(_m) {
}
iz3checker(iz3mgr &_m)
: iz3base(_m) {
}
};
template <class T>
std::vector<T> to_std_vector(const ::vector<T> &v){
std::vector<T> _v(v.size());
for(unsigned i = 0; i < v.size(); i++)
_v[i] = v[i];
return _v;
}
bool iz3check(ast_manager &_m_manager,
solver *s,
std::ostream &err,
const ptr_vector<ast> &cnsts,
const ::vector<int> &parents,
const ptr_vector<ast> &interps,
const ptr_vector<ast> &theory)
{
iz3checker chk(_m_manager);
return chk.check(s,err,chk.cook(cnsts),to_std_vector(parents),chk.cook(interps),chk.cook(theory));
}
bool iz3check(iz3mgr &mgr,
solver *s,
std::ostream &err,
const std::vector<iz3mgr::ast> &cnsts,
const std::vector<int> &parents,
const std::vector<iz3mgr::ast> &interps,
const std::vector<iz3mgr::ast> &theory)
{
iz3checker chk(mgr);
return chk.check(s,err,cnsts,parents,interps,theory);
}
bool iz3check(ast_manager &_m_manager,
solver *s,
std::ostream &err,
const ptr_vector<ast> &_cnsts,
ast *tree,
const ptr_vector<ast> &interps)
{
iz3checker chk(_m_manager);
return chk.check(s,err,chk.cook(_cnsts),chk.cook(tree),chk.cook(interps));
}

49
src/interp/iz3checker.h Normal file
View file

@ -0,0 +1,49 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3checker.h
Abstract:
check correctness of an interpolant
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3_CHECKER_H
#define IZ3_CHECKER_H
#include "iz3mgr.h"
#include "solver.h"
bool iz3check(ast_manager &_m_manager,
solver *s,
std::ostream &err,
const ptr_vector<ast> &cnsts,
const ::vector<int> &parents,
const ptr_vector<ast> &interps,
const ptr_vector<ast> &theory);
bool iz3check(ast_manager &_m_manager,
solver *s,
std::ostream &err,
const ptr_vector<ast> &cnsts,
ast *tree,
const ptr_vector<ast> &interps);
bool iz3check(iz3mgr &mgr,
solver *s,
std::ostream &err,
const std::vector<iz3mgr::ast> &cnsts,
const std::vector<int> &parents,
const std::vector<iz3mgr::ast> &interps,
const ptr_vector<iz3mgr::ast> &theory);
#endif

359
src/interp/iz3foci.cpp Executable file
View file

@ -0,0 +1,359 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3foci.cpp
Abstract:
Implements a secondary solver using foci2.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include <sstream>
#include <iostream>
#include <assert.h>
#include "iz3hash.h"
#include "foci2.h"
#include "iz3foci.h"
#ifndef WIN32
using namespace stl_ext;
#endif
class iz3foci_impl : public iz3secondary {
int frames;
int *parents;
foci2 *foci;
foci2::symb select_op;
foci2::symb store_op;
foci2::symb mod_op;
public:
iz3foci_impl(iz3mgr *mgr, int _frames, int *_parents) : iz3secondary(*mgr) {
frames = _frames;
parents = _parents;
foci = 0;
}
typedef hash_map<ast,foci2::ast> AstToNode;
AstToNode ast_to_node; // maps Z3 ast's to foci expressions
typedef hash_map<foci2::ast,ast> NodeToAst;
NodeToAst node_to_ast; // maps Z3 ast's to foci expressions
// We only use this for FuncDeclToSymbol, which has no range destructor
struct symb_hash {
size_t operator()(const symb &s) const {
return (size_t) s;
}
};
typedef hash_map<symb,foci2::symb> FuncDeclToSymbol;
FuncDeclToSymbol func_decl_to_symbol; // maps Z3 func decls to symbols
typedef hash_map<foci2::symb,symb> SymbolToFuncDecl;
SymbolToFuncDecl symbol_to_func_decl; // maps symbols to Z3 func decls
int from_symb(symb func){
std::string name = string_of_symbol(func);
bool is_bool = is_bool_type(get_range_type(func));
foci2::symb f;
if(is_bool)
f = foci->mk_pred(name);
else
f = foci->mk_func(name);
symbol_to_func_decl[f] = func;
func_decl_to_symbol[func] = f;
return f;
}
// create a symbol corresponding to a DeBruijn index of a particular type
// the type has to be encoded into the name because the same index can
// occur with different types
foci2::symb make_deBruijn_symbol(int index, type ty){
std::ostringstream s;
// s << "#" << index << "#" << type;
return foci->mk_func(s.str());
}
int from_Z3_ast(ast t){
std::pair<ast,foci2::ast> foo(t,0);
std::pair<AstToNode::iterator, bool> bar = ast_to_node.insert(foo);
int &res = bar.first->second;
if(!bar.second) return res;
int nargs = num_args(t);
std::vector<foci2::ast> args(nargs);
for(int i = 0; i < nargs; i++)
args[i] = from_Z3_ast(arg(t,i));
switch(op(t)){
case True:
res = foci->mk_true(); break;
case False:
res = foci->mk_false(); break;
case And:
res = foci->mk_op(foci2::And,args); break;
case Or:
res = foci->mk_op(foci2::Or,args); break;
case Not:
res = foci->mk_op(foci2::Not,args[0]); break;
case Iff:
res = foci->mk_op(foci2::Iff,args); break;
case OP_OEQ: // bit of a mystery, this one...
if(args[0] == args[1]) res = foci->mk_true();
else res = foci->mk_op(foci2::Iff,args);
break;
case Ite:
if(is_bool_type(get_type(t)))
res = foci->mk_op(foci2::And,foci->mk_op(foci2::Or,foci->mk_op(foci2::Not,args[0]),args[1]),foci->mk_op(foci2::Or,args[0],args[2]));
else
res = foci->mk_op(foci2::Ite,args);
break;
case Equal:
res = foci->mk_op(foci2::Equal,args); break;
case Implies:
args[0] = foci->mk_op(foci2::Not,args[0]); res = foci->mk_op(foci2::Or,args); break;
case Xor:
res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Iff,args)); break;
case Leq:
res = foci->mk_op(foci2::Leq,args); break;
case Geq:
std::swap(args[0],args[1]); res = foci->mk_op(foci2::Leq,args); break;
case Gt:
res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Leq,args)); break;
case Lt:
std::swap(args[0],args[1]); res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Leq,args)); break;
case Plus:
res = foci->mk_op(foci2::Plus,args); break;
case Sub:
args[1] = foci->mk_op(foci2::Times,foci->mk_int("-1"),args[1]); res = foci->mk_op(foci2::Plus,args); break;
case Uminus:
res = foci->mk_op(foci2::Times,foci->mk_int("-1"),args[0]); break;
case Times:
res = foci->mk_op(foci2::Times,args); break;
case Idiv:
res = foci->mk_op(foci2::Div,args); break;
case Mod:
res = foci->mk_app(mod_op,args); break;
case Select:
res = foci->mk_app(select_op,args); break;
case Store:
res = foci->mk_app(store_op,args); break;
case Distinct:
res = foci->mk_op(foci2::Distinct,args); break;
case Uninterpreted: {
symb func = sym(t);
FuncDeclToSymbol::iterator it = func_decl_to_symbol.find(func);
foci2::symb f = (it == func_decl_to_symbol.end()) ? from_symb(func) : it->second;
if(foci->get_symb(f).substr(0,3) == "lbl" && args.size()==1) // HACK to handle Z3 labels
res = args[0];
else if(foci->get_symb(f).substr(0,3) == "lbl" && args.size()==0) // HACK to handle Z3 labels
res = foci->mk_true();
else res = foci->mk_app(f,args);
break;
}
case Numeral: {
std::string s = string_of_numeral(t);
res = foci->mk_int(s);
break;
}
case Forall:
case Exists: {
bool is_forall = op(t) == Forall;
foci2::ops qop = is_forall ? foci2::Forall : foci2::Exists;
int bvs = get_quantifier_num_bound(t);
std::vector<int> foci_bvs(bvs);
for(int i = 0; i < bvs; i++){
std::string name = get_quantifier_bound_name(t,i);
//Z3_string name = Z3_get_symbol_string(ctx,sym);
// type ty = get_quantifier_bound_type(t,i);
foci2::symb f = foci->mk_func(name);
foci2::ast v = foci->mk_app(f,std::vector<foci2::ast>());
foci_bvs[i] = v;
}
foci2::ast body = from_Z3_ast(get_quantifier_body(t));
foci_bvs.push_back(body);
res = foci->mk_op(qop,foci_bvs);
node_to_ast[res] = t; // desperate
break;
}
case Variable: { // a deBruijn index
int index = get_variable_index_value(t);
type ty = get_type(t);
foci2::symb symbol = make_deBruijn_symbol(index,ty);
res = foci->mk_app(symbol,std::vector<foci2::ast>());
break;
}
default:
{
std::cerr << "iZ3: unsupported Z3 operator in expression\n ";
print_expr(std::cerr,t);
std::cerr << "\n";
assert(0 && "iZ3: unsupported Z3 operator");
}
}
return res;
}
// convert an expr to Z3 ast
ast to_Z3_ast(foci2::ast i){
std::pair<foci2::ast,ast> foo(i,ast());
std::pair<NodeToAst::iterator,bool> bar = node_to_ast.insert(foo);
if(!bar.second) return bar.first->second;
ast &res = bar.first->second;
if(i < 0){
res = mk_not(to_Z3_ast(-i));
return res;
}
// get the arguments
unsigned n = foci->get_num_args(i);
std::vector<ast> args(n);
for(unsigned j = 0; j < n; j++)
args[j] = to_Z3_ast(foci->get_arg(i,j));
// handle operators
foci2::ops o;
foci2::symb f;
std::string nval;
if(foci->get_true(i))
res = mk_true();
else if(foci->get_false(i))
res = mk_false();
else if(foci->get_op(i,o)){
switch(o){
case foci2::And:
res = make(And,args); break;
case foci2::Or:
res = make(Or,args); break;
case foci2::Not:
res = mk_not(args[0]); break;
case foci2::Iff:
res = make(Iff,args[0],args[1]); break;
case foci2::Ite:
res = make(Ite,args[0],args[1],args[2]); break;
case foci2::Equal:
res = make(Equal,args[0],args[1]); break;
case foci2::Plus:
res = make(Plus,args); break;
case foci2::Times:
res = make(Times,args); break;
case foci2::Div:
res = make(Idiv,args[0],args[1]); break;
case foci2::Leq:
res = make(Leq,args[0],args[1]); break;
case foci2::Distinct:
res = make(Distinct,args);
break;
case foci2::Tsym:
res = mk_true();
break;
case foci2::Fsym:
res = mk_false();
break;
case foci2::Forall:
case foci2::Exists:
{
int nargs = n;
std::vector<ast> bounds(nargs-1);
for(int i = 0; i < nargs-1; i++)
bounds[i] = args[i];
opr oz = o == foci2::Forall ? Forall : Exists;
res = make_quant(oz,bounds,args[nargs-1]);
}
break;
default:
assert("unknown built-in op");
}
}
else if(foci->get_int(i,nval)){
res = make_int(nval);
}
else if(foci->get_func(i,f)){
if(f == select_op){
assert(n == 2);
res = make(Select,args[0],args[1]);
}
else if(f == store_op){
assert(n == 3);
res = make(Store,args[0],args[1],args[2]);
}
else if(f == mod_op){
assert(n == 2);
res = make(Mod,args[0],args[1]);
}
else {
std::pair<int,symb> foo(f,(symb)0);
std::pair<SymbolToFuncDecl::iterator,bool> bar = symbol_to_func_decl.insert(foo);
symb &func_decl = bar.first->second;
if(bar.second){
std::cout << "unknown function symbol:\n";
foci->show_ast(i);
assert(0);
}
res = make(func_decl,args);
}
}
else {
std::cerr << "iZ3: unknown FOCI expression kind\n";
assert(0 && "iZ3: unknown FOCI expression kind");
}
return res;
}
int interpolate(const std::vector<ast> &cnsts, std::vector<ast> &itps){
assert((int)cnsts.size() == frames);
std::string lia("lia");
#ifdef _FOCI2
foci = foci2::create(lia);
#else
foci = 0;
#endif
if(!foci){
std::cerr << "iZ3: cannot find foci lia solver.\n";
assert(0);
}
select_op = foci->mk_func("select");
store_op = foci->mk_func("store");
mod_op = foci->mk_func("mod");
std::vector<foci2::ast> foci_cnsts(frames), foci_itps(frames-1), foci_parents;
if(parents)
foci_parents.resize(frames);
for(int i = 0; i < frames; i++){
foci_cnsts[i] = from_Z3_ast(cnsts[i]);
if(parents)
foci_parents[i] = parents[i];
}
int res = foci->interpolate(foci_cnsts, foci_itps, foci_parents);
if(res == 0){
assert((int)foci_itps.size() == frames-1);
itps.resize(frames-1);
for(int i = 0; i < frames-1; i++){
// foci->show_ast(foci_itps[i]);
itps[i] = to_Z3_ast(foci_itps[i]);
}
}
ast_to_node.clear();
node_to_ast.clear();
func_decl_to_symbol.clear();
symbol_to_func_decl.clear();
delete foci;
return res;
}
};
iz3secondary *iz3foci::create(iz3mgr *mgr, int num, int *parents){
return new iz3foci_impl(mgr,num,parents);
}

32
src/interp/iz3foci.h Executable file
View file

@ -0,0 +1,32 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3foci.h
Abstract:
Implements a secondary solver using foci2.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3FOCI_H
#define IZ3FOCI_H
#include "iz3secondary.h"
/** Secondary prover based on Cadence FOCI. */
class iz3foci {
public:
static iz3secondary *create(iz3mgr *mgr, int num, int *parents);
};
#endif

171
src/interp/iz3hash.h Executable file
View file

@ -0,0 +1,171 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3hash.h
Abstract:
Wrapper for stl hash tables
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
// pull in the headers for has_map and hash_set
// these live in non-standard places
#ifndef IZ3_HASH_H
#define IZ3_HASH_H
//#define USE_UNORDERED_MAP
#ifdef USE_UNORDERED_MAP
#define stl_ext std
#define hash_space std
#include <unordered_map>
#include <unordered_set>
#define hash_map unordered_map
#define hash_set unordered_set
#else
#if __GNUC__ >= 3
#undef __DEPRECATED
#define stl_ext __gnu_cxx
#define hash_space stl_ext
#include <ext/hash_map>
#include <ext/hash_set>
#else
#ifdef WIN32
#define stl_ext stdext
#define hash_space std
#include <hash_map>
#include <hash_set>
#else
#define stl_ext std
#define hash_space std
#include <hash_map>
#include <hash_set>
#endif
#endif
#endif
#include <string>
// stupid STL doesn't include hash function for class string
#ifndef WIN32
namespace stl_ext {
template <>
class hash<std::string> {
stl_ext::hash<char *> H;
public:
size_t operator()(const std::string &s) const {
return H(s.c_str());
}
};
}
#endif
namespace hash_space {
template <>
class hash<std::pair<int,int> > {
public:
size_t operator()(const std::pair<int,int> &p) const {
return p.first + p.second;
}
};
}
#ifdef WIN32
template <> inline
size_t stdext::hash_value<std::pair<int,int> >(const std::pair<int,int>& p)
{ // hash _Keyval to size_t value one-to-one
return p.first + p.second;
}
#endif
namespace hash_space {
template <class T>
class hash<std::pair<T *, T *> > {
public:
size_t operator()(const std::pair<T *,T *> &p) const {
return (size_t)p.first + (size_t)p.second;
}
};
}
#if 0
template <class T> inline
size_t stdext::hash_value<std::pair<T *, T *> >(const std::pair<T *, T *>& p)
{ // hash _Keyval to size_t value one-to-one
return (size_t)p.first + (size_t)p.second;
}
#endif
#ifdef WIN32
namespace std {
template <>
class less<std::pair<int,int> > {
public:
bool operator()(const pair<int,int> &x, const pair<int,int> &y) const {
return x.first < y.first || x.first == y.first && x.second < y.second;
}
};
}
namespace std {
template <class T>
class less<std::pair<T *,T *> > {
public:
bool operator()(const pair<T *,T *> &x, const pair<T *,T *> &y) const {
return (size_t)x.first < (size_t)y.first || (size_t)x.first == (size_t)y.first && (size_t)x.second < (size_t)y.second;
}
};
}
#endif
#ifndef WIN32
#if 0
namespace stl_ext {
template <class T>
class hash<T *> {
public:
size_t operator()(const T *p) const {
return (size_t) p;
}
};
}
#endif
#endif
#ifdef WIN32
template <class K, class T>
class hash_map : public stl_ext::hash_map<K,T,stl_ext::hash_compare<K,std::less<K> > > {};
template <class K>
class hash_set : public stl_ext::hash_set<K,stl_ext::hash_compare<K,std::less<K> > > {};
#endif
#endif

504
src/interp/iz3interp.cpp Executable file
View file

@ -0,0 +1,504 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3interp.cpp
Abstract:
Interpolation based on proof translation.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
/* Copyright 2011 Microsoft Research. */
#include <assert.h>
#include <algorithm>
#include <stdio.h>
#include <fstream>
#include <sstream>
#include <set>
#include <iostream>
#include "iz3profiling.h"
#include "iz3translate.h"
#include "iz3foci.h"
#include "iz3proof.h"
#include "iz3hash.h"
#include "iz3interp.h"
#ifndef WIN32
using namespace stl_ext;
#endif
#if 1
struct frame_reducer : public iz3mgr {
int frames;
hash_map<ast,int> frame_map;
std::vector<int> assertions_map;
std::vector<int> orig_parents_copy;
std::vector<bool> used_frames;
frame_reducer(const iz3mgr &other)
: iz3mgr(other) {}
void get_proof_assumptions_rec(z3pf proof, hash_set<ast> &memo, std::vector<bool> &used_frames){
if(memo.count(proof))return;
memo.insert(proof);
pfrule dk = pr(proof);
if(dk == PR_ASSERTED){
ast con = conc(proof);
if(frame_map.find(con) != frame_map.end()){ // false for theory facts
int frame = frame_map[con];
used_frames[frame] = true;
}
}
else {
unsigned nprems = num_prems(proof);
for(unsigned i = 0; i < nprems; i++){
z3pf arg = prem(proof,i);
get_proof_assumptions_rec(arg,memo,used_frames);
}
}
}
void get_frames(const std::vector<std::vector<ast> >&z3_preds,
const std::vector<int> &orig_parents,
std::vector<std::vector<ast> >&assertions,
std::vector<int> &parents,
z3pf proof){
frames = z3_preds.size();
orig_parents_copy = orig_parents;
for(unsigned i = 0; i < z3_preds.size(); i++)
for(unsigned j = 0; j < z3_preds[i].size(); j++)
frame_map[z3_preds[i][j]] = i;
used_frames.resize(frames);
hash_set<ast> memo;
get_proof_assumptions_rec(proof,memo,used_frames);
std::vector<int> assertions_back_map(frames);
for(unsigned i = 0; i < z3_preds.size(); i++)
if(used_frames[i] || i == z3_preds.size() - 1){
assertions.push_back(z3_preds[i]);
assertions_map.push_back(i);
assertions_back_map[i] = assertions.size() - 1;
}
if(orig_parents.size()){
parents.resize(assertions.size());
for(unsigned i = 0; i < assertions.size(); i++){
int p = orig_parents[assertions_map[i]];
while(p != SHRT_MAX && !used_frames[p])
p = orig_parents[p];
parents[i] = p == SHRT_MAX ? p : assertions_back_map[p];
}
}
// std::cout << "used frames = " << frames << "\n";
}
void fix_interpolants(std::vector<ast> &interpolants){
std::vector<ast> unfixed = interpolants;
interpolants.resize(frames - 1);
for(int i = 0; i < frames - 1; i++)
interpolants[i] = mk_true();
for(unsigned i = 0; i < unfixed.size(); i++)
interpolants[assertions_map[i]] = unfixed[i];
for(int i = 0; i < frames-2; i++){
int p = orig_parents_copy.size() == 0 ? i+1 : orig_parents_copy[i];
if(p < frames - 1 && !used_frames[p])
interpolants[p] = interpolants[i];
}
}
};
#else
struct frame_reducer {
frame_reducer(context _ctx){
}
void get_frames(const std::vector<ast> &z3_preds,
const std::vector<int> &orig_parents,
std::vector<ast> &assertions,
std::vector<int> &parents,
ast proof){
assertions = z3_preds;
parents = orig_parents;
}
void fix_interpolants(std::vector<ast> &interpolants){
}
};
#endif
#if 0
static lbool test_secondary(context ctx,
int num,
ast *cnsts,
ast *interps,
int *parents = 0
){
iz3secondary *sp = iz3foci::create(ctx,num,parents);
std::vector<ast> frames(num), interpolants(num-1);
std::copy(cnsts,cnsts+num,frames.begin());
int res = sp->interpolate(frames,interpolants);
if(res == 0)
std::copy(interpolants.begin(),interpolants.end(),interps);
return res ? L_TRUE : L_FALSE;
}
#endif
template<class T>
struct killme {
T *p;
killme(){p = 0;}
void set(T *_p) {p = _p;}
~killme(){
if(p)
delete p;
}
};
class iz3interp : public iz3base {
public:
killme<iz3secondary> sp_killer;
killme<iz3translation> tr_killer;
bool is_linear(std::vector<int> &parents){
for(int i = 0; i < ((int)parents.size())-1; i++)
if(parents[i] != i+1)
return false;
return true;
}
void test_secondary(const std::vector<ast> &cnsts,
const std::vector<int> &parents,
std::vector<ast> &interps
){
int num = cnsts.size();
iz3secondary *sp = iz3foci::create(this,num,(int *)(parents.empty()?0:&parents[0]));
int res = sp->interpolate(cnsts, interps);
if(res != 0)
throw "secondary failed";
}
void proof_to_interpolant(z3pf proof,
const std::vector<std::vector<ast> > &cnsts,
const std::vector<int> &parents,
std::vector<ast> &interps,
const std::vector<ast> &theory,
interpolation_options_struct *options = 0
){
#if 0
test_secondary(cnsts,parents,interps);
return;
#endif
profiling::timer_start("Interpolation prep");
// get rid of frames not used in proof
std::vector<std::vector<ast> > cnsts_vec;
std::vector<int> parents_vec;
frame_reducer fr(*this);
fr.get_frames(cnsts,parents,cnsts_vec,parents_vec,proof);
int num = cnsts_vec.size();
std::vector<ast> interps_vec(num-1);
// if this is really a sequence problem, we can make it easier
if(is_linear(parents_vec))
parents_vec.clear();
// create a secondary prover
iz3secondary *sp = iz3foci::create(this,num,parents_vec.empty()?0:&parents_vec[0]);
sp_killer.set(sp); // kill this on exit
#define BINARY_INTERPOLATION
#ifndef BINARY_INTERPOLATION
// create a translator
iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec,parents_vec,theory);
tr_killer.set(tr);
// set the translation options, if needed
if(options)
for(hash_map<std::string,std::string>::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it)
tr->set_option(it->first, it->second);
// create a proof object to hold the translation
iz3proof pf(tr);
profiling::timer_stop("Interpolation prep");
// translate into an interpolatable proof
profiling::timer_start("Proof translation");
tr->translate(proof,pf);
profiling::timer_stop("Proof translation");
// translate the proof into interpolants
profiling::timer_start("Proof interpolation");
for(int i = 0; i < num-1; i++){
interps_vec[i] = pf.interpolate(tr->range_downward(i),tr->weak_mode());
interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(i));
}
profiling::timer_stop("Proof interpolation");
#else
iz3base the_base(*this,cnsts_vec,parents_vec,theory);
profiling::timer_stop("Interpolation prep");
for(int i = 0; i < num-1; i++){
range rng = the_base.range_downward(i);
std::vector<std::vector<ast> > cnsts_vec_vec(2);
for(unsigned j = 0; j < cnsts_vec.size(); j++){
bool is_A = the_base.in_range(j,rng);
for(unsigned k = 0; k < cnsts_vec[j].size(); k++)
cnsts_vec_vec[is_A ? 0 : 1].push_back(cnsts_vec[j][k]);
}
killme<iz3translation> tr_killer_i;
iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec_vec,std::vector<int>(),theory);
tr_killer_i.set(tr);
// set the translation options, if needed
if(options)
for(hash_map<std::string,std::string>::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it)
tr->set_option(it->first, it->second);
// create a proof object to hold the translation
iz3proof pf(tr);
// translate into an interpolatable proof
profiling::timer_start("Proof translation");
tr->translate(proof,pf);
profiling::timer_stop("Proof translation");
// translate the proof into interpolants
profiling::timer_start("Proof interpolation");
interps_vec[i] = pf.interpolate(tr->range_downward(0),tr->weak_mode());
interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(0));
profiling::timer_stop("Proof interpolation");
}
#endif
// put back in the removed frames
fr.fix_interpolants(interps_vec);
interps = interps_vec;
}
void proof_to_interpolant(z3pf proof,
std::vector<ast> &cnsts,
const std::vector<int> &parents,
std::vector<ast> &interps,
const std::vector<ast> &theory,
interpolation_options_struct *options = 0
){
std::vector<std::vector<ast> > cnsts_vec(cnsts.size());
for(unsigned i = 0; i < cnsts.size(); i++)
cnsts_vec[i].push_back(cnsts[i]);
proof_to_interpolant(proof,cnsts_vec,parents,interps,theory,options);
}
// same as above, but represents the tree using an ast
void proof_to_interpolant(const z3pf &proof,
const std::vector<ast> &_cnsts,
const ast &tree,
std::vector<ast> &interps,
interpolation_options_struct *options = 0
){
std::vector<int> pos_map;
// convert to the parents vector representation
to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map);
//use the parents vector representation to compute interpolant
proof_to_interpolant(proof,cnsts,parents,interps,theory,options);
// get the interps for the tree positions
std::vector<ast> _interps = interps;
interps.resize(pos_map.size());
for(unsigned i = 0; i < pos_map.size(); i++)
interps[i] = i < _interps.size() ? _interps[i] : mk_false();
}
bool has_interp(hash_map<ast,bool> &memo, const ast &t){
if(memo.find(t) != memo.end())
return memo[t];
bool res = false;
if(op(t) == Interp)
res = true;
else if(op(t) == And){
int nargs = num_args(t);
for(int i = 0; i < nargs; i++)
res |= has_interp(memo, arg(t,i));
}
memo[t] = res;
return res;
}
void collect_conjuncts(std::vector<ast> &cnsts, hash_map<ast,bool> &memo, const ast &t){
if(!has_interp(memo,t))
cnsts.push_back(t);
else {
int nargs = num_args(t);
for(int i = 0; i < nargs; i++)
collect_conjuncts(cnsts, memo, arg(t,i));
}
}
void assert_conjuncts(solver &s, std::vector<ast> &cnsts, const ast &t){
hash_map<ast,bool> memo;
collect_conjuncts(cnsts,memo,t);
for(unsigned i = 0; i < cnsts.size(); i++)
s.assert_expr(to_expr(cnsts[i].raw()));
}
iz3interp(ast_manager &_m_manager)
: iz3base(_m_manager) {}
};
void iz3interpolate(ast_manager &_m_manager,
ast *proof,
const ptr_vector<ast> &cnsts,
const ::vector<int> &parents,
ptr_vector<ast> &interps,
const ptr_vector<ast> &theory,
interpolation_options_struct * options)
{
iz3interp itp(_m_manager);
if(options)
options->apply(itp);
std::vector<iz3mgr::ast> _cnsts(cnsts.size());
std::vector<int> _parents(parents.size());
std::vector<iz3mgr::ast> _interps;
std::vector<iz3mgr::ast> _theory(theory.size());
for(unsigned i = 0; i < cnsts.size(); i++)
_cnsts[i] = itp.cook(cnsts[i]);
for(unsigned i = 0; i < parents.size(); i++)
_parents[i] = parents[i];
for(unsigned i = 0; i < theory.size(); i++)
_theory[i] = itp.cook(theory[i]);
iz3mgr::ast _proof = itp.cook(proof);
itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options);
interps.resize(_interps.size());
for(unsigned i = 0; i < interps.size(); i++)
interps[i] = itp.uncook(_interps[i]);
}
void iz3interpolate(ast_manager &_m_manager,
ast *proof,
const ::vector<ptr_vector<ast> > &cnsts,
const ::vector<int> &parents,
ptr_vector<ast> &interps,
const ptr_vector<ast> &theory,
interpolation_options_struct * options)
{
iz3interp itp(_m_manager);
if(options)
options->apply(itp);
std::vector<std::vector<iz3mgr::ast> > _cnsts(cnsts.size());
std::vector<int> _parents(parents.size());
std::vector<iz3mgr::ast> _interps;
std::vector<iz3mgr::ast> _theory(theory.size());
for(unsigned i = 0; i < cnsts.size(); i++)
for(unsigned j = 0; j < cnsts[i].size(); j++)
_cnsts[i].push_back(itp.cook(cnsts[i][j]));
for(unsigned i = 0; i < parents.size(); i++)
_parents[i] = parents[i];
for(unsigned i = 0; i < theory.size(); i++)
_theory[i] = itp.cook(theory[i]);
iz3mgr::ast _proof = itp.cook(proof);
itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options);
interps.resize(_interps.size());
for(unsigned i = 0; i < interps.size(); i++)
interps[i] = itp.uncook(_interps[i]);
}
void iz3interpolate(ast_manager &_m_manager,
ast *proof,
const ptr_vector<ast> &cnsts,
ast *tree,
ptr_vector<ast> &interps,
interpolation_options_struct * options)
{
iz3interp itp(_m_manager);
if(options)
options->apply(itp);
std::vector<iz3mgr::ast> _cnsts(cnsts.size());
std::vector<iz3mgr::ast> _interps;
for(unsigned i = 0; i < cnsts.size(); i++)
_cnsts[i] = itp.cook(cnsts[i]);
iz3mgr::ast _proof = itp.cook(proof);
iz3mgr::ast _tree = itp.cook(tree);
itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options);
interps.resize(_interps.size());
for(unsigned i = 0; i < interps.size(); i++)
interps[i] = itp.uncook(_interps[i]);
}
lbool iz3interpolate(ast_manager &_m_manager,
solver &s,
ast *tree,
ptr_vector<ast> &cnsts,
ptr_vector<ast> &interps,
model_ref &m,
interpolation_options_struct * options)
{
iz3interp itp(_m_manager);
if(options)
options->apply(itp);
iz3mgr::ast _tree = itp.cook(tree);
std::vector<iz3mgr::ast> _cnsts;
itp.assert_conjuncts(s,_cnsts,_tree);
profiling::timer_start("solving");
lbool res = s.check_sat(0,0);
profiling::timer_stop("solving");
if(res == l_false){
ast *proof = s.get_proof();
iz3mgr::ast _proof = itp.cook(proof);
std::vector<iz3mgr::ast> _interps;
itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options);
interps.resize(_interps.size());
for(unsigned i = 0; i < interps.size(); i++)
interps[i] = itp.uncook(_interps[i]);
}
else if(m){
s.get_model(m);
}
cnsts.resize(_cnsts.size());
for(unsigned i = 0; i < cnsts.size(); i++)
cnsts[i] = itp.uncook(_cnsts[i]);
return res;
}
void interpolation_options_struct::apply(iz3base &b){
for(stl_ext::hash_map<std::string,std::string>::iterator it = map.begin(), en = map.end();
it != en;
++it)
b.set_option((*it).first,(*it).second);
}

93
src/interp/iz3interp.h Normal file
View file

@ -0,0 +1,93 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3interp.h
Abstract:
Interpolation based on proof translation.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3_INTERP_H
#define IZ3_INTERP_H
#include "iz3hash.h"
#include "solver.h"
class iz3base;
struct interpolation_options_struct {
stl_ext::hash_map<std::string,std::string> map;
public:
void set(const std::string &name, const std::string &value){
map[name] = value;
}
void apply(iz3base &b);
};
/** This object is thrown if a tree interpolation problem is mal-formed */
struct iz3_bad_tree {
};
/** This object is thrown when iz3 fails due to an incompleteness in
the secondary solver. */
struct iz3_incompleteness {
};
typedef interpolation_options_struct *interpolation_options;
/* Compute an interpolant from a proof. This version uses the parents vector
representation, for compatibility with the old API. */
void iz3interpolate(ast_manager &_m_manager,
ast *proof,
const ptr_vector<ast> &cnsts,
const ::vector<int> &parents,
ptr_vector<ast> &interps,
const ptr_vector<ast> &theory,
interpolation_options_struct * options = 0);
/* Same as above, but each constraint is a vector of formulas. */
void iz3interpolate(ast_manager &_m_manager,
ast *proof,
const vector<ptr_vector<ast> > &cnsts,
const ::vector<int> &parents,
ptr_vector<ast> &interps,
const ptr_vector<ast> &theory,
interpolation_options_struct * options = 0);
/* Compute an interpolant from a proof. This version uses the ast
representation, for compatibility with the new API. */
void iz3interpolate(ast_manager &_m_manager,
ast *proof,
const ptr_vector<ast> &cnsts,
ast *tree,
ptr_vector<ast> &interps,
interpolation_options_struct * options);
/* Compute an interpolant from an ast representing an interpolation
problem, if unsat, else return a model (if enabled). Uses the
given solver to produce the proof/model. Also returns a vector
of the constraints in the problem, helpful for checking correctness.
*/
lbool iz3interpolate(ast_manager &_m_manager,
solver &s,
ast *tree,
ptr_vector<ast> &cnsts,
ptr_vector<ast> &interps,
model_ref &m,
interpolation_options_struct * options);
#endif

871
src/interp/iz3mgr.cpp Normal file
View file

@ -0,0 +1,871 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3mgr.cpp
Abstract:
A wrapper around an ast manager, providing convenience methods.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include "iz3mgr.h"
#include <stdio.h>
#include <fstream>
#include <iostream>
#include <ostream>
#include "expr_abstract.h"
#include "params.h"
#ifndef WIN32
using namespace stl_ext;
#endif
std::ostream &operator <<(std::ostream &s, const iz3mgr::ast &a){
return s;
}
iz3mgr::ast iz3mgr::make_var(const std::string &name, type ty){
symbol s = symbol(name.c_str());
return cook(m().mk_const(m().mk_const_decl(s, ty)));
}
iz3mgr::ast iz3mgr::make(opr op, int n, raw_ast **args){
switch(op) {
case True: return mki(m_basic_fid,OP_TRUE,n,args);
case False: return mki(m_basic_fid,OP_FALSE,n,args);
case Equal: return mki(m_basic_fid,OP_EQ,n,args);
case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args);
case Ite: return mki(m_basic_fid,OP_ITE,n,args);
case And: return mki(m_basic_fid,OP_AND,n,args);
case Or: return mki(m_basic_fid,OP_OR,n,args);
case Iff: return mki(m_basic_fid,OP_IFF,n,args);
case Xor: return mki(m_basic_fid,OP_XOR,n,args);
case Not: return mki(m_basic_fid,OP_NOT,n,args);
case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args);
case Oeq: return mki(m_basic_fid,OP_OEQ,n,args);
case Interp: return mki(m_basic_fid,OP_INTERP,n,args);
case Leq: return mki(m_arith_fid,OP_LE,n,args);
case Geq: return mki(m_arith_fid,OP_GE,n,args);
case Lt: return mki(m_arith_fid,OP_LT,n,args);
case Gt: return mki(m_arith_fid,OP_GT,n,args);
case Plus: return mki(m_arith_fid,OP_ADD,n,args);
case Sub: return mki(m_arith_fid,OP_SUB,n,args);
case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args);
case Times: return mki(m_arith_fid,OP_MUL,n,args);
case Div: return mki(m_arith_fid,OP_DIV,n,args);
case Idiv: return mki(m_arith_fid,OP_IDIV,n,args);
case Rem: return mki(m_arith_fid,OP_REM,n,args);
case Mod: return mki(m_arith_fid,OP_MOD,n,args);
case Power: return mki(m_arith_fid,OP_POWER,n,args);
case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args);
case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args);
case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args);
case Store: return mki(m_array_fid,OP_STORE,n,args);
case Select: return mki(m_array_fid,OP_SELECT,n,args);
case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args);
case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args);
case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args);
case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args);
case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args);
case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args);
case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args);
case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args);
case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args);
default:
assert(0);
return ast();
}
}
iz3mgr::ast iz3mgr::mki(family_id fid, decl_kind dk, int n, raw_ast **args){
return cook(m().mk_app(fid, dk, 0, 0, n, (expr **)args));
}
iz3mgr::ast iz3mgr::make(opr op, const std::vector<ast> &args){
static std::vector<raw_ast*> a(10);
if(a.size() < args.size())
a.resize(args.size());
for(unsigned i = 0; i < args.size(); i++)
a[i] = args[i].raw();
return make(op,args.size(), args.size() ? &a[0] : 0);
}
iz3mgr::ast iz3mgr::make(opr op){
return make(op,0,0);
}
iz3mgr::ast iz3mgr::make(opr op, const ast &arg0){
raw_ast *a = arg0.raw();
return make(op,1,&a);
}
iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1){
raw_ast *args[2];
args[0] = arg0.raw();
args[1] = arg1.raw();
return make(op,2,args);
}
iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1, const ast &arg2){
raw_ast *args[3];
args[0] = arg0.raw();
args[1] = arg1.raw();
args[2] = arg2.raw();
return make(op,3,args);
}
iz3mgr::ast iz3mgr::make(symb sym, int n, raw_ast **args){
return cook(m().mk_app(sym, n, (expr **) args));
}
iz3mgr::ast iz3mgr::make(symb sym, const std::vector<ast> &args){
static std::vector<raw_ast*> a(10);
if(a.size() < args.size())
a.resize(args.size());
for(unsigned i = 0; i < args.size(); i++)
a[i] = args[i].raw();
return make(sym,args.size(), args.size() ? &a[0] : 0);
}
iz3mgr::ast iz3mgr::make(symb sym){
return make(sym,0,0);
}
iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0){
raw_ast *a = arg0.raw();
return make(sym,1,&a);
}
iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1){
raw_ast *args[2];
args[0] = arg0.raw();
args[1] = arg1.raw();
return make(sym,2,args);
}
iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2){
raw_ast *args[3];
args[0] = arg0.raw();
args[1] = arg1.raw();
args[2] = arg2.raw();
return make(sym,3,args);
}
iz3mgr::ast iz3mgr::make_quant(opr op, const std::vector<ast> &bvs, ast &body){
if(bvs.size() == 0) return body;
std::vector<raw_ast *> foo(bvs.size());
std::vector<symbol> names;
std::vector<sort *> types;
std::vector<expr *> bound_asts;
unsigned num_bound = bvs.size();
for (unsigned i = 0; i < num_bound; ++i) {
app* a = to_app(bvs[i].raw());
symbol s(to_app(a)->get_decl()->get_name());
names.push_back(s);
types.push_back(m().get_sort(a));
bound_asts.push_back(a);
}
expr_ref abs_body(m());
expr_abstract(m(), 0, num_bound, &bound_asts[0], to_expr(body.raw()), abs_body);
expr_ref result(m());
result = m().mk_quantifier(
op == Forall,
names.size(), &types[0], &names[0], abs_body.get(),
0,
symbol("itp"),
symbol(),
0, 0,
0, 0
);
return cook(result.get());
}
// FIXME replace this with existing Z3 functionality
iz3mgr::ast iz3mgr::clone(const ast &t, const std::vector<ast> &_args){
if(_args.size() == 0)
return t;
ast_manager& m = m_manager;
expr* a = to_expr(t.raw());
static std::vector<raw_ast*> rargs(10);
if(rargs.size() < _args.size())
rargs.resize(_args.size());
for(unsigned i = 0; i < _args.size(); i++)
rargs[i] = _args[i].raw();
expr* const* args = (expr **)&rargs[0];
switch(a->get_kind()) {
case AST_APP: {
app* e = to_app(a);
if (e->get_num_args() != _args.size()) {
assert(0);
}
else {
a = m.mk_app(e->get_decl(), _args.size(), args);
}
break;
}
case AST_QUANTIFIER: {
if (_args.size() != 1) {
assert(0);
}
else {
a = m.update_quantifier(to_quantifier(a), args[0]);
}
break;
}
default:
break;
}
return cook(a);
}
void iz3mgr::show(ast t){
params_ref p;
p.set_bool("flat_assoc",false);
std::cout << mk_pp(t.raw(), m(), p) << std::endl;
}
void iz3mgr::show_symb(symb s){
std::cout << mk_pp(s, m()) << std::endl;
}
void iz3mgr::print_expr(std::ostream &s, const ast &e){
params_ref p;
p.set_bool("flat_assoc",false);
s << mk_pp(e.raw(), m(), p);
}
void iz3mgr::print_clause(std::ostream &s, std::vector<ast> &cls){
s << "(";
for(unsigned i = 0; i < cls.size(); i++){
if(i > 0) s << ",";
print_expr(s,cls[i]);
}
s << ")";
}
void iz3mgr::show_clause(std::vector<ast> &cls){
print_clause(std::cout,cls);
std::cout << std::endl;
}
void iz3mgr::print_lit(ast lit){
ast abslit = is_not(lit) ? arg(lit,0) : lit;
int f = op(abslit);
if(f == And || f == Or || f == Iff){
if(is_not(lit)) std::cout << "~";
std::cout << "[" << abslit << "]";
}
else
std::cout << lit;
}
static int pretty_cols = 79;
static int pretty_indent_chars = 2;
static int pretty_find_delim(const std::string &s, int pos){
int level = 0;
int end = s.size();
for(; pos < end; pos++){
int ch = s[pos];
if(ch == '(')level++;
if(ch == ')')level--;
if(level < 0 || (level == 0 && ch == ','))break;
}
return pos;
}
static void pretty_newline(std::ostream &f, int indent){
f << std::endl;
for(int i = 0; i < indent; i++)
f << " ";
}
void iz3mgr::pretty_print(std::ostream &f, const std::string &s){
int cur_indent = 0;
int indent = 0;
int col = 0;
int pos = 0;
while(pos < (int)s.size()){
int delim = pretty_find_delim(s,pos);
if(s[pos] != ')' && s[pos] != ',' && cur_indent > indent){
pretty_newline(f,indent);
cur_indent = indent;
col = indent;
continue;
}
if (col + delim - pos > pretty_cols) {
if (col > indent) {
pretty_newline(f,indent);
cur_indent = indent;
col = indent;
continue;
}
int paren = s.find('(',pos);
if(paren != (int)std::string::npos){
int chars = paren - pos + 1;
f << s.substr(pos,chars);
indent += pretty_indent_chars;
if(col) pretty_newline(f,indent);
cur_indent = indent;
pos += chars;
col = indent;
continue;
}
}
int chars = delim - pos + 1;
f << s.substr(pos,chars);
pos += chars;
col += chars;
if(s[delim] == ')')
indent -= pretty_indent_chars;
}
}
iz3mgr::opr iz3mgr::op(const ast &t){
ast_kind dk = t.raw()->get_kind();
switch(dk){
case AST_APP: {
expr * e = to_expr(t.raw());
func_decl *d = to_app(t.raw())->get_decl();
if (null_family_id == d->get_family_id())
return Uninterpreted;
// return (opr)d->get_decl_kind();
if (m_basic_fid == d->get_family_id()) {
switch(d->get_decl_kind()) {
case OP_TRUE: return True;
case OP_FALSE: return False;
case OP_EQ: return Equal;
case OP_DISTINCT: return Distinct;
case OP_ITE: return Ite;
case OP_AND: return And;
case OP_OR: return Or;
case OP_IFF: return Iff;
case OP_XOR: return Xor;
case OP_NOT: return Not;
case OP_IMPLIES: return Implies;
case OP_OEQ: return Oeq;
case OP_INTERP: return Interp;
default:
return Other;
}
}
if (m_arith_fid == d->get_family_id()) {
switch(d->get_decl_kind()) {
case OP_LE: return Leq;
case OP_GE: return Geq;
case OP_LT: return Lt;
case OP_GT: return Gt;
case OP_ADD: return Plus;
case OP_SUB: return Sub;
case OP_UMINUS: return Uminus;
case OP_MUL: return Times;
case OP_DIV: return Div;
case OP_IDIV: return Idiv;
case OP_REM: return Rem;
case OP_MOD: return Mod;
case OP_POWER: return Power;
case OP_TO_REAL: return ToReal;
case OP_TO_INT: return ToInt;
case OP_IS_INT: return IsInt;
default:
if (m().is_unique_value(e))
return Numeral;
return Other;
}
}
if (m_array_fid == d->get_family_id()) {
switch(d->get_decl_kind()) {
case OP_STORE: return Store;
case OP_SELECT: return Select;
case OP_CONST_ARRAY: return ConstArray;
case OP_ARRAY_DEFAULT: return ArrayDefault;
case OP_ARRAY_MAP: return ArrayMap;
case OP_SET_UNION: return SetUnion;
case OP_SET_INTERSECT: return SetIntersect;
case OP_SET_DIFFERENCE: return SetDifference;
case OP_SET_COMPLEMENT: return SetComplement;
case OP_SET_SUBSET: return SetSubSet;
case OP_AS_ARRAY: return AsArray;
default:
return Other;
}
}
return Other;
}
case AST_QUANTIFIER:
return to_quantifier(t.raw())->is_forall() ? Forall : Exists;
case AST_VAR:
return Variable;
default:;
}
return Other;
}
iz3mgr::pfrule iz3mgr::pr(const ast &t){
func_decl *d = to_app(t.raw())->get_decl();
assert(m_basic_fid == d->get_family_id());
return d->get_decl_kind();
}
void iz3mgr::print_sat_problem(std::ostream &out, const ast &t){
ast_smt_pp pp(m());
pp.set_simplify_implies(false);
pp.display_smt2(out, to_expr(t.raw()));
}
iz3mgr::ast iz3mgr::z3_simplify(const ast &e){
::expr * a = to_expr(e.raw());
params_ref p;
th_rewriter m_rw(m(), p);
expr_ref result(m());
m_rw(a, result);
return cook(result);
}
iz3mgr::ast iz3mgr::z3_really_simplify(const ast &e){
::expr * a = to_expr(e.raw());
params_ref simp_params;
simp_params.set_bool(":som",true);
simp_params.set_bool(":sort-sums",true);
th_rewriter m_rw(m(), simp_params);
expr_ref result(m());
m_rw(a, result);
return cook(result);
}
#if 0
static rational lcm(const rational &x, const rational &y){
int a = x.numerator();
int b = y.numerator();
return rational(a * b / gcd(a, b));
}
#endif
static rational extract_lcd(std::vector<rational> &res){
if(res.size() == 0) return rational(1); // shouldn't happen
rational lcd = denominator(res[0]);
for(unsigned i = 1; i < res.size(); i++)
lcd = lcm(lcd,denominator(res[i]));
for(unsigned i = 0; i < res.size(); i++)
res[i] *= lcd;
return lcd;
}
void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector<ast>& coeffs){
std::vector<rational> rats;
get_farkas_coeffs(proof,rats);
coeffs.resize(rats.size());
for(unsigned i = 0; i < rats.size(); i++){
sort *is = m().mk_sort(m_arith_fid, INT_SORT);
ast coeff = cook(m_arith_util.mk_numeral(rats[i],is));
coeffs[i] = coeff;
}
}
static void abs_rat(std::vector<rational> &rats){
// check that they are all non-neg -- if neg, take abs val and warn!
for(unsigned i = 0; i < rats.size(); i++)
if(rats[i].is_neg()){
// std::cout << "negative Farkas coeff!\n";
rats[i] = -rats[i];
}
}
bool iz3mgr::is_farkas_coefficient_negative(const ast &proof, int n){
rational r;
symb s = sym(proof);
bool ok = s->get_parameter(n+2).is_rational(r);
if(!ok)
throw "Bad Farkas coefficient";
return r.is_neg();
}
void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector<rational>& rats){
symb s = sym(proof);
int numps = s->get_num_parameters();
rats.resize(numps-2);
#if 0
if(num_prems(proof) < numps-2){
std::cout << "bad farkas rule: " << num_prems(proof) << " premises should be " << numps-2 << "\n";
}
#endif
for(int i = 2; i < numps; i++){
rational r;
bool ok = s->get_parameter(i).is_rational(r);
if(!ok)
throw "Bad Farkas coefficient";
#if 0
{
ast con = conc(prem(proof,i-2));
ast temp = make_real(r); // for debugging
opr o = is_not(con) ? op(arg(con,0)) : op(con);
if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt))
r = -r;
}
#endif
rats[i-2] = r;
}
#if 0
if(rats.size() != 0 && rats[0].is_neg()){
for(unsigned i = 0; i < rats.size(); i++){
assert(rats[i].is_neg());
rats[i] = -rats[i];
}
}
#endif
abs_rat(rats);
extract_lcd(rats);
}
void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector<ast>& coeffs){
std::vector<rational> rats;
get_assign_bounds_coeffs(proof,rats);
coeffs.resize(rats.size());
for(unsigned i = 0; i < rats.size(); i++){
coeffs[i] = make_int(rats[i]);
}
}
void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector<rational>& rats){
symb s = sym(proof);
int numps = s->get_num_parameters();
rats.resize(numps-1);
rats[0] = rational(1);
ast conseq = arg(conc(proof),0);
opr conseq_o = is_not(conseq) ? op(arg(conseq,0)) : op(conseq);
bool conseq_neg = is_not(conseq) ? (conseq_o == Leq || conseq_o == Lt) : (conseq_o == Geq || conseq_o == Gt);
for(int i = 2; i < numps; i++){
rational r;
bool ok = s->get_parameter(i).is_rational(r);
if(!ok)
throw "Bad Farkas coefficient";
{
ast con = arg(conc(proof),i-1);
ast temp = make_real(r); // for debugging
opr o = is_not(con) ? op(arg(con,0)) : op(con);
if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt))
r = -r;
if(conseq_neg)
r = -r;
}
rats[i-1] = r;
}
#if 0
if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them
for(unsigned i = 1; i < rats.size(); i++){
if(!rats[i].is_neg())
throw "Bad Farkas coefficients";
rats[i] = -rats[i];
}
}
#endif
abs_rat(rats);
extract_lcd(rats);
}
void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector<ast>& coeffs){
std::vector<rational> rats;
get_assign_bounds_rule_coeffs(proof,rats);
coeffs.resize(rats.size());
for(unsigned i = 0; i < rats.size(); i++){
coeffs[i] = make_int(rats[i]);
}
}
void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector<rational>& rats){
symb s = sym(proof);
int numps = s->get_num_parameters();
rats.resize(numps-1);
rats[0] = rational(1);
ast conseq = arg(conc(proof),0);
opr conseq_o = is_not(conseq) ? op(arg(conseq,0)) : op(conseq);
bool conseq_neg = is_not(conseq) ? (conseq_o == Leq || conseq_o == Lt) : (conseq_o == Geq || conseq_o == Gt);
for(int i = 2; i < numps; i++){
rational r;
bool ok = s->get_parameter(i).is_rational(r);
if(!ok)
throw "Bad Farkas coefficient";
{
ast con = conc(prem(proof,i-2));
ast temp = make_real(r); // for debugging
opr o = is_not(con) ? op(arg(con,0)) : op(con);
if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt))
r = -r;
if(conseq_neg)
r = -r;
}
rats[i-1] = r;
}
#if 0
if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them
for(unsigned i = 1; i < rats.size(); i++){
if(!rats[i].is_neg())
throw "Bad Farkas coefficients";
rats[i] = -rats[i];
}
}
#endif
abs_rat(rats);
extract_lcd(rats);
}
/** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */
void iz3mgr::linear_comb(ast &P, const ast &c, const ast &Q){
ast Qrhs;
bool strict = op(P) == Lt;
if(is_not(Q)){
ast nQ = arg(Q,0);
switch(op(nQ)){
case Gt:
Qrhs = make(Sub,arg(nQ,1),arg(nQ,0));
break;
case Lt:
Qrhs = make(Sub,arg(nQ,0),arg(nQ,1));
break;
case Geq:
Qrhs = make(Sub,arg(nQ,1),arg(nQ,0));
strict = true;
break;
case Leq:
Qrhs = make(Sub,arg(nQ,0),arg(nQ,1));
strict = true;
break;
default:
throw "not an inequality";
}
}
else {
switch(op(Q)){
case Leq:
Qrhs = make(Sub,arg(Q,1),arg(Q,0));
break;
case Geq:
Qrhs = make(Sub,arg(Q,0),arg(Q,1));
break;
case Lt:
Qrhs = make(Sub,arg(Q,1),arg(Q,0));
strict = true;
break;
case Gt:
Qrhs = make(Sub,arg(Q,0),arg(Q,1));
strict = true;
break;
default:
throw "not an inequality";
}
}
Qrhs = make(Times,c,Qrhs);
if(strict)
P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs));
else
P = make(Leq,arg(P,0),make(Plus,arg(P,1),Qrhs));
}
iz3mgr::ast iz3mgr::sum_inequalities(const std::vector<ast> &coeffs, const std::vector<ast> &ineqs){
ast zero = make_int("0");
ast thing = make(Leq,zero,zero);
for(unsigned i = 0; i < ineqs.size(); i++){
linear_comb(thing,coeffs[i],ineqs[i]);
}
thing = simplify_ineq(thing);
return thing;
}
void iz3mgr::mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac){
opr o = op(t);
if(o == Plus){
int nargs = num_args(t);
for(int i = 0; i < nargs; i++)
mk_idiv(arg(t,i),d,whole,frac);
return;
}
else if(o == Times){
rational coeff;
if(is_numeral(arg(t,0),coeff)){
if(gcd(coeff,d) == d){
whole = make(Plus,whole,make(Times,make_int(coeff/d),arg(t,1)));
return;
}
}
}
frac = make(Plus,frac,t);
}
iz3mgr::ast iz3mgr::mk_idiv(const ast& q, const rational &d){
ast t = z3_simplify(q);
if(d == rational(1))
return t;
else {
ast whole = make_int("0");
ast frac = whole;
mk_idiv(t,d,whole,frac);
return z3_simplify(make(Plus,whole,make(Idiv,z3_simplify(frac),make_int(d))));
}
}
iz3mgr::ast iz3mgr::mk_idiv(const ast& t, const ast &d){
rational r;
if(is_numeral(d,r))
return mk_idiv(t,r);
return make(Idiv,t,d);
}
// does variable occur in expression?
int iz3mgr::occurs_in1(stl_ext::hash_map<ast,bool> &occurs_in_memo,ast var, ast e){
std::pair<ast,bool> foo(e,false);
std::pair<hash_map<ast,bool>::iterator,bool> bar = occurs_in_memo.insert(foo);
bool &res = bar.first->second;
if(bar.second){
if(e == var) res = true;
int nargs = num_args(e);
for(int i = 0; i < nargs; i++)
res |= occurs_in1(occurs_in_memo,var,arg(e,i));
}
return res;
}
int iz3mgr::occurs_in(ast var, ast e){
hash_map<ast,bool> memo;
return occurs_in1(memo,var,e);
}
bool iz3mgr::solve_arith(const ast &v, const ast &x, const ast &y, ast &res){
if(op(x) == Plus){
int n = num_args(x);
for(int i = 0; i < n; i++){
if(arg(x,i) == v){
res = z3_simplify(make(Sub, y, make(Sub, x, v)));
return true;
}
}
}
return false;
}
// find a controlling equality for a given variable v in a term
// a controlling equality is of the form v = t, which, being
// false would force the formula to have the specifid truth value
// returns t, or null if no such
iz3mgr::ast iz3mgr::cont_eq(stl_ext::hash_set<ast> &cont_eq_memo, bool truth, ast v, ast e){
if(is_not(e)) return cont_eq(cont_eq_memo, !truth,v,arg(e,0));
if(cont_eq_memo.find(e) != cont_eq_memo.end())
return ast();
cont_eq_memo.insert(e);
if(!truth && op(e) == Equal){
if(arg(e,0) == v) return(arg(e,1));
if(arg(e,1) == v) return(arg(e,0));
ast res;
if(solve_arith(v,arg(e,0),arg(e,1),res)) return res;
if(solve_arith(v,arg(e,1),arg(e,0),res)) return res;
}
if((!truth && op(e) == And) || (truth && op(e) == Or)){
int nargs = num_args(e);
for(int i = 0; i < nargs; i++){
ast res = cont_eq(cont_eq_memo, truth, v, arg(e,i));
if(!res.null()) return res;
}
}
if(truth && op(e) == Implies){
ast res = cont_eq(cont_eq_memo, !truth, v, arg(e,0));
if(!res.null()) return res;
res = cont_eq(cont_eq_memo, truth, v, arg(e,1));
if(!res.null()) return res;
}
return ast();
}
// substitute a term t for unbound occurrences of variable v in e
iz3mgr::ast iz3mgr::subst(stl_ext::hash_map<ast,ast> &subst_memo, ast var, ast t, ast e){
if(e == var) return t;
std::pair<ast,ast> foo(e,ast());
std::pair<hash_map<ast,ast>::iterator,bool> bar = subst_memo.insert(foo);
ast &res = bar.first->second;
if(bar.second){
int nargs = num_args(e);
std::vector<ast> args(nargs);
for(int i = 0; i < nargs; i++)
args[i] = subst(subst_memo,var,t,arg(e,i));
opr f = op(e);
if(f == Equal && args[0] == args[1]) res = mk_true();
else res = clone(e,args);
}
return res;
}
iz3mgr::ast iz3mgr::subst(ast var, ast t, ast e){
hash_map<ast,ast> memo;
return subst(memo,var,t,e);
}
iz3mgr::ast iz3mgr::subst(stl_ext::hash_map<ast,ast> &subst_memo,ast e){
std::pair<ast,ast> foo(e,ast());
std::pair<hash_map<ast,ast>::iterator,bool> bar = subst_memo.insert(foo);
ast &res = bar.first->second;
if(bar.second){
int nargs = num_args(e);
std::vector<ast> args(nargs);
for(int i = 0; i < nargs; i++)
args[i] = subst(subst_memo,arg(e,i));
opr f = op(e);
if(f == Equal && args[0] == args[1]) res = mk_true();
else res = clone(e,args);
}
return res;
}
// apply a quantifier to a formula, with some optimizations
// 1) bound variable does not occur -> no quantifier
// 2) bound variable must be equal to some term -> substitute
iz3mgr::ast iz3mgr::apply_quant(opr quantifier, ast var, ast e){
if((quantifier == Forall && op(e) == And)
|| (quantifier == Exists && op(e) == Or)){
int n = num_args(e);
std::vector<ast> args(n);
for(int i = 0; i < n; i++)
args[i] = apply_quant(quantifier,var,arg(e,i));
return make(op(e),args);
}
if(!occurs_in(var,e))return e;
hash_set<ast> cont_eq_memo;
ast cterm = cont_eq(cont_eq_memo, quantifier == Forall, var, e);
if(!cterm.null()){
return subst(var,cterm,e);
}
std::vector<ast> bvs; bvs.push_back(var);
return make_quant(quantifier,bvs,e);
}

718
src/interp/iz3mgr.h Normal file
View file

@ -0,0 +1,718 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3mgr.h
Abstract:
A wrapper around an ast manager, providing convenience methods.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3MGR_H
#define IZ3MGR_H
#include <assert.h>
#include "iz3hash.h"
#include"well_sorted.h"
#include"arith_decl_plugin.h"
#include"bv_decl_plugin.h"
#include"datatype_decl_plugin.h"
#include"array_decl_plugin.h"
#include"ast_translation.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"ast_smt_pp.h"
#include"ast_smt2_pp.h"
#include"th_rewriter.h"
#include"var_subst.h"
#include"expr_substitution.h"
#include"pp.h"
#include"scoped_ctrl_c.h"
#include"cancel_eh.h"
#include"scoped_timer.h"
//# include"pp_params.hpp"
/* A wrapper around an ast manager, providing convenience methods. */
/** Shorthands for some built-in operators. */
// rename this to keep it accessible, as we use ast for something else
typedef ast raw_ast;
/** Wrapper around an ast pointer */
class ast_i {
protected:
raw_ast *_ast;
public:
raw_ast * const &raw() const {return _ast;}
ast_i(raw_ast *a){_ast = a;}
ast_i(){_ast = 0;}
bool eq(const ast_i &other) const {
return _ast == other._ast;
}
bool lt(const ast_i &other) const {
return _ast->get_id() < other._ast->get_id();
}
friend bool operator==(const ast_i &x, const ast_i&y){
return x.eq(y);
}
friend bool operator!=(const ast_i &x, const ast_i&y){
return !x.eq(y);
}
friend bool operator<(const ast_i &x, const ast_i&y){
return x.lt(y);
}
size_t hash() const {return _ast->get_id();}
bool null() const {return !_ast;}
};
/** Reference counting verison of above */
class ast_r : public ast_i {
ast_manager *_m;
public:
ast_r(ast_manager *m, raw_ast *a) : ast_i(a) {
_m = m;
m->inc_ref(a);
}
ast_r() {_m = 0;}
ast_r(const ast_r &other) : ast_i(other) {
_m = other._m;
_m->inc_ref(_ast);
}
ast_r &operator=(const ast_r &other) {
if(_ast)
_m->dec_ref(_ast);
_ast = other._ast;
_m = other._m;
_m->inc_ref(_ast);
return *this;
}
~ast_r(){
if(_ast)
_m->dec_ref(_ast);
}
ast_manager *mgr() const {return _m;}
};
// to make ast_r hashable
namespace hash_space {
template <>
class hash<ast_r> {
public:
size_t operator()(const ast_r &s) const {
return s.raw()->get_id();
}
};
}
// to make ast_r hashable in windows
#ifdef WIN32
template <> inline
size_t stdext::hash_value<ast_r >(const ast_r& s)
{
return s.raw()->get_id();
}
#endif
// to make ast_r usable in ordered collections
namespace std {
template <>
class less<ast_r> {
public:
bool operator()(const ast_r &s, const ast_r &t) const {
// return s.raw() < t.raw();
return s.raw()->get_id() < t.raw()->get_id();
}
};
}
/** Wrapper around an AST manager, providing convenience methods. */
class iz3mgr {
public:
typedef ast_r ast;
// typedef decl_kind opr;
typedef func_decl *symb;
typedef sort *type;
typedef ast_r z3pf;
typedef decl_kind pfrule;
enum opr {
True,
False,
And,
Or,
Not,
Iff,
Ite,
Equal,
Implies,
Distinct,
Xor,
Oeq,
Interp,
Leq,
Geq,
Lt,
Gt,
Plus,
Sub,
Uminus,
Times,
Div,
Idiv,
Rem,
Mod,
Power,
ToReal,
ToInt,
IsInt,
Select,
Store,
ConstArray,
ArrayDefault,
ArrayMap,
SetUnion,
SetIntersect,
SetDifference,
SetComplement,
SetSubSet,
AsArray,
Numeral,
Forall,
Exists,
Variable,
Uninterpreted,
Other
};
opr op(const ast &t);
unsigned ast_id(const ast &x)
{
return to_expr(x.raw())->get_id();
}
/** Overloads for constructing ast. */
ast make_var(const std::string &name, type ty);
ast make(opr op, const std::vector<ast> &args);
ast make(opr op);
ast make(opr op, const ast &arg0);
ast make(opr op, const ast &arg0, const ast &arg1);
ast make(opr op, const ast &arg0, const ast &arg1, const ast &arg2);
ast make(symb sym, const std::vector<ast> &args);
ast make(symb sym);
ast make(symb sym, const ast &arg0);
ast make(symb sym, const ast &arg0, const ast &arg1);
ast make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2);
ast make_quant(opr op, const std::vector<ast> &bvs, ast &body);
ast clone(const ast &t, const std::vector<ast> &args);
ast_manager &m() {return m_manager;}
ast cook(raw_ast *a) {return ast(&m_manager,a);}
std::vector<ast> cook(ptr_vector<raw_ast> v) {
std::vector<ast> _v(v.size());
for(unsigned i = 0; i < v.size(); i++)
_v[i] = cook(v[i]);
return _v;
}
raw_ast *uncook(const ast &a) {
m_manager.inc_ref(a.raw());
return a.raw();
}
/** Methods for destructing ast. */
int num_args(ast t){
ast_kind dk = t.raw()->get_kind();
switch(dk){
case AST_APP:
return to_app(t.raw())->get_num_args();
case AST_QUANTIFIER:
return 1;
case AST_VAR:
return 0;
default:;
}
assert(0);
}
ast arg(const ast &t, int i){
ast_kind dk = t.raw()->get_kind();
switch(dk){
case AST_APP:
return cook(to_app(t.raw())->get_arg(i));
case AST_QUANTIFIER:
return cook(to_quantifier(t.raw())->get_expr());
default:;
}
assert(0);
return ast();
}
void get_args(const ast &t, std::vector<ast> &res){
res.resize(num_args(t));
for(unsigned i = 0; i < res.size(); i++)
res[i] = arg(t,i);
}
symb sym(ast t){
return to_app(t.raw())->get_decl();
}
std::string string_of_symbol(symb s){
symbol _s = s->get_name();
if (_s.is_numerical()) {
std::ostringstream buffer;
buffer << _s.get_num();
return buffer.str();
}
else {
return _s.bare_str();
}
}
type get_type(ast t){
return m().get_sort(to_expr(t.raw()));
}
std::string string_of_numeral(const ast& t){
rational r;
expr* e = to_expr(t.raw());
assert(e);
if (m_arith_util.is_numeral(e, r))
return r.to_string();
assert(0);
return "NaN";
}
bool is_numeral(const ast& t, rational &r){
expr* e = to_expr(t.raw());
assert(e);
return m_arith_util.is_numeral(e, r);
}
rational get_coeff(const ast& t){
rational res;
if(op(t) == Times && is_numeral(arg(t,0),res))
return res;
return rational(1);
}
ast get_linear_var(const ast& t){
rational res;
if(op(t) == Times && is_numeral(arg(t,0),res))
return arg(t,1);
return t;
}
int get_quantifier_num_bound(const ast &t) {
return to_quantifier(t.raw())->get_num_decls();
}
std::string get_quantifier_bound_name(const ast &t, unsigned i) {
return to_quantifier(t.raw())->get_decl_names()[i].bare_str();
}
type get_quantifier_bound_type(const ast &t, unsigned i) {
return to_quantifier(t.raw())->get_decl_sort(i);
}
ast get_quantifier_body(const ast &t) {
return cook(to_quantifier(t.raw())->get_expr());
}
unsigned get_variable_index_value(const ast &t) {
var* va = to_var(t.raw());
return va->get_idx();
}
bool is_bool_type(type t){
family_id fid = to_sort(t)->get_family_id();
decl_kind k = to_sort(t)->get_decl_kind();
return fid == m().get_basic_family_id() && k == BOOL_SORT;
}
bool is_array_type(type t){
family_id fid = to_sort(t)->get_family_id();
decl_kind k = to_sort(t)->get_decl_kind();
return fid == m_array_fid && k == ARRAY_SORT;
}
type get_range_type(symb s){
return to_func_decl(s)->get_range();
}
int get_num_parameters(const symb &s){
return to_func_decl(s)->get_num_parameters();
}
ast get_ast_parameter(const symb &s, int idx){
return cook(to_func_decl(s)->get_parameters()[idx].get_ast());
}
enum lemma_theory {ArithTheory,ArrayTheory,UnknownTheory};
lemma_theory get_theory_lemma_theory(const ast &proof){
symb s = sym(proof);
::symbol p0;
bool ok = s->get_parameter(0).is_symbol(p0);
if(!ok) return UnknownTheory;
std::string foo(p0.bare_str());
if(foo == "arith")
return ArithTheory;
if(foo == "array")
return ArrayTheory;
return UnknownTheory;
}
enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,UnknownKind};
lemma_kind get_theory_lemma_kind(const ast &proof){
symb s = sym(proof);
::symbol p0;
bool ok = s->get_parameter(1).is_symbol(p0);
if(!ok) return UnknownKind;
std::string foo(p0.bare_str());
if(foo == "farkas")
return FarkasKind;
if(foo == "triangle-eq")
return is_not(arg(conc(proof),0)) ? Eq2LeqKind : Leq2EqKind;
if(foo == "gcd-test")
return GCDTestKind;
if(foo == "assign-bounds")
return AssignBoundsKind;
if(foo == "eq-propagate")
return EqPropagateKind;
return UnknownKind;
}
void get_farkas_coeffs(const ast &proof, std::vector<ast>& coeffs);
void get_farkas_coeffs(const ast &proof, std::vector<rational>& rats);
void get_assign_bounds_coeffs(const ast &proof, std::vector<rational>& rats);
void get_assign_bounds_coeffs(const ast &proof, std::vector<ast>& rats);
void get_assign_bounds_rule_coeffs(const ast &proof, std::vector<rational>& rats);
void get_assign_bounds_rule_coeffs(const ast &proof, std::vector<ast>& rats);
bool is_farkas_coefficient_negative(const ast &proof, int n);
bool is_true(ast t){
return op(t) == True;
}
bool is_false(ast t){
return op(t) == False;
}
bool is_iff(ast t){
return op(t) == Iff;
}
bool is_or(ast t){
return op(t) == Or;
}
bool is_not(ast t){
return op(t) == Not;
}
/** Simplify an expression using z3 simplifier */
ast z3_simplify(const ast& e);
/** Simplify, sorting sums */
ast z3_really_simplify(const ast &e);
// Some constructors that simplify things
ast mk_not(ast x){
opr o = op(x);
if(o == True) return make(False);
if(o == False) return make(True);
if(o == Not) return arg(x,0);
return make(Not,x);
}
ast mk_and(ast x, ast y){
opr ox = op(x);
opr oy = op(y);
if(ox == True) return y;
if(oy == True) return x;
if(ox == False) return x;
if(oy == False) return y;
if(x == y) return x;
return make(And,x,y);
}
ast mk_or(ast x, ast y){
opr ox = op(x);
opr oy = op(y);
if(ox == False) return y;
if(oy == False) return x;
if(ox == True) return x;
if(oy == True) return y;
if(x == y) return x;
return make(Or,x,y);
}
ast mk_implies(ast x, ast y){
opr ox = op(x);
opr oy = op(y);
if(ox == True) return y;
if(oy == False) return mk_not(x);
if(ox == False) return mk_true();
if(oy == True) return y;
if(x == y) return mk_true();
return make(Implies,x,y);
}
ast mk_or(const std::vector<ast> &x){
ast res = mk_false();
for(unsigned i = 0; i < x.size(); i++)
res = mk_or(res,x[i]);
return res;
}
ast mk_and(const std::vector<ast> &x){
std::vector<ast> conjs;
for(unsigned i = 0; i < x.size(); i++){
const ast &e = x[i];
opr o = op(e);
if(o == False)
return mk_false();
if(o != True)
conjs.push_back(e);
}
if(conjs.size() == 0)
return mk_true();
if(conjs.size() == 1)
return conjs[0];
return make(And,conjs);
}
ast mk_equal(ast x, ast y){
if(x == y) return make(True);
opr ox = op(x);
opr oy = op(y);
if(ox == True) return y;
if(oy == True) return x;
if(ox == False) return mk_not(y);
if(oy == False) return mk_not(x);
if(ox == False && oy == True) return make(False);
if(oy == False && ox == True) return make(False);
return make(Equal,x,y);
}
ast z3_ite(ast x, ast y, ast z){
opr ox = op(x);
opr oy = op(y);
opr oz = op(z);
if(ox == True) return y;
if(ox == False) return z;
if(y == z) return y;
if(oy == True && oz == False) return x;
if(oz == True && oy == False) return mk_not(x);
return make(Ite,x,y,z);
}
ast make_int(const std::string &s) {
sort *r = m().mk_sort(m_arith_fid, INT_SORT);
return cook(m_arith_util.mk_numeral(rational(s.c_str()),r));
}
ast make_int(const rational &s) {
sort *r = m().mk_sort(m_arith_fid, INT_SORT);
return cook(m_arith_util.mk_numeral(s,r));
}
ast make_real(const std::string &s) {
sort *r = m().mk_sort(m_arith_fid, REAL_SORT);
return cook(m_arith_util.mk_numeral(rational(s.c_str()),r));
}
ast make_real(const rational &s) {
sort *r = m().mk_sort(m_arith_fid, REAL_SORT);
return cook(m_arith_util.mk_numeral(s,r));
}
ast mk_false() { return make(False); }
ast mk_true() { return make(True); }
ast mk_fresh_constant(char const * prefix, type s){
return cook(m().mk_fresh_const(prefix, s));
}
type bool_type() {
::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT);
return s;
}
type int_type() {
::sort *s = m().mk_sort(m_arith_fid, INT_SORT);
return s;
}
type real_type() {
::sort *s = m().mk_sort(m_arith_fid, REAL_SORT);
return s;
}
type array_type(type d, type r) {
parameter params[2] = { parameter(d), parameter(to_sort(r)) };
::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params);
return s;
}
symb function(const std::string &str_name, unsigned arity, type *domain, type range) {
::symbol name = ::symbol(str_name.c_str());
std::vector< ::sort *> sv(arity);
for(unsigned i = 0; i < arity; i++)
sv[i] = domain[i];
::func_decl* d = m().mk_func_decl(name,arity,&sv[0],range);
return d;
}
void linear_comb(ast &P, const ast &c, const ast &Q);
ast sum_inequalities(const std::vector<ast> &coeffs, const std::vector<ast> &ineqs);
ast simplify_ineq(const ast &ineq){
ast res = make(op(ineq),arg(ineq,0),z3_simplify(arg(ineq,1)));
return res;
}
void mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac);
ast mk_idiv(const ast& t, const rational &d);
ast mk_idiv(const ast& t, const ast &d);
/** methods for destructing proof terms */
pfrule pr(const z3pf &t);
int num_prems(const z3pf &t){return to_app(t.raw())->get_num_args()-1;}
z3pf prem(const z3pf &t, int n){return arg(t,n);}
z3pf conc(const z3pf &t){return arg(t,num_prems(t));}
/* quantifier handling */
// substitute a term t for unbound occurrences of variable v in e
ast subst(ast var, ast t, ast e);
// apply a substitution defined by a map
ast subst(stl_ext::hash_map<ast,ast> &map, ast e);
// apply a quantifier to a formula, with some optimizations
// 1) bound variable does not occur -> no quantifier
// 2) bound variable must be equal to some term -> substitute
ast apply_quant(opr quantifier, ast var, ast e);
/** For debugging */
void show(ast);
void show_symb(symb s);
/** Constructor */
void print_lit(ast lit);
void print_expr(std::ostream &s, const ast &e);
void print_clause(std::ostream &s, std::vector<ast> &cls);
void print_sat_problem(std::ostream &out, const ast &t);
void show_clause(std::vector<ast> &cls);
static void pretty_print(std::ostream &f, const std::string &s);
iz3mgr(ast_manager &_m_manager)
: m_manager(_m_manager),
m_arith_util(_m_manager)
{
m_basic_fid = m().get_basic_family_id();
m_arith_fid = m().mk_family_id("arith");
m_bv_fid = m().mk_family_id("bv");
m_array_fid = m().mk_family_id("array");
m_dt_fid = m().mk_family_id("datatype");
m_datalog_fid = m().mk_family_id("datalog_relation");
}
iz3mgr(const iz3mgr& other)
: m_manager(other.m_manager),
m_arith_util(other.m_manager)
{
m_basic_fid = m().get_basic_family_id();
m_arith_fid = m().mk_family_id("arith");
m_bv_fid = m().mk_family_id("bv");
m_array_fid = m().mk_family_id("array");
m_dt_fid = m().mk_family_id("datatype");
m_datalog_fid = m().mk_family_id("datalog_relation");
}
protected:
ast_manager &m_manager;
int occurs_in(ast var, ast e);
private:
ast mki(family_id fid, decl_kind sk, int n, raw_ast **args);
ast make(opr op, int n, raw_ast **args);
ast make(symb sym, int n, raw_ast **args);
int occurs_in1(stl_ext::hash_map<ast,bool> &occurs_in_memo, ast var, ast e);
bool solve_arith(const ast &v, const ast &x, const ast &y, ast &res);
ast cont_eq(stl_ext::hash_set<ast> &cont_eq_memo, bool truth, ast v, ast e);
ast subst(stl_ext::hash_map<ast,ast> &subst_memo, ast var, ast t, ast e);
family_id m_basic_fid;
family_id m_array_fid;
family_id m_arith_fid;
family_id m_bv_fid;
family_id m_dt_fid;
family_id m_datalog_fid;
arith_util m_arith_util;
};
#endif

5
src/interp/iz3params.pyg Normal file
View file

@ -0,0 +1,5 @@
def_module_params('interp',
description='interpolation parameters',
export=True,
params=(('profile', BOOL, False, '(INTERP) profile interpolation'),
))

189
src/interp/iz3pp.cpp Normal file
View file

@ -0,0 +1,189 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
iz3pp.cpp
Abstract:
Pretty-print interpolation problems
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
/* Copyright 2011 Microsoft Research. */
#include <assert.h>
#include <algorithm>
#include <stdio.h>
#include <fstream>
#include <sstream>
#include <set>
#include <iostream>
#include "iz3mgr.h"
#include "iz3pp.h"
#include "func_decl_dependencies.h"
#include"for_each_expr.h"
#include"ast_smt_pp.h"
#include"ast_smt2_pp.h"
#include"expr_functors.h"
#include"expr_abstract.h"
#ifndef WIN32
using namespace stl_ext;
#endif
#ifndef WIN32
// We promise not to use this for hash_map with range destructor
namespace stl_ext {
template <>
class hash<expr *> {
public:
size_t operator()(const expr *p) const {
return (size_t) p;
}
};
}
#endif
// TBD: algebraic data-types declarations will not be printed.
class free_func_visitor {
ast_manager& m;
func_decl_set m_funcs;
obj_hashtable<sort> m_sorts;
public:
free_func_visitor(ast_manager& m): m(m) {}
void operator()(var * n) { }
void operator()(app * n) {
m_funcs.insert(n->get_decl());
sort* s = m.get_sort(n);
if (s->get_family_id() == null_family_id) {
m_sorts.insert(s);
}
}
void operator()(quantifier * n) { }
func_decl_set& funcs() { return m_funcs; }
obj_hashtable<sort>& sorts() { return m_sorts; }
};
class iz3pp_helper : public iz3mgr {
public:
void print_tree(const ast &tree, hash_map<expr*,symbol> &cnames, std::ostream &out){
hash_map<expr*,symbol>::iterator foo = cnames.find(to_expr(tree.raw()));
if(foo != cnames.end()){
symbol nm = foo->second;
if (is_smt2_quoted_symbol(nm)) {
out << mk_smt2_quoted_symbol(nm);
}
else {
out << nm;
}
}
else if(op(tree) == And){
out << "(and";
int nargs = num_args(tree);
for(int i = 0; i < nargs; i++){
out << " ";
print_tree(arg(tree,i), cnames, out);
}
out << ")";
}
else if(op(tree) == Interp){
out << "(interp ";
print_tree(arg(tree,0), cnames, out);
out << ")";
}
else throw iz3pp_bad_tree();
}
iz3pp_helper(ast_manager &_m_manager)
: iz3mgr(_m_manager) {}
};
void iz3pp(ast_manager &m,
const ptr_vector<expr> &cnsts_vec,
expr *tree,
std::ostream& out) {
unsigned sz = cnsts_vec.size();
expr* const* cnsts = &cnsts_vec[0];
out << "(set-option :produce-interpolants true)\n";
free_func_visitor visitor(m);
expr_mark visited;
bool print_low_level = true; // m_params.print_low_level_smt2();
#define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env);
smt2_pp_environment_dbg env(m);
for (unsigned i = 0; i < sz; ++i) {
expr* e = cnsts[i];
for_each_expr(visitor, visited, e);
}
// name all the constraints
hash_map<expr *, symbol> cnames;
int ctr = 1;
for(unsigned i = 0; i < sz; i++){
symbol nm;
std::ostringstream s;
s << "f!" << (ctr++);
cnames[cnsts[i]] = symbol(s.str().c_str());
}
func_decl_set &funcs = visitor.funcs();
func_decl_set::iterator it = funcs.begin(), end = funcs.end();
obj_hashtable<sort>& sorts = visitor.sorts();
obj_hashtable<sort>::iterator sit = sorts.begin(), send = sorts.end();
for (; sit != send; ++sit) {
PP(*sit);
}
for (; it != end; ++it) {
func_decl* f = *it;
if(f->get_family_id() == null_family_id){
PP(f);
out << "\n";
}
}
for (unsigned i = 0; i < sz; ++i) {
out << "(assert ";
expr* r = cnsts[i];
symbol nm = cnames[r];
out << "(! ";
PP(r);
out << " :named ";
if (is_smt2_quoted_symbol(nm)) {
out << mk_smt2_quoted_symbol(nm);
}
else {
out << nm;
}
out << ")";
out << ")\n";
}
out << "(check-sat)\n";
out << "(get-interpolant ";
iz3pp_helper pp(m);
pp.print_tree(pp.cook(tree),cnames,out);
out << ")\n";
}

35
src/interp/iz3pp.h Normal file
View file

@ -0,0 +1,35 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
iz3pp.cpp
Abstract:
Pretty-print interpolation problems
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3_PP_H
#define IZ3_PP_H
#include "iz3mgr.h"
/** Exception thrown in case of mal-formed tree interpoloation
specification */
struct iz3pp_bad_tree {
};
void iz3pp(ast_manager &m,
const ptr_vector<expr> &cnsts_vec,
expr *tree,
std::ostream& out);
#endif

146
src/interp/iz3profiling.cpp Executable file
View file

@ -0,0 +1,146 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3profiling.h
Abstract:
Some routines for measuring performance.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include "iz3profiling.h"
#include <map>
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include "stopwatch.h"
// FIXME fill in these stubs
#define clock_t double
static double current_time()
{
static stopwatch sw;
static bool started = false;
if(!started){
sw.start();
started = true;
}
return sw.get_current_seconds();
}
static void output_time(std::ostream &os, clock_t time){
os << time;
}
namespace profiling {
void show_time(){
output_time(std::cout,current_time());
std::cout << "\n";
}
typedef std::map<const char*, struct node> nmap;
struct node {
std::string name;
clock_t time;
clock_t start_time;
nmap sub;
struct node *parent;
node();
} top;
node::node(){
time = 0;
parent = 0;
}
struct node *current;
struct init {
init(){
top.name = "TOTAL";
current = &top;
}
} initializer;
struct time_entry {
clock_t t;
time_entry(){t = 0;};
void add(clock_t incr){t += incr;}
};
struct ltstr
{
bool operator()(const char* s1, const char* s2) const
{
return strcmp(s1, s2) < 0;
}
};
typedef std::map<const char*, time_entry, ltstr> tmap;
static std::ostream *pfs;
void print_node(node &top, int indent, tmap &totals){
for(int i = 0; i < indent; i++) (*pfs) << " ";
(*pfs) << top.name;
int dots = 70 - 2 * indent - top.name.size();
for(int i = 0; i <dots; i++) (*pfs) << ".";
output_time(*pfs, top.time);
(*pfs) << std::endl;
if(indent != 0)totals[top.name.c_str()].add(top.time);
for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++)
print_node(it->second,indent+1,totals);
}
void print(std::ostream &os) {
pfs = &os;
top.time = 0;
for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++)
top.time += it->second.time;
tmap totals;
print_node(top,0,totals);
(*pfs) << "TOTALS:" << std::endl;
for(tmap::iterator it = totals.begin(); it != totals.end(); it++){
(*pfs) << (it->first) << " ";
output_time(*pfs, it->second.t);
(*pfs) << std::endl;
}
}
void timer_start(const char *name){
node &child = current->sub[name];
if(child.name.empty()){ // a new node
child.parent = current;
child.name = name;
}
child.start_time = current_time();
current = &child;
}
void timer_stop(const char *name){
if(current->name != name || !current->parent){
std::cerr << "imbalanced timer_start and timer_stop";
exit(1);
}
current->time += (current_time() - current->start_time);
current = current->parent;
}
}

37
src/interp/iz3profiling.h Executable file
View file

@ -0,0 +1,37 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3profiling.h
Abstract:
Some routines for measuring performance.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3PROFILING_H
#define IZ3PROFILING_H
#include <ostream>
namespace profiling {
/** Start a timer with given name */
void timer_start(const char *);
/** Stop a timer with given name */
void timer_stop(const char *);
/** Print out timings */
void print(std::ostream &s);
/** Show the current time. */
void show_time();
}
#endif

622
src/interp/iz3proof.cpp Executable file
View file

@ -0,0 +1,622 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3proof.cpp
Abstract:
This class defines a simple interpolating proof system.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include "iz3proof.h"
#include "iz3profiling.h"
#include<algorithm>
#include <iterator>
#include <iostream>
#include <sstream>
// #define FACTOR_INTERPS
// #define CHECK_PROOFS
void iz3proof::resolve(ast pivot, std::vector<ast> &cls1, const std::vector<ast> &cls2){
#ifdef CHECK_PROOFS
std::vector<ast> orig_cls1 = cls1;
#endif
ast neg_pivot = pv->mk_not(pivot);
bool found_pivot1 = false, found_pivot2 = false;
for(unsigned i = 0; i < cls1.size(); i++){
if(cls1[i] == neg_pivot){
cls1[i] = cls1.back();
cls1.pop_back();
found_pivot1 = true;
break;
}
}
{
std::set<ast> memo;
memo.insert(cls1.begin(),cls1.end());
for(unsigned j = 0; j < cls2.size(); j++){
if(cls2[j] == pivot)
found_pivot2 = true;
else
if(memo.find(cls2[j]) == memo.end())
cls1.push_back(cls2[j]);
}
}
if(found_pivot1 && found_pivot2)
return;
#ifdef CHECK_PROOFS
std::cerr << "resolution anomaly: " << nodes.size()-1 << "\n";
#if 0
std::cerr << "pivot: "; {pv->print_lit(pivot); std::cout << "\n";}
std::cerr << "left clause:\n";
for(unsigned i = 0; i < orig_cls1.size(); i++)
{pv->print_lit(orig_cls1[i]); std::cout << "\n";}
std::cerr << "right clause:\n";
for(unsigned i = 0; i < cls2.size(); i++)
{pv->print_lit(cls2[i]); std::cout << "\n";}
throw proof_error();
#endif
#endif
}
iz3proof::node iz3proof::make_resolution(ast pivot, node premise1, node premise2)
{
if(nodes[premise1].rl == Hypothesis) return premise2; // resolve with hyp is noop
if(nodes[premise2].rl == Hypothesis) return premise1;
node res = make_node();
node_struct &n = nodes[res];
n.rl = Resolution;
n.aux = pivot;
n.premises.resize(2);
n.premises[0] = (premise1);
n.premises[1] = (premise2);
#ifdef CHECK_PROOFS
n.conclusion = nodes[premise1].conclusion;
resolve(pivot,n.conclusion,nodes[premise2].conclusion);
n.frame = 1;
#else
n.frame = 0; // compute conclusion lazily
#endif
return res;
}
iz3proof::node iz3proof::resolve_lemmas(ast pivot, node premise1, node premise2)
{
std::vector<ast> lits(nodes[premise1].conclusion), itp; // no interpolant
resolve(pivot,lits,nodes[premise2].conclusion);
return make_lemma(lits,itp);
}
iz3proof::node iz3proof::make_assumption(int frame, const std::vector<ast> &assumption){
#if 0
std::cout << "assumption: \n";
for(unsigned i = 0; i < assumption.size(); i++)
pv->show(assumption[i]);
std::cout << "\n";
#endif
node res = make_node();
node_struct &n = nodes[res];
n.rl = Assumption;
n.conclusion.resize(1);
n.conclusion = assumption;
n.frame = frame;
return res;
}
iz3proof::node iz3proof::make_hypothesis(ast hypothesis){
node res = make_node();
node_struct &n = nodes[res];
n.rl = Hypothesis;
n.conclusion.resize(2);
n.conclusion[0] = hypothesis;
n.conclusion[1] = pv->mk_not(hypothesis);
return res;
}
iz3proof::node iz3proof::make_theory(const std::vector<ast> &conclusion, std::vector<node> premises){
node res = make_node();
node_struct &n = nodes[res];
n.rl = Theory;
n.conclusion = conclusion;
n.premises = premises;
return res;
}
iz3proof::node iz3proof::make_axiom(const std::vector<ast> &conclusion){
node res = make_node();
node_struct &n = nodes[res];
n.rl = Axiom;
n.conclusion = conclusion;
return res;
}
iz3proof::node iz3proof::make_contra(node prem, const std::vector<ast> &conclusion){
node res = make_node();
node_struct &n = nodes[res];
n.rl = Contra;
n.conclusion = conclusion;
#ifdef CHECK_PROOFS
//if(!(conclusion == nodes[prem].conclusion)){
//std::cerr << "internal error: proof error\n";
//assert(0 && "proof error");
//}
#endif
n.premises.push_back(prem);
return res;
}
iz3proof::node iz3proof::make_lemma(const std::vector<ast> &conclusion, const std::vector<ast> &interpolation){
node res = make_node();
node_struct &n = nodes[res];
n.rl = Lemma;
n.conclusion = conclusion;
n.frame = interps.size();
interps.push_back(interpolation);
return res;
}
/** Make a Reflexivity node. This rule produces |- x = x */
iz3proof::node iz3proof::make_reflexivity(ast con){
node res = make_node();
node_struct &n = nodes[res];
n.rl = Reflexivity;
n.conclusion.push_back(con);
return res;
}
/** Make a Symmetry node. This takes a derivation of |- x = y and
produces | y = x */
iz3proof::node iz3proof::make_symmetry(ast con, node prem){
node res = make_node();
node_struct &n = nodes[res];
n.rl = Reflexivity;
n.conclusion.push_back(con);
n.premises.push_back(prem);
return res;
}
/** Make a transitivity node. This takes derivations of |- x = y
and |- y = z produces | x = z */
iz3proof::node iz3proof::make_transitivity(ast con, node prem1, node prem2){
node res = make_node();
node_struct &n = nodes[res];
n.rl = Transitivity;
n.conclusion.push_back(con);
n.premises.push_back(prem1);
n.premises.push_back(prem2);
return res;
}
/** Make a congruence node. This takes derivations of |- x_i = y_i
and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */
iz3proof::node iz3proof::make_congruence(ast con, const std::vector<node> &prems){
node res = make_node();
node_struct &n = nodes[res];
n.rl = Congruence;
n.conclusion.push_back(con);
n.premises = prems;
return res;
}
/** Make an equality contradicition node. This takes |- x = y
and |- !(x = y) and produces false. */
iz3proof::node iz3proof::make_eqcontra(node prem1, node prem2){
node res = make_node();
node_struct &n = nodes[res];
n.rl = EqContra;
n.premises.push_back(prem1);
n.premises.push_back(prem2);
return res;
}
iz3proof::node iz3proof::copy_rec(stl_ext::hash_map<node,node> &memo, iz3proof &src, node n){
stl_ext::hash_map<node,node>::iterator it = memo.find(n);
if(it != memo.end()) return (*it).second;
node_struct &ns = src.nodes[n];
std::vector<node> prems(ns.premises.size());
for(unsigned i = 0; i < prems.size(); i++)
prems[i] = copy_rec(memo,src,ns.premises[i]);
nodes.push_back(ns);
nodes.back().premises.swap(prems);
if(ns.rl == Lemma){
nodes.back().frame = interps.size();
interps.push_back(src.interps[ns.frame]);
}
int res = nodes.size()-1;
memo[n] = res;
return res;
}
iz3proof::node iz3proof::copy(iz3proof &src, node n){
stl_ext::hash_map<node,node> memo;
return copy_rec(memo, src, n);
}
bool iz3proof::pred_in_A(ast id){
return weak
? pv->ranges_intersect(pv->ast_range(id),rng) :
pv->range_contained(pv->ast_range(id),rng);
}
bool iz3proof::term_in_B(ast id){
prover::range r = pv->ast_scope(id);
if(weak) {
if(pv->range_min(r) == SHRT_MIN)
return !pv->range_contained(r,rng);
else
return !pv->ranges_intersect(r,rng);
}
else
return !pv->range_contained(r,rng);
}
bool iz3proof::frame_in_A(int frame){
return pv->in_range(frame,rng);
}
bool iz3proof::lit_in_B(ast lit){
return
b_lits.find(lit) != b_lits.end()
|| b_lits.find(pv->mk_not(lit)) != b_lits.end();
}
iz3proof::ast iz3proof::my_or(ast x, ast y){
return pv->mk_not(pv->mk_and(pv->mk_not(x),pv->mk_not(y)));
}
iz3proof::ast iz3proof::get_A_lits(std::vector<ast> &cls){
ast foo = pv->mk_false();
for(unsigned i = 0; i < cls.size(); i++){
ast lit = cls[i];
if(b_lits.find(pv->mk_not(lit)) == b_lits.end()){
if(pv->range_max(pv->ast_scope(lit)) == pv->range_min(pv->ast_scope(lit))){
std::cout << "bad lit: " << pv->range_max(rng) << " : " << pv->range_max(pv->ast_scope(lit)) << " : " << (pv->ast_id(lit)) << " : ";
pv->show(lit);
}
foo = my_or(foo,lit);
}
}
return foo;
}
iz3proof::ast iz3proof::get_B_lits(std::vector<ast> &cls){
ast foo = pv->mk_false();
for(unsigned i = 0; i < cls.size(); i++){
ast lit = cls[i];
if(b_lits.find(pv->mk_not(lit)) != b_lits.end())
foo = my_or(foo,lit);
}
return foo;
}
void iz3proof::set_of_B_lits(std::vector<ast> &cls, std::set<ast> &res){
for(unsigned i = 0; i < cls.size(); i++){
ast lit = cls[i];
if(b_lits.find(pv->mk_not(lit)) != b_lits.end())
res.insert(lit);
}
}
void iz3proof::set_of_A_lits(std::vector<ast> &cls, std::set<ast> &res){
for(unsigned i = 0; i < cls.size(); i++){
ast lit = cls[i];
if(b_lits.find(pv->mk_not(lit)) == b_lits.end())
res.insert(lit);
}
}
void iz3proof::find_B_lits(){
b_lits.clear();
for(unsigned i = 0; i < nodes.size(); i++){
node_struct &n = nodes[i];
std::vector<ast> &cls = n.conclusion;
if(n.rl == Assumption){
if(weak) goto lemma;
if(!frame_in_A(n.frame))
for(unsigned j = 0; j < cls.size(); j++)
b_lits.insert(cls[j]);
}
else if(n.rl == Lemma) {
lemma:
for(unsigned j = 0; j < cls.size(); j++)
if(term_in_B(cls[j]))
b_lits.insert(cls[j]);
}
}
}
iz3proof::ast iz3proof::disj_of_set(std::set<ast> &s){
ast res = pv->mk_false();
for(std::set<ast>::iterator it = s.begin(), en = s.end(); it != en; ++it)
res = my_or(*it,res);
return res;
}
void iz3proof::mk_and_factor(int p1, int p2, int i, std::vector<ast> &itps, std::vector<std::set<ast> > &disjs){
#ifdef FACTOR_INTERPS
std::set<ast> &d1 = disjs[p1];
std::set<ast> &d2 = disjs[p2];
if(!weak){
if(pv->is_true(itps[p1])){
itps[i] = itps[p2];
disjs[i] = disjs[p2];
}
else if(pv->is_true(itps[p2])){
itps[i] = itps[p1];
disjs[i] = disjs[p1];
}
else {
std::set<ast> p1only,p2only;
std::insert_iterator<std::set<ast> > p1o(p1only,p1only.begin());
std::insert_iterator<std::set<ast> > p2o(p2only,p2only.begin());
std::insert_iterator<std::set<ast> > dio(disjs[i],disjs[i].begin());
std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o);
std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o);
std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio);
ast p1i = my_or(itps[p1],disj_of_set(p1only));
ast p2i = my_or(itps[p2],disj_of_set(p2only));
itps[i] = pv->mk_and(p1i,p2i);
}
}
else {
itps[i] = pv->mk_and(itps[p1],itps[p2]);
std::insert_iterator<std::set<ast> > dio(disjs[i],disjs[i].begin());
std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio);
}
#endif
}
void iz3proof::mk_or_factor(int p1, int p2, int i, std::vector<ast> &itps, std::vector<std::set<ast> > &disjs){
#ifdef FACTOR_INTERPS
std::set<ast> &d1 = disjs[p1];
std::set<ast> &d2 = disjs[p2];
if(weak){
if(pv->is_false(itps[p1])){
itps[i] = itps[p2];
disjs[i] = disjs[p2];
}
else if(pv->is_false(itps[p2])){
itps[i] = itps[p1];
disjs[i] = disjs[p1];
}
else {
std::set<ast> p1only,p2only;
std::insert_iterator<std::set<ast> > p1o(p1only,p1only.begin());
std::insert_iterator<std::set<ast> > p2o(p2only,p2only.begin());
std::insert_iterator<std::set<ast> > dio(disjs[i],disjs[i].begin());
std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o);
std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o);
std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio);
ast p1i = pv->mk_and(itps[p1],pv->mk_not(disj_of_set(p1only)));
ast p2i = pv->mk_and(itps[p2],pv->mk_not(disj_of_set(p2only)));
itps[i] = my_or(p1i,p2i);
}
}
else {
itps[i] = my_or(itps[p1],itps[p2]);
std::insert_iterator<std::set<ast> > dio(disjs[i],disjs[i].begin());
std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio);
}
#endif
}
void iz3proof::interpolate_lemma(node_struct &n){
if(interps[n.frame].size())
return; // already computed
pv->interpolate_clause(n.conclusion,interps[n.frame]);
}
iz3proof::ast iz3proof::interpolate(const prover::range &_rng, bool _weak
#ifdef CHECK_PROOFS
, ast assump
, std::vector<int> *parents
#endif
){
// std::cout << "proof size: " << nodes.size() << "\n";
rng = _rng;
weak = _weak;
#ifdef CHECK_PROOFS
if(nodes[nodes.size()-1].conclusion.size() != 0)
std::cerr << "internal error: proof conclusion is not empty clause\n";
if(!child_interps.size()){
child_interps.resize(nodes.size());
for(unsigned j = 0; j < nodes.size(); j++)
child_interps[j] = pv->mk_true();
}
#endif
std::vector<ast> itps(nodes.size());
#ifdef FACTOR_INTERPS
std::vector<std::set<ast> > disjs(nodes.size());
#endif
profiling::timer_start("Blits");
find_B_lits();
profiling::timer_stop("Blits");
profiling::timer_start("interp_proof");
// strengthen();
for(unsigned i = 0; i < nodes.size(); i++){
node_struct &n = nodes[i];
ast &q = itps[i];
switch(n.rl){
case Assumption: {
if(frame_in_A(n.frame)){
/* HypC-A */
if(!weak)
#ifdef FACTOR_INTERPS
{
q = pv->mk_false();
set_of_B_lits(n.conclusion,disjs[i]);
}
#else
q = get_B_lits(n.conclusion);
#endif
else
q = pv->mk_false();
}
else {
/* HypEq-B */
if(!weak)
q = pv->mk_true();
else
#ifdef FACTOR_INTERPS
{
q = pv->mk_true();
set_of_A_lits(n.conclusion,disjs[i]);
}
#else
q = pv->mk_not(get_A_lits(n.conclusion));
#endif
}
break;
}
case Resolution: {
ast p = n.aux;
p = pv->is_not(p) ? pv->mk_not(p) : p; // should be positive, but just in case
if(lit_in_B(p))
#ifdef FACTOR_INTERPS
mk_and_factor(n.premises[0],n.premises[1],i,itps,disjs);
#else
q = pv->mk_and(itps[n.premises[0]],itps[n.premises[1]]);
#endif
else
#ifdef FACTOR_INTERPS
mk_or_factor(n.premises[0],n.premises[1],i,itps,disjs);
#else
q = my_or(itps[n.premises[0]],itps[n.premises[1]]);
#endif
break;
}
case Lemma: {
interpolate_lemma(n); // make sure lemma interpolants have been computed
q = interps[n.frame][pv->range_max(rng)];
break;
}
case Contra: {
q = itps[n.premises[0]];
#ifdef FACTOR_INTERPS
disjs[i] = disjs[n.premises[0]];
#endif
break;
}
default:
assert(0 && "rule not allowed in interpolated proof");
}
#ifdef CHECK_PROOFS
int this_frame = pv->range_max(rng);
if(0 && this_frame == 39) {
std::vector<ast> alits;
ast s = pv->mk_true();
for(unsigned j = 0; j < n.conclusion.size(); j++)
if(pred_in_A(n.conclusion[j])){
int scpmax = pv->range_max(pv->ast_scope(n.conclusion[j]));
if(scpmax == this_frame)
s = pv->mk_and(s,pv->mk_not(n.conclusion[j]));
}
ast ci = child_interps[i];
s = pv->mk_and(pv->mk_and(s,pv->mk_and(assump,pv->mk_not(q))),ci);
if(pv->is_sat(s)){
std::cout << "interpolation invariant violated at step " << i << "\n";
assert(0 && "interpolation invariant violated");
}
}
if((*parents)[this_frame] == 39)
child_interps[i] = pv->mk_and(child_interps[i],q);
#endif
}
ast &bar = itps[nodes.size()-1];
#ifdef FACTOR_INTERPS
if(!weak)
bar = my_or(bar,disj_of_set(disjs[nodes.size()-1]));
else
bar = pv->mk_and(bar,pv->mk_not(disj_of_set(disjs[nodes.size()-1])));
#endif
profiling::timer_stop("interp_proof");
profiling::timer_start("simplifying");
bar = pv->simplify(bar);
profiling::timer_stop("simplifying");
return bar;
}
void iz3proof::print(std::ostream &s, int id){
node_struct &n = nodes[id];
switch(n.rl){
case Assumption:
s << "Assumption(";
pv->print_clause(s,n.conclusion);
s << ")";
break;
case Hypothesis:
s << "Hyp("; pv->print_expr(s,n.conclusion[0]); s << ")"; break;
case Reflexivity:
s << "Refl("; pv->print_expr(s,n.conclusion[0]); s << ")"; break;
case Symmetry:
s << "Symm("; print(s,n.premises[0]); s << ")"; break;
case Transitivity:
s << "Trans("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break;
case Congruence:
s << "Cong("; pv->print_expr(s,n.conclusion[0]);
for(unsigned i = 0; i < n.premises.size(); i++){
s << ",";
print(s,n.premises[i]);
}
s << ")"; break;
case EqContra:
s << "EqContra("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break;
case Resolution:
s << "Res(";
pv->print_expr(s,n.aux); s << ",";
print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")";
break;
case Lemma:
s << "Lemma(";
pv->print_clause(s,n.conclusion);
for(unsigned i = 0; i < n.premises.size(); i++){
s << ",";
print(s,n.premises[i]);
}
s << ")";
break;
case Contra:
s << "Contra(";
print(s,n.premises[0]);
s << ")";
break;
default:;
}
}
void iz3proof::show(int id){
std::ostringstream ss;
print(ss,id);
iz3base::pretty_print(std::cout,ss.str());
// std::cout << ss.str();
std::cout << "\n";
}

272
src/interp/iz3proof.h Executable file
View file

@ -0,0 +1,272 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3proof.h
Abstract:
This class defines a simple interpolating proof system.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3PROOF_H
#define IZ3PROOF_H
#include <set>
#include "iz3base.h"
#include "iz3secondary.h"
// #define CHECK_PROOFS
/** This class defines a simple proof system.
A proof is a dag consisting of "nodes". The children of each node
are its "premises". Each node has a "conclusion" that is a clause,
represented as a vector of literals.
The literals are represented by abstract syntax trees. Operations
on these, including computation of scopes are provided by iz3base.
A proof can be interpolated, provided it is restricted to the
rules Resolution, Assumption, Contra and Lemma, and that all
clauses are strict (i.e., each literal in each clause is local).
*/
class iz3proof {
public:
/** The type of proof nodes (nodes in the derivation tree). */
typedef int node;
/** Enumeration of proof rules. */
enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra};
/** Interface to prover. */
typedef iz3base prover;
/** Ast type. */
typedef prover::ast ast;
/** Object thrown in case of a proof error. */
struct proof_error {};
/* Null proof node */
static const node null = -1;
/** Make a resolution node with given pivot liter and premises.
The conclusion of premise1 should contain the negation of the
pivot literal, while the conclusion of premise2 should containe the
pivot literal.
*/
node make_resolution(ast pivot, node premise1, node premise2);
/** Make an assumption node. The given clause is assumed in the given frame. */
node make_assumption(int frame, const std::vector<ast> &assumption);
/** Make a hypothesis node. If phi is the hypothesis, this is
effectively phi |- phi. */
node make_hypothesis(ast hypothesis);
/** Make a theory node. This can be any inference valid in the theory. */
node make_theory(const std::vector<ast> &conclusion, std::vector<node> premises);
/** Make an axiom node. The conclusion must be an instance of an axiom. */
node make_axiom(const std::vector<ast> &conclusion);
/** Make a Contra node. This rule takes a derivation of the form
Gamma |- False and produces |- \/~Gamma. */
node make_contra(node prem, const std::vector<ast> &conclusion);
/** Make a lemma node. A lemma node must have an interpolation. */
node make_lemma(const std::vector<ast> &conclusion, const std::vector<ast> &interpolation);
/** Make a Reflexivity node. This rule produces |- x = x */
node make_reflexivity(ast con);
/** Make a Symmetry node. This takes a derivation of |- x = y and
produces | y = x */
node make_symmetry(ast con, node prem);
/** Make a transitivity node. This takes derivations of |- x = y
and |- y = z produces | x = z */
node make_transitivity(ast con, node prem1, node prem2);
/** Make a congruence node. This takes derivations of |- x_i = y_i
and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */
node make_congruence(ast con, const std::vector<node> &prems);
/** Make an equality contradicition node. This takes |- x = y
and |- !(x = y) and produces false. */
node make_eqcontra(node prem1, node prem2);
/** Get the rule of a node in a proof. */
rule get_rule(node n){
return nodes[n].rl;
}
/** Get the pivot of a resolution node. */
ast get_pivot(node n){
return nodes[n].aux;
}
/** Get the frame of an assumption node. */
int get_frame(node n){
return nodes[n].frame;
}
/** Get the number of literals of the conclusion of a node. */
int get_num_conclusion_lits(node n){
return get_conclusion(n).size();
}
/** Get the nth literal of the conclusion of a node. */
ast get_nth_conclusion_lit(node n, int i){
return get_conclusion(n)[i];
}
/** Get the conclusion of a node. */
void get_conclusion(node n, std::vector<ast> &result){
result = get_conclusion(n);
}
/** Get the number of premises of a node. */
int get_num_premises(node n){
return nodes[n].premises.size();
}
/** Get the nth premise of a node. */
int get_nth_premise(node n, int i){
return nodes[n].premises[i];
}
/** Get all the premises of a node. */
void get_premises(node n, std::vector<node> &result){
result = nodes[n].premises;
}
/** Create a new proof node, replacing the premises of an old
one. */
node clone(node n, std::vector<node> &premises){
if(premises == nodes[n].premises)
return n;
nodes.push_back(nodes[n]);
nodes.back().premises = premises;
return nodes.size()-1;
}
/** Copy a proof node from src */
node copy(iz3proof &src, node n);
/** Resolve two lemmas on a given literal. */
node resolve_lemmas(ast pivot, node left, node right);
/** Swap two proofs. */
void swap(iz3proof &other){
std::swap(pv,other.pv);
nodes.swap(other.nodes);
interps.swap(other.interps);
}
/** Compute an interpolant for a proof, where the "A" side is defined by
the given range of frames. Parameter "weak", when true, uses different
interpolation system that resutls in generally weaker interpolants.
*/
ast interpolate(const prover::range &_rng, bool weak = false
#ifdef CHECK_PROOFS
, Z3_ast assump = (Z3_ast)0, std::vector<int> *parents = 0
#endif
);
/** print proof node to a stream */
void print(std::ostream &s, node n);
/** show proof node on stdout */
void show(node n);
/** Construct a proof, with a given prover. */
iz3proof(prover *p){
pv = p;
}
/** Default constructor */
iz3proof(){pv = 0;}
protected:
struct node_struct {
rule rl;
ast aux;
int frame;
std::vector<ast> conclusion;
std::vector<node> premises;
};
std::vector<node_struct> nodes;
std::vector<std::vector<ast> > interps; // interpolations of lemmas
prover *pv;
node make_node(){
nodes.push_back(node_struct());
return nodes.size()-1;
}
void resolve(ast pivot, std::vector<ast> &cls1, const std::vector<ast> &cls2);
node copy_rec(stl_ext::hash_map<node,node> &memo, iz3proof &src, node n);
void interpolate_lemma(node_struct &n);
// lazily compute the result of resolution
// the node member "frame" indicates result is computed
const std::vector<ast> &get_conclusion(node x){
node_struct &n = nodes[x];
if(n.rl == Resolution && !n.frame){
n.conclusion = get_conclusion(n.premises[0]);
resolve(n.aux,n.conclusion,get_conclusion(n.premises[1]));
n.frame = 1;
}
return n.conclusion;
}
prover::range rng;
bool weak;
stl_ext::hash_set<ast> b_lits;
ast my_or(ast x, ast y);
#ifdef CHECK_PROOFS
std::vector<Z3_ast> child_interps;
#endif
bool pred_in_A(ast id);
bool term_in_B(ast id);
bool frame_in_A(int frame);
bool lit_in_B(ast lit);
ast get_A_lits(std::vector<ast> &cls);
ast get_B_lits(std::vector<ast> &cls);
void find_B_lits();
ast disj_of_set(std::set<ast> &s);
void mk_or_factor(int p1, int p2, int i, std::vector<ast> &itps, std::vector<std::set<ast> > &disjs);
void mk_and_factor(int p1, int p2, int i, std::vector<ast> &itps, std::vector<std::set<ast> > &disjs);
void set_of_B_lits(std::vector<ast> &cls, std::set<ast> &res);
void set_of_A_lits(std::vector<ast> &cls, std::set<ast> &res);
};
#endif

2676
src/interp/iz3proof_itp.cpp Normal file

File diff suppressed because it is too large Load diff

141
src/interp/iz3proof_itp.h Normal file
View file

@ -0,0 +1,141 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3proof.h
Abstract:
This class defines a simple interpolating proof system.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3PROOF_ITP_H
#define IZ3PROOF_ITP_H
#include <set>
#include "iz3base.h"
#include "iz3secondary.h"
// #define CHECK_PROOFS
/** This class defines a simple proof system.
As opposed to iz3proof, this class directly computes interpolants,
so the proof representation is just the interpolant itself.
*/
class iz3proof_itp : public iz3mgr {
public:
/** Enumeration of proof rules. */
enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra};
/** Interface to prover. */
typedef iz3base prover;
/** Ast type. */
typedef prover::ast ast;
/** The type of proof nodes (just interpolants). */
typedef ast node;
/** Object thrown in case of a proof error. */
struct proof_error {};
/** Make a resolution node with given pivot literal and premises.
The conclusion of premise1 should contain the negation of the
pivot literal, while the conclusion of premise2 should containe the
pivot literal.
*/
virtual node make_resolution(ast pivot, const std::vector<ast> &conc, node premise1, node premise2) = 0;
/** Make an assumption node. The given clause is assumed in the given frame. */
virtual node make_assumption(int frame, const std::vector<ast> &assumption) = 0;
/** Make a hypothesis node. If phi is the hypothesis, this is
effectively phi |- phi. */
virtual node make_hypothesis(const ast &hypothesis) = 0;
/** Make an axiom node. The conclusion must be an instance of an axiom. */
virtual node make_axiom(const std::vector<ast> &conclusion) = 0;
/** Make an axiom node. The conclusion must be an instance of an axiom. Localize axiom instance to range*/
virtual node make_axiom(const std::vector<ast> &conclusion, prover::range) = 0;
/** Make a Contra node. This rule takes a derivation of the form
Gamma |- False and produces |- \/~Gamma. */
virtual node make_contra(node prem, const std::vector<ast> &conclusion) = 0;
/** Make a Reflexivity node. This rule produces |- x = x */
virtual node make_reflexivity(ast con) = 0;
/** Make a Symmetry node. This takes a derivation of |- x = y and
produces | y = x */
virtual node make_symmetry(ast con, const ast &premcon, node prem) = 0;
/** Make a transitivity node. This takes derivations of |- x = y
and |- y = z produces | x = z */
virtual node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2) = 0;
/** Make a congruence node. This takes a derivation of |- x_i = y_i
and produces |- f(...x_i,...) = f(...,y_i,...) */
virtual node make_congruence(const ast &xi_eq_yi, const ast &con, const ast &prem1) = 0;
/** Make a congruence node. This takes derivations of |- x_i1 = y_i1, |- x_i2 = y_i2,...
and produces |- f(...x_i1...x_i2...) = f(...y_i1...y_i2...) */
virtual node make_congruence(const std::vector<ast> &xi_eq_yi, const ast &con, const std::vector<ast> &prems) = 0;
/** Make a modus-ponens node. This takes derivations of |- x
and |- x = y and produces |- y */
virtual node make_mp(const ast &x_eq_y, const ast &prem1, const ast &prem2) = 0;
/** Make a farkas proof node. */
virtual node make_farkas(ast con, const std::vector<node> &prems, const std::vector<ast> &prem_cons, const std::vector<ast> &coeffs) = 0;
/* Make an axiom instance of the form |- x<=y, y<= x -> x =y */
virtual node make_leq2eq(ast x, ast y, const ast &xleqy, const ast &yleqx) = 0;
/* Make an axiom instance of the form |- x = y -> x <= y */
virtual node make_eq2leq(ast x, ast y, const ast &xeqy) = 0;
/* Make an inference of the form t <= c |- t/d <= floor(c/d) where t
is an affine term divisble by d and c is an integer constant */
virtual node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem) = 0;
/* Return an interpolant from a proof of false */
virtual ast interpolate(const node &pf) = 0;
/** Create proof object to construct an interpolant. */
static iz3proof_itp *create(prover *p, const prover::range &r, bool _weak);
protected:
iz3proof_itp(iz3mgr &m)
: iz3mgr(m)
{
}
public:
virtual ~iz3proof_itp(){
}
};
#endif

321
src/interp/iz3scopes.cpp Executable file
View file

@ -0,0 +1,321 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3scopes.cpp
Abstract:
Calculations with scopes, for both sequence and tree interpolation.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#include <assert.h>
#include <algorithm>
#include "iz3scopes.h"
/** computes the least common ancestor of two nodes in the tree, or SHRT_MAX if none */
int scopes::tree_lca(int n1, int n2){
if(!tree_mode())
return std::max(n1,n2);
if(n1 == SHRT_MIN) return n2;
if(n2 == SHRT_MIN) return n1;
if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX;
while(n1 != n2){
if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX;
assert(n1 >= 0 && n2 >= 0 && n1 < (int)parents.size() && n2 < (int)parents.size());
if(n1 < n2) n1 = parents[n1];
else n2 = parents[n2];
}
return n1;
}
/** computes the greatest common descendant two nodes in the tree, or SHRT_MIN if none */
int scopes::tree_gcd(int n1, int n2){
if(!tree_mode())
return std::min(n1,n2);
int foo = tree_lca(n1,n2);
if(foo == n1) return n2;
if(foo == n2) return n1;
return SHRT_MIN;
}
#ifndef FULL_TREE
/** test whether a tree node is contained in a range */
bool scopes::in_range(int n, const range &rng){
return tree_lca(rng.lo,n) == n && tree_gcd(rng.hi,n) == n;
}
/** test whether two ranges of tree nodes intersect */
bool scopes::ranges_intersect(const range &rng1, const range &rng2){
return tree_lca(rng1.lo,rng2.hi) == rng2.hi && tree_lca(rng1.hi,rng2.lo) == rng1.hi;
}
bool scopes::range_contained(const range &rng1, const range &rng2){
return tree_lca(rng2.lo,rng1.lo) == rng1.lo
&& tree_lca(rng1.hi,rng2.hi) == rng2.hi;
}
scopes::range scopes::range_lub(const range &rng1, const range &rng2){
range res;
res.lo = tree_gcd(rng1.lo,rng2.lo);
res.hi = tree_lca(rng1.hi,rng2.hi);
return res;
}
scopes::range scopes::range_glb(const range &rng1, const range &rng2){
range res;
res.lo = tree_lca(rng1.lo,rng2.lo);
res.hi = tree_gcd(rng1.hi,rng2.hi);
return res;
}
#else
namespace std {
template <>
class hash<scopes::range_lo > {
public:
size_t operator()(const scopes::range_lo &p) const {
return p.lo + (size_t)p.next;
}
};
}
template <> inline
size_t stdext::hash_value<scopes::range_lo >(const scopes::range_lo& p)
{
std::hash<scopes::range_lo> h;
return h(p);
}
namespace std {
template <>
class less<scopes::range_lo > {
public:
bool operator()(const scopes::range_lo &x, const scopes::range_lo &y) const {
return x.lo < y.lo || x.lo == y.lo && (size_t)x.next < (size_t)y.next;
}
};
}
struct range_op {
scopes::range_lo *x, *y;
int hi;
range_op(scopes::range_lo *_x, scopes::range_lo *_y, int _hi){
x = _x; y = _y; hi = _hi;
}
};
namespace std {
template <>
class hash<range_op > {
public:
size_t operator()(const range_op &p) const {
return (size_t) p.x + (size_t)p.y + p.hi;
}
};
}
template <> inline
size_t stdext::hash_value<range_op >(const range_op& p)
{
std::hash<range_op> h;
return h(p);
}
namespace std {
template <>
class less<range_op > {
public:
bool operator()(const range_op &x, const range_op &y) const {
return (size_t)x.x < (size_t)y.x || x.x == y.x &&
((size_t)x.y < (size_t)y.y || x.y == y.y && x.hi < y.hi);
}
};
}
struct range_tables {
hash_map<scopes::range_lo, scopes::range_lo *> unique;
hash_map<range_op,scopes::range_lo *> lub;
hash_map<range_op,scopes::range_lo *> glb;
};
scopes::range_lo *scopes::find_range_lo(int lo, range_lo *next){
range_lo foo(lo,next);
std::pair<range_lo,range_lo *> baz(foo,(range_lo *)0);
std::pair<hash_map<range_lo,scopes::range_lo *>::iterator,bool> bar = rt->unique.insert(baz);
if(bar.second)
bar.first->second = new range_lo(lo,next);
return bar.first->second;
//std::pair<hash_set<scopes::range_lo>::iterator,bool> bar = rt->unique.insert(foo);
// const range_lo *baz = &*(bar.first);
// return (range_lo *)baz; // exit const hell
}
scopes::range_lo *scopes::range_lub_lo(range_lo *rng1, range_lo *rng2){
if(!rng1) return rng2;
if(!rng2) return rng1;
if(rng1->lo > rng2->lo)
std::swap(rng1,rng2);
std::pair<range_op,range_lo *> foo(range_op(rng1,rng2,0),(range_lo *)0);
std::pair<hash_map<range_op,scopes::range_lo *>::iterator,bool> bar = rt->lub.insert(foo);
range_lo *&res = bar.first->second;
if(!bar.second) return res;
if(!(rng1->next && rng1->next->lo <= rng2->lo)){
for(int lo = rng1->lo; lo <= rng2->lo; lo = parents[lo])
if(lo == rng2->lo)
{rng2 = rng2->next; break;}
}
range_lo *baz = range_lub_lo(rng1->next,rng2);
res = find_range_lo(rng1->lo,baz);
return res;
}
scopes::range_lo *scopes::range_glb_lo(range_lo *rng1, range_lo *rng2, int hi){
if(!rng1) return rng1;
if(!rng2) return rng2;
if(rng1->lo > rng2->lo)
std::swap(rng1,rng2);
std::pair<range_op,range_lo *> cand(range_op(rng1,rng2,hi),(range_lo *)0);
std::pair<hash_map<range_op,scopes::range_lo *>::iterator,bool> bar = rt->glb.insert(cand);
range_lo *&res = bar.first->second;
if(!bar.second) return res;
range_lo *foo;
if(!(rng1->next && rng1->next->lo <= rng2->lo)){
int lim = hi;
if(rng1->next) lim = std::min(lim,rng1->next->lo);
int a = rng1->lo, b = rng2->lo;
while(a != b && b <= lim){
a = parents[a];
if(a > b)std::swap(a,b);
}
if(a == b && b <= lim){
foo = range_glb_lo(rng1->next,rng2->next,hi);
foo = find_range_lo(b,foo);
}
else
foo = range_glb_lo(rng2,rng1->next,hi);
}
else foo = range_glb_lo(rng1->next,rng2,hi);
res = foo;
return res;
}
/** computes the lub (smallest containing subtree) of two ranges */
scopes::range scopes::range_lub(const range &rng1, const range &rng2){
int hi = tree_lca(rng1.hi,rng2.hi);
if(hi == SHRT_MAX) return range_full();
range_lo *lo = range_lub_lo(rng1.lo,rng2.lo);
return range(hi,lo);
}
/** computes the glb (intersection) of two ranges */
scopes::range scopes::range_glb(const range &rng1, const range &rng2){
if(rng1.hi == SHRT_MAX) return rng2;
if(rng2.hi == SHRT_MAX) return rng1;
int hi = tree_gcd(rng1.hi,rng2.hi);
range_lo *lo = hi == SHRT_MIN ? 0 : range_glb_lo(rng1.lo,rng2.lo,hi);
if(!lo) hi = SHRT_MIN;
return range(hi,lo);
}
/** is this range empty? */
bool scopes::range_is_empty(const range &rng){
return rng.hi == SHRT_MIN;
}
/** return an empty range */
scopes::range scopes::range_empty(){
return range(SHRT_MIN,0);
}
/** return a full range */
scopes::range scopes::range_full(){
return range(SHRT_MAX,0);
}
/** return the maximal element of a range */
int scopes::range_max(const range &rng){
return rng.hi;
}
/** return a minimal (not necessarily unique) element of a range */
int scopes::range_min(const range &rng){
if(rng.hi == SHRT_MAX) return SHRT_MIN;
return rng.lo ? rng.lo->lo : SHRT_MAX;
}
/** return range consisting of downward closure of a point */
scopes::range scopes::range_downward(int _hi){
std::vector<bool> descendants(parents.size());
for(int i = descendants.size() - 1; i >= 0 ; i--)
descendants[i] = i == _hi || parents[i] < parents.size() && descendants[parents[i]];
for(unsigned i = 0; i < descendants.size() - 1; i++)
if(parents[i] < parents.size())
descendants[parents[i]] = false;
range_lo *foo = 0;
for(int i = descendants.size() - 1; i >= 0; --i)
if(descendants[i]) foo = find_range_lo(i,foo);
return range(_hi,foo);
}
/** add an element to a range */
void scopes::range_add(int i, range &n){
range foo = range(i, find_range_lo(i,0));
n = range_lub(foo,n);
}
/** Choose an element of rng1 that is near to rng2 */
int scopes::range_near(const range &rng1, const range &rng2){
int frame;
int thing = tree_lca(rng1.hi,rng2.hi);
if(thing != rng1.hi) return rng1.hi;
range line = range(rng1.hi,find_range_lo(rng2.hi,(range_lo *)0));
line = range_glb(line,rng1);
return range_min(line);
}
/** test whether a tree node is contained in a range */
bool scopes::in_range(int n, const range &rng){
range r = range_empty();
range_add(n,r);
r = range_glb(rng,r);
return !range_is_empty(r);
}
/** test whether two ranges of tree nodes intersect */
bool scopes::ranges_intersect(const range &rng1, const range &rng2){
range r = range_glb(rng1,rng2);
return !range_is_empty(r);
}
bool scopes::range_contained(const range &rng1, const range &rng2){
range r = range_glb(rng1,rng2);
return r.hi == rng1.hi && r.lo == rng1.lo;
}
#endif

197
src/interp/iz3scopes.h Executable file
View file

@ -0,0 +1,197 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3scopes.h
Abstract:
Calculations with scopes, for both sequence and tree interpolation.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3SOPES_H
#define IZ3SOPES_H
#include <vector>
#include <limits.h>
class scopes {
public:
/** Construct from parents vector. */
scopes(const std::vector<int> &_parents){
parents = _parents;
}
scopes(){
}
void initialize(const std::vector<int> &_parents){
parents = _parents;
}
/** The parents vector defining the tree structure */
std::vector<int> parents;
// #define FULL_TREE
#ifndef FULL_TREE
struct range {
range(){
lo = SHRT_MAX;
hi = SHRT_MIN;
}
short lo, hi;
};
/** computes the lub (smallest containing subtree) of two ranges */
range range_lub(const range &rng1, const range &rng2);
/** computes the glb (intersection) of two ranges */
range range_glb(const range &rng1, const range &rng2);
/** is this range empty? */
bool range_is_empty(const range &rng){
return rng.hi < rng.lo;
}
/** return an empty range */
range range_empty(){
range res;
res.lo = SHRT_MAX;
res.hi = SHRT_MIN;
return res;
}
/** return an empty range */
range range_full(){
range res;
res.lo = SHRT_MIN;
res.hi = SHRT_MAX;
return res;
}
/** return the maximal element of a range */
int range_max(const range &rng){
return rng.hi;
}
/** return a minimal (not necessarily unique) element of a range */
int range_min(const range &rng){
return rng.lo;
}
/** return range consisting of downward closure of a point */
range range_downward(int _hi){
range foo;
foo.lo = SHRT_MIN;
foo.hi = _hi;
return foo;
}
void range_add(int i, range &n){
#if 0
if(i < n.lo) n.lo = i;
if(i > n.hi) n.hi = i;
#else
range rng; rng.lo = i; rng.hi = i;
n = range_lub(rng,n);
#endif
}
/** Choose an element of rng1 that is near to rng2 */
int range_near(const range &rng1, const range &rng2){
int frame;
int thing = tree_lca(rng1.lo,rng2.hi);
if(thing == rng1.lo) frame = rng1.lo;
else frame = tree_gcd(thing,rng1.hi);
return frame;
}
#else
struct range_lo {
int lo;
range_lo *next;
range_lo(int _lo, range_lo *_next){
lo = _lo;
next = _next;
}
};
struct range {
int hi;
range_lo *lo;
range(int _hi, range_lo *_lo){
hi = _hi;
lo = _lo;
}
range(){
hi = SHRT_MIN;
lo = 0;
}
};
range_tables *rt;
/** computes the lub (smallest containing subtree) of two ranges */
range range_lub(const range &rng1, const range &rng2);
/** computes the glb (intersection) of two ranges */
range range_glb(const range &rng1, const range &rng2);
/** is this range empty? */
bool range_is_empty(const range &rng);
/** return an empty range */
range range_empty();
/** return a full range */
range range_full();
/** return the maximal element of a range */
int range_max(const range &rng);
/** return a minimal (not necessarily unique) element of a range */
int range_min(const range &rng);
/** return range consisting of downward closure of a point */
range range_downward(int _hi);
/** add an element to a range */
void range_add(int i, range &n);
/** Choose an element of rng1 that is near to rng2 */
int range_near(const range &rng1, const range &rng2);
range_lo *find_range_lo(int lo, range_lo *next);
range_lo *range_lub_lo(range_lo *rng1, range_lo *rng2);
range_lo *range_glb_lo(range_lo *rng1, range_lo *rng2, int lim);
#endif
/** test whether a tree node is contained in a range */
bool in_range(int n, const range &rng);
/** test whether two ranges of tree nodes intersect */
bool ranges_intersect(const range &rng1, const range &rng2);
/** test whether range rng1 contained in range rng2 */
bool range_contained(const range &rng1, const range &rng2);
private:
int tree_lca(int n1, int n2);
int tree_gcd(int n1, int n2);
bool tree_mode(){return parents.size() != 0;}
};
#endif

40
src/interp/iz3secondary.h Executable file
View file

@ -0,0 +1,40 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3secondary
Abstract:
Interface for secondary provers.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3SECONDARY_H
#define IZ3SECONDARY_H
/** Interface class for secondary provers. */
#include "iz3base.h"
#include <vector>
class iz3secondary : public iz3mgr {
public:
virtual int interpolate(const std::vector<ast> &frames, std::vector<ast> &interpolants) = 0;
virtual ~iz3secondary(){}
protected:
iz3secondary(const iz3mgr &mgr) : iz3mgr(mgr) {}
};
#endif

1778
src/interp/iz3translate.cpp Executable file

File diff suppressed because it is too large Load diff

65
src/interp/iz3translate.h Executable file
View file

@ -0,0 +1,65 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
iz3translate.h
Abstract:
Interface for proof translations from Z3 proofs to interpolatable
proofs.
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#ifndef IZ3TRANSLATION_H
#define IZ3TRANSLATION_H
#include "iz3proof.h"
#include "iz3secondary.h"
// This is a interface class for translation from Z3 proof terms to
// an interpolatable proof
class iz3translation : public iz3base {
public:
virtual iz3proof::node translate(ast, iz3proof &) = 0;
virtual ast quantify(ast e, const range &rng){return e;}
virtual ~iz3translation(){}
/** This is thrown when the proof cannot be translated. */
struct unsupported {
};
static iz3translation *create(iz3mgr &mgr,
iz3secondary *secondary,
const std::vector<std::vector<ast> > &frames,
const std::vector<int> &parents,
const std::vector<ast> &theory);
protected:
iz3translation(iz3mgr &mgr,
const std::vector<std::vector<ast> > &_cnsts,
const std::vector<int> &_parents,
const std::vector<ast> &_theory)
: iz3base(mgr,_cnsts,_parents,_theory) {}
};
//#define IZ3_TRANSLATE_DIRECT2
#ifdef _FOCI2
#define IZ3_TRANSLATE_DIRECT
#else
#define IZ3_TRANSLATE_FULL
#endif
#endif

1767
src/interp/iz3translate_direct.cpp Executable file

File diff suppressed because it is too large Load diff

View file

@ -179,6 +179,7 @@ namespace datalog {
void context::push() {
m_trail.push_scope();
m_trail.push(restore_rules(m_rule_set));
m_trail.push(restore_vec_size_trail<context,expr_ref_vector>(m_rule_fmls));
m_trail.push(restore_vec_size_trail<context,expr_ref_vector>(m_background));
}
@ -186,6 +187,10 @@ namespace datalog {
if (m_trail.get_num_scopes() == 0) {
throw default_exception("there are no backtracking points to pop to");
}
if(m_engine.get()){
if(get_engine() != DUALITY_ENGINE)
throw default_exception("operation is not supported by engine");
}
m_trail.pop_scope(1);
}
@ -699,6 +704,10 @@ namespace datalog {
check_existential_tail(r);
check_positive_predicates(r);
break;
case DUALITY_ENGINE:
check_existential_tail(r);
check_positive_predicates(r);
break;
case CLP_ENGINE:
check_existential_tail(r);
check_positive_predicates(r);
@ -920,6 +929,9 @@ namespace datalog {
else if (e == symbol("clp")) {
m_engine_type = CLP_ENGINE;
}
else if (e == symbol("duality")) {
m_engine_type = DUALITY_ENGINE;
}
if (m_engine_type == LAST_ENGINE) {
expr_fast_mark1 mark;
@ -945,6 +957,18 @@ namespace datalog {
}
lbool context::query(expr* query) {
#if 0
// TODO: what?
if(get_engine() != DUALITY_ENGINE) {
new_query();
rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end();
rule_ref r(m_rule_manager);
for (; it != end; ++it) {
r = *it;
check_rule(r);
}
}
#endif
m_mc = mk_skip_model_converter();
m_last_status = OK;
m_last_answer = 0;
@ -958,6 +982,8 @@ namespace datalog {
case CLP_ENGINE:
flush_add_rules();
break;
case DUALITY_ENGINE:
break;
default:
UNREACHABLE();
}
@ -983,6 +1009,7 @@ namespace datalog {
if (get_engine() == DATALOG_ENGINE) {
m_rel = dynamic_cast<rel_context_base*>(m_engine.get());
}
}
}
@ -1014,7 +1041,6 @@ namespace datalog {
out << "\n---------------\n";
out << "Original rules\n";
display_rules(out);
out << "\n---------------\n";
out << "Transformed rules\n";
m_transformed_rule_set.display(out);
@ -1076,6 +1102,15 @@ namespace datalog {
}
}
void context::get_raw_rule_formulas(expr_ref_vector& rules, svector<symbol>& names){
for (unsigned i = 0; i < m_rule_fmls.size(); ++i) {
expr_ref r = bind_variables(m_rule_fmls[i].get(), true);
rules.push_back(r.get());
// rules.push_back(m_rule_fmls[i].get());
names.push_back(m_rule_names[i]);
}
}
void context::get_rules_as_formulas(expr_ref_vector& rules, svector<symbol>& names) {
expr_ref fml(m);
datalog::rule_manager& rm = get_rule_manager();

View file

@ -357,6 +357,7 @@ namespace datalog {
rule_set & get_rules() { flush_add_rules(); return m_rule_set; }
void get_rules_as_formulas(expr_ref_vector& fmls, svector<symbol>& names);
void get_raw_rule_formulas(expr_ref_vector& fmls, svector<symbol>& names);
void add_fact(app * head);
void add_fact(func_decl * pred, const relation_fact & fact);
@ -492,14 +493,16 @@ namespace datalog {
/**
\brief retrieve model from inductive invariant that shows query is unsat.
\pre engine == 'pdr' - this option is only supported for PDR mode.
\pre engine == 'pdr' || engine == 'duality' - this option is only supported
for PDR mode and Duality mode.
*/
model_ref get_model();
/**
\brief retrieve proof from derivation of the query.
\pre engine == 'pdr' - this option is only supported for PDR mode.
\pre engine == 'pdr' || engine == 'duality'- this option is only supported
for PDR mode and Duality mode.
*/
proof_ref get_proof();

View file

@ -30,7 +30,8 @@ namespace datalog {
QBMC_ENGINE,
TAB_ENGINE,
CLP_ENGINE,
LAST_ENGINE
LAST_ENGINE,
DUALITY_ENGINE
};
class engine_base {

View file

@ -57,7 +57,6 @@ namespace datalog {
LAST_CACHE_MODE
};
struct std_string_hash_proc {
unsigned operator()(const std::string & s) const
{ return string_hash(s.c_str(), static_cast<unsigned>(s.length()), 17); }

View file

@ -67,6 +67,13 @@ def_module_params('fixedpoint',
('print_statistics', BOOL, False, 'print statistics'),
('use_utvpi', BOOL, True, 'PDR: Enable UTVPI strategy'),
('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'),
('full_expand', BOOL, False, 'DUALITY: Fully expand derivation trees'),
('no_conj', BOOL, False, 'DUALITY: No forced covering (conjectures)'),
('feasible_edges', BOOL, True, 'DUALITY: Don\'t expand definitley infeasible edges'),
('use_underapprox', BOOL, False, 'DUALITY: Use underapproximations'),
('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'),
('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'),
('profile', BOOL, False, 'DUALITY: profile run time'),
('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'),
))

View file

@ -0,0 +1,493 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
duality_dl_interface.cpp
Abstract:
SMT2 interface for Duality
Author:
Krystof Hoder (t-khoder) 2011-9-22.
Modified by Ken McMIllan (kenmcmil) 2013-4-18.
Revision History:
--*/
#include "dl_context.h"
#include "dl_mk_coi_filter.h"
#include "dl_mk_interp_tail_simplifier.h"
#include "dl_mk_subsumption_checker.h"
#include "dl_mk_rule_inliner.h"
#include "dl_rule.h"
#include "dl_rule_transformer.h"
#include "smt2parser.h"
#include "duality_dl_interface.h"
#include "dl_rule_set.h"
#include "dl_mk_slice.h"
#include "dl_mk_unfold.h"
#include "dl_mk_coalesce.h"
#include "expr_abstract.h"
#include "model_smt2_pp.h"
#include "model_v2_pp.h"
#include "fixedpoint_params.hpp"
#include "scoped_proof.h"
// template class symbol_table<family_id>;
#include "duality.h"
#include "duality_profiling.h"
// using namespace Duality;
namespace Duality {
enum DualityStatus {StatusModel, StatusRefutation, StatusUnknown, StatusNull};
class duality_data {
public:
context ctx;
RPFP::LogicSolver *ls;
RPFP *rpfp;
DualityStatus status;
std::vector<expr> clauses;
std::vector<std::vector<RPFP::label_struct> > clause_labels;
hash_map<RPFP::Edge *,int> map; // edges to clauses
Solver::Counterexample cex;
duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) {
ls = 0;
rpfp = 0;
status = StatusNull;
}
~duality_data(){
if(rpfp)
dealloc(rpfp);
if(ls)
dealloc(ls);
if(cex.tree)
delete cex.tree;
}
};
dl_interface::dl_interface(datalog::context& dl_ctx) :
engine_base(dl_ctx.get_manager(), "duality"),
m_ctx(dl_ctx)
{
_d = 0;
// dl_ctx.get_manager().toggle_proof_mode(PGM_FINE);
}
dl_interface::~dl_interface() {
if(_d)
dealloc(_d);
}
//
// Check if the new rules are weaker so that we can
// re-use existing context.
//
#if 0
void dl_interface::check_reset() {
// TODO
datalog::rule_ref_vector const& new_rules = m_ctx.get_rules().get_rules();
datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules();
bool is_subsumed = !old_rules.empty();
for (unsigned i = 0; is_subsumed && i < new_rules.size(); ++i) {
is_subsumed = false;
for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) {
if (m_ctx.check_subsumes(*old_rules[j], *new_rules[i])) {
is_subsumed = true;
}
}
if (!is_subsumed) {
TRACE("pdr", new_rules[i]->display(m_ctx, tout << "Fresh rule "););
m_context->reset();
}
}
m_old_rules.reset();
m_old_rules.add_rules(new_rules.size(), new_rules.c_ptr());
}
#endif
lbool dl_interface::query(::expr * query) {
// we restore the initial state in the datalog context
m_ctx.ensure_opened();
// if there is old data, get the cex and dispose (later)
Solver::Counterexample old_cex;
duality_data *old_data = _d;
if(old_data)
old_cex = old_data->cex;
scoped_proof generate_proofs_please(m_ctx.get_manager());
// make a new problem and solver
_d = alloc(duality_data,m_ctx.get_manager());
_d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx);
_d->rpfp = alloc(RPFP,_d->ls);
expr_ref_vector rules(m_ctx.get_manager());
svector< ::symbol> names;
// m_ctx.get_rules_as_formulas(rules, names);
m_ctx.get_raw_rule_formulas(rules, names);
// get all the rules as clauses
std::vector<expr> &clauses = _d->clauses;
clauses.clear();
for (unsigned i = 0; i < rules.size(); ++i) {
expr e(_d->ctx,rules[i].get());
clauses.push_back(e);
}
// turn the query into a clause
expr q(_d->ctx,m_ctx.bind_variables(query,false));
std::vector<sort> b_sorts;
std::vector<symbol> b_names;
if (q.is_quantifier() && !q.is_quantifier_forall()) {
int bound = q.get_quantifier_num_bound();
for(int j = 0; j < bound; j++){
b_sorts.push_back(q.get_quantifier_bound_sort(j));
b_names.push_back(q.get_quantifier_bound_name(j));
}
q = q.arg(0);
}
expr qc = implies(q,_d->ctx.bool_val(false));
qc = _d->ctx.make_quant(Forall,b_sorts,b_names,qc);
clauses.push_back(qc);
// get the background axioms
unsigned num_asserts = m_ctx.get_num_assertions();
for (unsigned i = 0; i < num_asserts; ++i) {
expr e(_d->ctx,m_ctx.get_assertion(i));
_d->rpfp->AssertAxiom(e);
}
// creates 1-1 map between clauses and rpfp edges
_d->rpfp->FromClauses(clauses);
// populate the edge-to-clause map
for(unsigned i = 0; i < _d->rpfp->edges.size(); ++i)
_d->map[_d->rpfp->edges[i]] = i;
// create a solver object
Solver *rs = Solver::Create("duality", _d->rpfp);
rs->LearnFrom(old_cex); // new solver gets hints from old cex
// set its options
IF_VERBOSE(1, rs->SetOption("report","1"););
rs->SetOption("full_expand",m_ctx.get_params().full_expand() ? "1" : "0");
rs->SetOption("no_conj",m_ctx.get_params().no_conj() ? "1" : "0");
rs->SetOption("feasible_edges",m_ctx.get_params().feasible_edges() ? "1" : "0");
rs->SetOption("use_underapprox",m_ctx.get_params().use_underapprox() ? "1" : "0");
rs->SetOption("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "1" : "0");
unsigned rb = m_ctx.get_params().recursion_bound();
if(rb != UINT_MAX){
std::ostringstream os; os << rb;
rs->SetOption("recursion_bound", os.str());
}
// Solve!
bool ans;
try {
ans = rs->Solve();
}
catch (Duality::solver::cancel_exception &exn){
throw default_exception("duality canceled");
}
// profile!
if(m_ctx.get_params().profile())
print_profile(std::cout);
// save the result and counterexample if there is one
_d->status = ans ? StatusModel : StatusRefutation;
_d->cex = rs->GetCounterexample();
if(old_data){
old_data->cex.tree = 0; // we own it now
dealloc(old_data);
}
dealloc(rs);
// true means the RPFP problem is SAT, so the query is UNSAT
return ans ? l_false : l_true;
}
expr_ref dl_interface::get_cover_delta(int level, ::func_decl* pred_orig) {
SASSERT(false);
return expr_ref(m_ctx.get_manager());
}
void dl_interface::add_cover(int level, ::func_decl* pred, ::expr* property) {
SASSERT(false);
}
unsigned dl_interface::get_num_levels(::func_decl* pred) {
SASSERT(false);
return 0;
}
void dl_interface::collect_statistics(::statistics& st) const {
}
void dl_interface::reset_statistics() {
}
static hash_set<func_decl> *local_func_decls;
static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexample &cex) {
context &ctx = d->dd()->ctx;
RPFP::Node &node = *cex.root;
RPFP::Edge &edge = *node.Outgoing;
// first, prove the children (that are actually used)
for(unsigned i = 0; i < edge.Children.size(); i++){
if(!cex.tree->Empty(edge.Children[i])){
Solver::Counterexample foo = cex;
foo.root = edge.Children[i];
print_proof(d,out,foo);
}
}
// print the label and the proved fact
out << "(step s!" << node.number;
out << " (" << node.Name.name();
for(unsigned i = 0; i < edge.F.IndParams.size(); i++)
out << " " << cex.tree->Eval(&edge,edge.F.IndParams[i]);
out << ")\n";
// print the rule number
out << " rule!" << node.Outgoing->map->number;
// print the substitution
out << " (subst\n";
RPFP::Edge *orig_edge = edge.map;
int orig_clause = d->dd()->map[orig_edge];
expr &t = d->dd()->clauses[orig_clause];
if (t.is_quantifier() && t.is_quantifier_forall()) {
int bound = t.get_quantifier_num_bound();
std::vector<sort> sorts;
std::vector<symbol> names;
hash_map<int,expr> subst;
for(int j = 0; j < bound; j++){
sort the_sort = t.get_quantifier_bound_sort(j);
symbol name = t.get_quantifier_bound_name(j);
expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort));
out << " (= " << skolem << " " << cex.tree->Eval(&edge,skolem) << ")\n";
expr local_skolem = cex.tree->Localize(&edge,skolem);
(*local_func_decls).insert(local_skolem.decl());
}
}
out << " )\n";
out << " (labels";
std::vector<symbol> labels;
cex.tree->GetLabels(&edge,labels);
for(unsigned j = 0; j < labels.size(); j++){
out << " " << labels[j];
}
out << " )\n";
// reference the proofs of all the children, in syntactic order
// "true" means the child is not needed
out << " (ref ";
for(unsigned i = 0; i < edge.Children.size(); i++){
if(!cex.tree->Empty(edge.Children[i]))
out << " s!" << edge.Children[i]->number;
else
out << " true";
}
out << " )";
out << ")\n";
}
void dl_interface::display_certificate(std::ostream& out) const {
((dl_interface *)this)->display_certificate_non_const(out);
}
void dl_interface::display_certificate_non_const(std::ostream& out) {
if(_d->status == StatusModel){
ast_manager &m = m_ctx.get_manager();
model_ref md = get_model();
model_smt2_pp(out, m, *md.get(), 0);
}
else if(_d->status == StatusRefutation){
out << "(derivation\n";
// negation of the query is the last clause -- prove it
hash_set<func_decl> locals;
local_func_decls = &locals;
print_proof(this,out,_d->cex);
out << ")\n";
out << "(model \n\"";
::model mod(m_ctx.get_manager());
model orig_model = _d->cex.tree->dualModel;
for(unsigned i = 0; i < orig_model.num_consts(); i++){
func_decl cnst = orig_model.get_const_decl(i);
if(locals.find(cnst) == locals.end()){
expr thing = orig_model.get_const_interp(cnst);
mod.register_decl(to_func_decl(cnst.raw()),to_expr(thing.raw()));
}
}
for(unsigned i = 0; i < orig_model.num_funcs(); i++){
func_decl cnst = orig_model.get_func_decl(i);
if(locals.find(cnst) == locals.end()){
func_interp thing = orig_model.get_func_interp(cnst);
::func_interp *thing_raw = thing;
mod.register_decl(to_func_decl(cnst.raw()),thing_raw->copy());
}
}
model_v2_pp(out,mod);
out << "\")\n";
}
}
expr_ref dl_interface::get_answer() {
SASSERT(false);
return expr_ref(m_ctx.get_manager());
}
void dl_interface::cancel() {
#if 0
if(_d && _d->ls)
_d->ls->cancel();
#else
// HACK: duality can't cancel at all times, we just exit here
std::cout << "(error \"duality canceled\")\nunknown\n";
abort();
#endif
}
void dl_interface::cleanup() {
}
void dl_interface::updt_params() {
}
model_ref dl_interface::get_model() {
ast_manager &m = m_ctx.get_manager();
model_ref md(alloc(::model, m));
std::vector<RPFP::Node *> &nodes = _d->rpfp->nodes;
expr_ref_vector conjs(m);
for (unsigned i = 0; i < nodes.size(); ++i) {
RPFP::Node *node = nodes[i];
func_decl &pred = node->Name;
expr_ref prop(m);
prop = to_expr(node->Annotation.Formula);
std::vector<expr> &params = node->Annotation.IndParams;
expr_ref q(m);
expr_ref_vector sig_vars(m);
for (unsigned j = 0; j < params.size(); ++j)
sig_vars.push_back(params[params.size()-j-1]); // TODO: why backwards?
expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q);
if (params.empty()) {
md->register_decl(pred, q);
}
else {
::func_interp* fi = alloc(::func_interp, m, params.size());
fi->set_else(q);
md->register_decl(pred, fi);
}
}
return md;
}
static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) {
context &ctx = d->dd()->ctx;
ast_manager &mgr = ctx.m();
RPFP::Node &node = *cex.root;
RPFP::Edge &edge = *node.Outgoing;
RPFP::Edge *orig_edge = edge.map;
// first, prove the children (that are actually used)
proof_ref_vector prems(mgr);
::vector<expr_ref_vector> substs;
int orig_clause = d->dd()->map[orig_edge];
expr &t = d->dd()->clauses[orig_clause];
prems.push_back(mgr.mk_asserted(ctx.uncook(t)));
substs.push_back(expr_ref_vector(mgr));
if (t.is_quantifier() && t.is_quantifier_forall()) {
int bound = t.get_quantifier_num_bound();
std::vector<sort> sorts;
std::vector<symbol> names;
hash_map<int,expr> subst;
for(int j = 0; j < bound; j++){
sort the_sort = t.get_quantifier_bound_sort(j);
symbol name = t.get_quantifier_bound_name(j);
expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort));
expr val = cex.tree->Eval(&edge,skolem);
expr_ref thing(ctx.uncook(val),mgr);
substs[0].push_back(thing);
expr local_skolem = cex.tree->Localize(&edge,skolem);
(*local_func_decls).insert(local_skolem.decl());
}
}
svector<std::pair<unsigned, unsigned> > pos;
for(unsigned i = 0; i < edge.Children.size(); i++){
if(!cex.tree->Empty(edge.Children[i])){
pos.push_back(std::pair<unsigned,unsigned>(i+1,0));
Solver::Counterexample foo = cex;
foo.root = edge.Children[i];
proof_ref prem = extract_proof(d,foo);
prems.push_back(prem);
substs.push_back(expr_ref_vector(mgr));
}
}
func_decl f = node.Name;
std::vector<expr> args;
for(unsigned i = 0; i < edge.F.IndParams.size(); i++)
args.push_back(cex.tree->Eval(&edge,edge.F.IndParams[i]));
expr conc = f(args);
::vector< ::proof *> pprems;
for(unsigned i = 0; i < prems.size(); i++)
pprems.push_back(prems[i].get());
proof_ref res(mgr.mk_hyper_resolve(pprems.size(),&pprems[0], ctx.uncook(conc), pos, substs),mgr);
return res;
}
proof_ref dl_interface::get_proof() {
if(_d->status == StatusRefutation){
hash_set<func_decl> locals;
local_func_decls = &locals;
return extract_proof(this,_d->cex);
}
else
return proof_ref(m_ctx.get_manager());
}
}

View file

@ -0,0 +1,80 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
duality_dl_interface.h
Abstract:
SMT2 interface for Duality
Author:
Krystof Hoder (t-khoder) 2011-9-22.
Modified by Ken McMIllan (kenmcmil) 2013-4-18.
Revision History:
--*/
#ifndef _DUALITY_DL_INTERFACE_H_
#define _DUALITY_DL_INTERFACE_H_
#include "lbool.h"
#include "dl_rule.h"
#include "dl_rule_set.h"
#include "dl_engine_base.h"
#include "statistics.h"
namespace datalog {
class context;
}
namespace Duality {
class duality_data;
class dl_interface : public datalog::engine_base {
duality_data *_d;
datalog::context &m_ctx;
public:
dl_interface(datalog::context& ctx);
~dl_interface();
lbool query(expr* query);
void cancel();
void cleanup();
void display_certificate(std::ostream& out) const;
void collect_statistics(statistics& st) const;
void reset_statistics();
expr_ref get_answer();
unsigned get_num_levels(func_decl* pred);
expr_ref get_cover_delta(int level, func_decl* pred);
void add_cover(int level, func_decl* pred, expr* property);
void updt_params();
model_ref get_model();
proof_ref get_proof();
duality_data *dd(){return _d;}
private:
void display_certificate_non_const(std::ostream& out);
};
}
#endif

View file

@ -454,6 +454,44 @@ public:
}
};
/**
\brief fixedpoint-push command.
*/
class dl_push_cmd : public cmd {
ref<dl_context> m_dl_ctx;
public:
dl_push_cmd(dl_context * dl_ctx):
cmd("fixedpoint-push"),
m_dl_ctx(dl_ctx)
{}
virtual char const * get_usage() const { return ""; }
virtual char const * get_descr(cmd_context & ctx) const { return "push the fixedpoint context"; }
virtual unsigned get_arity() const { return 0; }
virtual void execute(cmd_context & ctx) {
m_dl_ctx->push();
}
};
/**
\brief fixedpoint-pop command.
*/
class dl_pop_cmd : public cmd {
ref<dl_context> m_dl_ctx;
public:
dl_pop_cmd(dl_context * dl_ctx):
cmd("fixedpoint-pop"),
m_dl_ctx(dl_ctx)
{}
virtual char const * get_usage() const { return ""; }
virtual char const * get_descr(cmd_context & ctx) const { return "pop the fixedpoint context"; }
virtual unsigned get_arity() const { return 0; }
virtual void execute(cmd_context & ctx) {
m_dl_ctx->pop();
}
};
static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_cmds) {
dl_context * dl_ctx = alloc(dl_context, ctx, collected_cmds);
@ -461,6 +499,13 @@ static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_c
ctx.insert(alloc(dl_query_cmd, dl_ctx));
ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx));
ctx.insert(alloc(dl_declare_var_cmd, dl_ctx));
// #ifndef _EXTERNAL_RELEASE
// TODO: we need these!
#if 1
ctx.insert(alloc(dl_push_cmd, dl_ctx)); // not exposed to keep command-extensions simple.
ctx.insert(alloc(dl_pop_cmd, dl_ctx));
#endif
// #endif
}
void install_dl_cmds(cmd_context & ctx) {

View file

@ -22,6 +22,7 @@ Revision History:
#include "tab_context.h"
#include "rel_context.h"
#include "pdr_dl_interface.h"
#include "duality_dl_interface.h"
namespace datalog {
register_engine::register_engine(): m_ctx(0) {}
@ -40,6 +41,8 @@ namespace datalog {
return alloc(tab, *m_ctx);
case CLP_ENGINE:
return alloc(clp, *m_ctx);
case DUALITY_ENGINE:
return alloc(Duality::dl_interface, *m_ctx);
case LAST_ENGINE:
UNREACHABLE();
return 0;

View file

@ -1125,9 +1125,7 @@ namespace pdr {
n->mk_instantiate(r0, r1, binding);
proof_ref p1(m), p2(m);
p1 = r0->get_proof();
if (!p1) {
r0->display(dctx, std::cout);
}
IF_VERBOSE(0, if (!p1) r0->display(dctx, verbose_stream()););
SASSERT(p1);
pfs[0] = p1;
rls[0] = r1;

View file

@ -19,6 +19,7 @@ Revision History:
#include "dl_mk_array_blast.h"
#include "qe_util.h"
#include "scoped_proof.h"
namespace datalog {
@ -270,7 +271,7 @@ namespace datalog {
}
}
expr_ref fml2(m), body(m), head(m);
expr_ref fml1(m), fml2(m), body(m), head(m);
body = m.mk_and(new_conjs.size(), new_conjs.c_ptr());
head = r.get_head();
sub(body);
@ -287,9 +288,17 @@ namespace datalog {
proof_ref p(m);
rule_set new_rules(m_ctx);
rm.mk_rule(fml2, p, new_rules, r.name());
rule_ref new_rule(rm);
if (m_simplifier.transform_rule(new_rules.last(), new_rule)) {
if (r.get_proof()) {
scoped_proof _sc(m);
r.to_formula(fml1);
p = m.mk_rewrite(fml1, fml2);
p = m.mk_modus_ponens(r.get_proof(), p);
new_rule->set_proof(m, p);
}
rules.add_rule(new_rule.get());
rm.mk_rule_rewrite_proof(r, *new_rule.get());
TRACE("dl", new_rule->display(m_ctx, tout << "new rule\n"););

View file

@ -25,6 +25,7 @@ Revision History:
#include "filter_model_converter.h"
#include "dl_mk_interp_tail_simplifier.h"
#include "fixedpoint_params.hpp"
#include "scoped_proof.h"
namespace datalog {
@ -268,7 +269,8 @@ namespace datalog {
r->to_formula(fml);
if (blast(r, fml)) {
proof_ref pr(m);
if (m_context.generate_proof_trace()) {
if (r->get_proof()) {
scoped_proof _sc(m);
pr = m.mk_asserted(fml); // loses original proof of r.
}
// TODO add logic for pc:

View file

@ -341,7 +341,6 @@ namespace datalog {
}
head = mk_head(source, *result, r.get_head(), cnt);
fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), head);
rule_ref_vector added_rules(rm);
proof_ref pr(m);
rm.mk_rule(fml, pr, *result);
TRACE("dl", result->last()->display(m_ctx, tout););

View file

@ -527,6 +527,9 @@ namespace datalog {
bool mk_rule_inliner::do_eager_inlining(rule * r, rule_set const& rules, rule_ref& res) {
if (r->has_negation()) {
return false;
}
SASSERT(rules.is_closed());
const rule_stratifier& strat = rules.get_stratifier();

View file

@ -416,6 +416,9 @@ namespace opt {
if (m_engine == symbol("wpm2")) {
return wpm2_solve();
}
if (m_engine == symbol("wpm2b")) {
return wpm2b_solve();
}
return incremental_solve();
}
@ -664,12 +667,6 @@ namespace opt {
m_lower = m_upper = rational::zero();
obj_map<expr, unsigned> ans_index;
#ifdef WPM2b
// change from CP'13
for (unsigned i = 0; i < s.get_num_assertions(); ++i) {
al.push_back(s.get_assertion(i));
}
#endif
vector<rational> amk;
vector<uint_set> sc;
for (unsigned i = 0; i < m_soft.size(); ++i) {
@ -758,10 +755,7 @@ namespace opt {
}
}
rational k;
std::cout << "new bound";
is_sat = new_bound(al, ws, bs, k);
std::cout << " " << k << "\n";
std::cout.flush();
if (is_sat != l_true) {
return is_sat;
}
@ -783,6 +777,177 @@ namespace opt {
amk.push_back(k);
}
}
// Version from CP'13
lbool wpm2b_solve() {
solver::scoped_push _s(s);
pb_util u(m);
app_ref fml(m), a(m), b(m), c(m);
expr_ref val(m);
expr_ref_vector block(m), ans(m), am(m), soft(m);
m_lower = m_upper = rational::zero();
obj_map<expr, unsigned> ans_index;
vector<rational> amk;
vector<uint_set> sc; // vector of indices used in at last constraints
expr_ref_vector al(m); // vector of at least constraints.
rational wmax;
for (unsigned i = 0; i < m_soft.size(); ++i) {
rational w = m_weights[i];
m_upper += w;
if (wmax < w) wmax = w;
b = m.mk_fresh_const("b", m.mk_bool_sort());
expr* bb = b;
s.mc().insert(b->get_decl());
a = m.mk_fresh_const("a", m.mk_bool_sort());
s.mc().insert(a->get_decl());
ans.push_back(a);
ans_index.insert(a, i);
soft.push_back(0); // assert soft constraints lazily.
c = m.mk_fresh_const("c", m.mk_bool_sort());
s.mc().insert(c->get_decl());
fml = m.mk_implies(c, u.mk_le(1,&w,&bb,rational(0)));
s.assert_expr(fml);
sc.push_back(uint_set());
sc.back().insert(i);
am.push_back(c);
al.push_back(u.mk_ge(1,&w,&bb,rational(0)));
s.assert_expr(al.back());
amk.push_back(rational(0));
}
++wmax;
while (true) {
enable_soft(soft, block, ans, wmax);
expr_ref_vector asms(m);
asms.append(ans);
asms.append(am);
lbool is_sat = s.check_sat(asms.size(), asms.c_ptr());
if (m_cancel && is_sat != l_false) {
is_sat = l_undef;
}
if (is_sat == l_undef) {
return l_undef;
}
if (is_sat == l_true && wmax.is_zero()) {
s.get_model(m_model);
m_upper = m_lower;
for (unsigned i = 0; i < block.size(); ++i) {
VERIFY(m_model->eval(block[i].get(), val));
m_assignment[i] = m.is_false(val);
}
return l_true;
}
if (is_sat == l_true) {
rational W(0);
for (unsigned i = 0; i < m_weights.size(); ++i) {
if (m_weights[i] < wmax) W += m_weights[i];
}
harden(am, W);
wmax = decrease(wmax);
continue;
}
SASSERT(is_sat == l_false);
ptr_vector<expr> core;
s.get_unsat_core(core);
if (core.empty()) {
return l_false;
}
uint_set A;
for (unsigned i = 0; i < core.size(); ++i) {
unsigned j;
if (ans_index.find(core[i], j) && soft[j].get()) {
A.insert(j);
}
}
if (A.empty()) {
return l_false;
}
uint_set B;
for (unsigned i = 0; i < sc.size(); ++i) {
uint_set t(sc[i]);
t &= A;
if (!t.empty()) {
B |= sc[i];
m_lower -= amk[i];
sc[i] = sc.back();
sc.pop_back();
am[i] = am.back();
am.pop_back();
amk[i] = amk.back();
amk.pop_back();
--i;
}
}
vector<rational> ws;
expr_ref_vector bs(m);
for (unsigned i = 0; i < m_soft.size(); ++i) {
if (B.contains(i)) {
ws.push_back(m_weights[i]);
bs.push_back(block[i].get());
}
}
rational k;
expr_ref_vector al2(al);
for (unsigned i = 0; i < s.get_num_assertions(); ++i) {
al2.push_back(s.get_assertion(i));
}
is_sat = new_bound(al2, ws, bs, k);
if (is_sat != l_true) {
return is_sat;
}
m_lower += k;
expr_ref B_le_k(m), B_ge_k(m);
B_le_k = u.mk_le(ws.size(), ws.c_ptr(), bs.c_ptr(), k);
B_ge_k = u.mk_ge(ws.size(), ws.c_ptr(), bs.c_ptr(), k);
s.assert_expr(B_ge_k);
al.push_back(B_ge_k);
IF_VERBOSE(1, verbose_stream() << "(wmaxsat.wpm2 lower bound: " << m_lower << ")\n";);
IF_VERBOSE(2, verbose_stream() << "New lower bound: " << B_ge_k << "\n";);
c = m.mk_fresh_const("c", m.mk_bool_sort());
s.mc().insert(c->get_decl());
fml = m.mk_implies(c, B_le_k);
s.assert_expr(fml);
sc.push_back(B);
am.push_back(c);
amk.push_back(k);
}
}
void harden(expr_ref_vector& am, rational const& W) {
// TBD
}
rational decrease(rational const& wmax) {
rational wmin(0);
for (unsigned i = 0; i < m_weights.size(); ++i) {
rational w = m_weights[i];
if (w < wmax && wmin < w) wmin = w;
}
return wmin;
}
// enable soft constraints that have reached wmax.
void enable_soft(expr_ref_vector& soft,
expr_ref_vector const& block,
expr_ref_vector const& ans,
rational wmax) {
for (unsigned i = 0; i < m_soft.size(); ++i) {
rational w = m_weights[i];
if (w >= wmax && !soft[i].get()) {
soft[i] = m.mk_or(m_soft[i].get(), block[i], m.mk_not(ans[i]));
s.assert_expr(soft[i].get());
}
}
}
lbool new_bound(expr_ref_vector const& al,
vector<rational> const& ws,

View file

@ -9,7 +9,7 @@ Abstract:
SAT simplification procedures that use a "full" occurrence list:
Subsumption, Blocked Clause Removal, Variable Elimination, ...
Author:
@ -54,21 +54,21 @@ namespace sat {
m_use_list[l2.index()].erase(c);
}
}
simplifier::simplifier(solver & _s, params_ref const & p):
s(_s),
m_num_calls(0) {
updt_params(p);
reset_statistics();
}
simplifier::~simplifier() {
}
inline watch_list & simplifier::get_wlist(literal l) { return s.get_wlist(l); }
inline watch_list const & simplifier::get_wlist(literal l) const { return s.get_wlist(l); }
inline bool simplifier::is_external(bool_var v) const { return s.is_external(v); }
inline bool simplifier::was_eliminated(bool_var v) const { return s.was_eliminated(v); }
@ -78,7 +78,7 @@ namespace sat {
lbool simplifier::value(literal l) const { return s.value(l); }
inline void simplifier::checkpoint() { s.checkpoint(); }
void simplifier::register_clauses(clause_vector & cs) {
std::stable_sort(cs.begin(), cs.end(), size_lt());
clause_vector::iterator it = cs.begin();
@ -117,7 +117,7 @@ namespace sat {
SASSERT(s.get_wlist(~l1).contains(watched(l2, learned)));
s.get_wlist(~l1).erase(watched(l2, learned));
}
void simplifier::init_visited() {
m_visited.reset();
m_visited.resize(2*s.num_vars(), false);
@ -155,7 +155,7 @@ namespace sat {
if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls))
elim_blocked_clauses();
if (!learned)
m_num_calls++;
@ -180,6 +180,7 @@ namespace sat {
bool vars_eliminated = m_num_elim_vars > old_num_elim_vars;
if (!m_need_cleanup) {
TRACE("after_simplifier", tout << "skipping cleanup...\n";);
if (vars_eliminated) {
// must remove learned clauses with eliminated variables
cleanup_clauses(s.m_learned, true, true, learned_in_use_lists);
@ -189,6 +190,7 @@ namespace sat {
free_memory();
return;
}
TRACE("after_simplifier", tout << "cleanning watches...\n";);
cleanup_watches();
cleanup_clauses(s.m_learned, true, vars_eliminated, learned_in_use_lists);
cleanup_clauses(s.m_clauses, false, vars_eliminated, true);
@ -234,7 +236,7 @@ namespace sat {
s.del_clause(c);
continue;
}
if (learned && vars_eliminated) {
unsigned sz = c.size();
unsigned i;
@ -293,7 +295,7 @@ namespace sat {
mark_visited(c[i]);
}
}
void simplifier::unmark_all(clause const & c) {
unsigned sz = c.size();
for (unsigned i = 0; i < sz; i++)
@ -325,7 +327,7 @@ namespace sat {
*/
bool simplifier::subsumes1(clause const & c1, clause const & c2, literal & l) {
unsigned sz2 = c2.size();
for (unsigned i = 0; i < sz2; i++)
for (unsigned i = 0; i < sz2; i++)
mark_visited(c2[i]);
bool r = true;
@ -344,7 +346,7 @@ namespace sat {
}
}
for (unsigned i = 0; i < sz2; i++)
for (unsigned i = 0; i < sz2; i++)
unmark_visited(c2[i]);
return r;
}
@ -353,7 +355,7 @@ namespace sat {
\brief Return the clauses subsumed by c1 and the clauses that can be subsumed resolved using c1.
The collections is populated using the use list of target.
*/
void simplifier::collect_subsumed1_core(clause const & c1, clause_vector & out, literal_vector & out_lits,
void simplifier::collect_subsumed1_core(clause const & c1, clause_vector & out, literal_vector & out_lits,
literal target) {
clause_use_list const & cs = m_use_list.get(target);
clause_use_list::iterator it = cs.mk_iterator();
@ -362,7 +364,7 @@ namespace sat {
CTRACE("resolution_bug", c2.was_removed(), tout << "clause has been removed:\n" << c2 << "\n";);
SASSERT(!c2.was_removed());
if (&c2 != &c1 &&
c1.size() <= c2.size() &&
c1.size() <= c2.size() &&
approx_subset(c1.approx(), c2.approx())) {
m_sub_counter -= c1.size() + c2.size();
literal l;
@ -373,7 +375,7 @@ namespace sat {
}
it.next();
}
}
}
/**
\brief Return the clauses subsumed by c1 and the clauses that can be subsumed resolved using c1.
@ -400,12 +402,12 @@ namespace sat {
if (*l_it == null_literal) {
// c2 was subsumed
if (c1.is_learned() && !c2.is_learned())
c1.unset_learned();
c1.unset_learned();
TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";);
remove_clause(c2);
m_num_subsumed++;
}
else {
else if (!c2.was_removed()) {
// subsumption resolution
TRACE("subsumption_resolution", tout << c1 << " sub-ref(" << *l_it << ") " << c2 << "\n";);
elim_lit(c2, *l_it);
@ -447,9 +449,9 @@ namespace sat {
*/
bool simplifier::subsumes0(clause const & c1, clause const & c2) {
unsigned sz2 = c2.size();
for (unsigned i = 0; i < sz2; i++)
for (unsigned i = 0; i < sz2; i++)
mark_visited(c2[i]);
bool r = true;
unsigned sz1 = c1.size();
for (unsigned i = 0; i < sz1; i++) {
@ -459,12 +461,12 @@ namespace sat {
}
}
for (unsigned i = 0; i < sz2; i++)
for (unsigned i = 0; i < sz2; i++)
unmark_visited(c2[i]);
return r;
}
/**
\brief Collect the clauses subsumed by c1 (using the occurrence list of target).
*/
@ -475,7 +477,7 @@ namespace sat {
clause & c2 = it.curr();
SASSERT(!c2.was_removed());
if (&c2 != &c1 &&
c1.size() <= c2.size() &&
c1.size() <= c2.size() &&
approx_subset(c1.approx(), c2.approx())) {
m_sub_counter -= c1.size() + c2.size();
if (subsumes0(c1, c2)) {
@ -485,7 +487,7 @@ namespace sat {
it.next();
}
}
/**
\brief Collect the clauses subsumed by c1
*/
@ -493,8 +495,8 @@ namespace sat {
literal l = get_min_occ_var0(c1);
collect_subsumed0_core(c1, out, l);
}
/**
\brief Perform backward subsumption using c1.
*/
@ -507,16 +509,16 @@ namespace sat {
clause & c2 = *(*it);
// c2 was subsumed
if (c1.is_learned() && !c2.is_learned())
c1.unset_learned();
c1.unset_learned();
TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";);
remove_clause(c2);
m_num_subsumed++;
}
}
/**
\brief Eliminate false literals from c, and update occurrence lists
Return true if the clause is satisfied
*/
bool simplifier::cleanup_clause(clause & c, bool in_use_list) {
@ -666,7 +668,7 @@ namespace sat {
back_subsumption1(c);
if (w.is_learned() && !c.is_learned()) {
SASSERT(wlist[j] == w);
TRACE("mark_not_learned_bug",
TRACE("mark_not_learned_bug",
tout << "marking as not learned: " << l2 << " " << wlist[j].is_learned() << "\n";);
wlist[j].mark_not_learned();
mark_as_not_learned_core(get_wlist(~l2), l);
@ -735,7 +737,7 @@ namespace sat {
continue;
}
if (it2->get_literal() == last_lit) {
TRACE("subsumption", tout << "eliminating: " << ~to_literal(l_idx)
TRACE("subsumption", tout << "eliminating: " << ~to_literal(l_idx)
<< " " << it2->get_literal() << "\n";);
elim++;
}
@ -762,12 +764,12 @@ namespace sat {
m_num_sub_res(s.m_num_sub_res) {
m_watch.start();
}
~subsumption_report() {
m_watch.stop();
IF_VERBOSE(SAT_VB_LVL,
IF_VERBOSE(SAT_VB_LVL,
verbose_stream() << " (sat-subsumer :subsumed "
<< (m_simplifier.m_num_subsumed - m_num_subsumed)
<< (m_simplifier.m_num_subsumed - m_num_subsumed)
<< " :subsumption-resolution " << (m_simplifier.m_num_sub_res - m_num_sub_res)
<< " :threshold " << m_simplifier.m_sub_counter
<< mem_stat()
@ -847,12 +849,12 @@ namespace sat {
vector<watch_list> const & m_watches;
public:
literal_lt(use_list const & l, vector<watch_list> const & ws):m_use_list(l), m_watches(ws) {}
unsigned weight(unsigned l_idx) const {
return 2*m_use_list.get(~to_literal(l_idx)).size() + m_watches[l_idx].size();
}
bool operator()(unsigned l_idx1, unsigned l_idx2) const {
bool operator()(unsigned l_idx1, unsigned l_idx2) const {
return weight(l_idx1) < weight(l_idx2);
}
};
@ -861,9 +863,9 @@ namespace sat {
heap<literal_lt> m_queue;
public:
queue(use_list const & l, vector<watch_list> const & ws):m_queue(128, literal_lt(l, ws)) {}
void insert(literal l) {
void insert(literal l) {
unsigned idx = l.index();
m_queue.reserve(idx + 1);
m_queue.reserve(idx + 1);
SASSERT(!m_queue.contains(idx));
m_queue.insert(idx);
}
@ -877,14 +879,14 @@ namespace sat {
literal next() { SASSERT(!empty()); return to_literal(m_queue.erase_min()); }
bool empty() const { return m_queue.empty(); }
};
simplifier & s;
int m_counter;
model_converter & mc;
queue m_queue;
clause_vector m_to_remove;
blocked_clause_elim(simplifier & _s, unsigned limit, model_converter & _mc, use_list & l,
blocked_clause_elim(simplifier & _s, unsigned limit, model_converter & _mc, use_list & l,
vector<watch_list> & wlist):
s(_s),
m_counter(limit),
@ -946,7 +948,7 @@ namespace sat {
clause_vector::iterator it = m_to_remove.begin();
clause_vector::iterator end = m_to_remove.end();
for (; it != end; ++it) {
s.remove_clause(*(*it));
s.remove_clause(*(*it));
}
}
{
@ -1025,12 +1027,12 @@ namespace sat {
m_num_blocked_clauses(s.m_num_blocked_clauses) {
m_watch.start();
}
~blocked_cls_report() {
m_watch.stop();
IF_VERBOSE(SAT_VB_LVL,
IF_VERBOSE(SAT_VB_LVL,
verbose_stream() << " (sat-blocked-clauses :elim-blocked-clauses "
<< (m_simplifier.m_num_blocked_clauses - m_num_blocked_clauses)
<< (m_simplifier.m_num_blocked_clauses - m_num_blocked_clauses)
<< mem_stat()
<< " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";);
}
@ -1062,8 +1064,8 @@ namespace sat {
unsigned num_neg = m_use_list.get(neg_l).size();
unsigned num_bin_pos = get_num_non_learned_bin(pos_l);
unsigned num_bin_neg = get_num_non_learned_bin(neg_l);
unsigned cost = 2 * num_pos * num_neg + num_pos * num_bin_neg + num_neg * num_bin_pos;
CTRACE("elim_vars_detail", cost == 0, tout << v << " num_pos: " << num_pos << " num_neg: " << num_neg << " num_bin_pos: " << num_bin_pos
unsigned cost = 2 * num_pos * num_neg + num_pos * num_bin_neg + num_neg * num_bin_pos;
CTRACE("elim_vars_detail", cost == 0, tout << v << " num_pos: " << num_pos << " num_neg: " << num_neg << " num_bin_pos: " << num_bin_pos
<< " num_bin_neg: " << num_bin_neg << " cost: " << cost << "\n";);
return cost;
}
@ -1071,7 +1073,7 @@ namespace sat {
typedef std::pair<bool_var, unsigned> bool_var_and_cost;
struct bool_var_and_cost_lt {
bool operator()(bool_var_and_cost const & p1, bool_var_and_cost const & p2) const { return p1.second < p2.second; }
bool operator()(bool_var_and_cost const & p1, bool_var_and_cost const & p2) const { return p1.second < p2.second; }
};
void simplifier::order_vars_for_elim(bool_var_vector & r) {
@ -1104,7 +1106,7 @@ namespace sat {
r.push_back(it2->first);
}
}
/**
\brief Collect clauses and binary clauses containing l.
*/
@ -1116,7 +1118,7 @@ namespace sat {
SASSERT(r.back().size() == it.curr().size());
it.next();
}
watch_list & wlist = get_wlist(~l);
watch_list::iterator it2 = wlist.begin();
watch_list::iterator end2 = wlist.end();
@ -1129,7 +1131,7 @@ namespace sat {
}
/**
\brief Resolve clauses c1 and c2.
\brief Resolve clauses c1 and c2.
c1 must contain l.
c2 must contain ~l.
Store result in r.
@ -1149,7 +1151,7 @@ namespace sat {
m_visited[l2.index()] = true;
r.push_back(l2);
}
literal not_l = ~l;
sz = c2.size();
m_elim_counter -= sz;
@ -1164,7 +1166,7 @@ namespace sat {
if (!m_visited[l2.index()])
r.push_back(l2);
}
sz = c1.size();
for (unsigned i = 0; i < sz; i++) {
literal l2 = c1[i];
@ -1200,7 +1202,7 @@ namespace sat {
break;
}
}
CTRACE("resolve_bug", it2 == end2,
CTRACE("resolve_bug", it2 == end2,
tout << ~l1 << " -> ";
display(tout, s.m_cls_allocator, wlist1); tout << "\n" << ~l2 << " -> ";
display(tout, s.m_cls_allocator, wlist2); tout << "\n";);
@ -1262,7 +1264,7 @@ namespace sat {
TRACE("resolution_bug", tout << "processing: " << v << "\n";);
if (value(v) != l_undef)
return false;
literal pos_l(v, false);
literal neg_l(v, true);
unsigned num_bin_pos = get_num_non_learned_bin(pos_l);
@ -1274,12 +1276,12 @@ namespace sat {
m_elim_counter -= num_pos + num_neg;
TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << "\n";);
if (num_pos >= m_res_occ_cutoff && num_neg >= m_res_occ_cutoff)
return false;
unsigned before_lits = num_bin_pos*2 + num_bin_neg*2;
{
clause_use_list::iterator it = pos_occs.mk_iterator();
while (!it.at_end()) {
@ -1287,7 +1289,7 @@ namespace sat {
it.next();
}
}
{
clause_use_list::iterator it2 = neg_occs.mk_iterator();
while (!it2.at_end()) {
@ -1297,23 +1299,23 @@ namespace sat {
}
TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << " before_lits: " << before_lits << "\n";);
if (num_pos >= m_res_occ_cutoff3 && num_neg >= m_res_occ_cutoff3 && before_lits > m_res_lit_cutoff3 && s.m_clauses.size() > m_res_cls_cutoff2)
return false;
if (num_pos >= m_res_occ_cutoff2 && num_neg >= m_res_occ_cutoff2 && before_lits > m_res_lit_cutoff2 &&
if (num_pos >= m_res_occ_cutoff2 && num_neg >= m_res_occ_cutoff2 && before_lits > m_res_lit_cutoff2 &&
s.m_clauses.size() > m_res_cls_cutoff1 && s.m_clauses.size() <= m_res_cls_cutoff2)
return false;
if (num_pos >= m_res_occ_cutoff1 && num_neg >= m_res_occ_cutoff1 && before_lits > m_res_lit_cutoff1 &&
if (num_pos >= m_res_occ_cutoff1 && num_neg >= m_res_occ_cutoff1 && before_lits > m_res_lit_cutoff1 &&
s.m_clauses.size() <= m_res_cls_cutoff1)
return false;
m_pos_cls.reset();
m_neg_cls.reset();
collect_clauses(pos_l, m_pos_cls);
collect_clauses(neg_l, m_neg_cls);
m_elim_counter -= num_pos * num_neg + before_lits;
TRACE("resolution_detail", tout << "collecting number of after_clauses\n";);
unsigned before_clauses = num_pos + num_neg;
unsigned after_clauses = 0;
@ -1350,7 +1352,7 @@ namespace sat {
neg_occs.reset();
m_elim_counter -= num_pos * num_neg + before_lits;
it1 = m_pos_cls.begin();
end1 = m_pos_cls.end();
for (; it1 != end1; ++it1) {
@ -1393,7 +1395,7 @@ namespace sat {
return true;
}
}
return true;
}
@ -1406,10 +1408,10 @@ namespace sat {
m_num_elim_vars(s.m_num_elim_vars) {
m_watch.start();
}
~elim_var_report() {
m_watch.stop();
IF_VERBOSE(SAT_VB_LVL,
IF_VERBOSE(SAT_VB_LVL,
verbose_stream() << " (sat-resolution :elim-bool-vars "
<< (m_simplifier.m_num_elim_vars - m_num_elim_vars)
<< " :threshold " << m_simplifier.m_elim_counter
@ -1417,12 +1419,12 @@ namespace sat {
<< " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";);
}
};
void simplifier::elim_vars() {
elim_var_report rpt(*this);
bool_var_vector vars;
order_vars_for_elim(vars);
bool_var_vector::iterator it = vars.begin();
bool_var_vector::iterator end = vars.end();
for (; it != end; ++it) {
@ -1463,7 +1465,7 @@ namespace sat {
void simplifier::collect_param_descrs(param_descrs & r) {
sat_simplifier_params::collect_param_descrs(r);
}
void simplifier::collect_statistics(statistics & st) {
st.update("subsumed", m_num_subsumed);
st.update("subsumption resolution", m_num_sub_res);
@ -1471,7 +1473,7 @@ namespace sat {
st.update("elim bool vars", m_num_elim_vars);
st.update("elim blocked clauses", m_num_blocked_clauses);
}
void simplifier::reset_statistics() {
m_num_blocked_clauses = 0;
m_num_subsumed = 0;

View file

@ -27,7 +27,7 @@ Revision History:
// define to create a copy of the solver before starting the search
// useful for checking models
// #define CLONE_BEFORE_SOLVING
namespace sat {
solver::solver(params_ref const & p, extension * ext):
@ -103,7 +103,7 @@ namespace sat {
}
}
}
// -----------------------
//
// Variable & Clause creation
@ -312,7 +312,7 @@ namespace sat {
/**
\brief Select a watch literal starting the search at the given position.
This method is only used for clauses created during the search.
I use the following rules to select a watch literal.
1- select a literal l in idx >= starting_at such that value(l) = l_true,
@ -329,7 +329,7 @@ namespace sat {
lvl(l) >= lvl(l')
Without rule 3, boolean propagation is incomplete, that is, it may miss possible propagations.
\remark The method select_lemma_watch_lit is used to select the watch literal for regular learned clauses.
*/
unsigned solver::select_watch_lit(clause const & cls, unsigned starting_at) const {
@ -443,7 +443,7 @@ namespace sat {
erase_clause_watch(get_wlist(~c[0]), cls_off);
erase_clause_watch(get_wlist(~c[1]), cls_off);
}
void solver::dettach_ter_clause(clause & c) {
erase_ternary_watch(get_wlist(~c[0]), c[1], c[2]);
erase_ternary_watch(get_wlist(~c[1]), c[0], c[2]);
@ -498,10 +498,10 @@ namespace sat {
unsigned sz = c.size();
for (unsigned i = 0; i < sz; i++) {
switch (value(c[i])) {
case l_true:
case l_true:
return l_true;
case l_undef:
found_undef = true;
case l_undef:
found_undef = true;
break;
default:
break;
@ -515,7 +515,7 @@ namespace sat {
// Propagation
//
// -----------------------
bool solver::propagate_core(bool update) {
if (m_inconsistent)
return false;
@ -545,7 +545,7 @@ namespace sat {
}
for (; it != end; ++it) {
switch (it->get_kind()) {
case watched::BINARY:
case watched::BINARY:
l1 = it->get_literal();
switch (value(l1)) {
case l_false:
@ -585,15 +585,30 @@ namespace sat {
break;
case watched::CLAUSE: {
if (value(it->get_blocked_literal()) == l_true) {
TRACE("propagate_clause_bug", tout << "blocked literal " << it->get_blocked_literal() << "\n";
clause_offset cls_off = it->get_clause_offset();
clause & c = *(m_cls_allocator.get_clause(cls_off));
tout << c << "\n";);
*it2 = *it;
it2++;
break;
}
clause_offset cls_off = it->get_clause_offset();
clause & c = *(m_cls_allocator.get_clause(cls_off));
TRACE("propagate_clause_bug", tout << "processing... " << c << "\nwas_removed: " << c.was_removed() << "\n";);
if (c[0] == not_l)
std::swap(c[0], c[1]);
CTRACE("propagate_bug", c[1] != not_l, tout << "l: " << l << " " << c << "\n";);
if (c.was_removed() || c[1] != not_l) {
// Remark: this method may be invoked when the watch lists are not in a consistent state,
// and may contain dead/removed clauses, or clauses with removed literals.
// See: method propagate_unit at sat_simplifier.cpp
// So, we must check whether the clause was marked for deletion, or
// c[1] != not_l
*it2 = *it;
it2++;
break;
}
SASSERT(c[1] == not_l);
if (value(c[0]) == l_true) {
it2->set_clause(c[0], cls_off);
@ -694,7 +709,7 @@ namespace sat {
m_conflicts_since_restart = 0;
m_restart_threshold = m_config.m_restart_initial;
}
// iff3_finder(*this)();
simplify_problem();
@ -705,10 +720,10 @@ namespace sat {
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"abort: max-conflicts = 0\"\n";);
return l_undef;
}
while (true) {
SASSERT(!inconsistent());
lbool r = bounded_search();
if (r != l_undef)
return r;
@ -717,7 +732,7 @@ namespace sat {
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"abort: max-conflicts = " << m_conflicts << "\"\n";);
return l_undef;
}
restart();
if (m_conflicts >= m_next_simplify) {
simplify_problem();
@ -735,7 +750,7 @@ namespace sat {
bool_var solver::next_var() {
bool_var next;
if (m_rand() < static_cast<int>(m_config.m_random_freq * random_gen::max_value())) {
if (num_vars() == 0)
return null_bool_var;
@ -744,16 +759,16 @@ namespace sat {
if (value(next) == l_undef && !was_eliminated(next))
return next;
}
while (!m_case_split_queue.empty()) {
next = m_case_split_queue.next_var();
if (value(next) == l_undef && !was_eliminated(next))
return next;
}
return null_bool_var;
}
bool solver::decide() {
bool_var next = next_var();
if (next == null_bool_var)
@ -761,7 +776,7 @@ namespace sat {
push();
m_stats.m_decision++;
lbool phase = m_ext ? m_ext->get_phase(next) : l_undef;
if (phase == l_undef) {
switch (m_config.m_phase) {
case PS_ALWAYS_TRUE:
@ -785,7 +800,7 @@ namespace sat {
break;
}
}
SASSERT(phase != l_undef);
literal next_lit(next, phase == l_false);
assign(next_lit, justification());
@ -808,23 +823,23 @@ namespace sat {
return l_undef;
if (scope_lvl() == 0) {
cleanup(); // cleaner may propagate frozen clauses
if (inconsistent())
if (inconsistent())
return l_false;
gc();
}
}
gc();
if (!decide()) {
if (m_ext) {
switch (m_ext->check()) {
case CR_DONE:
case CR_DONE:
mk_model();
return l_true;
case CR_CONTINUE:
case CR_CONTINUE:
break;
case CR_GIVEUP:
case CR_GIVEUP:
throw abort_solver();
}
}
@ -850,23 +865,23 @@ namespace sat {
m_stopwatch.reset();
m_stopwatch.start();
}
/**
\brief Apply all simplifications.
*/
void solver::simplify_problem() {
SASSERT(scope_lvl() == 0);
m_cleaner();
CASSERT("sat_simplify_bug", check_invariant());
m_scc();
CASSERT("sat_simplify_bug", check_invariant());
m_simplifier(false);
m_simplifier(false);
CASSERT("sat_simplify_bug", check_invariant());
CASSERT("sat_missed_prop", check_missed_propagation());
if (!m_learned.empty()) {
m_simplifier(true);
CASSERT("sat_missed_prop", check_missed_propagation());
@ -879,11 +894,11 @@ namespace sat {
m_probing();
CASSERT("sat_missed_prop", check_missed_propagation());
CASSERT("sat_simplify_bug", check_invariant());
m_asymm_branch();
CASSERT("sat_missed_prop", check_missed_propagation());
CASSERT("sat_simplify_bug", check_invariant());
if (m_ext) {
m_ext->clauses_modifed();
m_ext->simplify();
@ -957,7 +972,7 @@ namespace sat {
}
}
}
if (!m_mc.check_model(m))
if (!m_mc.check_model(m))
ok = false;
CTRACE("sat_model_bug", !ok, tout << m << "\n";);
return ok;
@ -965,9 +980,9 @@ namespace sat {
void solver::restart() {
m_stats.m_restart++;
IF_VERBOSE(1,
IF_VERBOSE(1,
verbose_stream() << "(sat-restart :conflicts " << m_stats.m_conflict << " :decisions " << m_stats.m_decision
<< " :restarts " << m_stats.m_restart << mk_stat(*this)
<< " :restarts " << m_stats.m_restart << mk_stat(*this)
<< " :time " << std::fixed << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n";);
IF_VERBOSE(30, display_status(verbose_stream()););
pop(scope_lvl());
@ -992,9 +1007,9 @@ namespace sat {
// GC
//
// -----------------------
void solver::gc() {
if (m_conflicts_since_gc <= m_gc_threshold)
if (m_conflicts_since_gc <= m_gc_threshold)
return;
CASSERT("sat_gc_bug", check_invariant());
switch (m_config.m_gc_strategy) {
@ -1074,7 +1089,7 @@ namespace sat {
std::stable_sort(m_learned.begin(), m_learned.end(), glue_lt());
gc_half("glue");
}
void solver::gc_psm() {
save_psm();
std::stable_sort(m_learned.begin(), m_learned.end(), psm_lt());
@ -1135,8 +1150,8 @@ namespace sat {
void solver::gc_dyn_psm() {
// To do gc at scope_lvl() > 0, I will need to use the reinitialization stack, or live with the fact
// that I may miss some propagations for reactivated clauses.
SASSERT(scope_lvl() == 0);
// compute
SASSERT(scope_lvl() == 0);
// compute
// d_tk
unsigned h = 0;
unsigned V_tk = 0;
@ -1153,7 +1168,7 @@ namespace sat {
double d_tk = V_tk == 0 ? static_cast<double>(num_vars() + 1) : static_cast<double>(h)/static_cast<double>(V_tk);
if (d_tk < m_min_d_tk)
m_min_d_tk = d_tk;
TRACE("sat_frozen", tout << "m_min_d_tk: " << m_min_d_tk << "\n";);
TRACE("sat_frozen", tout << "m_min_d_tk: " << m_min_d_tk << "\n";);
unsigned frozen = 0;
unsigned deleted = 0;
unsigned activated = 0;
@ -1219,15 +1234,15 @@ namespace sat {
++it2;
}
m_learned.set_end(it2);
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :d_tk " << d_tk << " :min-d_tk " << m_min_d_tk <<
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :d_tk " << d_tk << " :min-d_tk " << m_min_d_tk <<
" :frozen " << frozen << " :activated " << activated << " :deleted " << deleted << ")\n";);
}
// return true if should keep the clause, and false if we should delete it.
bool solver::activate_frozen_clause(clause & c) {
bool solver::activate_frozen_clause(clause & c) {
TRACE("sat_gc", tout << "reactivating:\n" << c << "\n";);
SASSERT(scope_lvl() == 0);
// do some cleanup
// do some cleanup
unsigned sz = c.size();
unsigned j = 0;
for (unsigned i = 0; i < sz; i++) {
@ -1291,13 +1306,13 @@ namespace sat {
bool solver::resolve_conflict() {
while (true) {
bool r = resolve_conflict_core();
CASSERT("sat_check_marks", check_marks());
// after pop, clauses are reinitialized, this may trigger another conflict.
if (!r)
if (!r)
return false;
if (!inconsistent())
return true;
}
CASSERT("sat_check_marks", check_marks());
}
bool solver::resolve_conflict_core() {
@ -1312,7 +1327,7 @@ namespace sat {
if (m_conflict_lvl == 0)
return false;
m_lemma.reset();
forget_phase_of_vars(m_conflict_lvl);
unsigned idx = skip_literals_above_conflict_level();
@ -1324,10 +1339,10 @@ namespace sat {
TRACE("sat_conflict", tout << "not_l: " << m_not_l << "\n";);
process_antecedent(m_not_l, num_marks);
}
literal consequent = m_not_l;
justification js = m_conflict;
do {
TRACE("sat_conflict_detail", tout << "processing consequent: " << consequent << "\n";
tout << "num_marks: " << num_marks << ", js kind: " << js.get_kind() << "\n";);
@ -1363,7 +1378,7 @@ namespace sat {
fill_ext_antecedents(consequent, js);
literal_vector::iterator it = m_ext_antecedents.begin();
literal_vector::iterator end = m_ext_antecedents.end();
for (; it != end; ++it)
for (; it != end; ++it)
process_antecedent(*it, num_marks);
break;
}
@ -1371,10 +1386,10 @@ namespace sat {
UNREACHABLE();
break;
}
while (true) {
literal l = m_trail[idx];
if (is_marked(l.var()))
if (is_marked(l.var()))
break;
SASSERT(idx > 0);
idx--;
@ -1387,9 +1402,9 @@ namespace sat {
idx--;
num_marks--;
reset_mark(c_var);
}
}
while (num_marks > 0);
m_lemma[0] = ~consequent;
TRACE("sat_lemma", tout << "new lemma size: " << m_lemma.size() << "\n" << m_lemma << "\n";);
@ -1400,7 +1415,9 @@ namespace sat {
dyn_sub_res();
TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";);
}
else
reset_lemma_var_marks();
literal_vector::iterator it = m_lemma.begin();
literal_vector::iterator end = m_lemma.end();
unsigned new_scope_lvl = 0;
@ -1431,10 +1448,10 @@ namespace sat {
return 0;
unsigned r = 0;
if (consequent != null_literal)
r = lvl(consequent);
switch (js.get_kind()) {
case justification::NONE:
break;
@ -1467,7 +1484,7 @@ namespace sat {
fill_ext_antecedents(consequent, js);
literal_vector::iterator it = m_ext_antecedents.begin();
literal_vector::iterator end = m_ext_antecedents.end();
for (; it != end; ++it)
for (; it != end; ++it)
r = std::max(r, lvl(*it));
break;
}
@ -1496,7 +1513,7 @@ namespace sat {
}
return idx;
}
void solver::process_antecedent(literal antecedent, unsigned & num_marks) {
bool_var var = antecedent.var();
unsigned var_lvl = lvl(var);
@ -1510,7 +1527,7 @@ namespace sat {
m_lemma.push_back(~antecedent);
}
}
/**
\brief js is an external justification. Collect its antecedents and store at m_ext_antecedents.
*/
@ -1577,7 +1594,7 @@ namespace sat {
unsigned var_lvl = lvl(var);
if (!is_marked(var) && var_lvl > 0) {
if (m_lvl_set.may_contain(var_lvl)) {
mark(var);
mark(var);
m_unmark.push_back(var);
m_lemma_min_stack.push_back(var);
}
@ -1589,17 +1606,17 @@ namespace sat {
}
/**
\brief Return true if lit is implied by other marked literals
and/or literals assigned at the base level.
The set lvl_set is used as an optimization.
\brief Return true if lit is implied by other marked literals
and/or literals assigned at the base level.
The set lvl_set is used as an optimization.
The idea is to stop the recursive search with a failure
as soon as we find a literal assigned in a level that is not in lvl_set.
as soon as we find a literal assigned in a level that is not in lvl_set.
*/
bool solver::implied_by_marked(literal lit) {
m_lemma_min_stack.reset(); // avoid recursive function
m_lemma_min_stack.push_back(lit.var());
unsigned old_size = m_unmark.size();
while (!m_lemma_min_stack.empty()) {
bool_var var = m_lemma_min_stack.back();
m_lemma_min_stack.pop_back();
@ -1700,7 +1717,7 @@ namespace sat {
void solver::minimize_lemma() {
m_unmark.reset();
updt_lemma_lvl_set();
unsigned sz = m_lemma.size();
unsigned i = 1; // the first literal is the FUIP
unsigned j = 1;
@ -1716,12 +1733,12 @@ namespace sat {
j++;
}
}
reset_unmark(0);
m_lemma.shrink(j);
m_stats.m_minimized_lits += sz - j;
}
/**
\brief Reset the mark of the variables in the current lemma.
*/
@ -1741,17 +1758,17 @@ namespace sat {
Only binary and ternary clauses are used.
*/
void solver::dyn_sub_res() {
unsigned sz = m_lemma.size();
unsigned sz = m_lemma.size();
for (unsigned i = 0; i < sz; i++) {
mark_lit(m_lemma[i]);
}
literal l0 = m_lemma[0];
// l0 is the FUIP, and we never remove the FUIP.
//
//
// In the following loop, we use unmark_lit(l) to remove a
// literal from m_lemma.
for (unsigned i = 0; i < sz; i++) {
literal l = m_lemma[i];
if (!is_marked_lit(l))
@ -1763,8 +1780,8 @@ namespace sat {
for (; it != end; ++it) {
// In this for-loop, the conditions l0 != ~l2 and l0 != ~l3
// are not really needed if the solver does not miss unit propagations.
// However, we add them anyway because we don't want to rely on this
// property of the propagator.
// However, we add them anyway because we don't want to rely on this
// property of the propagator.
// For example, if this property is relaxed in the future, then the code
// without the conditions l0 != ~l2 and l0 != ~l3 may remove the FUIP
if (it->is_binary_clause()) {
@ -1810,10 +1827,10 @@ namespace sat {
// p1 \/ ~p2
// p2 \/ ~p3
// p3 \/ ~p4
// q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2
// q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2
// ~q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2
// ~q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2
// q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2
// q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2
// ~q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2
// ~q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2
// ...
//
// 2. Now suppose we learned the lemma
@ -1824,15 +1841,15 @@ namespace sat {
// That is, l \/ l2 is an implied clause. Note that probing does not add
// this clause to the clause database (there are too many).
//
// 4. Lemma (*) is deleted (garbage collected).
// 4. Lemma (*) is deleted (garbage collected).
//
// 5. l is decided to be false, p1, p2, p3 and p4 are propagated using BCP,
// but l2 is not since the lemma (*) was deleted.
//
//
// Probing module still "knows" that l \/ l2 is valid binary clause
//
//
// 6. A new lemma is created where ~l2 is the FUIP and the lemma also contains l.
// If we remove l0 != ~l2 may try to delete the FUIP.
// If we remove l0 != ~l2 may try to delete the FUIP.
if (is_marked_lit(~l2) && l0 != ~l2) {
// eliminate ~l2 from lemma because we have the clause l \/ l2
unmark_lit(~l2);
@ -1843,7 +1860,7 @@ namespace sat {
// can't eliminat FUIP
SASSERT(is_marked_lit(m_lemma[0]));
unsigned j = 0;
for (unsigned i = 0; i < sz; i++) {
literal l = m_lemma[i];
@ -1855,7 +1872,7 @@ namespace sat {
}
m_stats.m_dyn_sub_res += sz - j;
SASSERT(j >= 1);
m_lemma.shrink(j);
}
@ -1948,7 +1965,7 @@ namespace sat {
// Misc
//
// -----------------------
void solver::updt_params(params_ref const & p) {
m_params = p;
m_config.updt_params(p);
@ -1970,8 +1987,8 @@ namespace sat {
void solver::set_cancel(bool f) {
m_cancel = f;
}
void solver::collect_statistics(statistics & st) {
void solver::collect_statistics(statistics & st) {
m_stats.collect_statistics(st);
m_cleaner.collect_statistics(st);
m_simplifier.collect_statistics(st);
@ -2066,7 +2083,7 @@ namespace sat {
}
}
}
void solver::display_units(std::ostream & out) const {
unsigned end = scope_lvl() == 0 ? m_trail.size() : m_scopes[0].m_trail_lim;
for (unsigned i = 0; i < end; i++) {
@ -2220,26 +2237,26 @@ namespace sat {
// Simplification
//
// -----------------------
void solver::cleanup() {
if (scope_lvl() > 0 || inconsistent())
return;
void solver::cleanup() {
if (scope_lvl() > 0 || inconsistent())
return;
if (m_cleaner() && m_ext)
m_ext->clauses_modifed();
}
void solver::simplify(bool learned) {
if (scope_lvl() > 0 || inconsistent())
return;
m_simplifier(learned);
m_simplifier.free_memory();
void solver::simplify(bool learned) {
if (scope_lvl() > 0 || inconsistent())
return;
m_simplifier(learned);
m_simplifier.free_memory();
if (m_ext)
m_ext->clauses_modifed();
}
unsigned solver::scc_bin() {
if (scope_lvl() > 0 || inconsistent())
return 0;
unsigned r = m_scc();
unsigned solver::scc_bin() {
if (scope_lvl() > 0 || inconsistent())
return 0;
unsigned r = m_scc();
if (r > 0 && m_ext)
m_ext->clauses_modifed();
return r;
@ -2313,10 +2330,10 @@ namespace sat {
out << " :inconsistent " << (m_inconsistent ? "true" : "false") << "\n";
out << " :vars " << num_vars() << "\n";
out << " :elim-vars " << num_elim << "\n";
out << " :lits " << num_lits << "\n";
out << " :lits " << num_lits << "\n";
out << " :assigned " << m_trail.size() << "\n";
out << " :binary-clauses " << num_bin << "\n";
out << " :ternary-clauses " << num_ter << "\n";
out << " :binary-clauses " << num_bin << "\n";
out << " :ternary-clauses " << num_ter << "\n";
out << " :clauses " << num_cls << "\n";
out << " :del-clause " << m_stats.m_del_clause << "\n";
out << " :avg-clause-size " << (total_cls == 0 ? 0.0 : static_cast<double>(num_lits) / static_cast<double>(total_cls)) << "\n";

View file

@ -30,6 +30,7 @@ Revision History:
#include"polynomial_cmds.h"
#include"subpaving_cmds.h"
#include"smt_strategic_solver.h"
#include"smt_solver.h"
extern bool g_display_statistics;
extern void display_config();
@ -106,6 +107,7 @@ unsigned read_smtlib2_commands(char const * file_name) {
cmd_context ctx;
ctx.set_solver_factory(mk_smt_strategic_solver_factory());
ctx.set_interpolating_solver_factory(mk_smt_solver_factory());
install_dl_cmds(ctx);
install_dbg_cmds(ctx);

View file

@ -27,6 +27,7 @@ void qi_params::updt_params(params_ref const & _p) {
m_mbqi_max_iterations = p.mbqi_max_iterations();
m_mbqi_trace = p.mbqi_trace();
m_mbqi_force_template = p.mbqi_force_template();
m_mbqi_id = p.mbqi_id();
m_qi_profile = p.qi_profile();
m_qi_profile_freq = p.qi_profile_freq();
m_qi_max_instances = p.qi_max_instances();

View file

@ -51,6 +51,7 @@ struct qi_params {
unsigned m_mbqi_max_iterations;
bool m_mbqi_trace;
unsigned m_mbqi_force_template;
const char * m_mbqi_id;
qi_params(params_ref const & p = params_ref()):
/*
@ -97,7 +98,9 @@ struct qi_params {
m_mbqi_max_cexs_incr(1),
m_mbqi_max_iterations(1000),
m_mbqi_trace(false),
m_mbqi_force_template(10) {
m_mbqi_force_template(10),
m_mbqi_id(0)
{
updt_params(p);
}

View file

@ -40,6 +40,7 @@ void smt_params::updt_local_params(params_ref const & _p) {
m_arith_pivot_strategy = ARITH_PIVOT_GREATEST_ERROR;
else if (_p.get_bool("arith.least_error_pivot", false))
m_arith_pivot_strategy = ARITH_PIVOT_LEAST_ERROR;
theory_array_params::updt_params(_p);
}
void smt_params::updt_params(params_ref const & p) {
@ -48,6 +49,7 @@ void smt_params::updt_params(params_ref const & p) {
theory_arith_params::updt_params(p);
theory_bv_params::updt_params(p);
theory_pb_params::updt_params(p);
// theory_array_params::updt_params(p);
updt_local_params(p);
}

View file

@ -21,6 +21,7 @@ def_module_params(module_name='smt',
('mbqi.max_iterations', UINT, 1000, 'maximum number of rounds of MBQI'),
('mbqi.trace', BOOL, False, 'generate tracing messages for Model Based Quantifier Instantiation (MBQI). It will display a message before every round of MBQI, and the quantifiers that were not satisfied'),
('mbqi.force_template', UINT, 10, 'some quantifiers can be used as templates for building interpretations for functions. Z3 uses heuristics to decide whether a quantifier will be used as a template or not. Quantifiers with weight >= mbqi.force_template are forced to be used as a template'),
('mbqi.id', STRING, '', 'Only use model-based instantiation for quantifiers with id\'s beginning with string'),
('qi.profile', BOOL, False, 'profile quantifier instantiation'),
('qi.profile_freq', UINT, UINT_MAX, 'how frequent results are reported by qi.profile'),
('qi.max_instances', UINT, UINT_MAX, 'maximum number of quantifier instantiations'),
@ -44,5 +45,7 @@ def_module_params(module_name='smt',
('arith.ignore_int', BOOL, False, 'treat integer variables as real'),
('pb.conflict_frequency', UINT, 0, 'conflict frequency for Pseudo-Boolean theory'),
('pb.learn_complements', BOOL, True, 'learn complement literals for Pseudo-Boolean theory'),
('pb.enable_compilation', BOOL, True, 'enable compilation into sorting circuits for Pseudo-Boolean')))
('pb.enable_compilation', BOOL, True, 'enable compilation into sorting circuits for Pseudo-Boolean'),
('array.weak', BOOL, False, 'weak array theory'),
('array.extensional', BOOL, True, 'extensional array theory')
))

View file

@ -0,0 +1,28 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_array_params.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-06.
Revision History:
--*/
#include"theory_array_params.h"
#include"smt_params_helper.hpp"
void theory_array_params::updt_params(params_ref const & _p) {
smt_params_helper p(_p);
m_array_weak = p.array_weak();
m_array_extensional = p.array_extensional();
}

View file

@ -51,6 +51,9 @@ struct theory_array_params : public array_simplifier_params {
m_array_lazy_ieq_delay(10) {
}
void updt_params(params_ref const & _p);
#if 0
void register_params(ini_params & p) {
p.register_int_param("array_solver", 0, 3, reinterpret_cast<int&>(m_array_mode), "0 - no array, 1 - simple, 2 - model based, 3 - full");

View file

@ -322,6 +322,7 @@ namespace smt {
for (; it != end; ++it) {
quantifier * q = *it;
if(!m_qm->mbqi_enabled(q)) continue;
if (m_context->is_relevant(q) && m_context->get_assignment(q) == l_true) {
if (m_params.m_mbqi_trace && q->get_qid() != symbol::null) {
verbose_stream() << "(smt.mbqi :checking " << q->get_qid() << ")\n";

View file

@ -335,6 +335,10 @@ namespace smt {
return m_imp->m_plugin->model_based();
}
bool quantifier_manager::mbqi_enabled(quantifier *q) const {
return m_imp->m_plugin->mbqi_enabled(q);
}
void quantifier_manager::adjust_model(proto_model * m) {
m_imp->m_plugin->adjust_model(m);
}
@ -434,10 +438,24 @@ namespace smt {
virtual bool model_based() const { return m_fparams->m_mbqi; }
virtual bool mbqi_enabled(quantifier *q) const {
if(!m_fparams->m_mbqi_id) return true;
const symbol &s = q->get_qid();
unsigned len = strlen(m_fparams->m_mbqi_id);
if(s == symbol::null || s.is_numerical())
return len == 0;
return strncmp(s.bare_str(),m_fparams->m_mbqi_id,static_cast<unsigned>(len)) == 0;
}
/* Quantifier id's must begin with the prefix specified by
parameter mbqi.id to be instantiated with MBQI. The default
value is the empty string, so all quantifiers are
instantiated.
*/
virtual void add(quantifier * q) {
if (m_fparams->m_mbqi) {
m_model_finder->register_quantifier(q);
}
if (m_fparams->m_mbqi && mbqi_enabled(q)) {
m_model_finder->register_quantifier(q);
}
}
virtual void del(quantifier * q) {

View file

@ -75,6 +75,7 @@ namespace smt {
};
bool model_based() const;
bool mbqi_enabled(quantifier *q) const; // can mbqi instantiate this quantifier?
void adjust_model(proto_model * m);
check_model_result check_model(proto_model * m, obj_map<enode, app *> const & root2value);
@ -144,6 +145,11 @@ namespace smt {
*/
virtual bool model_based() const = 0;
/**
\brief Is "model based" instantiate allowed to instantiate this quantifier?
*/
virtual bool mbqi_enabled(quantifier *q) const {return true;}
/**
\brief Give a change to the plugin to adjust the interpretation of unintepreted functions.
It can basically change the "else" of each uninterpreted function.

View file

@ -50,7 +50,7 @@ class factor_tactic : public tactic {
return args[0];
return m_util.mk_mul(sz, args);
}
expr * mk_zero_for(expr * arg) {
return m_util.mk_numeral(rational(0), m_util.is_int(arg));
}
@ -92,10 +92,10 @@ class factor_tactic : public tactic {
return k;
}
}
// p1^{2*k1} * p2^{2*k2 + 1} >=< 0
// -->
// (p1^2)*p2 >=<0
// (p1^2)*p2 >=<0
void mk_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) {
SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE);
expr_ref_buffer args(m);
@ -127,7 +127,7 @@ class factor_tactic : public tactic {
}
}
}
// Strict case
// p1^{2*k1} * p2^{2*k2 + 1} >< 0
// -->
@ -158,11 +158,11 @@ class factor_tactic : public tactic {
args.push_back(m.mk_app(m_util.get_family_id(), k, mk_mul(odd_factors.size(), odd_factors.c_ptr()), mk_zero_for(odd_factors[0])));
}
SASSERT(!args.empty());
if (args.size() == 1)
if (args.size() == 1)
result = args[0];
else if (strict)
result = m.mk_and(args.size(), args.c_ptr());
else
else
result = m.mk_or(args.size(), args.c_ptr());
}
@ -173,7 +173,7 @@ class factor_tactic : public tactic {
scoped_mpz d2(m_qm);
m_expr2poly.to_polynomial(lhs, p1, d1);
m_expr2poly.to_polynomial(rhs, p2, d2);
TRACE("factor_tactic_bug",
TRACE("factor_tactic_bug",
tout << "lhs: " << mk_ismt2_pp(lhs, m) << "\n";
tout << "p1: " << p1 << "\n";
tout << "d1: " << d1 << "\n";
@ -195,18 +195,18 @@ class factor_tactic : public tactic {
SASSERT(fs.distinct_factors() > 0);
TRACE("factor_tactic_bug", tout << "factors:\n"; fs.display(tout); tout << "\n";);
if (fs.distinct_factors() == 1 && fs.get_degree(0) == 1)
return BR_FAILED;
return BR_FAILED;
if (m.is_eq(f)) {
if (m_split_factors)
mk_split_eq(fs, result);
else
else
mk_eq(fs, result);
}
else {
decl_kind k = f->get_decl_kind();
if (m_qm.is_neg(fs.get_constant()))
k = flip(k);
if (m_split_factors)
mk_split_comp(k, fs, result);
else
@ -215,10 +215,10 @@ class factor_tactic : public tactic {
return BR_DONE;
}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
if (num != 2)
return BR_FAILED;
if (m.is_eq(f) && (m_util.is_arith_expr(args[0]) || m_util.is_arith_expr(args[1])))
if (m.is_eq(f) && (m_util.is_arith_expr(args[0]) || m_util.is_arith_expr(args[1])) && (!m.is_bool(args[0])))
return factor(f, args[0], args[1], result);
if (f->get_family_id() != m_util.get_family_id())
return BR_FAILED;
@ -232,10 +232,10 @@ class factor_tactic : public tactic {
return BR_FAILED;
}
};
struct rw : public rewriter_tpl<rw_cfg> {
rw_cfg m_cfg;
rw(ast_manager & m, params_ref const & p):
rewriter_tpl<rw_cfg>(m, m.proofs_enabled(), m_cfg),
m_cfg(m, p) {
@ -245,24 +245,24 @@ class factor_tactic : public tactic {
struct imp {
ast_manager & m;
rw m_rw;
imp(ast_manager & _m, params_ref const & p):
m(_m),
m_rw(m, p) {
}
void set_cancel(bool f) {
m_rw.set_cancel(f);
m_rw.cfg().m_pm.set_cancel(f);
}
void updt_params(params_ref const & p) {
m_rw.cfg().updt_params(p);
}
void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
SASSERT(g->is_well_sorted());
@ -288,7 +288,7 @@ class factor_tactic : public tactic {
SASSERT(g->is_well_sorted());
}
};
imp * m_imp;
params_ref m_params;
public:
@ -300,7 +300,7 @@ public:
virtual tactic * translate(ast_manager & m) {
return alloc(factor_tactic, m, m_params);
}
virtual ~factor_tactic() {
dealloc(m_imp);
}
@ -311,14 +311,14 @@ public:
}
virtual void collect_param_descrs(param_descrs & r) {
r.insert("split_factors", CPK_BOOL,
r.insert("split_factors", CPK_BOOL,
"(default: true) apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0)).");
polynomial::factor_params::get_param_descrs(r);
}
virtual void operator()(goal_ref const & in,
goal_ref_buffer & result,
model_converter_ref & mc,
virtual void operator()(goal_ref const & in,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
try {
@ -331,7 +331,7 @@ public:
throw tactic_exception(ex.msg());
}
}
virtual void cleanup() {
ast_manager & m = m_imp->m;
imp * d = m_imp;

View file

@ -480,7 +480,10 @@ private:
break;
}
SASSERT (i < sz);
if (i >= sz) {
// [Christoph]: In this case, all the m_a are equal to m_c.
return;
}
// copy lits [0, i) to m_clause
for (unsigned j = 0; j < i; j++)
@ -500,6 +503,7 @@ private:
}
void mk_pbc(polynomial & m_p, numeral & m_c, expr_ref & r, bool enable_split) {
TRACE("mk_pbc", display(tout, m_p, m_c); );
if (m_c.is_nonpos()) {
// constraint is equivalent to true.
r = m.mk_true();
@ -507,7 +511,7 @@ private:
}
polynomial::iterator it = m_p.begin();
polynomial::iterator end = m_p.end();
numeral a_gcd = it->m_a;
numeral a_gcd = (it->m_a > m_c) ? m_c : it->m_a;
for (; it != end; ++it) {
if (it->m_a > m_c)
it->m_a = m_c; // trimming coefficients
@ -520,6 +524,7 @@ private:
it->m_a /= a_gcd;
m_c = ceil(m_c/a_gcd);
}
TRACE("mk_pbc", tout << "GCD = " << a_gcd << "; Normalized: "; display(tout, m_p, m_c); tout << "\n"; );
it = m_p.begin();
numeral a_sum;
for (; it != end; ++it) {

View file

@ -140,7 +140,7 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) {
s_sig = m_bv_util.mk_sort(sbits-1);
s_exp = m_bv_util.mk_sort(ebits);
#ifdef _DEBUG
#ifdef Z3DEBUG
std::string p("fpa2bv");
std::string name = f->get_name().str();
@ -271,7 +271,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) {
SASSERT(is_rm_sort(f->get_range()));
result = m.mk_fresh_const(
#ifdef _DEBUG
#ifdef Z3DEBUG
"fpa2bv_rm"
#else
0
@ -1588,8 +1588,7 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr *
v2 = x;
unsigned ebits = m_util.get_ebits(f->get_range());
unsigned sbits = m_util.get_sbits(f->get_range());
SASSERT(ebits < sbits);
unsigned sbits = m_util.get_sbits(f->get_range());
expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m);
unpack(x, a_sgn, a_sig, a_exp, a_lz, true);
@ -1619,9 +1618,22 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr *
expr_ref shift(m), r_shifted(m), l_shifted(m);
shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits-1, ebits+1),
m_bv_util.mk_sign_extend(1, a_exp));
r_shifted = m_bv_util.mk_bv_lshr(a_sig, m_bv_util.mk_zero_extend(sbits-ebits-1, shift));
if (sbits > (ebits+1))
r_shifted = m_bv_util.mk_bv_lshr(a_sig, m_bv_util.mk_zero_extend(sbits-(ebits+1), shift));
else if (sbits < (ebits+1))
r_shifted = m_bv_util.mk_extract(ebits, ebits-sbits+1, m_bv_util.mk_bv_lshr(m_bv_util.mk_zero_extend(ebits+1-sbits, a_sig), shift));
else // sbits == ebits+1
r_shifted = m_bv_util.mk_bv_lshr(a_sig, shift);
SASSERT(is_well_sorted(m, r_shifted));
SASSERT(m_bv_util.get_bv_size(r_shifted) == sbits);
l_shifted = m_bv_util.mk_bv_shl(r_shifted, m_bv_util.mk_zero_extend(sbits-ebits-1, shift));
if (sbits > (ebits+1))
l_shifted = m_bv_util.mk_bv_shl(r_shifted, m_bv_util.mk_zero_extend(sbits-(ebits+1), shift));
else if (sbits < (ebits+1))
l_shifted = m_bv_util.mk_extract(ebits, ebits-sbits+1, m_bv_util.mk_bv_shl(m_bv_util.mk_zero_extend(ebits+1-sbits, r_shifted), shift));
else // sbits == ebits+1
l_shifted = m_bv_util.mk_bv_shl(r_shifted, shift);
SASSERT(is_well_sorted(m, l_shifted));
SASSERT(m_bv_util.get_bv_size(l_shifted) == sbits);
res_sig = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, 1),
@ -1825,146 +1837,158 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a
mk_triple(args[0], args[1], args[2], result);
}
else if (num == 2 && is_app(args[1]) && m_util.is_float(m.get_sort(args[1]))) {
// We also support float to float conversion
// We also support float to float conversion
sort * s = f->get_range();
expr_ref rm(m), x(m);
rm = args[0];
x = args[1];
expr_ref c1(m), c2(m), c3(m), c4(m), c5(m);
expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m);
expr_ref one1(m);
one1 = m_bv_util.mk_numeral(1, 1);
expr_ref ninf(m), pinf(m);
mk_plus_inf(f, pinf);
mk_minus_inf(f, ninf);
// NaN -> NaN
mk_is_nan(x, c1);
mk_nan(f, v1);
// +0 -> +0
mk_is_pzero(x, c2);
mk_pzero(f, v2);
// -0 -> -0
mk_is_nzero(x, c3);
mk_nzero(f, v3);
// +oo -> +oo
mk_is_pinf(x, c4);
v4 = pinf;
// -oo -> -oo
mk_is_ninf(x, c5);
v5 = ninf;
// otherwise: the actual conversion with rounding.
sort * s = f->get_range();
expr_ref sgn(m), sig(m), exp(m), lz(m);
unpack(x, sgn, sig, exp, lz, true);
dbg_decouple("fpa2bv_to_float_x_sig", sig);
dbg_decouple("fpa2bv_to_float_x_exp", exp);
dbg_decouple("fpa2bv_to_float_lz", lz);
expr_ref res_sgn(m), res_sig(m), res_exp(m);
res_sgn = sgn;
unsigned from_sbits = m_util.get_sbits(m.get_sort(args[1]));
unsigned from_ebits = m_util.get_ebits(m.get_sort(args[1]));
unsigned from_sbits = m_util.get_sbits(m.get_sort(x));
unsigned from_ebits = m_util.get_ebits(m.get_sort(x));
unsigned to_sbits = m_util.get_sbits(s);
unsigned to_ebits = m_util.get_ebits(s);
SASSERT(m_bv_util.get_bv_size(sgn) == 1);
SASSERT(m_bv_util.get_bv_size(sig) == from_sbits);
SASSERT(m_bv_util.get_bv_size(exp) == from_ebits);
SASSERT(m_bv_util.get_bv_size(lz) == from_ebits);
if (from_sbits == to_sbits && from_ebits == to_ebits)
result = x;
else {
expr_ref c1(m), c2(m), c3(m), c4(m), c5(m);
expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m);
expr_ref one1(m);
if (from_sbits < (to_sbits + 3))
{
// make sure that sig has at least to_sbits + 3
res_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, to_sbits+3-from_sbits));
}
else if (from_sbits > (to_sbits + 3))
{
// collapse the extra bits into a sticky bit.
expr_ref sticky(m), low(m), high(m);
low = m_bv_util.mk_extract(from_sbits - to_sbits - 3, 0, sig);
high = m_bv_util.mk_extract(from_sbits - 1, from_sbits - to_sbits - 2, sig);
sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, low.get());
res_sig = m_bv_util.mk_concat(high, sticky);
}
else
res_sig = sig;
one1 = m_bv_util.mk_numeral(1, 1);
expr_ref ninf(m), pinf(m);
mk_plus_inf(f, pinf);
mk_minus_inf(f, ninf);
res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder.
unsigned sig_sz = m_bv_util.get_bv_size(res_sig);
SASSERT(sig_sz == to_sbits+4);
// NaN -> NaN
mk_is_nan(x, c1);
mk_nan(f, v1);
expr_ref exponent_overflow(m);
exponent_overflow = m.mk_false();
// +0 -> +0
mk_is_pzero(x, c2);
mk_pzero(f, v2);
if (from_ebits < (to_ebits + 2))
{
res_exp = m_bv_util.mk_sign_extend(to_ebits-from_ebits+2, exp);
}
else if (from_ebits > (to_ebits + 2))
{
expr_ref high(m), low(m), lows(m), high_red_or(m), high_red_and(m), h_or_eq(m), h_and_eq(m);
expr_ref no_ovf(m), zero1(m), s_is_one(m), s_is_zero(m);
high = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, exp);
low = m_bv_util.mk_extract(to_ebits+1, 0, exp);
lows = m_bv_util.mk_extract(to_ebits+1, to_ebits+1, low);
// -0 -> -0
mk_is_nzero(x, c3);
mk_nzero(f, v3);
// +oo -> +oo
mk_is_pinf(x, c4);
v4 = pinf;
// -oo -> -oo
mk_is_ninf(x, c5);
v5 = ninf;
high_red_or = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, high.get());
high_red_and = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, high.get());
// otherwise: the actual conversion with rounding.
expr_ref sgn(m), sig(m), exp(m), lz(m);
unpack(x, sgn, sig, exp, lz, true);
zero1 = m_bv_util.mk_numeral(0, 1);
m_simp.mk_eq(high_red_and, one1, h_and_eq);
m_simp.mk_eq(high_red_or, zero1, h_or_eq);
m_simp.mk_eq(lows, zero1, s_is_zero);
m_simp.mk_eq(lows, one1, s_is_one);
dbg_decouple("fpa2bv_to_float_x_sig", sig);
dbg_decouple("fpa2bv_to_float_x_exp", exp);
dbg_decouple("fpa2bv_to_float_lz", lz);
expr_ref res_sgn(m), res_sig(m), res_exp(m);
res_sgn = sgn;
SASSERT(m_bv_util.get_bv_size(sgn) == 1);
SASSERT(m_bv_util.get_bv_size(sig) == from_sbits);
SASSERT(m_bv_util.get_bv_size(exp) == from_ebits);
SASSERT(m_bv_util.get_bv_size(lz) == from_ebits);
if (from_sbits < (to_sbits + 3))
{
// make sure that sig has at least to_sbits + 3
res_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, to_sbits+3-from_sbits));
}
else if (from_sbits > (to_sbits + 3))
{
// collapse the extra bits into a sticky bit.
expr_ref sticky(m), low(m), high(m);
low = m_bv_util.mk_extract(from_sbits - to_sbits - 3, 0, sig);
high = m_bv_util.mk_extract(from_sbits - 1, from_sbits - to_sbits - 2, sig);
sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, low.get());
res_sig = m_bv_util.mk_concat(high, sticky);
}
else
res_sig = sig;
res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder.
unsigned sig_sz = m_bv_util.get_bv_size(res_sig);
SASSERT(sig_sz == to_sbits+4);
expr_ref exponent_overflow(m);
exponent_overflow = m.mk_false();
if (from_ebits < (to_ebits + 2))
{
res_exp = m_bv_util.mk_sign_extend(to_ebits-from_ebits+2, exp);
// subtract lz for subnormal numbers.
expr_ref lz_ext(m);
lz_ext = m_bv_util.mk_zero_extend(to_ebits-from_ebits+2, lz);
res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext);
}
else if (from_ebits > (to_ebits + 2))
{
expr_ref high(m), low(m), lows(m), high_red_or(m), high_red_and(m), h_or_eq(m), h_and_eq(m);
expr_ref no_ovf(m), zero1(m), s_is_one(m), s_is_zero(m);
high = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, exp);
low = m_bv_util.mk_extract(to_ebits+1, 0, exp);
lows = m_bv_util.mk_extract(to_ebits+1, to_ebits+1, low);
expr_ref c2(m);
m_simp.mk_ite(h_or_eq, s_is_one, m.mk_false(), c2);
m_simp.mk_ite(h_and_eq, s_is_zero, c2, exponent_overflow);
high_red_or = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, high.get());
high_red_and = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, high.get());
zero1 = m_bv_util.mk_numeral(0, 1);
m_simp.mk_eq(high_red_and, one1, h_and_eq);
m_simp.mk_eq(high_red_or, zero1, h_or_eq);
m_simp.mk_eq(lows, zero1, s_is_zero);
m_simp.mk_eq(lows, one1, s_is_one);
// Note: Upon overflow, we _could_ try to shift the significand around...
expr_ref c2(m);
m_simp.mk_ite(h_or_eq, s_is_one, m.mk_false(), c2);
m_simp.mk_ite(h_and_eq, s_is_zero, c2, exponent_overflow);
// Note: Upon overflow, we _could_ try to shift the significand around...
res_exp = low;
}
else
res_exp = exp;
// subtract lz for subnormal numbers.
expr_ref lz_ext(m), lz_rest(m), lz_redor(m), lz_redor_bool(m);
lz_ext = m_bv_util.mk_extract(to_ebits+1, 0, lz);
lz_rest = m_bv_util.mk_extract(from_ebits-1, to_ebits+2, lz);
lz_redor = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, lz_rest.get());
m_simp.mk_eq(lz_redor, one1, lz_redor_bool);
m_simp.mk_or(exponent_overflow, lz_redor_bool, exponent_overflow);
// subtract lz for subnormal numbers.
expr_ref lz_ext(m);
lz_ext = m_bv_util.mk_zero_extend(to_ebits-from_ebits+2, lz);
res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext);
SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits+2);
res_exp = m_bv_util.mk_bv_sub(low, lz_ext);
}
else // from_ebits == (to_ebits + 2)
res_exp = m_bv_util.mk_bv_sub(exp, lz);
dbg_decouple("fpa2bv_to_float_res_sig", res_sig);
dbg_decouple("fpa2bv_to_float_res_exp", res_exp);
SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits+2);
SASSERT(is_well_sorted(m, res_exp));
expr_ref rounded(m);
round(s, rm, res_sgn, res_sig, res_exp, rounded);
dbg_decouple("fpa2bv_to_float_res_sig", res_sig);
dbg_decouple("fpa2bv_to_float_res_exp", res_exp);
expr_ref is_neg(m), sig_inf(m);
m_simp.mk_eq(sgn, one1, is_neg);
mk_ite(is_neg, ninf, pinf, sig_inf);
expr_ref rounded(m);
round(s, rm, res_sgn, res_sig, res_exp, rounded);
expr_ref is_neg(m), sig_inf(m);
m_simp.mk_eq(sgn, one1, is_neg);
mk_ite(is_neg, ninf, pinf, sig_inf);
dbg_decouple("fpa2bv_to_float_exp_ovf", exponent_overflow);
dbg_decouple("fpa2bv_to_float_exp_ovf", exponent_overflow);
mk_ite(exponent_overflow, sig_inf, rounded, v6);
mk_ite(exponent_overflow, sig_inf, rounded, v6);
// And finally, we tie them together.
mk_ite(c5, v5, v6, result);
mk_ite(c4, v4, result, result);
mk_ite(c3, v3, result, result);
mk_ite(c2, v2, result, result);
mk_ite(c1, v1, result, result);
// And finally, we tie them together.
mk_ite(c5, v5, v6, result);
mk_ite(c4, v4, result, result);
mk_ite(c3, v3, result, result);
mk_ite(c2, v2, result, result);
mk_ite(c1, v1, result, result);
}
}
else {
// .. other than that, we only support rationals for asFloat
@ -2167,14 +2191,14 @@ void fpa2bv_converter::mk_bot_exp(unsigned sz, expr_ref & result) {
}
void fpa2bv_converter::mk_min_exp(unsigned ebits, expr_ref & result) {
SASSERT(ebits > 0);
SASSERT(ebits >= 2);
const mpz & z = m_mpf_manager.m_powers2.m1(ebits-1, true);
result = m_bv_util.mk_numeral(z + mpz(1), ebits);
}
void fpa2bv_converter::mk_max_exp(unsigned ebits, expr_ref & result) {
SASSERT(ebits > 0);
result = m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(ebits-1, false), ebits);
SASSERT(ebits >= 2);
result = m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(ebits-1, false), ebits);
}
void fpa2bv_converter::mk_leading_zeros(expr * e, unsigned max_bits, expr_ref & result) {
@ -2220,14 +2244,14 @@ void fpa2bv_converter::mk_bias(expr * e, expr_ref & result) {
unsigned ebits = m_bv_util.get_bv_size(e);
SASSERT(ebits >= 2);
expr_ref mask(m);
mask = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits);
result = m_bv_util.mk_bv_add(e, mask);
expr_ref bias(m);
bias = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits);
result = m_bv_util.mk_bv_add(e, bias);
}
void fpa2bv_converter::mk_unbias(expr * e, expr_ref & result) {
unsigned ebits = m_bv_util.get_bv_size(e);
SASSERT(ebits >= 2);
SASSERT(ebits >= 3);
expr_ref e_plus_one(m);
e_plus_one = m_bv_util.mk_bv_add(e, m_bv_util.mk_numeral(1, ebits));
@ -2263,6 +2287,7 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref
expr_ref denormal_sig(m), denormal_exp(m);
denormal_sig = m_bv_util.mk_zero_extend(1, sig);
SASSERT(ebits >= 3); // Note: when ebits=2 there is no 1-exponent, so mk_unbias will produce a 0.
denormal_exp = m_bv_util.mk_numeral(1, ebits);
mk_unbias(denormal_exp, denormal_exp);
dbg_decouple("fpa2bv_unpack_denormal_exp", denormal_exp);
@ -2345,7 +2370,7 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result)
}
void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) {
#ifdef _DEBUG
#ifdef Z3DEBUG
return;
// CMW: This works only for quantifier-free formulas.
expr_ref new_e(m);
@ -2436,13 +2461,12 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref &
t = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2));
t = m_bv_util.mk_bv_sub(t, lz);
t = m_bv_util.mk_bv_sub(t, m_bv_util.mk_sign_extend(2, e_min));
expr_ref TINY(m);
dbg_decouple("fpa2bv_rnd_t", t);
expr_ref TINY(m);
TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral((unsigned)-1, ebits+2));
TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;);
SASSERT(is_well_sorted(m, TINY));
dbg_decouple("fpa2bv_rnd_TINY", TINY);
TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;);
SASSERT(is_well_sorted(m, TINY));
expr_ref beta(m);
beta = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(exp, lz), m_bv_util.mk_numeral(1, ebits+2));
@ -2455,7 +2479,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref &
dbg_decouple("fpa2bv_rnd_e_min", e_min);
dbg_decouple("fpa2bv_rnd_e_max", e_max);
expr_ref sigma(m), sigma_add(m), e_min_p2(m);
expr_ref sigma(m), sigma_add(m);
sigma_add = m_bv_util.mk_bv_sub(exp, m_bv_util.mk_sign_extend(2, e_min));
sigma_add = m_bv_util.mk_bv_add(sigma_add, m_bv_util.mk_numeral(1, ebits+2));
m_simp.mk_ite(TINY, sigma_add, lz, sigma);
@ -2477,9 +2501,10 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref &
rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m);
sigma_neg = m_bv_util.mk_bv_neg(sigma);
sigma_cap = m_bv_util.mk_numeral(sbits+2, sigma_size);
sigma_le_cap = m_bv_util.mk_sle(sigma_neg, sigma_cap);
sigma_le_cap = m_bv_util.mk_ule(sigma_neg, sigma_cap);
m_simp.mk_ite(sigma_le_cap, sigma_neg, sigma_cap, sigma_neg_capped);
dbg_decouple("fpa2bv_rnd_sigma_neg", sigma_neg);
dbg_decouple("fpa2bv_rnd_sigma_cap", sigma_cap);
dbg_decouple("fpa2bv_rnd_sigma_neg_capped", sigma_neg_capped);
sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral((unsigned)-1, sigma_size));
dbg_decouple("fpa2bv_rnd_sigma_lt_zero", sigma_lt_zero);

View file

@ -255,7 +255,8 @@ void goal::get_formulas(ptr_vector<expr> & result) {
}
void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) {
SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr)));
// KLM: don't know why this assertion is no longer true
// SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr)));
if (m_inconsistent)
return;
if (proofs_enabled()) {

View file

@ -189,14 +189,14 @@ class sls_tactic : public tactic {
bool what_if(goal_ref const & g, func_decl * fd, const unsigned & fd_inx, const mpz & temp,
double & best_score, unsigned & best_const, mpz & best_value) {
#ifdef _DEBUG
#ifdef Z3DEBUG
mpz old_value;
m_mpz_manager.set(old_value, m_tracker.get_value(fd));
#endif
double r = incremental_score(g, fd, temp);
#ifdef _DEBUG
#ifdef Z3DEBUG
TRACE("sls_whatif", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) <<
" --> " << r << std::endl; );

View file

@ -43,6 +43,8 @@ tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) {
simp2_p.set_bool("push_ite_bv", false);
simp2_p.set_bool("local_ctx", true);
simp2_p.set_uint("local_ctx_limit", 10000000);
simp2_p.set_bool("flat", true); // required by som
simp2_p.set_bool("hoist_mul", false); // required by som
params_ref local_ctx_p = p;
local_ctx_p.set_bool("local_ctx", true);

View file

@ -57,6 +57,11 @@ public:
m_free_ids.finalize();
}
unsigned show_hash(){
unsigned h = string_hash((char *)&m_free_ids[0],m_free_ids.size()*sizeof(unsigned),17);
return hash_u_u(h,m_next_id);
}
/**
\brief Return N if the range of ids generated by this module is in the set [0..N)
*/

View file

@ -360,6 +360,8 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode
mk_inf(ebits, sbits, x.sign, o);
else if (is_zero(x))
mk_zero(ebits, sbits, x.sign, o);
else if (x.ebits == ebits && x.sbits == sbits)
set(o, x);
else {
set(o, x);
unpack(o, true);
@ -1378,12 +1380,12 @@ bool mpf_manager::has_top_exp(mpf const & x) {
}
mpf_exp_t mpf_manager::mk_bot_exp(unsigned ebits) {
SASSERT(ebits > 0);
SASSERT(ebits >= 2);
return m_mpz_manager.get_int64(m_powers2.m1(ebits-1, true));
}
mpf_exp_t mpf_manager::mk_top_exp(unsigned ebits) {
SASSERT(ebits > 0);
SASSERT(ebits >= 2);
return m_mpz_manager.get_int64(m_powers2(ebits-1));
}

View file

@ -31,7 +31,7 @@ mpn_manager static_mpn_manager;
const mpn_digit mpn_manager::zero = 0;
mpn_manager::mpn_manager() {
#ifdef _DEBUG
#ifdef Z3DEBUG
trace_enabled=true;
#endif
}
@ -43,7 +43,7 @@ int mpn_manager::compare(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb) const {
int res = 0;
#ifdef _DEBUG
#ifdef Z3DEBUG
if (trace_enabled)
STRACE("mpn", tout << "[mpn] "; );
#endif
@ -60,7 +60,7 @@ int mpn_manager::compare(mpn_digit const * a, size_t const lnga,
res = -1;
}
#ifdef _DEBUG
#ifdef Z3DEBUG
if (trace_enabled)
STRACE("mpn", tout << ((res == 1) ? " > " : (res == -1) ? " < " : " == "); );
#endif
@ -212,7 +212,7 @@ bool mpn_manager::div(mpn_digit const * numer, size_t const lnum,
trace(numer, lnum, denom, lden, "%");
trace_nl(rem, lden);
#ifdef _DEBUG
#ifdef Z3DEBUG
mpn_sbuffer temp(lnum+1, 0);
mul(quot, lnum-lden+1, denom, lden, temp.c_ptr());
size_t real_size;
@ -340,7 +340,7 @@ bool mpn_manager::div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom,
// Replace numer[j+n]...numer[j] with
// numer[j+n]...numer[j] - q * (denom[n-1]...denom[0])
mpn_digit q_hat_small = (mpn_digit)q_hat;
#ifdef _DEBUG
#ifdef Z3DEBUG
trace_enabled = false;
#endif
mul(&q_hat_small, 1, denom.c_ptr(), n, t_ms.c_ptr());
@ -354,7 +354,7 @@ bool mpn_manager::div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom,
for (size_t i = 0; i < n+1; i++)
numer[j+i] = t_ab[i];
}
#ifdef _DEBUG
#ifdef Z3DEBUG
trace_enabled = true;
#endif
STRACE("mpn_div", tout << "q_hat=" << q_hat << " r_hat=" << r_hat;
@ -416,7 +416,7 @@ void mpn_manager::display_raw(std::ostream & out, mpn_digit const * a, size_t co
void mpn_manager::trace(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb,
const char * op) const {
#ifdef _DEBUG
#ifdef Z3DEBUG
if (trace_enabled)
STRACE("mpn", tout << "[mpn] " << to_string(a, lnga, char_buf, sizeof(char_buf));
tout << " " << op << " " << to_string(b, lngb, char_buf, sizeof(char_buf));
@ -425,14 +425,14 @@ void mpn_manager::trace(mpn_digit const * a, size_t const lnga,
}
void mpn_manager::trace(mpn_digit const * a, size_t const lnga) const {
#ifdef _DEBUG
#ifdef Z3DEBUG
if (trace_enabled)
STRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)); );
#endif
}
void mpn_manager::trace_nl(mpn_digit const * a, size_t const lnga) const {
#ifdef _DEBUG
#ifdef Z3DEBUG
if (trace_enabled)
STRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)) << std::endl; );
#endif

View file

@ -101,7 +101,7 @@ private:
bool div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom,
mpn_digit * quot, mpn_digit * rem);
#ifdef _DEBUG
#ifdef Z3DEBUG
mutable char char_buf[4096];
bool trace_enabled;
#endif