3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-26 18:45:33 +00:00

Merge branch 'unstable' of https://git01.codeplex.com/z3 into fpa-api

Conflicts:
	scripts/mk_project.py

Signed-off-by: Christoph M. Wintersteiger <cwinter@microsoft.com>
This commit is contained in:
Christoph M. Wintersteiger 2013-12-04 13:50:42 +00:00
commit 16ebceb9ff
309 changed files with 38782 additions and 4439 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

18
examples/tptp/README Normal file
View file

@ -0,0 +1,18 @@
TPTP front-end and utilities as a sample using the C++ bindings.
To build the example execute
make examples
in the build directory.
This command will create the executable tptp.
On Windows, you can just execute it.
On OSX and Linux, you must install z3 first using
sudo make install
OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX)
with the build directory. You need that to be able to
find the Z3 shared library.
The sample illustrates using Z3 from the TPTP language.
The TPTP language is documented on http://tptp.org
It also exposes utilities for converting between SMT-LIB
and TPTP format.

2480
examples/tptp/tptp5.cpp Normal file

File diff suppressed because it is too large Load diff

38
examples/tptp/tptp5.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef TPTP5_H_
#define TPTP5_H_
class TreeNode;
#if 0
class named_formulas {
expr_ref_vector m_fmls;
svector<symbol> m_names;
bool m_has_conjecture;
unsigned m_conjecture_index;
public:
named_formulas(ast_manager& m) :
m_fmls(m),
m_has_conjecture(false),
m_conjecture_index(0)
{}
void push_back(expr* fml, char const* name) {
m_fmls.push_back(fml);
m_names.push_back(symbol(name));
}
unsigned size() const { return m_fmls.size(); }
expr*const* c_ptr() const { return m_fmls.c_ptr(); }
expr* operator[](unsigned i) { return m_fmls[i].get(); }
symbol const& name(unsigned i) { return m_names[i]; }
void set_has_conjecture() {
m_has_conjecture = true;
m_conjecture_index = m_fmls.size();
}
bool has_conjecture() const { return m_has_conjecture; }
unsigned conjecture_index() const { return m_conjecture_index; }
};
bool tptp5_parse(ast_manager& m, char const* filename, named_formulas& fmls);
#endif
#endif

2672
examples/tptp/tptp5.lex.cpp Normal file

File diff suppressed because it is too large Load diff

4475
examples/tptp/tptp5.tab.c Normal file

File diff suppressed because it is too large Load diff

138
examples/tptp/tptp5.tab.h Normal file
View file

@ -0,0 +1,138 @@
/* A Bison parser, made by GNU Bison 2.4.2. */
/* Skeleton interface for Bison's Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software
Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
/* Put the tokens into the symbol table, so that GDB and other debuggers
know about them. */
enum yytokentype {
AMPERSAND = 258,
AT_SIGN = 259,
AT_SIGN_MINUS = 260,
AT_SIGN_PLUS = 261,
CARET = 262,
COLON = 263,
COLON_EQUALS = 264,
COMMA = 265,
EQUALS = 266,
EQUALS_GREATER = 267,
EXCLAMATION = 268,
EXCLAMATION_EQUALS = 269,
EXCLAMATION_EXCLAMATION = 270,
EXCLAMATION_GREATER = 271,
LBRKT = 272,
LESS_EQUALS = 273,
LESS_EQUALS_GREATER = 274,
LESS_TILDE_GREATER = 275,
LPAREN = 276,
MINUS = 277,
MINUS_MINUS_GREATER = 278,
PERIOD = 279,
QUESTION = 280,
QUESTION_QUESTION = 281,
QUESTION_STAR = 282,
RBRKT = 283,
RPAREN = 284,
STAR = 285,
TILDE = 286,
TILDE_AMPERSAND = 287,
TILDE_VLINE = 288,
VLINE = 289,
_DLR_cnf = 290,
_DLR_fof = 291,
_DLR_fot = 292,
_DLR_itef = 293,
_DLR_itetf = 294,
_DLR_itett = 295,
_DLR_tff = 296,
_DLR_thf = 297,
_LIT_cnf = 298,
_LIT_fof = 299,
_LIT_include = 300,
_LIT_tff = 301,
_LIT_thf = 302,
arrow = 303,
comment = 304,
comment_line = 305,
decimal = 306,
decimal_exponent = 307,
decimal_fraction = 308,
distinct_object = 309,
dollar_dollar_word = 310,
dollar_word = 311,
dot_decimal = 312,
integer = 313,
less_sign = 314,
lower_word = 315,
plus = 316,
positive_decimal = 317,
rational = 318,
real = 319,
signed_integer = 320,
signed_rational = 321,
signed_real = 322,
single_quoted = 323,
star = 324,
unrecognized = 325,
unsigned_integer = 326,
unsigned_rational = 327,
unsigned_real = 328,
upper_word = 329,
vline = 330
};
#endif
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
{
/* Line 1685 of yacc.c */
#line 148 "tptp5.y"
int ival; double dval; char* sval; TreeNode* pval;
/* Line 1685 of yacc.c */
#line 130 "tptp5.tab.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
#endif
extern YYSTYPE yylval;

View file

@ -14,6 +14,7 @@ def init_project_def():
add_lib('polynomial', ['util'], 'math/polynomial')
add_lib('sat', ['util'])
add_lib('nlsat', ['polynomial', 'sat'])
add_lib('hilbert', ['util'], 'math/hilbert')
add_lib('interval', ['util'], 'math/interval')
add_lib('realclosure', ['interval'], 'math/realclosure')
add_lib('subpaving', ['interval'], 'math/subpaving')
@ -33,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')
@ -53,14 +55,27 @@ def init_project_def():
add_lib('fpa', ['core_tactics', 'bv_tactics', 'sat_tactic'], 'tactic/fpa')
add_lib('smt_tactic', ['smt'], 'smt/tactic')
add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls')
# TODO: split muz_qe into muz, qe. Perhaps, we should also consider breaking muz into muz and pdr.
add_lib('muz_qe', ['smt', 'sat', 'smt2parser', 'aig_tactic'])
add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'muz_qe'], 'tactic/smtlogics')
add_lib('duality', ['smt', 'interp'])
add_lib('qe', ['smt','sat'], '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')
add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'smt_tactic'], 'muz/pdr')
add_lib('clp', ['muz', 'transforms'], 'muz/clp')
add_lib('tab', ['muz', 'transforms'], 'muz/tab')
add_lib('bmc', ['muz', 'transforms'], 'muz/bmc')
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', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio')
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_dll('foci2', ['util'], 'interp/foci2stub',
# dll_name='foci2',
# export_files=['foci2stub.cpp'])
# add_lib('interp', ['solver','foci2'])
API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_fpa.h']
add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure'],
add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure', 'interp'],
includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files)
add_exe('shell', ['api', 'sat', 'extra_cmds'], exe_name='z3')
add_exe('test', ['api', 'fuzzing'], exe_name='test-z3', install=False)
@ -75,6 +90,8 @@ 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')
add_dotnet_example('dotnet_example', 'dotnet')

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))
@ -931,6 +952,9 @@ class ExtraExeComponent(ExeComponent):
def main_component(self):
return False
def require_mem_initializer(self):
return False
def get_so_ext():
sysname = os.uname()[0]
if sysname == 'Darwin':
@ -954,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)
@ -976,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:
@ -985,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))
@ -1227,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 $(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)
@ -1399,7 +1430,8 @@ def mk_config():
'LINK_OUT_FLAG=/Fe\n'
'SO_EXT=.dll\n'
'SLINK=cl\n'
'SLINK_OUT_FLAG=/Fe\n')
'SLINK_OUT_FLAG=/Fe\n'
'OS_DEFINES=/D _WINDOWS\n')
extra_opt = ''
if GIT_HASH:
extra_opt = '%s /D Z3GITHASH=%s' % (extra_opt, GIT_HASH)
@ -1446,7 +1478,8 @@ 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()
CXX = find_cxx_compiler()
@ -1463,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
@ -1475,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
@ -1488,18 +1530,21 @@ def mk_config():
SLIBFLAGS = '-dynamiclib'
elif sysname == 'Linux':
CXXFLAGS = '%s -fno-strict-aliasing -D_LINUX_' % CXXFLAGS
OS_DEFINES = '-D_LINUX'
SO_EXT = '.so'
LDFLAGS = '%s -lrt' % LDFLAGS
SLIBFLAGS = '-shared'
SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS
elif sysname == 'FreeBSD':
CXXFLAGS = '%s -fno-strict-aliasing -D_FREEBSD_' % CXXFLAGS
OS_DEFINES = '-D_FREEBSD_'
SO_EXT = '.so'
LDFLAGS = '%s -lrt' % LDFLAGS
SLIBFLAGS = '-shared'
SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS
elif sysname[:6] == 'CYGWIN':
CXXFLAGS = '%s -D_CYGWIN -fno-strict-aliasing' % CXXFLAGS
OS_DEFINES = '-D_CYGWIN'
SO_EXT = '.dll'
SLIBFLAGS = '-shared'
else:
@ -1518,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')
@ -1534,6 +1580,7 @@ def mk_config():
config.write('SLINK_FLAGS=%s\n' % SLIBFLAGS)
config.write('SLINK_EXTRA_FLAGS=%s\n' % SLIBEXTRAFLAGS)
config.write('SLINK_OUT_FLAG=-o \n')
config.write('OS_DEFINES=%s\n' % OS_DEFINES)
if is_verbose():
print('Host platform: %s' % sysname)
print('C++ Compiler: %s' % CXX)

View file

@ -31,7 +31,7 @@ Revision History:
#include"ast_smt2_pp.h"
#include"th_rewriter.h"
#include"var_subst.h"
#include"expr_substitution.h"
#include"expr_safe_replace.h"
#include"pp.h"
#include"scoped_ctrl_c.h"
#include"cancel_eh.h"
@ -208,6 +208,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));
@ -792,17 +793,12 @@ extern "C" {
RETURN_Z3(of_expr(0));
}
}
expr_substitution subst(m);
expr_safe_replace subst(m);
for (unsigned i = 0; i < num_exprs; i++) {
subst.insert(from[i], to[i]);
}
th_rewriter m_rw(m);
m_rw.set_substitution(&subst);
expr_ref new_a(m);
proof_ref pr(m);
m_rw(a, new_a, pr);
subst(a, new_a);
mk_c(c)->save_ast_trail(new_a);
r = new_a.get();
RETURN_Z3(of_expr(r));
@ -933,6 +929,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;

View file

@ -141,7 +141,7 @@ namespace api {
if (m_interruptable)
(*m_interruptable)();
m().set_cancel(true);
if (m_rcf_manager.get() == 0)
if (m_rcf_manager.get() != 0)
m_rcf_manager->set_cancel(true);
}
}

View file

@ -18,7 +18,6 @@ Revision History:
#include"api_datalog.h"
#include"api_context.h"
#include"api_util.h"
#include"dl_context.h"
#include"ast_pp.h"
#include"api_ast_vector.h"
#include"api_log_macros.h"
@ -30,8 +29,10 @@ Revision History:
#include"cmd_context.h"
#include"smt2parser.h"
#include"dl_context.h"
#include"dl_register_engine.h"
#include"dl_external_relation.h"
#include"dl_decl_plugin.h"
#include"rel_context.h"
namespace api {
@ -39,6 +40,7 @@ namespace api {
void * m_state;
reduce_app_callback_fptr m_reduce_app;
reduce_assign_callback_fptr m_reduce_assign;
datalog::register_engine m_register_engine;
datalog::context m_context;
ast_ref_vector m_trail;
public:
@ -46,7 +48,7 @@ namespace api {
m_state(0),
m_reduce_app(0),
m_reduce_assign(0),
m_context(m, p),
m_context(m, m_register_engine, p),
m_trail(m) {}
virtual ~fixedpoint_context() {}
@ -59,7 +61,7 @@ namespace api {
if (!m.has_plugin(name)) {
m.register_plugin(name, alloc(datalog::dl_decl_plugin));
}
datalog::rel_context* rel = m_context.get_rel_context();
datalog::rel_context_base* rel = m_context.get_rel_context();
if (rel) {
datalog::relation_manager& r = rel->get_rmanager();
r.register_plugin(alloc(datalog::external_relation_plugin, *this, r));

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

@ -0,0 +1,608 @@
/*++
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
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];
int eqpos = tok.find('=');
if(eqpos >= 0 && eqpos < (int)tok.size()){
std::string left = tok.substr(0,eqpos);
std::string right = tok.substr(eqpos+1,tok.size()-eqpos-1);
params[left] = right;
}
}
}
f.close();
}
}
extern "C" {
#if 0
static void iZ3_write_seq(Z3_context ctx, int num, Z3_ast *cnsts, const char *filename, int num_theory, Z3_ast *theory){
int num_fmlas = num+num_theory;
std::vector<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,std::vector<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<std::vector<Z3_ast> > chs(num);
for(int i = 0; i < num-1; i++){
std::vector<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);
}
{
std::vector<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, std::vector<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);
int num_theory = 0;
if(file_params.find("THEORY") != file_params.end())
num_theory = atoi(file_params["THEORY"].c_str());
std::vector<Z3_ast> assertions;
if(!iZ3_parse(ctx,filename,error,assertions))
return false;
if(num_theory > (int)assertions.size())
num_theory = assertions.size();
int num = assertions.size() - num_theory;
read_cnsts.resize(num);
read_parents.resize(num);
read_theory.resize(num_theory);
for(int j = 0; j < num_theory; j++)
read_theory[j] = assertions[j];
for(int j = 0; j < num; j++)
read_cnsts[j] = assertions[j+num_theory];
if(ret_num_theory)
*ret_num_theory = num_theory;
if(theory)
*theory = &read_theory[0];
if(!parents){
*_num = num;
*cnsts = &read_cnsts[0];
return true;
}
for(int j = 0; j < num; j++)
read_parents[j] = SHRT_MAX;
hash_map<Z3_ast,int> pred_map;
for(int j = 0; j < num; j++){
Z3_ast lhs = 0, rhs = read_cnsts[j];
if(Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx,Z3_to_app(ctx,rhs))) == Z3_OP_IMPLIES){
Z3_app app1 = Z3_to_app(ctx,rhs);
Z3_ast lhs1 = Z3_get_app_arg(ctx,app1,0);
Z3_ast rhs1 = Z3_get_app_arg(ctx,app1,1);
if(Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx,Z3_to_app(ctx,lhs1))) == Z3_OP_AND){
Z3_app app2 = Z3_to_app(ctx,lhs1);
int nconjs = Z3_get_app_num_args(ctx,app2);
for(int k = nconjs - 1; k >= 0; --k)
rhs1 = Z3_mk_implies(ctx,Z3_get_app_arg(ctx,app2,k),rhs1);
rhs = rhs1;
}
}
while(1){
Z3_app app = Z3_to_app(ctx,rhs);
Z3_func_decl func = Z3_get_app_decl(ctx,app);
Z3_decl_kind dk = Z3_get_decl_kind(ctx,func);
if(dk == Z3_OP_IMPLIES){
if(lhs){
Z3_ast child = lhs;
if(pred_map.find(child) == pred_map.end()){
read_error << "formula " << j+1 << ": unknown: " << Z3_ast_to_string(ctx,child);
goto fail;
}
int child_num = pred_map[child];
if(read_parents[child_num] != SHRT_MAX){
read_error << "formula " << j+1 << ": multiple reference: " << Z3_ast_to_string(ctx,child);
goto fail;
}
read_parents[child_num] = j;
}
lhs = Z3_get_app_arg(ctx,app,0);
rhs = Z3_get_app_arg(ctx,app,1);
}
else {
if(!lhs){
read_error << "formula " << j+1 << ": should be (implies {children} fmla parent)";
goto fail;
}
read_cnsts[j] = lhs;
Z3_ast name = rhs;
if(pred_map.find(name) != pred_map.end()){
read_error << "formula " << j+1 << ": duplicate symbol";
goto fail;
}
pred_map[name] = j;
break;
}
}
}
for(int j = 0; j < num-1; j++)
if(read_parents[j] == SHRT_MIN){
read_error << "formula " << j+1 << ": unreferenced";
goto fail;
}
*_num = num;
*cnsts = &read_cnsts[0];
*parents = &read_parents[0];
return true;
fail:
read_msg = read_error.str();
*error = read_msg.c_str();
return false;
}
}

View file

@ -165,7 +165,10 @@ extern "C" {
}
for (unsigned i = 0; i < num_bound; ++i) {
app* a = to_app(bound[i]);
SASSERT(a->get_kind() == AST_APP);
if (a->get_kind() != AST_APP) {
SET_ERROR_CODE(Z3_INVALID_ARG);
RETURN_Z3(0);
}
symbol s(to_app(a)->get_decl()->get_name());
names.push_back(of_symbol(s));
types.push_back(of_sort(mk_c(c)->m().get_sort(a)));

View file

@ -204,6 +204,8 @@ namespace z3 {
func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range);
func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range);
func_decl function(symbol const& name, sort_vector const& domain, sort const& range);
func_decl function(char const * name, sort_vector const& domain, sort const& range);
func_decl function(char const * name, sort const & domain, sort const & range);
func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range);
func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range);
@ -429,6 +431,7 @@ namespace z3 {
expr operator()() const;
expr operator()(unsigned n, expr const * args) const;
expr operator()(expr_vector const& v) const;
expr operator()(expr const & a) const;
expr operator()(int a) const;
expr operator()(expr const & a1, expr const & a2) const;
@ -1516,6 +1519,22 @@ namespace z3 {
inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) {
return function(range.ctx().str_symbol(name), arity, domain, range);
}
inline func_decl context::function(symbol const& name, sort_vector const& domain, sort const& range) {
array<Z3_sort> args(domain.size());
for (unsigned i = 0; i < domain.size(); i++) {
check_context(domain[i], range);
args[i] = domain[i];
}
Z3_func_decl f = Z3_mk_func_decl(m_ctx, name, domain.size(), args.ptr(), range);
check_error();
return func_decl(*this, f);
}
inline func_decl context::function(char const * name, sort_vector const& domain, sort const& range) {
return function(range.ctx().str_symbol(name), domain, range);
}
inline func_decl context::function(char const * name, sort const & domain, sort const & range) {
check_context(domain, range);
@ -1602,6 +1621,16 @@ namespace z3 {
return expr(ctx(), r);
}
inline expr func_decl::operator()(expr_vector const& args) const {
array<Z3_ast> _args(args.size());
for (unsigned i = 0; i < args.size(); i++) {
check_context(*this, args[i]);
_args[i] = args[i];
}
Z3_ast r = Z3_mk_app(ctx(), *this, args.size(), _args.ptr());
check_error();
return expr(ctx(), r);
}
inline expr func_decl::operator()() const {
Z3_ast r = Z3_mk_app(ctx(), *this, 0, 0);
ctx().check_error();

View file

@ -303,6 +303,9 @@ namespace Microsoft.Z3
/// <summary>
/// Create a new finite domain sort.
/// <param name="name">The name used to identify the sort</param>
/// <param size="size">The size of the sort</param>
/// <returns>The result is a sort</returns>
/// </summary>
public FiniteDomainSort MkFiniteDomainSort(Symbol name, ulong size)
{
@ -315,6 +318,11 @@ namespace Microsoft.Z3
/// <summary>
/// Create a new finite domain sort.
/// <param name="name">The name used to identify the sort</param>
/// <param size="size">The size of the sort</param>
/// <returns>The result is a sort</returns>
/// Elements of the sort are created using <seealso cref="MkNumeral"/>,
/// and the elements range from 0 to <tt>size-1</tt>.
/// </summary>
public FiniteDomainSort MkFiniteDomainSort(string name, ulong size)
{

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

@ -3856,32 +3856,6 @@ def is_array(a):
"""
return isinstance(a, ArrayRef)
def is_select(a):
"""Return `True` if `a` is a Z3 array select.
>>> a = Array('a', IntSort(), IntSort())
>>> is_select(a)
False
>>> i = Int('i')
>>> is_select(a[i])
True
"""
return is_app_of(a, Z3_OP_SELECT)
def is_store(a):
"""Return `True` if `a` is a Z3 array store.
>>> a = Array('a', IntSort(), IntSort())
>>> is_store(a)
False
>>> i = Int('i')
>>> is_store(a[i])
False
>>> is_store(Store(a, i, i + 1))
True
"""
return is_app_of(a, Z3_OP_STORE)
def is_const_array(a):
"""Return `True` if `a` is a Z3 constant array.
@ -4072,7 +4046,8 @@ def is_select(a):
>>> a = Array('a', IntSort(), IntSort())
>>> is_select(a)
False
>>> is_select(a[0])
>>> i = Int('i')
>>> is_select(a[i])
True
"""
return is_app_of(a, Z3_OP_SELECT)
@ -6449,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)
@ -6474,7 +6449,7 @@ class Tactic:
def _to_goal(a):
if isinstance(a, BoolRef):
goal = Goal()
goal = Goal(ctx = a.ctx)
goal.add(a)
return goal
else:
@ -6957,10 +6932,10 @@ def substitute(t, *m):
>>> x = Int('x')
>>> y = Int('y')
>>> substitute(x + 1, (x, y + 1))
2 + y
y + 1 + 1
>>> f = Function('f', IntSort(), IntSort())
>>> substitute(f(x) + f(y), (f(x), IntVal(1)), (f(y), IntVal(1)))
2
1 + 1
"""
if isinstance(m, tuple):
m1 = _get_args(m)

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.
@ -892,6 +894,7 @@ typedef enum {
Z3_OP_NOT,
Z3_OP_IMPLIES,
Z3_OP_OEQ,
Z3_OP_INTERP,
// Arithmetic
Z3_OP_ANUM = 0x200,
@ -1723,6 +1726,8 @@ extern "C" {
To create constants that belong to the finite domain,
use the APIs for creating numerals and pass a numeric
constant together with the sort returned by this call.
The numeric constant should be between 0 and the less
than the size of the domain.
\sa Z3_get_finite_domain_sort_size
@ -2104,6 +2109,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,
@ -7662,6 +7677,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,11 @@ 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;
}

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
@ -2000,6 +2001,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

@ -84,7 +84,8 @@ class datatype_decl {
ptr_vector<constructor_decl> m_constructors;
public:
datatype_decl(const symbol & n, unsigned num_constructors, constructor_decl * const * constructors):
m_name(n), m_constructors(num_constructors, constructors) {}
m_name(n), m_constructors(num_constructors, constructors) {
}
~datatype_decl() {
std::for_each(m_constructors.begin(), m_constructors.end(), delete_proc<constructor_decl>());
}

View file

@ -657,7 +657,9 @@ namespace datalog {
SASSERT(value == 1);
return m.mk_true();
}
m.raise_exception("unrecognized sort");
std::stringstream strm;
strm << "sort '" << mk_pp(s, m) << "' is not recognized as a sort that contains numeric values.\nUse Bool, BitVec, Int, Real, or a Finite domain sort";
m.raise_exception(strm.str().c_str());
return 0;
}

View file

@ -85,6 +85,24 @@ bool float_decl_plugin::is_value(expr * n, mpf & val) {
m_fm.set(val, m_values[to_app(n)->get_decl()->get_parameter(0).get_ext_id()]);
return true;
}
else if (is_app_of(n, m_family_id, OP_FLOAT_MINUS_INF)) {
unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int();
unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int();
m_fm.mk_ninf(ebits, sbits, val);
return true;
}
else if (is_app_of(n, m_family_id, OP_FLOAT_PLUS_INF)) {
unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int();
unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int();
m_fm.mk_pinf(ebits, sbits, val);
return true;
}
else if (is_app_of(n, m_family_id, OP_FLOAT_NAN)) {
unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int();
unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int();
m_fm.mk_nan(ebits, sbits, val);
return true;
}
return false;
}
@ -457,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));
@ -468,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));
@ -497,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) {
@ -523,6 +585,9 @@ bool float_decl_plugin::is_value(app * e) const {
case OP_RM_TOWARD_NEGATIVE:
case OP_RM_TOWARD_ZERO:
case OP_FLOAT_VALUE:
case OP_FLOAT_PLUS_INF:
case OP_FLOAT_MINUS_INF:
case OP_FLOAT_NAN:
return true;
case OP_TO_FLOAT:
return m_manager->is_value(e->get_arg(0));

View file

@ -118,7 +118,7 @@ public:
void mk_eq(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_eq_core(arg1, arg2, result) == BR_FAILED)
result = m_util.mk_le(arg1, arg2);
result = m_util.mk_eq(arg1, arg2);
}
void mk_le(expr * arg1, expr * arg2, expr_ref & result) {
if (mk_le_core(arg1, arg2, result) == BR_FAILED)

View file

@ -108,6 +108,7 @@ void var_counter::count_vars(ast_manager & m, const app * pred, int coef) {
unsigned var_counter::get_max_var(bool& has_var) {
has_var = false;
unsigned max_var = 0;
ptr_vector<quantifier> qs;
while (!m_todo.empty()) {
expr* e = m_todo.back();
m_todo.pop_back();
@ -117,14 +118,7 @@ unsigned var_counter::get_max_var(bool& has_var) {
m_visited.mark(e, true);
switch(e->get_kind()) {
case AST_QUANTIFIER: {
var_counter aux_counter;
quantifier* q = to_quantifier(e);
bool has_var1 = false;
unsigned max_v = aux_counter.get_max_var(has_var1);
if (max_v > max_var + q->get_num_decls()) {
max_var = max_v - q->get_num_decls();
has_var = true;
}
qs.push_back(to_quantifier(e));
break;
}
case AST_VAR: {
@ -147,6 +141,20 @@ unsigned var_counter::get_max_var(bool& has_var) {
}
}
m_visited.reset();
while (!qs.empty()) {
var_counter aux_counter;
quantifier* q = qs.back();
qs.pop_back();
aux_counter.m_todo.push_back(q->get_expr());
bool has_var1 = false;
unsigned max_v = aux_counter.get_max_var(has_var1);
if (max_v >= max_var + q->get_num_decls()) {
max_var = max_v - q->get_num_decls();
has_var = has_var || has_var1;
}
}
return max_var;
}

View file

@ -354,7 +354,7 @@ br_status float_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
if (m_util.is_minus_inf(arg1)) {
// -oo < arg2 --> not(arg2 = -oo) and not(arg2 = NaN)
result = m().mk_and(m().mk_not(m().mk_eq(arg2, arg1)), mk_neq_nan(arg2));
return BR_REWRITE2;
return BR_REWRITE3;
}
if (m_util.is_minus_inf(arg2)) {
// arg1 < -oo --> false
@ -369,7 +369,7 @@ br_status float_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
if (m_util.is_plus_inf(arg2)) {
// arg1 < +oo --> not(arg1 = +oo) and not(arg1 = NaN)
result = m().mk_and(m().mk_not(m().mk_eq(arg1, arg2)), mk_neq_nan(arg1));
return BR_REWRITE2;
return BR_REWRITE3;
}
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
@ -490,7 +490,11 @@ br_status float_rewriter::mk_is_sign_minus(expr * arg1, expr_ref & result) {
br_status float_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
scoped_mpf v1(m_util.fm()), v2(m_util.fm());
if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) {
result = (v1 == v2) ? m().mk_true() : m().mk_false();
// Note: == is the floats-equality, here we need normal equality.
result = (m_fm.is_nan(v1) && m_fm.is_nan(v2)) ? m().mk_true() :
(m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1)!=m_fm.sgn(v2)) ? m().mk_false() :
(v1 == v2) ? m().mk_true() :
m().mk_false();
return BR_DONE;
}

55
src/ast/scoped_proof.h Normal file
View file

@ -0,0 +1,55 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
scoped_proof.h
Abstract:
Scoped proof environments. Toggles enabling proofs.
Author:
Nikolaj Bjorner (nbjorner) 2013-08-28
Revision History:
--*/
#ifndef _SCOPED_PROOF__H_
#define _SCOPED_PROOF_H_
#include "ast.h"
class scoped_proof_mode {
ast_manager& m;
proof_gen_mode m_mode;
public:
scoped_proof_mode(ast_manager& m, proof_gen_mode mode): m(m) {
m_mode = m.proof_mode();
m.toggle_proof_mode(mode);
}
~scoped_proof_mode() {
m.toggle_proof_mode(m_mode);
}
};
class scoped_proof : public scoped_proof_mode {
public:
scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_FINE) {}
};
class scoped_no_proof : public scoped_proof_mode {
public:
scoped_no_proof(ast_manager& m): scoped_proof_mode(m, PGM_DISABLED) {}
};
class scoped_restore_proof : public scoped_proof_mode {
public:
scoped_restore_proof(ast_manager& m): scoped_proof_mode(m, m.proof_mode()) {}
};
#endif

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

@ -179,7 +179,7 @@ bool bv_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const
}
SASSERT(result.get());
TRACE("bv_simplifier",
TRACE("bv_simplifier",
tout << mk_pp(f, m_manager) << "\n";
for (unsigned i = 0; i < num_args; ++i) {
tout << mk_pp(args[i], m_manager) << " ";

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

@ -38,6 +38,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)) {
@ -323,6 +324,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();
@ -380,6 +382,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;
}
@ -388,6 +403,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;
}
@ -1456,11 +1480,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;
@ -253,6 +254,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; }
@ -275,12 +278,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; }
@ -294,6 +301,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

854
src/duality/duality.h Normal file
View file

@ -0,0 +1,854 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
duality.h
Abstract:
main header for duality
Author:
Ken McMillan (kenmcmil)
Revision History:
--*/
#pragma once
#include "duality_wrapper.h"
#include <list>
#include <map>
// make hash_map and hash_set available
#ifndef WIN32
using namespace stl_ext;
#endif
namespace Duality {
/* Generic operations on Z3 formulas */
struct Z3User {
context &ctx;
solver &slvr;
typedef func_decl FuncDecl;
typedef expr Term;
Z3User(context &_ctx, solver &_slvr) : ctx(_ctx), slvr(_slvr){}
const char *string_of_int(int n);
Term conjoin(const std::vector<Term> &args);
Term sum(const std::vector<Term> &args);
Term CloneQuantifier(const Term &t, const Term &new_body);
Term SubstRec(hash_map<ast, Term> &memo, const Term &t);
void Strengthen(Term &x, const Term &y);
// return the func_del of an app if it is uninterpreted
bool get_relation(const Term &t, func_decl &R);
// return true if term is an individual variable
// TODO: have to check that it is not a background symbol
bool is_variable(const Term &t);
FuncDecl SuffixFuncDecl(Term t, int n);
Term SubstRecHide(hash_map<ast, Term> &memo, const Term &t, int number);
void CollectConjuncts(const Term &f, std::vector<Term> &lits, bool pos = true);
void SortTerms(std::vector<Term> &terms);
Term SortSum(const Term &t);
void Summarize(const Term &t);
int CumulativeDecisions();
private:
void SummarizeRec(hash_set<ast> &memo, std::vector<expr> &lits, int &ops, const Term &t);
};
/** This class represents a relation post-fixed point (RPFP) problem as
* a "problem graph". The graph consists of Nodes and hyper-edges.
*
* A node consists of
* - Annotation, a symbolic relation
* - Bound, a symbolic relation giving an upper bound on Annotation
*
*
* A hyper-edge consists of:
* - Children, a sequence of children Nodes,
* - F, a symbolic relational transformer,
* - Parent, a single parent Node.
*
* The graph is "solved" when:
* - For every Node n, n.Annotation subseteq n.Bound
* - For every hyperedge e, e.F(e.Children.Annotation) subseteq e.Parent.Annotation
*
* where, if x is a sequence of Nodes, x.Annotation is the sequences
* of Annotations of the nodes in the sequence.
*
* A symbolic Transformer consists of
* - RelParams, a sequence of relational symbols
* - IndParams, a sequence of individual symbols
* - Formula, a formula over RelParams and IndParams
*
* A Transformer t represents a function that takes sequence R of relations
* and yields the relation lambda (t.Indparams). Formula(R/RelParams).
*
* As a special case, a nullary Transformer (where RelParams is the empty sequence)
* represents a fixed relation.
*
* An RPFP consists of
* - Nodes, a set of Nodes
* - Edges, a set of hyper-edges
* - Context, a prover context that contains formula AST's
*
* Multiple RPFP's can use the same Context, but you should be careful
* that only one RPFP asserts constraints in the context at any time.
*
* */
class RPFP : public Z3User
{
public:
class Edge;
class Node;
bool HornClauses;
/** Interface class for interpolating solver. */
class LogicSolver {
public:
context *ctx; /** Z3 context for formulas */
solver *slvr; /** Z3 solver */
bool need_goals; /** Can the solver use the goal tree to optimize interpolants? */
/** Tree interpolation. This method assumes the formulas in TermTree
"assumptions" are currently asserted in the solver. The return
value indicates whether the assertions are satisfiable. In the
UNSAT case, a tree interpolant is returned in "interpolants".
In the SAT case, a model is returned.
*/
virtual
lbool interpolate_tree(TermTree *assumptions,
TermTree *&interpolants,
model &_model,
TermTree *goals = 0,
bool weak = false
) = 0;
/** Declare a constant in the background theory. */
virtual void declare_constant(const func_decl &f) = 0;
/** Is this a background constant? */
virtual bool is_constant(const func_decl &f) = 0;
/** Assert a background axiom. */
virtual void assert_axiom(const expr &axiom) = 0;
/** Return a string describing performance. */
virtual std::string profile() = 0;
virtual void write_interpolation_problem(const std::string &file_name,
const std::vector<expr> &assumptions,
const std::vector<expr> &theory
){}
/** Cancel, throw Canceled object if possible. */
virtual void cancel(){ }
virtual ~LogicSolver(){}
};
/** This solver uses iZ3. */
class iZ3LogicSolver : public LogicSolver {
public:
interpolating_context *ictx; /** iZ3 context for formulas */
interpolating_solver *islvr; /** iZ3 solver */
lbool interpolate_tree(TermTree *assumptions,
TermTree *&interpolants,
model &_model,
TermTree *goals = 0,
bool weak = false)
{
literals _labels;
islvr->SetWeakInterpolants(weak);
return islvr->interpolate_tree(assumptions,interpolants,_model,_labels,true);
}
void assert_axiom(const expr &axiom){
islvr->AssertInterpolationAxiom(axiom);
}
std::string profile(){
return islvr->profile();
}
#if 0
iZ3LogicSolver(config &_config){
ctx = ictx = new interpolating_context(_config);
slvr = islvr = new interpolating_solver(*ictx);
need_goals = false;
islvr->SetWeakInterpolants(true);
}
#endif
iZ3LogicSolver(context &c){
ctx = ictx = &c;
slvr = islvr = new interpolating_solver(*ictx);
need_goals = false;
islvr->SetWeakInterpolants(true);
}
void write_interpolation_problem(const std::string &file_name,
const std::vector<expr> &assumptions,
const std::vector<expr> &theory
){
#if 0
islvr->write_interpolation_problem(file_name,assumptions,theory);
#endif
}
void cancel(){islvr->cancel();}
/** Declare a constant in the background theory. */
virtual void declare_constant(const func_decl &f){
bckg.insert(f);
}
/** Is this a background constant? */
virtual bool is_constant(const func_decl &f){
return bckg.find(f) != bckg.end();
}
~iZ3LogicSolver(){
// delete ictx;
delete islvr;
}
private:
hash_set<func_decl> bckg;
};
#if 0
/** Create a logic solver from a Z3 configuration. */
static iZ3LogicSolver *CreateLogicSolver(config &_config){
return new iZ3LogicSolver(_config);
}
#endif
/** Create a logic solver from a low-level Z3 context.
Only use this if you know what you're doing. */
static iZ3LogicSolver *CreateLogicSolver(context c){
return new iZ3LogicSolver(c);
}
LogicSolver *ls;
private:
int nodeCount;
int edgeCount;
class stack_entry
{
public:
std::list<Edge *> edges;
std::list<Node *> nodes;
};
public:
model dualModel;
private:
literals dualLabels;
std::list<stack_entry> stack;
std::vector<Term> axioms; // only saved here for printing purposes
public:
/** Construct an RPFP graph with a given interpolating prover context. It is allowed to
have multiple RPFP's use the same context, but you should never have teo RPFP's
with the same conext asserting nodes or edges at the same time. Note, if you create
axioms in one RPFP, them create a second RPFP with the same context, the second will
inherit the axioms.
*/
RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx), *(_ls->slvr)), dualModel(*(_ls->ctx))
{
ls = _ls;
nodeCount = 0;
edgeCount = 0;
stack.push_back(stack_entry());
HornClauses = false;
}
~RPFP();
/** Symbolic representation of a relational transformer */
class Transformer
{
public:
std::vector<FuncDecl> RelParams;
std::vector<Term> IndParams;
Term Formula;
RPFP *owner;
hash_map<std::string,Term> labels;
Transformer *Clone()
{
return new Transformer(*this);
}
void SetEmpty(){
Formula = owner->ctx.bool_val(false);
}
void SetFull(){
Formula = owner->ctx.bool_val(true);
}
bool IsEmpty(){
return eq(Formula,owner->ctx.bool_val(false));
}
bool IsFull(){
return eq(Formula,owner->ctx.bool_val(true));
}
void UnionWith(const Transformer &other){
Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula);
Formula = Formula || t;
}
void IntersectWith(const Transformer &other){
Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula);
Formula = Formula && t;
}
bool SubsetEq(const Transformer &other){
Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula);
expr test = Formula && !t;
owner->slvr.push();
owner->slvr.add(test);
check_result res = owner->slvr.check();
owner->slvr.pop(1);
return res == unsat;
}
void Complement(){
Formula = !Formula;
}
void Simplify(){
Formula = Formula.simplify();
}
Transformer(const std::vector<FuncDecl> &_RelParams, const std::vector<Term> &_IndParams, const Term &_Formula, RPFP *_owner)
: RelParams(_RelParams), IndParams(_IndParams), Formula(_Formula) {owner = _owner;}
};
/** Create a symbolic transformer. */
Transformer CreateTransformer(const std::vector<FuncDecl> &_RelParams, const std::vector<Term> &_IndParams, const Term &_Formula)
{
// var ops = new Util.ContextOps(ctx);
// var foo = ops.simplify_lhs(_Formula);
// t.Formula = foo.Item1;
// t.labels = foo.Item2;
return Transformer(_RelParams,_IndParams,_Formula,this);
}
/** Create a relation (nullary relational transformer) */
Transformer CreateRelation(const std::vector<Term> &_IndParams, const Term &_Formula)
{
return CreateTransformer(std::vector<FuncDecl>(), _IndParams, _Formula);
}
/** A node in the RPFP graph */
class Node
{
public:
FuncDecl Name;
Transformer Annotation;
Transformer Bound;
Transformer Underapprox;
RPFP *owner;
int number;
Edge *Outgoing;
std::vector<Edge *> Incoming;
Term dual;
Node *map;
Node(const FuncDecl &_Name, const Transformer &_Annotation, const Transformer &_Bound, const Transformer &_Underapprox, const Term &_dual, RPFP *_owner, int _number)
: Name(_Name), Annotation(_Annotation), Bound(_Bound), Underapprox(_Underapprox), dual(_dual) {owner = _owner; number = _number; Outgoing = 0;}
};
/** Create a node in the graph. The input is a term R(v_1...v_n)
* where R is an arbitrary relational symbol and v_1...v_n are
* arbitary distinct variables. The names are only of mnemonic value,
* however, the number and type of arguments determine the type
* of the relation at this node. */
Node *CreateNode(const Term &t)
{
std::vector<Term> _IndParams;
int nargs = t.num_args();
for(int i = 0; i < nargs; i++)
_IndParams.push_back(t.arg(i));
Node *n = new Node(t.decl(),
CreateRelation(_IndParams,ctx.bool_val(true)),
CreateRelation(_IndParams,ctx.bool_val(true)),
CreateRelation(_IndParams,ctx.bool_val(false)),
expr(ctx), this, ++nodeCount
);
nodes.push_back(n);
return n;
}
/** Clone a node (can be from another graph). */
Node *CloneNode(Node *old)
{
Node *n = new Node(old->Name,
old->Annotation,
old->Bound,
old->Underapprox,
expr(ctx),
this,
++nodeCount
);
nodes.push_back(n);
n->map = old;
return n;
}
/** This class represents a hyper-edge in the RPFP graph */
class Edge
{
public:
Transformer F;
Node *Parent;
std::vector<Node *> Children;
RPFP *owner;
int number;
// these should be internal...
Term dual;
hash_map<func_decl,int> relMap;
hash_map<ast,Term> varMap;
Edge *map;
Term labeled;
Edge(Node *_Parent, const Transformer &_F, const std::vector<Node *> &_Children, RPFP *_owner, int _number)
: F(_F), Parent(_Parent), Children(_Children), dual(expr(_owner->ctx)) {
owner = _owner;
number = _number;
}
};
/** Create a hyper-edge. */
Edge *CreateEdge(Node *_Parent, const Transformer &_F, const std::vector<Node *> &_Children)
{
Edge *e = new Edge(_Parent,_F,_Children,this,++edgeCount);
_Parent->Outgoing = e;
for(unsigned i = 0; i < _Children.size(); i++)
_Children[i]->Incoming.push_back(e);
edges.push_back(e);
return e;
}
/** Create an edge that lower-bounds its parent. */
Edge *CreateLowerBoundEdge(Node *_Parent)
{
return CreateEdge(_Parent, _Parent->Annotation, std::vector<Node *>());
}
/** For incremental solving, asserts the constraint associated
* with this edge in the SMT context. If this edge is removed,
* you must pop the context accordingly. The second argument is
* the number of pushes we are inside. */
void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false);
/** For incremental solving, asserts the negation of the upper bound associated
* with a node.
* */
void AssertNode(Node *n);
/** Declare a constant in the background theory. */
void DeclareConstant(const FuncDecl &f);
/** Assert a background axiom. Background axioms can be used to provide the
* theory of auxilliary functions or relations. All symbols appearing in
* background axioms are considered global, and may appear in both transformer
* and relational solutions. Semantically, a solution to the RPFP gives
* an interpretation of the unknown relations for each interpretation of the
* auxilliary symbols that is consistent with the axioms. Axioms should be
* asserted before any calls to Push. They cannot be de-asserted by Pop. */
void AssertAxiom(const Term &t);
#if 0
/** Do not call this. */
void RemoveAxiom(const Term &t);
#endif
/** Solve an RPFP graph. This means either strengthen the annotation
* so that the bound at the given root node is satisfied, or
* show that this cannot be done by giving a dual solution
* (i.e., a counterexample).
*
* In the current implementation, this only works for graphs that
* are:
* - tree-like
*
* - closed.
*
* In a tree-like graph, every nod has out most one incoming and one out-going edge,
* and there are no cycles. In a closed graph, every node has exactly one out-going
* edge. This means that the leaves of the tree are all hyper-edges with no
* children. Such an edge represents a relation (nullary transformer) and thus
* a lower bound on its parent. The parameter root must be the root of this tree.
*
* If Solve returns LBool.False, this indicates success. The annotation of the tree
* has been updated to satisfy the upper bound at the root.
*
* If Solve returns LBool.True, this indicates a counterexample. For each edge,
* you can then call Eval to determine the values of symbols in the transformer formula.
* You can also call Empty on a node to determine if its value in the counterexample
* is the empty relation.
*
* \param root The root of the tree
* \param persist Number of context pops through which result should persist
*
*
*/
lbool Solve(Node *root, int persist);
/** Get the constraint tree (but don't solve it) */
TermTree *GetConstraintTree(Node *root);
/** Dispose of the dual model (counterexample) if there is one. */
void DisposeDualModel();
/** Check satisfiability of asserted edges and nodes. Same functionality as
* Solve, except no primal solution (interpolant) is generated in the unsat case. */
check_result Check(Node *root, std::vector<Node *> underapproxes = std::vector<Node *>(),
std::vector<Node *> *underapprox_core = 0);
/** Update the model, attempting to make the propositional literals in assumps true. If possible,
return sat, else return unsat and keep the old model. */
check_result CheckUpdateModel(Node *root, std::vector<expr> assumps);
/** Determines the value in the counterexample of a symbol occuring in the transformer formula of
* a given edge. */
Term Eval(Edge *e, Term t);
/** Return the fact derived at node p in a counterexample. */
Term EvalNode(Node *p);
/** Returns true if the given node is empty in the primal solution. For proecudure summaries,
this means that the procedure is not called in the current counter-model. */
bool Empty(Node *p);
/** Compute an underapproximation of every node in a tree rooted at "root",
based on a previously computed counterexample. */
Term ComputeUnderapprox(Node *root, int persist);
/** Push a scope. Assertions made after Push can be undone by Pop. */
void Push();
/** Exception thrown when bad clause is encountered */
struct bad_clause {
std::string msg;
int i;
bad_clause(const std::string &_msg, int _i){
msg = _msg;
i = _i;
}
};
struct parse_error {
std::string msg;
parse_error(const std::string &_msg){
msg = _msg;
}
};
struct file_not_found {
};
struct bad_format {
};
/** Pop a scope (see Push). Note, you cannot pop axioms. */
void Pop(int num_scopes);
/** Convert a collection of clauses to Nodes and Edges in the RPFP.
Predicate unknowns are uninterpreted predicates not
occurring in the background theory.
Clauses are of the form
B => P(t_1,...,t_k)
where P is a predicate unknown and predicate unknowns
occur only positivey in H and only under existential
quantifiers in prenex form.
Each predicate unknown maps to a node. Each clause maps to
an edge. Let C be a clause B => P(t_1,...,t_k) where the
sequence of predicate unknowns occurring in B (in order
of occurrence) is P_1..P_n. The clause maps to a transformer
T where:
T.Relparams = P_1..P_n
T.Indparams = x_1...x+k
T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k
Throws exception bad_clause(msg,i) if a clause i is
in the wrong form.
*/
struct label_struct {
symbol name;
expr value;
bool pos;
label_struct(const symbol &s, const expr &e, bool b)
: name(s), value(e), pos(b) {}
};
#ifdef WIN32
__declspec(dllexport)
#endif
void FromClauses(const std::vector<Term> &clauses);
void FromFixpointContext(fixedpoint fp, std::vector<Term> &queries);
void WriteSolution(std::ostream &s);
void WriteCounterexample(std::ostream &s, Node *node);
enum FileFormat {DualityFormat, SMT2Format, HornFormat};
/** Write the RPFP to a file (currently in SMTLIB 1.2 format) */
void WriteProblemToFile(std::string filename, FileFormat format = DualityFormat);
/** Read the RPFP from a file (in specificed format) */
void ReadProblemFromFile(std::string filename, FileFormat format = DualityFormat);
/** Translate problem to Horn clause form */
void ToClauses(std::vector<Term> &cnsts, FileFormat format = DualityFormat);
/** Translate the RPFP to a fixed point context, with queries */
fixedpoint ToFixedPointProblem(std::vector<expr> &queries);
/** Nodes of the graph. */
std::vector<Node *> nodes;
/** Edges of the graph. */
std::vector<Edge *> edges;
Term SubstParams(const std::vector<Term> &from,
const std::vector<Term> &to, const Term &t);
Term Localize(Edge *e, const Term &t);
void EvalNodeAsConstraint(Node *p, Transformer &res);
TermTree *GetGoalTree(Node *root);
int EvalTruth(hash_map<ast,int> &memo, Edge *e, const Term &f);
void GetLabels(Edge *e, std::vector<symbol> &labels);
// int GetLabelsRec(hash_map<ast,int> *memo, const Term &f, std::vector<symbol> &labels, bool labpos);
private:
Term SuffixVariable(const Term &t, int n);
Term HideVariable(const Term &t, int n);
void RedVars(Node *node, Term &b, std::vector<Term> &v);
Term RedDualRela(Edge *e, std::vector<Term> &args, int idx);
Term LocalizeRec(Edge *e, hash_map<ast,Term> &memo, const Term &t);
void SetEdgeMaps(Edge *e);
Term ReducedDualEdge(Edge *e);
TermTree *ToTermTree(Node *root);
TermTree *ToGoalTree(Node *root);
void DecodeTree(Node *root, TermTree *interp, int persist);
Term GetUpperBound(Node *n);
TermTree *AddUpperBound(Node *root, TermTree *t);
#if 0
void WriteInterps(System.IO.StreamWriter f, TermTree t);
#endif
void WriteEdgeVars(Edge *e, hash_map<ast,int> &memo, const Term &t, std::ostream &s);
void WriteEdgeAssignment(std::ostream &s, Edge *e);
// Scan the clause body for occurrences of the predicate unknowns
Term ScanBody(hash_map<ast,Term> &memo,
const Term &t,
hash_map<func_decl,Node *> &pmap,
std::vector<func_decl> &res,
std::vector<Node *> &nodes);
Term RemoveLabelsRec(hash_map<ast,Term> &memo, const Term &t, std::vector<label_struct> &lbls);
Term RemoveLabels(const Term &t, std::vector<label_struct > &lbls);
Term GetAnnotation(Node *n);
Term GetUnderapprox(Node *n);
Term UnderapproxFlag(Node *n);
hash_map<ast,Node *> underapprox_flag_rev;
Node *UnderapproxFlagRev(const Term &flag);
Term ProjectFormula(std::vector<Term> &keep_vec, const Term &f);
int SubtermTruth(hash_map<ast,int> &memo, const Term &);
void ImplicantRed(hash_map<ast,int> &memo, const Term &f, std::vector<Term> &lits,
hash_set<ast> *done, bool truth, hash_set<ast> &dont_cares);
void Implicant(hash_map<ast,int> &memo, const Term &f, std::vector<Term> &lits, hash_set<ast> &dont_cares);
Term UnderapproxFormula(const Term &f, hash_set<ast> &dont_cares);
Term ToRuleRec(Edge *e, hash_map<ast,Term> &memo, const Term &t, std::vector<expr> &quants);
hash_map<ast,Term> resolve_ite_memo;
Term ResolveIte(hash_map<ast,int> &memo, const Term &t, std::vector<Term> &lits,
hash_set<ast> *done, hash_set<ast> &dont_cares);
struct ArrayValue {
bool defined;
std::map<ast,ast> entries;
expr def_val;
};
void EvalArrayTerm(const Term &t, ArrayValue &res);
Term EvalArrayEquality(const Term &f);
Term ModelValueAsConstraint(const Term &t);
void GetLabelsRec(hash_map<ast,int> &memo, const Term &f, std::vector<symbol> &labels,
hash_set<ast> *done, bool truth);
Term SubstBoundRec(hash_map<int,hash_map<ast,Term> > &memo, hash_map<int,Term> &subst, int level, const Term &t);
Term SubstBound(hash_map<int,Term> &subst, const Term &t);
};
/** RPFP solver base class. */
class Solver {
public:
struct Counterexample {
RPFP *tree;
RPFP::Node *root;
Counterexample(){
tree = 0;
root = 0;
}
};
/** Solve the problem. You can optionally give an old
counterexample to use as a guide. This is chiefly useful for
abstraction refinement metholdologies, and is only used as a
heuristic. */
virtual bool Solve() = 0;
virtual Counterexample GetCounterexample() = 0;
virtual bool SetOption(const std::string &option, const std::string &value) = 0;
/** Learn heuristic information from another solver. This
is chiefly useful for abstraction refinement, when we want to
solve a series of similar problems. */
virtual void LearnFrom(Counterexample &old_cex) = 0;
virtual ~Solver(){}
static Solver *Create(const std::string &solver_class, RPFP *rpfp);
/** This can be called asynchrnously to cause Solve to throw a
Canceled exception at some time in the future.
*/
virtual void Cancel() = 0;
/** Object thrown on cancellation */
struct Canceled {};
};
}

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

2185
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,599 @@
/*++
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"
namespace Duality {
solver::solver(Duality::context& c) : 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);
scoped_ptr<solver_factory> sf = mk_smt_solver_factory();
m_solver = (*sf)(m(), p, true, true, true, ::symbol::null);
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 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<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] = 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] = assumptions->getTerm();
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<expr> linear_assumptions(size);
std::vector<int> parents(size);
linearize_assumptions(0,assumptions,linear_assumptions,parents);
ptr_vector< ::ast> _interpolants(size-1);
ptr_vector< ::ast>_assumptions(size);
for(int i = 0; i < size; i++)
_assumptions[i] = linear_assumptions[i];
::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];
push();
if(!incremental){
for(unsigned i = 0; i < linear_assumptions.size(); i++)
add(linear_assumptions[i]);
}
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
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 "";
}
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 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();
}
}

1372
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]);
}

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

@ -0,0 +1,187 @@
/*++
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"
/* 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

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

@ -0,0 +1,352 @@
/*++
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
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

169
src/interp/iz3hash.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

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

@ -0,0 +1,465 @@
/*++
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<ast> &z3_preds,
const std::vector<int> &orig_parents,
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++)
frame_map[z3_preds[i]] = 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<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<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
std::vector<std::vector<ast> > cnsts_vec_vec(cnsts_vec.size());
for(unsigned i = 0; i < cnsts_vec.size(); i++)
cnsts_vec_vec[i].push_back(cnsts_vec[i]);
iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec_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);
cnsts_vec_vec[is_A ? 0 : 1].push_back(cnsts_vec[j]);
}
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;
}
// 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 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);
}

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

@ -0,0 +1,83 @@
/*++
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);
/* 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

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

@ -0,0 +1,831 @@
/*++
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(),
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);
}
// 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));
}
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);
}
// 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(!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);
}

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

@ -0,0 +1,707 @@
/*++
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 < other._ast;
}
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 (size_t)_ast;}
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(); // 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;
}
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 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;
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);
int occurs_in(ast var, ast e);
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'),
))

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

@ -0,0 +1,175 @@
/*++
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
// 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

2166
src/interp/iz3proof_itp.cpp Normal file

File diff suppressed because it is too large Load diff

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

@ -0,0 +1,138 @@
/*++
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 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

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

@ -0,0 +1,319 @@
/*++
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 "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

1734
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

1749
src/interp/iz3translate_direct.cpp Executable file

File diff suppressed because it is too large Load diff

12
src/muz/README Normal file
View file

@ -0,0 +1,12 @@
muZ: routines related to solving satisfiability of Horn clauses and
solving Datalog programs.
- base - contains base routines and the main context for
maintaining fixedpoint solvers
- transforms - common rule transformations
- rel - relational algebra based Datalog engine
- pdr - PDR based Horn clause solver
- clp - Dart/Symbolic execution-based solver
- tab - Tabulation based solver
- bmc - Bounded model checking based solver
- fp - main exported routines

View file

@ -49,7 +49,7 @@ Example from Boogie:
#include "model_pp.h"
#include "proof_utils.h"
#include "ast_pp.h"
#include "dl_util.h"
#include "qe_util.h"
namespace datalog {
@ -91,7 +91,7 @@ namespace datalog {
if (!m.is_implies(premise, l1, l2)) {
continue;
}
datalog::flatten_and(l1, literals);
qe::flatten_and(l1, literals);
positions2.reset();
premises2.reset();
premises2.push_back(premise);
@ -176,7 +176,7 @@ namespace datalog {
step &s = steps[j];
// TBD
s.m_labels;
// s.m_labels;
// set references, compensate for reverse ordering.
for (unsigned i = 0; i < s.m_refs.size(); ++i) {

View file

@ -19,36 +19,15 @@ Revision History:
#include<sstream>
#include<limits>
#include"arith_simplifier_plugin.h"
#include"basic_simplifier_plugin.h"
#include"arith_decl_plugin.h"
#include"bv_decl_plugin.h"
#include"dl_table.h"
#include"dl_table_relation.h"
#include"dl_rule_transformer.h"
#include"dl_mk_coi_filter.h"
#include"dl_mk_explanations.h"
#include"dl_mk_filter_rules.h"
#include"dl_mk_interp_tail_simplifier.h"
#include"dl_mk_rule_inliner.h"
#include"dl_mk_simple_joins.h"
#include"dl_mk_similarity_compressor.h"
#include"dl_mk_unbound_compressor.h"
#include"dl_mk_subsumption_checker.h"
#include"dl_compiler.h"
#include"dl_instruction.h"
#include"dl_context.h"
#include"for_each_expr.h"
#include"ast_smt_pp.h"
#include"ast_smt2_pp.h"
#include"dl_mk_partial_equiv.h"
#include"dl_mk_bit_blast.h"
#include"dl_mk_array_blast.h"
#include"dl_mk_karr_invariants.h"
#include"dl_mk_magic_symbolic.h"
#include"dl_mk_quantifier_abstraction.h"
#include"dl_mk_quantifier_instantiation.h"
#include"datatype_decl_plugin.h"
#include"scoped_proof.h"
#include"fixedpoint_params.hpp"
namespace datalog {
@ -200,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));
}
@ -207,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);
}
@ -216,11 +200,12 @@ namespace datalog {
//
// -----------------------------------
context::context(ast_manager & m, smt_params& fp, params_ref const& pa):
context::context(ast_manager & m, register_engine_base& re, smt_params& fp, params_ref const& pa):
m(m),
m_register_engine(re),
m_fparams(fp),
m_params_ref(pa),
m_params(m_params_ref),
m_params(alloc(fixedpoint_params, m_params_ref)),
m_decl_util(m),
m_rewriter(m),
m_var_subst(m),
@ -247,10 +232,12 @@ namespace datalog {
m_last_answer(m),
m_engine_type(LAST_ENGINE),
m_cancel(false) {
re.set_context(this);
}
context::~context() {
reset();
dealloc(m_params);
}
void context::reset() {
@ -283,6 +270,38 @@ namespace datalog {
return *m_sorts.find(s);
}
bool context::generate_proof_trace() const { return m_params->generate_proof_trace(); }
bool context::output_profile() const { return m_params->output_profile(); }
bool context::output_tuples() const { return m_params->output_tuples(); }
bool context::use_map_names() const { return m_params->use_map_names(); }
bool context::fix_unbound_vars() const { return m_params->fix_unbound_vars(); }
symbol context::default_table() const { return m_params->default_table(); }
symbol context::default_relation() const { return m_params->default_relation(); } // external_relation_plugin::get_name());
symbol context::default_table_checker() const { return m_params->default_table_checker(); }
bool context::default_table_checked() const { return m_params->default_table_checked(); }
bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->dbg_fpr_nonempty_relation_signature(); }
unsigned context::dl_profile_milliseconds_threshold() const { return m_params->profile_timeout_milliseconds(); }
bool context::all_or_nothing_deltas() const { return m_params->all_or_nothing_deltas(); }
bool context::compile_with_widening() const { return m_params->compile_with_widening(); }
bool context::unbound_compressor() const { return m_params->unbound_compressor(); }
bool context::similarity_compressor() const { return m_params->similarity_compressor(); }
unsigned context::similarity_compressor_threshold() const { return m_params->similarity_compressor_threshold(); }
unsigned context::soft_timeout() const { return m_fparams.m_soft_timeout; }
unsigned context::initial_restart_timeout() const { return m_params->initial_restart_timeout(); }
bool context::generate_explanations() const { return m_params->generate_explanations(); }
bool context::explanations_on_relation_level() const { return m_params->explanations_on_relation_level(); }
bool context::magic_sets_for_queries() const { return m_params->magic_sets_for_queries(); }
bool context::eager_emptiness_checking() const { return m_params->eager_emptiness_checking(); }
bool context::bit_blast() const { return m_params->bit_blast(); }
bool context::karr() const { return m_params->karr(); }
bool context::scale() const { return m_params->scale(); }
bool context::magic() const { return m_params->magic(); }
bool context::quantify_arrays() const { return m_params->quantify_arrays(); }
bool context::instantiate_quantifiers() const { return m_params->instantiate_quantifiers(); }
void context::register_finite_sort(sort * s, sort_kind k) {
m_pinned.push_back(s);
SASSERT(!m_sorts.contains(s));
@ -562,7 +581,7 @@ namespace datalog {
void context::check_uninterpreted_free(rule_ref& r) {
func_decl* f = 0;
if (r->has_uninterpreted_non_predicates(f)) {
if (r->has_uninterpreted_non_predicates(m, f)) {
std::stringstream stm;
stm << "Uninterpreted '"
<< f->get_name()
@ -651,6 +670,7 @@ namespace datalog {
if (check_pred(e)) {
std::ostringstream out;
out << "recursive predicate " << mk_ismt2_pp(e, get_manager()) << " occurs nested in body";
r->display(*this, out << "\n");
throw default_exception(out.str());
}
@ -667,9 +687,11 @@ namespace datalog {
case PDR_ENGINE:
check_existential_tail(r);
check_positive_predicates(r);
check_uninterpreted_free(r);
break;
case QPDR_ENGINE:
check_positive_predicates(r);
check_uninterpreted_free(r);
break;
case BMC_ENGINE:
check_positive_predicates(r);
@ -682,10 +704,15 @@ 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);
break;
case LAST_ENGINE:
default:
UNREACHABLE();
break;
@ -777,29 +804,6 @@ namespace datalog {
m_closed = false;
}
void context::transform_rules() {
m_transf.reset();
m_transf.register_plugin(alloc(mk_coi_filter, *this));
m_transf.register_plugin(alloc(mk_filter_rules, *this));
m_transf.register_plugin(alloc(mk_simple_joins, *this));
if (unbound_compressor()) {
m_transf.register_plugin(alloc(mk_unbound_compressor, *this));
}
if (similarity_compressor()) {
m_transf.register_plugin(alloc(mk_similarity_compressor, *this));
}
m_transf.register_plugin(alloc(mk_partial_equivalence_transformer, *this));
m_transf.register_plugin(alloc(mk_rule_inliner, *this));
m_transf.register_plugin(alloc(mk_interp_tail_simplifier, *this));
if (get_params().bit_blast()) {
m_transf.register_plugin(alloc(mk_bit_blast, *this, 22000));
m_transf.register_plugin(alloc(mk_interp_tail_simplifier, *this, 21000));
}
transform_rules(m_transf);
}
void context::transform_rules(rule_transformer::plugin* plugin) {
rule_transformer transformer(*this);
transformer.register_plugin(plugin);
@ -831,44 +835,6 @@ namespace datalog {
}
void context::apply_default_transformation() {
ensure_closed();
m_transf.reset();
m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this));
m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this));
m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 35005));
m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 35000));
m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34990));
m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34980));
//and another round of inlining
m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34975));
m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34970));
m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34960));
m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34950));
m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34940));
m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34930));
m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34920));
m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34910));
m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34900));
m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34890));
m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34880));
if (get_params().quantify_arrays()) {
m_transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, *this, 33000));
m_transf.register_plugin(alloc(datalog::mk_array_blast, *this, 32500));
}
m_transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, *this, 32000));
m_transf.register_plugin(alloc(datalog::mk_bit_blast, *this, 35000));
m_transf.register_plugin(alloc(datalog::mk_array_blast, *this, 36000));
m_transf.register_plugin(alloc(datalog::mk_karr_invariants, *this, 36010));
if (get_params().magic()) {
m_transf.register_plugin(alloc(datalog::mk_magic_symbolic, *this, 36020));
}
transform_rules(m_transf);
}
void context::collect_params(param_descrs& p) {
@ -940,7 +906,7 @@ namespace datalog {
};
void context::configure_engine() {
symbol e = m_params.engine();
symbol e = m_params->engine();
if (e == symbol("datalog")) {
m_engine_type = DATALOG_ENGINE;
@ -963,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;
@ -988,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;
@ -1001,6 +982,8 @@ namespace datalog {
case CLP_ENGINE:
flush_add_rules();
break;
case DUALITY_ENGINE:
break;
default:
UNREACHABLE();
}
@ -1020,37 +1003,19 @@ namespace datalog {
void context::ensure_engine() {
if (!m_engine.get()) {
switch (get_engine()) {
case PDR_ENGINE:
case QPDR_ENGINE:
m_engine = alloc(pdr::dl_interface, *this);
break;
case DATALOG_ENGINE:
m_rel = alloc(rel_context, *this);
m_engine = m_rel;
break;
case BMC_ENGINE:
case QBMC_ENGINE:
m_engine = alloc(bmc, *this);
break;
case TAB_ENGINE:
m_engine = alloc(tab, *this);
break;
case CLP_ENGINE:
m_engine = alloc(clp, *this);
break;
m_engine = m_register_engine.mk_engine(get_engine());
// break abstraction.
if (get_engine() == DATALOG_ENGINE) {
m_rel = dynamic_cast<rel_context_base*>(m_engine.get());
}
}
}
}
lbool context::rel_query(unsigned num_rels, func_decl * const* rels) {
ensure_engine();
if (m_rel) {
return m_rel->query(num_rels, rels);
}
else {
return l_undef;
}
return m_engine->query(num_rels, rels);
}
expr* context::get_answer_as_formula() {
@ -1067,11 +1032,15 @@ namespace datalog {
m_engine->display_certificate(out);
}
void context::display(std::ostream & out) const {
display_rules(out);
if (m_rel) m_rel->display_facts(out);
}
void context::display_profile(std::ostream& out) const {
out << "\n---------------\n";
out << "Original rules\n";
display_rules(out);
out << "\n---------------\n";
out << "Transformed rules\n";
m_transformed_rule_set.display(out);
@ -1133,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();
@ -1175,9 +1153,9 @@ namespace datalog {
expr_ref fml(m);
expr_ref_vector rules(m);
svector<symbol> names;
bool use_fixedpoint_extensions = m_params.print_with_fixedpoint_extensions();
bool print_low_level = m_params.print_low_level_smt2();
bool do_declare_vars = m_params.print_with_variable_declarations();
bool use_fixedpoint_extensions = m_params->print_with_fixedpoint_extensions();
bool print_low_level = m_params->print_low_level_smt2();
bool do_declare_vars = m_params->print_with_variable_declarations();
#define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env);

View file

@ -28,15 +28,9 @@ Revision History:
#include"th_rewriter.h"
#include"str_hashtable.h"
#include"var_subst.h"
#include"dl_base.h"
#include"dl_costs.h"
#include"dl_decl_plugin.h"
#include"dl_relation_manager.h"
#include"dl_rule_set.h"
#include"pdr_dl_interface.h"
#include"dl_bmc_engine.h"
#include"tab_context.h"
#include"rel_context.h"
#include"lbool.h"
#include"statistics.h"
#include"params.h"
@ -47,7 +41,9 @@ Revision History:
#include"dl_rule_transformer.h"
#include"expr_abstract.h"
#include"expr_functors.h"
#include"clp_context.h"
#include"dl_engine_base.h"
struct fixedpoint_params;
namespace datalog {
@ -60,6 +56,84 @@ namespace datalog {
CANCELED
};
class relation_manager;
typedef sort * relation_sort;
typedef uint64 table_element;
typedef svector<table_element> table_fact;
typedef app * relation_element;
typedef app_ref relation_element_ref;
class relation_fact : public app_ref_vector {
public:
class el_proxy {
friend class relation_fact;
relation_fact & m_parent;
unsigned m_idx;
el_proxy(relation_fact & parent, unsigned idx) : m_parent(parent), m_idx(idx) {}
public:
operator relation_element() const {
return m_parent.get(m_idx);
}
relation_element operator->() const {
return m_parent.get(m_idx);
}
relation_element operator=(const relation_element & val) const {
m_parent.set(m_idx, val);
return m_parent.get(m_idx);
}
relation_element operator=(const el_proxy & val) {
m_parent.set(m_idx, val);
return m_parent.get(m_idx);
}
};
typedef const relation_element * iterator;
relation_fact(ast_manager & m) : app_ref_vector(m) {}
relation_fact(ast_manager & m, unsigned sz) : app_ref_vector(m) { resize(sz); }
relation_fact(context & ctx);
iterator begin() const { return c_ptr(); }
iterator end() const { return c_ptr()+size(); }
relation_element operator[](unsigned i) const { return get(i); }
el_proxy operator[](unsigned i) { return el_proxy(*this, i); }
};
// attempt to modularize context code.
class rel_context_base : public engine_base {
public:
rel_context_base(ast_manager& m, char const* name): engine_base(m, name) {}
virtual ~rel_context_base() {}
virtual relation_manager & get_rmanager() = 0;
virtual const relation_manager & get_rmanager() const = 0;
virtual relation_base & get_relation(func_decl * pred) = 0;
virtual relation_base * try_get_relation(func_decl * pred) const = 0;
virtual bool is_empty_relation(func_decl* pred) const = 0;
virtual expr_ref try_get_formula(func_decl * pred) const = 0;
virtual void display_output_facts(rule_set const& rules, std::ostream & out) const = 0;
virtual void display_facts(std::ostream & out) const = 0;
virtual void display_profile(std::ostream& out) = 0;
virtual void restrict_predicates(func_decl_set const& predicates) = 0;
virtual bool result_contains_fact(relation_fact const& f) = 0;
virtual void add_fact(func_decl* pred, relation_fact const& fact) = 0;
virtual void add_fact(func_decl* pred, table_fact const& fact) = 0;
virtual bool has_facts(func_decl * pred) const = 0;
virtual void store_relation(func_decl * pred, relation_base * rel) = 0;
virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) = 0;
virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
symbol const * relation_names) = 0;
virtual bool output_profile() const = 0;
virtual void collect_non_empty_predicates(func_decl_set& preds) = 0;
virtual void transform_rules() = 0;
virtual bool try_get_size(func_decl* pred, unsigned& rel_sz) const = 0;
virtual lbool saturate() = 0;
};
class context {
public:
typedef unsigned finite_element;
@ -93,9 +167,10 @@ namespace datalog {
ast_manager & m;
register_engine_base& m_register_engine;
smt_params & m_fparams;
params_ref m_params_ref;
fixedpoint_params m_params;
fixedpoint_params* m_params;
dl_decl_util m_decl_util;
th_rewriter m_rewriter;
var_subst m_var_subst;
@ -122,7 +197,7 @@ namespace datalog {
model_converter_ref m_mc;
proof_converter_ref m_pc;
rel_context* m_rel;
rel_context_base* m_rel;
scoped_ptr<engine_base> m_engine;
bool m_closed;
@ -143,7 +218,7 @@ namespace datalog {
public:
context(ast_manager & m, smt_params& fp, params_ref const& p = params_ref());
context(ast_manager & m, register_engine_base& re, smt_params& fp, params_ref const& p = params_ref());
~context();
void reset();
@ -158,32 +233,41 @@ namespace datalog {
ast_manager & get_manager() const { return m; }
rule_manager & get_rule_manager() { return m_rule_manager; }
smt_params & get_fparams() const { return m_fparams; }
fixedpoint_params const& get_params() const { return m_params; }
fixedpoint_params const& get_params() const { return *m_params; }
DL_ENGINE get_engine() { configure_engine(); return m_engine_type; }
register_engine_base& get_register_engine() { return m_register_engine; }
th_rewriter& get_rewriter() { return m_rewriter; }
var_subst & get_var_subst() { return m_var_subst; }
dl_decl_util & get_decl_util() { return m_decl_util; }
bool generate_proof_trace() const { return m_params.generate_proof_trace(); }
bool output_profile() const { return m_params.output_profile(); }
bool fix_unbound_vars() const { return m_params.fix_unbound_vars(); }
symbol default_table() const { return m_params.default_table(); }
symbol default_relation() const { return m_params.default_relation(); } // external_relation_plugin::get_name());
symbol default_table_checker() const { return m_params.default_table_checker(); }
bool default_table_checked() const { return m_params.default_table_checked(); }
bool dbg_fpr_nonempty_relation_signature() const { return m_params.dbg_fpr_nonempty_relation_signature(); }
unsigned dl_profile_milliseconds_threshold() const { return m_params.profile_timeout_milliseconds(); }
bool all_or_nothing_deltas() const { return m_params.all_or_nothing_deltas(); }
bool compile_with_widening() const { return m_params.compile_with_widening(); }
bool unbound_compressor() const { return m_params.unbound_compressor(); }
bool similarity_compressor() const { return m_params.similarity_compressor(); }
unsigned similarity_compressor_threshold() const { return m_params.similarity_compressor_threshold(); }
unsigned soft_timeout() const { return m_fparams.m_soft_timeout; }
unsigned initial_restart_timeout() const { return m_params.initial_restart_timeout(); }
bool generate_explanations() const { return m_params.generate_explanations(); }
bool explanations_on_relation_level() const { return m_params.explanations_on_relation_level(); }
bool magic_sets_for_queries() const { return m_params.magic_sets_for_queries(); }
bool eager_emptiness_checking() const { return m_params.eager_emptiness_checking(); }
bool generate_proof_trace() const;
bool output_profile() const;
bool output_tuples() const;
bool use_map_names() const;
bool fix_unbound_vars() const;
symbol default_table() const;
symbol default_relation() const;
symbol default_table_checker() const;
bool default_table_checked() const;
bool dbg_fpr_nonempty_relation_signature() const;
unsigned dl_profile_milliseconds_threshold() const;
bool all_or_nothing_deltas() const;
bool compile_with_widening() const;
bool unbound_compressor() const;
bool similarity_compressor() const;
unsigned similarity_compressor_threshold() const;
unsigned soft_timeout() const;
unsigned initial_restart_timeout() const;
bool generate_explanations() const;
bool explanations_on_relation_level() const;
bool magic_sets_for_queries() const;
bool eager_emptiness_checking() const;
bool bit_blast() const;
bool karr() const;
bool scale() const;
bool magic() const;
bool quantify_arrays() const;
bool instantiate_quantifiers() const;
void register_finite_sort(sort * s, sort_kind k);
@ -273,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);
@ -355,7 +440,6 @@ namespace datalog {
proof_converter_ref& get_proof_converter() { return m_pc; }
void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); }
void transform_rules();
void transform_rules(rule_transformer& transf);
void transform_rules(rule_transformer::plugin* plugin);
void replace_rules(rule_set const& rs);
@ -371,10 +455,7 @@ namespace datalog {
m_rule_set.display(out);
}
void display(std::ostream & out) const {
display_rules(out);
if (m_rel) m_rel->display_facts(out);
}
void display(std::ostream & out) const;
void display_smt2(unsigned num_queries, expr* const* queries, std::ostream& out);
@ -412,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();
@ -459,7 +542,7 @@ namespace datalog {
*/
bool result_contains_fact(relation_fact const& f);
rel_context* get_rel_context() { ensure_engine(); return m_rel; }
rel_context_base* get_rel_context() { ensure_engine(); return m_rel; }
private:

View file

@ -0,0 +1,83 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
dl_engine_base.h
Abstract:
Base class for Datalog engines.
Author:
Nikolaj Bjorner (nbjorner) 2013-08-28
Revision History:
--*/
#ifndef _DL_ENGINE_BASE_H_
#define _DL_ENGINE_BASE_H_
#include "model.h"
namespace datalog {
enum DL_ENGINE {
DATALOG_ENGINE,
PDR_ENGINE,
QPDR_ENGINE,
BMC_ENGINE,
QBMC_ENGINE,
TAB_ENGINE,
CLP_ENGINE,
LAST_ENGINE,
DUALITY_ENGINE
};
class engine_base {
ast_manager& m;
std::string m_name;
public:
engine_base(ast_manager& m, char const* name): m(m), m_name(name) {}
virtual ~engine_base() {}
virtual expr_ref get_answer() = 0;
virtual lbool query(expr* q) = 0;
virtual lbool query(unsigned num_rels, func_decl*const* rels) { return l_undef; }
virtual void reset_statistics() {}
virtual void display_profile(std::ostream& out) const {}
virtual void collect_statistics(statistics& st) const {}
virtual unsigned get_num_levels(func_decl* pred) {
throw default_exception(std::string("get_num_levels is not supported for ") + m_name);
}
virtual expr_ref get_cover_delta(int level, func_decl* pred) {
throw default_exception(std::string("operation is not supported for ") + m_name);
}
virtual void add_cover(int level, func_decl* pred, expr* property) {
throw default_exception(std::string("operation is not supported for ") + m_name);
}
virtual void display_certificate(std::ostream& out) const {
throw default_exception(std::string("certificates are not supported for ") + m_name);
}
virtual model_ref get_model() {
return model_ref(alloc(model, m));
}
virtual proof_ref get_proof() {
return proof_ref(m.mk_asserted(m.mk_true()), m);
}
virtual void updt_params() {}
virtual void cancel() {}
virtual void cleanup() {}
};
class context;
class register_engine_base {
public:
virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0;
virtual void set_context(context* ctx) = 0;
};
}
#endif

View file

@ -41,6 +41,9 @@ Revision History:
#include"expr_replacer.h"
#include"bool_rewriter.h"
#include"expr_safe_replace.h"
#include"filter_model_converter.h"
#include"scoped_proof.h"
#include"datatype_decl_plugin.h"
namespace datalog {
@ -185,15 +188,20 @@ namespace datalog {
}
void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) {
DEBUG_CODE(ptr_vector<sort> sorts;
::get_free_vars(fml, sorts); );
expr_ref_vector fmls(m);
proof_ref_vector prs(m);
m_hnf.reset();
m_hnf.set_name(name);
m_hnf(fml, p, fmls, prs);
for (unsigned i = 0; i < m_hnf.get_fresh_predicates().size(); ++i) {
m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false);
}
for (unsigned i = 0; i < fmls.size(); ++i) {
DEBUG_CODE(ptr_vector<sort> sorts;
::get_free_vars(fmls[i].get(), sorts); );
mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name);
}
}
@ -257,14 +265,14 @@ namespace datalog {
if (m.is_implies(fml, e1, e2)) {
expr_ref_vector es(m);
head = ensure_app(e2);
datalog::flatten_and(e1, es);
qe::flatten_and(e1, es);
for (unsigned i = 0; i < es.size(); ++i) {
body.push_back(ensure_app(es[i].get()));
}
}
else {
head = ensure_app(fml);
}
}
return index;
}
@ -335,8 +343,14 @@ namespace datalog {
vars.reverse();
names.reverse();
func_decl* qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred);
m_ctx.register_predicate(qpred, false);
m_ctx.register_predicate(qpred, false);
rules.set_output_predicate(qpred);
if (m_ctx.get_model_converter()) {
filter_model_converter* mc = alloc(filter_model_converter, m);
mc->insert(qpred);
m_ctx.add_model_converter(mc);
}
expr_ref_vector qhead_args(m);
for (unsigned i = 0; i < vars.size(); i++) {
@ -368,7 +382,7 @@ namespace datalog {
for (unsigned i = 0; i < body.size(); ++i) {
r.push_back(body[i].get());
}
flatten_and(r);
qe::flatten_and(r);
body.reset();
for (unsigned i = 0; i < r.size(); ++i) {
body.push_back(ensure_app(r[i].get()));
@ -484,6 +498,12 @@ namespace datalog {
app * * uninterp_tail = r->m_tail; //grows upwards
app * * interp_tail = r->m_tail+n; //grows downwards
DEBUG_CODE(ptr_vector<sort> sorts;
::get_free_vars(head, sorts);
for (unsigned i = 0; i < n; ++i) {
::get_free_vars(tail[i], sorts);
});
bool has_neg = false;
for (unsigned i = 0; i < n; i++) {
@ -862,9 +882,12 @@ namespace datalog {
}
struct uninterpreted_function_finder_proc {
ast_manager& m;
datatype_util m_dt;
bool m_found;
func_decl* m_func;
uninterpreted_function_finder_proc() : m_found(false), m_func(0) {}
uninterpreted_function_finder_proc(ast_manager& m):
m(m), m_dt(m), m_found(false), m_func(0) {}
void operator()(var * n) { }
void operator()(quantifier * n) { }
void operator()(app * n) {
@ -872,6 +895,14 @@ namespace datalog {
m_found = true;
m_func = n->get_decl();
}
else if (m_dt.is_accessor(n)) {
sort* s = m.get_sort(n->get_arg(0));
SASSERT(m_dt.is_datatype(s));
if (m_dt.get_datatype_constructors(s)->size() > 1) {
m_found = true;
m_func = n->get_decl();
}
}
}
bool found(func_decl*& f) const { f = m_func; return m_found; }
@ -881,9 +912,9 @@ namespace datalog {
// non-predicates may appear only in the interpreted tail, it is therefore
// sufficient only to check the tail.
//
bool rule::has_uninterpreted_non_predicates(func_decl*& f) const {
bool rule::has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const {
unsigned sz = get_tail_size();
uninterpreted_function_finder_proc proc;
uninterpreted_function_finder_proc proc(m);
expr_mark visited;
for (unsigned i = get_uninterpreted_tail_size(); i < sz && !proc.found(f); ++i) {
for_each_expr(proc, visited, get_tail(i));
@ -891,6 +922,7 @@ namespace datalog {
return proc.found(f);
}
struct quantifier_finder_proc {
bool m_exist;
bool m_univ;

View file

@ -293,7 +293,7 @@ namespace datalog {
*/
bool is_in_tail(const func_decl * p, bool only_positive=false) const;
bool has_uninterpreted_non_predicates(func_decl*& f) const;
bool has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const;
void has_quantifiers(bool& existential, bool& universal) const;
bool has_quantifiers() const;
bool has_negation() const;

View file

@ -22,6 +22,7 @@ Revision History:
#include"dl_context.h"
#include"dl_rule_transformer.h"
#include"stopwatch.h"
namespace datalog {
@ -37,7 +38,7 @@ namespace datalog {
void rule_transformer::reset() {
plugin_vector::iterator it = m_plugins.begin();
plugin_vector::iterator end = m_plugins.end();
for(; it!=end; ++it) {
for(; it!=end; ++it) {
dealloc(*it);
}
m_plugins.reset();
@ -86,8 +87,16 @@ namespace datalog {
for(; it!=end && !m_context.canceled(); ++it) {
plugin & p = **it;
IF_VERBOSE(1, verbose_stream() << "(transform " << typeid(p).name() << "...";);
stopwatch sw;
sw.start();
rule_set * new_rules1 = p(*new_rules);
sw.stop();
double sec = sw.get_seconds();
if (sec < 0.001) sec = 0.0;
if (!new_rules1) {
IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";);
continue;
}
if (p.can_destratify_negation() &&
@ -96,6 +105,7 @@ namespace datalog {
warning_msg("a rule transformation skipped "
"because it destratified negation");
dealloc(new_rules1);
IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";);
continue;
}
modified = true;
@ -103,6 +113,7 @@ namespace datalog {
new_rules = new_rules1;
new_rules->ensure_closed();
IF_VERBOSE(1, verbose_stream() << new_rules->get_num_rules() << " rules " << sec << "s)\n";);
TRACE("dl_rule_transf",
tout << typeid(p).name()<<":\n";
new_rules->display(tout);

View file

@ -25,133 +25,35 @@ Revision History:
#endif
#include"ast_pp.h"
#include"bool_rewriter.h"
#include"for_each_expr.h"
#include"scoped_proof.h"
#include"dl_context.h"
#include"dl_rule.h"
#include"for_each_expr.h"
#include"dl_util.h"
#include"stopwatch.h"
namespace datalog {
void universal_delete(relation_base* ptr) {
ptr->deallocate();
verbose_action::verbose_action(char const* msg, unsigned lvl): m_lvl(lvl), m_sw(0) {
IF_VERBOSE(m_lvl,
(verbose_stream() << msg << "...").flush();
m_sw = alloc(stopwatch);
m_sw->start(););
}
void universal_delete(table_base* ptr) {
ptr->deallocate();
}
void flatten_and(expr_ref_vector& result) {
ast_manager& m = result.get_manager();
expr* e1, *e2, *e3;
for (unsigned i = 0; i < result.size(); ++i) {
if (m.is_and(result[i].get())) {
app* a = to_app(result[i].get());
unsigned num_args = a->get_num_args();
for (unsigned j = 0; j < num_args; ++j) {
result.push_back(a->get_arg(j));
}
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) {
result[i] = e2;
--i;
}
else if (m.is_not(result[i].get(), e1) && m.is_or(e1)) {
app* a = to_app(e1);
unsigned num_args = a->get_num_args();
for (unsigned j = 0; j < num_args; ++j) {
result.push_back(m.mk_not(a->get_arg(j)));
}
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) {
result.push_back(e2);
result[i] = m.mk_not(e3);
--i;
}
else if (m.is_true(result[i].get()) ||
(m.is_not(result[i].get(), e1) &&
m.is_false(e1))) {
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_false(result[i].get()) ||
(m.is_not(result[i].get(), e1) &&
m.is_true(e1))) {
result.reset();
result.push_back(m.mk_false());
return;
}
}
}
void flatten_and(expr* fml, expr_ref_vector& result) {
SASSERT(result.get_manager().is_bool(fml));
result.push_back(fml);
flatten_and(result);
}
void flatten_or(expr_ref_vector& result) {
ast_manager& m = result.get_manager();
expr* e1, *e2, *e3;
for (unsigned i = 0; i < result.size(); ++i) {
if (m.is_or(result[i].get())) {
app* a = to_app(result[i].get());
unsigned num_args = a->get_num_args();
for (unsigned j = 0; j < num_args; ++j) {
result.push_back(a->get_arg(j));
}
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) {
result[i] = e2;
--i;
}
else if (m.is_not(result[i].get(), e1) && m.is_and(e1)) {
app* a = to_app(e1);
unsigned num_args = a->get_num_args();
for (unsigned j = 0; j < num_args; ++j) {
result.push_back(m.mk_not(a->get_arg(j)));
}
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_implies(result[i].get(),e2,e3)) {
result.push_back(e3);
result[i] = m.mk_not(e2);
--i;
}
else if (m.is_false(result[i].get()) ||
(m.is_not(result[i].get(), e1) &&
m.is_true(e1))) {
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_true(result[i].get()) ||
(m.is_not(result[i].get(), e1) &&
m.is_false(e1))) {
result.reset();
result.push_back(m.mk_true());
return;
}
}
verbose_action::~verbose_action() {
double sec = 0.0;
if (m_sw) m_sw->stop();
sec = m_sw?m_sw->get_seconds():0.0;
if (sec < 0.001) sec = 0.0;
IF_VERBOSE(m_lvl,
(verbose_stream() << sec << "s\n").flush();
);
dealloc(m_sw);
}
void flatten_or(expr* fml, expr_ref_vector& result) {
SASSERT(result.get_manager().is_bool(fml));
result.push_back(fml);
flatten_or(result);
}
bool contains_var(expr * trm, unsigned var_idx) {
ptr_vector<sort> vars;
@ -532,18 +434,6 @@ namespace datalog {
}
}
void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig,
expr_ref_vector & renaming_arg) {
ast_manager & m = renaming_arg.get_manager();
unsigned sz = map.size();
unsigned ofs = sz-1;
renaming_arg.resize(sz, static_cast<expr *>(0));
for(unsigned i=0; i<sz; i++) {
if(map[i]!=UINT_MAX) {
renaming_arg.set(ofs-i, m.mk_var(map[i], orig_sig[i]));
}
}
}
void print_renaming(const expr_ref_vector & cont, std::ostream & out) {
unsigned len = cont.size();
@ -643,13 +533,6 @@ namespace datalog {
}
}
void dealloc_ptr_vector_content(ptr_vector<relation_base> & v) {
ptr_vector<relation_base>::iterator it = v.begin();
ptr_vector<relation_base>::iterator end = v.end();
for(; it!=end; ++it) {
(*it)->deallocate();
}
}
// -----------------------------------

View file

@ -26,10 +26,10 @@ Revision History:
#include"horn_subsume_model_converter.h"
#include"replace_proof_converter.h"
#include"substitution.h"
#include"fixedpoint_params.hpp"
#include"ast_counter.h"
#include"statistics.h"
#include"lbool.h"
#include"qe_util.h"
namespace datalog {
@ -42,6 +42,14 @@ namespace datalog {
class relation_fact;
class relation_signature;
class verbose_action {
unsigned m_lvl;
class stopwatch* m_sw;
public:
verbose_action(char const* msg, unsigned lvl = 1);
~verbose_action();
};
enum PDR_CACHE_MODE {
NO_CACHE,
HASH_CACHE,
@ -49,53 +57,6 @@ namespace datalog {
LAST_CACHE_MODE
};
enum DL_ENGINE {
DATALOG_ENGINE,
PDR_ENGINE,
QPDR_ENGINE,
BMC_ENGINE,
QBMC_ENGINE,
TAB_ENGINE,
CLP_ENGINE,
LAST_ENGINE
};
class engine_base {
ast_manager& m;
std::string m_name;
public:
engine_base(ast_manager& m, char const* name): m(m), m_name(name) {}
virtual ~engine_base() {}
virtual expr_ref get_answer() = 0;
virtual lbool query(expr* q) = 0;
virtual void reset_statistics() {}
virtual void display_profile(std::ostream& out) const {}
virtual void collect_statistics(statistics& st) const {}
virtual unsigned get_num_levels(func_decl* pred) {
throw default_exception(std::string("get_num_levels is not supported for ") + m_name);
}
virtual expr_ref get_cover_delta(int level, func_decl* pred) {
throw default_exception(std::string("operation is not supported for ") + m_name);
}
virtual void add_cover(int level, func_decl* pred, expr* property) {
throw default_exception(std::string("operation is not supported for ") + m_name);
}
virtual void display_certificate(std::ostream& out) const {
throw default_exception(std::string("certificates are not supported for ") + m_name);
}
virtual model_ref get_model() {
return model_ref(alloc(model, m));
}
virtual proof_ref get_proof() {
return proof_ref(m.mk_asserted(m.mk_true()), m);
}
virtual void updt_params() {}
virtual void cancel() {}
virtual void cleanup() {}
};
struct std_string_hash_proc {
unsigned operator()(const std::string & s) const
{ return string_hash(s.c_str(), static_cast<unsigned>(s.length()), 17); }
@ -107,18 +68,6 @@ namespace datalog {
typedef u_map<var *> varidx2var_map;
typedef obj_hashtable<func_decl> func_decl_set; //!< Rule dependencies.
typedef vector<std::string> string_vector;
/**
\brief Collect top-level conjunctions and disjunctions.
*/
void flatten_and(expr_ref_vector& result);
void flatten_and(expr* fml, expr_ref_vector& result);
void flatten_or(expr_ref_vector& result);
void flatten_or(expr* fml, expr_ref_vector& result);
bool contains_var(expr * trm, unsigned var_idx);
@ -181,36 +130,6 @@ namespace datalog {
*/
void display_fact(context & ctx, app * f, std::ostream & out);
class scoped_proof_mode {
ast_manager& m;
proof_gen_mode m_mode;
public:
scoped_proof_mode(ast_manager& m, proof_gen_mode mode): m(m) {
m_mode = m.proof_mode();
m.toggle_proof_mode(mode);
}
~scoped_proof_mode() {
m.toggle_proof_mode(m_mode);
}
};
class scoped_proof : public scoped_proof_mode {
public:
scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_FINE) {}
};
class scoped_no_proof : public scoped_proof_mode {
public:
scoped_no_proof(ast_manager& m): scoped_proof_mode(m, PGM_DISABLED) {}
};
class scoped_restore_proof : public scoped_proof_mode {
public:
scoped_restore_proof(ast_manager& m): scoped_proof_mode(m, m.proof_mode()) {}
};
class variable_intersection
@ -446,14 +365,6 @@ namespace datalog {
void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt);
/**
\brief Populate vector \c renaming_args so that it can be used as an argument to \c var_subst.
The renaming we want is one that transforms variables with numbers of indexes of \c map into the
values of at those indexes. If a value if \c UINT_MAX, it means we do not transform the index
corresponding to it.
*/
void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig,
expr_ref_vector & renaming_arg);
void print_renaming(const expr_ref_vector & cont, std::ostream & out);
@ -645,8 +556,6 @@ namespace datalog {
}
}
void dealloc_ptr_vector_content(ptr_vector<relation_base> & v);
/**
\brief Add elements from an iterable object \c src into the vector \c vector.
*/
@ -803,32 +712,6 @@ namespace datalog {
dealloc(ptr);
}
void universal_delete(relation_base* ptr);
void universal_delete(table_base* ptr);
template<typename T>
class scoped_rel {
T* m_t;
public:
scoped_rel(T* t) : m_t(t) {}
~scoped_rel() { if (m_t) { universal_delete(m_t); } }
scoped_rel() : m_t(0) {}
scoped_rel& operator=(T* t) { if (m_t) { universal_delete(m_t); } m_t = t; return *this; }
T* operator->() { return m_t; }
const T* operator->() const { return m_t; }
T& operator*() { return *m_t; }
const T& operator*() const { return *m_t; }
operator bool() const { return m_t!=0; }
T* get() { return m_t; }
/**
\brief Remove object from \c scoped_rel without deleting it.
*/
T* release() {
T* res = m_t;
m_t = 0;
return res;
}
};
/**
\brief If it is possible to convert the beginning of \c s to uint64,

View file

@ -11,6 +11,7 @@ def_module_params('fixedpoint',
('explanations_on_relation_level', BOOL, False, '(DATALOG) if true, explanations are generated as history of each relation, rather than per fact (generate_explanations must be set to true for this option to have any effect)'),
('magic_sets_for_queries', BOOL, False, "(DATALOG) magic set transformation will be used for queries"),
('magic', BOOL, False, "perform symbolic magic set transformation"),
('scale', BOOL, False, "add scaling variable to linear real arithemtic clauses"),
('unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables in rule heads"),
('similarity_compressor', BOOL, True, "(DATALOG) rules that differ only in values of constants will be merged into a single rule"),
('similarity_compressor_threshold', UINT, 11, "(DATALOG) if similarity_compressor is on, this value determines how many similar rules there must be in order for them to be merged"),
@ -49,7 +50,8 @@ def_module_params('fixedpoint',
('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"),
('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"),
('use_arith_inductive_generalizer', BOOL, False, "PDR: generalize lemmas using arithmetic heuristics for induction strengthening"),
('use_convex_hull_generalizer', BOOL, False, "PDR: generalize using convex hulls of lemmas"),
('use_convex_closure_generalizer', BOOL, False, "PDR: generalize using convex closures of lemmas"),
('use_convex_interior_generalizer', BOOL, False, "PDR: generalize using convex interiors of lemmas"),
('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"),
('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when "
"checking for reachability (not only during cube weakening)"),
@ -65,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

@ -128,7 +128,12 @@ public:
}
void set_name(symbol const& n) {
m_name = n;
if (n == symbol::null) {
m_name = symbol("P");
}
else {
m_name = n;
}
}
func_decl_ref_vector const& get_fresh_predicates() {
@ -209,7 +214,7 @@ private:
m_body.push_back(e1);
head = e2;
}
datalog::flatten_and(m_body);
qe::flatten_and(m_body);
if (premise) {
p = m.mk_rewrite(fml0, mk_implies(m_body, head));
}

Some files were not shown because too many files have changed in this diff Show more