diff --git a/.gitattributes b/.gitattributes index b86ed5df79b..8df0f39d948 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,4 @@ +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto + src/api/dotnet/Properties/AssemblyInfo.cs text eol=crlf diff --git a/.gitignore b/.gitignore index 2b3149d04d8..75a87c37b18 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,8 @@ src/api/api_log_macros.cpp src/api/dll/api_dll.def src/api/dotnet/Enumerations.cs src/api/dotnet/Native.cs +src/api/dotnet/Properties/AssemblyInfo.cs +src/api/dotnet/Microsoft.Z3.xml src/api/python/z3consts.py src/api/python/z3core.py src/ast/pattern/database.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 301282870a0..ce1014ebeec 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -43,17 +43,18 @@ def init_project_def(): # Simplifier module will be deleted in the future. # It has been replaced with rewriter module. add_lib('simplifier', ['rewriter'], 'ast/simplifier') + add_lib('fpa', ['ast', 'util', 'simplifier'], 'ast/fpa') add_lib('macros', ['simplifier'], 'ast/macros') add_lib('pattern', ['normal_forms', 'smt2parser', 'simplifier'], 'ast/pattern') add_lib('bit_blaster', ['rewriter', 'simplifier'], 'ast/rewriter/bit_blaster') add_lib('smt_params', ['ast', 'simplifier', 'pattern', 'bit_blaster'], 'smt/params') add_lib('proto_model', ['model', 'simplifier', 'smt_params'], 'smt/proto_model') add_lib('smt', ['bit_blaster', 'macros', 'normal_forms', 'cmd_context', 'proto_model', - 'substitution', 'grobner', 'euclid', 'simplex', 'proof_checker', 'pattern', 'parser_util']) + 'substitution', 'grobner', 'euclid', 'simplex', 'proof_checker', 'pattern', 'parser_util', 'fpa']) add_lib('user_plugin', ['smt'], 'smt/user_plugin') add_lib('bv_tactics', ['tactic', 'bit_blaster'], 'tactic/bv') add_lib('fuzzing', ['ast'], 'test/fuzzing') - add_lib('fpa', ['core_tactics', 'bv_tactics', 'sat_tactic'], 'tactic/fpa') + add_lib('fpa_tactics', ['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') add_lib('qe', ['smt','sat'], 'qe') @@ -69,7 +70,7 @@ def init_project_def(): add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf'], 'muz/fp') add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') - add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') + add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') add_lib('opt', ['smt', 'smtlogic_tactics', 'sls_tactic'], 'opt') API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h'] diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 88349261434..89cabe87e05 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -54,6 +54,7 @@ def getenv(name, default): IS_WINDOWS=False IS_LINUX=False IS_OSX=False +IS_FREEBSD=False VERBOSE=True DEBUG_MODE=False SHOW_CPPS = True @@ -98,6 +99,9 @@ def is_windows(): def is_linux(): return IS_LINUX +def is_freebsd(): + return IS_FREEBSD + def is_osx(): return IS_OSX @@ -426,6 +430,8 @@ def check_eol(): IS_OSX=True elif os.uname()[0] == 'Linux': IS_LINUX=True + elif os.uname()[0] == 'FreeBSD': + IS_FREEBSD=True def display_help(exit_code): print("mk_make.py: Z3 Makefile generator\n") @@ -1181,6 +1187,8 @@ def mk_makefile(self, out): t = t.replace('PLATFORM', 'darwin') elif IS_LINUX: t = t.replace('PLATFORM', 'linux') + elif IS_FREEBSD: + t = t.replace('PLATFORM', 'freebsd') else: t = t.replace('PLATFORM', 'win32') out.write(t) diff --git a/scripts/mk_win_dist.py b/scripts/mk_win_dist.py index a52c8af4306..3a95fb730f0 100644 --- a/scripts/mk_win_dist.py +++ b/scripts/mk_win_dist.py @@ -144,7 +144,7 @@ def mk_z3_core(x64): cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" amd64') cmds.append('cd %s' % BUILD_X64_DIR) else: - cmds.append('"call %VCINSTALLDIR%vcvarsall.bat" x86') + cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" x86') cmds.append('cd %s' % BUILD_X86_DIR) cmds.append('nmake') if exec_cmds(cmds) != 0: diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index 07d4fda18bb..0109d6e6b2b 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -117,6 +117,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_sort int_s = Z3_mk_int_sort(c); if (is_signed) { Z3_ast r = Z3_mk_bv2int(c, n, false); + Z3_inc_ref(c, r); Z3_sort s = Z3_get_sort(c, n); unsigned sz = Z3_get_bv_sort_size(c, s); rational max_bound = power(rational(2), sz); @@ -135,6 +136,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_dec_ref(c, pred); Z3_dec_ref(c, sub); Z3_dec_ref(c, zero); + Z3_dec_ref(c, r); RETURN_Z3(res); } else { diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 847115951d7..fcbba1ff6c7 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -302,11 +302,11 @@ public ListSort MkListSort(string name, Sort elemSort) } /// - /// Create a new finite domain sort. - /// The name used to identify the sort - /// The size of the sort - /// The result is a sort + /// Create a new finite domain sort. + /// The result is a sort /// + /// The name used to identify the sort + /// The size of the sort public FiniteDomainSort MkFiniteDomainSort(Symbol name, ulong size) { Contract.Requires(name != null); @@ -317,13 +317,13 @@ public FiniteDomainSort MkFiniteDomainSort(Symbol name, ulong size) } /// - /// Create a new finite domain sort. - /// The name used to identify the sort - /// The size of the sort - /// The result is a sort - /// Elements of the sort are created using , - /// and the elements range from 0 to size-1. + /// Create a new finite domain sort. + /// The result is a sort + /// Elements of the sort are created using , + /// and the elements range from 0 to size-1. /// + /// The name used to identify the sort + /// The size of the sort public FiniteDomainSort MkFiniteDomainSort(string name, ulong size) { Contract.Ensures(Contract.Result() != null); diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index 49b46edebab..c8fdfb51f4b 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -99,7 +99,7 @@ public void Update(Expr[] args) Contract.Requires(Contract.ForAll(args, a => a != null)); Context.CheckContextMatch(args); - if (args.Length != NumArgs) + if (IsApp && args.Length != NumArgs) throw new Z3Exception("Number of arguments does not match"); NativeObject = Native.Z3_update_term(Context.nCtx, NativeObject, (uint)args.Length, Expr.ArrayToNative(args)); } @@ -269,57 +269,57 @@ public bool IsBool /// /// Indicates whether the term is the constant true. /// - public bool IsTrue { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TRUE; } } + public bool IsTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TRUE; } } /// /// Indicates whether the term is the constant false. /// - public bool IsFalse { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FALSE; } } + public bool IsFalse { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FALSE; } } /// /// Indicates whether the term is an equality predicate. /// - public bool IsEq { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EQ; } } + public bool IsEq { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EQ; } } /// /// Indicates whether the term is an n-ary distinct predicate (every argument is mutually distinct). /// - public bool IsDistinct { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DISTINCT; } } + public bool IsDistinct { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DISTINCT; } } /// /// Indicates whether the term is a ternary if-then-else term /// - public bool IsITE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ITE; } } + public bool IsITE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ITE; } } /// /// Indicates whether the term is an n-ary conjunction /// - public bool IsAnd { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AND; } } + public bool IsAnd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AND; } } /// /// Indicates whether the term is an n-ary disjunction /// - public bool IsOr { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OR; } } + public bool IsOr { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OR; } } /// /// Indicates whether the term is an if-and-only-if (Boolean equivalence, binary) /// - public bool IsIff { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IFF; } } + public bool IsIff { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IFF; } } /// /// Indicates whether the term is an exclusive or /// - public bool IsXor { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR; } } + public bool IsXor { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR; } } /// /// Indicates whether the term is a negation /// - public bool IsNot { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_NOT; } } + public bool IsNot { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_NOT; } } /// /// Indicates whether the term is an implication /// - public bool IsImplies { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IMPLIES; } } + public bool IsImplies { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IMPLIES; } } #endregion @@ -347,82 +347,82 @@ public bool IsReal /// /// Indicates whether the term is an arithmetic numeral. /// - public bool IsArithmeticNumeral { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ANUM; } } + public bool IsArithmeticNumeral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ANUM; } } /// /// Indicates whether the term is a less-than-or-equal /// - public bool IsLE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LE; } } + public bool IsLE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LE; } } /// /// Indicates whether the term is a greater-than-or-equal /// - public bool IsGE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GE; } } + public bool IsGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GE; } } /// /// Indicates whether the term is a less-than /// - public bool IsLT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LT; } } + public bool IsLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LT; } } /// /// Indicates whether the term is a greater-than /// - public bool IsGT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GT; } } + public bool IsGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GT; } } /// /// Indicates whether the term is addition (binary) /// - public bool IsAdd { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ADD; } } + public bool IsAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ADD; } } /// /// Indicates whether the term is subtraction (binary) /// - public bool IsSub { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SUB; } } + public bool IsSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SUB; } } /// /// Indicates whether the term is a unary minus /// - public bool IsUMinus { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UMINUS; } } + public bool IsUMinus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UMINUS; } } /// /// Indicates whether the term is multiplication (binary) /// - public bool IsMul { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MUL; } } + public bool IsMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MUL; } } /// /// Indicates whether the term is division (binary) /// - public bool IsDiv { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DIV; } } + public bool IsDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DIV; } } /// /// Indicates whether the term is integer division (binary) /// - public bool IsIDiv { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IDIV; } } + public bool IsIDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IDIV; } } /// /// Indicates whether the term is remainder (binary) /// - public bool IsRemainder { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REM; } } + public bool IsRemainder { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REM; } } /// /// Indicates whether the term is modulus (binary) /// - public bool IsModulus { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MOD; } } + public bool IsModulus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MOD; } } /// /// Indicates whether the term is a coercion of integer to real (unary) /// - public bool IsIntToReal { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_REAL; } } + public bool IsIntToReal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_REAL; } } /// /// Indicates whether the term is a coercion of real to integer (unary) /// - public bool IsRealToInt { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_INT; } } + public bool IsRealToInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_INT; } } /// /// Indicates whether the term is a check that tests whether a real is integral (unary) /// - public bool IsRealIsInt { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IS_INT; } } + public bool IsRealIsInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IS_INT; } } #endregion #region Array Terms @@ -444,64 +444,64 @@ public bool IsArray /// /// It satisfies select(store(a,i,v),j) = if i = j then v else select(a,j). /// Array store takes at least 3 arguments. - public bool IsStore { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_STORE; } } + public bool IsStore { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_STORE; } } /// /// Indicates whether the term is an array select. /// - public bool IsSelect { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SELECT; } } + public bool IsSelect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SELECT; } } /// /// Indicates whether the term is a constant array. /// /// For example, select(const(v),i) = v holds for every v and i. The function is unary. - public bool IsConstantArray { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONST_ARRAY; } } + public bool IsConstantArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONST_ARRAY; } } /// /// Indicates whether the term is a default array. /// /// For example default(const(v)) = v. The function is unary. - public bool IsDefaultArray { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } } + public bool IsDefaultArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } } /// /// Indicates whether the term is an array map. /// /// It satisfies map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. - public bool IsArrayMap { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_MAP; } } + public bool IsArrayMap { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_MAP; } } /// /// Indicates whether the term is an as-array term. /// /// An as-array term is n array value that behaves as the function graph of the /// function passed as parameter. - public bool IsAsArray { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AS_ARRAY; } } + public bool IsAsArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AS_ARRAY; } } #endregion #region Set Terms /// /// Indicates whether the term is set union /// - public bool IsSetUnion { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_UNION; } } + public bool IsSetUnion { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_UNION; } } /// /// Indicates whether the term is set intersection /// - public bool IsSetIntersect { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_INTERSECT; } } + public bool IsSetIntersect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_INTERSECT; } } /// /// Indicates whether the term is set difference /// - public bool IsSetDifference { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } } + public bool IsSetDifference { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } } /// /// Indicates whether the term is set complement /// - public bool IsSetComplement { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } } + public bool IsSetComplement { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } } /// /// Indicates whether the term is set subset /// - public bool IsSetSubset { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_SUBSET; } } + public bool IsSetSubset { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_SUBSET; } } #endregion #region Bit-vector terms @@ -516,266 +516,266 @@ public bool IsBV /// /// Indicates whether the term is a bit-vector numeral /// - public bool IsBVNumeral { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNUM; } } + public bool IsBVNumeral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNUM; } } /// /// Indicates whether the term is a one-bit bit-vector with value one /// - public bool IsBVBitOne { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT1; } } + public bool IsBVBitOne { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT1; } } /// /// Indicates whether the term is a one-bit bit-vector with value zero /// - public bool IsBVBitZero { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT0; } } + public bool IsBVBitZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT0; } } /// /// Indicates whether the term is a bit-vector unary minus /// - public bool IsBVUMinus { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNEG; } } + public bool IsBVUMinus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNEG; } } /// /// Indicates whether the term is a bit-vector addition (binary) /// - public bool IsBVAdd { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BADD; } } + public bool IsBVAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BADD; } } /// /// Indicates whether the term is a bit-vector subtraction (binary) /// - public bool IsBVSub { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSUB; } } + public bool IsBVSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSUB; } } /// /// Indicates whether the term is a bit-vector multiplication (binary) /// - public bool IsBVMul { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BMUL; } } + public bool IsBVMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BMUL; } } /// /// Indicates whether the term is a bit-vector signed division (binary) /// - public bool IsBVSDiv { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV; } } + public bool IsBVSDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV; } } /// /// Indicates whether the term is a bit-vector unsigned division (binary) /// - public bool IsBVUDiv { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV; } } + public bool IsBVUDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV; } } /// /// Indicates whether the term is a bit-vector signed remainder (binary) /// - public bool IsBVSRem { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM; } } + public bool IsBVSRem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM; } } /// /// Indicates whether the term is a bit-vector unsigned remainder (binary) /// - public bool IsBVURem { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM; } } + public bool IsBVURem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM; } } /// /// Indicates whether the term is a bit-vector signed modulus /// - public bool IsBVSMod { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD; } } + public bool IsBVSMod { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD; } } /// /// Indicates whether the term is a bit-vector signed division by zero /// - internal bool IsBVSDiv0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV0; } } + internal bool IsBVSDiv0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV0; } } /// /// Indicates whether the term is a bit-vector unsigned division by zero /// - internal bool IsBVUDiv0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV0; } } + internal bool IsBVUDiv0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV0; } } /// /// Indicates whether the term is a bit-vector signed remainder by zero /// - internal bool IsBVSRem0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM0; } } + internal bool IsBVSRem0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM0; } } /// /// Indicates whether the term is a bit-vector unsigned remainder by zero /// - internal bool IsBVURem0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM0; } } + internal bool IsBVURem0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM0; } } /// /// Indicates whether the term is a bit-vector signed modulus by zero /// - internal bool IsBVSMod0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD0; } } + internal bool IsBVSMod0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD0; } } /// /// Indicates whether the term is an unsigned bit-vector less-than-or-equal /// - public bool IsBVULE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULEQ; } } + public bool IsBVULE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULEQ; } } /// /// Indicates whether the term is a signed bit-vector less-than-or-equal /// - public bool IsBVSLE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLEQ; } } + public bool IsBVSLE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLEQ; } } /// /// Indicates whether the term is an unsigned bit-vector greater-than-or-equal /// - public bool IsBVUGE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGEQ; } } + public bool IsBVUGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGEQ; } } /// /// Indicates whether the term is a signed bit-vector greater-than-or-equal /// - public bool IsBVSGE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGEQ; } } + public bool IsBVSGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGEQ; } } /// /// Indicates whether the term is an unsigned bit-vector less-than /// - public bool IsBVULT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULT; } } + public bool IsBVULT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULT; } } /// /// Indicates whether the term is a signed bit-vector less-than /// - public bool IsBVSLT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLT; } } + public bool IsBVSLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLT; } } /// /// Indicates whether the term is an unsigned bit-vector greater-than /// - public bool IsBVUGT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGT; } } + public bool IsBVUGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGT; } } /// /// Indicates whether the term is a signed bit-vector greater-than /// - public bool IsBVSGT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGT; } } + public bool IsBVSGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGT; } } /// /// Indicates whether the term is a bit-wise AND /// - public bool IsBVAND { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BAND; } } + public bool IsBVAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BAND; } } /// /// Indicates whether the term is a bit-wise OR /// - public bool IsBVOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BOR; } } + public bool IsBVOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BOR; } } /// /// Indicates whether the term is a bit-wise NOT /// - public bool IsBVNOT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOT; } } + public bool IsBVNOT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOT; } } /// /// Indicates whether the term is a bit-wise XOR /// - public bool IsBVXOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXOR; } } + public bool IsBVXOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXOR; } } /// /// Indicates whether the term is a bit-wise NAND /// - public bool IsBVNAND { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNAND; } } + public bool IsBVNAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNAND; } } /// /// Indicates whether the term is a bit-wise NOR /// - public bool IsBVNOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOR; } } + public bool IsBVNOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOR; } } /// /// Indicates whether the term is a bit-wise XNOR /// - public bool IsBVXNOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXNOR; } } + public bool IsBVXNOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXNOR; } } /// /// Indicates whether the term is a bit-vector concatenation (binary) /// - public bool IsBVConcat { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONCAT; } } + public bool IsBVConcat { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONCAT; } } /// /// Indicates whether the term is a bit-vector sign extension /// - public bool IsBVSignExtension { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SIGN_EXT; } } + public bool IsBVSignExtension { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SIGN_EXT; } } /// /// Indicates whether the term is a bit-vector zero extension /// - public bool IsBVZeroExtension { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ZERO_EXT; } } + public bool IsBVZeroExtension { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ZERO_EXT; } } /// /// Indicates whether the term is a bit-vector extraction /// - public bool IsBVExtract { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXTRACT; } } + public bool IsBVExtract { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXTRACT; } } /// /// Indicates whether the term is a bit-vector repetition /// - public bool IsBVRepeat { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REPEAT; } } + public bool IsBVRepeat { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REPEAT; } } /// /// Indicates whether the term is a bit-vector reduce OR /// - public bool IsBVReduceOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDOR; } } + public bool IsBVReduceOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDOR; } } /// /// Indicates whether the term is a bit-vector reduce AND /// - public bool IsBVReduceAND { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDAND; } } + public bool IsBVReduceAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDAND; } } /// /// Indicates whether the term is a bit-vector comparison /// - public bool IsBVComp { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BCOMP; } } + public bool IsBVComp { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BCOMP; } } /// /// Indicates whether the term is a bit-vector shift left /// - public bool IsBVShiftLeft { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSHL; } } + public bool IsBVShiftLeft { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSHL; } } /// /// Indicates whether the term is a bit-vector logical shift right /// - public bool IsBVShiftRightLogical { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BLSHR; } } + public bool IsBVShiftRightLogical { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BLSHR; } } /// /// Indicates whether the term is a bit-vector arithmetic shift left /// - public bool IsBVShiftRightArithmetic { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BASHR; } } + public bool IsBVShiftRightArithmetic { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BASHR; } } /// /// Indicates whether the term is a bit-vector rotate left /// - public bool IsBVRotateLeft { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } } + public bool IsBVRotateLeft { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } } /// /// Indicates whether the term is a bit-vector rotate right /// - public bool IsBVRotateRight { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } } + public bool IsBVRotateRight { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } } /// /// Indicates whether the term is a bit-vector rotate left (extended) /// /// Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator instead of a parametric one. - public bool IsBVRotateLeftExtended { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } } + public bool IsBVRotateLeftExtended { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } } /// /// Indicates whether the term is a bit-vector rotate right (extended) /// /// Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator instead of a parametric one. - public bool IsBVRotateRightExtended { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } } + public bool IsBVRotateRightExtended { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } } /// /// Indicates whether the term is a coercion from integer to bit-vector /// /// This function is not supported by the decision procedures. Only the most /// rudimentary simplification rules are applied to this function. - public bool IsIntToBV { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INT2BV; } } + public bool IsIntToBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INT2BV; } } /// /// Indicates whether the term is a coercion from bit-vector to integer /// /// This function is not supported by the decision procedures. Only the most /// rudimentary simplification rules are applied to this function. - public bool IsBVToInt { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BV2INT; } } + public bool IsBVToInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BV2INT; } } /// /// Indicates whether the term is a bit-vector carry /// /// Compute the carry bit in a full-adder. The meaning is given by the /// equivalence (carry l1 l2 l3) <=> (or (and l1 l2) (and l1 l3) (and l2 l3))) - public bool IsBVCarry { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CARRY; } } + public bool IsBVCarry { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CARRY; } } /// /// Indicates whether the term is a bit-vector ternary XOR /// /// The meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor l1 l2) l3) - public bool IsBVXOR3 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR3; } } + public bool IsBVXOR3 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR3; } } #endregion @@ -784,13 +784,13 @@ public bool IsBV /// Indicates whether the term is a label (used by the Boogie Verification condition generator). /// /// The label has two parameters, a string and a Boolean polarity. It takes one argument, a formula. - public bool IsLabel { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL; } } + public bool IsLabel { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL; } } /// /// Indicates whether the term is a label literal (used by the Boogie Verification condition generator). /// /// A label literal has a set of string parameters. It takes no arguments. - public bool IsLabelLit { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL_LIT; } } + public bool IsLabelLit { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL_LIT; } } #endregion #region Proof Terms @@ -799,22 +799,22 @@ public bool IsBV /// /// This binary predicate is used in proof terms. /// It captures equisatisfiability and equivalence modulo renamings. - public bool IsOEQ { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OEQ; } } + public bool IsOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OEQ; } } /// /// Indicates whether the term is a Proof for the expression 'true'. /// - public bool IsProofTrue { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRUE; } } + public bool IsProofTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRUE; } } /// /// Indicates whether the term is a proof for a fact asserted by the user. /// - public bool IsProofAsserted { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ASSERTED; } } + public bool IsProofAsserted { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ASSERTED; } } /// /// Indicates whether the term is a proof for a fact (tagged as goal) asserted by the user. /// - public bool IsProofGoal { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_GOAL; } } + public bool IsProofGoal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_GOAL; } } /// /// Indicates whether the term is proof via modus ponens @@ -825,7 +825,7 @@ public bool IsBV /// T2: (implies p q) /// [mp T1 T2]: q /// The second antecedents may also be a proof for (iff p q). - public bool IsProofModusPonens { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } } + public bool IsProofModusPonens { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } } /// /// Indicates whether the term is a proof for (R t t), where R is a reflexive relation. @@ -834,7 +834,7 @@ public bool IsBV /// The only reflexive relations that are used are /// equivalence modulo namings, equality and equivalence. /// That is, R is either '~', '=' or 'iff'. - public bool IsProofReflexivity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } } + public bool IsProofReflexivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } } /// /// Indicates whether the term is proof by symmetricity of a relation @@ -845,7 +845,7 @@ public bool IsBV /// [symmetry T1]: (R s t) /// T1 is the antecedent of this proof object. /// - public bool IsProofSymmetry { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } } + public bool IsProofSymmetry { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } } /// /// Indicates whether the term is a proof by transitivity of a relation @@ -857,7 +857,7 @@ public bool IsBV /// T2: (R s u) /// [trans T1 T2]: (R t u) /// - public bool IsProofTransitivity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } } + public bool IsProofTransitivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } } /// /// Indicates whether the term is a proof by condensed transitivity of a relation @@ -878,7 +878,7 @@ public bool IsBV /// if there is a path from s to t, if we view every /// antecedent (R a b) as an edge between a and b. /// - public bool IsProofTransitivityStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } } + public bool IsProofTransitivityStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } } /// @@ -892,7 +892,7 @@ public bool IsBV /// Remark: if t_i == s_i, then the antecedent Ti is suppressed. /// That is, reflexivity proofs are supressed to save space. /// - public bool IsProofMonotonicity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } } + public bool IsProofMonotonicity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } } /// /// Indicates whether the term is a quant-intro proof @@ -902,7 +902,7 @@ public bool IsBV /// T1: (~ p q) /// [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) /// - public bool IsProofQuantIntro { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } } + public bool IsProofQuantIntro { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } } /// /// Indicates whether the term is a distributivity proof object. @@ -920,7 +920,7 @@ public bool IsBV /// Remark. This rule is used by the CNF conversion pass and /// instantiated by f = or, and g = and. /// - public bool IsProofDistributivity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } } + public bool IsProofDistributivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } } /// /// Indicates whether the term is a proof by elimination of AND @@ -930,7 +930,7 @@ public bool IsBV /// T1: (and l_1 ... l_n) /// [and-elim T1]: l_i /// - public bool IsProofAndElimination { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } } + public bool IsProofAndElimination { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } } /// /// Indicates whether the term is a proof by eliminiation of not-or @@ -940,7 +940,7 @@ public bool IsBV /// T1: (not (or l_1 ... l_n)) /// [not-or-elim T1]: (not l_i) /// - public bool IsProofOrElimination { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } } + public bool IsProofOrElimination { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } } /// /// Indicates whether the term is a proof by rewriting @@ -959,7 +959,7 @@ public bool IsBV /// (= (+ x 1 2) (+ 3 x)) /// (iff (or x false) x) /// - public bool IsProofRewrite { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE; } } + public bool IsProofRewrite { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE; } } /// /// Indicates whether the term is a proof by rewriting @@ -975,7 +975,7 @@ public bool IsBV /// - When converting bit-vectors to Booleans (BIT2BOOL=true) /// - When pulling ite expression up (PULL_CHEAP_ITE_TREES=true) /// - public bool IsProofRewriteStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } } + public bool IsProofRewriteStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } } /// /// Indicates whether the term is a proof for pulling quantifiers out. @@ -983,7 +983,7 @@ public bool IsBV /// /// A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. /// - public bool IsProofPullQuant { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } } + public bool IsProofPullQuant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } } /// /// Indicates whether the term is a proof for pulling quantifiers out. @@ -993,7 +993,7 @@ public bool IsBV /// This proof object is only used if the parameter PROOF_MODE is 1. /// This proof object has no antecedents /// - public bool IsProofPullQuantStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; } } + public bool IsProofPullQuantStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; } } /// /// Indicates whether the term is a proof for pushing quantifiers in. @@ -1006,7 +1006,7 @@ public bool IsBV /// (forall (x_1 ... x_m) p_n[x_1 ... x_m]))) /// This proof object has no antecedents /// - public bool IsProofPushQuant { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } } + public bool IsProofPushQuant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } } /// /// Indicates whether the term is a proof for elimination of unused variables. @@ -1018,7 +1018,7 @@ public bool IsBV /// It is used to justify the elimination of unused variables. /// This proof object has no antecedents. /// - public bool IsProofElimUnusedVars { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } } + public bool IsProofElimUnusedVars { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } } /// /// Indicates whether the term is a proof for destructive equality resolution @@ -1032,7 +1032,7 @@ public bool IsBV /// /// Several variables can be eliminated simultaneously. /// - public bool IsProofDER { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DER; } } + public bool IsProofDER { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DER; } } /// /// Indicates whether the term is a proof for quantifier instantiation @@ -1040,13 +1040,13 @@ public bool IsBV /// /// A proof of (or (not (forall (x) (P x))) (P a)) /// - public bool IsProofQuantInst { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } } + public bool IsProofQuantInst { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } } /// /// Indicates whether the term is a hypthesis marker. /// /// Mark a hypothesis in a natural deduction style proof. - public bool IsProofHypothesis { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } } + public bool IsProofHypothesis { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } } /// /// Indicates whether the term is a proof by lemma @@ -1059,7 +1059,7 @@ public bool IsBV /// It converts the proof in a proof for (or (not l_1) ... (not l_n)), /// when T1 contains the hypotheses: l_1, ..., l_n. /// - public bool IsProofLemma { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_LEMMA; } } + public bool IsProofLemma { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_LEMMA; } } /// /// Indicates whether the term is a proof by unit resolution @@ -1071,7 +1071,7 @@ public bool IsBV /// T(n+1): (not l_n) /// [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') /// - public bool IsProofUnitResolution { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } } + public bool IsProofUnitResolution { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } } /// /// Indicates whether the term is a proof by iff-true @@ -1080,7 +1080,7 @@ public bool IsBV /// T1: p /// [iff-true T1]: (iff p true) /// - public bool IsProofIFFTrue { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } } + public bool IsProofIFFTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } } /// /// Indicates whether the term is a proof by iff-false @@ -1089,7 +1089,7 @@ public bool IsBV /// T1: (not p) /// [iff-false T1]: (iff p false) /// - public bool IsProofIFFFalse { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } } + public bool IsProofIFFFalse { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } } /// /// Indicates whether the term is a proof by commutativity @@ -1102,7 +1102,7 @@ public bool IsBV /// This proof object has no antecedents. /// Remark: if f is bool, then = is iff. /// - public bool IsProofCommutativity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } } + public bool IsProofCommutativity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } } /// /// Indicates whether the term is a proof for Tseitin-like axioms @@ -1138,7 +1138,7 @@ public bool IsBV /// unfolding the Boolean connectives in the axioms a small /// bounded number of steps (=3). /// - public bool IsProofDefAxiom { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } } + public bool IsProofDefAxiom { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } } /// /// Indicates whether the term is a proof for introduction of a name @@ -1161,7 +1161,7 @@ public bool IsBV /// Otherwise: /// [def-intro]: (= n e) /// - public bool IsProofDefIntro { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } } + public bool IsProofDefIntro { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } } /// /// Indicates whether the term is a proof for application of a definition @@ -1171,7 +1171,7 @@ public bool IsBV /// F is 'equivalent' to n, given that T1 is a proof that /// n is a name for F. /// - public bool IsProofApplyDef { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } } + public bool IsProofApplyDef { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } } /// /// Indicates whether the term is a proof iff-oeq @@ -1180,7 +1180,7 @@ public bool IsBV /// T1: (iff p q) /// [iff~ T1]: (~ p q) /// - public bool IsProofIFFOEQ { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } } + public bool IsProofIFFOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } } /// /// Indicates whether the term is a proof for a positive NNF step @@ -1208,7 +1208,7 @@ public bool IsBV /// NNF_NEG furthermore handles the case where negation is pushed /// over Boolean connectives 'and' and 'or'. /// - public bool IsProofNNFPos { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_POS; } } + public bool IsProofNNFPos { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_POS; } } /// /// Indicates whether the term is a proof for a negative NNF step @@ -1233,7 +1233,7 @@ public bool IsBV /// [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) /// (and (or r_1 r_2) (or r_1' r_2'))) /// - public bool IsProofNNFNeg { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } } + public bool IsProofNNFNeg { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } } /// /// Indicates whether the term is a proof for (~ P Q) here Q is in negation normal form. @@ -1245,7 +1245,7 @@ public bool IsBV /// /// This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. /// - public bool IsProofNNFStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_STAR; } } + public bool IsProofNNFStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_STAR; } } /// /// Indicates whether the term is a proof for (~ P Q) where Q is in conjunctive normal form. @@ -1255,7 +1255,7 @@ public bool IsBV /// This proof object is only used if the parameter PROOF_MODE is 1. /// This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. /// - public bool IsProofCNFStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_CNF_STAR; } } + public bool IsProofCNFStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_CNF_STAR; } } /// /// Indicates whether the term is a proof for a Skolemization step @@ -1268,7 +1268,7 @@ public bool IsBV /// /// This proof object has no antecedents. /// - public bool IsProofSkolemize { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } } + public bool IsProofSkolemize { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } } /// /// Indicates whether the term is a proof by modus ponens for equi-satisfiability. @@ -1279,7 +1279,7 @@ public bool IsBV /// T2: (~ p q) /// [mp~ T1 T2]: q /// - public bool IsProofModusPonensOEQ { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } } + public bool IsProofModusPonensOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } } /// /// Indicates whether the term is a proof for theory lemma @@ -1298,7 +1298,7 @@ public bool IsBV /// (iff (= t1 t2) (and (<= t1 t2) (<= t2 t1))) /// - gcd-test - Indicates an integer linear arithmetic lemma that uses a gcd test. /// - public bool IsProofTheoryLemma { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } } + public bool IsProofTheoryLemma { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } } #endregion #region Relational Terms @@ -1323,40 +1323,40 @@ public bool IsRelation /// The function takes n+1 arguments, where the first argument is the relation and the remaining n elements /// correspond to the n columns of the relation. /// - public bool IsRelationStore { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_STORE; } } + public bool IsRelationStore { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_STORE; } } /// /// Indicates whether the term is an empty relation /// - public bool IsEmptyRelation { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_EMPTY; } } + public bool IsEmptyRelation { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_EMPTY; } } /// /// Indicates whether the term is a test for the emptiness of a relation /// - public bool IsIsEmptyRelation { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } } + public bool IsIsEmptyRelation { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } } /// /// Indicates whether the term is a relational join /// - public bool IsRelationalJoin { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_JOIN; } } + public bool IsRelationalJoin { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_JOIN; } } /// /// Indicates whether the term is the union or convex hull of two relations. /// /// The function takes two arguments. - public bool IsRelationUnion { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_UNION; } } + public bool IsRelationUnion { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_UNION; } } /// /// Indicates whether the term is the widening of two relations /// /// The function takes two arguments. - public bool IsRelationWiden { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_WIDEN; } } + public bool IsRelationWiden { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_WIDEN; } } /// /// Indicates whether the term is a projection of columns (provided as numbers in the parameters). /// /// The function takes one argument. - public bool IsRelationProject { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_PROJECT; } } + public bool IsRelationProject { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_PROJECT; } } /// /// Indicates whether the term is a relation filter @@ -1368,7 +1368,7 @@ public bool IsRelation /// corresponding to the columns of the relation. /// So the first column in the relation has index 0. /// - public bool IsRelationFilter { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_FILTER; } } + public bool IsRelationFilter { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_FILTER; } } /// /// Indicates whether the term is an intersection of a relation with the negation of another. @@ -1384,7 +1384,7 @@ public bool IsRelation /// target are elements in x in pos, such that there is no y in neg that agrees with /// x on the columns c1, d1, .., cN, dN. /// - public bool IsRelationNegationFilter { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } } + public bool IsRelationNegationFilter { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } } /// /// Indicates whether the term is the renaming of a column in a relation @@ -1393,12 +1393,12 @@ public bool IsRelation /// The function takes one argument. /// The parameters contain the renaming as a cycle. /// - public bool IsRelationRename { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_RENAME; } } + public bool IsRelationRename { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_RENAME; } } /// /// Indicates whether the term is the complement of a relation /// - public bool IsRelationComplement { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } } + public bool IsRelationComplement { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } } /// /// Indicates whether the term is a relational select @@ -1408,7 +1408,7 @@ public bool IsRelation /// The function takes n+1 arguments, where the first argument is a relation, /// and the remaining n arguments correspond to a record. /// - public bool IsRelationSelect { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_SELECT; } } + public bool IsRelationSelect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_SELECT; } } /// /// Indicates whether the term is a relational clone (copy) @@ -1420,7 +1420,7 @@ public bool IsRelation /// for terms of kind /// to perform destructive updates to the first argument. /// - public bool IsRelationClone { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_CLONE; } } + public bool IsRelationClone { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_CLONE; } } #endregion #region Finite domain terms @@ -1439,7 +1439,7 @@ public bool IsFiniteDomain /// /// Indicates whether the term is a less than predicate over a finite domain. /// - public bool IsFiniteDomainLT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FD_LT; } } + public bool IsFiniteDomainLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FD_LT; } } #endregion #endregion diff --git a/src/api/dotnet/Global.cs b/src/api/dotnet/Global.cs index 707264c828a..787c9b78890 100644 --- a/src/api/dotnet/Global.cs +++ b/src/api/dotnet/Global.cs @@ -43,7 +43,7 @@ public static class Global /// The parameter names are case-insensitive. The character '-' should be viewed as an "alias" for '_'. /// Thus, the following parameter names are considered equivalent: "pp.decimal-precision" and "PP.DECIMAL_PRECISION". /// This function can be used to set parameters for a specific Z3 module. - /// This can be done by using .. + /// This can be done by using [module-name].[parameter-name]. /// For example: /// Z3_global_param_set('pp.decimal', 'true') /// will set the parameter "decimal" in the module "pp" to true. diff --git a/src/api/dotnet/Microsoft.Z3.csproj b/src/api/dotnet/Microsoft.Z3.csproj index 9eb9eb660da..0a062054dc5 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj +++ b/src/api/dotnet/Microsoft.Z3.csproj @@ -24,8 +24,7 @@ prompt 4 true - - + ..\Debug\Microsoft.Z3.XML False False True @@ -140,6 +139,7 @@ Full %28none%29 0 + ..\x64\Debug\Microsoft.Z3.XML ..\x64\external_64\ @@ -193,7 +193,7 @@ ..\x64\external\ true - ..\external\Microsoft.Z3.xml + ..\x64\external\Microsoft.Z3.XML true pdbonly x64 @@ -220,7 +220,7 @@ ..\Release_delaysign\ true - ..\Release_delaysign\Microsoft.Z3.xml + ..\Release_delaysign\Microsoft.Z3.XML true pdbonly AnyCPU @@ -238,7 +238,7 @@ bin\x64\Release_delaysign\ true - ..\x64\external_64\Microsoft.Z3.xml + bin\x64\Release_delaysign\Microsoft.Z3.XML true pdbonly x64 @@ -266,11 +266,12 @@ MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + bin\x86\Debug\Microsoft.Z3.XML bin\x86\Release\ true - ..\external\Microsoft.Z3.xml + bin\x86\Release\Microsoft.Z3.xml true pdbonly x86 @@ -285,7 +286,7 @@ bin\x86\external\ true - ..\external\Microsoft.Z3.xml + bin\x86\external\Microsoft.Z3.XML true pdbonly x86 @@ -303,7 +304,7 @@ bin\x86\Release_delaysign\ DELAYSIGN true - ..\Release_delaysign\Microsoft.Z3.xml + bin\x86\Release_delaysign\Microsoft.Z3.XML true pdbonly x86 @@ -399,4 +400,4 @@ --> - + \ No newline at end of file diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index 555a2466fac..9de515e8629 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -132,7 +132,8 @@ public void Add(params BoolExpr[] constraints) /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination - /// of the Boolean variables provided using and the Boolean literals + /// of the Boolean variables provided using + /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(BoolExpr[] constraints, BoolExpr[] ps) @@ -156,7 +157,8 @@ public void AssertAndTrack(BoolExpr[] constraints, BoolExpr[] ps) /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination - /// of the Boolean variables provided using and the Boolean literals + /// of the Boolean variables provided using + /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(BoolExpr constraint, BoolExpr p) diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java index 7793a16e5e7..f7edc6a2b14 100644 --- a/src/api/java/Expr.java +++ b/src/api/java/Expr.java @@ -94,7 +94,7 @@ public Expr[] getArgs() throws Z3Exception public void update(Expr[] args) throws Z3Exception { getContext().checkContextMatch(args); - if (args.length != getNumArgs()) + if (isApp() && args.length != getNumArgs()) throw new Z3Exception("Number of arguments does not match"); setNativeObject(Native.updateTerm(getContext().nCtx(), getNativeObject(), (int) args.length, Expr.arrayToNative(args))); @@ -245,7 +245,7 @@ public boolean isBool() throws Z3Exception **/ public boolean isTrue() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TRUE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TRUE; } /** @@ -253,7 +253,7 @@ public boolean isTrue() throws Z3Exception **/ public boolean isFalse() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FALSE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FALSE; } /** @@ -261,7 +261,7 @@ public boolean isFalse() throws Z3Exception **/ public boolean isEq() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EQ; } /** @@ -270,7 +270,7 @@ public boolean isEq() throws Z3Exception **/ public boolean isDistinct() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DISTINCT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DISTINCT; } /** @@ -278,7 +278,7 @@ public boolean isDistinct() throws Z3Exception **/ public boolean isITE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ITE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ITE; } /** @@ -286,7 +286,7 @@ public boolean isITE() throws Z3Exception **/ public boolean isAnd() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AND; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AND; } /** @@ -294,7 +294,7 @@ public boolean isAnd() throws Z3Exception **/ public boolean isOr() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OR; } /** @@ -303,7 +303,7 @@ public boolean isOr() throws Z3Exception **/ public boolean isIff() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IFF; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IFF; } /** @@ -311,7 +311,7 @@ public boolean isIff() throws Z3Exception **/ public boolean isXor() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR; } /** @@ -319,7 +319,7 @@ public boolean isXor() throws Z3Exception **/ public boolean isNot() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_NOT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_NOT; } /** @@ -327,7 +327,7 @@ public boolean isNot() throws Z3Exception **/ public boolean isImplies() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IMPLIES; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IMPLIES; } /** @@ -356,7 +356,7 @@ public boolean isReal() throws Z3Exception **/ public boolean isArithmeticNumeral() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ANUM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ANUM; } /** @@ -364,7 +364,7 @@ public boolean isArithmeticNumeral() throws Z3Exception **/ public boolean isLE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LE; } /** @@ -372,7 +372,7 @@ public boolean isLE() throws Z3Exception **/ public boolean isGE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GE; } /** @@ -380,7 +380,7 @@ public boolean isGE() throws Z3Exception **/ public boolean isLT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LT; } /** @@ -388,7 +388,7 @@ public boolean isLT() throws Z3Exception **/ public boolean isGT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GT; } /** @@ -396,7 +396,7 @@ public boolean isGT() throws Z3Exception **/ public boolean isAdd() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ADD; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ADD; } /** @@ -404,7 +404,7 @@ public boolean isAdd() throws Z3Exception **/ public boolean isSub() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SUB; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SUB; } /** @@ -412,7 +412,7 @@ public boolean isSub() throws Z3Exception **/ public boolean isUMinus() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UMINUS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UMINUS; } /** @@ -420,7 +420,7 @@ public boolean isUMinus() throws Z3Exception **/ public boolean isMul() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MUL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MUL; } /** @@ -428,7 +428,7 @@ public boolean isMul() throws Z3Exception **/ public boolean isDiv() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DIV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DIV; } /** @@ -436,7 +436,7 @@ public boolean isDiv() throws Z3Exception **/ public boolean isIDiv() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IDIV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IDIV; } /** @@ -444,7 +444,7 @@ public boolean isIDiv() throws Z3Exception **/ public boolean isRemainder() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REM; } /** @@ -452,7 +452,7 @@ public boolean isRemainder() throws Z3Exception **/ public boolean isModulus() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MOD; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MOD; } /** @@ -460,7 +460,7 @@ public boolean isModulus() throws Z3Exception **/ public boolean isIntToReal() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_REAL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_REAL; } /** @@ -468,7 +468,7 @@ public boolean isIntToReal() throws Z3Exception **/ public boolean isRealToInt() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_INT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_INT; } /** @@ -477,7 +477,7 @@ public boolean isRealToInt() throws Z3Exception **/ public boolean isRealIsInt() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IS_INT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IS_INT; } /** @@ -497,7 +497,7 @@ public boolean isArray() throws Z3Exception **/ public boolean isStore() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_STORE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_STORE; } /** @@ -505,7 +505,7 @@ public boolean isStore() throws Z3Exception **/ public boolean isSelect() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SELECT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SELECT; } /** @@ -515,7 +515,7 @@ public boolean isSelect() throws Z3Exception **/ public boolean isConstantArray() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONST_ARRAY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONST_ARRAY; } /** @@ -524,7 +524,7 @@ public boolean isConstantArray() throws Z3Exception **/ public boolean isDefaultArray() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } /** @@ -533,7 +533,7 @@ public boolean isDefaultArray() throws Z3Exception **/ public boolean isArrayMap() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_MAP; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_MAP; } /** @@ -543,7 +543,7 @@ public boolean isArrayMap() throws Z3Exception **/ public boolean isAsArray() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AS_ARRAY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AS_ARRAY; } /** @@ -551,7 +551,7 @@ public boolean isAsArray() throws Z3Exception **/ public boolean isSetUnion() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_UNION; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_UNION; } /** @@ -559,7 +559,7 @@ public boolean isSetUnion() throws Z3Exception **/ public boolean isSetIntersect() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_INTERSECT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_INTERSECT; } /** @@ -567,7 +567,7 @@ public boolean isSetIntersect() throws Z3Exception **/ public boolean isSetDifference() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } /** @@ -575,7 +575,7 @@ public boolean isSetDifference() throws Z3Exception **/ public boolean isSetComplement() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } /** @@ -583,7 +583,7 @@ public boolean isSetComplement() throws Z3Exception **/ public boolean isSetSubset() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_SUBSET; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_SUBSET; } /** @@ -601,7 +601,7 @@ public boolean isBV() throws Z3Exception **/ public boolean isBVNumeral() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNUM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNUM; } /** @@ -609,7 +609,7 @@ public boolean isBVNumeral() throws Z3Exception **/ public boolean isBVBitOne() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT1; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT1; } /** @@ -617,7 +617,7 @@ public boolean isBVBitOne() throws Z3Exception **/ public boolean isBVBitZero() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT0; } /** @@ -625,7 +625,7 @@ public boolean isBVBitZero() throws Z3Exception **/ public boolean isBVUMinus() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNEG; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNEG; } /** @@ -633,7 +633,7 @@ public boolean isBVUMinus() throws Z3Exception **/ public boolean isBVAdd() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BADD; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BADD; } /** @@ -641,7 +641,7 @@ public boolean isBVAdd() throws Z3Exception **/ public boolean isBVSub() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSUB; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSUB; } /** @@ -649,7 +649,7 @@ public boolean isBVSub() throws Z3Exception **/ public boolean isBVMul() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BMUL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BMUL; } /** @@ -657,7 +657,7 @@ public boolean isBVMul() throws Z3Exception **/ public boolean isBVSDiv() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV; } /** @@ -665,7 +665,7 @@ public boolean isBVSDiv() throws Z3Exception **/ public boolean isBVUDiv() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV; } /** @@ -673,7 +673,7 @@ public boolean isBVUDiv() throws Z3Exception **/ public boolean isBVSRem() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM; } /** @@ -681,7 +681,7 @@ public boolean isBVSRem() throws Z3Exception **/ public boolean isBVURem() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM; } /** @@ -689,7 +689,7 @@ public boolean isBVURem() throws Z3Exception **/ public boolean isBVSMod() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD; } /** @@ -697,7 +697,7 @@ public boolean isBVSMod() throws Z3Exception **/ boolean IsBVSDiv0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV0; } /** @@ -705,7 +705,7 @@ boolean IsBVSDiv0() throws Z3Exception **/ boolean IsBVUDiv0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV0; } /** @@ -713,7 +713,7 @@ boolean IsBVUDiv0() throws Z3Exception **/ boolean IsBVSRem0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM0; } /** @@ -721,7 +721,7 @@ boolean IsBVSRem0() throws Z3Exception **/ boolean IsBVURem0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM0; } /** @@ -729,7 +729,7 @@ boolean IsBVURem0() throws Z3Exception **/ boolean IsBVSMod0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD0; } /** @@ -737,7 +737,7 @@ boolean IsBVSMod0() throws Z3Exception **/ public boolean isBVULE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULEQ; } /** @@ -745,7 +745,7 @@ public boolean isBVULE() throws Z3Exception **/ public boolean isBVSLE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLEQ; } /** @@ -754,7 +754,7 @@ public boolean isBVSLE() throws Z3Exception **/ public boolean isBVUGE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGEQ; } /** @@ -762,7 +762,7 @@ public boolean isBVUGE() throws Z3Exception **/ public boolean isBVSGE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGEQ; } /** @@ -770,7 +770,7 @@ public boolean isBVSGE() throws Z3Exception **/ public boolean isBVULT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULT; } /** @@ -778,7 +778,7 @@ public boolean isBVULT() throws Z3Exception **/ public boolean isBVSLT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLT; } /** @@ -786,7 +786,7 @@ public boolean isBVSLT() throws Z3Exception **/ public boolean isBVUGT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGT; } /** @@ -794,7 +794,7 @@ public boolean isBVUGT() throws Z3Exception **/ public boolean isBVSGT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGT; } /** @@ -802,7 +802,7 @@ public boolean isBVSGT() throws Z3Exception **/ public boolean isBVAND() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BAND; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BAND; } /** @@ -810,7 +810,7 @@ public boolean isBVAND() throws Z3Exception **/ public boolean isBVOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BOR; } /** @@ -818,7 +818,7 @@ public boolean isBVOR() throws Z3Exception **/ public boolean isBVNOT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOT; } /** @@ -826,7 +826,7 @@ public boolean isBVNOT() throws Z3Exception **/ public boolean isBVXOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXOR; } /** @@ -834,7 +834,7 @@ public boolean isBVXOR() throws Z3Exception **/ public boolean isBVNAND() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNAND; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNAND; } /** @@ -842,7 +842,7 @@ public boolean isBVNAND() throws Z3Exception **/ public boolean isBVNOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOR; } /** @@ -850,7 +850,7 @@ public boolean isBVNOR() throws Z3Exception **/ public boolean isBVXNOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXNOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXNOR; } /** @@ -858,7 +858,7 @@ public boolean isBVXNOR() throws Z3Exception **/ public boolean isBVConcat() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONCAT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONCAT; } /** @@ -866,7 +866,7 @@ public boolean isBVConcat() throws Z3Exception **/ public boolean isBVSignExtension() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SIGN_EXT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SIGN_EXT; } /** @@ -874,7 +874,7 @@ public boolean isBVSignExtension() throws Z3Exception **/ public boolean isBVZeroExtension() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ZERO_EXT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ZERO_EXT; } /** @@ -882,7 +882,7 @@ public boolean isBVZeroExtension() throws Z3Exception **/ public boolean isBVExtract() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXTRACT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXTRACT; } /** @@ -890,7 +890,7 @@ public boolean isBVExtract() throws Z3Exception **/ public boolean isBVRepeat() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REPEAT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REPEAT; } /** @@ -898,7 +898,7 @@ public boolean isBVRepeat() throws Z3Exception **/ public boolean isBVReduceOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDOR; } /** @@ -906,7 +906,7 @@ public boolean isBVReduceOR() throws Z3Exception **/ public boolean isBVReduceAND() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDAND; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDAND; } /** @@ -914,7 +914,7 @@ public boolean isBVReduceAND() throws Z3Exception **/ public boolean isBVComp() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BCOMP; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BCOMP; } /** @@ -922,7 +922,7 @@ public boolean isBVComp() throws Z3Exception **/ public boolean isBVShiftLeft() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSHL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSHL; } /** @@ -930,7 +930,7 @@ public boolean isBVShiftLeft() throws Z3Exception **/ public boolean isBVShiftRightLogical() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BLSHR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BLSHR; } /** @@ -938,7 +938,7 @@ public boolean isBVShiftRightLogical() throws Z3Exception **/ public boolean isBVShiftRightArithmetic() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BASHR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BASHR; } /** @@ -946,7 +946,7 @@ public boolean isBVShiftRightArithmetic() throws Z3Exception **/ public boolean isBVRotateLeft() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_LEFT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } /** @@ -954,7 +954,7 @@ public boolean isBVRotateLeft() throws Z3Exception **/ public boolean isBVRotateRight() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } /** @@ -964,7 +964,7 @@ public boolean isBVRotateRight() throws Z3Exception **/ public boolean isBVRotateLeftExtended() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } /** @@ -974,7 +974,7 @@ public boolean isBVRotateLeftExtended() throws Z3Exception **/ public boolean isBVRotateRightExtended() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } /** @@ -985,7 +985,7 @@ public boolean isBVRotateRightExtended() throws Z3Exception **/ public boolean isIntToBV() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_INT2BV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_INT2BV; } /** @@ -996,7 +996,7 @@ public boolean isIntToBV() throws Z3Exception **/ public boolean isBVToInt() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BV2INT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BV2INT; } /** @@ -1006,7 +1006,7 @@ public boolean isBVToInt() throws Z3Exception **/ public boolean isBVCarry() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CARRY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CARRY; } /** @@ -1016,7 +1016,7 @@ public boolean isBVCarry() throws Z3Exception **/ public boolean isBVXOR3() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR3; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR3; } /** @@ -1026,7 +1026,7 @@ public boolean isBVXOR3() throws Z3Exception **/ public boolean isLabel() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL; } /** @@ -1036,7 +1036,7 @@ public boolean isLabel() throws Z3Exception **/ public boolean isLabelLit() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL_LIT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL_LIT; } /** @@ -1046,7 +1046,7 @@ public boolean isLabelLit() throws Z3Exception **/ public boolean isOEQ() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OEQ; } /** @@ -1054,7 +1054,7 @@ public boolean isOEQ() throws Z3Exception **/ public boolean isProofTrue() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRUE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRUE; } /** @@ -1062,7 +1062,7 @@ public boolean isProofTrue() throws Z3Exception **/ public boolean isProofAsserted() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ASSERTED; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ASSERTED; } /** @@ -1071,7 +1071,7 @@ public boolean isProofAsserted() throws Z3Exception **/ public boolean isProofGoal() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_GOAL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_GOAL; } /** @@ -1082,7 +1082,7 @@ public boolean isProofGoal() throws Z3Exception **/ public boolean isProofModusPonens() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } /** @@ -1094,7 +1094,7 @@ public boolean isProofModusPonens() throws Z3Exception **/ public boolean isProofReflexivity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } /** @@ -1105,7 +1105,7 @@ public boolean isProofReflexivity() throws Z3Exception **/ public boolean isProofSymmetry() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SYMMETRY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } /** @@ -1116,7 +1116,7 @@ public boolean isProofSymmetry() throws Z3Exception **/ public boolean isProofTransitivity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } /** @@ -1134,7 +1134,7 @@ public boolean isProofTransitivity() throws Z3Exception **/ public boolean isProofTransitivityStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } /** @@ -1146,7 +1146,7 @@ public boolean isProofTransitivityStar() throws Z3Exception **/ public boolean isProofMonotonicity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } /** @@ -1156,7 +1156,7 @@ public boolean isProofMonotonicity() throws Z3Exception **/ public boolean isProofQuantIntro() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } /** @@ -1172,7 +1172,7 @@ public boolean isProofQuantIntro() throws Z3Exception **/ public boolean isProofDistributivity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } /** @@ -1182,7 +1182,7 @@ public boolean isProofDistributivity() throws Z3Exception **/ public boolean isProofAndElimination() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_AND_ELIM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } /** @@ -1192,7 +1192,7 @@ public boolean isProofAndElimination() throws Z3Exception **/ public boolean isProofOrElimination() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } /** @@ -1209,7 +1209,7 @@ public boolean isProofOrElimination() throws Z3Exception **/ public boolean isProofRewrite() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE; } /** @@ -1225,7 +1225,7 @@ public boolean isProofRewrite() throws Z3Exception **/ public boolean isProofRewriteStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } /** @@ -1235,7 +1235,7 @@ public boolean isProofRewriteStar() throws Z3Exception **/ public boolean isProofPullQuant() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } /** @@ -1246,7 +1246,7 @@ public boolean isProofPullQuant() throws Z3Exception **/ public boolean isProofPullQuantStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; } /** @@ -1258,7 +1258,7 @@ public boolean isProofPullQuantStar() throws Z3Exception **/ public boolean isProofPushQuant() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } /** @@ -1271,7 +1271,7 @@ public boolean isProofPushQuant() throws Z3Exception **/ public boolean isProofElimUnusedVars() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } /** @@ -1285,7 +1285,7 @@ public boolean isProofElimUnusedVars() throws Z3Exception **/ public boolean isProofDER() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DER; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DER; } /** @@ -1294,7 +1294,7 @@ public boolean isProofDER() throws Z3Exception **/ public boolean isProofQuantInst() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INST; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } /** @@ -1303,7 +1303,7 @@ public boolean isProofQuantInst() throws Z3Exception **/ public boolean isProofHypothesis() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } /** @@ -1316,7 +1316,7 @@ public boolean isProofHypothesis() throws Z3Exception **/ public boolean isProofLemma() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_LEMMA; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_LEMMA; } /** @@ -1326,7 +1326,7 @@ public boolean isProofLemma() throws Z3Exception **/ public boolean isProofUnitResolution() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } /** @@ -1335,7 +1335,7 @@ public boolean isProofUnitResolution() throws Z3Exception **/ public boolean isProofIFFTrue() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } /** @@ -1344,7 +1344,7 @@ public boolean isProofIFFTrue() throws Z3Exception **/ public boolean isProofIFFFalse() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } /** @@ -1358,7 +1358,7 @@ public boolean isProofIFFFalse() throws Z3Exception **/ public boolean isProofCommutativity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } /** @@ -1381,7 +1381,7 @@ public boolean isProofCommutativity() throws Z3Exception **/ public boolean isProofDefAxiom() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } /** @@ -1402,7 +1402,7 @@ public boolean isProofDefAxiom() throws Z3Exception **/ public boolean isProofDefIntro() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } /** @@ -1412,7 +1412,7 @@ public boolean isProofDefIntro() throws Z3Exception **/ public boolean isProofApplyDef() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } /** @@ -1421,7 +1421,7 @@ public boolean isProofApplyDef() throws Z3Exception **/ public boolean isProofIFFOEQ() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } /** @@ -1446,7 +1446,7 @@ public boolean isProofIFFOEQ() throws Z3Exception **/ public boolean isProofNNFPos() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_POS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_POS; } /** @@ -1462,7 +1462,7 @@ public boolean isProofNNFPos() throws Z3Exception **/ public boolean isProofNNFNeg() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_NEG; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } /** @@ -1477,7 +1477,7 @@ public boolean isProofNNFNeg() throws Z3Exception **/ public boolean isProofNNFStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_STAR; } /** @@ -1489,7 +1489,7 @@ public boolean isProofNNFStar() throws Z3Exception **/ public boolean isProofCNFStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_CNF_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_CNF_STAR; } /** @@ -1503,7 +1503,7 @@ public boolean isProofCNFStar() throws Z3Exception **/ public boolean isProofSkolemize() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } /** @@ -1513,7 +1513,7 @@ public boolean isProofSkolemize() throws Z3Exception **/ public boolean isProofModusPonensOEQ() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } /** @@ -1532,7 +1532,7 @@ public boolean isProofModusPonensOEQ() throws Z3Exception **/ public boolean isProofTheoryLemma() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } /** @@ -1554,7 +1554,7 @@ public boolean isRelation() throws Z3Exception **/ public boolean isRelationStore() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_STORE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_STORE; } /** @@ -1562,7 +1562,7 @@ public boolean isRelationStore() throws Z3Exception **/ public boolean isEmptyRelation() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_EMPTY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_EMPTY; } /** @@ -1570,7 +1570,7 @@ public boolean isEmptyRelation() throws Z3Exception **/ public boolean isIsEmptyRelation() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } /** @@ -1578,7 +1578,7 @@ public boolean isIsEmptyRelation() throws Z3Exception **/ public boolean isRelationalJoin() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_JOIN; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_JOIN; } /** @@ -1587,7 +1587,7 @@ public boolean isRelationalJoin() throws Z3Exception **/ public boolean isRelationUnion() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_UNION; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_UNION; } /** @@ -1596,7 +1596,7 @@ public boolean isRelationUnion() throws Z3Exception **/ public boolean isRelationWiden() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_WIDEN; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_WIDEN; } /** @@ -1606,7 +1606,7 @@ public boolean isRelationWiden() throws Z3Exception **/ public boolean isRelationProject() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_PROJECT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_PROJECT; } /** @@ -1618,7 +1618,7 @@ public boolean isRelationProject() throws Z3Exception **/ public boolean isRelationFilter() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_FILTER; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_FILTER; } /** @@ -1635,7 +1635,7 @@ public boolean isRelationFilter() throws Z3Exception **/ public boolean isRelationNegationFilter() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } /** @@ -1645,7 +1645,7 @@ public boolean isRelationNegationFilter() throws Z3Exception **/ public boolean isRelationRename() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_RENAME; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_RENAME; } /** @@ -1653,7 +1653,7 @@ public boolean isRelationRename() throws Z3Exception **/ public boolean isRelationComplement() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } /** @@ -1664,7 +1664,7 @@ public boolean isRelationComplement() throws Z3Exception **/ public boolean isRelationSelect() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_SELECT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_SELECT; } /** @@ -1676,7 +1676,7 @@ public boolean isRelationSelect() throws Z3Exception **/ public boolean isRelationClone() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_CLONE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_CLONE; } /** @@ -1695,7 +1695,7 @@ public boolean isFiniteDomain() throws Z3Exception **/ public boolean isFiniteDomainLT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FD_LT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FD_LT; } /** diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp similarity index 92% rename from src/tactic/fpa/fpa2bv_converter.cpp rename to src/ast/fpa/fpa2bv_converter.cpp index 2d1d6705f7a..20925a0f98d 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -872,8 +872,19 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient)); res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky); - SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); + SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); + expr_ref res_sig_lz(m); + mk_leading_zeros(res_sig, sbits + 4, res_sig_lz); + dbg_decouple("fpa2bv_div_res_sig_lz", res_sig_lz); + expr_ref res_sig_shift_amount(m); + res_sig_shift_amount = m_bv_util.mk_bv_sub(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); + dbg_decouple("fpa2bv_div_res_sig_shift_amount", res_sig_shift_amount); + expr_ref shift_cond(m); + shift_cond = m_bv_util.mk_ule(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); + m_simp.mk_ite(shift_cond, res_sig, m_bv_util.mk_bv_shl(res_sig, res_sig_shift_amount), res_sig); + m_simp.mk_ite(shift_cond, res_exp, m_bv_util.mk_bv_sub(res_exp, m_bv_util.mk_extract(ebits+1, 0, res_sig_shift_amount)), res_exp); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, v8); // And finally, we tie them together. @@ -2743,215 +2754,3 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & TRACE("fpa2bv_round", tout << "ROUND = " << mk_ismt2_pp(result, m) << std::endl; ); } - -void fpa2bv_model_converter::display(std::ostream & out) { - out << "(fpa2bv-model-converter"; - for (obj_map::iterator it = m_const2bv.begin(); - it != m_const2bv.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value, m, indent) << ")"; - } - for (obj_map::iterator it = m_rm_const2bv.begin(); - it != m_rm_const2bv.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value, m, indent) << ")"; - } - for (obj_map::iterator it = m_uf2bvuf.begin(); - it != m_uf2bvuf.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value, m, indent) << ")"; - } - for (obj_map::iterator it = m_uf23bvuf.begin(); - it != m_uf23bvuf.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value.f_sgn, m, indent) << " ; " << - mk_ismt2_pp(it->m_value.f_sig, m, indent) << " ; " << - mk_ismt2_pp(it->m_value.f_exp, m, indent) << " ; " << - ")"; - } - out << ")" << std::endl; -} - -model_converter * fpa2bv_model_converter::translate(ast_translation & translator) { - fpa2bv_model_converter * res = alloc(fpa2bv_model_converter, translator.to()); - for (obj_map::iterator it = m_const2bv.begin(); - it != m_const2bv.end(); - it++) - { - func_decl * k = translator(it->m_key); - expr * v = translator(it->m_value); - res->m_const2bv.insert(k, v); - translator.to().inc_ref(k); - translator.to().inc_ref(v); - } - for (obj_map::iterator it = m_rm_const2bv.begin(); - it != m_rm_const2bv.end(); - it++) - { - func_decl * k = translator(it->m_key); - expr * v = translator(it->m_value); - res->m_rm_const2bv.insert(k, v); - translator.to().inc_ref(k); - translator.to().inc_ref(v); - } - return res; -} - -void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { - float_util fu(m); - bv_util bu(m); - mpf fp_val; - unsynch_mpz_manager & mpzm = fu.fm().mpz_manager(); - unsynch_mpq_manager & mpqm = fu.fm().mpq_manager(); - - TRACE("fpa2bv_mc", tout << "BV Model: " << std::endl; - for (unsigned i = 0 ; i < bv_mdl->get_num_constants(); i++) - tout << bv_mdl->get_constant(i)->get_name() << " --> " << - mk_ismt2_pp(bv_mdl->get_const_interp(bv_mdl->get_constant(i)), m) << std::endl; - ); - - obj_hashtable seen; - - for (obj_map::iterator it = m_const2bv.begin(); - it != m_const2bv.end(); - it++) - { - func_decl * var = it->m_key; - app * a = to_app(it->m_value); - SASSERT(fu.is_float(var->get_range())); - SASSERT(var->get_range()->get_num_parameters() == 2); - - unsigned ebits = fu.get_ebits(var->get_range()); - unsigned sbits = fu.get_sbits(var->get_range()); - - expr_ref sgn(m), sig(m), exp(m); - sgn = bv_mdl->get_const_interp(to_app(a->get_arg(0))->get_decl()); - sig = bv_mdl->get_const_interp(to_app(a->get_arg(1))->get_decl()); - exp = bv_mdl->get_const_interp(to_app(a->get_arg(2))->get_decl()); - - seen.insert(to_app(a->get_arg(0))->get_decl()); - seen.insert(to_app(a->get_arg(1))->get_decl()); - seen.insert(to_app(a->get_arg(2))->get_decl()); - - if (!sgn && !sig && !exp) - continue; - - unsigned sgn_sz = bu.get_bv_size(m.get_sort(a->get_arg(0))); - unsigned sig_sz = bu.get_bv_size(m.get_sort(a->get_arg(1))) - 1; - unsigned exp_sz = bu.get_bv_size(m.get_sort(a->get_arg(2))); - - rational sgn_q(0), sig_q(0), exp_q(0); - - if (sgn) bu.is_numeral(sgn, sgn_q, sgn_sz); - if (sig) bu.is_numeral(sig, sig_q, sig_sz); - if (exp) bu.is_numeral(exp, exp_q, exp_sz); - - // un-bias exponent - rational exp_unbiased_q; - exp_unbiased_q = exp_q - fu.fm().m_powers2.m1(ebits-1); - - mpz sig_z; mpf_exp_t exp_z; - mpzm.set(sig_z, sig_q.to_mpq().numerator()); - exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator()); - - TRACE("fpa2bv_mc", tout << var->get_name() << " == [" << sgn_q.to_string() << " " << - mpzm.to_string(sig_z) << " " << exp_z << "(" << exp_q.to_string() << ")]" << std::endl; ); - - fu.fm().set(fp_val, ebits, sbits, !mpqm.is_zero(sgn_q.to_mpq()), sig_z, exp_z); - - float_mdl->register_decl(var, fu.mk_value(fp_val)); - - mpzm.del(sig_z); - } - - for (obj_map::iterator it = m_rm_const2bv.begin(); - it != m_rm_const2bv.end(); - it++) - { - func_decl * var = it->m_key; - app * a = to_app(it->m_value); - SASSERT(fu.is_rm(var->get_range())); - rational val(0); - unsigned sz = 0; - if (a && bu.is_numeral(a, val, sz)) { - TRACE("fpa2bv_mc", tout << var->get_name() << " == " << val.to_string() << std::endl; ); - SASSERT(val.is_uint64()); - switch (val.get_uint64()) - { - case BV_RM_TIES_TO_AWAY: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_away()); break; - case BV_RM_TIES_TO_EVEN: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_even()); break; - case BV_RM_TO_NEGATIVE: float_mdl->register_decl(var, fu.mk_round_toward_negative()); break; - case BV_RM_TO_POSITIVE: float_mdl->register_decl(var, fu.mk_round_toward_positive()); break; - case BV_RM_TO_ZERO: - default: float_mdl->register_decl(var, fu.mk_round_toward_zero()); - } - seen.insert(var); - } - } - - for (obj_map::iterator it = m_uf2bvuf.begin(); - it != m_uf2bvuf.end(); - it++) - seen.insert(it->m_value); - - for (obj_map::iterator it = m_uf23bvuf.begin(); - it != m_uf23bvuf.end(); - it++) - { - seen.insert(it->m_value.f_sgn); - seen.insert(it->m_value.f_sig); - seen.insert(it->m_value.f_exp); - } - - fu.fm().del(fp_val); - - // Keep all the non-float constants. - unsigned sz = bv_mdl->get_num_constants(); - for (unsigned i = 0; i < sz; i++) - { - func_decl * c = bv_mdl->get_constant(i); - if (!seen.contains(c)) - float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); - } - - // And keep everything else - sz = bv_mdl->get_num_functions(); - for (unsigned i = 0; i < sz; i++) - { - func_decl * f = bv_mdl->get_function(i); - if (!seen.contains(f)) - { - TRACE("fpa2bv_mc", tout << "Keeping: " << mk_ismt2_pp(f, m) << std::endl; ); - func_interp * val = bv_mdl->get_func_interp(f); - float_mdl->register_decl(f, val); - } - } - - sz = bv_mdl->get_num_uninterpreted_sorts(); - for (unsigned i = 0; i < sz; i++) - { - sort * s = bv_mdl->get_uninterpreted_sort(i); - ptr_vector u = bv_mdl->get_universe(s); - float_mdl->register_usort(s, u.size(), u.c_ptr()); - } -} - -model_converter * mk_fpa2bv_model_converter(ast_manager & m, - obj_map const & const2bv, - obj_map const & rm_const2bv, - obj_map const & uf2bvuf, - obj_map const & uf23bvuf) { - return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv, uf2bvuf, uf23bvuf); -} diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h similarity index 72% rename from src/tactic/fpa/fpa2bv_converter.h rename to src/ast/fpa/fpa2bv_converter.h index 79c8039ef83..2ccdac6a9aa 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -24,13 +24,10 @@ Module Name: #include"ref_util.h" #include"float_decl_plugin.h" #include"bv_decl_plugin.h" -#include"model_converter.h" #include"basic_simplifier_plugin.h" typedef enum { BV_RM_TIES_TO_AWAY=0, BV_RM_TIES_TO_EVEN=1, BV_RM_TO_NEGATIVE=2, BV_RM_TO_POSITIVE=3, BV_RM_TO_ZERO=4 } BV_RM_VAL; -class fpa2bv_model_converter; - struct func_decl_triple { func_decl_triple () { f_sgn = 0; f_sig = 0; f_exp = 0; } func_decl_triple (func_decl * sgn, func_decl * sig, func_decl * exp) @@ -173,86 +170,4 @@ class fpa2bv_converter { expr_ref & res_sgn, expr_ref & res_sig, expr_ref & res_exp); }; - -class fpa2bv_model_converter : public model_converter { - ast_manager & m; - obj_map m_const2bv; - obj_map m_rm_const2bv; - obj_map m_uf2bvuf; - obj_map m_uf23bvuf; - -public: - fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv, - obj_map const & uf2bvuf, - obj_map const & uf23bvuf) : - m(m) { - // Just create a copy? - for (obj_map::iterator it = const2bv.begin(); - it != const2bv.end(); - it++) - { - m_const2bv.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value); - } - for (obj_map::iterator it = rm_const2bv.begin(); - it != rm_const2bv.end(); - it++) - { - m_rm_const2bv.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value); - } - for (obj_map::iterator it = uf2bvuf.begin(); - it != uf2bvuf.end(); - it++) - { - m_uf2bvuf.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value); - } - for (obj_map::iterator it = uf23bvuf.begin(); - it != uf23bvuf.end(); - it++) - { - m_uf23bvuf.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - } - } - - virtual ~fpa2bv_model_converter() { - dec_ref_map_key_values(m, m_const2bv); - dec_ref_map_key_values(m, m_rm_const2bv); - } - - virtual void operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); - model * new_model = alloc(model, m); - obj_hashtable bits; - convert(md.get(), new_model); - md = new_model; - } - - virtual void operator()(model_ref & md) { - operator()(md, 0); - } - - void display(std::ostream & out); - - virtual model_converter * translate(ast_translation & translator); - -protected: - fpa2bv_model_converter(ast_manager & m) : m(m) { } - - void convert(model * bv_mdl, model * float_mdl); -}; - - -model_converter * mk_fpa2bv_model_converter(ast_manager & m, - obj_map const & const2bv, - obj_map const & rm_const2bv, - obj_map const & uf2bvuf, - obj_map const & uf23bvuf); - #endif diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/ast/fpa/fpa2bv_rewriter.h similarity index 100% rename from src/tactic/fpa/fpa2bv_rewriter.h rename to src/ast/fpa/fpa2bv_rewriter.h diff --git a/src/duality/duality.h b/src/duality/duality.h old mode 100755 new mode 100644 index aa147d93e5e..fc70ffa70a2 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1,1317 +1,1321 @@ -/*++ -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 -#include - -// make hash_map and hash_set available -using namespace stl_ext; - -namespace Duality { - - struct implicant_solver; - - /* Generic operations on Z3 formulas */ - - struct Z3User { - - context &ctx; - - typedef func_decl FuncDecl; - typedef expr Term; - - Z3User(context &_ctx) : ctx(_ctx){} - - const char *string_of_int(int n); - - Term conjoin(const std::vector &args); - - Term sum(const std::vector &args); - - Term CloneQuantifier(const Term &t, const Term &new_body); - - Term SubstRec(hash_map &memo, const Term &t); - - Term SubstRec(hash_map &memo, hash_map &map, 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 &memo, const Term &t, int number); - - void CollectConjuncts(const Term &f, std::vector &lits, bool pos = true); - - void SortTerms(std::vector &terms); - - Term SortSum(const Term &t); - - void Summarize(const Term &t); - - int CountOperators(const Term &t); - - Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); - - Term CloneQuantAndSimp(const expr &t, const expr &body); - - Term RemoveRedundancy(const Term &t); - - Term IneqToEq(const Term &t); - - bool IsLiteral(const expr &lit, expr &atom, expr &val); - - expr Negate(const expr &f); - - expr SimplifyAndOr(const std::vector &args, bool is_and); - - expr ReallySimplifyAndOr(const std::vector &args, bool is_and); - - int MaxIndex(hash_map &memo, const Term &t); - - bool IsClosedFormula(const Term &t); - - Term AdjustQuantifiers(const Term &t); - - FuncDecl RenumberPred(const FuncDecl &f, int n); - - Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); - - -protected: - - void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); - int CountOperatorsRec(hash_set &memo, const Term &t); - void RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo); - Term RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t); - Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); - expr ReduceAndOr(const std::vector &args, bool is_and, std::vector &res); - expr FinishAndOr(const std::vector &args, bool is_and); - expr PullCommonFactors(std::vector &args, bool is_and); - Term IneqToEqRec(hash_map &memo, 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? */ - solver aux_solver; /** For temporary use -- don't leave assertions here. */ - - /** 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; - - /** Get the constants in the background vocabulary */ - virtual hash_set &get_constants() = 0; - - /** Assert a background axiom. */ - virtual void assert_axiom(const expr &axiom) = 0; - - /** Get the background axioms. */ - virtual const std::vector &get_axioms() = 0; - - /** Return a string describing performance. */ - virtual std::string profile() = 0; - - virtual void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ){} - - /** Cancel, throw Canceled object if possible. */ - virtual void cancel(){ } - - /* Note: aux solver uses extensional array theory, since it - needs to be able to produce counter-models for - interpolants the have array equalities in them. - */ - LogicSolver(context &c) : aux_solver(c,true){} - - 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); - } - - const std::vector &get_axioms() { - return islvr->GetInterpolationAxioms(); - } - - 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, bool models = true) : LogicSolver(c) { - ctx = ictx = &c; - slvr = islvr = new interpolating_solver(*ictx, models); - need_goals = false; - islvr->SetWeakInterpolants(true); - } - - void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &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(); - } - - /** Get the constants in the background vocabulary */ - virtual hash_set &get_constants(){ - return bckg; - } - - ~iZ3LogicSolver(){ - // delete ictx; - delete islvr; - } - private: - hash_set 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; - - protected: - int nodeCount; - int edgeCount; - - class stack_entry - { - public: - std::list edges; - std::list nodes; - std::list > constraints; - }; - - - public: - model dualModel; - protected: - literals dualLabels; - std::list stack; - std::vector axioms; // only saved here for printing purposes - solver &aux_solver; - hash_set *proof_core; - - 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)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) - { - ls = _ls; - nodeCount = 0; - edgeCount = 0; - stack.push_back(stack_entry()); - HornClauses = false; - proof_core = 0; - } - - virtual ~RPFP(); - - /** Symbolic representation of a relational transformer */ - class Transformer - { - public: - std::vector RelParams; - std::vector IndParams; - Term Formula; - RPFP *owner; - hash_map 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->aux_solver.push(); - owner->aux_solver.add(test); - check_result res = owner->aux_solver.check(); - owner->aux_solver.pop(1); - return res == unsat; - } - - void Complement(){ - Formula = !Formula; - } - - void Simplify(){ - Formula = Formula.simplify(); - } - - Transformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula, RPFP *_owner) - : RelParams(_RelParams), IndParams(_IndParams), Formula(_Formula) {owner = _owner;} - }; - - /** Create a symbolic transformer. */ - Transformer CreateTransformer(const std::vector &_RelParams, const std::vector &_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 &_IndParams, const Term &_Formula) - { - return CreateTransformer(std::vector(), _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 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 _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; - } - - /** Delete a node. You can only do this if not connected to any edges.*/ - void DeleteNode(Node *node){ - if(node->Outgoing || !node->Incoming.empty()) - throw "cannot delete RPFP node"; - for(std::vector::iterator it = nodes.end(), en = nodes.begin(); it != en;){ - if(*(--it) == node){ - nodes.erase(it); - break; - } - } - delete node; - } - - /** This class represents a hyper-edge in the RPFP graph */ - - class Edge - { - public: - Transformer F; - Node *Parent; - std::vector Children; - RPFP *owner; - int number; - // these should be internal... - Term dual; - hash_map relMap; - hash_map varMap; - Edge *map; - Term labeled; - std::vector constraints; - - Edge(Node *_Parent, const Transformer &_F, const std::vector &_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 &_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; - } - - - /** Delete a hyper-edge and unlink it from any nodes. */ - void DeleteEdge(Edge *edge){ - if(edge->Parent) - edge->Parent->Outgoing = 0; - for(unsigned int i = 0; i < edge->Children.size(); i++){ - std::vector &ic = edge->Children[i]->Incoming; - for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ - if(*it == edge){ - ic.erase(it); - break; - } - } - } - for(std::vector::iterator it = edges.end(), en = edges.begin(); it != en;){ - if(*(--it) == edge){ - edges.erase(it); - break; - } - } - delete edge; - } - - /** Create an edge that lower-bounds its parent. */ - Edge *CreateLowerBoundEdge(Node *_Parent) - { - return CreateEdge(_Parent, _Parent->Annotation, std::vector()); - } - - - /** 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. */ - - virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); - - /* Constrain an edge by the annotation of one of its children. */ - - void ConstrainParent(Edge *parent, Node *child); - - /** For incremental solving, asserts the negation of the upper bound associated - * with a node. - * */ - - void AssertNode(Node *n); - - /** Assert a constraint on an edge in the SMT context. - */ - void ConstrainEdge(Edge *e, const Term &t); - - /** Fix the truth values of atomic propositions in the given - edge to their values in the current assignment. */ - void FixCurrentState(Edge *root); - - void FixCurrentStateFull(Edge *edge, const expr &extra); - - void FixCurrentStateFull(Edge *edge, const std::vector &assumps, const hash_map &renaming); - - /** 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); - - /** Same as Solve, but annotates only a single node. */ - - lbool SolveSingleNode(Node *root, Node *node); - - /** Get the constraint tree (but don't solve it) */ - - TermTree *GetConstraintTree(Node *root, Node *skip_descendant = 0); - - /** 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 underapproxes = std::vector(), - std::vector *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 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); - - /** Try to strengthen the annotation of a node by removing disjuncts. */ - void Generalize(Node *root, Node *node); - - - /** Compute disjunctive interpolant for node by case splitting */ - void InterpolateByCases(Node *root, Node *node); - - /** 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 { - }; - - // thrown on internal error - struct Bad { - }; - - /** Pop a scope (see Push). Note, you cannot pop axioms. */ - - void Pop(int num_scopes); - - /** Erase the proof by performing a Pop, Push and re-assertion of - all the popped constraints */ - void PopPush(); - - /** Return true if the given edge is used in the proof of unsat. - Can be called only after Solve or Check returns an unsat result. */ - - bool EdgeUsedInProof(Edge *edge); - - - /** 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 _WINDOWS - __declspec(dllexport) -#endif - void FromClauses(const std::vector &clauses); - - void FromFixpointContext(fixedpoint fp, std::vector &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 &cnsts, FileFormat format = DualityFormat); - - /** Translate the RPFP to a fixed point context, with queries */ - fixedpoint ToFixedPointProblem(std::vector &queries); - - /** Nodes of the graph. */ - std::vector nodes; - - /** Edges of the graph. */ - std::vector edges; - - /** Fuse a vector of transformers. If the total number of inputs of the transformers - is N, then the result is an N-ary transfomer whose output is the union of - the outputs of the given transformers. The is, suppose we have a vetor of transfoermers - {T_i(r_i1,...,r_iN(i) : i=1..M}. The the result is a transformer - - F(r_11,...,r_iN(1),...,r_M1,...,r_MN(M)) = - T_1(r_11,...,r_iN(1)) U ... U T_M(r_M1,...,r_MN(M)) - */ - - Transformer Fuse(const std::vector &trs); - - /** Fuse edges so that each node is the output of at most one edge. This - transformation is solution-preserving, but changes the numbering of edges in - counterexamples. - */ - void FuseEdges(); - - void RemoveDeadNodes(); - - Term SubstParams(const std::vector &from, - const std::vector &to, const Term &t); - - Term SubstParamsNoCapture(const std::vector &from, - const std::vector &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 &memo, Edge *e, const Term &f); - - void GetLabels(Edge *e, std::vector &labels); - - // int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); - - /** Compute and save the proof core for future calls to - EdgeUsedInProof. You only need to call this if you will pop - the solver before calling EdgeUsedInProof. - */ - void ComputeProofCore(); - - int CumulativeDecisions(); - - solver &slvr(){ - return *ls->slvr; - } - - protected: - - void ClearProofCore(){ - if(proof_core) - delete proof_core; - proof_core = 0; - } - - Term SuffixVariable(const Term &t, int n); - - Term HideVariable(const Term &t, int n); - - void RedVars(Node *node, Term &b, std::vector &v); - - Term RedDualRela(Edge *e, std::vector &args, int idx); - - Term LocalizeRec(Edge *e, hash_map &memo, const Term &t); - - void SetEdgeMaps(Edge *e); - - Term ReducedDualEdge(Edge *e); - - TermTree *ToTermTree(Node *root, Node *skip_descendant = 0); - - TermTree *ToGoalTree(Node *root); - - void CollapseTermTreeRec(TermTree *root, TermTree *node); - - TermTree *CollapseTermTree(TermTree *node); - - 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 &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 &memo, - const Term &t, - hash_map &pmap, - std::vector &res, - std::vector &nodes); - - Term RemoveLabelsRec(hash_map &memo, const Term &t, std::vector &lbls); - - Term RemoveLabels(const Term &t, std::vector &lbls); - - Term GetAnnotation(Node *n); - - - Term GetUnderapprox(Node *n); - - Term UnderapproxFlag(Node *n); - - hash_map underapprox_flag_rev; - - Node *UnderapproxFlagRev(const Term &flag); - - Term ProjectFormula(std::vector &keep_vec, const Term &f); - - int SubtermTruth(hash_map &memo, const Term &); - - void ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set *done, bool truth, hash_set &dont_cares); - - void Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares); - - Term UnderapproxFormula(const Term &f, hash_set &dont_cares); - - void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares, bool extensional = true); - - public: - Term UnderapproxFullFormula(const Term &f, bool extensional = true); - - protected: - Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); - - hash_map resolve_ite_memo; - - Term ResolveIte(hash_map &memo, const Term &t, std::vector &lits, - hash_set *done, hash_set &dont_cares); - - struct ArrayValue { - bool defined; - std::map 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 &memo, const Term &f, std::vector &labels, - hash_set *done, bool truth); - - Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); - - Term SubstBound(hash_map &subst, const Term &t); - - void ConstrainEdgeLocalized(Edge *e, const Term &t); - - void GreedyReduce(solver &s, std::vector &conjuncts); - - void NegateLits(std::vector &lits); - - expr SimplifyOr(std::vector &lits); - - expr SimplifyAnd(std::vector &lits); - - void SetAnnotation(Node *root, const expr &t); - - void AddEdgeToSolver(Edge *edge); - - void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge); - - void AddToProofCore(hash_set &core); - - void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); - - Term StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits); - - expr NegateLit(const expr &f); - - expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); - - bool IsVar(const expr &t); - - void GetVarsRec(hash_set &memo, const expr &cnst, std::vector &vars); - - expr UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params); - - void AddParamsToTransformer(Transformer &trans, const std::vector ¶ms); - - expr AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms); - - expr GetRelRec(hash_set &memo, const expr &t, const func_decl &rel); - - expr GetRel(Edge *edge, int child_idx); - - void GetDefs(const expr &cnst, hash_map &defs); - - void GetDefsRec(const expr &cnst, hash_map &defs); - - void AddParamsToNode(Node *node, const std::vector ¶ms); - - void UnhoistLoop(Edge *loop_edge, Edge *init_edge); - - void Unhoist(); - - Term ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts); - - Term ElimIte(const Term &t); - - void MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node); - - virtual void slvr_add(const expr &e); - - virtual void slvr_pop(int i); - - virtual void slvr_push(); - - virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); - - virtual lbool ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false); - - virtual bool proof_core_contains(const expr &e); - - }; - - - /** RPFP solver base class. */ - - class Solver { - - public: - - class Counterexample { - private: - RPFP *tree; - RPFP::Node *root; - public: - Counterexample(){ - tree = 0; - root = 0; - } - Counterexample(RPFP *_tree, RPFP::Node *_root){ - tree = _tree; - root = _root; - } - ~Counterexample(){ - if(tree) delete tree; - } - void swap(Counterexample &other){ - std::swap(tree,other.tree); - std::swap(root,other.root); - } - void set(RPFP *_tree, RPFP::Node *_root){ - if(tree) delete tree; - tree = _tree; - root = _root; - } - void clear(){ - if(tree) delete tree; - tree = 0; - } - RPFP *get_tree() const {return tree;} - RPFP::Node *get_root() const {return root;} - private: - Counterexample &operator=(const Counterexample &); - Counterexample(const Counterexample &); - }; - - /** 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(Solver *old_solver) = 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 {}; - - /** Object thrown on incompleteness */ - struct Incompleteness {}; - }; -} - - -// Allow to hash on nodes and edges in deterministic way - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::RPFP::Node *p) const { - return p->number; - } - }; -} - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::RPFP::Edge *p) const { - return p->number; - } - }; -} - -// allow to walk sets of nodes without address dependency - -namespace std { - template <> - class less { - public: - bool operator()(Duality::RPFP::Node * const &s, Duality::RPFP::Node * const &t) const { - return s->number < t->number; // s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// #define LIMIT_STACK_WEIGHT 5 - - -namespace Duality { - /** Caching version of RPFP. Instead of asserting constraints, returns assumption literals */ - - class RPFP_caching : public RPFP { - public: - - /** appends assumption literals for edge to lits. if with_children is true, - includes that annotation of the edge's children. - */ - void AssertEdgeCache(Edge *e, std::vector &lits, bool with_children = false); - - /** appends assumption literals for node to lits */ - void AssertNodeCache(Node *, std::vector lits); - - /** check assumption lits, and return core */ - check_result CheckCore(const std::vector &assumps, std::vector &core); - - /** Clone another RPFP into this one, keeping a map */ - void Clone(RPFP *other); - - /** Get the clone of a node */ - Node *GetNodeClone(Node *other_node); - - /** Get the clone of an edge */ - Edge *GetEdgeClone(Edge *other_edge); - - /** Try to strengthen the parent of an edge */ - void GeneralizeCache(Edge *edge); - - /** Try to propagate some facts from children to parents of edge. - Return true if success. */ - bool PropagateCache(Edge *edge); - - /** Construct a caching RPFP using a LogicSolver */ - RPFP_caching(LogicSolver *_ls) : RPFP(_ls) {} - - /** Constraint an edge by its child's annotation. Return - assumption lits. */ - void ConstrainParentCache(Edge *parent, Node *child, std::vector &lits); - -#ifdef LIMIT_STACK_WEIGHT - virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); -#endif - - virtual ~RPFP_caching(){} - - protected: - hash_map AssumptionLits; - hash_map NodeCloneMap; - hash_map EdgeCloneMap; - std::vector alit_stack; - std::vector alit_stack_sizes; - - // to let us use one solver per edge - struct edge_solver { - hash_map AssumptionLits; - uptr slvr; - }; - hash_map edge_solvers; - -#ifdef LIMIT_STACK_WEIGHT - struct weight_counter { - int val; - weight_counter(){val = 0;} - void swap(weight_counter &other){ - std::swap(val,other.val); - } - }; - - struct big_stack_entry { - weight_counter weight_added; - std::vector new_alits; - std::vector alit_stack; - std::vector alit_stack_sizes; - }; - - std::vector new_alits; - weight_counter weight_added; - std::vector big_stack; -#endif - - - - void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = 0); - - void GreedyReduceCache(std::vector &assumps, std::vector &core); - - void FilterCore(std::vector &core, std::vector &full_core); - void ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits); - - virtual void slvr_add(const expr &e); - - virtual void slvr_pop(int i); - - virtual void slvr_push(); - - virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); - - virtual lbool ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false); - - virtual bool proof_core_contains(const expr &e); - - void GetTermTreeAssertionLiterals(TermTree *assumptions); - - void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); - - edge_solver &SolverForEdge(Edge *edge, bool models, bool axioms); - - public: - struct scoped_solver_for_edge { - solver *orig_slvr; - RPFP_caching *rpfp; - edge_solver *es; - scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false, bool axioms = false){ - rpfp = _rpfp; - orig_slvr = rpfp->ls->slvr; - es = &(rpfp->SolverForEdge(edge,models,axioms)); - rpfp->ls->slvr = es->slvr.get(); - rpfp->AssumptionLits.swap(es->AssumptionLits); - } - ~scoped_solver_for_edge(){ - rpfp->ls->slvr = orig_slvr; - rpfp->AssumptionLits.swap(es->AssumptionLits); - } - }; - - }; - -} +/*++ +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 +#include + +// make hash_map and hash_set available +using namespace stl_ext; + +namespace Duality { + + class implicant_solver; + + /* Generic operations on Z3 formulas */ + + struct Z3User { + + context &ctx; + + typedef func_decl FuncDecl; + typedef expr Term; + + Z3User(context &_ctx) : ctx(_ctx){} + + const char *string_of_int(int n); + + Term conjoin(const std::vector &args); + + Term sum(const std::vector &args); + + Term CloneQuantifier(const Term &t, const Term &new_body); + + Term SubstRec(hash_map &memo, const Term &t); + + Term SubstRec(hash_map &memo, hash_map &map, 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 &memo, const Term &t, int number); + + void CollectConjuncts(const Term &f, std::vector &lits, bool pos = true); + + void SortTerms(std::vector &terms); + + Term SortSum(const Term &t); + + void Summarize(const Term &t); + + int CountOperators(const Term &t); + + Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); + + Term CloneQuantAndSimp(const expr &t, const expr &body); + + Term RemoveRedundancy(const Term &t); + + Term IneqToEq(const Term &t); + + bool IsLiteral(const expr &lit, expr &atom, expr &val); + + expr Negate(const expr &f); + + expr SimplifyAndOr(const std::vector &args, bool is_and); + + expr ReallySimplifyAndOr(const std::vector &args, bool is_and); + + int MaxIndex(hash_map &memo, const Term &t); + + bool IsClosedFormula(const Term &t); + + Term AdjustQuantifiers(const Term &t); + + FuncDecl RenumberPred(const FuncDecl &f, int n); + + Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); + + +protected: + + void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); + int CountOperatorsRec(hash_set &memo, const Term &t); + void RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo); + Term RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t); + Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); + expr ReduceAndOr(const std::vector &args, bool is_and, std::vector &res); + expr FinishAndOr(const std::vector &args, bool is_and); + expr PullCommonFactors(std::vector &args, bool is_and); + Term IneqToEqRec(hash_map &memo, const Term &t); + Term CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall); + Term PushQuantifier(const expr &t, const expr &body, bool is_forall); + void CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate); + Term DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t); + Term DeleteBound(int level, int num, 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? */ + solver aux_solver; /** For temporary use -- don't leave assertions here. */ + + /** 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; + + /** Get the constants in the background vocabulary */ + virtual hash_set &get_constants() = 0; + + /** Assert a background axiom. */ + virtual void assert_axiom(const expr &axiom) = 0; + + /** Get the background axioms. */ + virtual const std::vector &get_axioms() = 0; + + /** Return a string describing performance. */ + virtual std::string profile() = 0; + + virtual void write_interpolation_problem(const std::string &file_name, + const std::vector &assumptions, + const std::vector &theory + ){} + + /** Cancel, throw Canceled object if possible. */ + virtual void cancel(){ } + + /* Note: aux solver uses extensional array theory, since it + needs to be able to produce counter-models for + interpolants the have array equalities in them. + */ + LogicSolver(context &c) : aux_solver(c,true){} + + 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); + } + + const std::vector &get_axioms() { + return islvr->GetInterpolationAxioms(); + } + + 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, bool models = true) : LogicSolver(c) { + ctx = ictx = &c; + slvr = islvr = new interpolating_solver(*ictx, models); + need_goals = false; + islvr->SetWeakInterpolants(true); + } + + void write_interpolation_problem(const std::string &file_name, + const std::vector &assumptions, + const std::vector &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(); + } + + /** Get the constants in the background vocabulary */ + virtual hash_set &get_constants(){ + return bckg; + } + + ~iZ3LogicSolver(){ + // delete ictx; + delete islvr; + } + private: + hash_set 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; + + protected: + int nodeCount; + int edgeCount; + + class stack_entry + { + public: + std::list edges; + std::list nodes; + std::list > constraints; + }; + + + public: + model dualModel; + protected: + literals dualLabels; + std::list stack; + std::vector axioms; // only saved here for printing purposes + solver &aux_solver; + hash_set *proof_core; + + 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)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) + { + ls = _ls; + nodeCount = 0; + edgeCount = 0; + stack.push_back(stack_entry()); + HornClauses = false; + proof_core = 0; + } + + virtual ~RPFP(); + + /** Symbolic representation of a relational transformer */ + class Transformer + { + public: + std::vector RelParams; + std::vector IndParams; + Term Formula; + RPFP *owner; + hash_map 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->aux_solver.push(); + owner->aux_solver.add(test); + check_result res = owner->aux_solver.check(); + owner->aux_solver.pop(1); + return res == unsat; + } + + void Complement(){ + Formula = !Formula; + } + + void Simplify(){ + Formula = Formula.simplify(); + } + + Transformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula, RPFP *_owner) + : RelParams(_RelParams), IndParams(_IndParams), Formula(_Formula) {owner = _owner;} + }; + + /** Create a symbolic transformer. */ + Transformer CreateTransformer(const std::vector &_RelParams, const std::vector &_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 &_IndParams, const Term &_Formula) + { + return CreateTransformer(std::vector(), _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 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 _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; + } + + /** Delete a node. You can only do this if not connected to any edges.*/ + void DeleteNode(Node *node){ + if(node->Outgoing || !node->Incoming.empty()) + throw "cannot delete RPFP node"; + for(std::vector::iterator it = nodes.end(), en = nodes.begin(); it != en;){ + if(*(--it) == node){ + nodes.erase(it); + break; + } + } + delete node; + } + + /** This class represents a hyper-edge in the RPFP graph */ + + class Edge + { + public: + Transformer F; + Node *Parent; + std::vector Children; + RPFP *owner; + int number; + // these should be internal... + Term dual; + hash_map relMap; + hash_map varMap; + Edge *map; + Term labeled; + std::vector constraints; + + Edge(Node *_Parent, const Transformer &_F, const std::vector &_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 &_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; + } + + + /** Delete a hyper-edge and unlink it from any nodes. */ + void DeleteEdge(Edge *edge){ + if(edge->Parent) + edge->Parent->Outgoing = 0; + for(unsigned int i = 0; i < edge->Children.size(); i++){ + std::vector &ic = edge->Children[i]->Incoming; + for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ + if(*it == edge){ + ic.erase(it); + break; + } + } + } + for(std::vector::iterator it = edges.end(), en = edges.begin(); it != en;){ + if(*(--it) == edge){ + edges.erase(it); + break; + } + } + delete edge; + } + + /** Create an edge that lower-bounds its parent. */ + Edge *CreateLowerBoundEdge(Node *_Parent) + { + return CreateEdge(_Parent, _Parent->Annotation, std::vector()); + } + + + /** 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. */ + + virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); + + /* Constrain an edge by the annotation of one of its children. */ + + void ConstrainParent(Edge *parent, Node *child); + + /** For incremental solving, asserts the negation of the upper bound associated + * with a node. + * */ + + void AssertNode(Node *n); + + /** Assert a constraint on an edge in the SMT context. + */ + void ConstrainEdge(Edge *e, const Term &t); + + /** Fix the truth values of atomic propositions in the given + edge to their values in the current assignment. */ + void FixCurrentState(Edge *root); + + void FixCurrentStateFull(Edge *edge, const expr &extra); + + void FixCurrentStateFull(Edge *edge, const std::vector &assumps, const hash_map &renaming); + + /** 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); + + /** Same as Solve, but annotates only a single node. */ + + lbool SolveSingleNode(Node *root, Node *node); + + /** Get the constraint tree (but don't solve it) */ + + TermTree *GetConstraintTree(Node *root, Node *skip_descendant = 0); + + /** 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 underapproxes = std::vector(), + std::vector *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 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); + + /** Try to strengthen the annotation of a node by removing disjuncts. */ + void Generalize(Node *root, Node *node); + + + /** Compute disjunctive interpolant for node by case splitting */ + void InterpolateByCases(Node *root, Node *node); + + /** 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 { + }; + + // thrown on internal error + struct Bad { + }; + + /** Pop a scope (see Push). Note, you cannot pop axioms. */ + + void Pop(int num_scopes); + + /** Erase the proof by performing a Pop, Push and re-assertion of + all the popped constraints */ + void PopPush(); + + /** Return true if the given edge is used in the proof of unsat. + Can be called only after Solve or Check returns an unsat result. */ + + bool EdgeUsedInProof(Edge *edge); + + + /** 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 _WINDOWS + __declspec(dllexport) +#endif + void FromClauses(const std::vector &clauses); + + void FromFixpointContext(fixedpoint fp, std::vector &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 &cnsts, FileFormat format = DualityFormat); + + /** Translate the RPFP to a fixed point context, with queries */ + fixedpoint ToFixedPointProblem(std::vector &queries); + + /** Nodes of the graph. */ + std::vector nodes; + + /** Edges of the graph. */ + std::vector edges; + + /** Fuse a vector of transformers. If the total number of inputs of the transformers + is N, then the result is an N-ary transfomer whose output is the union of + the outputs of the given transformers. The is, suppose we have a vetor of transfoermers + {T_i(r_i1,...,r_iN(i) : i=1..M}. The the result is a transformer + + F(r_11,...,r_iN(1),...,r_M1,...,r_MN(M)) = + T_1(r_11,...,r_iN(1)) U ... U T_M(r_M1,...,r_MN(M)) + */ + + Transformer Fuse(const std::vector &trs); + + /** Fuse edges so that each node is the output of at most one edge. This + transformation is solution-preserving, but changes the numbering of edges in + counterexamples. + */ + void FuseEdges(); + + void RemoveDeadNodes(); + + Term SubstParams(const std::vector &from, + const std::vector &to, const Term &t); + + Term SubstParamsNoCapture(const std::vector &from, + const std::vector &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 &memo, Edge *e, const Term &f); + + void GetLabels(Edge *e, std::vector &labels); + + // int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); + + /** Compute and save the proof core for future calls to + EdgeUsedInProof. You only need to call this if you will pop + the solver before calling EdgeUsedInProof. + */ + void ComputeProofCore(); + + int CumulativeDecisions(); + + solver &slvr(){ + return *ls->slvr; + } + + protected: + + void ClearProofCore(){ + if(proof_core) + delete proof_core; + proof_core = 0; + } + + Term SuffixVariable(const Term &t, int n); + + Term HideVariable(const Term &t, int n); + + void RedVars(Node *node, Term &b, std::vector &v); + + Term RedDualRela(Edge *e, std::vector &args, int idx); + + Term LocalizeRec(Edge *e, hash_map &memo, const Term &t); + + void SetEdgeMaps(Edge *e); + + Term ReducedDualEdge(Edge *e); + + TermTree *ToTermTree(Node *root, Node *skip_descendant = 0); + + TermTree *ToGoalTree(Node *root); + + void CollapseTermTreeRec(TermTree *root, TermTree *node); + + TermTree *CollapseTermTree(TermTree *node); + + 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 &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 &memo, + const Term &t, + hash_map &pmap, + std::vector &res, + std::vector &nodes); + + Term RemoveLabelsRec(hash_map &memo, const Term &t, std::vector &lbls); + + Term RemoveLabels(const Term &t, std::vector &lbls); + + Term GetAnnotation(Node *n); + + + Term GetUnderapprox(Node *n); + + Term UnderapproxFlag(Node *n); + + hash_map underapprox_flag_rev; + + Node *UnderapproxFlagRev(const Term &flag); + + Term ProjectFormula(std::vector &keep_vec, const Term &f); + + int SubtermTruth(hash_map &memo, const Term &); + + void ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set *done, bool truth, hash_set &dont_cares); + + void Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares); + + Term UnderapproxFormula(const Term &f, hash_set &dont_cares); + + void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set &done, hash_set &dont_cares, bool extensional = true); + + public: + Term UnderapproxFullFormula(const Term &f, bool extensional = true); + + protected: + Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); + + hash_map resolve_ite_memo; + + Term ResolveIte(hash_map &memo, const Term &t, std::vector &lits, + hash_set *done, hash_set &dont_cares); + + struct ArrayValue { + bool defined; + std::map 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 &memo, const Term &f, std::vector &labels, + hash_set *done, bool truth); + + Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); + + Term SubstBound(hash_map &subst, const Term &t); + + void ConstrainEdgeLocalized(Edge *e, const Term &t); + + void GreedyReduce(solver &s, std::vector &conjuncts); + + void NegateLits(std::vector &lits); + + expr SimplifyOr(std::vector &lits); + + expr SimplifyAnd(std::vector &lits); + + void SetAnnotation(Node *root, const expr &t); + + void AddEdgeToSolver(Edge *edge); + + void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge); + + void AddToProofCore(hash_set &core); + + void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); + + Term StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits); + + expr NegateLit(const expr &f); + + expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); + + bool IsVar(const expr &t); + + void GetVarsRec(hash_set &memo, const expr &cnst, std::vector &vars); + + expr UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params); + + void AddParamsToTransformer(Transformer &trans, const std::vector ¶ms); + + expr AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms); + + expr GetRelRec(hash_set &memo, const expr &t, const func_decl &rel); + + expr GetRel(Edge *edge, int child_idx); + + void GetDefs(const expr &cnst, hash_map &defs); + + void GetDefsRec(const expr &cnst, hash_map &defs); + + void AddParamsToNode(Node *node, const std::vector ¶ms); + + void UnhoistLoop(Edge *loop_edge, Edge *init_edge); + + void Unhoist(); + + Term ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts); + + Term ElimIte(const Term &t); + + void MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node); + + virtual void slvr_add(const expr &e); + + virtual void slvr_pop(int i); + + virtual void slvr_push(); + + virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); + + virtual lbool ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false); + + virtual bool proof_core_contains(const expr &e); + + }; + + + /** RPFP solver base class. */ + + class Solver { + + public: + + class Counterexample { + private: + RPFP *tree; + RPFP::Node *root; + public: + Counterexample(){ + tree = 0; + root = 0; + } + Counterexample(RPFP *_tree, RPFP::Node *_root){ + tree = _tree; + root = _root; + } + ~Counterexample(){ + if(tree) delete tree; + } + void swap(Counterexample &other){ + std::swap(tree,other.tree); + std::swap(root,other.root); + } + void set(RPFP *_tree, RPFP::Node *_root){ + if(tree) delete tree; + tree = _tree; + root = _root; + } + void clear(){ + if(tree) delete tree; + tree = 0; + } + RPFP *get_tree() const {return tree;} + RPFP::Node *get_root() const {return root;} + private: + Counterexample &operator=(const Counterexample &); + Counterexample(const Counterexample &); + }; + + /** 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(Solver *old_solver) = 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 {}; + + /** Object thrown on incompleteness */ + struct Incompleteness {}; + }; +} + + +// Allow to hash on nodes and edges in deterministic way + +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::RPFP::Node *p) const { + return p->number; + } + }; +} + +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::RPFP::Edge *p) const { + return p->number; + } + }; +} + +// allow to walk sets of nodes without address dependency + +namespace std { + template <> + class less { + public: + bool operator()(Duality::RPFP::Node * const &s, Duality::RPFP::Node * const &t) const { + return s->number < t->number; // s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + +// #define LIMIT_STACK_WEIGHT 5 + + +namespace Duality { + /** Caching version of RPFP. Instead of asserting constraints, returns assumption literals */ + + class RPFP_caching : public RPFP { + public: + + /** appends assumption literals for edge to lits. if with_children is true, + includes that annotation of the edge's children. + */ + void AssertEdgeCache(Edge *e, std::vector &lits, bool with_children = false); + + /** appends assumption literals for node to lits */ + void AssertNodeCache(Node *, std::vector lits); + + /** check assumption lits, and return core */ + check_result CheckCore(const std::vector &assumps, std::vector &core); + + /** Clone another RPFP into this one, keeping a map */ + void Clone(RPFP *other); + + /** Get the clone of a node */ + Node *GetNodeClone(Node *other_node); + + /** Get the clone of an edge */ + Edge *GetEdgeClone(Edge *other_edge); + + /** Try to strengthen the parent of an edge */ + void GeneralizeCache(Edge *edge); + + /** Try to propagate some facts from children to parents of edge. + Return true if success. */ + bool PropagateCache(Edge *edge); + + /** Construct a caching RPFP using a LogicSolver */ + RPFP_caching(LogicSolver *_ls) : RPFP(_ls) {} + + /** Constraint an edge by its child's annotation. Return + assumption lits. */ + void ConstrainParentCache(Edge *parent, Node *child, std::vector &lits); + +#ifdef LIMIT_STACK_WEIGHT + virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); +#endif + + virtual ~RPFP_caching(){} + + protected: + hash_map AssumptionLits; + hash_map NodeCloneMap; + hash_map EdgeCloneMap; + std::vector alit_stack; + std::vector alit_stack_sizes; + + // to let us use one solver per edge + struct edge_solver { + hash_map AssumptionLits; + uptr slvr; + }; + hash_map edge_solvers; + +#ifdef LIMIT_STACK_WEIGHT + struct weight_counter { + int val; + weight_counter(){val = 0;} + void swap(weight_counter &other){ + std::swap(val,other.val); + } + }; + + struct big_stack_entry { + weight_counter weight_added; + std::vector new_alits; + std::vector alit_stack; + std::vector alit_stack_sizes; + }; + + std::vector new_alits; + weight_counter weight_added; + std::vector big_stack; +#endif + + + + void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = 0); + + void GreedyReduceCache(std::vector &assumps, std::vector &core); + + void FilterCore(std::vector &core, std::vector &full_core); + void ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits); + + virtual void slvr_add(const expr &e); + + virtual void slvr_pop(int i); + + virtual void slvr_push(); + + virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); + + virtual lbool ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false); + + virtual bool proof_core_contains(const expr &e); + + void GetTermTreeAssertionLiterals(TermTree *assumptions); + + void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); + + edge_solver &SolverForEdge(Edge *edge, bool models, bool axioms); + + public: + struct scoped_solver_for_edge { + solver *orig_slvr; + RPFP_caching *rpfp; + edge_solver *es; + scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false, bool axioms = false){ + rpfp = _rpfp; + orig_slvr = rpfp->ls->slvr; + es = &(rpfp->SolverForEdge(edge,models,axioms)); + rpfp->ls->slvr = es->slvr.get(); + rpfp->AssumptionLits.swap(es->AssumptionLits); + } + ~scoped_solver_for_edge(){ + rpfp->ls->slvr = orig_slvr; + rpfp->AssumptionLits.swap(es->AssumptionLits); + } + }; + + }; + +} diff --git a/src/duality/duality_profiling.cpp b/src/duality/duality_profiling.cpp index f409bfd5327..d5dac0811cb 100755 --- a/src/duality/duality_profiling.cpp +++ b/src/duality/duality_profiling.cpp @@ -1,134 +1,134 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - duality_profiling.cpp - -Abstract: - - collection performance information for duality - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ - - -#include -#include -#include -#include -#include - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include "duality_wrapper.h" -#include "iz3profiling.h" - -namespace Duality { - - void show_time(){ - output_time(std::cout,current_time()); - std::cout << "\n"; - } - - typedef std::map 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 = ⊤ - } - } 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 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 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; - } - profiling::print(os); // print the interpolation stats - } - - 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; - } -} +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + duality_profiling.cpp + +Abstract: + + collection performance information for duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + + +#include +#include +#include +#include +#include + +#ifdef _WINDOWS +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#endif + +#include "duality_wrapper.h" +#include "iz3profiling.h" + +namespace Duality { + + void show_time(){ + output_time(std::cout,current_time()); + std::cout << "\n"; + } + + typedef std::map 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 = ⊤ + } + } 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 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 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; + } + profiling::print(os); // print the interpolation stats + } + + 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; + } +} diff --git a/src/duality/duality_profiling.h b/src/duality/duality_profiling.h index ff70fae23e7..2b226440576 100755 --- a/src/duality/duality_profiling.h +++ b/src/duality/duality_profiling.h @@ -1,38 +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 - -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 - +/*++ +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 + +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 + diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 25c5f9c7dfa..ef37dd8a2a3 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -1,4146 +1,4228 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - duality_rpfp.h - -Abstract: - - implements relational post-fixedpoint problem - (RPFP) data structure. - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ - - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include -#include -#include -#include - - -#include "duality.h" -#include "duality_profiling.h" - -// TODO: do we need these? -#ifdef Z3OPS - -class Z3_subterm_truth { - public: - virtual bool eval(Z3_ast f) = 0; - ~Z3_subterm_truth(){} -}; - -Z3_subterm_truth *Z3_mk_subterm_truth(Z3_context ctx, Z3_model model); - -#endif - -#include - -// TODO: use something official for this -int debug_gauss = 0; - -namespace Duality { - - static char string_of_int_buffer[20]; - - const char *Z3User::string_of_int(int n){ - sprintf(string_of_int_buffer,"%d",n); - return string_of_int_buffer; - } - - RPFP::Term RPFP::SuffixVariable(const Term &t, int n) - { - std::string name = t.decl().name().str() + "_" + string_of_int(n); - return ctx.constant(name.c_str(), t.get_sort()); - } - - RPFP::Term RPFP::HideVariable(const Term &t, int n) - { - std::string name = std::string("@p_") + t.decl().name().str() + "_" + string_of_int(n); - return ctx.constant(name.c_str(), t.get_sort()); - } - - void RPFP::RedVars(Node *node, Term &b, std::vector &v) - { - int idx = node->number; - if(HornClauses) - b = ctx.bool_val(true); - else { - std::string name = std::string("@b_") + string_of_int(idx); - symbol sym = ctx.str_symbol(name.c_str()); - b = ctx.constant(sym,ctx.bool_sort()); - } - // ctx.constant(name.c_str(), ctx.bool_sort()); - v = node->Annotation.IndParams; - for(unsigned i = 0; i < v.size(); i++) - v[i] = SuffixVariable(v[i],idx); - } - - void Z3User::SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if(k == And || k == Or || k == Not || k == Implies || k == Iff){ - ops++; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - SummarizeRec(memo,lits,ops,t.arg(i)); - return; - } - } - lits.push_back(t); - } - - int RPFP::CumulativeDecisions(){ -#if 0 - std::string stats = Z3_statistics_to_string(ctx); - int pos = stats.find("decisions:"); - pos += 10; - int end = stats.find('\n',pos); - std::string val = stats.substr(pos,end-pos); - return atoi(val.c_str()); -#endif - return slvr().get_num_decisions(); - } - - - void Z3User::Summarize(const Term &t){ - hash_set memo; std::vector lits; int ops = 0; - SummarizeRec(memo, lits, ops, t); - std::cout << ops << ": "; - for(unsigned i = 0; i < lits.size(); i++){ - if(i > 0) std::cout << ", "; - std::cout << lits[i]; - } - } - - int Z3User::CountOperatorsRec(hash_set &memo, const Term &t){ - if(memo.find(t) != memo.end()) - return 0; - memo.insert(t); - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if(k == And || k == Or){ - int count = 1; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - count += CountOperatorsRec(memo,t.arg(i)); - return count; - } - return 0; - } - if(t.is_quantifier()){ - int nbv = t.get_quantifier_num_bound(); - return CountOperatorsRec(memo,t.body()) + 2 * nbv; // count 2 for each quantifier - } - return 0; - } - - int Z3User::CountOperators(const Term &t){ - hash_set memo; - return CountOperatorsRec(memo,t); - } - - - Z3User::Term Z3User::conjoin(const std::vector &args){ - return ctx.make(And,args); - } - - Z3User::Term Z3User::sum(const std::vector &args){ - return ctx.make(Plus,args); - } - - RPFP::Term RPFP::RedDualRela(Edge *e, std::vector &args, int idx){ - Node *child = e->Children[idx]; - Term b(ctx); - std::vector v; - RedVars(child, b, v); - for (unsigned i = 0; i < args.size(); i++) - { - if (eq(args[i].get_sort(),ctx.bool_sort())) - args[i] = ctx.make(Iff,args[i], v[i]); - else - args[i] = args[i] == v[i]; - } - return args.size() > 0 ? (b && conjoin(args)) : b; - } - - Z3User::Term Z3User::CloneQuantifier(const Term &t, const Term &new_body){ -#if 0 - Z3_context c = ctx; - Z3_ast a = t; - std::vector pats; - int num_pats = Z3_get_quantifier_num_patterns(c,a); - for(int i = 0; i < num_pats; i++) - pats.push_back(Z3_get_quantifier_pattern_ast(c,a,i)); - std::vector no_pats; - int num_no_pats = Z3_get_quantifier_num_patterns(c,a); - for(int i = 0; i < num_no_pats; i++) - no_pats.push_back(Z3_get_quantifier_no_pattern_ast(c,a,i)); - int bound = Z3_get_quantifier_num_bound(c,a); - std::vector sorts; - std::vector names; - for(int i = 0; i < bound; i++){ - sorts.push_back(Z3_get_quantifier_bound_sort(c,a,i)); - names.push_back(Z3_get_quantifier_bound_name(c,a,i)); - } - Z3_ast foo = Z3_mk_quantifier_ex(c, - Z3_is_quantifier_forall(c,a), - Z3_get_quantifier_weight(c,a), - 0, - 0, - num_pats, - &pats[0], - num_no_pats, - &no_pats[0], - bound, - &sorts[0], - &names[0], - new_body); - return expr(ctx,foo); -#endif - return clone_quantifier(t,new_body); - } - - - RPFP::Term RPFP::LocalizeRec(Edge *e, hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - hash_map::iterator it = e->varMap.find(t); - if (it != e->varMap.end()){ - res = it->second; - return res; - } - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(LocalizeRec(e, memo, t.arg(i))); - hash_map::iterator rit = e->relMap.find(f); - if(rit != e->relMap.end()) - res = RedDualRela(e,args,(rit->second)); - else { - if (args.size() == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)) - { - res = HideVariable(t,e->number); - } - else - { - res = f(args.size(),&args[0]); - } - } - } - else if (t.is_quantifier()) - { - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = LocalizeRec(e,memo,pats[i]); - Term body = LocalizeRec(e,memo,t.body()); - res = clone_quantifier(t, body, pats); - } - else res = t; - return res; - } - - void RPFP::SetEdgeMaps(Edge *e){ - timer_start("SetEdgeMaps"); - e->relMap.clear(); - e->varMap.clear(); - for(unsigned i = 0; i < e->F.RelParams.size(); i++){ - e->relMap[e->F.RelParams[i]] = i; - } - Term b(ctx); - std::vector v; - RedVars(e->Parent, b, v); - for(unsigned i = 0; i < e->F.IndParams.size(); i++){ - // func_decl parentParam = e->Parent.Annotation.IndParams[i]; - expr oldname = e->F.IndParams[i]; - expr newname = v[i]; - e->varMap[oldname] = newname; - } - timer_stop("SetEdgeMaps"); - - } - - - RPFP::Term RPFP::Localize(Edge *e, const Term &t){ - timer_start("Localize"); - hash_map memo; - if(e->F.IndParams.size() > 0 && e->varMap.empty()) - SetEdgeMaps(e); // TODO: why is this needed? - Term res = LocalizeRec(e,memo,t); - timer_stop("Localize"); - return res; - } - - - RPFP::Term RPFP::ReducedDualEdge(Edge *e) - { - SetEdgeMaps(e); - timer_start("RedVars"); - Term b(ctx); - std::vector v; - RedVars(e->Parent, b, v); - timer_stop("RedVars"); - // ast_to_track = (ast)b; - return implies(b, Localize(e, e->F.Formula)); - } - - TermTree *RPFP::ToTermTree(Node *root, Node *skip_descendant) - { - if(skip_descendant && root == skip_descendant) - return new TermTree(ctx.bool_val(true)); - Edge *e = root->Outgoing; - if(!e) return new TermTree(ctx.bool_val(true), std::vector()); - std::vector children(e->Children.size()); - for(unsigned i = 0; i < children.size(); i++) - children[i] = ToTermTree(e->Children[i],skip_descendant); - // Term top = ReducedDualEdge(e); - Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; - TermTree *res = new TermTree(top, children); - for(unsigned i = 0; i < e->constraints.size(); i++) - res->addTerm(e->constraints[i]); - return res; - } - - TermTree *RPFP::GetGoalTree(Node *root){ - std::vector children(1); - children[0] = ToGoalTree(root); - return new TermTree(ctx.bool_val(false),children); - } - - TermTree *RPFP::ToGoalTree(Node *root) - { - Term b(ctx); - std::vector v; - RedVars(root, b, v); - Term goal = root->Name(v); - Edge *e = root->Outgoing; - if(!e) return new TermTree(goal, std::vector()); - std::vector children(e->Children.size()); - for(unsigned i = 0; i < children.size(); i++) - children[i] = ToGoalTree(e->Children[i]); - // Term top = ReducedDualEdge(e); - return new TermTree(goal, children); - } - - Z3User::Term Z3User::SubstRec(hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, t.arg(i))); - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()) - { - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstRec(memo,pats[i]); - Term body = SubstRec(memo,t.body()); - res = clone_quantifier(t, body, pats); - } - // res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - Z3User::Term Z3User::SubstRec(hash_map &memo, hash_map &map, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, map, t.arg(i))); - hash_map::iterator it = map.find(f); - if(it != map.end()) - f = it->second; - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()) - { - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstRec(memo, map, pats[i]); - Term body = SubstRec(memo, map, t.body()); - res = clone_quantifier(t, body, pats); - } - // res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - Z3User::Term Z3User::ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(ExtractStores(memo, t.arg(i),cnstrs,renaming)); - res = f(args.size(),&args[0]); - if(f.get_decl_kind() == Store){ - func_decl fresh = ctx.fresh_func_decl("@arr", res.get_sort()); - expr y = fresh(); - expr equ = ctx.make(Equal,y,res); - cnstrs.push_back(equ); - renaming[y] = res; - res = y; - } - } - else res = t; - return res; - } - - - bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ - if(!(lit.is_quantifier() && IsClosedFormula(lit))){ - if(!lit.is_app()) - return false; - decl_kind k = lit.decl().get_decl_kind(); - if(k == Not){ - if(IsLiteral(lit.arg(0),atom,val)){ - val = eq(val,ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); - return true; - } - return false; - } - if(k == And || k == Or || k == Iff || k == Implies) - return false; - } - atom = lit; - val = ctx.bool_val(true); - return true; - } - - expr Z3User::Negate(const expr &f){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - return f.arg(0); - else if(eq(f,ctx.bool_val(true))) - return ctx.bool_val(false); - else if(eq(f,ctx.bool_val(false))) - return ctx.bool_val(true); - return !f; - } - - expr Z3User::ReduceAndOr(const std::vector &args, bool is_and, std::vector &res){ - for(unsigned i = 0; i < args.size(); i++) - if(!eq(args[i],ctx.bool_val(is_and))){ - if(eq(args[i],ctx.bool_val(!is_and))) - return ctx.bool_val(!is_and); - res.push_back(args[i]); - } - return expr(); - } - - expr Z3User::FinishAndOr(const std::vector &args, bool is_and){ - if(args.size() == 0) - return ctx.bool_val(is_and); - if(args.size() == 1) - return args[0]; - return ctx.make(is_and ? And : Or,args); - } - - expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ - std::vector sargs; - expr res = ReduceAndOr(args,is_and,sargs); - if(!res.null()) return res; - return FinishAndOr(sargs,is_and); - } - - expr Z3User::PullCommonFactors(std::vector &args, bool is_and){ - - // first check if there's anything to do... - if(args.size() < 2) - return FinishAndOr(args,is_and); - for(unsigned i = 0; i < args.size(); i++){ - const expr &a = args[i]; - if(!(a.is_app() && a.decl().get_decl_kind() == (is_and ? Or : And))) - return FinishAndOr(args,is_and); - } - std::vector common; - for(unsigned i = 0; i < args.size(); i++){ - unsigned n = args[i].num_args(); - std::vector v(n),w; - for(unsigned j = 0; j < n; j++) - v[j] = args[i].arg(j); - std::less comp; - std::sort(v.begin(),v.end(),comp); - if(i == 0) - common.swap(v); - else { - std::set_intersection(common.begin(),common.end(),v.begin(),v.end(),std::inserter(w,w.begin()),comp); - common.swap(w); - } - } - if(common.empty()) - return FinishAndOr(args,is_and); - std::set common_set(common.begin(),common.end()); - for(unsigned i = 0; i < args.size(); i++){ - unsigned n = args[i].num_args(); - std::vector lits; - for(unsigned j = 0; j < n; j++){ - const expr b = args[i].arg(j); - if(common_set.find(b) == common_set.end()) - lits.push_back(b); - } - args[i] = SimplifyAndOr(lits,!is_and); - } - common.push_back(SimplifyAndOr(args,is_and)); - return SimplifyAndOr(common,!is_and); - } - - expr Z3User::ReallySimplifyAndOr(const std::vector &args, bool is_and){ - std::vector sargs; - expr res = ReduceAndOr(args,is_and,sargs); - if(!res.null()) return res; - return PullCommonFactors(sargs,is_and); - } - - Z3User::Term Z3User::SubstAtomTriv(const expr &foo, const expr &atom, const expr &val){ - if(eq(foo,atom)) - return val; - else if(foo.is_app() && foo.decl().get_decl_kind() == Not && eq(foo.arg(0),atom)) - return Negate(val); - else - return foo; - } - - Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ - if(t.is_quantifier_forall() && body.is_app() && body.decl().get_decl_kind() == And){ - int nargs = body.num_args(); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = CloneQuantAndSimp(t, body.arg(i)); - return ctx.make(And,args); - } - if(!t.is_quantifier_forall() && body.is_app() && body.decl().get_decl_kind() == Or){ - int nargs = body.num_args(); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = CloneQuantAndSimp(t, body.arg(i)); - return ctx.make(Or,args); - } - return clone_quantifier(t,body); - } - - - Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val){ - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()){ - func_decl f = t.decl(); - decl_kind k = f.get_decl_kind(); - - // TODO: recur here, but how much? We don't want to be quadractic in formula size - - if(k == And || k == Or){ - int nargs = t.num_args(); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = SubstAtom(memo,t.arg(i),atom,val); - res = ReallySimplifyAndOr(args, k==And); - return res; - } - } - else if(t.is_quantifier() && atom.is_quantifier()){ - if(eq(t,atom)) - res = val; - else - res = clone_quantifier(t,SubstAtom(memo,t.body(),atom,val)); - return res; - } - res = SubstAtomTriv(t,atom,val); - return res; - } - - void Z3User::RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo){ - for(unsigned i = 0; i < args.size(); i++){ - const expr &lit = args[i]; - expr atom, val; - if(IsLiteral(lit,atom,val)){ - if(atom.is_app() && atom.decl().get_decl_kind() == Equal) - if(pol ? eq(val,ctx.bool_val(true)) : eq(val,ctx.bool_val(false))){ - expr lhs = atom.arg(0), rhs = atom.arg(1); - if(lhs.is_numeral()) - std::swap(lhs,rhs); - if(rhs.is_numeral() && lhs.is_app()){ - for(unsigned j = 0; j < args.size(); j++) - if(j != i){ - smemo.clear(); - smemo[lhs] = rhs; - args[j] = SubstRec(smemo,args[j]); - } - } - } - for(unsigned j = 0; j < args.size(); j++) - if(j != i){ - smemo.clear(); - args[j] = SubstAtom(smemo,args[j],atom,pol ? val : !val); - } - } - } - } - - - Z3User::Term Z3User::RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(RemoveRedundancyRec(memo, smemo, t.arg(i))); - - decl_kind k = f.get_decl_kind(); - if(k == And){ - RemoveRedundancyOp(true,args,smemo); - res = ReallySimplifyAndOr(args, true); - } - else if(k == Or){ - RemoveRedundancyOp(false,args,smemo); - res = ReallySimplifyAndOr(args, false); - } - else { - if(k == Equal && args[0].get_id() > args[1].get_id()) - std::swap(args[0],args[1]); - res = f(args.size(),&args[0]); - } - } - else if (t.is_quantifier()) - { - Term body = RemoveRedundancyRec(memo,smemo,t.body()); - res = clone_quantifier(t, body); - } - else res = t; - return res; - } - - Z3User::Term Z3User::RemoveRedundancy(const Term &t){ - hash_map memo; - hash_map smemo; - return RemoveRedundancyRec(memo,smemo,t); - } - - Z3User::Term Z3User::AdjustQuantifiers(const Term &t) - { - if(t.is_quantifier() || (t.is_app() && t.has_quantifiers())) - return t.qe_lite(); - return t; - } - - Z3User::Term Z3User::IneqToEqRec(hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(IneqToEqRec(memo, t.arg(i))); - - decl_kind k = f.get_decl_kind(); - if(k == And){ - for(int i = 0; i < nargs-1; i++){ - if((args[i].is_app() && args[i].decl().get_decl_kind() == Geq && - args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Leq) - || - (args[i].is_app() && args[i].decl().get_decl_kind() == Leq && - args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Geq)) - if(eq(args[i].arg(0),args[i+1].arg(0)) && eq(args[i].arg(1),args[i+1].arg(1))){ - args[i] = ctx.make(Equal,args[i].arg(0),args[i].arg(1)); - args[i+1] = ctx.bool_val(true); - } - } - } - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()) - { - Term body = IneqToEqRec(memo,t.body()); - res = clone_quantifier(t, body); - } - else res = t; - return res; - } - - Z3User::Term Z3User::IneqToEq(const Term &t){ - hash_map memo; - return IneqToEqRec(memo,t); - } - - Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ - std::string name = std::string("@q_") + t.decl().name().str() + "_" + string_of_int(number); - res = ctx.constant(name.c_str(), t.get_sort()); - return res; - } - for(int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, t.arg(i))); - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - RPFP::Term RPFP::SubstParams(const std::vector &from, - const std::vector &to, const Term &t){ - hash_map memo; - bool some_diff = false; - for(unsigned i = 0; i < from.size(); i++) - if(i < to.size() && !eq(from[i],to[i])){ - memo[from[i]] = to[i]; - some_diff = true; - } - return some_diff ? SubstRec(memo,t) : t; - } - - RPFP::Term RPFP::SubstParamsNoCapture(const std::vector &from, - const std::vector &to, const Term &t){ - hash_map memo; - bool some_diff = false; - for(unsigned i = 0; i < from.size(); i++) - if(i < to.size() && !eq(from[i],to[i])){ - memo[from[i]] = to[i]; - // if the new param is not being mapped to anything else, we need to rename it to prevent capture - // note, if the new param *is* mapped later in the list, it will override this substitution - const expr &w = to[i]; - if(memo.find(w) == memo.end()){ - std::string old_name = w.decl().name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); - expr y = fresh(); - memo[w] = y; - } - some_diff = true; - } - return some_diff ? SubstRec(memo,t) : t; - } - - - - RPFP::Transformer RPFP::Fuse(const std::vector &trs){ - assert(!trs.empty()); - const std::vector ¶ms = trs[0]->IndParams; - std::vector fmlas(trs.size()); - fmlas[0] = trs[0]->Formula; - for(unsigned i = 1; i < trs.size(); i++) - fmlas[i] = SubstParamsNoCapture(trs[i]->IndParams,params,trs[i]->Formula); - std::vector rel_params = trs[0]->RelParams; - for(unsigned i = 1; i < trs.size(); i++){ - const std::vector ¶ms2 = trs[i]->RelParams; - hash_map map; - for(unsigned j = 0; j < params2.size(); j++){ - func_decl rel = RenumberPred(params2[j],rel_params.size()); - rel_params.push_back(rel); - map[params2[j]] = rel; - } - hash_map memo; - fmlas[i] = SubstRec(memo,map,fmlas[i]); - } - return Transformer(rel_params,params,ctx.make(Or,fmlas),trs[0]->owner); - } - - - void Z3User::Strengthen(Term &x, const Term &y) - { - if (eq(x,ctx.bool_val(true))) - x = y; - else - x = x && y; - } - - void RPFP::SetAnnotation(Node *root, const expr &t){ - hash_map memo; - Term b; - std::vector v; - RedVars(root, b, v); - memo[b] = ctx.bool_val(true); - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = root->Annotation.IndParams[i]; - Term annot = SubstRec(memo, t); - // Strengthen(ref root.Annotation.Formula, annot); - root->Annotation.Formula = annot; - } - - void RPFP::DecodeTree(Node *root, TermTree *interp, int persist) - { - std::vector &ic = interp->getChildren(); - if (ic.size() > 0) - { - std::vector &nc = root->Outgoing->Children; - for (unsigned i = 0; i < nc.size(); i++) - DecodeTree(nc[i], ic[i], persist); - } - SetAnnotation(root,interp->getTerm()); -#if 0 - if(persist != 0) - Z3_persist_ast(ctx,root->Annotation.Formula,persist); -#endif - } - - RPFP::Term RPFP::GetUpperBound(Node *n) - { - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned int i = 0; i < v.size(); i++) - memo[n->Bound.IndParams[i]] = v[i]; - Term cnst = SubstRec(memo, n->Bound.Formula); - return b && !cnst; - } - - RPFP::Term RPFP::GetAnnotation(Node *n) - { - if(eq(n->Annotation.Formula,ctx.bool_val(true))) - return n->Annotation.Formula; - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[n->Annotation.IndParams[i]] = v[i]; - Term cnst = SubstRec(memo, n->Annotation.Formula); - return !b || cnst; - } - - RPFP::Term RPFP::GetUnderapprox(Node *n) - { - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[n->Underapprox.IndParams[i]] = v[i]; - Term cnst = SubstRecHide(memo, n->Underapprox.Formula, n->number); - return !b || cnst; - } - - TermTree *RPFP::AddUpperBound(Node *root, TermTree *t) - { - Term f = !((ast)(root->dual)) ? ctx.bool_val(true) : root->dual; - std::vector c(1); c[0] = t; - return new TermTree(f, c); - } - -#if 0 - void RPFP::WriteInterps(System.IO.StreamWriter f, TermTree t) - { - foreach (var c in t.getChildren()) - WriteInterps(f, c); - f.WriteLine(t.getTerm()); - } -#endif - - - expr RPFP::GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox) - { - if (e->dual.null()) { - timer_start("ReducedDualEdge"); - e->dual = ReducedDualEdge(e); - timer_stop("ReducedDualEdge"); - timer_start("getting children"); - if(underapprox){ - std::vector cus(e->Children.size()); - for(unsigned i = 0; i < e->Children.size(); i++) - cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); - expr cnst = conjoin(cus); - e->dual = e->dual && cnst; - } - timer_stop("getting children"); - timer_start("Persisting"); - std::list::reverse_iterator it = stack.rbegin(); - for(int i = 0; i < persist && it != stack.rend(); i++) - it++; - if(it != stack.rend()) - it->edges.push_back(e); - timer_stop("Persisting"); - //Console.WriteLine("{0}", cnst); - } - return e->dual; - } - - /** 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. The constraint formula - * will survive "persist" pops of the context. If you plan - * to reassert the edge after popping the context once, - * you can save time re-constructing the formula by setting - * "persist" to one. If you set "persist" too high, however, - * you could have a memory leak. - * - * The flag "with children" causes the annotations of the children - * to be asserted. The flag underapprox causes the underapproximations - * of the children to be asserted *conditionally*. See Check() on - * how to actually enforce these constraints. - * - */ - - void RPFP::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) - { - if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); - timer_start("solver add"); - slvr_add(e->dual); - timer_stop("solver add"); - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - ConstrainParent(e,e->Children[i]); - } - - -#ifdef LIMIT_STACK_WEIGHT - void RPFP_caching::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) - { - unsigned old_new_alits = new_alits.size(); - if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); - timer_start("solver add"); - slvr_add(e->dual); - timer_stop("solver add"); - if(old_new_alits < new_alits.size()) - weight_added.val++; - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - ConstrainParent(e,e->Children[i]); - } -#endif - - // caching verion of above - void RPFP_caching::AssertEdgeCache(Edge *e, std::vector &lits, bool with_children){ - if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, 0, with_children, false); - GetAssumptionLits(fmla,lits); - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - ConstrainParentCache(e,e->Children[i],lits); - } - - void RPFP::slvr_add(const expr &e){ - slvr().add(e); - } - - void RPFP_caching::slvr_add(const expr &e){ - GetAssumptionLits(e,alit_stack); - } - - void RPFP::slvr_pop(int i){ - slvr().pop(i); - } - - void RPFP::slvr_push(){ - slvr().push(); - } - - void RPFP_caching::slvr_pop(int i){ - for(int j = 0; j < i; j++){ -#ifdef LIMIT_STACK_WEIGHT - if(alit_stack_sizes.empty()){ - if(big_stack.empty()) - throw "stack underflow"; - for(unsigned k = 0; k < new_alits.size(); k++){ - if(AssumptionLits.find(new_alits[k]) == AssumptionLits.end()) - throw "foo!"; - AssumptionLits.erase(new_alits[k]); - } - big_stack_entry &bsb = big_stack.back(); - bsb.alit_stack_sizes.swap(alit_stack_sizes); - bsb.alit_stack.swap(alit_stack); - bsb.new_alits.swap(new_alits); - bsb.weight_added.swap(weight_added); - big_stack.pop_back(); - slvr().pop(1); - continue; - } -#endif - alit_stack.resize(alit_stack_sizes.back()); - alit_stack_sizes.pop_back(); - } - } - - void RPFP_caching::slvr_push(){ -#ifdef LIMIT_STACK_WEIGHT - if(weight_added.val > LIMIT_STACK_WEIGHT){ - big_stack.resize(big_stack.size()+1); - big_stack_entry &bsb = big_stack.back(); - bsb.alit_stack_sizes.swap(alit_stack_sizes); - bsb.alit_stack.swap(alit_stack); - bsb.new_alits.swap(new_alits); - bsb.weight_added.swap(weight_added); - slvr().push(); - for(unsigned i = 0; i < bsb.alit_stack.size(); i++) - slvr().add(bsb.alit_stack[i]); - return; - } -#endif - alit_stack_sizes.push_back(alit_stack.size()); - } - - check_result RPFP::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - return slvr().check(n, assumptions, core_size, core); - } - - check_result RPFP_caching::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - unsigned oldsiz = alit_stack.size(); - if(n && assumptions) - std::copy(assumptions,assumptions+n,std::inserter(alit_stack,alit_stack.end())); - check_result res; - if(core_size && core){ - std::vector full_core(alit_stack.size()), core1(n); - std::copy(assumptions,assumptions+n,core1.begin()); - res = slvr().check(alit_stack.size(), &alit_stack[0], core_size, &full_core[0]); - full_core.resize(*core_size); - if(res == unsat){ - FilterCore(core1,full_core); - *core_size = core1.size(); - std::copy(core1.begin(),core1.end(),core); - } - } - else - res = slvr().check(alit_stack.size(), &alit_stack[0]); - alit_stack.resize(oldsiz); - return res; - } - - lbool RPFP::ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals, - bool weak){ - return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); - } - - lbool RPFP_caching::ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals, - bool weak){ - GetTermTreeAssertionLiterals(assumptions); - return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); - } - - void RPFP_caching::GetTermTreeAssertionLiteralsRec(TermTree *assumptions){ - std::vector alits; - hash_map map; - GetAssumptionLits(assumptions->getTerm(),alits,&map); - std::vector &ts = assumptions->getTerms(); - for(unsigned i = 0; i < ts.size(); i++) - GetAssumptionLits(ts[i],alits,&map); - assumptions->setTerm(ctx.bool_val(true)); - ts = alits; - for(unsigned i = 0; i < alits.size(); i++) - ts.push_back(ctx.make(Implies,alits[i],map[alits[i]])); - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - GetTermTreeAssertionLiterals(assumptions->getChildren()[i]); - return; - } - - void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions){ - // optimize binary case - if(assumptions->getChildren().size() == 1 - && assumptions->getChildren()[0]->getChildren().size() == 0){ - hash_map map; - TermTree *child = assumptions->getChildren()[0]; - std::vector dummy; - GetAssumptionLits(child->getTerm(),dummy,&map); - std::vector &ts = child->getTerms(); - for(unsigned i = 0; i < ts.size(); i++) - GetAssumptionLits(ts[i],dummy,&map); - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - if(!proof_core){ // save the proof core for later use - proof_core = new hash_set; - for(unsigned i = 0; i < assumps.size(); i++) - proof_core->insert(assumps[i]); - } - std::vector *cnsts[2] = {&child->getTerms(),&assumptions->getTerms()}; - for(unsigned i = 0; i < assumps.size(); i++){ - expr &ass = assumps[i]; - expr alit = (ass.is_app() && ass.decl().get_decl_kind() == Implies) ? ass.arg(0) : ass; - bool isA = map.find(alit) != map.end(); - cnsts[isA ? 0 : 1]->push_back(ass); - } - } - else - GetTermTreeAssertionLiteralsRec(assumptions); - } - - void RPFP::AddToProofCore(hash_set &core){ - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - core.insert(assumps[i]); - } - - void RPFP::ComputeProofCore(){ - if(!proof_core){ - proof_core = new hash_set; - AddToProofCore(*proof_core); - } - } - - - void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map){ - std::vector conjs; - CollectConjuncts(fmla,conjs); - for(unsigned i = 0; i < conjs.size(); i++){ - const expr &conj = conjs[i]; - std::pair foo(conj,expr(ctx)); - std::pair::iterator, bool> bar = AssumptionLits.insert(foo); - Term &res = bar.first->second; - if(bar.second){ - func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); - res = pred(); -#ifdef LIMIT_STACK_WEIGHT - new_alits.push_back(conj); -#endif - slvr().add(ctx.make(Implies,res,conj)); - // std::cout << res << ": " << conj << "\n"; - } - if(opt_map) - (*opt_map)[res] = conj; - lits.push_back(res); - } - } - - void RPFP::ConstrainParent(Edge *parent, Node *child){ - ConstrainEdgeLocalized(parent,GetAnnotation(child)); - } - - void RPFP_caching::ConstrainParentCache(Edge *parent, Node *child, std::vector &lits){ - ConstrainEdgeLocalizedCache(parent,GetAnnotation(child),lits); - } - - - /** For incremental solving, asserts the negation of the upper bound associated - * with a node. - * */ - - void RPFP::AssertNode(Node *n) - { - if (n->dual.null()) - { - n->dual = GetUpperBound(n); - stack.back().nodes.push_back(n); - slvr_add(n->dual); - } - } - - // caching version of above - void RPFP_caching::AssertNodeCache(Node *n, std::vector lits){ - if (n->dual.null()) - { - n->dual = GetUpperBound(n); - stack.back().nodes.push_back(n); - GetAssumptionLits(n->dual,lits); - } - } - - /** Clone another RPFP into this one, keeping a map */ - void RPFP_caching::Clone(RPFP *other){ -#if 0 - for(unsigned i = 0; i < other->nodes.size(); i++) - NodeCloneMap[other->nodes[i]] = CloneNode(other->nodes[i]); -#endif - for(unsigned i = 0; i < other->edges.size(); i++){ - Edge *edge = other->edges[i]; - Node *parent = CloneNode(edge->Parent); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++) - // cs.push_back(NodeCloneMap[edge->Children[j]]); - cs.push_back(CloneNode(edge->Children[j])); - EdgeCloneMap[edge] = CreateEdge(parent,edge->F,cs); - } - } - - /** Get the clone of a node */ - RPFP::Node *RPFP_caching::GetNodeClone(Node *other_node){ - return NodeCloneMap[other_node]; - } - - /** Get the clone of an edge */ - RPFP::Edge *RPFP_caching::GetEdgeClone(Edge *other_edge){ - return EdgeCloneMap[other_edge]; - } - - /** check assumption lits, and return core */ - check_result RPFP_caching::CheckCore(const std::vector &assumps, std::vector &core){ - core.resize(assumps.size()); - unsigned core_size; - check_result res = slvr().check(assumps.size(),(expr *)&assumps[0],&core_size,&core[0]); - if(res == unsat) - core.resize(core_size); - else - core.clear(); - return res; - } - - - /** Assert a constraint on an edge in the SMT context. - */ - - void RPFP::ConstrainEdge(Edge *e, const Term &t) - { - Term tl = Localize(e, t); - ConstrainEdgeLocalized(e,tl); - } - - void RPFP::ConstrainEdgeLocalized(Edge *e, const Term &tl) - { - e->constraints.push_back(tl); - stack.back().constraints.push_back(std::pair(e,tl)); - slvr_add(tl); - } - - void RPFP_caching::ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits) - { - e->constraints.push_back(tl); - stack.back().constraints.push_back(std::pair(e,tl)); - GetAssumptionLits(tl,lits); - } - - - /** Declare a constant in the background theory. */ - - void RPFP::DeclareConstant(const FuncDecl &f){ - ls->declare_constant(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 RPFP::AssertAxiom(const Term &t) - { - ls->assert_axiom(t); - axioms.push_back(t); // for printing only - } - -#if 0 - /** Do not call this. */ - - void RPFP::RemoveAxiom(const Term &t) - { - slvr().RemoveInterpolationAxiom(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 RPFP::Solve(Node *root, int persist) - { - timer_start("Solve"); - TermTree *tree = GetConstraintTree(root); - TermTree *interpolant = NULL; - TermTree *goals = NULL; - if(ls->need_goals) - goals = GetGoalTree(root); - ClearProofCore(); - - // if (dualModel != null) dualModel.Dispose(); - // if (dualLabels != null) dualLabels.Dispose(); - - timer_start("interpolate_tree"); - lbool res = ls_interpolate_tree(tree, interpolant, dualModel,goals,true); - timer_stop("interpolate_tree"); - if (res == l_false) - { - DecodeTree(root, interpolant->getChildren()[0], persist); - delete interpolant; - } - - delete tree; - if(goals) - delete goals; - - timer_stop("Solve"); - return res; - } - - void RPFP::CollapseTermTreeRec(TermTree *root, TermTree *node){ - root->addTerm(node->getTerm()); - std::vector &cnsts = node->getTerms(); - for(unsigned i = 0; i < cnsts.size(); i++) - root->addTerm(cnsts[i]); - std::vector &chs = node->getChildren(); - for(unsigned i = 0; i < chs.size(); i++){ - CollapseTermTreeRec(root,chs[i]); - } - } - - TermTree *RPFP::CollapseTermTree(TermTree *node){ - std::vector &chs = node->getChildren(); - for(unsigned i = 0; i < chs.size(); i++) - CollapseTermTreeRec(node,chs[i]); - for(unsigned i = 0; i < chs.size(); i++) - delete chs[i]; - chs.clear(); - return node; - } - - lbool RPFP::SolveSingleNode(Node *root, Node *node) - { - timer_start("Solve"); - TermTree *tree = CollapseTermTree(GetConstraintTree(root,node)); - tree->getChildren().push_back(CollapseTermTree(ToTermTree(node))); - TermTree *interpolant = NULL; - ClearProofCore(); - - timer_start("interpolate_tree"); - lbool res = ls_interpolate_tree(tree, interpolant, dualModel,0,true); - timer_stop("interpolate_tree"); - if (res == l_false) - { - DecodeTree(node, interpolant->getChildren()[0], 0); - delete interpolant; - } - - delete tree; - timer_stop("Solve"); - return res; - } - - /** Get the constraint tree (but don't solve it) */ - - TermTree *RPFP::GetConstraintTree(Node *root, Node *skip_descendant) - { - return AddUpperBound(root, ToTermTree(root,skip_descendant)); - } - - /** Dispose of the dual model (counterexample) if there is one. */ - - void RPFP::DisposeDualModel() - { - dualModel = model(ctx,NULL); - } - - RPFP::Term RPFP::UnderapproxFlag(Node *n){ - expr flag = ctx.constant((std::string("@under") + string_of_int(n->number)).c_str(), ctx.bool_sort()); - underapprox_flag_rev[flag] = n; - return flag; - } - - RPFP::Node *RPFP::UnderapproxFlagRev(const Term &flag){ - return underapprox_flag_rev[flag]; - } - - /** Check satisfiability of asserted edges and nodes. Same functionality as - * Solve, except no primal solution (interpolant) is generated in the unsat case. - * The vector underapproxes gives the set of node underapproximations to be enforced - * (assuming these were conditionally asserted by AssertEdge). - * - */ - - check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core ) - { - timer_start("Check"); - ClearProofCore(); - // if (dualModel != null) dualModel.Dispose(); - check_result res; - if(!underapproxes.size()) - res = slvr_check(); - else { - std::vector us(underapproxes.size()); - for(unsigned i = 0; i < underapproxes.size(); i++) - us[i] = UnderapproxFlag(underapproxes[i]); - slvr_check(); // TODO: no idea why I need to do this - if(underapprox_core){ - std::vector unsat_core(us.size()); - unsigned core_size = 0; - res = slvr_check(us.size(),&us[0],&core_size,&unsat_core[0]); - underapprox_core->resize(core_size); - for(unsigned i = 0; i < core_size; i++) - (*underapprox_core)[i] = UnderapproxFlagRev(unsat_core[i]); - } - else { - res = slvr_check(us.size(),&us[0]); - bool dump = false; - if(dump){ - std::vector cnsts; - // cnsts.push_back(axioms[0]); - cnsts.push_back(root->dual); - cnsts.push_back(root->Outgoing->dual); - ls->write_interpolation_problem("temp.smt",cnsts,std::vector()); - } - } - // check_result temp = slvr_check(); - } - dualModel = slvr().get_model(); - timer_stop("Check"); - return res; - } - - check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ - // check_result temp1 = slvr_check(); // no idea why I need to do this - ClearProofCore(); - check_result res = slvr_check(assumps.size(),&assumps[0]); - model mod = slvr().get_model(); - if(!mod.null()) - dualModel = mod;; - return res; - } - - /** Determines the value in the counterexample of a symbol occuring in the transformer formula of - * a given edge. */ - - RPFP::Term RPFP::Eval(Edge *e, Term t) - { - Term tl = Localize(e, t); - return dualModel.eval(tl); - } - - /** 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 RPFP::Empty(Node *p) - { - Term b; std::vector v; - RedVars(p, b, v); - // dualModel.show_internals(); - // std::cout << "b: " << b << std::endl; - expr bv = dualModel.eval(b); - // std::cout << "bv: " << bv << std::endl; - bool res = !eq(bv,ctx.bool_val(true)); - return res; - } - - RPFP::Term RPFP::EvalNode(Node *p) - { - Term b; std::vector v; - RedVars(p, b, v); - std::vector args; - for(unsigned i = 0; i < v.size(); i++) - args.push_back(dualModel.eval(v[i])); - return (p->Name)(args); - } - - void RPFP::EvalArrayTerm(const RPFP::Term &t, ArrayValue &res){ - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if(k == AsArray){ - func_decl fd = t.decl().get_func_decl_parameter(0); - func_interp r = dualModel.get_func_interp(fd); - int num = r.num_entries(); - res.defined = true; - for(int i = 0; i < num; i++){ - expr arg = r.get_arg(i,0); - expr value = r.get_value(i); - res.entries[arg] = value; - } - res.def_val = r.else_value(); - return; - } - else if(k == Store){ - EvalArrayTerm(t.arg(0),res); - if(!res.defined)return; - expr addr = t.arg(1); - expr val = t.arg(2); - if(addr.is_numeral() && val.is_numeral()){ - if(eq(val,res.def_val)) - res.entries.erase(addr); - else - res.entries[addr] = val; - } - else - res.defined = false; - return; - } - } - res.defined = false; - } - - int eae_count = 0; - - RPFP::Term RPFP::EvalArrayEquality(const RPFP::Term &f){ - ArrayValue lhs,rhs; - eae_count++; - EvalArrayTerm(f.arg(0),lhs); - EvalArrayTerm(f.arg(1),rhs); - if(lhs.defined && rhs.defined){ - if(eq(lhs.def_val,rhs.def_val)) - if(lhs.entries == rhs.entries) - return ctx.bool_val(true); - return ctx.bool_val(false); - } - return f; - } - - /** Compute truth values of all boolean subterms in current model. - Assumes formulas has been simplified by Z3, so only boolean ops - ands and, or, not. Returns result in memo. - */ - - int RPFP::SubtermTruth(hash_map &memo, const Term &f){ - if(memo.find(f) != memo.end()){ - return memo[f]; - } - int res; - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies){ - res = SubtermTruth(memo,!f.arg(0) || f.arg(1)); - goto done; - } - if(k == And) { - res = 1; - for(int i = 0; i < nargs; i++){ - int ar = SubtermTruth(memo,f.arg(i)); - if(ar == 0){ - res = 0; - goto done; - } - if(ar == 2)res = 2; - } - goto done; - } - else if(k == Or) { - res = 0; - for(int i = 0; i < nargs; i++){ - int ar = SubtermTruth(memo,f.arg(i)); - if(ar == 1){ - res = 1; - goto done; - } - if(ar == 2)res = 2; - } - goto done; - } - else if(k == Not) { - int ar = SubtermTruth(memo,f.arg(0)); - res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); - goto done; - } - } - { - bool pos; std::vector names; - if(f.is_label(pos,names)){ - res = SubtermTruth(memo,f.arg(0)); - goto done; - } - } - { - expr bv = dualModel.eval(f); - if(bv.is_app() && bv.decl().get_decl_kind() == Equal && - bv.arg(0).is_array()){ - bv = EvalArrayEquality(bv); - } - // Hack!!!! array equalities can occur negatively! - if(bv.is_app() && bv.decl().get_decl_kind() == Not && - bv.arg(0).decl().get_decl_kind() == Equal && - bv.arg(0).arg(0).is_array()){ - bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); - } - if(eq(bv,ctx.bool_val(true))) - res = 1; - else if(eq(bv,ctx.bool_val(false))) - res = 0; - else - res = 2; - } - done: - memo[f] = res; - return res; - } - - int RPFP::EvalTruth(hash_map &memo, Edge *e, const Term &f){ - Term tl = Localize(e, f); - return SubtermTruth(memo,tl); - } - - /** Compute truth values of all boolean subterms in current model. - Assumes formulas has been simplified by Z3, so only boolean ops - ands and, or, not. Returns result in memo. - */ - -#if 0 - int RPFP::GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos){ - if(memo[labpos].find(f) != memo[labpos].end()){ - return memo[labpos][f]; - } - int res; - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies){ - res = GetLabelsRec(memo,f.arg(1) || !f.arg(0), labels, labpos); - goto done; - } - if(k == And) { - res = 1; - for(int i = 0; i < nargs; i++){ - int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); - if(ar == 0){ - res = 0; - goto done; - } - if(ar == 2)res = 2; - } - goto done; - } - else if(k == Or) { - res = 0; - for(int i = 0; i < nargs; i++){ - int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); - if(ar == 1){ - res = 1; - goto done; - } - if(ar == 2)res = 2; - } - goto done; - } - else if(k == Not) { - int ar = GetLabelsRec(memo,f.arg(0), labels, !labpos); - res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); - goto done; - } - } - { - bool pos; std::vector names; - if(f.is_label(pos,names)){ - res = GetLabelsRec(memo,f.arg(0), labels, labpos); - if(pos == labpos && res == (pos ? 1 : 0)) - for(unsigned i = 0; i < names.size(); i++) - labels.push_back(names[i]); - goto done; - } - } - { - expr bv = dualModel.eval(f); - if(bv.is_app() && bv.decl().get_decl_kind() == Equal && - bv.arg(0).is_array()){ - bv = EvalArrayEquality(bv); - } - // Hack!!!! array equalities can occur negatively! - if(bv.is_app() && bv.decl().get_decl_kind() == Not && - bv.arg(0).decl().get_decl_kind() == Equal && - bv.arg(0).arg(0).is_array()){ - bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); - } - if(eq(bv,ctx.bool_val(true))) - res = 1; - else if(eq(bv,ctx.bool_val(false))) - res = 0; - else - res = 2; - } - done: - memo[labpos][f] = res; - return res; - } -#endif - - void RPFP::GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, - hash_set *done, bool truth){ - if(done[truth].find(f) != done[truth].end()) - return; /* already processed */ - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies){ - GetLabelsRec(memo,f.arg(1) || !f.arg(0) ,labels,done,truth); - goto done; - } - if(k == Iff){ - int b = SubtermTruth(memo,f.arg(0)); - if(b == 2) - throw "disaster in GetLabelsRec"; - GetLabelsRec(memo,f.arg(1),labels,done,truth ? b : !b); - goto done; - } - if(truth ? k == And : k == Or) { - for(int i = 0; i < nargs; i++) - GetLabelsRec(memo,f.arg(i),labels,done,truth); - goto done; - } - if(truth ? k == Or : k == And) { - for(int i = 0; i < nargs; i++){ - Term a = f.arg(i); - timer_start("SubtermTruth"); - int b = SubtermTruth(memo,a); - timer_stop("SubtermTruth"); - if(truth ? (b == 1) : (b == 0)){ - GetLabelsRec(memo,a,labels,done,truth); - goto done; - } - } - /* Unreachable! */ - // throw "error in RPFP::GetLabelsRec"; - goto done; - } - else if(k == Not) { - GetLabelsRec(memo,f.arg(0),labels,done,!truth); - goto done; - } - else { - bool pos; std::vector names; - if(f.is_label(pos,names)){ - GetLabelsRec(memo,f.arg(0), labels, done, truth); - if(pos == truth) - for(unsigned i = 0; i < names.size(); i++) - labels.push_back(names[i]); - goto done; - } - } - } - done: - done[truth].insert(f); - } - - void RPFP::GetLabels(Edge *e, std::vector &labels){ - if(!e->map || e->map->labeled.null()) - return; - Term tl = Localize(e, e->map->labeled); - hash_map memo; - hash_set done[2]; - GetLabelsRec(memo,tl,labels,done,true); - } - -#ifdef Z3OPS - static Z3_subterm_truth *stt; -#endif - - int ir_count = 0; - - void RPFP::ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set *done, bool truth, hash_set &dont_cares){ - if(done[truth].find(f) != done[truth].end()) - return; /* already processed */ -#if 0 - int this_count = ir_count++; - if(this_count == 50092) - std::cout << "foo!\n"; -#endif - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies){ - ImplicantRed(memo,f.arg(1) || !f.arg(0) ,lits,done,truth,dont_cares); - goto done; - } - if(k == Iff){ - int b = SubtermTruth(memo,f.arg(0)); - if(b == 2) - throw "disaster in ImplicantRed"; - ImplicantRed(memo,f.arg(1),lits,done,truth ? b : !b,dont_cares); - goto done; - } - if(truth ? k == And : k == Or) { - for(int i = 0; i < nargs; i++) - ImplicantRed(memo,f.arg(i),lits,done,truth,dont_cares); - goto done; - } - if(truth ? k == Or : k == And) { - for(int i = 0; i < nargs; i++){ - Term a = f.arg(i); -#if 0 - if(i == nargs - 1){ // last chance! - ImplicantRed(memo,a,lits,done,truth,dont_cares); - goto done; - } -#endif - timer_start("SubtermTruth"); -#ifdef Z3OPS - bool b = stt->eval(a); -#else - int b = SubtermTruth(memo,a); -#endif - timer_stop("SubtermTruth"); - if(truth ? (b == 1) : (b == 0)){ - ImplicantRed(memo,a,lits,done,truth,dont_cares); - goto done; - } - } - /* Unreachable! */ - // TODO: need to indicate this failure to caller - // std::cerr << "error in RPFP::ImplicantRed"; - goto done; - } - else if(k == Not) { - ImplicantRed(memo,f.arg(0),lits,done,!truth,dont_cares); - goto done; - } - } - { - if(dont_cares.find(f) == dont_cares.end()){ - expr rf = ResolveIte(memo,f,lits,done,dont_cares); - expr bv = truth ? rf : !rf; - lits.push_back(bv); - } - } - done: - done[truth].insert(f); - } - - void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares, bool extensional){ - if(done.find(f) != done.end()) - return; /* already processed */ - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies || k == Iff || k == And || k == Or || k == Not){ - for(int i = 0; i < nargs; i++) - ImplicantFullRed(memo,f.arg(i),lits,done,dont_cares, extensional); - goto done; - } - } - { - if(dont_cares.find(f) == dont_cares.end()){ - int b = SubtermTruth(memo,f); - if(b != 0 && b != 1) goto done; - if(f.is_app() && f.decl().get_decl_kind() == Equal && f.arg(0).is_array()){ - if(b == 1 && !extensional){ - expr x = dualModel.eval(f.arg(0)); expr y = dualModel.eval(f.arg(1)); - if(!eq(x,y)) - b = 0; - } - if(b == 0) - goto done; - } - expr bv = (b==1) ? f : !f; - lits.push_back(bv); - } - } - done: - done.insert(f); - } - - RPFP::Term RPFP::ResolveIte(hash_map &memo, const Term &t, std::vector &lits, - hash_set *done, hash_set &dont_cares){ - if(resolve_ite_memo.find(t) != resolve_ite_memo.end()) - return resolve_ite_memo[t]; - Term res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if(f.get_decl_kind() == Ite){ - timer_start("SubtermTruth"); -#ifdef Z3OPS - bool sel = stt->eval(t.arg(0)); -#else - int xval = SubtermTruth(memo,t.arg(0)); - bool sel; - if(xval == 0)sel = false; - else if(xval == 1)sel = true; - else - throw "unresolved ite in model"; -#endif - timer_stop("SubtermTruth"); - ImplicantRed(memo,t.arg(0),lits,done,sel,dont_cares); - res = ResolveIte(memo,t.arg(sel?1:2),lits,done,dont_cares); - } - else { - for(int i = 0; i < nargs; i++) - args.push_back(ResolveIte(memo,t.arg(i),lits,done,dont_cares)); - res = f(args.size(),&args[0]); - } - } - else res = t; - resolve_ite_memo[t] = res; - return res; - } - - RPFP::Term RPFP::ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts){ - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(bar.second){ - if(t.is_app()){ - int nargs = t.num_args(); - std::vector args; - if(t.decl().get_decl_kind() == Equal){ - expr lhs = t.arg(0); - expr rhs = t.arg(1); - if(rhs.decl().get_decl_kind() == Ite){ - expr rhs_args[3]; - lhs = ElimIteRec(memo,lhs,cnsts); - for(int i = 0; i < 3; i++) - rhs_args[i] = ElimIteRec(memo,rhs.arg(i),cnsts); - res = (rhs_args[0] && (lhs == rhs_args[1])) || ((!rhs_args[0]) && (lhs == rhs_args[2])); - goto done; - } - } - if(t.decl().get_decl_kind() == Ite){ - func_decl sym = ctx.fresh_func_decl("@ite", t.get_sort()); - res = sym(); - cnsts.push_back(ElimIteRec(memo,ctx.make(Equal,res,t),cnsts)); - } - else { - for(int i = 0; i < nargs; i++) - args.push_back(ElimIteRec(memo,t.arg(i),cnsts)); - res = t.decl()(args.size(),&args[0]); - } - } - else if(t.is_quantifier()) - res = clone_quantifier(t,ElimIteRec(memo,t.body(),cnsts)); - else - res = t; - } - done: - return res; - } - - RPFP::Term RPFP::ElimIte(const Term &t){ - hash_map memo; - std::vector cnsts; - expr res = ElimIteRec(memo,t,cnsts); - if(!cnsts.empty()){ - cnsts.push_back(res); - res = ctx.make(And,cnsts); - } - return res; - } - - void RPFP::Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares){ - hash_set done[2]; - ImplicantRed(memo,f,lits,done,true, dont_cares); - } - - - /** Underapproximate a formula using current counterexample. */ - - RPFP::Term RPFP::UnderapproxFormula(const Term &f, hash_set &dont_cares){ - /* first compute truth values of subterms */ - hash_map memo; - #ifdef Z3OPS - stt = Z3_mk_subterm_truth(ctx,dualModel); - #endif - // SubtermTruth(memo,f); - /* now compute an implicant */ - std::vector lits; - Implicant(memo,f,lits, dont_cares); -#ifdef Z3OPS - delete stt; stt = 0; -#endif - /* return conjunction of literals */ - return conjoin(lits); - } - - RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - /* first compute truth values of subterms */ - hash_map memo; - hash_set done; - std::vector lits; - ImplicantFullRed(memo,f,lits,done,dont_cares, extensional); - timer_stop("UnderapproxFormula"); - /* return conjunction of literals */ - return conjoin(lits); - } - - struct VariableProjector : Z3User { - - struct elim_cand { - Term var; - int sup; - Term val; - }; - - typedef expr Term; - - hash_set keep; - hash_map var_ord; - int num_vars; - std::vector elim_cands; - hash_map > sup_map; - hash_map elim_map; - std::vector ready_cands; - hash_map cand_map; - params simp_params; - - VariableProjector(Z3User &_user, std::vector &keep_vec) : - Z3User(_user), simp_params() - { - num_vars = 0; - for(unsigned i = 0; i < keep_vec.size(); i++){ - keep.insert(keep_vec[i]); - var_ord[keep_vec[i]] = num_vars++; - } - } - - int VarNum(const Term &v){ - if(var_ord.find(v) == var_ord.end()) - var_ord[v] = num_vars++; - return var_ord[v]; - } - - bool IsVar(const Term &t){ - return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; - } - - bool IsPropLit(const Term &t, Term &a){ - if(IsVar(t)){ - a = t; - return true; - } - else if(t.is_app() && t.decl().get_decl_kind() == Not) - return IsPropLit(t.arg(0),a); - return false; - } - - void CountOtherVarsRec(hash_map &memo, - const Term &t, - int id, - int &count){ - std::pair foo(t,0); - std::pair::iterator, bool> bar = memo.insert(foo); - // int &res = bar.first->second; - if(!bar.second) return; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ - if(cand_map.find(t) != cand_map.end()){ - count++; - sup_map[t].push_back(id); - } - } - for(int i = 0; i < nargs; i++) - CountOtherVarsRec(memo, t.arg(i), id, count); - } - else if (t.is_quantifier()) - CountOtherVarsRec(memo, t.body(), id, count); - } - - void NewElimCand(const Term &lhs, const Term &rhs){ - if(debug_gauss){ - std::cout << "mapping " << lhs << " to " << rhs << std::endl; - } - elim_cand cand; - cand.var = lhs; - cand.sup = 0; - cand.val = rhs; - elim_cands.push_back(cand); - cand_map[lhs] = elim_cands.size()-1; - } - - void MakeElimCand(const Term &lhs, const Term &rhs){ - if(eq(lhs,rhs)) - return; - if(!IsVar(lhs)){ - if(IsVar(rhs)){ - MakeElimCand(rhs,lhs); - return; - } - else{ - std::cout << "would have mapped a non-var\n"; - return; - } - } - if(IsVar(rhs) && VarNum(rhs) > VarNum(lhs)){ - MakeElimCand(rhs,lhs); - return; - } - if(keep.find(lhs) != keep.end()) - return; - if(cand_map.find(lhs) == cand_map.end()) - NewElimCand(lhs,rhs); - else { - int cand_idx = cand_map[lhs]; - if(IsVar(rhs) && cand_map.find(rhs) == cand_map.end() - && keep.find(rhs) == keep.end()) - NewElimCand(rhs,elim_cands[cand_idx].val); - elim_cands[cand_idx].val = rhs; - } - } - - Term FindRep(const Term &t){ - if(cand_map.find(t) == cand_map.end()) - return t; - Term &res = elim_cands[cand_map[t]].val; - if(IsVar(res)){ - assert(VarNum(res) < VarNum(t)); - res = FindRep(res); - return res; - } - return t; - } - - void GaussElimCheap(const std::vector &lits_in, - std::vector &lits_out){ - for(unsigned i = 0; i < lits_in.size(); i++){ - Term lit = lits_in[i]; - if(lit.is_app()){ - decl_kind k = lit.decl().get_decl_kind(); - if(k == Equal || k == Iff) - MakeElimCand(FindRep(lit.arg(0)),FindRep(lit.arg(1))); - } - } - - for(unsigned i = 0; i < elim_cands.size(); i++){ - elim_cand &cand = elim_cands[i]; - hash_map memo; - CountOtherVarsRec(memo,cand.val,i,cand.sup); - if(cand.sup == 0) - ready_cands.push_back(i); - } - - while(!ready_cands.empty()){ - elim_cand &cand = elim_cands[ready_cands.back()]; - ready_cands.pop_back(); - Term rep = FindRep(cand.var); - if(!eq(rep,cand.var)) - if(cand_map.find(rep) != cand_map.end()){ - int rep_pos = cand_map[rep]; - cand.val = elim_cands[rep_pos].val; - } - Term val = SubstRec(elim_map,cand.val); - if(debug_gauss){ - std::cout << "subbing " << cand.var << " --> " << val << std::endl; - } - elim_map[cand.var] = val; - std::vector &sup = sup_map[cand.var]; - for(unsigned i = 0; i < sup.size(); i++){ - int c = sup[i]; - if((--elim_cands[c].sup) == 0) - ready_cands.push_back(c); - } - } - - for(unsigned i = 0; i < lits_in.size(); i++){ - Term lit = lits_in[i]; - lit = SubstRec(elim_map,lit); - lit = lit.simplify(); - if(eq(lit,ctx.bool_val(true))) - continue; - Term a; - if(IsPropLit(lit,a)) - if(keep.find(lit) == keep.end()) - continue; - lits_out.push_back(lit); - } - } - - // maps variables to constrains in which the occur pos, neg - hash_map la_index[2]; - hash_map la_coeffs[2]; - std::vector la_pos_vars; - bool fixing; - - void IndexLAcoeff(const Term &coeff1, const Term &coeff2, Term t, int id){ - Term coeff = coeff1 * coeff2; - coeff = coeff.simplify(); - Term is_pos = (coeff >= ctx.int_val(0)); - is_pos = is_pos.simplify(); - if(eq(is_pos,ctx.bool_val(true))) - IndexLA(true,coeff,t, id); - else - IndexLA(false,coeff,t, id); - } - - void IndexLAremove(const Term &t){ - if(IsVar(t)){ - la_index[0][t] = -1; // means ineligible to be eliminated - la_index[1][t] = -1; // (more that one occurrence, or occurs not in linear comb) - } - else if(t.is_app()){ - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - IndexLAremove(t.arg(i)); - } - // TODO: quantifiers? - } - - - void IndexLA(bool pos, const Term &coeff, const Term &t, int id){ - if(t.is_numeral()) - return; - if(t.is_app()){ - int nargs = t.num_args(); - switch(t.decl().get_decl_kind()){ - case Plus: - for(int i = 0; i < nargs; i++) - IndexLA(pos,coeff,t.arg(i), id); - break; - case Sub: - IndexLA(pos,coeff,t.arg(0), id); - IndexLA(!pos,coeff,t.arg(1), id); - break; - case Times: - if(t.arg(0).is_numeral()) - IndexLAcoeff(coeff,t.arg(0),t.arg(1), id); - else if(t.arg(1).is_numeral()) - IndexLAcoeff(coeff,t.arg(1),t.arg(0), id); - break; - default: - if(IsVar(t) && (fixing || la_index[pos].find(t) == la_index[pos].end())){ - la_index[pos][t] = id; - la_coeffs[pos][t] = coeff; - if(pos && !fixing) - la_pos_vars.push_back(t); // this means we only add a var once - } - else - IndexLAremove(t); - } - } - } - - void IndexLAstart(bool pos, const Term &t, int id){ - IndexLA(pos,(pos ? ctx.int_val(1) : ctx.int_val(-1)), t, id); - } - - void IndexLApred(bool pos, const Term &p, int id){ - if(p.is_app()){ - switch(p.decl().get_decl_kind()){ - case Not: - IndexLApred(!pos, p.arg(0),id); - break; - case Leq: - case Lt: - IndexLAstart(!pos, p.arg(0), id); - IndexLAstart(pos, p.arg(1), id); - break; - case Geq: - case Gt: - IndexLAstart(pos,p.arg(0), id); - IndexLAstart(!pos,p.arg(1), id); - break; - default: - IndexLAremove(p); - } - } - } - - void IndexLAfix(const Term &p, int id){ - fixing = true; - IndexLApred(true,p,id); - fixing = false; - } - - bool IsCanonIneq(const Term &lit, Term &term, Term &bound){ - // std::cout << Z3_simplify_get_help(ctx) << std::endl; - bool pos = lit.decl().get_decl_kind() != Not; - Term atom = pos ? lit : lit.arg(0); - if(atom.decl().get_decl_kind() == Leq){ - if(pos){ - bound = atom.arg(0); - term = atom.arg(1).simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - else { - bound = (atom.arg(1) + ctx.int_val(1)); - term = atom.arg(0); - // std::cout << "simplifying bound: " << bound << std::endl; - bound = bound.simplify(); - term = term.simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - return true; - } - else if(atom.decl().get_decl_kind() == Geq){ - if(pos){ - bound = atom.arg(1); // integer axiom - term = atom.arg(0).simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - return true; - } - else{ - bound = -(atom.arg(1) - ctx.int_val(1)); // integer axiom - term = -atom.arg(0); - bound = bound.simplify(); - term = term.simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - return true; - } - return false; - } - - Term CanonIneqTerm(const Term &p){ - Term term,bound; - Term ps = p.simplify(); - bool ok = IsCanonIneq(ps,term,bound); - assert(ok); - return term - bound; - } - - void ElimRedundantBounds(std::vector &lits){ - hash_map best_bound; - for(unsigned i = 0; i < lits.size(); i++){ - lits[i] = lits[i].simplify(simp_params); - Term term,bound; - if(IsCanonIneq(lits[i],term,bound)){ - if(best_bound.find(term) == best_bound.end()) - best_bound[term] = i; - else { - int best = best_bound[term]; - Term bterm,bbound; - IsCanonIneq(lits[best],bterm,bbound); - Term comp = bound > bbound; - comp = comp.simplify(); - if(eq(comp,ctx.bool_val(true))){ - lits[best] = ctx.bool_val(true); - best_bound[term] = i; - } - else { - lits[i] = ctx.bool_val(true); - } - } - } - } - } - - void FourierMotzkinCheap(const std::vector &lits_in, - std::vector &lits_out){ - simp_params.set(":som",true); - simp_params.set(":sort-sums",true); - fixing = false; lits_out = lits_in; - ElimRedundantBounds(lits_out); - for(unsigned i = 0; i < lits_out.size(); i++) - IndexLApred(true,lits_out[i],i); - - for(unsigned i = 0; i < la_pos_vars.size(); i++){ - Term var = la_pos_vars[i]; - if(la_index[false].find(var) != la_index[false].end()){ - int pos_idx = la_index[true][var]; - int neg_idx = la_index[false][var]; - if(pos_idx >= 0 && neg_idx >= 0){ - if(keep.find(var) != keep.end()){ - std::cout << "would have eliminated keep var\n"; - continue; - } - Term tpos = CanonIneqTerm(lits_out[pos_idx]); - Term tneg = CanonIneqTerm(lits_out[neg_idx]); - Term pos_coeff = la_coeffs[true][var]; - Term neg_coeff = -la_coeffs[false][var]; - Term comb = neg_coeff * tpos + pos_coeff * tneg; - Term ineq = ctx.int_val(0) <= comb; - ineq = ineq.simplify(); - lits_out[pos_idx] = ineq; - lits_out[neg_idx] = ctx.bool_val(true); - IndexLAfix(ineq,pos_idx); - } - } - } - } - - Term ProjectFormula(const Term &f){ - std::vector lits, new_lits1, new_lits2; - CollectConjuncts(f,lits); - timer_start("GaussElimCheap"); - GaussElimCheap(lits,new_lits1); - timer_stop("GaussElimCheap"); - timer_start("FourierMotzkinCheap"); - FourierMotzkinCheap(new_lits1,new_lits2); - timer_stop("FourierMotzkinCheap"); - return conjoin(new_lits2); - } - }; - - void Z3User::CollectConjuncts(const Term &f, std::vector &lits, bool pos){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - CollectConjuncts(f.arg(0), lits, !pos); - else if(pos && f.is_app() && f.decl().get_decl_kind() == And){ - int num_args = f.num_args(); - for(int i = 0; i < num_args; i++) - CollectConjuncts(f.arg(i),lits,true); - } - else if(!pos && f.is_app() && f.decl().get_decl_kind() == Or){ - int num_args = f.num_args(); - for(int i = 0; i < num_args; i++) - CollectConjuncts(f.arg(i),lits,false); - } - else if(pos){ - if(!eq(f,ctx.bool_val(true))) - lits.push_back(f); - } - else { - if(!eq(f,ctx.bool_val(false))) - lits.push_back(!f); - } - } - - struct TermLt { - bool operator()(const expr &x, const expr &y){ - unsigned xid = x.get_id(); - unsigned yid = y.get_id(); - return xid < yid; - } - }; - - void Z3User::SortTerms(std::vector &terms){ - TermLt foo; - std::sort(terms.begin(),terms.end(),foo); - } - - Z3User::Term Z3User::SortSum(const Term &t){ - if(!(t.is_app() && t.decl().get_decl_kind() == Plus)) - return t; - int nargs = t.num_args(); - if(nargs < 2) return t; - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = t.arg(i); - SortTerms(args); - if(nargs == 2) - return args[0] + args[1]; - return sum(args); - } - - - RPFP::Term RPFP::ProjectFormula(std::vector &keep_vec, const Term &f){ - VariableProjector vp(*this,keep_vec); - return vp.ProjectFormula(f); - } - - /** Compute an underapproximation of every node in a tree rooted at "root", - based on a previously computed counterexample. The underapproximation - may contain free variables that are implicitly existentially quantified. - */ - - RPFP::Term RPFP::ComputeUnderapprox(Node *root, int persist){ - /* if terminated underapprox is empty set (false) */ - bool show_model = false; - if(show_model) - std::cout << dualModel << std::endl; - if(!root->Outgoing){ - root->Underapprox.SetEmpty(); - return ctx.bool_val(true); - } - /* if not used in cex, underapprox is empty set (false) */ - if(Empty(root)){ - root->Underapprox.SetEmpty(); - return ctx.bool_val(true); - } - /* compute underapprox of children first */ - std::vector &chs = root->Outgoing->Children; - std::vector chu(chs.size()); - for(unsigned i = 0; i < chs.size(); i++) - chu[i] = ComputeUnderapprox(chs[i],persist); - - Term b; std::vector v; - RedVars(root, b, v); - /* underapproximate the edge formula */ - hash_set dont_cares; - dont_cares.insert(b); - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = root->Outgoing->dual.null() ? ctx.bool_val(true) : root->Outgoing->dual; - Term eu = UnderapproxFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - /* combine with children */ - chu.push_back(eu); - eu = conjoin(chu); - /* project onto appropriate variables */ - eu = ProjectFormula(v,eu); - eu = eu.simplify(); - -#if 0 - /* check the result is consistent */ - { - hash_map memo; - int res = SubtermTruth(memo, eu); - if(res != 1) - throw "inconsistent projection"; - } -#endif - - /* rename to appropriate variable names */ - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = root->Annotation.IndParams[i]; /* copy names from annotation */ - Term funder = SubstRec(memo, eu); - root->Underapprox = CreateRelation(root->Annotation.IndParams,funder); -#if 0 - if(persist) - Z3_persist_ast(ctx,root->Underapprox.Formula,persist); -#endif - return eu; - } - - void RPFP::FixCurrentState(Edge *edge){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; - Term eu = UnderapproxFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - ConstrainEdgeLocalized(edge,eu); - } - - void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ - if(memo[under].find(f) != memo[under].end()) - return; - memo[under].insert(f); - if(f.is_app()){ - if(!under && !f.has_quantifiers()) - return; - decl_kind k = f.decl().get_decl_kind(); - if(k == And || k == Or || k == Implies || k == Iff){ - int num_args = f.num_args(); - for(int i = 0; i < num_args; i++) - GetGroundLitsUnderQuants(memo,f.arg(i),res,under); - return; - } - } - else if (f.is_quantifier()){ -#if 0 - // treat closed quantified formula as a literal 'cause we hate nested quantifiers - if(under && IsClosedFormula(f)) - res.push_back(f); - else -#endif - GetGroundLitsUnderQuants(memo,f.body(),res,1); - return; - } - if(under && f.is_ground()) - res.push_back(f); - } - - RPFP::Term RPFP::StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits){ - hash_set memo[2]; - std::vector lits; - GetGroundLitsUnderQuants(memo, f, lits, 0); - hash_set lits_hash; - for(unsigned i = 0; i < lits.size(); i++) - lits_hash.insert(lits[i]); - hash_map subst; - hash_map stt_memo; - std::vector conjuncts; - for(unsigned i = 0; i < lits.size(); i++){ - const expr &lit = lits[i]; - if(lits_hash.find(NegateLit(lit)) == lits_hash.end()){ - case_lits.push_back(lit); - bool tval = false; - expr atom = lit; - if(lit.is_app() && lit.decl().get_decl_kind() == Not){ - tval = true; - atom = lit.arg(0); - } - expr etval = ctx.bool_val(tval); - if(atom.is_quantifier()) - subst[atom] = etval; // this is a bit desperate, since we can't eval quants - else { - int b = SubtermTruth(stt_memo,atom); - if(b == (tval ? 1 : 0)) - subst[atom] = etval; - else { - if(b == 0 || b == 1){ - etval = ctx.bool_val(b ? true : false); - subst[atom] = etval; - conjuncts.push_back(b ? atom : !atom); - } - } - } - } - } - expr g = f; - if(!subst.empty()){ - g = SubstRec(subst,f); - if(conjuncts.size()) - g = g && ctx.make(And,conjuncts); - g = g.simplify(); - } -#if 1 - expr g_old = g; - g = RemoveRedundancy(g); - bool changed = !eq(g,g_old); - g = g.simplify(); - if(changed) { // a second pass can get some more simplification - g = RemoveRedundancy(g); - g = g.simplify(); - } -#else - g = RemoveRedundancy(g); - g = g.simplify(); -#endif - g = AdjustQuantifiers(g); - return g; - } - - RPFP::Term RPFP::ModelValueAsConstraint(const Term &t){ - if(t.is_array()){ - ArrayValue arr; - Term e = dualModel.eval(t); - EvalArrayTerm(e, arr); - if(arr.defined){ - std::vector cs; - for(std::map::iterator it = arr.entries.begin(), en = arr.entries.end(); it != en; ++it){ - expr foo = select(t,expr(ctx,it->first)) == expr(ctx,it->second); - cs.push_back(foo); - } - return conjoin(cs); - } - } - else { - expr r = dualModel.get_const_interp(t.decl()); - if(!r.null()){ - expr res = t == expr(ctx,r); - return res; - } - } - return ctx.bool_val(true); - } - - void RPFP::EvalNodeAsConstraint(Node *p, Transformer &res) - { - Term b; std::vector v; - RedVars(p, b, v); - std::vector args; - for(unsigned i = 0; i < v.size(); i++){ - expr val = ModelValueAsConstraint(v[i]); - if(!eq(val,ctx.bool_val(true))) - args.push_back(val); - } - expr cnst = conjoin(args); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = p->Annotation.IndParams[i]; /* copy names from annotation */ - Term funder = SubstRec(memo, cnst); - res = CreateRelation(p->Annotation.IndParams,funder); - } - -#if 0 - void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ - // verify - s.push(); - expr conj = ctx.make(And,conjuncts); - s.add(conj); - check_result res = s.check(); - if(res != unsat) - throw "should be unsat"; - s.pop(1); - - for(unsigned i = 0; i < conjuncts.size(); ){ - std::swap(conjuncts[i],conjuncts.back()); - expr save = conjuncts.back(); - conjuncts.pop_back(); - s.push(); - expr conj = ctx.make(And,conjuncts); - s.add(conj); - check_result res = s.check(); - s.pop(1); - if(res != unsat){ - conjuncts.push_back(save); - std::swap(conjuncts[i],conjuncts.back()); - i++; - } - } - } -#endif - - void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ - std::vector lits(conjuncts.size()); - for(unsigned i = 0; i < lits.size(); i++){ - func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); - lits[i] = pred(); - s.add(ctx.make(Implies,lits[i],conjuncts[i])); - } - - // verify - check_result res = s.check(lits.size(),&lits[0]); - if(res != unsat){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - s.add(theory[i]); - for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! - if(s.check(lits.size(),&lits[0]) == unsat) - goto is_unsat; - throw "should be unsat"; - } - is_unsat: - for(unsigned i = 0; i < conjuncts.size(); ){ - std::swap(conjuncts[i],conjuncts.back()); - std::swap(lits[i],lits.back()); - check_result res = s.check(lits.size()-1,&lits[0]); - if(res != unsat){ - std::swap(conjuncts[i],conjuncts.back()); - std::swap(lits[i],lits.back()); - i++; - } - else { - conjuncts.pop_back(); - lits.pop_back(); - } - } - } - - void RPFP_caching::FilterCore(std::vector &core, std::vector &full_core){ - hash_set core_set; - std::copy(full_core.begin(),full_core.end(),std::inserter(core_set,core_set.begin())); - std::vector new_core; - for(unsigned i = 0; i < core.size(); i++) - if(core_set.find(core[i]) != core_set.end()) - new_core.push_back(core[i]); - core.swap(new_core); - } - - void RPFP_caching::GreedyReduceCache(std::vector &assumps, std::vector &core){ - std::vector lits = assumps, full_core; - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - - // verify - check_result res = CheckCore(lits,full_core); - if(res != unsat){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - GetAssumptionLits(theory[i],assumps); - lits = assumps; - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - - for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! - if((res = CheckCore(lits,full_core)) == unsat) - goto is_unsat; - throw "should be unsat"; - } - is_unsat: - FilterCore(core,full_core); - - std::vector dummy; - if(CheckCore(full_core,dummy) != unsat) - throw "should be unsat"; - - for(unsigned i = 0; i < core.size(); ){ - expr temp = core[i]; - std::swap(core[i],core.back()); - core.pop_back(); - lits.resize(assumps.size()); - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - res = CheckCore(lits,full_core); - if(res != unsat){ - core.push_back(temp); - std::swap(core[i],core.back()); - i++; - } - } - } - - expr RPFP::NegateLit(const expr &f){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - return f.arg(0); - else - return !f; - } - - void RPFP::NegateLits(std::vector &lits){ - for(unsigned i = 0; i < lits.size(); i++){ - expr &f = lits[i]; - if(f.is_app() && f.decl().get_decl_kind() == Not) - f = f.arg(0); - else - f = !f; - } - } - - expr RPFP::SimplifyOr(std::vector &lits){ - if(lits.size() == 0) - return ctx.bool_val(false); - if(lits.size() == 1) - return lits[0]; - return ctx.make(Or,lits); - } - - expr RPFP::SimplifyAnd(std::vector &lits){ - if(lits.size() == 0) - return ctx.bool_val(true); - if(lits.size() == 1) - return lits[0]; - return ctx.make(And,lits); - } - - - /* This is a wrapper for a solver that is intended to compute - implicants from models. It works around a problem in Z3 with - models in the non-extensional array theory. It does this by - naming all of the store terms in a formula. That is, (store ...) - is replaced by "name" with an added constraint name = (store - ...). This allows us to determine from the model whether an array - equality is true or false (it is false if the two sides are - mapped to different function symbols, even if they have the same - contents). - */ - - struct implicant_solver { - RPFP *owner; - solver &aux_solver; - std::vector assumps, namings; - std::vector assump_stack, naming_stack; - hash_map renaming, renaming_memo; - - void add(const expr &e){ - expr t = e; - if(!aux_solver.extensional_array_theory()){ - unsigned i = namings.size(); - t = owner->ExtractStores(renaming_memo,t,namings,renaming); - for(; i < namings.size(); i++) - aux_solver.add(namings[i]); - } - assumps.push_back(t); - aux_solver.add(t); - } - - void push() { - assump_stack.push_back(assumps.size()); - naming_stack.push_back(namings.size()); - aux_solver.push(); - } - - // When we pop the solver, we have to re-add any namings that were lost - - void pop(int n) { - aux_solver.pop(n); - int new_assumps = assump_stack[assump_stack.size()-n]; - int new_namings = naming_stack[naming_stack.size()-n]; - for(unsigned i = new_namings; i < namings.size(); i++) - aux_solver.add(namings[i]); - assumps.resize(new_assumps); - namings.resize(new_namings); - assump_stack.resize(assump_stack.size()-1); - naming_stack.resize(naming_stack.size()-1); - } - - check_result check() { - return aux_solver.check(); - } - - model get_model() { - return aux_solver.get_model(); - } - - expr get_implicant() { - owner->dualModel = aux_solver.get_model(); - expr dual = owner->ctx.make(And,assumps); - bool ext = aux_solver.extensional_array_theory(); - expr eu = owner->UnderapproxFullFormula(dual,ext); - // if we renamed store terms, we have to undo - if(!ext) - eu = owner->SubstRec(renaming,eu); - return eu; - } - - implicant_solver(RPFP *_owner, solver &_aux_solver) - : owner(_owner), aux_solver(_aux_solver) - {} - }; - - // set up edge constraint in aux solver - void RPFP::AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge){ - if(!edge->dual.null()) - aux_solver.add(edge->dual); - for(unsigned i = 0; i < edge->constraints.size(); i++){ - expr tl = edge->constraints[i]; - aux_solver.add(tl); - } - } - - void RPFP::AddEdgeToSolver(Edge *edge){ - if(!edge->dual.null()) - aux_solver.add(edge->dual); - for(unsigned i = 0; i < edge->constraints.size(); i++){ - expr tl = edge->constraints[i]; - aux_solver.add(tl); - } - } - - static int by_case_counter = 0; - - void RPFP::InterpolateByCases(Node *root, Node *node){ - timer_start("InterpolateByCases"); - bool axioms_added = false; - hash_set axioms_needed; - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - axioms_needed.insert(theory[i]); - implicant_solver is(this,aux_solver); - is.push(); - AddEdgeToSolver(is,node->Outgoing); - node->Annotation.SetEmpty(); - hash_set *core = new hash_set; - core->insert(node->Outgoing->dual); - while(1){ - by_case_counter++; - is.push(); - expr annot = !GetAnnotation(node); - is.add(annot); - if(is.check() == unsat){ - is.pop(1); - break; - } - is.pop(1); - Push(); - ConstrainEdgeLocalized(node->Outgoing,is.get_implicant()); - ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this? - check_result foo = Check(root); - if(foo != unsat){ - slvr().print("should_be_unsat.smt2"); - throw "should be unsat"; - } - std::vector assumps, axioms_to_add; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++){ - (*core).insert(assumps[i]); - if(axioms_needed.find(assumps[i]) != axioms_needed.end()){ - axioms_to_add.push_back(assumps[i]); - axioms_needed.erase(assumps[i]); - } - } - // AddToProofCore(*core); - Transformer old_annot = node->Annotation; - SolveSingleNode(root,node); - - { - expr itp = GetAnnotation(node); - dualModel = is.get_model(); // TODO: what does this mean? - std::vector case_lits; - itp = StrengthenFormulaByCaseSplitting(itp, case_lits); - SetAnnotation(node,itp); - node->Annotation.Formula = node->Annotation.Formula.simplify(); - } - - for(unsigned i = 0; i < axioms_to_add.size(); i++) - is.add(axioms_to_add[i]); - -#define TEST_BAD -#ifdef TEST_BAD - { - static int bad_count = 0, num_bads = 1; - if(bad_count >= num_bads){ - bad_count = 0; - num_bads = num_bads * 2; - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw Bad(); - } - bad_count++; - } -#endif - - if(node->Annotation.IsEmpty()){ - if(!axioms_added){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - is.add(theory[i]); - axioms_added = true; - } - else { -#ifdef KILL_ON_BAD_INTERPOLANT - std::cout << "bad in InterpolateByCase -- core:\n"; -#if 0 - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - assumps[i].show(); -#endif - std::cout << "checking for inconsistency\n"; - std::cout << "model:\n"; - is.get_model().show(); - expr impl = is.get_implicant(); - std::vector conjuncts; - CollectConjuncts(impl,conjuncts,true); - std::cout << "impl:\n"; - for(unsigned i = 0; i < conjuncts.size(); i++) - conjuncts[i].show(); - std::cout << "annot:\n"; - annot.show(); - is.add(annot); - for(unsigned i = 0; i < conjuncts.size(); i++) - is.add(conjuncts[i]); - if(is.check() == unsat){ - std::cout << "inconsistent!\n"; - std::vector is_assumps; - is.aux_solver.get_proof().get_assumptions(is_assumps); - std::cout << "core:\n"; - for(unsigned i = 0; i < is_assumps.size(); i++) - is_assumps[i].show(); - } - else { - std::cout << "consistent!\n"; - is.aux_solver.print("should_be_inconsistent.smt2"); - } - std::cout << "by_case_counter = " << by_case_counter << "\n"; - throw "ack!"; -#endif - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw Bad(); - } - } - Pop(1); - node->Annotation.UnionWith(old_annot); - } - if(proof_core) - delete proof_core; // shouldn't happen - proof_core = core; - is.pop(1); - timer_stop("InterpolateByCases"); - } - - void RPFP::Generalize(Node *root, Node *node){ - timer_start("Generalize"); - aux_solver.push(); - AddEdgeToSolver(node->Outgoing); - expr fmla = GetAnnotation(node); - std::vector conjuncts; - CollectConjuncts(fmla,conjuncts,false); - GreedyReduce(aux_solver,conjuncts); // try to remove conjuncts one at a tme - aux_solver.pop(1); - NegateLits(conjuncts); - SetAnnotation(node,SimplifyOr(conjuncts)); - timer_stop("Generalize"); - } - - RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models, bool axioms){ - edge_solver &es = edge_solvers[edge]; - uptr &p = es.slvr; - if(!p.get()){ - scoped_no_proof no_proofs_please(ctx.m()); // no proofs - p.set(new solver(ctx,true,models)); // no models - if(axioms){ - RPFP::LogicSolver *ls = edge->owner->ls; - const std::vector &axs = ls->get_axioms(); - for(unsigned i = 0; i < axs.size(); i++) - p.get()->add(axs[i]); - } - } - return es; - } - - - // caching version of above - void RPFP_caching::GeneralizeCache(Edge *edge){ - timer_start("Generalize"); - scoped_solver_for_edge ssfe(this,edge); - Node *node = edge->Parent; - std::vector assumps, core, conjuncts; - AssertEdgeCache(edge,assumps); - for(unsigned i = 0; i < edge->Children.size(); i++){ - expr ass = GetAnnotation(edge->Children[i]); - std::vector clauses; - if(!ass.is_true()){ - CollectConjuncts(ass.arg(1),clauses); - for(unsigned j = 0; j < clauses.size(); j++) - GetAssumptionLits(ass.arg(0) || clauses[j],assumps); - } - } - expr fmla = GetAnnotation(node); - std::vector lits; - if(fmla.is_true()){ - timer_stop("Generalize"); - return; - } - assumps.push_back(fmla.arg(0).arg(0)); - CollectConjuncts(!fmla.arg(1),lits); -#if 0 - for(unsigned i = 0; i < lits.size(); i++){ - const expr &lit = lits[i]; - if(lit.is_app() && lit.decl().get_decl_kind() == Equal){ - lits[i] = ctx.make(Leq,lit.arg(0),lit.arg(1)); - lits.push_back(ctx.make(Leq,lit.arg(1),lit.arg(0))); - } - } -#endif - hash_map lit_map; - for(unsigned i = 0; i < lits.size(); i++) - GetAssumptionLits(lits[i],core,&lit_map); - GreedyReduceCache(assumps,core); - for(unsigned i = 0; i < core.size(); i++) - conjuncts.push_back(lit_map[core[i]]); - NegateLits(conjuncts); - SetAnnotation(node,SimplifyOr(conjuncts)); - timer_stop("Generalize"); - } - - // caching version of above - bool RPFP_caching::PropagateCache(Edge *edge){ - timer_start("PropagateCache"); - scoped_solver_for_edge ssfe(this,edge); - bool some = false; - { - std::vector candidates, skip; - Node *node = edge->Parent; - CollectConjuncts(node->Annotation.Formula,skip); - for(unsigned i = 0; i < edge->Children.size(); i++){ - Node *child = edge->Children[i]; - if(child->map == node->map){ - CollectConjuncts(child->Annotation.Formula,candidates); - break; - } - } - if(candidates.empty()) goto done; - hash_set skip_set; - std::copy(skip.begin(),skip.end(),std::inserter(skip_set,skip_set.begin())); - std::vector new_candidates; - for(unsigned i = 0; i < candidates.size(); i++) - if(skip_set.find(candidates[i]) == skip_set.end()) - new_candidates.push_back(candidates[i]); - candidates.swap(new_candidates); - if(candidates.empty()) goto done; - std::vector assumps, core, conjuncts; - AssertEdgeCache(edge,assumps); - for(unsigned i = 0; i < edge->Children.size(); i++){ - expr ass = GetAnnotation(edge->Children[i]); - if(eq(ass,ctx.bool_val(true))) - continue; - std::vector clauses; - CollectConjuncts(ass.arg(1),clauses); - for(unsigned j = 0; j < clauses.size(); j++) - GetAssumptionLits(ass.arg(0) || clauses[j],assumps); - } - for(unsigned i = 0; i < candidates.size(); i++){ - unsigned old_size = assumps.size(); - node->Annotation.Formula = candidates[i]; - expr fmla = GetAnnotation(node); - assumps.push_back(fmla.arg(0).arg(0)); - GetAssumptionLits(!fmla.arg(1),assumps); - std::vector full_core; - check_result res = CheckCore(assumps,full_core); - if(res == unsat) - conjuncts.push_back(candidates[i]); - assumps.resize(old_size); - } - if(conjuncts.empty()) - goto done; - SetAnnotation(node,SimplifyAnd(conjuncts)); - some = true; - } - done: - timer_stop("PropagateCache"); - return some; - } - - - /** Push a scope. Assertions made after Push can be undone by Pop. */ - - void RPFP::Push() - { - stack.push_back(stack_entry()); - slvr_push(); - } - - /** Pop a scope (see Push). Note, you cannot pop axioms. */ - - void RPFP::Pop(int num_scopes) - { - slvr_pop(num_scopes); - for (int i = 0; i < num_scopes; i++) - { - stack_entry &back = stack.back(); - for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) - (*it)->dual = expr(ctx,NULL); - for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) - (*it)->dual = expr(ctx,NULL); - for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - (*it).first->constraints.pop_back(); - stack.pop_back(); - } - } - - /** Erase the proof by performing a Pop, Push and re-assertion of - all the popped constraints */ - - void RPFP::PopPush(){ - slvr_pop(1); - slvr_push(); - stack_entry &back = stack.back(); - for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) - slvr_add((*it)->dual); - for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) - slvr_add((*it)->dual); - for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - slvr_add((*it).second); - } - - - - // This returns a new FuncDel with same sort as top-level function - // of term t, but with numeric suffix appended to name. - - Z3User::FuncDecl Z3User::SuffixFuncDecl(Term t, int n) - { - std::string name = t.decl().name().str() + "_" + string_of_int(n); - std::vector sorts; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - sorts.push_back(t.arg(i).get_sort()); - return ctx.function(name.c_str(), nargs, &sorts[0], t.get_sort()); - } - - Z3User::FuncDecl Z3User::RenumberPred(const FuncDecl &f, int n) - { - std::string name = f.name().str(); - name = name.substr(0,name.rfind('_')) + "_" + string_of_int(n); - int arity = f.arity(); - std::vector domain; - for(int i = 0; i < arity; i++) - domain.push_back(f.domain(i)); - return ctx.function(name.c_str(), arity, &domain[0], f.range()); - } - - // Scan the clause body for occurrences of the predicate unknowns - - RPFP::Term RPFP::ScanBody(hash_map &memo, - const Term &t, - hash_map &pmap, - std::vector &parms, - std::vector &nodes) - { - if(memo.find(t) != memo.end()) - return memo[t]; - Term res(ctx); - if (t.is_app()) { - func_decl f = t.decl(); - if(pmap.find(f) != pmap.end()){ - nodes.push_back(pmap[f]); - f = SuffixFuncDecl(t,parms.size()); - parms.push_back(f); - } - int nargs = t.num_args(); - std::vector args; - for(int i = 0; i < nargs; i++) - args.push_back(ScanBody(memo,t.arg(i),pmap,parms,nodes)); - res = f(nargs,&args[0]); - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,ScanBody(memo,t.body(),pmap,parms,nodes)); - else - res = t; - memo[t] = res; - return res; - } - - // return the func_del of an app if it is uninterpreted - - bool Z3User::get_relation(const Term &t, func_decl &R){ - if(!t.is_app()) - return false; - R = t.decl(); - return R.get_decl_kind() == Uninterpreted; - } - - // return true if term is an individual variable - // TODO: have to check that it is not a background symbol - - bool Z3User::is_variable(const Term &t){ - if(!t.is_app()) - return t.is_var(); - return t.decl().get_decl_kind() == Uninterpreted - && t.num_args() == 0; - } - - RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t, - std::vector &lbls){ - if(memo.find(t) != memo.end()) - return memo[t]; - Term res(ctx); - if (t.is_app()){ - func_decl f = t.decl(); - std::vector names; - bool pos; - if(t.is_label(pos,names)){ - res = RemoveLabelsRec(memo,t.arg(0),lbls); - for(unsigned i = 0; i < names.size(); i++) - lbls.push_back(label_struct(names[i],res,pos)); - } - else { - int nargs = t.num_args(); - std::vector args; - for(int i = 0; i < nargs; i++) - args.push_back(RemoveLabelsRec(memo,t.arg(i),lbls)); - res = f(nargs,&args[0]); - } - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body(),lbls)); - else - res = t; - memo[t] = res; - return res; - } - - RPFP::Term RPFP::RemoveLabels(const Term &t, std::vector &lbls){ - hash_map memo ; - return RemoveLabelsRec(memo,t,lbls); - } - - RPFP::Term RPFP::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo[level].insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if(nargs == 0 && f.get_decl_kind() == Uninterpreted) - ls->declare_constant(f); // keep track of background constants - for(int i = 0; i < nargs; i++) - args.push_back(SubstBoundRec(memo, subst, level, t.arg(i))); - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstBoundRec(memo, subst, level + bound, pats[i]); - res = clone_quantifier(t, SubstBoundRec(memo, subst, level + bound, t.body()), pats); - } - else if (t.is_var()) { - int idx = t.get_index_value(); - if(idx >= level && subst.find(idx-level) != subst.end()){ - res = subst[idx-level]; - } - else res = t; - } - else res = t; - return res; - } - - RPFP::Term RPFP::SubstBound(hash_map &subst, const Term &t){ - hash_map > memo; - return SubstBoundRec(memo, subst, 0, t); - } - - int Z3User::MaxIndex(hash_map &memo, const Term &t) - { - std::pair foo(t,-1); - std::pair::iterator, bool> bar = memo.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()){ - func_decl f = t.decl(); - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - int m = MaxIndex(memo, t.arg(i)); - if(m > res) - res = m; - } - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - res = MaxIndex(memo,t.body()) - bound; - } - else if (t.is_var()) { - res = t.get_index_value(); - } - return res; - } - - bool Z3User::IsClosedFormula(const Term &t){ - hash_map memo; - return MaxIndex(memo,t) < 0; - } - - - /** 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. - - */ - - - static bool canonical_clause(const expr &clause){ - if(clause.decl().get_decl_kind() != Implies) - return false; - expr arg = clause.arg(1); - return arg.is_app() && (arg.decl().get_decl_kind() == False || - arg.decl().get_decl_kind() == Uninterpreted); - } - -#define USE_QE_LITE - - void RPFP::FromClauses(const std::vector &unskolemized_clauses){ - hash_map pmap; - func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); - - std::vector clauses(unskolemized_clauses); - // first, skolemize the clauses - -#ifndef USE_QE_LITE - for(unsigned i = 0; i < clauses.size(); i++){ - expr &t = clauses[i]; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - hash_map subst; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - subst[bound-1-j] = skolem; - } - t = SubstBound(subst,t.body()); - } - } -#else - std::vector > substs(clauses.size()); -#endif - - // create the nodes from the heads of the clauses - - for(unsigned i = 0; i < clauses.size(); i++){ - Term &clause = clauses[i]; - -#ifdef USE_QE_LITE - Term &t = clause; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - substs[i][bound-1-j] = skolem; - } - clause = t.body(); - } - -#endif - - if(clause.is_app() && clause.decl().get_decl_kind() == Uninterpreted) - clause = implies(ctx.bool_val(true),clause); - if(!canonical_clause(clause)) - clause = implies((!clause).simplify(),ctx.bool_val(false)); - Term head = clause.arg(1); - func_decl R(ctx); - bool is_query = false; - if (eq(head,ctx.bool_val(false))){ - R = fail_pred; - // R = ctx.constant("@Fail", ctx.bool_sort()).decl(); - is_query = true; - } - else if(!get_relation(head,R)) - throw bad_clause("rhs must be a predicate application",i); - if(pmap.find(R) == pmap.end()){ - - // If the node doesn't exitst, create it. The Indparams - // are arbitrary, but we use the rhs arguments if they - // are variables for mnomonic value. - - hash_set seen; - std::vector Indparams; - for(unsigned j = 0; j < head.num_args(); j++){ - Term arg = head.arg(j); - if(!is_variable(arg) || seen.find(arg) != seen.end()){ - std::string name = std::string("@a_") + string_of_int(j); - arg = ctx.constant(name.c_str(),arg.get_sort()); - } - seen.insert(arg); - Indparams.push_back(arg); - } -#ifdef USE_QE_LITE - { - hash_map > sb_memo; - for(unsigned j = 0; j < Indparams.size(); j++) - Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); - } -#endif - Node *node = CreateNode(R(Indparams.size(),&Indparams[0])); - //nodes.push_back(node); - pmap[R] = node; - if (is_query) - node->Bound = CreateRelation(std::vector(), ctx.bool_val(false)); - } - } - - bool some_labels = false; - - // create the edges - - for(unsigned i = 0; i < clauses.size(); i++){ - Term clause = clauses[i]; - Term body = clause.arg(0); - Term head = clause.arg(1); - func_decl R(ctx); - if (eq(head,ctx.bool_val(false))) - R = fail_pred; - //R = ctx.constant("@Fail", ctx.bool_sort()).decl(); - else get_relation(head,R); - Node *Parent = pmap[R]; - std::vector Indparams; - hash_set seen; - for(unsigned j = 0; j < head.num_args(); j++){ - Term arg = head.arg(j); - if(!is_variable(arg) || seen.find(arg) != seen.end()){ - std::string name = std::string("@a_") + string_of_int(j); - Term var = ctx.constant(name.c_str(),arg.get_sort()); - body = body && (arg == var); - arg = var; - } - seen.insert(arg); - Indparams.push_back(arg); - } - - // We extract the relparams positionally - - std::vector Relparams; - hash_map scan_memo; - std::vector Children; - body = ScanBody(scan_memo,body,pmap,Relparams,Children); - Term labeled = body; - std::vector lbls; // TODO: throw this away for now - body = RemoveLabels(body,lbls); - if(!eq(labeled,body)) - some_labels = true; // remember if there are labels, as we then can't do qe_lite - // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. - body = body.simplify(); - -#ifdef USE_QE_LITE - std::set idxs; - if(!some_labels){ // can't do qe_lite if we have to reconstruct labels - for(unsigned j = 0; j < Indparams.size(); j++) - if(Indparams[j].is_var()) - idxs.insert(Indparams[j].get_index_value()); - body = body.qe_lite(idxs,false); - } - hash_map > sb_memo; - body = SubstBoundRec(sb_memo,substs[i],0,body); - if(some_labels) - labeled = SubstBoundRec(sb_memo,substs[i],0,labeled); - for(unsigned j = 0; j < Indparams.size(); j++) - Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); -#endif - - // Create the edge - Transformer T = CreateTransformer(Relparams,Indparams,body); - Edge *edge = CreateEdge(Parent,T,Children); - edge->labeled = labeled;; // remember for label extraction - // edges.push_back(edge); - } - - // undo hoisting of expressions out of loops - RemoveDeadNodes(); - Unhoist(); - // FuseEdges(); - } - - - // The following mess is used to undo hoisting of expressions outside loops by compilers - - expr RPFP::UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params){ - if(memo.find(w) != memo.end()) - return memo[w]; - expr res; - if(init_defs.find(w) != init_defs.end()){ - expr d = init_defs[w]; - std::vector vars; - hash_set get_vars_memo; - GetVarsRec(get_vars_memo,d,vars); - hash_map map; - for(unsigned j = 0; j < vars.size(); j++){ - expr x = vars[j]; - map[x] = UnhoistPullRec(memo,x,init_defs,const_params,const_params_inv,new_params); - } - expr defn = SubstRec(map,d); - res = defn; - } - else if(const_params_inv.find(w) == const_params_inv.end()){ - std::string old_name = w.decl().name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); - expr y = fresh(); - const_params[y] = w; - const_params_inv[w] = y; - new_params.push_back(y); - res = y; - } - else - res = const_params_inv[w]; - memo[w] = res; - return res; - } - - void RPFP::AddParamsToTransformer(Transformer &trans, const std::vector ¶ms){ - std::copy(params.begin(),params.end(),std::inserter(trans.IndParams,trans.IndParams.end())); - } - - expr RPFP::AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms){ - int n = app.num_args(); - std::vector args(n); - for (int i = 0; i < n; i++) - args[i] = app.arg(i); - std::copy(params.begin(),params.end(),std::inserter(args,args.end())); - return new_decl(args); - } - - expr RPFP::GetRelRec(hash_set &memo, const expr &t, const func_decl &rel){ - if(memo.find(t) != memo.end()) - return expr(); - memo.insert(t); - if (t.is_app()) - { - func_decl f = t.decl(); - if(f == rel) - return t; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - expr res = GetRelRec(memo,t.arg(i),rel); - if(!res.null()) - return res; - } - } - else if (t.is_quantifier()) - return GetRelRec(memo,t.body(),rel); - return expr(); - } - - expr RPFP::GetRel(Edge *edge, int child_idx){ - func_decl &rel = edge->F.RelParams[child_idx]; - hash_set memo; - return GetRelRec(memo,edge->F.Formula,rel); - } - - void RPFP::GetDefsRec(const expr &cnst, hash_map &defs){ - if(cnst.is_app()){ - switch(cnst.decl().get_decl_kind()){ - case And: { - int n = cnst.num_args(); - for(int i = 0; i < n; i++) - GetDefsRec(cnst.arg(i),defs); - break; - } - case Equal: { - expr lhs = cnst.arg(0); - expr rhs = cnst.arg(1); - if(IsVar(lhs)) - defs[lhs] = rhs; - break; - } - default: - break; - } - } - } - - void RPFP::GetDefs(const expr &cnst, hash_map &defs){ - // GetDefsRec(IneqToEq(cnst),defs); - GetDefsRec(cnst,defs); - } - - bool RPFP::IsVar(const expr &t){ - return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; - } - - void RPFP::GetVarsRec(hash_set &memo, const expr &t, std::vector &vars){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if (t.is_app()) - { - if(IsVar(t)){ - vars.push_back(t); - return; - } - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - GetVarsRec(memo,t.arg(i),vars); - } - } - else if (t.is_quantifier()) - GetVarsRec(memo,t.body(),vars); - } - - void RPFP::AddParamsToNode(Node *node, const std::vector ¶ms){ - int arity = node->Annotation.IndParams.size(); - std::vector domain; - for(int i = 0; i < arity; i++) - domain.push_back(node->Annotation.IndParams[i].get_sort()); - for(unsigned i = 0; i < params.size(); i++) - domain.push_back(params[i].get_sort()); - std::string old_name = node->Name.name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), domain, ctx.bool_sort()); - node->Name = fresh; - AddParamsToTransformer(node->Annotation,params); - AddParamsToTransformer(node->Bound,params); - AddParamsToTransformer(node->Underapprox,params); - } - - void RPFP::UnhoistLoop(Edge *loop_edge, Edge *init_edge){ - loop_edge->F.Formula = IneqToEq(loop_edge->F.Formula); - init_edge->F.Formula = IneqToEq(init_edge->F.Formula); - expr pre = GetRel(loop_edge,0); - if(pre.null()) - return; // this means the loop got simplified away - int nparams = loop_edge->F.IndParams.size(); - hash_map const_params, const_params_inv; - std::vector work_list; - // find the parameters that are constant in the loop - for(int i = 0; i < nparams; i++){ - if(eq(pre.arg(i),loop_edge->F.IndParams[i])){ - const_params[pre.arg(i)] = init_edge->F.IndParams[i]; - const_params_inv[init_edge->F.IndParams[i]] = pre.arg(i); - work_list.push_back(pre.arg(i)); - } - } - // get the definitions in the initialization - hash_map defs,memo,subst; - GetDefs(init_edge->F.Formula,defs); - // try to pull them inside the loop - std::vector new_params; - for(unsigned i = 0; i < work_list.size(); i++){ - expr v = work_list[i]; - expr w = const_params[v]; - expr def = UnhoistPullRec(memo,w,defs,const_params,const_params_inv,new_params); - if(!eq(def,v)) - subst[v] = def; - } - // do the substitutions - if(subst.empty()) - return; - subst[pre] = pre; // don't substitute inside the precondition itself - loop_edge->F.Formula = SubstRec(subst,loop_edge->F.Formula); - loop_edge->F.Formula = ElimIte(loop_edge->F.Formula); - init_edge->F.Formula = ElimIte(init_edge->F.Formula); - // add the new parameters - if(new_params.empty()) - return; - Node *parent = loop_edge->Parent; - AddParamsToNode(parent,new_params); - AddParamsToTransformer(loop_edge->F,new_params); - AddParamsToTransformer(init_edge->F,new_params); - std::vector &incoming = parent->Incoming; - for(unsigned i = 0; i < incoming.size(); i++){ - Edge *in_edge = incoming[i]; - std::vector &chs = in_edge->Children; - for(unsigned j = 0; j < chs.size(); j++) - if(chs[j] == parent){ - expr lit = GetRel(in_edge,j); - expr new_lit = AddParamsToApp(lit,parent->Name,new_params); - func_decl fd = SuffixFuncDecl(new_lit,j); - int nargs = new_lit.num_args(); - std::vector args; - for(int k = 0; k < nargs; k++) - args.push_back(new_lit.arg(k)); - new_lit = fd(nargs,&args[0]); - in_edge->F.RelParams[j] = fd; - hash_map map; - map[lit] = new_lit; - in_edge->F.Formula = SubstRec(map,in_edge->F.Formula); - } - } - } - - void RPFP::Unhoist(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++) - outgoing[edges[i]->Parent].push_back(edges[i]); - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &outs = outgoing[node]; - // if we're not a simple loop with one entry, give up - if(outs.size() == 2){ - for(int j = 0; j < 2; j++){ - Edge *loop_edge = outs[j]; - Edge *init_edge = outs[1-j]; - if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent){ - UnhoistLoop(loop_edge,init_edge); - break; - } - } - } - } - } - - void RPFP::FuseEdges(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++){ - outgoing[edges[i]->Parent].push_back(edges[i]); - } - hash_set edges_to_delete; - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &outs = outgoing[node]; - if(outs.size() > 1 && outs.size() <= 16){ - std::vector trs(outs.size()); - for(unsigned j = 0; j < outs.size(); j++) - trs[j] = &outs[j]->F; - Transformer tr = Fuse(trs); - std::vector chs; - for(unsigned j = 0; j < outs.size(); j++) - for(unsigned k = 0; k < outs[j]->Children.size(); k++) - chs.push_back(outs[j]->Children[k]); - CreateEdge(node,tr,chs); - for(unsigned j = 0; j < outs.size(); j++) - edges_to_delete.insert(outs[j]); - } - } - std::vector new_edges; - hash_set all_nodes; - for(unsigned j = 0; j < edges.size(); j++){ - if(edges_to_delete.find(edges[j]) == edges_to_delete.end()){ -#if 0 - if(all_nodes.find(edges[j]->Parent) != all_nodes.end()) - throw "help!"; - all_nodes.insert(edges[j]->Parent); -#endif - new_edges.push_back(edges[j]); - } - else - delete edges[j]; - } - edges.swap(new_edges); - } - - void RPFP::MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node){ - if(live_nodes.find(node) != live_nodes.end()) - return; - live_nodes.insert(node); - std::vector &outs = outgoing[node]; - for(unsigned i = 0; i < outs.size(); i++) - for(unsigned j = 0; j < outs[i]->Children.size(); j++) - MarkLiveNodes(outgoing, live_nodes,outs[i]->Children[j]); - } - - void RPFP::RemoveDeadNodes(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++) - outgoing[edges[i]->Parent].push_back(edges[i]); - hash_set live_nodes; - for(unsigned i = 0; i < nodes.size(); i++) - if(!nodes[i]->Bound.IsFull()) - MarkLiveNodes(outgoing,live_nodes,nodes[i]); - std::vector new_edges; - for(unsigned j = 0; j < edges.size(); j++){ - if(live_nodes.find(edges[j]->Parent) != live_nodes.end()) - new_edges.push_back(edges[j]); - else { - Edge *edge = edges[j]; - for(unsigned int i = 0; i < edge->Children.size(); i++){ - std::vector &ic = edge->Children[i]->Incoming; - for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ - if(*it == edge){ - ic.erase(it); - break; - } - } - } - delete edge; - } - } - edges.swap(new_edges); - std::vector new_nodes; - for(unsigned j = 0; j < nodes.size(); j++){ - if(live_nodes.find(nodes[j]) != live_nodes.end()) - new_nodes.push_back(nodes[j]); - else - delete nodes[j]; - } - nodes.swap(new_nodes); - } - - void RPFP::WriteSolution(std::ostream &s){ - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - Term asgn = (node->Name)(node->Annotation.IndParams) == node->Annotation.Formula; - s << asgn << std::endl; - } - } - - void RPFP::WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s) - { - std::pair foo(t,0); - std::pair::iterator, bool> bar = memo.insert(foo); - // int &res = bar.first->second; - if(!bar.second) return; - hash_map::iterator it = e->varMap.find(t); - if (it != e->varMap.end()){ - return; - } - if (t.is_app()) - { - func_decl f = t.decl(); - // int idx; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - WriteEdgeVars(e, memo, t.arg(i),s); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)){ - Term rename = HideVariable(t,e->number); - Term value = dualModel.eval(rename); - s << " (= " << t << " " << value << ")\n"; - } - } - else if (t.is_quantifier()) - WriteEdgeVars(e,memo,t.body(),s); - return; - } - - void RPFP::WriteEdgeAssignment(std::ostream &s, Edge *e){ - s << "(\n"; - hash_map memo; - WriteEdgeVars(e, memo, e->F.Formula ,s); - s << ")\n"; - } - - void RPFP::WriteCounterexample(std::ostream &s, Node *node){ - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *child = node->Outgoing->Children[i]; - if(!Empty(child)) - WriteCounterexample(s,child); - } - s << "(" << node->number << " : " << EvalNode(node) << " <- "; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *child = node->Outgoing->Children[i]; - if(!Empty(child)) - s << " " << node->Outgoing->Children[i]->number; - } - s << ")" << std::endl; - WriteEdgeAssignment(s,node->Outgoing); - } - - RPFP::Term RPFP::ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - // int idx; - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(ToRuleRec(e, memo, t.arg(i),quants)); - hash_map::iterator rit = e->relMap.find(f); - if(rit != e->relMap.end()){ - Node* child = e->Children[rit->second]; - FuncDecl op = child->Name; - res = op(args.size(),&args[0]); - } - else { - res = f(args.size(),&args[0]); - if(nargs == 0 && t.decl().get_decl_kind() == Uninterpreted) - quants.push_back(t); - } - } - else if (t.is_quantifier()) - { - Term body = ToRuleRec(e,memo,t.body(),quants); - res = CloneQuantifier(t,body); - } - else res = t; - return res; - } - - - void RPFP::ToClauses(std::vector &cnsts, FileFormat format){ - cnsts.resize(edges.size()); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - SetEdgeMaps(edge); - std::vector quants; - hash_map memo; - Term lhs = ToRuleRec(edge, memo, edge->F.Formula,quants); - Term rhs = (edge->Parent->Name)(edge->F.IndParams.size(),&edge->F.IndParams[0]); - for(unsigned j = 0; j < edge->F.IndParams.size(); j++) - ToRuleRec(edge,memo,edge->F.IndParams[j],quants); // just to get quants - Term cnst = implies(lhs,rhs); -#if 0 - for(unsigned i = 0; i < quants.size(); i++){ - std::cout << expr(ctx,(Z3_ast)quants[i]) << " : " << sort(ctx,Z3_get_sort(ctx,(Z3_ast)quants[i])) << std::endl; - } -#endif - if(format != DualityFormat) - cnst= forall(quants,cnst); - cnsts[i] = cnst; - } - // int num_rules = cnsts.size(); - - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - if(!node->Bound.IsFull()){ - Term lhs = (node->Name)(node->Bound.IndParams) && !node->Bound.Formula; - Term cnst = implies(lhs,ctx.bool_val(false)); - if(format != DualityFormat){ - std::vector quants; - for(unsigned j = 0; j < node->Bound.IndParams.size(); j++) - quants.push_back(node->Bound.IndParams[j]); - if(format == HornFormat) - cnst= exists(quants,!cnst); - else - cnst= forall(quants, cnst); - } - cnsts.push_back(cnst); - } - } - - } - - - bool RPFP::proof_core_contains(const expr &e){ - return proof_core->find(e) != proof_core->end(); - } - - bool RPFP_caching::proof_core_contains(const expr &e){ - std::vector foo; - GetAssumptionLits(e,foo); - for(unsigned i = 0; i < foo.size(); i++) - if(proof_core->find(foo[i]) != proof_core->end()) - return true; - return false; - } - - bool RPFP::EdgeUsedInProof(Edge *edge){ - ComputeProofCore(); - if(!edge->dual.null() && proof_core_contains(edge->dual)) - return true; - for(unsigned i = 0; i < edge->constraints.size(); i++) - if(proof_core_contains(edge->constraints[i])) - return true; - return false; - } - -RPFP::~RPFP(){ - ClearProofCore(); - for(unsigned i = 0; i < nodes.size(); i++) - delete nodes[i]; - for(unsigned i = 0; i < edges.size(); i++) - delete edges[i]; - } -} - -#if 0 -void show_ast(expr *a){ - std::cout << *a; -} -#endif +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality_rpfp.h + +Abstract: + + implements relational post-fixedpoint problem + (RPFP) data structure. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + + + +#ifdef _WINDOWS +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#endif + +#include +#include +#include +#include + + +#include "duality.h" +#include "duality_profiling.h" + +// TODO: do we need these? +#ifdef Z3OPS + +class Z3_subterm_truth { + public: + virtual bool eval(Z3_ast f) = 0; + ~Z3_subterm_truth(){} +}; + +Z3_subterm_truth *Z3_mk_subterm_truth(Z3_context ctx, Z3_model model); + +#endif + +#include + +// TODO: use something official for this +int debug_gauss = 0; + +namespace Duality { + + static char string_of_int_buffer[20]; + + const char *Z3User::string_of_int(int n){ + sprintf(string_of_int_buffer,"%d",n); + return string_of_int_buffer; + } + + RPFP::Term RPFP::SuffixVariable(const Term &t, int n) + { + std::string name = t.decl().name().str() + "_" + string_of_int(n); + return ctx.constant(name.c_str(), t.get_sort()); + } + + RPFP::Term RPFP::HideVariable(const Term &t, int n) + { + std::string name = std::string("@p_") + t.decl().name().str() + "_" + string_of_int(n); + return ctx.constant(name.c_str(), t.get_sort()); + } + + void RPFP::RedVars(Node *node, Term &b, std::vector &v) + { + int idx = node->number; + if(HornClauses) + b = ctx.bool_val(true); + else { + std::string name = std::string("@b_") + string_of_int(idx); + symbol sym = ctx.str_symbol(name.c_str()); + b = ctx.constant(sym,ctx.bool_sort()); + } + // ctx.constant(name.c_str(), ctx.bool_sort()); + v = node->Annotation.IndParams; + for(unsigned i = 0; i < v.size(); i++) + v[i] = SuffixVariable(v[i],idx); + } + + void Z3User::SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t){ + if(memo.find(t) != memo.end()) + return; + memo.insert(t); + if(t.is_app()){ + decl_kind k = t.decl().get_decl_kind(); + if(k == And || k == Or || k == Not || k == Implies || k == Iff){ + ops++; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + SummarizeRec(memo,lits,ops,t.arg(i)); + return; + } + } + lits.push_back(t); + } + + int RPFP::CumulativeDecisions(){ +#if 0 + std::string stats = Z3_statistics_to_string(ctx); + int pos = stats.find("decisions:"); + pos += 10; + int end = stats.find('\n',pos); + std::string val = stats.substr(pos,end-pos); + return atoi(val.c_str()); +#endif + return slvr().get_num_decisions(); + } + + + void Z3User::Summarize(const Term &t){ + hash_set memo; std::vector lits; int ops = 0; + SummarizeRec(memo, lits, ops, t); + std::cout << ops << ": "; + for(unsigned i = 0; i < lits.size(); i++){ + if(i > 0) std::cout << ", "; + std::cout << lits[i]; + } + } + + int Z3User::CountOperatorsRec(hash_set &memo, const Term &t){ + if(memo.find(t) != memo.end()) + return 0; + memo.insert(t); + if(t.is_app()){ + decl_kind k = t.decl().get_decl_kind(); + if(k == And || k == Or){ + int count = 1; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + count += CountOperatorsRec(memo,t.arg(i)); + return count; + } + return 0; + } + if(t.is_quantifier()){ + int nbv = t.get_quantifier_num_bound(); + return CountOperatorsRec(memo,t.body()) + 2 * nbv; // count 2 for each quantifier + } + return 0; + } + + int Z3User::CountOperators(const Term &t){ + hash_set memo; + return CountOperatorsRec(memo,t); + } + + + Z3User::Term Z3User::conjoin(const std::vector &args){ + return ctx.make(And,args); + } + + Z3User::Term Z3User::sum(const std::vector &args){ + return ctx.make(Plus,args); + } + + RPFP::Term RPFP::RedDualRela(Edge *e, std::vector &args, int idx){ + Node *child = e->Children[idx]; + Term b(ctx); + std::vector v; + RedVars(child, b, v); + for (unsigned i = 0; i < args.size(); i++) + { + if (eq(args[i].get_sort(),ctx.bool_sort())) + args[i] = ctx.make(Iff,args[i], v[i]); + else + args[i] = args[i] == v[i]; + } + return args.size() > 0 ? (b && conjoin(args)) : b; + } + + Z3User::Term Z3User::CloneQuantifier(const Term &t, const Term &new_body){ +#if 0 + Z3_context c = ctx; + Z3_ast a = t; + std::vector pats; + int num_pats = Z3_get_quantifier_num_patterns(c,a); + for(int i = 0; i < num_pats; i++) + pats.push_back(Z3_get_quantifier_pattern_ast(c,a,i)); + std::vector no_pats; + int num_no_pats = Z3_get_quantifier_num_patterns(c,a); + for(int i = 0; i < num_no_pats; i++) + no_pats.push_back(Z3_get_quantifier_no_pattern_ast(c,a,i)); + int bound = Z3_get_quantifier_num_bound(c,a); + std::vector sorts; + std::vector names; + for(int i = 0; i < bound; i++){ + sorts.push_back(Z3_get_quantifier_bound_sort(c,a,i)); + names.push_back(Z3_get_quantifier_bound_name(c,a,i)); + } + Z3_ast foo = Z3_mk_quantifier_ex(c, + Z3_is_quantifier_forall(c,a), + Z3_get_quantifier_weight(c,a), + 0, + 0, + num_pats, + &pats[0], + num_no_pats, + &no_pats[0], + bound, + &sorts[0], + &names[0], + new_body); + return expr(ctx,foo); +#endif + return clone_quantifier(t,new_body); + } + + + RPFP::Term RPFP::LocalizeRec(Edge *e, hash_map &memo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + hash_map::iterator it = e->varMap.find(t); + if (it != e->varMap.end()){ + res = it->second; + return res; + } + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(LocalizeRec(e, memo, t.arg(i))); + hash_map::iterator rit = e->relMap.find(f); + if(rit != e->relMap.end()) + res = RedDualRela(e,args,(rit->second)); + else { + if (args.size() == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)) + { + res = HideVariable(t,e->number); + } + else + { + res = f(args.size(),&args[0]); + } + } + } + else if (t.is_quantifier()) + { + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = LocalizeRec(e,memo,pats[i]); + Term body = LocalizeRec(e,memo,t.body()); + res = clone_quantifier(t, body, pats); + } + else res = t; + return res; + } + + void RPFP::SetEdgeMaps(Edge *e){ + timer_start("SetEdgeMaps"); + e->relMap.clear(); + e->varMap.clear(); + for(unsigned i = 0; i < e->F.RelParams.size(); i++){ + e->relMap[e->F.RelParams[i]] = i; + } + Term b(ctx); + std::vector v; + RedVars(e->Parent, b, v); + for(unsigned i = 0; i < e->F.IndParams.size(); i++){ + // func_decl parentParam = e->Parent.Annotation.IndParams[i]; + expr oldname = e->F.IndParams[i]; + expr newname = v[i]; + e->varMap[oldname] = newname; + } + timer_stop("SetEdgeMaps"); + + } + + + RPFP::Term RPFP::Localize(Edge *e, const Term &t){ + timer_start("Localize"); + hash_map memo; + if(e->F.IndParams.size() > 0 && e->varMap.empty()) + SetEdgeMaps(e); // TODO: why is this needed? + Term res = LocalizeRec(e,memo,t); + timer_stop("Localize"); + return res; + } + + + RPFP::Term RPFP::ReducedDualEdge(Edge *e) + { + SetEdgeMaps(e); + timer_start("RedVars"); + Term b(ctx); + std::vector v; + RedVars(e->Parent, b, v); + timer_stop("RedVars"); + // ast_to_track = (ast)b; + return implies(b, Localize(e, e->F.Formula)); + } + + TermTree *RPFP::ToTermTree(Node *root, Node *skip_descendant) + { + if(skip_descendant && root == skip_descendant) + return new TermTree(ctx.bool_val(true)); + Edge *e = root->Outgoing; + if(!e) return new TermTree(ctx.bool_val(true), std::vector()); + std::vector children(e->Children.size()); + for(unsigned i = 0; i < children.size(); i++) + children[i] = ToTermTree(e->Children[i],skip_descendant); + // Term top = ReducedDualEdge(e); + Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; + TermTree *res = new TermTree(top, children); + for(unsigned i = 0; i < e->constraints.size(); i++) + res->addTerm(e->constraints[i]); + return res; + } + + TermTree *RPFP::GetGoalTree(Node *root){ + std::vector children(1); + children[0] = ToGoalTree(root); + return new TermTree(ctx.bool_val(false),children); + } + + TermTree *RPFP::ToGoalTree(Node *root) + { + Term b(ctx); + std::vector v; + RedVars(root, b, v); + Term goal = root->Name(v); + Edge *e = root->Outgoing; + if(!e) return new TermTree(goal, std::vector()); + std::vector children(e->Children.size()); + for(unsigned i = 0; i < children.size(); i++) + children[i] = ToGoalTree(e->Children[i]); + // Term top = ReducedDualEdge(e); + return new TermTree(goal, children); + } + + Z3User::Term Z3User::SubstRec(hash_map &memo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(SubstRec(memo, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + { + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = SubstRec(memo,pats[i]); + Term body = SubstRec(memo,t.body()); + res = clone_quantifier(t, body, pats); + } + // res = CloneQuantifier(t,SubstRec(memo, t.body())); + else res = t; + return res; + } + + Z3User::Term Z3User::SubstRec(hash_map &memo, hash_map &map, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(SubstRec(memo, map, t.arg(i))); + hash_map::iterator it = map.find(f); + if(it != map.end()) + f = it->second; + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + { + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = SubstRec(memo, map, pats[i]); + Term body = SubstRec(memo, map, t.body()); + res = clone_quantifier(t, body, pats); + } + // res = CloneQuantifier(t,SubstRec(memo, t.body())); + else res = t; + return res; + } + + Z3User::Term Z3User::ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(ExtractStores(memo, t.arg(i),cnstrs,renaming)); + res = f(args.size(),&args[0]); + if(f.get_decl_kind() == Store){ + func_decl fresh = ctx.fresh_func_decl("@arr", res.get_sort()); + expr y = fresh(); + expr equ = ctx.make(Equal,y,res); + cnstrs.push_back(equ); + renaming[y] = res; + res = y; + } + } + else res = t; + return res; + } + + + bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ + if(!(lit.is_quantifier() && IsClosedFormula(lit))){ + if(!lit.is_app()) + return false; + decl_kind k = lit.decl().get_decl_kind(); + if(k == Not){ + if(IsLiteral(lit.arg(0),atom,val)){ + val = eq(val,ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); + return true; + } + return false; + } + if(k == And || k == Or || k == Iff || k == Implies) + return false; + } + atom = lit; + val = ctx.bool_val(true); + return true; + } + + expr Z3User::Negate(const expr &f){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + return f.arg(0); + else if(eq(f,ctx.bool_val(true))) + return ctx.bool_val(false); + else if(eq(f,ctx.bool_val(false))) + return ctx.bool_val(true); + return !f; + } + + expr Z3User::ReduceAndOr(const std::vector &args, bool is_and, std::vector &res){ + for(unsigned i = 0; i < args.size(); i++) + if(!eq(args[i],ctx.bool_val(is_and))){ + if(eq(args[i],ctx.bool_val(!is_and))) + return ctx.bool_val(!is_and); + res.push_back(args[i]); + } + return expr(); + } + + expr Z3User::FinishAndOr(const std::vector &args, bool is_and){ + if(args.size() == 0) + return ctx.bool_val(is_and); + if(args.size() == 1) + return args[0]; + return ctx.make(is_and ? And : Or,args); + } + + expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ + std::vector sargs; + expr res = ReduceAndOr(args,is_and,sargs); + if(!res.null()) return res; + return FinishAndOr(sargs,is_and); + } + + expr Z3User::PullCommonFactors(std::vector &args, bool is_and){ + + // first check if there's anything to do... + if(args.size() < 2) + return FinishAndOr(args,is_and); + for(unsigned i = 0; i < args.size(); i++){ + const expr &a = args[i]; + if(!(a.is_app() && a.decl().get_decl_kind() == (is_and ? Or : And))) + return FinishAndOr(args,is_and); + } + std::vector common; + for(unsigned i = 0; i < args.size(); i++){ + unsigned n = args[i].num_args(); + std::vector v(n),w; + for(unsigned j = 0; j < n; j++) + v[j] = args[i].arg(j); + std::less comp; + std::sort(v.begin(),v.end(),comp); + if(i == 0) + common.swap(v); + else { + std::set_intersection(common.begin(),common.end(),v.begin(),v.end(),std::inserter(w,w.begin()),comp); + common.swap(w); + } + } + if(common.empty()) + return FinishAndOr(args,is_and); + std::set common_set(common.begin(),common.end()); + for(unsigned i = 0; i < args.size(); i++){ + unsigned n = args[i].num_args(); + std::vector lits; + for(unsigned j = 0; j < n; j++){ + const expr b = args[i].arg(j); + if(common_set.find(b) == common_set.end()) + lits.push_back(b); + } + args[i] = SimplifyAndOr(lits,!is_and); + } + common.push_back(SimplifyAndOr(args,is_and)); + return SimplifyAndOr(common,!is_and); + } + + expr Z3User::ReallySimplifyAndOr(const std::vector &args, bool is_and){ + std::vector sargs; + expr res = ReduceAndOr(args,is_and,sargs); + if(!res.null()) return res; + return PullCommonFactors(sargs,is_and); + } + + Z3User::Term Z3User::SubstAtomTriv(const expr &foo, const expr &atom, const expr &val){ + if(eq(foo,atom)) + return val; + else if(foo.is_app() && foo.decl().get_decl_kind() == Not && eq(foo.arg(0),atom)) + return Negate(val); + else + return foo; + } + + Z3User::Term Z3User::PushQuantifier(const expr &t, const expr &body, bool is_forall){ + if(t.get_quantifier_num_bound() == 1){ + std::vector fmlas,free,not_free; + CollectJuncts(body,fmlas, is_forall ? Or : And, false); + for(unsigned i = 0; i < fmlas.size(); i++){ + const expr &fmla = fmlas[i]; + if(fmla.has_free(0)) + free.push_back(fmla); + else + not_free.push_back(fmla); + } + decl_kind op = is_forall ? Or : And; + if(free.empty()) + return DeleteBound(0,1,SimplifyAndOr(not_free,op == And)); + expr q = clone_quantifier(is_forall ? Forall : Exists,t, SimplifyAndOr(free, op == And)); + if(!not_free.empty()) + q = ctx.make(op,q,DeleteBound(0,1,SimplifyAndOr(not_free, op == And))); + return q; + } + return clone_quantifier(is_forall ? Forall : Exists,t,body); + } + + Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall){ + if(body.is_app()){ + if(body.decl().get_decl_kind() == (is_forall ? And : Or)){ // quantifier distributes + int nargs = body.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = CloneQuantAndSimp(t, body.arg(i), is_forall); + return SimplifyAndOr(args, body.decl().get_decl_kind() == And); + } + else if(body.decl().get_decl_kind() == (is_forall ? Or : And)){ // quantifier distributes + return PushQuantifier(t,body,is_forall); // may distribute partially + } + else if(body.decl().get_decl_kind() == Not){ + return ctx.make(Not,CloneQuantAndSimp(t,body.arg(0),!is_forall)); + } + } + if(t.get_quantifier_num_bound() == 1 && !body.has_free(0)) + return DeleteBound(0,1,body); // drop the quantifier + return clone_quantifier(is_forall ? Forall : Exists,t,body); + } + + Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ + return CloneQuantAndSimp(t,body,t.is_quantifier_forall()); + } + + Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val){ + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()){ + func_decl f = t.decl(); + decl_kind k = f.get_decl_kind(); + + // TODO: recur here, but how much? We don't want to be quadractic in formula size + + if(k == And || k == Or){ + int nargs = t.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = SubstAtom(memo,t.arg(i),atom,val); + res = ReallySimplifyAndOr(args, k==And); + return res; + } + } + else if(t.is_quantifier() && atom.is_quantifier()){ + if(eq(t,atom)) + res = val; + else + res = clone_quantifier(t,SubstAtom(memo,t.body(),atom,val)); + return res; + } + res = SubstAtomTriv(t,atom,val); + return res; + } + + void Z3User::RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo){ + for(unsigned i = 0; i < args.size(); i++){ + const expr &lit = args[i]; + expr atom, val; + if(IsLiteral(lit,atom,val)){ + if(atom.is_app() && atom.decl().get_decl_kind() == Equal) + if(pol ? eq(val,ctx.bool_val(true)) : eq(val,ctx.bool_val(false))){ + expr lhs = atom.arg(0), rhs = atom.arg(1); + if(lhs.is_numeral()) + std::swap(lhs,rhs); + if(rhs.is_numeral() && lhs.is_app()){ + for(unsigned j = 0; j < args.size(); j++) + if(j != i){ + smemo.clear(); + smemo[lhs] = rhs; + args[j] = SubstRec(smemo,args[j]); + } + } + } + for(unsigned j = 0; j < args.size(); j++) + if(j != i){ + smemo.clear(); + args[j] = SubstAtom(smemo,args[j],atom,pol ? val : !val); + } + } + } + } + + + Z3User::Term Z3User::RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(RemoveRedundancyRec(memo, smemo, t.arg(i))); + + decl_kind k = f.get_decl_kind(); + if(k == And){ + RemoveRedundancyOp(true,args,smemo); + res = ReallySimplifyAndOr(args, true); + } + else if(k == Or){ + RemoveRedundancyOp(false,args,smemo); + res = ReallySimplifyAndOr(args, false); + } + else { + if(k == Equal && args[0].get_id() > args[1].get_id()) + std::swap(args[0],args[1]); + res = f(args.size(),&args[0]); + } + } + else if (t.is_quantifier()) + { + Term body = RemoveRedundancyRec(memo,smemo,t.body()); + res = CloneQuantAndSimp(t, body); + } + else res = t; + return res; + } + + Z3User::Term Z3User::RemoveRedundancy(const Term &t){ + hash_map memo; + hash_map smemo; + return RemoveRedundancyRec(memo,smemo,t); + } + + Z3User::Term Z3User::AdjustQuantifiers(const Term &t) + { + if(t.is_quantifier() || (t.is_app() && t.has_quantifiers())) + return t.qe_lite(); + return t; + } + + Z3User::Term Z3User::IneqToEqRec(hash_map &memo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(IneqToEqRec(memo, t.arg(i))); + + decl_kind k = f.get_decl_kind(); + if(k == And){ + for(int i = 0; i < nargs-1; i++){ + if((args[i].is_app() && args[i].decl().get_decl_kind() == Geq && + args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Leq) + || + (args[i].is_app() && args[i].decl().get_decl_kind() == Leq && + args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Geq)) + if(eq(args[i].arg(0),args[i+1].arg(0)) && eq(args[i].arg(1),args[i+1].arg(1))){ + args[i] = ctx.make(Equal,args[i].arg(0),args[i].arg(1)); + args[i+1] = ctx.bool_val(true); + } + } + } + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + { + Term body = IneqToEqRec(memo,t.body()); + res = clone_quantifier(t, body); + } + else res = t; + return res; + } + + Z3User::Term Z3User::IneqToEq(const Term &t){ + hash_map memo; + return IneqToEqRec(memo,t); + } + + Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ + std::string name = std::string("@q_") + t.decl().name().str() + "_" + string_of_int(number); + res = ctx.constant(name.c_str(), t.get_sort()); + return res; + } + for(int i = 0; i < nargs; i++) + args.push_back(SubstRec(memo, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,SubstRec(memo, t.body())); + else res = t; + return res; + } + + RPFP::Term RPFP::SubstParams(const std::vector &from, + const std::vector &to, const Term &t){ + hash_map memo; + bool some_diff = false; + for(unsigned i = 0; i < from.size(); i++) + if(i < to.size() && !eq(from[i],to[i])){ + memo[from[i]] = to[i]; + some_diff = true; + } + return some_diff ? SubstRec(memo,t) : t; + } + + RPFP::Term RPFP::SubstParamsNoCapture(const std::vector &from, + const std::vector &to, const Term &t){ + hash_map memo; + bool some_diff = false; + for(unsigned i = 0; i < from.size(); i++) + if(i < to.size() && !eq(from[i],to[i])){ + memo[from[i]] = to[i]; + // if the new param is not being mapped to anything else, we need to rename it to prevent capture + // note, if the new param *is* mapped later in the list, it will override this substitution + const expr &w = to[i]; + if(memo.find(w) == memo.end()){ + std::string old_name = w.decl().name().str(); + func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); + expr y = fresh(); + memo[w] = y; + } + some_diff = true; + } + return some_diff ? SubstRec(memo,t) : t; + } + + + + RPFP::Transformer RPFP::Fuse(const std::vector &trs){ + assert(!trs.empty()); + const std::vector ¶ms = trs[0]->IndParams; + std::vector fmlas(trs.size()); + fmlas[0] = trs[0]->Formula; + for(unsigned i = 1; i < trs.size(); i++) + fmlas[i] = SubstParamsNoCapture(trs[i]->IndParams,params,trs[i]->Formula); + std::vector rel_params = trs[0]->RelParams; + for(unsigned i = 1; i < trs.size(); i++){ + const std::vector ¶ms2 = trs[i]->RelParams; + hash_map map; + for(unsigned j = 0; j < params2.size(); j++){ + func_decl rel = RenumberPred(params2[j],rel_params.size()); + rel_params.push_back(rel); + map[params2[j]] = rel; + } + hash_map memo; + fmlas[i] = SubstRec(memo,map,fmlas[i]); + } + return Transformer(rel_params,params,ctx.make(Or,fmlas),trs[0]->owner); + } + + + void Z3User::Strengthen(Term &x, const Term &y) + { + if (eq(x,ctx.bool_val(true))) + x = y; + else + x = x && y; + } + + void RPFP::SetAnnotation(Node *root, const expr &t){ + hash_map memo; + Term b; + std::vector v; + RedVars(root, b, v); + memo[b] = ctx.bool_val(true); + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = root->Annotation.IndParams[i]; + Term annot = SubstRec(memo, t); + // Strengthen(ref root.Annotation.Formula, annot); + root->Annotation.Formula = annot; + } + + void RPFP::DecodeTree(Node *root, TermTree *interp, int persist) + { + std::vector &ic = interp->getChildren(); + if (ic.size() > 0) + { + std::vector &nc = root->Outgoing->Children; + for (unsigned i = 0; i < nc.size(); i++) + DecodeTree(nc[i], ic[i], persist); + } + SetAnnotation(root,interp->getTerm()); +#if 0 + if(persist != 0) + Z3_persist_ast(ctx,root->Annotation.Formula,persist); +#endif + } + + RPFP::Term RPFP::GetUpperBound(Node *n) + { + // TODO: cache this + Term b(ctx); std::vector v; + RedVars(n, b, v); + hash_map memo; + for (unsigned int i = 0; i < v.size(); i++) + memo[n->Bound.IndParams[i]] = v[i]; + Term cnst = SubstRec(memo, n->Bound.Formula); + return b && !cnst; + } + + RPFP::Term RPFP::GetAnnotation(Node *n) + { + if(eq(n->Annotation.Formula,ctx.bool_val(true))) + return n->Annotation.Formula; + // TODO: cache this + Term b(ctx); std::vector v; + RedVars(n, b, v); + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[n->Annotation.IndParams[i]] = v[i]; + Term cnst = SubstRec(memo, n->Annotation.Formula); + return !b || cnst; + } + + RPFP::Term RPFP::GetUnderapprox(Node *n) + { + // TODO: cache this + Term b(ctx); std::vector v; + RedVars(n, b, v); + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[n->Underapprox.IndParams[i]] = v[i]; + Term cnst = SubstRecHide(memo, n->Underapprox.Formula, n->number); + return !b || cnst; + } + + TermTree *RPFP::AddUpperBound(Node *root, TermTree *t) + { + Term f = !((ast)(root->dual)) ? ctx.bool_val(true) : root->dual; + std::vector c(1); c[0] = t; + return new TermTree(f, c); + } + +#if 0 + void RPFP::WriteInterps(System.IO.StreamWriter f, TermTree t) + { + foreach (var c in t.getChildren()) + WriteInterps(f, c); + f.WriteLine(t.getTerm()); + } +#endif + + + expr RPFP::GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox) + { + if (e->dual.null()) { + timer_start("ReducedDualEdge"); + e->dual = ReducedDualEdge(e); + timer_stop("ReducedDualEdge"); + timer_start("getting children"); + if(underapprox){ + std::vector cus(e->Children.size()); + for(unsigned i = 0; i < e->Children.size(); i++) + cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); + expr cnst = conjoin(cus); + e->dual = e->dual && cnst; + } + timer_stop("getting children"); + timer_start("Persisting"); + std::list::reverse_iterator it = stack.rbegin(); + for(int i = 0; i < persist && it != stack.rend(); i++) + it++; + if(it != stack.rend()) + it->edges.push_back(e); + timer_stop("Persisting"); + //Console.WriteLine("{0}", cnst); + } + return e->dual; + } + + /** 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. The constraint formula + * will survive "persist" pops of the context. If you plan + * to reassert the edge after popping the context once, + * you can save time re-constructing the formula by setting + * "persist" to one. If you set "persist" too high, however, + * you could have a memory leak. + * + * The flag "with children" causes the annotations of the children + * to be asserted. The flag underapprox causes the underapproximations + * of the children to be asserted *conditionally*. See Check() on + * how to actually enforce these constraints. + * + */ + + void RPFP::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) + { + if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) + return; + expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); + timer_start("solver add"); + slvr_add(e->dual); + timer_stop("solver add"); + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + ConstrainParent(e,e->Children[i]); + } + + +#ifdef LIMIT_STACK_WEIGHT + void RPFP_caching::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) + { + unsigned old_new_alits = new_alits.size(); + if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) + return; + expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); + timer_start("solver add"); + slvr_add(e->dual); + timer_stop("solver add"); + if(old_new_alits < new_alits.size()) + weight_added.val++; + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + ConstrainParent(e,e->Children[i]); + } +#endif + + // caching verion of above + void RPFP_caching::AssertEdgeCache(Edge *e, std::vector &lits, bool with_children){ + if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) + return; + expr fmla = GetEdgeFormula(e, 0, with_children, false); + GetAssumptionLits(fmla,lits); + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + ConstrainParentCache(e,e->Children[i],lits); + } + + void RPFP::slvr_add(const expr &e){ + slvr().add(e); + } + + void RPFP_caching::slvr_add(const expr &e){ + GetAssumptionLits(e,alit_stack); + } + + void RPFP::slvr_pop(int i){ + slvr().pop(i); + } + + void RPFP::slvr_push(){ + slvr().push(); + } + + void RPFP_caching::slvr_pop(int i){ + for(int j = 0; j < i; j++){ +#ifdef LIMIT_STACK_WEIGHT + if(alit_stack_sizes.empty()){ + if(big_stack.empty()) + throw "stack underflow"; + for(unsigned k = 0; k < new_alits.size(); k++){ + if(AssumptionLits.find(new_alits[k]) == AssumptionLits.end()) + throw "foo!"; + AssumptionLits.erase(new_alits[k]); + } + big_stack_entry &bsb = big_stack.back(); + bsb.alit_stack_sizes.swap(alit_stack_sizes); + bsb.alit_stack.swap(alit_stack); + bsb.new_alits.swap(new_alits); + bsb.weight_added.swap(weight_added); + big_stack.pop_back(); + slvr().pop(1); + continue; + } +#endif + alit_stack.resize(alit_stack_sizes.back()); + alit_stack_sizes.pop_back(); + } + } + + void RPFP_caching::slvr_push(){ +#ifdef LIMIT_STACK_WEIGHT + if(weight_added.val > LIMIT_STACK_WEIGHT){ + big_stack.resize(big_stack.size()+1); + big_stack_entry &bsb = big_stack.back(); + bsb.alit_stack_sizes.swap(alit_stack_sizes); + bsb.alit_stack.swap(alit_stack); + bsb.new_alits.swap(new_alits); + bsb.weight_added.swap(weight_added); + slvr().push(); + for(unsigned i = 0; i < bsb.alit_stack.size(); i++) + slvr().add(bsb.alit_stack[i]); + return; + } +#endif + alit_stack_sizes.push_back(alit_stack.size()); + } + + check_result RPFP::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ + return slvr().check(n, assumptions, core_size, core); + } + + check_result RPFP_caching::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ + unsigned oldsiz = alit_stack.size(); + if(n && assumptions) + std::copy(assumptions,assumptions+n,std::inserter(alit_stack,alit_stack.end())); + check_result res; + if(core_size && core){ + std::vector full_core(alit_stack.size()), core1(n); + std::copy(assumptions,assumptions+n,core1.begin()); + res = slvr().check(alit_stack.size(), &alit_stack[0], core_size, &full_core[0]); + full_core.resize(*core_size); + if(res == unsat){ + FilterCore(core1,full_core); + *core_size = core1.size(); + std::copy(core1.begin(),core1.end(),core); + } + } + else + res = slvr().check(alit_stack.size(), &alit_stack[0]); + alit_stack.resize(oldsiz); + return res; + } + + lbool RPFP::ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals, + bool weak){ + return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); + } + + lbool RPFP_caching::ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals, + bool weak){ + GetTermTreeAssertionLiterals(assumptions); + return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); + } + + void RPFP_caching::GetTermTreeAssertionLiteralsRec(TermTree *assumptions){ + std::vector alits; + hash_map map; + GetAssumptionLits(assumptions->getTerm(),alits,&map); + std::vector &ts = assumptions->getTerms(); + for(unsigned i = 0; i < ts.size(); i++) + GetAssumptionLits(ts[i],alits,&map); + assumptions->setTerm(ctx.bool_val(true)); + ts = alits; + for(unsigned i = 0; i < alits.size(); i++) + ts.push_back(ctx.make(Implies,alits[i],map[alits[i]])); + for(unsigned i = 0; i < assumptions->getChildren().size(); i++) + GetTermTreeAssertionLiterals(assumptions->getChildren()[i]); + return; + } + + void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions){ + // optimize binary case + if(assumptions->getChildren().size() == 1 + && assumptions->getChildren()[0]->getChildren().size() == 0){ + hash_map map; + TermTree *child = assumptions->getChildren()[0]; + std::vector dummy; + GetAssumptionLits(child->getTerm(),dummy,&map); + std::vector &ts = child->getTerms(); + for(unsigned i = 0; i < ts.size(); i++) + GetAssumptionLits(ts[i],dummy,&map); + std::vector assumps; + slvr().get_proof().get_assumptions(assumps); + if(!proof_core){ // save the proof core for later use + proof_core = new hash_set; + for(unsigned i = 0; i < assumps.size(); i++) + proof_core->insert(assumps[i]); + } + std::vector *cnsts[2] = {&child->getTerms(),&assumptions->getTerms()}; + for(unsigned i = 0; i < assumps.size(); i++){ + expr &ass = assumps[i]; + expr alit = (ass.is_app() && ass.decl().get_decl_kind() == Implies) ? ass.arg(0) : ass; + bool isA = map.find(alit) != map.end(); + cnsts[isA ? 0 : 1]->push_back(ass); + } + } + else + GetTermTreeAssertionLiteralsRec(assumptions); + } + + void RPFP::AddToProofCore(hash_set &core){ + std::vector assumps; + slvr().get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++) + core.insert(assumps[i]); + } + + void RPFP::ComputeProofCore(){ + if(!proof_core){ + proof_core = new hash_set; + AddToProofCore(*proof_core); + } + } + + + void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map){ + std::vector conjs; + CollectConjuncts(fmla,conjs); + for(unsigned i = 0; i < conjs.size(); i++){ + const expr &conj = conjs[i]; + std::pair foo(conj,expr(ctx)); + std::pair::iterator, bool> bar = AssumptionLits.insert(foo); + Term &res = bar.first->second; + if(bar.second){ + func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); + res = pred(); +#ifdef LIMIT_STACK_WEIGHT + new_alits.push_back(conj); +#endif + slvr().add(ctx.make(Implies,res,conj)); + // std::cout << res << ": " << conj << "\n"; + } + if(opt_map) + (*opt_map)[res] = conj; + lits.push_back(res); + } + } + + void RPFP::ConstrainParent(Edge *parent, Node *child){ + ConstrainEdgeLocalized(parent,GetAnnotation(child)); + } + + void RPFP_caching::ConstrainParentCache(Edge *parent, Node *child, std::vector &lits){ + ConstrainEdgeLocalizedCache(parent,GetAnnotation(child),lits); + } + + + /** For incremental solving, asserts the negation of the upper bound associated + * with a node. + * */ + + void RPFP::AssertNode(Node *n) + { + if (n->dual.null()) + { + n->dual = GetUpperBound(n); + stack.back().nodes.push_back(n); + slvr_add(n->dual); + } + } + + // caching version of above + void RPFP_caching::AssertNodeCache(Node *n, std::vector lits){ + if (n->dual.null()) + { + n->dual = GetUpperBound(n); + stack.back().nodes.push_back(n); + GetAssumptionLits(n->dual,lits); + } + } + + /** Clone another RPFP into this one, keeping a map */ + void RPFP_caching::Clone(RPFP *other){ +#if 0 + for(unsigned i = 0; i < other->nodes.size(); i++) + NodeCloneMap[other->nodes[i]] = CloneNode(other->nodes[i]); +#endif + for(unsigned i = 0; i < other->edges.size(); i++){ + Edge *edge = other->edges[i]; + Node *parent = CloneNode(edge->Parent); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++) + // cs.push_back(NodeCloneMap[edge->Children[j]]); + cs.push_back(CloneNode(edge->Children[j])); + EdgeCloneMap[edge] = CreateEdge(parent,edge->F,cs); + } + } + + /** Get the clone of a node */ + RPFP::Node *RPFP_caching::GetNodeClone(Node *other_node){ + return NodeCloneMap[other_node]; + } + + /** Get the clone of an edge */ + RPFP::Edge *RPFP_caching::GetEdgeClone(Edge *other_edge){ + return EdgeCloneMap[other_edge]; + } + + /** check assumption lits, and return core */ + check_result RPFP_caching::CheckCore(const std::vector &assumps, std::vector &core){ + core.resize(assumps.size()); + unsigned core_size; + check_result res = slvr().check(assumps.size(),(expr *)&assumps[0],&core_size,&core[0]); + if(res == unsat) + core.resize(core_size); + else + core.clear(); + return res; + } + + + /** Assert a constraint on an edge in the SMT context. + */ + + void RPFP::ConstrainEdge(Edge *e, const Term &t) + { + Term tl = Localize(e, t); + ConstrainEdgeLocalized(e,tl); + } + + void RPFP::ConstrainEdgeLocalized(Edge *e, const Term &tl) + { + e->constraints.push_back(tl); + stack.back().constraints.push_back(std::pair(e,tl)); + slvr_add(tl); + } + + void RPFP_caching::ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits) + { + e->constraints.push_back(tl); + stack.back().constraints.push_back(std::pair(e,tl)); + GetAssumptionLits(tl,lits); + } + + + /** Declare a constant in the background theory. */ + + void RPFP::DeclareConstant(const FuncDecl &f){ + ls->declare_constant(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 RPFP::AssertAxiom(const Term &t) + { + ls->assert_axiom(t); + axioms.push_back(t); // for printing only + } + +#if 0 + /** Do not call this. */ + + void RPFP::RemoveAxiom(const Term &t) + { + slvr().RemoveInterpolationAxiom(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 RPFP::Solve(Node *root, int persist) + { + timer_start("Solve"); + TermTree *tree = GetConstraintTree(root); + TermTree *interpolant = NULL; + TermTree *goals = NULL; + if(ls->need_goals) + goals = GetGoalTree(root); + ClearProofCore(); + + // if (dualModel != null) dualModel.Dispose(); + // if (dualLabels != null) dualLabels.Dispose(); + + timer_start("interpolate_tree"); + lbool res = ls_interpolate_tree(tree, interpolant, dualModel,goals,true); + timer_stop("interpolate_tree"); + if (res == l_false) + { + DecodeTree(root, interpolant->getChildren()[0], persist); + delete interpolant; + } + + delete tree; + if(goals) + delete goals; + + timer_stop("Solve"); + return res; + } + + void RPFP::CollapseTermTreeRec(TermTree *root, TermTree *node){ + root->addTerm(node->getTerm()); + std::vector &cnsts = node->getTerms(); + for(unsigned i = 0; i < cnsts.size(); i++) + root->addTerm(cnsts[i]); + std::vector &chs = node->getChildren(); + for(unsigned i = 0; i < chs.size(); i++){ + CollapseTermTreeRec(root,chs[i]); + } + } + + TermTree *RPFP::CollapseTermTree(TermTree *node){ + std::vector &chs = node->getChildren(); + for(unsigned i = 0; i < chs.size(); i++) + CollapseTermTreeRec(node,chs[i]); + for(unsigned i = 0; i < chs.size(); i++) + delete chs[i]; + chs.clear(); + return node; + } + + lbool RPFP::SolveSingleNode(Node *root, Node *node) + { + timer_start("Solve"); + TermTree *tree = CollapseTermTree(GetConstraintTree(root,node)); + tree->getChildren().push_back(CollapseTermTree(ToTermTree(node))); + TermTree *interpolant = NULL; + ClearProofCore(); + + timer_start("interpolate_tree"); + lbool res = ls_interpolate_tree(tree, interpolant, dualModel,0,true); + timer_stop("interpolate_tree"); + if (res == l_false) + { + DecodeTree(node, interpolant->getChildren()[0], 0); + delete interpolant; + } + + delete tree; + timer_stop("Solve"); + return res; + } + + /** Get the constraint tree (but don't solve it) */ + + TermTree *RPFP::GetConstraintTree(Node *root, Node *skip_descendant) + { + return AddUpperBound(root, ToTermTree(root,skip_descendant)); + } + + /** Dispose of the dual model (counterexample) if there is one. */ + + void RPFP::DisposeDualModel() + { + dualModel = model(ctx,NULL); + } + + RPFP::Term RPFP::UnderapproxFlag(Node *n){ + expr flag = ctx.constant((std::string("@under") + string_of_int(n->number)).c_str(), ctx.bool_sort()); + underapprox_flag_rev[flag] = n; + return flag; + } + + RPFP::Node *RPFP::UnderapproxFlagRev(const Term &flag){ + return underapprox_flag_rev[flag]; + } + + /** Check satisfiability of asserted edges and nodes. Same functionality as + * Solve, except no primal solution (interpolant) is generated in the unsat case. + * The vector underapproxes gives the set of node underapproximations to be enforced + * (assuming these were conditionally asserted by AssertEdge). + * + */ + + check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core ) + { + timer_start("Check"); + ClearProofCore(); + // if (dualModel != null) dualModel.Dispose(); + check_result res; + if(!underapproxes.size()) + res = slvr_check(); + else { + std::vector us(underapproxes.size()); + for(unsigned i = 0; i < underapproxes.size(); i++) + us[i] = UnderapproxFlag(underapproxes[i]); + slvr_check(); // TODO: no idea why I need to do this + if(underapprox_core){ + std::vector unsat_core(us.size()); + unsigned core_size = 0; + res = slvr_check(us.size(),&us[0],&core_size,&unsat_core[0]); + underapprox_core->resize(core_size); + for(unsigned i = 0; i < core_size; i++) + (*underapprox_core)[i] = UnderapproxFlagRev(unsat_core[i]); + } + else { + res = slvr_check(us.size(),&us[0]); + bool dump = false; + if(dump){ + std::vector cnsts; + // cnsts.push_back(axioms[0]); + cnsts.push_back(root->dual); + cnsts.push_back(root->Outgoing->dual); + ls->write_interpolation_problem("temp.smt",cnsts,std::vector()); + } + } + // check_result temp = slvr_check(); + } + dualModel = slvr().get_model(); + timer_stop("Check"); + return res; + } + + check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ + // check_result temp1 = slvr_check(); // no idea why I need to do this + ClearProofCore(); + check_result res = slvr_check(assumps.size(),&assumps[0]); + model mod = slvr().get_model(); + if(!mod.null()) + dualModel = mod;; + return res; + } + + /** Determines the value in the counterexample of a symbol occuring in the transformer formula of + * a given edge. */ + + RPFP::Term RPFP::Eval(Edge *e, Term t) + { + Term tl = Localize(e, t); + return dualModel.eval(tl); + } + + /** 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 RPFP::Empty(Node *p) + { + Term b; std::vector v; + RedVars(p, b, v); + // dualModel.show_internals(); + // std::cout << "b: " << b << std::endl; + expr bv = dualModel.eval(b); + // std::cout << "bv: " << bv << std::endl; + bool res = !eq(bv,ctx.bool_val(true)); + return res; + } + + RPFP::Term RPFP::EvalNode(Node *p) + { + Term b; std::vector v; + RedVars(p, b, v); + std::vector args; + for(unsigned i = 0; i < v.size(); i++) + args.push_back(dualModel.eval(v[i])); + return (p->Name)(args); + } + + void RPFP::EvalArrayTerm(const RPFP::Term &t, ArrayValue &res){ + if(t.is_app()){ + decl_kind k = t.decl().get_decl_kind(); + if(k == AsArray){ + func_decl fd = t.decl().get_func_decl_parameter(0); + func_interp r = dualModel.get_func_interp(fd); + int num = r.num_entries(); + res.defined = true; + for(int i = 0; i < num; i++){ + expr arg = r.get_arg(i,0); + expr value = r.get_value(i); + res.entries[arg] = value; + } + res.def_val = r.else_value(); + return; + } + else if(k == Store){ + EvalArrayTerm(t.arg(0),res); + if(!res.defined)return; + expr addr = t.arg(1); + expr val = t.arg(2); + if(addr.is_numeral() && val.is_numeral()){ + if(eq(val,res.def_val)) + res.entries.erase(addr); + else + res.entries[addr] = val; + } + else + res.defined = false; + return; + } + } + res.defined = false; + } + + int eae_count = 0; + + RPFP::Term RPFP::EvalArrayEquality(const RPFP::Term &f){ + ArrayValue lhs,rhs; + eae_count++; + EvalArrayTerm(f.arg(0),lhs); + EvalArrayTerm(f.arg(1),rhs); + if(lhs.defined && rhs.defined){ + if(eq(lhs.def_val,rhs.def_val)) + if(lhs.entries == rhs.entries) + return ctx.bool_val(true); + return ctx.bool_val(false); + } + return f; + } + + /** Compute truth values of all boolean subterms in current model. + Assumes formulas has been simplified by Z3, so only boolean ops + ands and, or, not. Returns result in memo. + */ + + int RPFP::SubtermTruth(hash_map &memo, const Term &f){ + if(memo.find(f) != memo.end()){ + return memo[f]; + } + int res; + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + res = SubtermTruth(memo,!f.arg(0) || f.arg(1)); + goto done; + } + if(k == And) { + res = 1; + for(int i = 0; i < nargs; i++){ + int ar = SubtermTruth(memo,f.arg(i)); + if(ar == 0){ + res = 0; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Or) { + res = 0; + for(int i = 0; i < nargs; i++){ + int ar = SubtermTruth(memo,f.arg(i)); + if(ar == 1){ + res = 1; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Not) { + int ar = SubtermTruth(memo,f.arg(0)); + res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); + goto done; + } + } + { + bool pos; std::vector names; + if(f.is_label(pos,names)){ + res = SubtermTruth(memo,f.arg(0)); + goto done; + } + } + { + expr bv = dualModel.eval(f); + if(bv.is_app() && bv.decl().get_decl_kind() == Equal && + bv.arg(0).is_array()){ + bv = EvalArrayEquality(bv); + } + // Hack!!!! array equalities can occur negatively! + if(bv.is_app() && bv.decl().get_decl_kind() == Not && + bv.arg(0).decl().get_decl_kind() == Equal && + bv.arg(0).arg(0).is_array()){ + bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); + } + if(eq(bv,ctx.bool_val(true))) + res = 1; + else if(eq(bv,ctx.bool_val(false))) + res = 0; + else + res = 2; + } + done: + memo[f] = res; + return res; + } + + int RPFP::EvalTruth(hash_map &memo, Edge *e, const Term &f){ + Term tl = Localize(e, f); + return SubtermTruth(memo,tl); + } + + /** Compute truth values of all boolean subterms in current model. + Assumes formulas has been simplified by Z3, so only boolean ops + ands and, or, not. Returns result in memo. + */ + +#if 0 + int RPFP::GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos){ + if(memo[labpos].find(f) != memo[labpos].end()){ + return memo[labpos][f]; + } + int res; + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + res = GetLabelsRec(memo,f.arg(1) || !f.arg(0), labels, labpos); + goto done; + } + if(k == And) { + res = 1; + for(int i = 0; i < nargs; i++){ + int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); + if(ar == 0){ + res = 0; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Or) { + res = 0; + for(int i = 0; i < nargs; i++){ + int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); + if(ar == 1){ + res = 1; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Not) { + int ar = GetLabelsRec(memo,f.arg(0), labels, !labpos); + res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); + goto done; + } + } + { + bool pos; std::vector names; + if(f.is_label(pos,names)){ + res = GetLabelsRec(memo,f.arg(0), labels, labpos); + if(pos == labpos && res == (pos ? 1 : 0)) + for(unsigned i = 0; i < names.size(); i++) + labels.push_back(names[i]); + goto done; + } + } + { + expr bv = dualModel.eval(f); + if(bv.is_app() && bv.decl().get_decl_kind() == Equal && + bv.arg(0).is_array()){ + bv = EvalArrayEquality(bv); + } + // Hack!!!! array equalities can occur negatively! + if(bv.is_app() && bv.decl().get_decl_kind() == Not && + bv.arg(0).decl().get_decl_kind() == Equal && + bv.arg(0).arg(0).is_array()){ + bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); + } + if(eq(bv,ctx.bool_val(true))) + res = 1; + else if(eq(bv,ctx.bool_val(false))) + res = 0; + else + res = 2; + } + done: + memo[labpos][f] = res; + return res; + } +#endif + + void RPFP::GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, + hash_set *done, bool truth){ + if(done[truth].find(f) != done[truth].end()) + return; /* already processed */ + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + GetLabelsRec(memo,f.arg(1) || !f.arg(0) ,labels,done,truth); + goto done; + } + if(k == Iff){ + int b = SubtermTruth(memo,f.arg(0)); + if(b == 2) + throw "disaster in GetLabelsRec"; + GetLabelsRec(memo,f.arg(1),labels,done,truth ? b : !b); + goto done; + } + if(truth ? k == And : k == Or) { + for(int i = 0; i < nargs; i++) + GetLabelsRec(memo,f.arg(i),labels,done,truth); + goto done; + } + if(truth ? k == Or : k == And) { + for(int i = 0; i < nargs; i++){ + Term a = f.arg(i); + timer_start("SubtermTruth"); + int b = SubtermTruth(memo,a); + timer_stop("SubtermTruth"); + if(truth ? (b == 1) : (b == 0)){ + GetLabelsRec(memo,a,labels,done,truth); + goto done; + } + } + /* Unreachable! */ + // throw "error in RPFP::GetLabelsRec"; + goto done; + } + else if(k == Not) { + GetLabelsRec(memo,f.arg(0),labels,done,!truth); + goto done; + } + else { + bool pos; std::vector names; + if(f.is_label(pos,names)){ + GetLabelsRec(memo,f.arg(0), labels, done, truth); + if(pos == truth) + for(unsigned i = 0; i < names.size(); i++) + labels.push_back(names[i]); + goto done; + } + } + } + done: + done[truth].insert(f); + } + + void RPFP::GetLabels(Edge *e, std::vector &labels){ + if(!e->map || e->map->labeled.null()) + return; + Term tl = Localize(e, e->map->labeled); + hash_map memo; + hash_set done[2]; + GetLabelsRec(memo,tl,labels,done,true); + } + +#ifdef Z3OPS + static Z3_subterm_truth *stt; +#endif + + int ir_count = 0; + + void RPFP::ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set *done, bool truth, hash_set &dont_cares){ + if(done[truth].find(f) != done[truth].end()) + return; /* already processed */ +#if 0 + int this_count = ir_count++; + if(this_count == 50092) + std::cout << "foo!\n"; +#endif + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + ImplicantRed(memo,f.arg(1) || !f.arg(0) ,lits,done,truth,dont_cares); + goto done; + } + if(k == Iff){ + int b = SubtermTruth(memo,f.arg(0)); + if(b == 2) + throw "disaster in ImplicantRed"; + ImplicantRed(memo,f.arg(1),lits,done,truth ? b : !b,dont_cares); + goto done; + } + if(truth ? k == And : k == Or) { + for(int i = 0; i < nargs; i++) + ImplicantRed(memo,f.arg(i),lits,done,truth,dont_cares); + goto done; + } + if(truth ? k == Or : k == And) { + for(int i = 0; i < nargs; i++){ + Term a = f.arg(i); +#if 0 + if(i == nargs - 1){ // last chance! + ImplicantRed(memo,a,lits,done,truth,dont_cares); + goto done; + } +#endif + timer_start("SubtermTruth"); +#ifdef Z3OPS + bool b = stt->eval(a); +#else + int b = SubtermTruth(memo,a); +#endif + timer_stop("SubtermTruth"); + if(truth ? (b == 1) : (b == 0)){ + ImplicantRed(memo,a,lits,done,truth,dont_cares); + goto done; + } + } + /* Unreachable! */ + // TODO: need to indicate this failure to caller + // std::cerr << "error in RPFP::ImplicantRed"; + goto done; + } + else if(k == Not) { + ImplicantRed(memo,f.arg(0),lits,done,!truth,dont_cares); + goto done; + } + } + { + if(dont_cares.find(f) == dont_cares.end()){ + expr rf = ResolveIte(memo,f,lits,done,dont_cares); + expr bv = truth ? rf : !rf; + lits.push_back(bv); + } + } + done: + done[truth].insert(f); + } + + void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set &done, hash_set &dont_cares, bool extensional){ + if(done.find(f) != done.end()) + return; /* already processed */ + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies || k == Iff || k == And || k == Or || k == Not){ + for(int i = 0; i < nargs; i++) + ImplicantFullRed(memo,f.arg(i),lits,done,dont_cares, extensional); + goto done; + } + } + { + if(dont_cares.find(f) == dont_cares.end()){ + int b = SubtermTruth(memo,f); + if(b != 0 && b != 1) goto done; + if(f.is_app() && f.decl().get_decl_kind() == Equal && f.arg(0).is_array()){ + if(b == 1 && !extensional){ + expr x = dualModel.eval(f.arg(0)); expr y = dualModel.eval(f.arg(1)); + if(!eq(x,y)) + b = 0; + } + if(b == 0) + goto done; + } + expr bv = (b==1) ? f : !f; + lits.push_back(bv); + } + } + done: + done.insert(f); + } + + RPFP::Term RPFP::ResolveIte(hash_map &memo, const Term &t, std::vector &lits, + hash_set *done, hash_set &dont_cares){ + if(resolve_ite_memo.find(t) != resolve_ite_memo.end()) + return resolve_ite_memo[t]; + Term res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if(f.get_decl_kind() == Ite){ + timer_start("SubtermTruth"); +#ifdef Z3OPS + bool sel = stt->eval(t.arg(0)); +#else + int xval = SubtermTruth(memo,t.arg(0)); + bool sel; + if(xval == 0)sel = false; + else if(xval == 1)sel = true; + else + throw "unresolved ite in model"; +#endif + timer_stop("SubtermTruth"); + ImplicantRed(memo,t.arg(0),lits,done,sel,dont_cares); + res = ResolveIte(memo,t.arg(sel?1:2),lits,done,dont_cares); + } + else { + for(int i = 0; i < nargs; i++) + args.push_back(ResolveIte(memo,t.arg(i),lits,done,dont_cares)); + res = f(args.size(),&args[0]); + } + } + else res = t; + resolve_ite_memo[t] = res; + return res; + } + + RPFP::Term RPFP::ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts){ + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(bar.second){ + if(t.is_app()){ + int nargs = t.num_args(); + std::vector args; + if(t.decl().get_decl_kind() == Equal){ + expr lhs = t.arg(0); + expr rhs = t.arg(1); + if(rhs.decl().get_decl_kind() == Ite){ + expr rhs_args[3]; + lhs = ElimIteRec(memo,lhs,cnsts); + for(int i = 0; i < 3; i++) + rhs_args[i] = ElimIteRec(memo,rhs.arg(i),cnsts); + res = (rhs_args[0] && (lhs == rhs_args[1])) || ((!rhs_args[0]) && (lhs == rhs_args[2])); + goto done; + } + } + if(t.decl().get_decl_kind() == Ite){ + func_decl sym = ctx.fresh_func_decl("@ite", t.get_sort()); + res = sym(); + cnsts.push_back(ElimIteRec(memo,ctx.make(Equal,res,t),cnsts)); + } + else { + for(int i = 0; i < nargs; i++) + args.push_back(ElimIteRec(memo,t.arg(i),cnsts)); + res = t.decl()(args.size(),&args[0]); + } + } + else if(t.is_quantifier()) + res = clone_quantifier(t,ElimIteRec(memo,t.body(),cnsts)); + else + res = t; + } + done: + return res; + } + + RPFP::Term RPFP::ElimIte(const Term &t){ + hash_map memo; + std::vector cnsts; + expr res = ElimIteRec(memo,t,cnsts); + if(!cnsts.empty()){ + cnsts.push_back(res); + res = ctx.make(And,cnsts); + } + return res; + } + + void RPFP::Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares){ + hash_set done[2]; + ImplicantRed(memo,f,lits,done,true, dont_cares); + } + + + /** Underapproximate a formula using current counterexample. */ + + RPFP::Term RPFP::UnderapproxFormula(const Term &f, hash_set &dont_cares){ + /* first compute truth values of subterms */ + hash_map memo; + #ifdef Z3OPS + stt = Z3_mk_subterm_truth(ctx,dualModel); + #endif + // SubtermTruth(memo,f); + /* now compute an implicant */ + std::vector lits; + Implicant(memo,f,lits, dont_cares); +#ifdef Z3OPS + delete stt; stt = 0; +#endif + /* return conjunction of literals */ + return conjoin(lits); + } + + RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){ + hash_set dont_cares; + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); + /* first compute truth values of subterms */ + hash_map memo; + hash_set done; + std::vector lits; + ImplicantFullRed(memo,f,lits,done,dont_cares, extensional); + timer_stop("UnderapproxFormula"); + /* return conjunction of literals */ + return conjoin(lits); + } + + struct VariableProjector : Z3User { + + struct elim_cand { + Term var; + int sup; + Term val; + }; + + typedef expr Term; + + hash_set keep; + hash_map var_ord; + int num_vars; + std::vector elim_cands; + hash_map > sup_map; + hash_map elim_map; + std::vector ready_cands; + hash_map cand_map; + params simp_params; + + VariableProjector(Z3User &_user, std::vector &keep_vec) : + Z3User(_user), simp_params() + { + num_vars = 0; + for(unsigned i = 0; i < keep_vec.size(); i++){ + keep.insert(keep_vec[i]); + var_ord[keep_vec[i]] = num_vars++; + } + } + + int VarNum(const Term &v){ + if(var_ord.find(v) == var_ord.end()) + var_ord[v] = num_vars++; + return var_ord[v]; + } + + bool IsVar(const Term &t){ + return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; + } + + bool IsPropLit(const Term &t, Term &a){ + if(IsVar(t)){ + a = t; + return true; + } + else if(t.is_app() && t.decl().get_decl_kind() == Not) + return IsPropLit(t.arg(0),a); + return false; + } + + void CountOtherVarsRec(hash_map &memo, + const Term &t, + int id, + int &count){ + std::pair foo(t,0); + std::pair::iterator, bool> bar = memo.insert(foo); + // int &res = bar.first->second; + if(!bar.second) return; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ + if(cand_map.find(t) != cand_map.end()){ + count++; + sup_map[t].push_back(id); + } + } + for(int i = 0; i < nargs; i++) + CountOtherVarsRec(memo, t.arg(i), id, count); + } + else if (t.is_quantifier()) + CountOtherVarsRec(memo, t.body(), id, count); + } + + void NewElimCand(const Term &lhs, const Term &rhs){ + if(debug_gauss){ + std::cout << "mapping " << lhs << " to " << rhs << std::endl; + } + elim_cand cand; + cand.var = lhs; + cand.sup = 0; + cand.val = rhs; + elim_cands.push_back(cand); + cand_map[lhs] = elim_cands.size()-1; + } + + void MakeElimCand(const Term &lhs, const Term &rhs){ + if(eq(lhs,rhs)) + return; + if(!IsVar(lhs)){ + if(IsVar(rhs)){ + MakeElimCand(rhs,lhs); + return; + } + else{ + std::cout << "would have mapped a non-var\n"; + return; + } + } + if(IsVar(rhs) && VarNum(rhs) > VarNum(lhs)){ + MakeElimCand(rhs,lhs); + return; + } + if(keep.find(lhs) != keep.end()) + return; + if(cand_map.find(lhs) == cand_map.end()) + NewElimCand(lhs,rhs); + else { + int cand_idx = cand_map[lhs]; + if(IsVar(rhs) && cand_map.find(rhs) == cand_map.end() + && keep.find(rhs) == keep.end()) + NewElimCand(rhs,elim_cands[cand_idx].val); + elim_cands[cand_idx].val = rhs; + } + } + + Term FindRep(const Term &t){ + if(cand_map.find(t) == cand_map.end()) + return t; + Term &res = elim_cands[cand_map[t]].val; + if(IsVar(res)){ + assert(VarNum(res) < VarNum(t)); + res = FindRep(res); + return res; + } + return t; + } + + void GaussElimCheap(const std::vector &lits_in, + std::vector &lits_out){ + for(unsigned i = 0; i < lits_in.size(); i++){ + Term lit = lits_in[i]; + if(lit.is_app()){ + decl_kind k = lit.decl().get_decl_kind(); + if(k == Equal || k == Iff) + MakeElimCand(FindRep(lit.arg(0)),FindRep(lit.arg(1))); + } + } + + for(unsigned i = 0; i < elim_cands.size(); i++){ + elim_cand &cand = elim_cands[i]; + hash_map memo; + CountOtherVarsRec(memo,cand.val,i,cand.sup); + if(cand.sup == 0) + ready_cands.push_back(i); + } + + while(!ready_cands.empty()){ + elim_cand &cand = elim_cands[ready_cands.back()]; + ready_cands.pop_back(); + Term rep = FindRep(cand.var); + if(!eq(rep,cand.var)) + if(cand_map.find(rep) != cand_map.end()){ + int rep_pos = cand_map[rep]; + cand.val = elim_cands[rep_pos].val; + } + Term val = SubstRec(elim_map,cand.val); + if(debug_gauss){ + std::cout << "subbing " << cand.var << " --> " << val << std::endl; + } + elim_map[cand.var] = val; + std::vector &sup = sup_map[cand.var]; + for(unsigned i = 0; i < sup.size(); i++){ + int c = sup[i]; + if((--elim_cands[c].sup) == 0) + ready_cands.push_back(c); + } + } + + for(unsigned i = 0; i < lits_in.size(); i++){ + Term lit = lits_in[i]; + lit = SubstRec(elim_map,lit); + lit = lit.simplify(); + if(eq(lit,ctx.bool_val(true))) + continue; + Term a; + if(IsPropLit(lit,a)) + if(keep.find(lit) == keep.end()) + continue; + lits_out.push_back(lit); + } + } + + // maps variables to constrains in which the occur pos, neg + hash_map la_index[2]; + hash_map la_coeffs[2]; + std::vector la_pos_vars; + bool fixing; + + void IndexLAcoeff(const Term &coeff1, const Term &coeff2, Term t, int id){ + Term coeff = coeff1 * coeff2; + coeff = coeff.simplify(); + Term is_pos = (coeff >= ctx.int_val(0)); + is_pos = is_pos.simplify(); + if(eq(is_pos,ctx.bool_val(true))) + IndexLA(true,coeff,t, id); + else + IndexLA(false,coeff,t, id); + } + + void IndexLAremove(const Term &t){ + if(IsVar(t)){ + la_index[0][t] = -1; // means ineligible to be eliminated + la_index[1][t] = -1; // (more that one occurrence, or occurs not in linear comb) + } + else if(t.is_app()){ + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + IndexLAremove(t.arg(i)); + } + // TODO: quantifiers? + } + + + void IndexLA(bool pos, const Term &coeff, const Term &t, int id){ + if(t.is_numeral()) + return; + if(t.is_app()){ + int nargs = t.num_args(); + switch(t.decl().get_decl_kind()){ + case Plus: + for(int i = 0; i < nargs; i++) + IndexLA(pos,coeff,t.arg(i), id); + break; + case Sub: + IndexLA(pos,coeff,t.arg(0), id); + IndexLA(!pos,coeff,t.arg(1), id); + break; + case Times: + if(t.arg(0).is_numeral()) + IndexLAcoeff(coeff,t.arg(0),t.arg(1), id); + else if(t.arg(1).is_numeral()) + IndexLAcoeff(coeff,t.arg(1),t.arg(0), id); + break; + default: + if(IsVar(t) && (fixing || la_index[pos].find(t) == la_index[pos].end())){ + la_index[pos][t] = id; + la_coeffs[pos][t] = coeff; + if(pos && !fixing) + la_pos_vars.push_back(t); // this means we only add a var once + } + else + IndexLAremove(t); + } + } + } + + void IndexLAstart(bool pos, const Term &t, int id){ + IndexLA(pos,(pos ? ctx.int_val(1) : ctx.int_val(-1)), t, id); + } + + void IndexLApred(bool pos, const Term &p, int id){ + if(p.is_app()){ + switch(p.decl().get_decl_kind()){ + case Not: + IndexLApred(!pos, p.arg(0),id); + break; + case Leq: + case Lt: + IndexLAstart(!pos, p.arg(0), id); + IndexLAstart(pos, p.arg(1), id); + break; + case Geq: + case Gt: + IndexLAstart(pos,p.arg(0), id); + IndexLAstart(!pos,p.arg(1), id); + break; + default: + IndexLAremove(p); + } + } + } + + void IndexLAfix(const Term &p, int id){ + fixing = true; + IndexLApred(true,p,id); + fixing = false; + } + + bool IsCanonIneq(const Term &lit, Term &term, Term &bound){ + // std::cout << Z3_simplify_get_help(ctx) << std::endl; + bool pos = lit.decl().get_decl_kind() != Not; + Term atom = pos ? lit : lit.arg(0); + if(atom.decl().get_decl_kind() == Leq){ + if(pos){ + bound = atom.arg(0); + term = atom.arg(1).simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + } + else { + bound = (atom.arg(1) + ctx.int_val(1)); + term = atom.arg(0); + // std::cout << "simplifying bound: " << bound << std::endl; + bound = bound.simplify(); + term = term.simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + } + return true; + } + else if(atom.decl().get_decl_kind() == Geq){ + if(pos){ + bound = atom.arg(1); // integer axiom + term = atom.arg(0).simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + return true; + } + else{ + bound = -(atom.arg(1) - ctx.int_val(1)); // integer axiom + term = -atom.arg(0); + bound = bound.simplify(); + term = term.simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + } + return true; + } + return false; + } + + Term CanonIneqTerm(const Term &p){ + Term term,bound; + Term ps = p.simplify(); + bool ok = IsCanonIneq(ps,term,bound); + assert(ok); + return term - bound; + } + + void ElimRedundantBounds(std::vector &lits){ + hash_map best_bound; + for(unsigned i = 0; i < lits.size(); i++){ + lits[i] = lits[i].simplify(simp_params); + Term term,bound; + if(IsCanonIneq(lits[i],term,bound)){ + if(best_bound.find(term) == best_bound.end()) + best_bound[term] = i; + else { + int best = best_bound[term]; + Term bterm,bbound; + IsCanonIneq(lits[best],bterm,bbound); + Term comp = bound > bbound; + comp = comp.simplify(); + if(eq(comp,ctx.bool_val(true))){ + lits[best] = ctx.bool_val(true); + best_bound[term] = i; + } + else { + lits[i] = ctx.bool_val(true); + } + } + } + } + } + + void FourierMotzkinCheap(const std::vector &lits_in, + std::vector &lits_out){ + simp_params.set(":som",true); + simp_params.set(":sort-sums",true); + fixing = false; lits_out = lits_in; + ElimRedundantBounds(lits_out); + for(unsigned i = 0; i < lits_out.size(); i++) + IndexLApred(true,lits_out[i],i); + + for(unsigned i = 0; i < la_pos_vars.size(); i++){ + Term var = la_pos_vars[i]; + if(la_index[false].find(var) != la_index[false].end()){ + int pos_idx = la_index[true][var]; + int neg_idx = la_index[false][var]; + if(pos_idx >= 0 && neg_idx >= 0){ + if(keep.find(var) != keep.end()){ + std::cout << "would have eliminated keep var\n"; + continue; + } + Term tpos = CanonIneqTerm(lits_out[pos_idx]); + Term tneg = CanonIneqTerm(lits_out[neg_idx]); + Term pos_coeff = la_coeffs[true][var]; + Term neg_coeff = -la_coeffs[false][var]; + Term comb = neg_coeff * tpos + pos_coeff * tneg; + Term ineq = ctx.int_val(0) <= comb; + ineq = ineq.simplify(); + lits_out[pos_idx] = ineq; + lits_out[neg_idx] = ctx.bool_val(true); + IndexLAfix(ineq,pos_idx); + } + } + } + } + + Term ProjectFormula(const Term &f){ + std::vector lits, new_lits1, new_lits2; + CollectConjuncts(f,lits); + timer_start("GaussElimCheap"); + GaussElimCheap(lits,new_lits1); + timer_stop("GaussElimCheap"); + timer_start("FourierMotzkinCheap"); + FourierMotzkinCheap(new_lits1,new_lits2); + timer_stop("FourierMotzkinCheap"); + return conjoin(new_lits2); + } + }; + + void Z3User::CollectConjuncts(const Term &f, std::vector &lits, bool pos){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + CollectConjuncts(f.arg(0), lits, !pos); + else if(pos && f.is_app() && f.decl().get_decl_kind() == And){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + CollectConjuncts(f.arg(i),lits,true); + } + else if(!pos && f.is_app() && f.decl().get_decl_kind() == Or){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + CollectConjuncts(f.arg(i),lits,false); + } + else if(pos){ + if(!eq(f,ctx.bool_val(true))) + lits.push_back(f); + } + else { + if(!eq(f,ctx.bool_val(false))) + lits.push_back(!f); + } + } + + void Z3User::CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + CollectJuncts(f.arg(0), lits, (op == And) ? Or : And, !negate); + else if(f.is_app() && f.decl().get_decl_kind() == op){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + CollectJuncts(f.arg(i),lits,op,negate); + } + else { + expr junct = negate ? Negate(f) : f; + lits.push_back(junct); + } + } + + struct TermLt { + bool operator()(const expr &x, const expr &y){ + unsigned xid = x.get_id(); + unsigned yid = y.get_id(); + return xid < yid; + } + }; + + void Z3User::SortTerms(std::vector &terms){ + TermLt foo; + std::sort(terms.begin(),terms.end(),foo); + } + + Z3User::Term Z3User::SortSum(const Term &t){ + if(!(t.is_app() && t.decl().get_decl_kind() == Plus)) + return t; + int nargs = t.num_args(); + if(nargs < 2) return t; + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = t.arg(i); + SortTerms(args); + if(nargs == 2) + return args[0] + args[1]; + return sum(args); + } + + + RPFP::Term RPFP::ProjectFormula(std::vector &keep_vec, const Term &f){ + VariableProjector vp(*this,keep_vec); + return vp.ProjectFormula(f); + } + + /** Compute an underapproximation of every node in a tree rooted at "root", + based on a previously computed counterexample. The underapproximation + may contain free variables that are implicitly existentially quantified. + */ + + RPFP::Term RPFP::ComputeUnderapprox(Node *root, int persist){ + /* if terminated underapprox is empty set (false) */ + bool show_model = false; + if(show_model) + std::cout << dualModel << std::endl; + if(!root->Outgoing){ + root->Underapprox.SetEmpty(); + return ctx.bool_val(true); + } + /* if not used in cex, underapprox is empty set (false) */ + if(Empty(root)){ + root->Underapprox.SetEmpty(); + return ctx.bool_val(true); + } + /* compute underapprox of children first */ + std::vector &chs = root->Outgoing->Children; + std::vector chu(chs.size()); + for(unsigned i = 0; i < chs.size(); i++) + chu[i] = ComputeUnderapprox(chs[i],persist); + + Term b; std::vector v; + RedVars(root, b, v); + /* underapproximate the edge formula */ + hash_set dont_cares; + dont_cares.insert(b); + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); + Term dual = root->Outgoing->dual.null() ? ctx.bool_val(true) : root->Outgoing->dual; + Term eu = UnderapproxFormula(dual,dont_cares); + timer_stop("UnderapproxFormula"); + /* combine with children */ + chu.push_back(eu); + eu = conjoin(chu); + /* project onto appropriate variables */ + eu = ProjectFormula(v,eu); + eu = eu.simplify(); + +#if 0 + /* check the result is consistent */ + { + hash_map memo; + int res = SubtermTruth(memo, eu); + if(res != 1) + throw "inconsistent projection"; + } +#endif + + /* rename to appropriate variable names */ + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = root->Annotation.IndParams[i]; /* copy names from annotation */ + Term funder = SubstRec(memo, eu); + root->Underapprox = CreateRelation(root->Annotation.IndParams,funder); +#if 0 + if(persist) + Z3_persist_ast(ctx,root->Underapprox.Formula,persist); +#endif + return eu; + } + + void RPFP::FixCurrentState(Edge *edge){ + hash_set dont_cares; + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); + Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; + Term eu = UnderapproxFormula(dual,dont_cares); + timer_stop("UnderapproxFormula"); + ConstrainEdgeLocalized(edge,eu); + } + + void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ + if(memo[under].find(f) != memo[under].end()) + return; + memo[under].insert(f); + if(f.is_app()){ + if(!under && !f.has_quantifiers()) + return; + decl_kind k = f.decl().get_decl_kind(); + if(k == And || k == Or || k == Implies || k == Iff){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + GetGroundLitsUnderQuants(memo,f.arg(i),res,under); + return; + } + } + else if (f.is_quantifier()){ +#if 0 + // treat closed quantified formula as a literal 'cause we hate nested quantifiers + if(under && IsClosedFormula(f)) + res.push_back(f); + else +#endif + GetGroundLitsUnderQuants(memo,f.body(),res,1); + return; + } + if(under && f.is_ground()) + res.push_back(f); + } + + RPFP::Term RPFP::StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits){ + hash_set memo[2]; + std::vector lits; + GetGroundLitsUnderQuants(memo, f, lits, 0); + hash_set lits_hash; + for(unsigned i = 0; i < lits.size(); i++) + lits_hash.insert(lits[i]); + hash_map subst; + hash_map stt_memo; + std::vector conjuncts; + for(unsigned i = 0; i < lits.size(); i++){ + const expr &lit = lits[i]; + if(lits_hash.find(NegateLit(lit)) == lits_hash.end()){ + case_lits.push_back(lit); + bool tval = false; + expr atom = lit; + if(lit.is_app() && lit.decl().get_decl_kind() == Not){ + tval = true; + atom = lit.arg(0); + } + expr etval = ctx.bool_val(tval); + if(atom.is_quantifier()) + subst[atom] = etval; // this is a bit desperate, since we can't eval quants + else { + int b = SubtermTruth(stt_memo,atom); + if(b == (tval ? 1 : 0)) + subst[atom] = etval; + else { + if(b == 0 || b == 1){ + etval = ctx.bool_val(b ? true : false); + subst[atom] = etval; + conjuncts.push_back(b ? atom : !atom); + } + } + } + } + } + expr g = f; + if(!subst.empty()){ + g = SubstRec(subst,f); + if(conjuncts.size()) + g = g && ctx.make(And,conjuncts); + g = g.simplify(); + } +#if 1 + expr g_old = g; + g = RemoveRedundancy(g); + bool changed = !eq(g,g_old); + g = g.simplify(); + if(changed) { // a second pass can get some more simplification + g = RemoveRedundancy(g); + g = g.simplify(); + } +#else + g = RemoveRedundancy(g); + g = g.simplify(); +#endif + g = AdjustQuantifiers(g); + return g; + } + + RPFP::Term RPFP::ModelValueAsConstraint(const Term &t){ + if(t.is_array()){ + ArrayValue arr; + Term e = dualModel.eval(t); + EvalArrayTerm(e, arr); + if(arr.defined){ + std::vector cs; + for(std::map::iterator it = arr.entries.begin(), en = arr.entries.end(); it != en; ++it){ + expr foo = select(t,expr(ctx,it->first)) == expr(ctx,it->second); + cs.push_back(foo); + } + return conjoin(cs); + } + } + else { + expr r = dualModel.get_const_interp(t.decl()); + if(!r.null()){ + expr res = t == expr(ctx,r); + return res; + } + } + return ctx.bool_val(true); + } + + void RPFP::EvalNodeAsConstraint(Node *p, Transformer &res) + { + Term b; std::vector v; + RedVars(p, b, v); + std::vector args; + for(unsigned i = 0; i < v.size(); i++){ + expr val = ModelValueAsConstraint(v[i]); + if(!eq(val,ctx.bool_val(true))) + args.push_back(val); + } + expr cnst = conjoin(args); + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = p->Annotation.IndParams[i]; /* copy names from annotation */ + Term funder = SubstRec(memo, cnst); + res = CreateRelation(p->Annotation.IndParams,funder); + } + +#if 0 + void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ + // verify + s.push(); + expr conj = ctx.make(And,conjuncts); + s.add(conj); + check_result res = s.check(); + if(res != unsat) + throw "should be unsat"; + s.pop(1); + + for(unsigned i = 0; i < conjuncts.size(); ){ + std::swap(conjuncts[i],conjuncts.back()); + expr save = conjuncts.back(); + conjuncts.pop_back(); + s.push(); + expr conj = ctx.make(And,conjuncts); + s.add(conj); + check_result res = s.check(); + s.pop(1); + if(res != unsat){ + conjuncts.push_back(save); + std::swap(conjuncts[i],conjuncts.back()); + i++; + } + } + } +#endif + + void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ + std::vector lits(conjuncts.size()); + for(unsigned i = 0; i < lits.size(); i++){ + func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); + lits[i] = pred(); + s.add(ctx.make(Implies,lits[i],conjuncts[i])); + } + + // verify + check_result res = s.check(lits.size(),&lits[0]); + if(res != unsat){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + s.add(theory[i]); + for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! + if(s.check(lits.size(),&lits[0]) == unsat) + goto is_unsat; + throw "should be unsat"; + } + is_unsat: + for(unsigned i = 0; i < conjuncts.size(); ){ + std::swap(conjuncts[i],conjuncts.back()); + std::swap(lits[i],lits.back()); + check_result res = s.check(lits.size()-1,&lits[0]); + if(res != unsat){ + std::swap(conjuncts[i],conjuncts.back()); + std::swap(lits[i],lits.back()); + i++; + } + else { + conjuncts.pop_back(); + lits.pop_back(); + } + } + } + + void RPFP_caching::FilterCore(std::vector &core, std::vector &full_core){ + hash_set core_set; + std::copy(full_core.begin(),full_core.end(),std::inserter(core_set,core_set.begin())); + std::vector new_core; + for(unsigned i = 0; i < core.size(); i++) + if(core_set.find(core[i]) != core_set.end()) + new_core.push_back(core[i]); + core.swap(new_core); + } + + void RPFP_caching::GreedyReduceCache(std::vector &assumps, std::vector &core){ + std::vector lits = assumps, full_core; + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + + // verify + check_result res = CheckCore(lits,full_core); + if(res != unsat){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + GetAssumptionLits(theory[i],assumps); + lits = assumps; + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + + for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! + if((res = CheckCore(lits,full_core)) == unsat) + goto is_unsat; + throw "should be unsat"; + } + is_unsat: + FilterCore(core,full_core); + + std::vector dummy; + if(CheckCore(full_core,dummy) != unsat) + throw "should be unsat"; + + for(unsigned i = 0; i < core.size(); ){ + expr temp = core[i]; + std::swap(core[i],core.back()); + core.pop_back(); + lits.resize(assumps.size()); + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + res = CheckCore(lits,full_core); + if(res != unsat){ + core.push_back(temp); + std::swap(core[i],core.back()); + i++; + } + } + } + + expr RPFP::NegateLit(const expr &f){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + return f.arg(0); + else + return !f; + } + + void RPFP::NegateLits(std::vector &lits){ + for(unsigned i = 0; i < lits.size(); i++){ + expr &f = lits[i]; + if(f.is_app() && f.decl().get_decl_kind() == Not) + f = f.arg(0); + else + f = !f; + } + } + + expr RPFP::SimplifyOr(std::vector &lits){ + if(lits.size() == 0) + return ctx.bool_val(false); + if(lits.size() == 1) + return lits[0]; + return ctx.make(Or,lits); + } + + expr RPFP::SimplifyAnd(std::vector &lits){ + if(lits.size() == 0) + return ctx.bool_val(true); + if(lits.size() == 1) + return lits[0]; + return ctx.make(And,lits); + } + + + /* This is a wrapper for a solver that is intended to compute + implicants from models. It works around a problem in Z3 with + models in the non-extensional array theory. It does this by + naming all of the store terms in a formula. That is, (store ...) + is replaced by "name" with an added constraint name = (store + ...). This allows us to determine from the model whether an array + equality is true or false (it is false if the two sides are + mapped to different function symbols, even if they have the same + contents). + */ + + struct implicant_solver { + RPFP *owner; + solver &aux_solver; + std::vector assumps, namings; + std::vector assump_stack, naming_stack; + hash_map renaming, renaming_memo; + + void add(const expr &e){ + expr t = e; + if(!aux_solver.extensional_array_theory()){ + unsigned i = namings.size(); + t = owner->ExtractStores(renaming_memo,t,namings,renaming); + for(; i < namings.size(); i++) + aux_solver.add(namings[i]); + } + assumps.push_back(t); + aux_solver.add(t); + } + + void push() { + assump_stack.push_back(assumps.size()); + naming_stack.push_back(namings.size()); + aux_solver.push(); + } + + // When we pop the solver, we have to re-add any namings that were lost + + void pop(int n) { + aux_solver.pop(n); + int new_assumps = assump_stack[assump_stack.size()-n]; + int new_namings = naming_stack[naming_stack.size()-n]; + for(unsigned i = new_namings; i < namings.size(); i++) + aux_solver.add(namings[i]); + assumps.resize(new_assumps); + namings.resize(new_namings); + assump_stack.resize(assump_stack.size()-1); + naming_stack.resize(naming_stack.size()-1); + } + + check_result check() { + return aux_solver.check(); + } + + model get_model() { + return aux_solver.get_model(); + } + + expr get_implicant() { + owner->dualModel = aux_solver.get_model(); + expr dual = owner->ctx.make(And,assumps); + bool ext = aux_solver.extensional_array_theory(); + expr eu = owner->UnderapproxFullFormula(dual,ext); + // if we renamed store terms, we have to undo + if(!ext) + eu = owner->SubstRec(renaming,eu); + return eu; + } + + implicant_solver(RPFP *_owner, solver &_aux_solver) + : owner(_owner), aux_solver(_aux_solver) + {} + }; + + // set up edge constraint in aux solver + void RPFP::AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge){ + if(!edge->dual.null()) + aux_solver.add(edge->dual); + for(unsigned i = 0; i < edge->constraints.size(); i++){ + expr tl = edge->constraints[i]; + aux_solver.add(tl); + } + } + + void RPFP::AddEdgeToSolver(Edge *edge){ + if(!edge->dual.null()) + aux_solver.add(edge->dual); + for(unsigned i = 0; i < edge->constraints.size(); i++){ + expr tl = edge->constraints[i]; + aux_solver.add(tl); + } + } + + static int by_case_counter = 0; + + void RPFP::InterpolateByCases(Node *root, Node *node){ + timer_start("InterpolateByCases"); + bool axioms_added = false; + hash_set axioms_needed; + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + axioms_needed.insert(theory[i]); + implicant_solver is(this,aux_solver); + is.push(); + AddEdgeToSolver(is,node->Outgoing); + node->Annotation.SetEmpty(); + hash_set *core = new hash_set; + core->insert(node->Outgoing->dual); + while(1){ + by_case_counter++; + is.push(); + expr annot = !GetAnnotation(node); + is.add(annot); + if(is.check() == unsat){ + is.pop(1); + break; + } + is.pop(1); + Push(); + ConstrainEdgeLocalized(node->Outgoing,is.get_implicant()); + ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this? + check_result foo = Check(root); + if(foo != unsat){ + slvr().print("should_be_unsat.smt2"); + throw "should be unsat"; + } + std::vector assumps, axioms_to_add; + slvr().get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++){ + (*core).insert(assumps[i]); + if(axioms_needed.find(assumps[i]) != axioms_needed.end()){ + axioms_to_add.push_back(assumps[i]); + axioms_needed.erase(assumps[i]); + } + } + // AddToProofCore(*core); + Transformer old_annot = node->Annotation; + SolveSingleNode(root,node); + + { + expr itp = GetAnnotation(node); + dualModel = is.get_model(); // TODO: what does this mean? + std::vector case_lits; + itp = StrengthenFormulaByCaseSplitting(itp, case_lits); + SetAnnotation(node,itp); + node->Annotation.Formula = node->Annotation.Formula.simplify(); + } + + for(unsigned i = 0; i < axioms_to_add.size(); i++) + is.add(axioms_to_add[i]); + +#define TEST_BAD +#ifdef TEST_BAD + { + static int bad_count = 0, num_bads = 1; + if(bad_count >= num_bads){ + bad_count = 0; + num_bads = num_bads * 2; + Pop(1); + is.pop(1); + delete core; + timer_stop("InterpolateByCases"); + throw Bad(); + } + bad_count++; + } +#endif + + if(node->Annotation.IsEmpty()){ + if(!axioms_added){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + is.add(theory[i]); + axioms_added = true; + } + else { +#ifdef KILL_ON_BAD_INTERPOLANT + std::cout << "bad in InterpolateByCase -- core:\n"; +#if 0 + std::vector assumps; + slvr().get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++) + assumps[i].show(); +#endif + std::cout << "checking for inconsistency\n"; + std::cout << "model:\n"; + is.get_model().show(); + expr impl = is.get_implicant(); + std::vector conjuncts; + CollectConjuncts(impl,conjuncts,true); + std::cout << "impl:\n"; + for(unsigned i = 0; i < conjuncts.size(); i++) + conjuncts[i].show(); + std::cout << "annot:\n"; + annot.show(); + is.add(annot); + for(unsigned i = 0; i < conjuncts.size(); i++) + is.add(conjuncts[i]); + if(is.check() == unsat){ + std::cout << "inconsistent!\n"; + std::vector is_assumps; + is.aux_solver.get_proof().get_assumptions(is_assumps); + std::cout << "core:\n"; + for(unsigned i = 0; i < is_assumps.size(); i++) + is_assumps[i].show(); + } + else { + std::cout << "consistent!\n"; + is.aux_solver.print("should_be_inconsistent.smt2"); + } + std::cout << "by_case_counter = " << by_case_counter << "\n"; + throw "ack!"; +#endif + Pop(1); + is.pop(1); + delete core; + timer_stop("InterpolateByCases"); + throw Bad(); + } + } + Pop(1); + node->Annotation.UnionWith(old_annot); + } + if(proof_core) + delete proof_core; // shouldn't happen + proof_core = core; + is.pop(1); + timer_stop("InterpolateByCases"); + } + + void RPFP::Generalize(Node *root, Node *node){ + timer_start("Generalize"); + aux_solver.push(); + AddEdgeToSolver(node->Outgoing); + expr fmla = GetAnnotation(node); + std::vector conjuncts; + CollectConjuncts(fmla,conjuncts,false); + GreedyReduce(aux_solver,conjuncts); // try to remove conjuncts one at a tme + aux_solver.pop(1); + NegateLits(conjuncts); + SetAnnotation(node,SimplifyOr(conjuncts)); + timer_stop("Generalize"); + } + + RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models, bool axioms){ + edge_solver &es = edge_solvers[edge]; + uptr &p = es.slvr; + if(!p.get()){ + scoped_no_proof no_proofs_please(ctx.m()); // no proofs + p.set(new solver(ctx,true,models)); // no models + if(axioms){ + RPFP::LogicSolver *ls = edge->owner->ls; + const std::vector &axs = ls->get_axioms(); + for(unsigned i = 0; i < axs.size(); i++) + p.get()->add(axs[i]); + } + } + return es; + } + + + // caching version of above + void RPFP_caching::GeneralizeCache(Edge *edge){ + timer_start("Generalize"); + scoped_solver_for_edge ssfe(this,edge); + Node *node = edge->Parent; + std::vector assumps, core, conjuncts; + AssertEdgeCache(edge,assumps); + for(unsigned i = 0; i < edge->Children.size(); i++){ + expr ass = GetAnnotation(edge->Children[i]); + std::vector clauses; + if(!ass.is_true()){ + CollectConjuncts(ass.arg(1),clauses); + for(unsigned j = 0; j < clauses.size(); j++) + GetAssumptionLits(ass.arg(0) || clauses[j],assumps); + } + } + expr fmla = GetAnnotation(node); + std::vector lits; + if(fmla.is_true()){ + timer_stop("Generalize"); + return; + } + assumps.push_back(fmla.arg(0).arg(0)); + CollectConjuncts(!fmla.arg(1),lits); +#if 0 + for(unsigned i = 0; i < lits.size(); i++){ + const expr &lit = lits[i]; + if(lit.is_app() && lit.decl().get_decl_kind() == Equal){ + lits[i] = ctx.make(Leq,lit.arg(0),lit.arg(1)); + lits.push_back(ctx.make(Leq,lit.arg(1),lit.arg(0))); + } + } +#endif + hash_map lit_map; + for(unsigned i = 0; i < lits.size(); i++) + GetAssumptionLits(lits[i],core,&lit_map); + GreedyReduceCache(assumps,core); + for(unsigned i = 0; i < core.size(); i++) + conjuncts.push_back(lit_map[core[i]]); + NegateLits(conjuncts); + SetAnnotation(node,SimplifyOr(conjuncts)); + timer_stop("Generalize"); + } + + // caching version of above + bool RPFP_caching::PropagateCache(Edge *edge){ + timer_start("PropagateCache"); + scoped_solver_for_edge ssfe(this,edge); + bool some = false; + { + std::vector candidates, skip; + Node *node = edge->Parent; + CollectConjuncts(node->Annotation.Formula,skip); + for(unsigned i = 0; i < edge->Children.size(); i++){ + Node *child = edge->Children[i]; + if(child->map == node->map){ + CollectConjuncts(child->Annotation.Formula,candidates); + break; + } + } + if(candidates.empty()) goto done; + hash_set skip_set; + std::copy(skip.begin(),skip.end(),std::inserter(skip_set,skip_set.begin())); + std::vector new_candidates; + for(unsigned i = 0; i < candidates.size(); i++) + if(skip_set.find(candidates[i]) == skip_set.end()) + new_candidates.push_back(candidates[i]); + candidates.swap(new_candidates); + if(candidates.empty()) goto done; + std::vector assumps, core, conjuncts; + AssertEdgeCache(edge,assumps); + for(unsigned i = 0; i < edge->Children.size(); i++){ + expr ass = GetAnnotation(edge->Children[i]); + if(eq(ass,ctx.bool_val(true))) + continue; + std::vector clauses; + CollectConjuncts(ass.arg(1),clauses); + for(unsigned j = 0; j < clauses.size(); j++) + GetAssumptionLits(ass.arg(0) || clauses[j],assumps); + } + for(unsigned i = 0; i < candidates.size(); i++){ + unsigned old_size = assumps.size(); + node->Annotation.Formula = candidates[i]; + expr fmla = GetAnnotation(node); + assumps.push_back(fmla.arg(0).arg(0)); + GetAssumptionLits(!fmla.arg(1),assumps); + std::vector full_core; + check_result res = CheckCore(assumps,full_core); + if(res == unsat) + conjuncts.push_back(candidates[i]); + assumps.resize(old_size); + } + if(conjuncts.empty()) + goto done; + SetAnnotation(node,SimplifyAnd(conjuncts)); + some = true; + } + done: + timer_stop("PropagateCache"); + return some; + } + + + /** Push a scope. Assertions made after Push can be undone by Pop. */ + + void RPFP::Push() + { + stack.push_back(stack_entry()); + slvr_push(); + } + + /** Pop a scope (see Push). Note, you cannot pop axioms. */ + + void RPFP::Pop(int num_scopes) + { + slvr_pop(num_scopes); + for (int i = 0; i < num_scopes; i++) + { + stack_entry &back = stack.back(); + for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) + (*it)->dual = expr(ctx,NULL); + for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) + (*it)->dual = expr(ctx,NULL); + for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) + (*it).first->constraints.pop_back(); + stack.pop_back(); + } + } + + /** Erase the proof by performing a Pop, Push and re-assertion of + all the popped constraints */ + + void RPFP::PopPush(){ + slvr_pop(1); + slvr_push(); + stack_entry &back = stack.back(); + for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) + slvr_add((*it)->dual); + for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) + slvr_add((*it)->dual); + for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) + slvr_add((*it).second); + } + + + + // This returns a new FuncDel with same sort as top-level function + // of term t, but with numeric suffix appended to name. + + Z3User::FuncDecl Z3User::SuffixFuncDecl(Term t, int n) + { + std::string name = t.decl().name().str() + "_" + string_of_int(n); + std::vector sorts; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + sorts.push_back(t.arg(i).get_sort()); + return ctx.function(name.c_str(), nargs, &sorts[0], t.get_sort()); + } + + Z3User::FuncDecl Z3User::RenumberPred(const FuncDecl &f, int n) + { + std::string name = f.name().str(); + name = name.substr(0,name.rfind('_')) + "_" + string_of_int(n); + int arity = f.arity(); + std::vector domain; + for(int i = 0; i < arity; i++) + domain.push_back(f.domain(i)); + return ctx.function(name.c_str(), arity, &domain[0], f.range()); + } + + // Scan the clause body for occurrences of the predicate unknowns + + RPFP::Term RPFP::ScanBody(hash_map &memo, + const Term &t, + hash_map &pmap, + std::vector &parms, + std::vector &nodes) + { + if(memo.find(t) != memo.end()) + return memo[t]; + Term res(ctx); + if (t.is_app()) { + func_decl f = t.decl(); + if(pmap.find(f) != pmap.end()){ + nodes.push_back(pmap[f]); + f = SuffixFuncDecl(t,parms.size()); + parms.push_back(f); + } + int nargs = t.num_args(); + std::vector args; + for(int i = 0; i < nargs; i++) + args.push_back(ScanBody(memo,t.arg(i),pmap,parms,nodes)); + res = f(nargs,&args[0]); + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,ScanBody(memo,t.body(),pmap,parms,nodes)); + else + res = t; + memo[t] = res; + return res; + } + + // return the func_del of an app if it is uninterpreted + + bool Z3User::get_relation(const Term &t, func_decl &R){ + if(!t.is_app()) + return false; + R = t.decl(); + return R.get_decl_kind() == Uninterpreted; + } + + // return true if term is an individual variable + // TODO: have to check that it is not a background symbol + + bool Z3User::is_variable(const Term &t){ + if(!t.is_app()) + return t.is_var(); + return t.decl().get_decl_kind() == Uninterpreted + && t.num_args() == 0; + } + + RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t, + std::vector &lbls){ + if(memo.find(t) != memo.end()) + return memo[t]; + Term res(ctx); + if (t.is_app()){ + func_decl f = t.decl(); + std::vector names; + bool pos; + if(t.is_label(pos,names)){ + res = RemoveLabelsRec(memo,t.arg(0),lbls); + for(unsigned i = 0; i < names.size(); i++) + lbls.push_back(label_struct(names[i],res,pos)); + } + else { + int nargs = t.num_args(); + std::vector args; + for(int i = 0; i < nargs; i++) + args.push_back(RemoveLabelsRec(memo,t.arg(i),lbls)); + res = f(nargs,&args[0]); + } + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body(),lbls)); + else + res = t; + memo[t] = res; + return res; + } + + RPFP::Term RPFP::RemoveLabels(const Term &t, std::vector &lbls){ + hash_map memo ; + return RemoveLabelsRec(memo,t,lbls); + } + + RPFP::Term RPFP::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo[level].insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if(nargs == 0 && f.get_decl_kind() == Uninterpreted) + ls->declare_constant(f); // keep track of background constants + for(int i = 0; i < nargs; i++) + args.push_back(SubstBoundRec(memo, subst, level, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()){ + int bound = t.get_quantifier_num_bound(); + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = SubstBoundRec(memo, subst, level + bound, pats[i]); + res = clone_quantifier(t, SubstBoundRec(memo, subst, level + bound, t.body()), pats); + } + else if (t.is_var()) { + int idx = t.get_index_value(); + if(idx >= level && subst.find(idx-level) != subst.end()){ + res = subst[idx-level]; + } + else res = t; + } + else res = t; + return res; + } + + RPFP::Term RPFP::SubstBound(hash_map &subst, const Term &t){ + hash_map > memo; + return SubstBoundRec(memo, subst, 0, t); + } + + // Eliminate the deBruijn indices from level to level+num-1 + Z3User::Term Z3User::DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo[level].insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(DeleteBoundRec(memo, level, num, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()){ + int bound = t.get_quantifier_num_bound(); + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = DeleteBoundRec(memo, level + bound, num, pats[i]); + res = clone_quantifier(t, DeleteBoundRec(memo, level + bound, num, t.body()), pats); + } + else if (t.is_var()) { + int idx = t.get_index_value(); + if(idx >= level){ + res = ctx.make_var(idx-num,t.get_sort()); + } + else res = t; + } + else res = t; + return res; + } + + Z3User::Term Z3User::DeleteBound(int level, int num, const Term &t){ + hash_map > memo; + return DeleteBoundRec(memo, level, num, t); + } + + int Z3User::MaxIndex(hash_map &memo, const Term &t) + { + std::pair foo(t,-1); + std::pair::iterator, bool> bar = memo.insert(foo); + int &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()){ + func_decl f = t.decl(); + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++){ + int m = MaxIndex(memo, t.arg(i)); + if(m > res) + res = m; + } + } + else if (t.is_quantifier()){ + int bound = t.get_quantifier_num_bound(); + res = MaxIndex(memo,t.body()) - bound; + } + else if (t.is_var()) { + res = t.get_index_value(); + } + return res; + } + + bool Z3User::IsClosedFormula(const Term &t){ + hash_map memo; + return MaxIndex(memo,t) < 0; + } + + + /** 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. + + */ + + + static bool canonical_clause(const expr &clause){ + if(clause.decl().get_decl_kind() != Implies) + return false; + expr arg = clause.arg(1); + return arg.is_app() && (arg.decl().get_decl_kind() == False || + arg.decl().get_decl_kind() == Uninterpreted); + } + +#define USE_QE_LITE + + void RPFP::FromClauses(const std::vector &unskolemized_clauses){ + hash_map pmap; + func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); + + std::vector clauses(unskolemized_clauses); + // first, skolemize the clauses + +#ifndef USE_QE_LITE + for(unsigned i = 0; i < clauses.size(); i++){ + expr &t = clauses[i]; + if (t.is_quantifier() && t.is_quantifier_forall()) { + int bound = t.get_quantifier_num_bound(); + std::vector sorts; + std::vector names; + hash_map subst; + for(int j = 0; j < bound; j++){ + sort the_sort = t.get_quantifier_bound_sort(j); + symbol name = t.get_quantifier_bound_name(j); + expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); + subst[bound-1-j] = skolem; + } + t = SubstBound(subst,t.body()); + } + } +#else + std::vector > substs(clauses.size()); +#endif + + // create the nodes from the heads of the clauses + + for(unsigned i = 0; i < clauses.size(); i++){ + Term &clause = clauses[i]; + +#ifdef USE_QE_LITE + Term &t = clause; + if (t.is_quantifier() && t.is_quantifier_forall()) { + int bound = t.get_quantifier_num_bound(); + std::vector sorts; + std::vector names; + for(int j = 0; j < bound; j++){ + sort the_sort = t.get_quantifier_bound_sort(j); + symbol name = t.get_quantifier_bound_name(j); + expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); + substs[i][bound-1-j] = skolem; + } + clause = t.body(); + } + +#endif + + if(clause.is_app() && clause.decl().get_decl_kind() == Uninterpreted) + clause = implies(ctx.bool_val(true),clause); + if(!canonical_clause(clause)) + clause = implies((!clause).simplify(),ctx.bool_val(false)); + Term head = clause.arg(1); + func_decl R(ctx); + bool is_query = false; + if (eq(head,ctx.bool_val(false))){ + R = fail_pred; + // R = ctx.constant("@Fail", ctx.bool_sort()).decl(); + is_query = true; + } + else if(!get_relation(head,R)) + throw bad_clause("rhs must be a predicate application",i); + if(pmap.find(R) == pmap.end()){ + + // If the node doesn't exitst, create it. The Indparams + // are arbitrary, but we use the rhs arguments if they + // are variables for mnomonic value. + + hash_set seen; + std::vector Indparams; + for(unsigned j = 0; j < head.num_args(); j++){ + Term arg = head.arg(j); + if(!is_variable(arg) || seen.find(arg) != seen.end()){ + std::string name = std::string("@a_") + string_of_int(j); + arg = ctx.constant(name.c_str(),arg.get_sort()); + } + seen.insert(arg); + Indparams.push_back(arg); + } +#ifdef USE_QE_LITE + { + hash_map > sb_memo; + for(unsigned j = 0; j < Indparams.size(); j++) + Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); + } +#endif + Node *node = CreateNode(R(Indparams.size(),&Indparams[0])); + //nodes.push_back(node); + pmap[R] = node; + if (is_query) + node->Bound = CreateRelation(std::vector(), ctx.bool_val(false)); + } + } + + bool some_labels = false; + + // create the edges + + for(unsigned i = 0; i < clauses.size(); i++){ + Term clause = clauses[i]; + Term body = clause.arg(0); + Term head = clause.arg(1); + func_decl R(ctx); + if (eq(head,ctx.bool_val(false))) + R = fail_pred; + //R = ctx.constant("@Fail", ctx.bool_sort()).decl(); + else get_relation(head,R); + Node *Parent = pmap[R]; + std::vector Indparams; + hash_set seen; + for(unsigned j = 0; j < head.num_args(); j++){ + Term arg = head.arg(j); + if(!is_variable(arg) || seen.find(arg) != seen.end()){ + std::string name = std::string("@a_") + string_of_int(j); + Term var = ctx.constant(name.c_str(),arg.get_sort()); + body = body && (arg == var); + arg = var; + } + seen.insert(arg); + Indparams.push_back(arg); + } + + // We extract the relparams positionally + + std::vector Relparams; + hash_map scan_memo; + std::vector Children; + body = ScanBody(scan_memo,body,pmap,Relparams,Children); + Term labeled = body; + std::vector lbls; // TODO: throw this away for now + body = RemoveLabels(body,lbls); + if(!eq(labeled,body)) + some_labels = true; // remember if there are labels, as we then can't do qe_lite + // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. + body = body.simplify(); + +#ifdef USE_QE_LITE + std::set idxs; + if(!some_labels){ // can't do qe_lite if we have to reconstruct labels + for(unsigned j = 0; j < Indparams.size(); j++) + if(Indparams[j].is_var()) + idxs.insert(Indparams[j].get_index_value()); + body = body.qe_lite(idxs,false); + } + hash_map > sb_memo; + body = SubstBoundRec(sb_memo,substs[i],0,body); + if(some_labels) + labeled = SubstBoundRec(sb_memo,substs[i],0,labeled); + for(unsigned j = 0; j < Indparams.size(); j++) + Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); +#endif + + // Create the edge + Transformer T = CreateTransformer(Relparams,Indparams,body); + Edge *edge = CreateEdge(Parent,T,Children); + edge->labeled = labeled;; // remember for label extraction + // edges.push_back(edge); + } + + // undo hoisting of expressions out of loops + RemoveDeadNodes(); + Unhoist(); + // FuseEdges(); + } + + + // The following mess is used to undo hoisting of expressions outside loops by compilers + + expr RPFP::UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params){ + if(memo.find(w) != memo.end()) + return memo[w]; + expr res; + if(init_defs.find(w) != init_defs.end()){ + expr d = init_defs[w]; + std::vector vars; + hash_set get_vars_memo; + GetVarsRec(get_vars_memo,d,vars); + hash_map map; + for(unsigned j = 0; j < vars.size(); j++){ + expr x = vars[j]; + map[x] = UnhoistPullRec(memo,x,init_defs,const_params,const_params_inv,new_params); + } + expr defn = SubstRec(map,d); + res = defn; + } + else if(const_params_inv.find(w) == const_params_inv.end()){ + std::string old_name = w.decl().name().str(); + func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); + expr y = fresh(); + const_params[y] = w; + const_params_inv[w] = y; + new_params.push_back(y); + res = y; + } + else + res = const_params_inv[w]; + memo[w] = res; + return res; + } + + void RPFP::AddParamsToTransformer(Transformer &trans, const std::vector ¶ms){ + std::copy(params.begin(),params.end(),std::inserter(trans.IndParams,trans.IndParams.end())); + } + + expr RPFP::AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms){ + int n = app.num_args(); + std::vector args(n); + for (int i = 0; i < n; i++) + args[i] = app.arg(i); + std::copy(params.begin(),params.end(),std::inserter(args,args.end())); + return new_decl(args); + } + + expr RPFP::GetRelRec(hash_set &memo, const expr &t, const func_decl &rel){ + if(memo.find(t) != memo.end()) + return expr(); + memo.insert(t); + if (t.is_app()) + { + func_decl f = t.decl(); + if(f == rel) + return t; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++){ + expr res = GetRelRec(memo,t.arg(i),rel); + if(!res.null()) + return res; + } + } + else if (t.is_quantifier()) + return GetRelRec(memo,t.body(),rel); + return expr(); + } + + expr RPFP::GetRel(Edge *edge, int child_idx){ + func_decl &rel = edge->F.RelParams[child_idx]; + hash_set memo; + return GetRelRec(memo,edge->F.Formula,rel); + } + + void RPFP::GetDefsRec(const expr &cnst, hash_map &defs){ + if(cnst.is_app()){ + switch(cnst.decl().get_decl_kind()){ + case And: { + int n = cnst.num_args(); + for(int i = 0; i < n; i++) + GetDefsRec(cnst.arg(i),defs); + break; + } + case Equal: { + expr lhs = cnst.arg(0); + expr rhs = cnst.arg(1); + if(IsVar(lhs)) + defs[lhs] = rhs; + break; + } + default: + break; + } + } + } + + void RPFP::GetDefs(const expr &cnst, hash_map &defs){ + // GetDefsRec(IneqToEq(cnst),defs); + GetDefsRec(cnst,defs); + } + + bool RPFP::IsVar(const expr &t){ + return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; + } + + void RPFP::GetVarsRec(hash_set &memo, const expr &t, std::vector &vars){ + if(memo.find(t) != memo.end()) + return; + memo.insert(t); + if (t.is_app()) + { + if(IsVar(t)){ + vars.push_back(t); + return; + } + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++){ + GetVarsRec(memo,t.arg(i),vars); + } + } + else if (t.is_quantifier()) + GetVarsRec(memo,t.body(),vars); + } + + void RPFP::AddParamsToNode(Node *node, const std::vector ¶ms){ + int arity = node->Annotation.IndParams.size(); + std::vector domain; + for(int i = 0; i < arity; i++) + domain.push_back(node->Annotation.IndParams[i].get_sort()); + for(unsigned i = 0; i < params.size(); i++) + domain.push_back(params[i].get_sort()); + std::string old_name = node->Name.name().str(); + func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), domain, ctx.bool_sort()); + node->Name = fresh; + AddParamsToTransformer(node->Annotation,params); + AddParamsToTransformer(node->Bound,params); + AddParamsToTransformer(node->Underapprox,params); + } + + void RPFP::UnhoistLoop(Edge *loop_edge, Edge *init_edge){ + loop_edge->F.Formula = IneqToEq(loop_edge->F.Formula); + init_edge->F.Formula = IneqToEq(init_edge->F.Formula); + expr pre = GetRel(loop_edge,0); + if(pre.null()) + return; // this means the loop got simplified away + int nparams = loop_edge->F.IndParams.size(); + hash_map const_params, const_params_inv; + std::vector work_list; + // find the parameters that are constant in the loop + for(int i = 0; i < nparams; i++){ + if(eq(pre.arg(i),loop_edge->F.IndParams[i])){ + const_params[pre.arg(i)] = init_edge->F.IndParams[i]; + const_params_inv[init_edge->F.IndParams[i]] = pre.arg(i); + work_list.push_back(pre.arg(i)); + } + } + // get the definitions in the initialization + hash_map defs,memo,subst; + GetDefs(init_edge->F.Formula,defs); + // try to pull them inside the loop + std::vector new_params; + for(unsigned i = 0; i < work_list.size(); i++){ + expr v = work_list[i]; + expr w = const_params[v]; + expr def = UnhoistPullRec(memo,w,defs,const_params,const_params_inv,new_params); + if(!eq(def,v)) + subst[v] = def; + } + // do the substitutions + if(subst.empty()) + return; + subst[pre] = pre; // don't substitute inside the precondition itself + loop_edge->F.Formula = SubstRec(subst,loop_edge->F.Formula); + loop_edge->F.Formula = ElimIte(loop_edge->F.Formula); + init_edge->F.Formula = ElimIte(init_edge->F.Formula); + // add the new parameters + if(new_params.empty()) + return; + Node *parent = loop_edge->Parent; + AddParamsToNode(parent,new_params); + AddParamsToTransformer(loop_edge->F,new_params); + AddParamsToTransformer(init_edge->F,new_params); + std::vector &incoming = parent->Incoming; + for(unsigned i = 0; i < incoming.size(); i++){ + Edge *in_edge = incoming[i]; + std::vector &chs = in_edge->Children; + for(unsigned j = 0; j < chs.size(); j++) + if(chs[j] == parent){ + expr lit = GetRel(in_edge,j); + expr new_lit = AddParamsToApp(lit,parent->Name,new_params); + func_decl fd = SuffixFuncDecl(new_lit,j); + int nargs = new_lit.num_args(); + std::vector args; + for(int k = 0; k < nargs; k++) + args.push_back(new_lit.arg(k)); + new_lit = fd(nargs,&args[0]); + in_edge->F.RelParams[j] = fd; + hash_map map; + map[lit] = new_lit; + in_edge->F.Formula = SubstRec(map,in_edge->F.Formula); + } + } + } + + void RPFP::Unhoist(){ + hash_map > outgoing; + for(unsigned i = 0; i < edges.size(); i++) + outgoing[edges[i]->Parent].push_back(edges[i]); + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + std::vector &outs = outgoing[node]; + // if we're not a simple loop with one entry, give up + if(outs.size() == 2){ + for(int j = 0; j < 2; j++){ + Edge *loop_edge = outs[j]; + Edge *init_edge = outs[1-j]; + if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent){ + UnhoistLoop(loop_edge,init_edge); + break; + } + } + } + } + } + + void RPFP::FuseEdges(){ + hash_map > outgoing; + for(unsigned i = 0; i < edges.size(); i++){ + outgoing[edges[i]->Parent].push_back(edges[i]); + } + hash_set edges_to_delete; + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + std::vector &outs = outgoing[node]; + if(outs.size() > 1 && outs.size() <= 16){ + std::vector trs(outs.size()); + for(unsigned j = 0; j < outs.size(); j++) + trs[j] = &outs[j]->F; + Transformer tr = Fuse(trs); + std::vector chs; + for(unsigned j = 0; j < outs.size(); j++) + for(unsigned k = 0; k < outs[j]->Children.size(); k++) + chs.push_back(outs[j]->Children[k]); + CreateEdge(node,tr,chs); + for(unsigned j = 0; j < outs.size(); j++) + edges_to_delete.insert(outs[j]); + } + } + std::vector new_edges; + hash_set all_nodes; + for(unsigned j = 0; j < edges.size(); j++){ + if(edges_to_delete.find(edges[j]) == edges_to_delete.end()){ +#if 0 + if(all_nodes.find(edges[j]->Parent) != all_nodes.end()) + throw "help!"; + all_nodes.insert(edges[j]->Parent); +#endif + new_edges.push_back(edges[j]); + } + else + delete edges[j]; + } + edges.swap(new_edges); + } + + void RPFP::MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node){ + if(live_nodes.find(node) != live_nodes.end()) + return; + live_nodes.insert(node); + std::vector &outs = outgoing[node]; + for(unsigned i = 0; i < outs.size(); i++) + for(unsigned j = 0; j < outs[i]->Children.size(); j++) + MarkLiveNodes(outgoing, live_nodes,outs[i]->Children[j]); + } + + void RPFP::RemoveDeadNodes(){ + hash_map > outgoing; + for(unsigned i = 0; i < edges.size(); i++) + outgoing[edges[i]->Parent].push_back(edges[i]); + hash_set live_nodes; + for(unsigned i = 0; i < nodes.size(); i++) + if(!nodes[i]->Bound.IsFull()) + MarkLiveNodes(outgoing,live_nodes,nodes[i]); + std::vector new_edges; + for(unsigned j = 0; j < edges.size(); j++){ + if(live_nodes.find(edges[j]->Parent) != live_nodes.end()) + new_edges.push_back(edges[j]); + else { + Edge *edge = edges[j]; + for(unsigned int i = 0; i < edge->Children.size(); i++){ + std::vector &ic = edge->Children[i]->Incoming; + for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ + if(*it == edge){ + ic.erase(it); + break; + } + } + } + delete edge; + } + } + edges.swap(new_edges); + std::vector new_nodes; + for(unsigned j = 0; j < nodes.size(); j++){ + if(live_nodes.find(nodes[j]) != live_nodes.end()) + new_nodes.push_back(nodes[j]); + else + delete nodes[j]; + } + nodes.swap(new_nodes); + } + + void RPFP::WriteSolution(std::ostream &s){ + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + Term asgn = (node->Name)(node->Annotation.IndParams) == node->Annotation.Formula; + s << asgn << std::endl; + } + } + + void RPFP::WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s) + { + std::pair foo(t,0); + std::pair::iterator, bool> bar = memo.insert(foo); + // int &res = bar.first->second; + if(!bar.second) return; + hash_map::iterator it = e->varMap.find(t); + if (it != e->varMap.end()){ + return; + } + if (t.is_app()) + { + func_decl f = t.decl(); + // int idx; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + WriteEdgeVars(e, memo, t.arg(i),s); + if (nargs == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)){ + Term rename = HideVariable(t,e->number); + Term value = dualModel.eval(rename); + s << " (= " << t << " " << value << ")\n"; + } + } + else if (t.is_quantifier()) + WriteEdgeVars(e,memo,t.body(),s); + return; + } + + void RPFP::WriteEdgeAssignment(std::ostream &s, Edge *e){ + s << "(\n"; + hash_map memo; + WriteEdgeVars(e, memo, e->F.Formula ,s); + s << ")\n"; + } + + void RPFP::WriteCounterexample(std::ostream &s, Node *node){ + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ + Node *child = node->Outgoing->Children[i]; + if(!Empty(child)) + WriteCounterexample(s,child); + } + s << "(" << node->number << " : " << EvalNode(node) << " <- "; + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ + Node *child = node->Outgoing->Children[i]; + if(!Empty(child)) + s << " " << node->Outgoing->Children[i]->number; + } + s << ")" << std::endl; + WriteEdgeAssignment(s,node->Outgoing); + } + + RPFP::Term RPFP::ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + // int idx; + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(ToRuleRec(e, memo, t.arg(i),quants)); + hash_map::iterator rit = e->relMap.find(f); + if(rit != e->relMap.end()){ + Node* child = e->Children[rit->second]; + FuncDecl op = child->Name; + res = op(args.size(),&args[0]); + } + else { + res = f(args.size(),&args[0]); + if(nargs == 0 && t.decl().get_decl_kind() == Uninterpreted) + quants.push_back(t); + } + } + else if (t.is_quantifier()) + { + Term body = ToRuleRec(e,memo,t.body(),quants); + res = CloneQuantifier(t,body); + } + else res = t; + return res; + } + + + void RPFP::ToClauses(std::vector &cnsts, FileFormat format){ + cnsts.resize(edges.size()); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *edge = edges[i]; + SetEdgeMaps(edge); + std::vector quants; + hash_map memo; + Term lhs = ToRuleRec(edge, memo, edge->F.Formula,quants); + Term rhs = (edge->Parent->Name)(edge->F.IndParams.size(),&edge->F.IndParams[0]); + for(unsigned j = 0; j < edge->F.IndParams.size(); j++) + ToRuleRec(edge,memo,edge->F.IndParams[j],quants); // just to get quants + Term cnst = implies(lhs,rhs); +#if 0 + for(unsigned i = 0; i < quants.size(); i++){ + std::cout << expr(ctx,(Z3_ast)quants[i]) << " : " << sort(ctx,Z3_get_sort(ctx,(Z3_ast)quants[i])) << std::endl; + } +#endif + if(format != DualityFormat) + cnst= forall(quants,cnst); + cnsts[i] = cnst; + } + // int num_rules = cnsts.size(); + + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + if(!node->Bound.IsFull()){ + Term lhs = (node->Name)(node->Bound.IndParams) && !node->Bound.Formula; + Term cnst = implies(lhs,ctx.bool_val(false)); + if(format != DualityFormat){ + std::vector quants; + for(unsigned j = 0; j < node->Bound.IndParams.size(); j++) + quants.push_back(node->Bound.IndParams[j]); + if(format == HornFormat) + cnst= exists(quants,!cnst); + else + cnst= forall(quants, cnst); + } + cnsts.push_back(cnst); + } + } + + } + + + bool RPFP::proof_core_contains(const expr &e){ + return proof_core->find(e) != proof_core->end(); + } + + bool RPFP_caching::proof_core_contains(const expr &e){ + std::vector foo; + GetAssumptionLits(e,foo); + for(unsigned i = 0; i < foo.size(); i++) + if(proof_core->find(foo[i]) != proof_core->end()) + return true; + return false; + } + + bool RPFP::EdgeUsedInProof(Edge *edge){ + ComputeProofCore(); + if(!edge->dual.null() && proof_core_contains(edge->dual)) + return true; + for(unsigned i = 0; i < edge->constraints.size(); i++) + if(proof_core_contains(edge->constraints[i])) + return true; + return false; + } + +RPFP::~RPFP(){ + ClearProofCore(); + for(unsigned i = 0; i < nodes.size(); i++) + delete nodes[i]; + for(unsigned i = 0; i < edges.size(); i++) + delete edges[i]; + } +} + +#if 0 +void show_ast(expr *a){ + std::cout << *a; +} +#endif diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp old mode 100755 new mode 100644 index dc261a351d1..ff3bc190b4f --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1,3132 +1,3132 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - duality_solver.h - -Abstract: - - implements relational post-fixedpoint problem - (RPFP) solver - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include "duality.h" -#include "duality_profiling.h" - -#include -#include -#include -#include -#include - -// TODO: make these official options or get rid of them - -#define NEW_CAND_SEL -// #define LOCALIZE_CONJECTURES -// #define CANDS_FROM_UPDATES -#define CANDS_FROM_COVER_FAIL -#define DEPTH_FIRST_EXPAND -#define MINIMIZE_CANDIDATES -// #define MINIMIZE_CANDIDATES_HARDER -#define BOUNDED -// #define CHECK_CANDS_FROM_IND_SET -#define UNDERAPPROX_NODES -#define NEW_EXPAND -#define EARLY_EXPAND -// #define TOP_DOWN -// #define EFFORT_BOUNDED_STRAT -#define SKIP_UNDERAPPROX_NODES -// #define KEEP_EXPANSIONS -// #define USE_CACHING_RPFP -// #define PROPAGATE_BEFORE_CHECK -#define NEW_STRATIFIED_INLINING - -#define USE_RPFP_CLONE -#define USE_NEW_GEN_CANDS - -//#define NO_PROPAGATE -//#define NO_GENERALIZE -//#define NO_DECISIONS - -namespace Duality { - - // TODO: must be a better place for this... - static char string_of_int_buffer[20]; - - static const char *string_of_int(int n){ - sprintf(string_of_int_buffer,"%d",n); - return string_of_int_buffer; - } - - /** Generic object for producing diagnostic output. */ - - class Reporter { - protected: - RPFP *rpfp; - public: - Reporter(RPFP *_rpfp){ - rpfp = _rpfp; - } - virtual void Extend(RPFP::Node *node){} - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){} - virtual void Bound(RPFP::Node *node){} - virtual void Expand(RPFP::Edge *edge){} - virtual void AddCover(RPFP::Node *covered, std::vector &covering){} - virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){} - virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){} - virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){} - virtual void Dominates(RPFP::Node *node, RPFP::Node *other){} - virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){} - virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} - virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} - virtual void Message(const std::string &msg){} - virtual void Depth(int){} - virtual ~Reporter(){} - }; - - Reporter *CreateStdoutReporter(RPFP *rpfp); - - /** Object we throw in case of catastrophe. */ - - struct InternalError { - std::string msg; - InternalError(const std::string _msg) - : msg(_msg) {} - }; - - - /** This is the main solver. It takes anarbitrary (possibly cyclic) - RPFP and either annotates it with a solution, or returns a - counterexample derivation in the form of an embedd RPFP tree. */ - - class Duality : public Solver { - - public: - Duality(RPFP *_rpfp) - : ctx(_rpfp->ctx), - slvr(_rpfp->slvr()), - nodes(_rpfp->nodes), - edges(_rpfp->edges) - { - rpfp = _rpfp; - reporter = 0; - heuristic = 0; - unwinding = 0; - FullExpand = false; - NoConj = false; - FeasibleEdges = true; - UseUnderapprox = true; - Report = false; - StratifiedInlining = false; - RecursionBound = -1; - BatchExpand = false; - { - scoped_no_proof no_proofs_please(ctx.m()); -#ifdef USE_RPFP_CLONE - clone_rpfp = new RPFP_caching(rpfp->ls); - clone_rpfp->Clone(rpfp); -#endif -#ifdef USE_NEW_GEN_CANDS - gen_cands_rpfp = new RPFP_caching(rpfp->ls); - gen_cands_rpfp->Clone(rpfp); -#endif - } - } - - ~Duality(){ -#ifdef USE_RPFP_CLONE - delete clone_rpfp; -#endif -#ifdef USE_NEW_GEN_CANDS - delete gen_cands_rpfp; -#endif - if(unwinding) delete unwinding; - } - -#ifdef USE_RPFP_CLONE - RPFP_caching *clone_rpfp; -#endif -#ifdef USE_NEW_GEN_CANDS - RPFP_caching *gen_cands_rpfp; -#endif - - - typedef RPFP::Node Node; - typedef RPFP::Edge Edge; - - /** This struct represents a candidate for extending the - unwinding. It consists of an edge to instantiate - and a vector of children for the new instance. */ - - struct Candidate { - Edge *edge; std::vector - Children; - }; - - /** Comparison operator, allowing us to sort Nodes - by their number field. */ - - struct lnode - { - bool operator()(const Node* s1, const Node* s2) const - { - return s1->number < s2->number; - } - }; - - typedef std::set Unexpanded; // sorted set of Nodes - - /** This class provides a heuristic for expanding a derivation - tree. */ - - class Heuristic { - RPFP *rpfp; - - /** Heuristic score for unwinding nodes. Currently this - counts the number of updates. */ - struct score { - int updates; - score() : updates(0) {} - }; - hash_map scores; - - public: - Heuristic(RPFP *_rpfp){ - rpfp = _rpfp; - } - - virtual ~Heuristic(){} - - virtual void Update(RPFP::Node *node){ - scores[node].updates++; - } - - /** Heuristic choice of nodes to expand. Takes a set "choices" - and returns a subset "best". We currently choose the - nodes with the fewest updates. - */ -#if 0 - virtual void ChooseExpand(const std::set &choices, std::set &best){ - int best_score = INT_MAX; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it)->map; - int score = scores[node].updates; - best_score = std::min(best_score,score); - } - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) - if(scores[(*it)->map].updates == best_score) - best.insert(*it); - } -#else - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false, bool best_only=false){ - if(high_priority) return; - int best_score = INT_MAX; - int worst_score = 0; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it)->map; - int score = scores[node].updates; - best_score = std::min(best_score,score); - worst_score = std::max(worst_score,score); - } - int cutoff = best_only ? best_score : (best_score + (worst_score-best_score)/2); - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) - if(scores[(*it)->map].updates <= cutoff) - best.insert(*it); - } -#endif - - /** Called when done expanding a tree */ - virtual void Done() {} - }; - - /** The Proposer class proposes conjectures eagerly. These can come - from any source, including predicate abstraction, templates, or - previous solver runs. The proposed conjectures are checked - with low effort when the unwinding is expanded. - */ - - class Proposer { - public: - /** Given a node in the unwinding, propose some conjectures */ - virtual std::vector &Propose(Node *node) = 0; - virtual ~Proposer(){}; - }; - - - class Covering; // see below - - // These members represent the state of the algorithm. - - RPFP *rpfp; // the input RPFP - Reporter *reporter; // object for logging - Heuristic *heuristic; // expansion heuristic - context &ctx; // Z3 context - solver &slvr; // Z3 solver - std::vector &nodes; // Nodes of input RPFP - std::vector &edges; // Edges of input RPFP - std::vector leaves; // leaf nodes of unwinding (unused) - Unexpanded unexpanded; // unexpanded nodes - std::list candidates; // candidates for expansion - // maps children to edges in input RPFP - hash_map > edges_by_child; - // maps each node in input RPFP to its expanded instances - hash_map > insts_of_node; - // maps each node in input RPFP to all its instances - hash_map > all_of_node; - RPFP *unwinding; // the unwinding - Covering *indset; // proposed inductive subset - Counterexample cex; // counterexample - std::list to_expand; - hash_set updated_nodes; - hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate - int last_decisions; - hash_set overapproxes; - std::vector proposers; - -#ifdef BOUNDED - struct Counter { - int val; - Counter(){val = 0;} - }; - typedef std::map NodeToCounter; - hash_map back_edges; // counts of back edges -#endif - - /** Solve the problem. */ - virtual bool Solve(){ - reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); -#ifndef LOCALIZE_CONJECTURES - heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); -#else - heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) - : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); -#endif - cex.clear(); // in case we didn't use it for heuristic - if(unwinding) delete unwinding; - unwinding = new RPFP(rpfp->ls); - unwinding->HornClauses = rpfp->HornClauses; - indset = new Covering(this); - last_decisions = 0; - CreateEdgesByChildMap(); -#ifndef TOP_DOWN - CreateInitialUnwinding(); -#else - CreateLeaves(); - for(unsigned i = 0; i < leaves.size(); i++) - if(!SatisfyUpperBound(leaves[i])) - return false; -#endif - StratifiedLeafCount = -1; - timer_start("SolveMain"); - bool res = SolveMain(); // does the actual work - timer_stop("SolveMain"); - // print_profile(std::cout); - delete indset; - delete heuristic; - // delete unwinding; // keep the unwinding for future mining of predicates - delete reporter; - for(unsigned i = 0; i < proposers.size(); i++) - delete proposers[i]; - return res; - } - - void CreateInitialUnwinding(){ - if(!StratifiedInlining){ - CreateLeaves(); - if(FeasibleEdges)NullaryCandidates(); - else InstantiateAllEdges(); - } - else { -#ifdef NEW_STRATIFIED_INLINING - -#else - CreateLeaves(); -#endif - } - - } - - void Cancel(){ - // TODO - } - -#if 0 - virtual void Restart(RPFP *_rpfp){ - rpfp = _rpfp; - delete unwinding; - nodes = _rpfp->nodes; - edges = _rpfp->edges; - leaves.clear(); - unexpanded.clear(); // unexpanded nodes - candidates.clear(); // candidates for expansion - edges_by_child.clear(); - insts_of_node.clear(); - all_of_node.clear(); - to_expand.clear(); - } -#endif - - virtual void LearnFrom(Solver *other_solver){ - // get the counterexample as a guide - cex.swap(other_solver->GetCounterexample()); - - // propose conjectures based on old unwinding - Duality *old_duality = dynamic_cast(other_solver); - if(old_duality) - proposers.push_back(new HistoryProposer(old_duality,this)); - } - - /** Return a reference to the counterexample */ - virtual Counterexample &GetCounterexample(){ - return cex; - } - - // options - bool FullExpand; // do not use partial expansion of derivation tree - bool NoConj; // do not use conjectures (no forced covering) - bool FeasibleEdges; // use only feasible edges in unwinding - bool UseUnderapprox; // use underapproximations - bool Report; // spew on stdout - bool StratifiedInlining; // Do stratified inlining as preprocessing step - int RecursionBound; // Recursion bound for bounded verification - bool BatchExpand; - - bool SetBoolOption(bool &opt, const std::string &value){ - if(value == "0") { - opt = false; - return true; - } - if(value == "1") { - opt = true; - return true; - } - return false; - } - - bool SetIntOption(int &opt, const std::string &value){ - opt = atoi(value.c_str()); - return true; - } - - /** Set options (not currently used) */ - virtual bool SetOption(const std::string &option, const std::string &value){ - if(option == "full_expand"){ - return SetBoolOption(FullExpand,value); - } - if(option == "no_conj"){ - return SetBoolOption(NoConj,value); - } - if(option == "feasible_edges"){ - return SetBoolOption(FeasibleEdges,value); - } - if(option == "use_underapprox"){ - return SetBoolOption(UseUnderapprox,value); - } - if(option == "report"){ - return SetBoolOption(Report,value); - } - if(option == "stratified_inlining"){ - return SetBoolOption(StratifiedInlining,value); - } - if(option == "batch_expand"){ - return SetBoolOption(BatchExpand,value); - } - if(option == "recursion_bound"){ - return SetIntOption(RecursionBound,value); - } - return false; - } - - /** Create an instance of a node in the unwinding. Set its - annotation to true, and mark it unexpanded. */ - Node* CreateNodeInstance(Node *node, int number = 0){ - RPFP::Node *inst = unwinding->CloneNode(node); - inst->Annotation.SetFull(); - if(number < 0) inst->number = number; - unexpanded.insert(inst); - all_of_node[node].push_back(inst); - return inst; - } - - /** Create an instance of an edge in the unwinding, with given - parent and children. */ - void CreateEdgeInstance(Edge *edge, Node *parent, const std::vector &children){ - RPFP::Edge *inst = unwinding->CreateEdge(parent,edge->F,children); - inst->map = edge; - } - - void MakeLeaf(Node *node, bool do_not_expand = false){ - node->Annotation.SetEmpty(); - Edge *e = unwinding->CreateLowerBoundEdge(node); -#ifdef TOP_DOWN - node->Annotation.SetFull(); // allow this node to cover others -#endif - if(StratifiedInlining) - node->Annotation.SetFull(); // allow this node to cover others - else - updated_nodes.insert(node); - e->map = 0; - reporter->Extend(node); -#ifdef EARLY_EXPAND - if(!do_not_expand) - TryExpandNode(node); -#endif - // e->F.SetEmpty(); - } - - void MakeOverapprox(Node *node){ - node->Annotation.SetFull(); - Edge *e = unwinding->CreateLowerBoundEdge(node); - overapproxes.insert(node); - e->map = 0; - } - - /** We start the unwinding with leaves that under-approximate - each relation with false. */ - void CreateLeaves(){ - unexpanded.clear(); - leaves.clear(); - for(unsigned i = 0; i < nodes.size(); i++){ - RPFP::Node *node = CreateNodeInstance(nodes[i]); - if(0 && nodes[i]->Outgoing->Children.size() == 0) - CreateEdgeInstance(nodes[i]->Outgoing,node,std::vector()); - else { - if(!StratifiedInlining) - MakeLeaf(node); - else { - MakeOverapprox(node); - LeafMap[nodes[i]] = node; - } - } - leaves.push_back(node); - } - } - - /** Create the map from children to edges in the input RPFP. This - is used to generate candidates for expansion. */ - void CreateEdgesByChildMap(){ - edges_by_child.clear(); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *e = edges[i]; - std::set done; - for(unsigned j = 0; j < e->Children.size(); j++){ - Node *c = e->Children[j]; - if(done.find(c) == done.end()) // avoid duplicates - edges_by_child[c].push_back(e); - done.insert(c); - } - } - } - - void NullaryCandidates(){ - for(unsigned i = 0; i < edges.size(); i++){ - RPFP::Edge *edge = edges[i]; - if(edge->Children.size() == 0){ - Candidate cand; - cand.edge = edge; - candidates.push_back(cand); - } - } - } - - void InstantiateAllEdges(){ - hash_map leaf_map; - for(unsigned i = 0; i < leaves.size(); i++){ - leaf_map[leaves[i]->map] = leaves[i]; - insts_of_node[leaves[i]->map].push_back(leaves[i]); - } - unexpanded.clear(); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - Candidate c; c.edge = edge; - c.Children.resize(edge->Children.size()); - for(unsigned j = 0; j < c.Children.size(); j++) - c.Children[j] = leaf_map[edge->Children[j]]; - Node *new_node; - Extend(c,new_node); -#ifdef EARLY_EXPAND - TryExpandNode(new_node); -#endif - } - for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it) - indset->Add(*it); - for(unsigned i = 0; i < leaves.size(); i++){ - std::vector &foo = insts_of_node[leaves[i]->map]; - foo.erase(foo.begin()); - } - } - - bool ProducedBySI(Edge *edge, std::vector &children){ - if(LeafMap.find(edge->Parent) == LeafMap.end()) return false; - Node *other = LeafMap[edge->Parent]; - if(other->Outgoing->map != edge) return false; - std::vector &ochs = other->Outgoing->Children; - for(unsigned i = 0; i < children.size(); i++) - if(ochs[i] != children[i]) return false; - return true; - } - - /** Add a candidate for expansion, but not if Stratified inlining has already - produced it */ - - void AddCandidate(Edge *edge, std::vector &children){ - if(StratifiedInlining && ProducedBySI(edge,children)) - return; - candidates.push_back(Candidate()); - candidates.back().edge = edge; - candidates.back().Children = children; - } - - /** Generate candidates for expansion, given a vector of candidate - sets for each argument position. This recursively produces - the cross product. - */ - void GenCandidatesRec(int pos, Edge *edge, - const std::vector > &vec, - std::vector &children){ - if(pos == (int)vec.size()){ - AddCandidate(edge,children); - } - else { - for(unsigned i = 0; i < vec[pos].size(); i++){ - children[pos] = vec[pos][i]; - GenCandidatesRec(pos+1,edge,vec,children); - } - } - } - - /** Setup for above recursion. */ - void GenCandidates(int pos, Edge *edge, - const std::vector > &vec){ - std::vector children(vec.size()); - GenCandidatesRec(0,edge,vec,children); - } - - /** Expand a node. We find all the candidates for expansion using - this node and other already expanded nodes. This is a little - tricky, since a node may be used for multiple argument - positions of an edge, and we don't want to produce duplicates. - */ - -#ifndef NEW_EXPAND - void ExpandNode(Node *node){ - std::vector &nedges = edges_by_child[node->map]; - for(unsigned i = 0; i < nedges.size(); i++){ - Edge *edge = nedges[i]; - for(unsigned npos = 0; npos < edge->Children.size(); ++npos){ - if(edge->Children[npos] == node->map){ - std::vector > vec(edge->Children.size()); - vec[npos].push_back(node); - for(unsigned j = 0; j < edge->Children.size(); j++){ - if(j != npos){ - std::vector &insts = insts_of_node[edge->Children[j]]; - for(unsigned k = 0; k < insts.size(); k++) - if(indset->Candidate(insts[k])) - vec[j].push_back(insts[k]); - } - if(j < npos && edge->Children[j] == node->map) - vec[j].push_back(node); - } - GenCandidates(0,edge,vec); - } - } - } - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } -#else - /** If the current proposed solution is not inductive, - use the induction failure to generate candidates for extension. */ - void ExpandNode(Node *node){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - timer_start("GenCandIndFailUsing"); - std::vector &nedges = edges_by_child[node->map]; - for(unsigned i = 0; i < nedges.size(); i++){ - Edge *edge = nedges[i]; - slvr.push(); - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerJustForEdge(edge,checker,true); - if(root){ - expr using_cond = ctx.bool_val(false); - for(unsigned npos = 0; npos < edge->Children.size(); ++npos) - if(edge->Children[npos] == node->map) - using_cond = using_cond || checker->Localize(root->Outgoing->Children[npos]->Outgoing,NodeMarker(node)); - slvr.add(using_cond); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - } - slvr.pop(1); - delete checker; - } - timer_stop("GenCandIndFailUsing"); - } -#endif - - void ExpandNodeFromOther(Node *node, Node *other){ - std::vector &in = other->Incoming; - for(unsigned i = 0; i < in.size(); i++){ - Edge *edge = in[i]; - Candidate cand; - cand.edge = edge->map; - cand.Children = edge->Children; - for(unsigned j = 0; j < cand.Children.size(); j++) - if(cand.Children[j] == other) - cand.Children[j] = node; - candidates.push_front(cand); - } - // unexpanded.erase(node); - // insts_of_node[node->map].push_back(node); - } - - /** Expand a node based on some uncovered node it dominates. - This pushes cahdidates onto the *front* of the candidate - queue, so these expansions are done depth-first. */ - bool ExpandNodeFromCoverFail(Node *node){ - if(!node->Outgoing || node->Outgoing->Children.size() == 0) - return false; - Node *other = indset->GetSimilarNode(node); - if(!other) - return false; -#ifdef UNDERAPPROX_NODES - Node *under_node = CreateUnderapproxNode(node); - underapprox_map[under_node] = node; - indset->CoverByNode(node,under_node); - ExpandNodeFromOther(under_node,other); - ExpandNode(under_node); -#else - ExpandNodeFromOther(node,other); - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); -#endif - return true; - } - - - /** Make a boolean variable to act as a "marker" for a node. */ - expr NodeMarker(Node *node){ - std::string name = std::string("@m_") + string_of_int(node->number); - return ctx.constant(name.c_str(),ctx.bool_sort()); - } - - /** Union the annotation of dst into src. If with_markers is - true, we conjoin the annotation formula of dst with its - marker. This allows us to discover which disjunct is - true in a satisfying assignment. */ - void UnionAnnotations(RPFP::Transformer &dst, Node *src, bool with_markers = false){ - if(!with_markers) - dst.UnionWith(src->Annotation); - else { - RPFP::Transformer t = src->Annotation; - t.Formula = t.Formula && NodeMarker(src); - dst.UnionWith(t); - } - } - - void GenNodeSolutionFromIndSet(Node *node, RPFP::Transformer &annot, bool with_markers = false){ - annot.SetEmpty(); - std::vector &insts = insts_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++) - if(indset->Contains(insts[j])) - UnionAnnotations(annot,insts[j],with_markers); - annot.Simplify(); - } - - /** Generate a proposed solution of the input RPFP from - the unwinding, by unioning the instances of each node. */ - void GenSolutionFromIndSet(bool with_markers = false){ - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - GenNodeSolutionFromIndSet(node,node->Annotation,with_markers); - } - } - -#ifdef BOUNDED - bool NodePastRecursionBound(Node *node){ - if(RecursionBound < 0) return false; - NodeToCounter &backs = back_edges[node]; - for(NodeToCounter::iterator it = backs.begin(), en = backs.end(); it != en; ++it){ - if(it->second.val > RecursionBound) - return true; - } - return false; - } -#endif - - /** Test whether a given extension candidate actually represents - an induction failure. Right now we approximate this: if - the resulting node in the unwinding could be labeled false, - it clearly is not an induction failure. */ - - bool CandidateFeasible(const Candidate &cand){ - if(!FeasibleEdges) return true; - timer_start("CandidateFeasible"); - RPFP *checker = new RPFP(rpfp->ls); - // std::cout << "Checking feasibility of extension " << cand.edge->Parent->number << std::endl; - checker->Push(); - std::vector chs(cand.Children.size()); - Node *root = checker->CloneNode(cand.edge->Parent); -#ifdef BOUNDED - for(unsigned i = 0; i < cand.Children.size(); i++) - if(NodePastRecursionBound(cand.Children[i])){ - timer_stop("CandidateFeasible"); - return false; - } -#endif -#ifdef NEW_CAND_SEL - GenNodeSolutionFromIndSet(cand.edge->Parent,root->Bound); -#else - root->Bound.SetEmpty(); -#endif - checker->AssertNode(root); - for(unsigned i = 0; i < cand.Children.size(); i++) - chs[i] = checker->CloneNode(cand.Children[i]); - Edge *e = checker->CreateEdge(root,cand.edge->F,chs); - checker->AssertEdge(e,0,true); - // std::cout << "Checking SAT: " << e->dual << std::endl; - bool res = checker->Check(root) != unsat; - // std::cout << "Result: " << res << std::endl; - if(!res)reporter->Reject(cand.edge,cand.Children); - checker->Pop(1); - delete checker; - timer_stop("CandidateFeasible"); - return res; - } - - - hash_map TopoSort; - int TopoSortCounter; - std::vector SortedEdges; - - void DoTopoSortRec(Node *node){ - if(TopoSort.find(node) != TopoSort.end()) - return; - TopoSort[node] = INT_MAX; // just to break cycles - Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge - if(edge){ - std::vector &chs = edge->Children; - for(unsigned i = 0; i < chs.size(); i++) - DoTopoSortRec(chs[i]); - } - TopoSort[node] = TopoSortCounter++; - SortedEdges.push_back(edge); - } - - void DoTopoSort(){ - TopoSort.clear(); - SortedEdges.clear(); - TopoSortCounter = 0; - for(unsigned i = 0; i < nodes.size(); i++) - DoTopoSortRec(nodes[i]); - } - - - int StratifiedLeafCount; - -#ifdef NEW_STRATIFIED_INLINING - - /** Stratified inlining builds an initial layered unwinding before - switching to the LAWI strategy. Currently the number of layers - is one. Only nodes that are the targets of back edges are - consider to be leaves. This assumes we have already computed a - topological sort. - */ - - bool DoStratifiedInlining(){ - DoTopoSort(); - int depth = 1; // TODO: make this an option - std::vector > unfolding_levels(depth+1); - for(int level = 1; level <= depth; level++) - for(unsigned i = 0; i < SortedEdges.size(); i++){ - Edge *edge = SortedEdges[i]; - Node *parent = edge->Parent; - std::vector &chs = edge->Children; - std::vector my_chs(chs.size()); - for(unsigned j = 0; j < chs.size(); j++){ - Node *child = chs[j]; - int ch_level = TopoSort[child] >= TopoSort[parent] ? level-1 : level; - if(unfolding_levels[ch_level].find(child) == unfolding_levels[ch_level].end()){ - if(ch_level == 0) - unfolding_levels[0][child] = CreateLeaf(child); - else - throw InternalError("in levelized unwinding"); - } - my_chs[j] = unfolding_levels[ch_level][child]; - } - Candidate cand; cand.edge = edge; cand.Children = my_chs; - Node *new_node; - bool ok = Extend(cand,new_node); - MarkExpanded(new_node); // we don't expand here -- just mark it done - if(!ok) return false; // got a counterexample - unfolding_levels[level][parent] = new_node; - } - return true; - } - - Node *CreateLeaf(Node *node){ - RPFP::Node *nchild = CreateNodeInstance(node); - MakeLeaf(nchild, /* do_not_expand = */ true); - nchild->Annotation.SetEmpty(); - return nchild; - } - - void MarkExpanded(Node *node){ - if(unexpanded.find(node) != unexpanded.end()){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } - } - -#else - - /** In stratified inlining, we build the unwinding from the bottom - down, trying to satisfy the node bounds. We do this as a pre-pass, - limiting the expansion. If we get a counterexample, we are done, - else we continue as usual expanding the unwinding upward. - */ - - bool DoStratifiedInlining(){ - timer_start("StratifiedInlining"); - DoTopoSort(); - for(unsigned i = 0; i < leaves.size(); i++){ - Node *node = leaves[i]; - bool res = SatisfyUpperBound(node); - if(!res){ - timer_stop("StratifiedInlining"); - return false; - } - } - // don't leave any dangling nodes! -#ifndef EFFORT_BOUNDED_STRAT - for(unsigned i = 0; i < leaves.size(); i++) - if(!leaves[i]->Outgoing) - MakeLeaf(leaves[i],true); -#endif - timer_stop("StratifiedInlining"); - return true; - } - -#endif - - /** Here, we do the downward expansion for stratified inlining */ - - hash_map LeafMap, StratifiedLeafMap; - - Edge *GetNodeOutgoing(Node *node, int last_decs = 0){ - if(overapproxes.find(node) == overapproxes.end()) return node->Outgoing; /* already expanded */ - overapproxes.erase(node); -#ifdef EFFORT_BOUNDED_STRAT - if(last_decs > 5000){ - // RPFP::Transformer save = node->Annotation; - node->Annotation.SetEmpty(); - Edge *e = unwinding->CreateLowerBoundEdge(node); - // node->Annotation = save; - insts_of_node[node->map].push_back(node); - // std::cout << "made leaf: " << node->number << std::endl; - return e; - } -#endif - Edge *edge = node->map->Outgoing; - std::vector &chs = edge->Children; - - // make sure we don't create a covered node in this process! - - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - if(TopoSort[child] < TopoSort[node->map]){ - Node *leaf = LeafMap[child]; - if(!indset->Contains(leaf)){ - node->Outgoing->F.Formula = ctx.bool_val(false); // make this a proper leaf, else bogus cex - return node->Outgoing; - } - } - } - - std::vector nchs(chs.size()); - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - if(TopoSort[child] < TopoSort[node->map]){ - Node *leaf = LeafMap[child]; - nchs[i] = leaf; - if(unexpanded.find(leaf) != unexpanded.end()){ - unexpanded.erase(leaf); - insts_of_node[child].push_back(leaf); - } - } - else { - if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ - RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); - MakeLeaf(nchild); - nchild->Annotation.SetEmpty(); - StratifiedLeafMap[child] = nchild; - indset->SetDominated(nchild); - } - nchs[i] = StratifiedLeafMap[child]; - } - } - CreateEdgeInstance(edge,node,nchs); - reporter->Extend(node); - return node->Outgoing; - } - - void SetHeuristicOldNode(Node *node){ - LocalHeuristic *h = dynamic_cast(heuristic); - if(h) - h->SetOldNode(node); - } - - /** This does the actual solving work. We try to generate - candidates for extension. If we succed, we extend the - unwinding. If we fail, we have a solution. */ - bool SolveMain(){ - if(StratifiedInlining && !DoStratifiedInlining()) - return false; -#ifdef BOUNDED - DoTopoSort(); -#endif - while(true){ - timer_start("ProduceCandidatesForExtension"); - ProduceCandidatesForExtension(); - timer_stop("ProduceCandidatesForExtension"); - if(candidates.empty()){ - GenSolutionFromIndSet(); - return true; - } - Candidate cand = candidates.front(); - candidates.pop_front(); - if(CandidateFeasible(cand)){ - Node *new_node; - if(!Extend(cand,new_node)) - return false; -#ifdef EARLY_EXPAND - TryExpandNode(new_node); -#endif - } - } - } - - // hack: put something local into the underapproximation formula - // without this, interpolants can be pretty bad - void AddThing(expr &conj){ - std::string name = "@thing"; - expr thing = ctx.constant(name.c_str(),ctx.bool_sort()); - if(conj.is_app() && conj.decl().get_decl_kind() == And){ - std::vector conjs(conj.num_args()+1); - for(unsigned i = 0; i+1 < conjs.size(); i++) - conjs[i] = conj.arg(i); - conjs[conjs.size()-1] = thing; - conj = rpfp->conjoin(conjs); - } - } - - - Node *CreateUnderapproxNode(Node *node){ - // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); - RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); - under_node->Annotation.IntersectWith(cex.get_root()->Underapprox); - AddThing(under_node->Annotation.Formula); - Edge *e = unwinding->CreateLowerBoundEdge(under_node); - under_node->Annotation.SetFull(); // allow this node to cover others - back_edges[under_node] = back_edges[node]; - e->map = 0; - reporter->Extend(under_node); - return under_node; - } - - /** Try to prove a conjecture about a node. If successful - update the unwinding annotation appropriately. */ - bool ProveConjecture(Node *node, const RPFP::Transformer &t,Node *other = 0, Counterexample *_cex = 0){ - reporter->Conjecture(node,t); - timer_start("ProveConjecture"); - RPFP::Transformer save = node->Bound; - node->Bound.IntersectWith(t); - -#ifndef LOCALIZE_CONJECTURES - bool ok = SatisfyUpperBound(node); -#else - SetHeuristicOldNode(other); - bool ok = SatisfyUpperBound(node); - SetHeuristicOldNode(0); -#endif - - if(ok){ - timer_stop("ProveConjecture"); - return true; - } -#ifdef UNDERAPPROX_NODES - if(UseUnderapprox && last_decisions > 500){ - std::cout << "making an underapprox\n"; - ExpandNodeFromCoverFail(node); - } -#endif - if(_cex) (*_cex).swap(cex); // return the cex if asked - cex.clear(); // throw away the useless cex - node->Bound = save; // put back original bound - timer_stop("ProveConjecture"); - return false; - } - - /** If a node is part of the inductive subset, expand it. - We ask the inductive subset to exclude the node if possible. - */ - void TryExpandNode(RPFP::Node *node){ - if(indset->Close(node)) return; - if(!NoConj && indset->Conjecture(node)){ -#ifdef UNDERAPPROX_NODES - /* TODO: temporary fix. this prevents an infinite loop in case - the node is covered by multiple others. This should be - removed when covering by a set is implemented. - */ - if(indset->Contains(node)){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } -#endif - return; - } -#ifdef UNDERAPPROX_NODES - if(!indset->Contains(node)) - return; // could be covered by an underapprox node -#endif - indset->Add(node); -#if defined(CANDS_FROM_COVER_FAIL) && !defined(UNDERAPPROX_NODES) - if(ExpandNodeFromCoverFail(node)) - return; -#endif - ExpandNode(node); - } - - /** Make the conjunction of markers for all (expanded) instances of - a node in the input RPFP. */ - expr AllNodeMarkers(Node *node){ - expr res = ctx.bool_val(true); - std::vector &insts = insts_of_node[node]; - for(int k = insts.size()-1; k >= 0; k--) - res = res && NodeMarker(insts[k]); - return res; - } - - void RuleOutNodesPastBound(Node *node, RPFP::Transformer &t){ -#ifdef BOUNDED - if(RecursionBound < 0)return; - std::vector &insts = insts_of_node[node]; - for(unsigned i = 0; i < insts.size(); i++) - if(NodePastRecursionBound(insts[i])) - t.Formula = t.Formula && !NodeMarker(insts[i]); -#endif - } - - - void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction){ -#ifdef BOUNDED - if(RecursionBound >= 0 && NodePastRecursionBound(node)) - return; -#endif - RPFP::Transformer temp = node->Annotation; - expr marker = NodeMarker(node); - temp.Formula = (!marker || temp.Formula); - annot.IntersectWith(temp); - marker_disjunction = marker_disjunction || marker; - } - - bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false){ - bool res = false; - annot.SetFull(); - expr marker_disjunction = ctx.bool_val(false); - std::vector &insts = expanded_only ? insts_of_node[node] : all_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++){ - Node *node = insts[j]; - if(indset->Contains(insts[j])){ - GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction); res = true; - } - } - annot.Formula = annot.Formula && marker_disjunction; - annot.Simplify(); - return res; - } - - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerJustForEdge(Edge *edge, RPFP *checker, bool expanded_only = false){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); - if(root->Bound.IsFull()) - return 0; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - if(!GenNodeSolutionWithMarkers(oc,nc->Annotation,expanded_only)) - return 0; - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - -#ifndef MINIMIZE_CANDIDATES_HARDER - -#if 0 - /** Make a checker to detheermine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - root->Bound = edge->Parent->Annotation; - root->Bound.Formula = (!AllNodeMarkers(edge->Parent)) || root->Bound.Formula; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - nc->Annotation = oc->Annotation; - RuleOutNodesPastBound(oc,nc->Annotation); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - -#else - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); -#if 0 - if(root->Bound.IsFull()) - return = 0; -#endif - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } -#endif - - /** If an edge is not satisfied, produce an extension candidate - using instances of its children that violate the parent annotation. - We find these using the marker predicates. */ - void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ - candidate.edge = edge; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *node = root->Outgoing->Children[j]; - Edge *lb = node->Outgoing; - std::vector &insts = insts_of_node[edge->Children[j]]; -#ifndef MINIMIZE_CANDIDATES - for(int k = insts.size()-1; k >= 0; k--) -#else - for(unsigned k = 0; k < insts.size(); k++) -#endif - { - Node *inst = insts[k]; - if(indset->Contains(inst)){ - if(checker->Empty(node) || - eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst)),ctx.bool_val(true))){ - candidate.Children.push_back(inst); - goto next_child; - } - } - } - throw InternalError("No candidate from induction failure"); - next_child:; - } - } -#else - - - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); - if(root->Bound.IsFull()) - return = 0; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - - /** If an edge is not satisfied, produce an extension candidate - using instances of its children that violate the parent annotation. - We find these using the marker predicates. */ - void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ - candidate.edge = edge; - std::vector assumps; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Edge *lb = root->Outgoing->Children[j]->Outgoing; - std::vector &insts = insts_of_node[edge->Children[j]]; - for(unsigned k = 0; k < insts.size(); k++) - { - Node *inst = insts[k]; - expr marker = NodeMarker(inst); - if(indset->Contains(inst)){ - if(checker->Empty(lb->Parent) || - eq(checker->Eval(lb,marker),ctx.bool_val(true))){ - candidate.Children.push_back(inst); - assumps.push_back(checker->Localize(lb,marker)); - goto next_child; - } - assumps.push_back(checker->Localize(lb,marker)); - if(checker->CheckUpdateModel(root,assumps) != unsat){ - candidate.Children.push_back(inst); - goto next_child; - } - assumps.pop_back(); - } - } - throw InternalError("No candidate from induction failure"); - next_child:; - } - } - -#endif - - - Node *CheckerForEdgeClone(Edge *edge, RPFP_caching *checker){ - Edge *gen_cands_edge = checker->GetEdgeClone(edge); - Node *root = gen_cands_edge->Parent; - root->Outgoing = gen_cands_edge; - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); -#if 0 - if(root->Bound.IsFull()) - return = 0; -#endif - checker->AssertNode(root); - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = gen_cands_edge->Children[j]; - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - } - checker->AssertEdge(gen_cands_edge,1,true); - return root; - } - - /** If the current proposed solution is not inductive, - use the induction failure to generate candidates for extension. */ - void GenCandidatesFromInductionFailure(bool full_scan = false){ - timer_start("GenCandIndFail"); - GenSolutionFromIndSet(true /* add markers */); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) - continue; -#ifndef USE_NEW_GEN_CANDS - slvr.push(); - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerForEdge(edge,checker); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - slvr.pop(1); - delete checker; -#else - RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); - gen_cands_rpfp->Push(); - Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); - if(gen_cands_rpfp->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - gen_cands_rpfp->Pop(1); -#endif - } - updated_nodes.clear(); - timer_stop("GenCandIndFail"); -#ifdef CHECK_CANDS_FROM_IND_SET - for(std::list::iterator it = candidates.begin(), en = candidates.end(); it != en; ++it){ - if(!CandidateFeasible(*it)) - throw "produced infeasible candidate"; - } -#endif - if(!full_scan && candidates.empty()){ - reporter->Message("No candidates from updates. Trying full scan."); - GenCandidatesFromInductionFailure(true); - } - } - -#ifdef CANDS_FROM_UPDATES - /** If the given edge is not inductive in the current proposed solution, - use the induction failure to generate candidates for extension. */ - void GenCandidatesFromEdgeInductionFailure(RPFP::Edge *edge){ - GenSolutionFromIndSet(true /* add markers */); - for(unsigned i = 0; i < edges.size(); i++){ - slvr.push(); - Edge *edge = edges[i]; - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerForEdge(edge,checker); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - slvr.pop(1); - delete checker; - } - } -#endif - - /** Find the unexpanded nodes in the inductive subset. */ - void FindNodesToExpand(){ - for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it){ - Node *node = *it; - if(indset->Candidate(node)) - to_expand.push_back(node); - } - } - - /** Try to create some extension candidates from the unexpanded - nodes. */ - void ProduceSomeCandidates(){ - while(candidates.empty() && !to_expand.empty()){ - Node *node = to_expand.front(); - to_expand.pop_front(); - TryExpandNode(node); - } - } - - std::list postponed_candidates; - - /** Try to produce some extension candidates, first from unexpanded - nides, and if this fails, from induction failure. */ - void ProduceCandidatesForExtension(){ - if(candidates.empty()) - ProduceSomeCandidates(); - while(candidates.empty()){ - FindNodesToExpand(); - if(to_expand.empty()) break; - ProduceSomeCandidates(); - } - if(candidates.empty()){ -#ifdef DEPTH_FIRST_EXPAND - if(postponed_candidates.empty()){ - GenCandidatesFromInductionFailure(); - postponed_candidates.swap(candidates); - } - if(!postponed_candidates.empty()){ - candidates.push_back(postponed_candidates.front()); - postponed_candidates.pop_front(); - } -#else - GenCandidatesFromInductionFailure(); -#endif - } - } - - bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){ - if(!node->Annotation.SubsetEq(fact)){ - reporter->Update(node,fact,eager); - indset->Update(node,fact); - updated_nodes.insert(node->map); - node->Annotation.IntersectWith(fact); - return true; - } - return false; - } - - bool UpdateNodeToNode(Node *node, Node *top){ - return Update(node,top->Annotation); - } - - /** Update the unwinding solution, using an interpolant for the - derivation tree. */ - void UpdateWithInterpolant(Node *node, RPFP *tree, Node *top){ - if(top->Outgoing) - for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) - UpdateWithInterpolant(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); - UpdateNodeToNode(node, top); - heuristic->Update(node); - } - - /** Update unwinding lower bounds, using a counterexample. */ - - void UpdateWithCounterexample(Node *node, RPFP *tree, Node *top){ - if(top->Outgoing) - for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) - UpdateWithCounterexample(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); - if(!top->Underapprox.SubsetEq(node->Underapprox)){ - reporter->UpdateUnderapprox(node,top->Underapprox); - // indset->Update(node,top->Annotation); - node->Underapprox.UnionWith(top->Underapprox); - heuristic->Update(node); - } - } - - /** Try to update the unwinding to satisfy the upper bound of a - node. */ - bool SatisfyUpperBound(Node *node){ - if(node->Bound.IsFull()) return true; -#ifdef PROPAGATE_BEFORE_CHECK - Propagate(); -#endif - reporter->Bound(node); - int start_decs = rpfp->CumulativeDecisions(); - DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); - DerivationTree &dt = *dtp; - bool res = dt.Derive(unwinding,node,UseUnderapprox); - int end_decs = rpfp->CumulativeDecisions(); - // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; - last_decisions = end_decs - start_decs; - if(res){ - cex.set(dt.tree,dt.top); // note tree is now owned by cex - if(UseUnderapprox){ - UpdateWithCounterexample(node,dt.tree,dt.top); - } - } - else { - UpdateWithInterpolant(node,dt.tree,dt.top); - delete dt.tree; - } - delete dtp; - return !res; - } - - /* For a given nod in the unwinding, get conjectures from the - proposers and check them locally. Update the node with any true - conjectures. - */ - - void DoEagerDeduction(Node *node){ - for(unsigned i = 0; i < proposers.size(); i++){ - const std::vector &conjectures = proposers[i]->Propose(node); - for(unsigned j = 0; j < conjectures.size(); j++){ - const RPFP::Transformer &conjecture = conjectures[j]; - RPFP::Transformer bound(conjecture); - std::vector conj_vec; - unwinding->CollectConjuncts(bound.Formula,conj_vec); - for(unsigned k = 0; k < conj_vec.size(); k++){ - bound.Formula = conj_vec[k]; - if(CheckEdgeCaching(node->Outgoing,bound) == unsat) - Update(node,bound, /* eager = */ true); - //else - //std::cout << "conjecture failed\n"; - } - } - } - } - - - check_result CheckEdge(RPFP *checker, Edge *edge){ - Node *root = edge->Parent; - checker->Push(); - checker->AssertNode(root); - checker->AssertEdge(edge,1,true); - check_result res = checker->Check(root); - checker->Pop(1); - return res; - } - - check_result CheckEdgeCaching(Edge *unwinding_edge, const RPFP::Transformer &bound){ - - // use a dedicated solver for this edge - // TODO: can this mess be hidden somehow? - - RPFP_caching *checker = gen_cands_rpfp; // TODO: a good choice? - Edge *edge = unwinding_edge->map; // get the edge in the original RPFP - RPFP_caching::scoped_solver_for_edge ssfe(checker,edge,true /* models */, true /*axioms*/); - Edge *checker_edge = checker->GetEdgeClone(edge); - - // copy the annotations and bound to the clone - Node *root = checker_edge->Parent; - root->Bound = bound; - for(unsigned j = 0; j < checker_edge->Children.size(); j++){ - Node *oc = unwinding_edge->Children[j]; - Node *nc = checker_edge->Children[j]; - nc->Annotation = oc->Annotation; - } - - return CheckEdge(checker,checker_edge); - } - - - /* If the counterexample derivation is partial due to - use of underapproximations, complete it. */ - - void BuildFullCex(Node *node){ - DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); - bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree - if(!res) throw "Duality internal error in BuildFullCex"; - cex.set(dt.tree,dt.top); - } - - void UpdateBackEdges(Node *node){ -#ifdef BOUNDED - std::vector &chs = node->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - bool is_back = TopoSort[child->map] >= TopoSort[node->map]; - NodeToCounter &nov = back_edges[node]; - NodeToCounter chv = back_edges[child]; - if(is_back) - chv[child->map].val++; - for(NodeToCounter::iterator it = chv.begin(), en = chv.end(); it != en; ++it){ - Node *back = it->first; - Counter &c = nov[back]; - c.val = std::max(c.val,it->second.val); - } - } -#endif - } - - /** Extend the unwinding, keeping it solved. */ - bool Extend(Candidate &cand, Node *&node){ - timer_start("Extend"); - node = CreateNodeInstance(cand.edge->Parent); - CreateEdgeInstance(cand.edge,node,cand.Children); - UpdateBackEdges(node); - reporter->Extend(node); - DoEagerDeduction(node); // first be eager... - bool res = SatisfyUpperBound(node); // then be lazy - if(res) indset->CloseDescendants(node); - else { -#ifdef UNDERAPPROX_NODES - ExpandUnderapproxNodes(cex.get_tree(), cex.get_root()); -#endif - if(UseUnderapprox) BuildFullCex(node); - timer_stop("Extend"); - return res; - } - timer_stop("Extend"); - return res; - } - - void ExpandUnderapproxNodes(RPFP *tree, Node *root){ - Node *node = root->map; - if(underapprox_map.find(node) != underapprox_map.end()){ - RPFP::Transformer cnst = root->Annotation; - tree->EvalNodeAsConstraint(root, cnst); - cnst.Complement(); - Node *orig = underapprox_map[node]; - RPFP::Transformer save = orig->Bound; - orig->Bound = cnst; - DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); - bool res = dt.Derive(unwinding,orig,UseUnderapprox,true,tree); - if(!res){ - UpdateWithInterpolant(orig,dt.tree,dt.top); - throw "bogus underapprox!"; - } - ExpandUnderapproxNodes(tree,dt.top); - } - else if(root->Outgoing){ - std::vector &chs = root->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++) - ExpandUnderapproxNodes(tree,chs[i]); - } - } - - // Propagate conjuncts up the unwinding - void Propagate(){ - reporter->Message("beginning propagation"); - timer_start("Propagate"); - std::vector sorted_nodes = unwinding->nodes; - std::sort(sorted_nodes.begin(),sorted_nodes.end(),std::less()); // sorts by sequence number - hash_map > facts; - for(unsigned i = 0; i < sorted_nodes.size(); i++){ - Node *node = sorted_nodes[i]; - std::set &node_facts = facts[node->map]; - if(!(node->Outgoing && indset->Contains(node))) - continue; - std::vector conj_vec; - unwinding->CollectConjuncts(node->Annotation.Formula,conj_vec); - std::set conjs; - std::copy(conj_vec.begin(),conj_vec.end(),std::inserter(conjs,conjs.begin())); - if(!node_facts.empty()){ - RPFP *checker = new RPFP(rpfp->ls); - slvr.push(); - Node *root = checker->CloneNode(node); - Edge *edge = node->Outgoing; - // checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - nc->Annotation = oc->Annotation; // is this needed? - cs.push_back(nc); - } - Edge *checker_edge = checker->CreateEdge(root,edge->F,cs); - checker->AssertEdge(checker_edge, 0, true, false); - std::vector propagated; - for(std::set ::iterator it = node_facts.begin(), en = node_facts.end(); it != en;){ - const expr &fact = *it; - if(conjs.find(fact) == conjs.end()){ - root->Bound.Formula = fact; - slvr.push(); - checker->AssertNode(root); - check_result res = checker->Check(root); - slvr.pop(); - if(res != unsat){ - std::set ::iterator victim = it; - ++it; - node_facts.erase(victim); // if it ain't true, nix it - continue; - } - propagated.push_back(fact); - } - ++it; - } - slvr.pop(); - for(unsigned i = 0; i < propagated.size(); i++){ - root->Annotation.Formula = propagated[i]; - UpdateNodeToNode(node,root); - } - delete checker; - } - for(std::set ::iterator it = conjs.begin(), en = conjs.end(); it != en; ++it){ - expr foo = *it; - node_facts.insert(foo); - } - } - timer_stop("Propagate"); - } - - /** This class represents a derivation tree. */ - class DerivationTree { - public: - - virtual ~DerivationTree(){} - - DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) - : slvr(rpfp->slvr()), - ctx(rpfp->ctx) - { - duality = _duality; - reporter = _reporter; - heuristic = _heuristic; - full_expand = _full_expand; - } - - Duality *duality; - Reporter *reporter; - Heuristic *heuristic; - solver &slvr; - context &ctx; - RPFP *tree; - RPFP::Node *top; - std::list leaves; - bool full_expand; - bool underapprox; - bool constrained; - bool false_approx; - std::vector underapprox_core; - int start_decs, last_decs; - - /* We build derivation trees in one of three modes: - - 1) In normal mode, we build the full tree without considering - underapproximations. - - 2) In underapprox mode, we use underapproximations to cut off - the tree construction. THis means the resulting tree may not - be complete. - - 3) In constrained mode, we build the full tree but use - underapproximations as upper bounds. This mode is used to - complete the partial derivation constructed in underapprox - mode. - */ - - bool Derive(RPFP *rpfp, RPFP::Node *root, bool _underapprox, bool _constrained = false, RPFP *_tree = 0){ - underapprox = _underapprox; - constrained = _constrained; - false_approx = true; - timer_start("Derive"); -#ifndef USE_CACHING_RPFP - tree = _tree ? _tree : new RPFP(rpfp->ls); -#else - RPFP::LogicSolver *cache_ls = new RPFP::iZ3LogicSolver(ctx); - cache_ls->slvr->push(); - tree = _tree ? _tree : new RPFP_caching(cache_ls); -#endif - tree->HornClauses = rpfp->HornClauses; - tree->Push(); // so we can clear out the solver later when finished - top = CreateApproximatedInstance(root); - tree->AssertNode(top); // assert the negation of the top-level spec - timer_start("Build"); - bool res = Build(); - heuristic->Done(); - timer_stop("Build"); - timer_start("Pop"); - tree->Pop(1); - timer_stop("Pop"); -#ifdef USE_CACHING_RPFP - cache_ls->slvr->pop(1); - delete cache_ls; - tree->ls = rpfp->ls; -#endif - timer_stop("Derive"); - return res; - } - -#define WITH_CHILDREN - - void InitializeApproximatedInstance(RPFP::Node *to){ - to->Annotation = to->map->Annotation; -#ifndef WITH_CHILDREN - tree->CreateLowerBoundEdge(to); -#endif - leaves.push_back(to); - } - - Node *CreateApproximatedInstance(RPFP::Node *from){ - Node *to = tree->CloneNode(from); - InitializeApproximatedInstance(to); - return to; - } - - bool CheckWithUnderapprox(){ - timer_start("CheckWithUnderapprox"); - std::vector leaves_vector(leaves.size()); - std::copy(leaves.begin(),leaves.end(),leaves_vector.begin()); - check_result res = tree->Check(top,leaves_vector); - timer_stop("CheckWithUnderapprox"); - return res != unsat; - } - - virtual bool Build(){ -#ifdef EFFORT_BOUNDED_STRAT - start_decs = tree->CumulativeDecisions(); -#endif - while(ExpandSomeNodes(true)); // do high-priority expansions - while (true) - { -#ifndef WITH_CHILDREN - timer_start("asserting leaves"); - timer_start("pushing"); - tree->Push(); - timer_stop("pushing"); - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - tree->AssertEdge((*it)->Outgoing,1); // assert the overapproximation, and keep it past pop - timer_stop("asserting leaves"); - lbool res = tree->Solve(top, 2); // incremental solve, keep interpolants for two pops - timer_start("popping leaves"); - tree->Pop(1); - timer_stop("popping leaves"); -#else - lbool res; - if((underapprox || false_approx) && top->Outgoing && CheckWithUnderapprox()){ - if(constrained) goto expand_some_nodes; // in constrained mode, keep expanding - goto we_are_sat; // else if underapprox is sat, we stop - } - // tree->Check(top); - res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop -#endif - if (res == l_false) - return false; - - expand_some_nodes: - if(ExpandSomeNodes()) - continue; - - we_are_sat: - if(underapprox && !constrained){ - timer_start("ComputeUnderapprox"); - tree->ComputeUnderapprox(top,1); - timer_stop("ComputeUnderapprox"); - } - else { -#ifdef UNDERAPPROX_NODES -#ifndef SKIP_UNDERAPPROX_NODES - timer_start("ComputeUnderapprox"); - tree->ComputeUnderapprox(top,1); - timer_stop("ComputeUnderapprox"); -#endif -#endif - } - return true; - } - } - - virtual void ExpandNode(RPFP::Node *p){ - // tree->RemoveEdge(p->Outgoing); - Edge *ne = p->Outgoing; - if(ne) { - // reporter->Message("Recycling edge..."); - std::vector &cs = ne->Children; - for(unsigned i = 0; i < cs.size(); i++) - InitializeApproximatedInstance(cs[i]); - // ne->dual = expr(); - } - else { - Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); - std::vector &cs = edge->Children; - std::vector children(cs.size()); - for(unsigned i = 0; i < cs.size(); i++) - children[i] = CreateApproximatedInstance(cs[i]); - ne = tree->CreateEdge(p, p->map->Outgoing->F, children); - ne->map = p->map->Outgoing->map; - } -#ifndef WITH_CHILDREN - tree->AssertEdge(ne); // assert the edge in the solver -#else - tree->AssertEdge(ne,0,!full_expand,(underapprox || false_approx)); // assert the edge in the solver -#endif - reporter->Expand(ne); - } - -#define UNDERAPPROXCORE -#ifndef UNDERAPPROXCORE - void ExpansionChoices(std::set &best){ - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - if (!tree->Empty(*it)) // if used in the counter-model - choices.insert(*it); - heuristic->ChooseExpand(choices, best); - } -#else -#if 0 - - void ExpansionChoices(std::set &best){ - std::vector unused_set, used_set; - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ - Node *n = *it; - if (!tree->Empty(n)) - used_set.push_back(n); - else - unused_set.push_back(n); - } - if(tree->Check(top,unused_set) == unsat) - throw "error in ExpansionChoices"; - for(unsigned i = 0; i < used_set.size(); i++){ - Node *n = used_set[i]; - unused_set.push_back(n); - if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ - unused_set.pop_back(); - choices.insert(n); - } - else - std::cout << "Using underapprox of " << n->number << std::endl; - } - heuristic->ChooseExpand(choices, best); - } -#else - void ExpansionChoicesFull(std::set &best, bool high_priority, bool best_only = false){ - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - if (high_priority || !tree->Empty(*it)) // if used in the counter-model - choices.insert(*it); - heuristic->ChooseExpand(choices, best, high_priority, best_only); - } - - void ExpansionChoicesRec(std::vector &unused_set, std::vector &used_set, - std::set &choices, int from, int to){ - if(from == to) return; - int orig_unused = unused_set.size(); - unused_set.resize(orig_unused + (to - from)); - std::copy(used_set.begin()+from,used_set.begin()+to,unused_set.begin()+orig_unused); - if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ - unused_set.resize(orig_unused); - if(to - from == 1){ -#if 1 - std::cout << "Not using underapprox of " << used_set[from] ->number << std::endl; -#endif - choices.insert(used_set[from]); - } - else { - int mid = from + (to - from)/2; - ExpansionChoicesRec(unused_set, used_set, choices, from, mid); - ExpansionChoicesRec(unused_set, used_set, choices, mid, to); - } - } - else { -#if 1 - std::cout << "Using underapprox of "; - for(int i = from; i < to; i++){ - std::cout << used_set[i]->number << " "; - if(used_set[i]->map->Underapprox.IsEmpty()) - std::cout << "(false!) "; - } - std::cout << std::endl; -#endif - } - } - - std::set old_choices; - - void ExpansionChoices(std::set &best, bool high_priority, bool best_only = false){ - if(!underapprox || constrained || high_priority){ - ExpansionChoicesFull(best, high_priority,best_only); - return; - } - std::vector unused_set, used_set; - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ - Node *n = *it; - if (!tree->Empty(n)){ - if(old_choices.find(n) != old_choices.end() || n->map->Underapprox.IsEmpty()) - choices.insert(n); - else - used_set.push_back(n); - } - else - unused_set.push_back(n); - } - if(tree->Check(top,unused_set) == unsat) - throw "error in ExpansionChoices"; - ExpansionChoicesRec(unused_set, used_set, choices, 0, used_set.size()); - old_choices = choices; - heuristic->ChooseExpand(choices, best, high_priority); - } -#endif -#endif - - bool ExpandSomeNodes(bool high_priority = false, int max = INT_MAX){ -#ifdef EFFORT_BOUNDED_STRAT - last_decs = tree->CumulativeDecisions() - start_decs; -#endif - timer_start("ExpandSomeNodes"); - timer_start("ExpansionChoices"); - std::set choices; - ExpansionChoices(choices,high_priority,max != INT_MAX); - timer_stop("ExpansionChoices"); - std::list leaves_copy = leaves; // copy so can modify orig - leaves.clear(); - int count = 0; - for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ - if(choices.find(*it) != choices.end() && count < max){ - count++; - ExpandNode(*it); - } - else leaves.push_back(*it); - } - timer_stop("ExpandSomeNodes"); - return !choices.empty(); - } - - void RemoveExpansion(RPFP::Node *p){ - Edge *edge = p->Outgoing; - Node *parent = edge->Parent; -#ifndef KEEP_EXPANSIONS - std::vector cs = edge->Children; - tree->DeleteEdge(edge); - for(unsigned i = 0; i < cs.size(); i++) - tree->DeleteNode(cs[i]); -#endif - leaves.push_back(parent); - } - - // remove all the descendants of tree root (but not root itself) - void RemoveTree(RPFP *tree, RPFP::Node *root){ - Edge *edge = root->Outgoing; - std::vector cs = edge->Children; - tree->DeleteEdge(edge); - for(unsigned i = 0; i < cs.size(); i++){ - RemoveTree(tree,cs[i]); - tree->DeleteNode(cs[i]); - } - } - }; - - class DerivationTreeSlow : public DerivationTree { - public: - - struct stack_entry { - unsigned level; // SMT solver stack level - std::vector expansions; - }; - - std::vector stack; - - hash_map updates; - - DerivationTreeSlow(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) - : DerivationTree(_duality, rpfp, _reporter, _heuristic, _full_expand) { - stack.push_back(stack_entry()); - } - - struct DoRestart {}; - - virtual bool Build(){ - while (true) { - try { - return BuildMain(); - } - catch (const DoRestart &) { - // clear the statck and try again - updated_nodes.clear(); - while(stack.size() > 1) - PopLevel(); - reporter->Message("restarted"); - } - } - } - - bool BuildMain(){ - - stack.back().level = tree->slvr().get_scope_level(); - bool was_sat = true; - int update_failures = 0; - - while (true) - { - lbool res; - - unsigned slvr_level = tree->slvr().get_scope_level(); - if(slvr_level != stack.back().level) - throw "stacks out of sync!"; - reporter->Depth(stack.size()); - - // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop - check_result foo = tree->Check(top); - res = foo == unsat ? l_false : l_true; - - if (res == l_false) { - if (stack.empty()) // should never happen - return false; - - { - std::vector &expansions = stack.back().expansions; - int update_count = 0; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - tree->SolveSingleNode(top,node); -#ifdef NO_GENERALIZE - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); -#else - try { - if(expansions.size() == 1 && NodeTooComplicated(node)) - SimplifyNode(node); - else - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - Generalize(node); - } - catch(const RPFP::Bad &){ - // bad interpolants can get us here - throw DoRestart(); - } -#endif - if(RecordUpdate(node)) - update_count++; - else - heuristic->Update(node->map); // make it less likely to expand this node in future - } - if(update_count == 0){ - if(was_sat){ - update_failures++; - if(update_failures > 10) - throw Incompleteness(); - } - reporter->Message("backtracked without learning"); - } - else update_failures = 0; - } - tree->ComputeProofCore(); // need to compute the proof core before popping solver - bool propagated = false; - while(1) { - bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop - PopLevel(); - if(stack.size() == 1)break; - if(prev_level_used){ - Node *node = stack.back().expansions[0]; -#ifndef NO_PROPAGATE - if(!Propagate(node)) break; -#endif - if(!RecordUpdate(node)) break; // shouldn't happen! - RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list - propagated = true; - continue; - } - if(propagated) break; // propagation invalidates the proof core, so disable non-chron backtrack - RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list - std::vector &unused_ex = stack.back().expansions; - for(unsigned i = 0; i < unused_ex.size(); i++) - heuristic->Update(unused_ex[i]->map); // make it less likely to expand this node in future - } - HandleUpdatedNodes(); - if(stack.size() == 1){ - if(top->Outgoing) - tree->DeleteEdge(top->Outgoing); // in case we kept the tree - return false; - } - was_sat = false; - } - else { - was_sat = true; - tree->Push(); - std::vector &expansions = stack.back().expansions; -#ifndef NO_DECISIONS - for(unsigned i = 0; i < expansions.size(); i++){ - tree->FixCurrentState(expansions[i]->Outgoing); - } -#endif -#if 0 - if(tree->slvr().check() == unsat) - throw "help!"; -#endif - int expand_max = 1; - if(0&&duality->BatchExpand){ - int thing = stack.size() /10; - expand_max = std::max(1,thing); - if(expand_max > 1) - std::cout << "foo!\n"; - } - - if(ExpandSomeNodes(false,expand_max)) - continue; - tree->Pop(1); - while(stack.size() > 1){ - tree->Pop(1); - stack.pop_back(); - } - return true; - } - } - } - - void PopLevel(){ - std::vector &expansions = stack.back().expansions; - tree->Pop(1); - hash_set leaves_to_remove; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - // if(node != top) - // tree->ConstrainParent(node->Incoming[0],node); - std::vector &cs = node->Outgoing->Children; - for(unsigned i = 0; i < cs.size(); i++){ - leaves_to_remove.insert(cs[i]); - UnmapNode(cs[i]); - if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) - throw "help!"; - } - } - RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - RemoveExpansion(node); - } - stack.pop_back(); - } - - bool NodeTooComplicated(Node *node){ - int ops = tree->CountOperators(node->Annotation.Formula); - if(ops > 10) return true; - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - return tree->CountOperators(node->Annotation.Formula) > 3; - } - - void SimplifyNode(Node *node){ - // have to destroy the old proof to get a new interpolant - timer_start("SimplifyNode"); - tree->PopPush(); - try { - tree->InterpolateByCases(top,node); - } - catch(const RPFP::Bad&){ - timer_stop("SimplifyNode"); - throw RPFP::Bad(); - } - timer_stop("SimplifyNode"); - } - - bool LevelUsedInProof(unsigned level){ - std::vector &expansions = stack[level].expansions; - for(unsigned i = 0; i < expansions.size(); i++) - if(tree->EdgeUsedInProof(expansions[i]->Outgoing)) - return true; - return false; - } - - void RemoveUpdateNodesAtCurrentLevel() { - for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ - Node *node = *it; - if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ - std::list::iterator victim = it; - ++it; - updated_nodes.erase(victim); - } - else - ++it; - } - } - - void RemoveLeaves(hash_set &leaves_to_remove){ - std::list leaves_copy; - leaves_copy.swap(leaves); - for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ - if(leaves_to_remove.find(*it) == leaves_to_remove.end()) - leaves.push_back(*it); - } - } - - hash_map > node_map; - std::list updated_nodes; - - virtual void ExpandNode(RPFP::Node *p){ - stack.push_back(stack_entry()); - stack.back().level = tree->slvr().get_scope_level(); - stack.back().expansions.push_back(p); - DerivationTree::ExpandNode(p); - std::vector &new_nodes = p->Outgoing->Children; - for(unsigned i = 0; i < new_nodes.size(); i++){ - Node *n = new_nodes[i]; - node_map[n->map].push_back(n); - } - } - - bool RecordUpdate(Node *node){ - bool res = duality->UpdateNodeToNode(node->map,node); - if(res){ - std::vector to_update = node_map[node->map]; - for(unsigned i = 0; i < to_update.size(); i++){ - Node *node2 = to_update[i]; - // maintain invariant that no nodes on updated list are created at current stack level - if(node2 == node || !(node->Incoming.size() > 0 && AtCurrentStackLevel(node2->Incoming[0]->Parent))){ - updated_nodes.push_back(node2); - if(node2 != node) - node2->Annotation = node->Annotation; - } - } - } - return res; - } - - void HandleUpdatedNodes(){ - for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ - Node *node = *it; - node->Annotation = node->map->Annotation; - if(node->Incoming.size() > 0) - tree->ConstrainParent(node->Incoming[0],node); - if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ - std::list::iterator victim = it; - ++it; - updated_nodes.erase(victim); - } - else - ++it; - } - } - - bool AtCurrentStackLevel(Node *node){ - std::vector vec = stack.back().expansions; - for(unsigned i = 0; i < vec.size(); i++) - if(vec[i] == node) - return true; - return false; - } - - void UnmapNode(Node *node){ - std::vector &vec = node_map[node->map]; - for(unsigned i = 0; i < vec.size(); i++){ - if(vec[i] == node){ - std::swap(vec[i],vec.back()); - vec.pop_back(); - return; - } - } - throw "can't unmap node"; - } - - void Generalize(Node *node){ -#ifndef USE_RPFP_CLONE - tree->Generalize(top,node); -#else - RPFP_caching *clone_rpfp = duality->clone_rpfp; - if(!node->Outgoing->map) return; - Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); - Node *clone_node = clone_edge->Parent; - clone_node->Annotation = node->Annotation; - for(unsigned i = 0; i < clone_edge->Children.size(); i++) - clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; - clone_rpfp->GeneralizeCache(clone_edge); - node->Annotation = clone_node->Annotation; -#endif - } - - bool Propagate(Node *node){ -#ifdef USE_RPFP_CLONE - RPFP_caching *clone_rpfp = duality->clone_rpfp; - Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); - Node *clone_node = clone_edge->Parent; - clone_node->Annotation = node->map->Annotation; - for(unsigned i = 0; i < clone_edge->Children.size(); i++) - clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; - bool res = clone_rpfp->PropagateCache(clone_edge); - if(res) - node->Annotation = clone_node->Annotation; - return res; -#else - return false; -#endif - } - - }; - - - class Covering { - - struct cover_info { - Node *covered_by; - std::list covers; - bool dominated; - std::set dominates; - cover_info(){ - covered_by = 0; - dominated = false; - } - }; - - typedef hash_map cover_map; - cover_map cm; - Duality *parent; - bool some_updates; - -#define NO_CONJ_ON_SIMPLE_LOOPS -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - hash_set simple_loops; -#endif - - Node *&covered_by(Node *node){ - return cm[node].covered_by; - } - - std::list &covers(Node *node){ - return cm[node].covers; - } - - std::vector &insts_of_node(Node *node){ - return parent->insts_of_node[node]; - } - - Reporter *reporter(){ - return parent->reporter; - } - - std::set &dominates(Node *x){ - return cm[x].dominates; - } - - bool dominates(Node *x, Node *y){ - std::set &d = cm[x].dominates; - return d.find(y) != d.end(); - } - - bool &dominated(Node *x){ - return cm[x].dominated; - } - - public: - - Covering(Duality *_parent){ - parent = _parent; - some_updates = false; - -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - hash_map > outgoing; - for(unsigned i = 0; i < parent->rpfp->edges.size(); i++) - outgoing[parent->rpfp->edges[i]->Parent].push_back(parent->rpfp->edges[i]); - for(unsigned i = 0; i < parent->rpfp->nodes.size(); i++){ - Node * node = parent->rpfp->nodes[i]; - std::vector &outs = outgoing[node]; - if(outs.size() == 2){ - for(int j = 0; j < 2; j++){ - Edge *loop_edge = outs[j]; - if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent) - simple_loops.insert(node); - } - } - } -#endif - - } - - bool IsCoveredRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return false; - memo.insert(node); - if(covered_by(node)) return true; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) - if(IsCoveredRec(memo,node->Outgoing->Children[i])) - return true; - return false; - } - - bool IsCovered(Node *node){ - hash_set memo; - return IsCoveredRec(memo,node); - } - -#ifndef UNDERAPPROX_NODES - void RemoveCoveringsBy(Node *node){ - std::list &cs = covers(node); - for(std::list::iterator it = cs.begin(), en = cs.end(); it != en; it++){ - covered_by(*it) = 0; - reporter()->RemoveCover(*it,node); - } - cs.clear(); - } -#else - void RemoveCoveringsBy(Node *node){ - std::vector &cs = parent->all_of_node[node->map]; - for(std::vector::iterator it = cs.begin(), en = cs.end(); it != en; it++){ - Node *other = *it; - if(covered_by(other) && CoverOrder(node,other)){ - covered_by(other) = 0; - reporter()->RemoveCover(*it,node); - } - } - } -#endif - - void RemoveAscendantCoveringsRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return; - memo.insert(node); - RemoveCoveringsBy(node); - for(std::vector::iterator it = node->Incoming.begin(), en = node->Incoming.end(); it != en; ++it) - RemoveAscendantCoveringsRec(memo,(*it)->Parent); - } - - void RemoveAscendantCoverings(Node *node){ - hash_set memo; - RemoveAscendantCoveringsRec(memo,node); - } - - bool CoverOrder(Node *covering, Node *covered){ -#ifdef UNDERAPPROX_NODES - if(parent->underapprox_map.find(covered) != parent->underapprox_map.end()) - return false; - if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) - return covering->number < covered->number || parent->underapprox_map[covering] == covered; -#endif - return covering->number < covered->number; - } - - bool CheckCover(Node *covered, Node *covering){ - return - CoverOrder(covering,covered) - && covered->Annotation.SubsetEq(covering->Annotation) - && !IsCovered(covering); - } - - bool CoverByNode(Node *covered, Node *covering){ - if(CheckCover(covered,covering)){ - covered_by(covered) = covering; - covers(covering).push_back(covered); - std::vector others; others.push_back(covering); - reporter()->AddCover(covered,others); - RemoveAscendantCoverings(covered); - return true; - } - else - return false; - } - -#ifdef UNDERAPPROX_NODES - bool CoverByAll(Node *covered){ - RPFP::Transformer all = covered->Annotation; - all.SetEmpty(); - std::vector &insts = parent->insts_of_node[covered->map]; - std::vector others; - for(unsigned i = 0; i < insts.size(); i++){ - Node *covering = insts[i]; - if(CoverOrder(covering,covered) && !IsCovered(covering)){ - others.push_back(covering); - all.UnionWith(covering->Annotation); - } - } - if(others.size() && covered->Annotation.SubsetEq(all)){ - covered_by(covered) = covered; // anything non-null will do - reporter()->AddCover(covered,others); - RemoveAscendantCoverings(covered); - return true; - } - else - return false; - } -#endif - - bool Close(Node *node){ - if(covered_by(node)) - return true; -#ifndef UNDERAPPROX_NODES - std::vector &insts = insts_of_node(node->map); - for(unsigned i = 0; i < insts.size(); i++) - if(CoverByNode(node,insts[i])) - return true; -#else - if(CoverByAll(node)) - return true; -#endif - return false; - } - - bool CloseDescendantsRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return false; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) - if(CloseDescendantsRec(memo,node->Outgoing->Children[i])) - return true; - if(Close(node)) - return true; - memo.insert(node); - return false; - } - - bool CloseDescendants(Node *node){ - timer_start("CloseDescendants"); - hash_set memo; - bool res = CloseDescendantsRec(memo,node); - timer_stop("CloseDescendants"); - return res; - } - - bool Contains(Node *node){ - timer_start("Contains"); - bool res = !IsCovered(node); - timer_stop("Contains"); - return res; - } - - bool Candidate(Node *node){ - timer_start("Candidate"); - bool res = !IsCovered(node) && !dominated(node); - timer_stop("Candidate"); - return res; - } - - void SetDominated(Node *node){ - dominated(node) = true; - } - - bool CouldCover(Node *covered, Node *covering){ -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - // Forsimple loops, we rely on propagation, not covering - if(simple_loops.find(covered->map) != simple_loops.end()) - return false; -#endif -#ifdef UNDERAPPROX_NODES - // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) - // return parent->underapprox_map[covering] == covered; -#endif - if(CoverOrder(covering,covered) - && !IsCovered(covering)){ - RPFP::Transformer f(covering->Annotation); f.SetEmpty(); -#if defined(TOP_DOWN) || defined(EFFORT_BOUNDED_STRAT) - if(parent->StratifiedInlining) - return true; -#endif - return !covering->Annotation.SubsetEq(f); - } - return false; - } - - bool ContainsCex(Node *node, Counterexample &cex){ - expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula); - return eq(val,parent->ctx.bool_val(true)); - } - - /** We conjecture that the annotations of similar nodes may be - true of this one. We start with later nodes, on the - principle that their annotations are likely weaker. We save - a counterexample -- if annotations of other nodes are true - in this counterexample, we don't need to check them. - */ - -#ifndef UNDERAPPROX_NODES - bool Conjecture(Node *node){ - std::vector &insts = insts_of_node(node->map); - Counterexample cex; - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CouldCover(node,other)){ - reporter()->Forcing(node,other); - if(cex.get_tree() && !ContainsCex(other,cex)) - continue; - cex.clear(); - if(parent->ProveConjecture(node,other->Annotation,other,&cex)) - if(CloseDescendants(node)) - return true; - } - } - cex.clear(); - return false; - } -#else - bool Conjecture(Node *node){ - std::vector &insts = insts_of_node(node->map); - Counterexample cex; - RPFP::Transformer Bound = node->Annotation; - Bound.SetEmpty(); - bool some_other = false; - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CouldCover(node,other)){ - reporter()->Forcing(node,other); - Bound.UnionWith(other->Annotation); - some_other = true; - } - } - if(some_other && parent->ProveConjecture(node,Bound)){ - CloseDescendants(node); - return true; - } - return false; - } -#endif - - void Update(Node *node, const RPFP::Transformer &update){ - RemoveCoveringsBy(node); - some_updates = true; - } - -#ifndef UNDERAPPROX_NODES - Node *GetSimilarNode(Node *node){ - if(!some_updates) - return 0; - std::vector &insts = insts_of_node(node->map); - for(int i = insts.size()-1; i >= 0; i--){ - Node *other = insts[i]; - if(dominates(node,other)) - if(CoverOrder(other,node) - && !IsCovered(other)) - return other; - } - return 0; - } -#else - Node *GetSimilarNode(Node *node){ - if(!some_updates) - return 0; - std::vector &insts = insts_of_node(node->map); - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CoverOrder(other,node) - && !IsCovered(other)) - return other; - } - return 0; - } -#endif - - bool Dominates(Node * node, Node *other){ - if(node == other) return false; - if(other->Outgoing->map == 0) return true; - if(node->Outgoing->map == other->Outgoing->map){ - assert(node->Outgoing->Children.size() == other->Outgoing->Children.size()); - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *nc = node->Outgoing->Children[i]; - Node *oc = other->Outgoing->Children[i]; - if(!(nc == oc || oc->Outgoing->map ==0 || dominates(nc,oc))) - return false; - } - return true; - } - return false; - } - - void Add(Node *node){ - std::vector &insts = insts_of_node(node->map); - for(unsigned i = 0; i < insts.size(); i++){ - Node *other = insts[i]; - if(Dominates(node,other)){ - cm[node].dominates.insert(other); - cm[other].dominated = true; - reporter()->Dominates(node, other); - } - } - } - - }; - - /* This expansion heuristic makes use of a previuosly obtained - counterexample as a guide. This is for use in abstraction - refinement schemes.*/ - - class ReplayHeuristic : public Heuristic { - - Counterexample old_cex; - public: - ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) - : Heuristic(_rpfp) - { - old_cex.swap(_old_cex); // take ownership from caller - } - - ~ReplayHeuristic(){ - } - - // Maps nodes of derivation tree into old cex - hash_map cex_map; - - void Done() { - cex_map.clear(); - old_cex.clear(); - } - - void ShowNodeAndChildren(Node *n){ - std::cout << n->Name.name() << ": "; - std::vector &chs = n->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++) - std::cout << chs[i]->Name.name() << " " ; - std::cout << std::endl; - } - - // HACK: When matching relation names, we drop suffixes used to - // make the names unique between runs. For compatibility - // with boggie, we drop suffixes beginning with @@ - std::string BaseName(const std::string &name){ - int pos = name.find("@@"); - if(pos >= 1) - return name.substr(0,pos); - return name; - } - - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ - if(!high_priority || !old_cex.get_tree()){ - Heuristic::ChooseExpand(choices,best,false); - return; - } - // first, try to match the derivatino tree nodes to the old cex - std::set matched, unmatched; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it); - if(cex_map.empty()) - cex_map[node] = old_cex.get_root(); // match the root nodes - if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node - Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! - if(cex_map.find(parent) == cex_map.end()) - throw "catastrophe in ReplayHeuristic::ChooseExpand"; - Node *old_parent = cex_map[parent]; - std::vector &chs = parent->Outgoing->Children; - if(old_parent && old_parent->Outgoing){ - std::vector &old_chs = old_parent->Outgoing->Children; - for(unsigned i = 0, j=0; i < chs.size(); i++){ - if(j < old_chs.size() && BaseName(chs[i]->Name.name().str()) == BaseName(old_chs[j]->Name.name().str())) - cex_map[chs[i]] = old_chs[j++]; - else { - std::cerr << "WARNING: duality: unmatched child: " << chs[i]->Name.name() << std::endl; - cex_map[chs[i]] = 0; - } - } - goto matching_done; - } - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = 0; - } - matching_done: - Node *old_node = cex_map[node]; - if(!old_node) - unmatched.insert(node); - else if(old_cex.get_tree()->Empty(old_node)) - unmatched.insert(node); - else - matched.insert(node); - } - if (matched.empty() && !high_priority) - Heuristic::ChooseExpand(unmatched,best,false); - else - Heuristic::ChooseExpand(matched,best,false); - } - }; - - - class LocalHeuristic : public Heuristic { - - RPFP::Node *old_node; - public: - LocalHeuristic(RPFP *_rpfp) - : Heuristic(_rpfp) - { - old_node = 0; - } - - void SetOldNode(RPFP::Node *_old_node){ - old_node = _old_node; - cex_map.clear(); - } - - // Maps nodes of derivation tree into old subtree - hash_map cex_map; - - virtual void ChooseExpand(const std::set &choices, std::set &best){ - if(old_node == 0){ - Heuristic::ChooseExpand(choices,best); - return; - } - // first, try to match the derivatino tree nodes to the old cex - std::set matched, unmatched; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it); - if(cex_map.empty()) - cex_map[node] = old_node; // match the root nodes - if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node - Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! - if(cex_map.find(parent) == cex_map.end()) - throw "catastrophe in ReplayHeuristic::ChooseExpand"; - Node *old_parent = cex_map[parent]; - std::vector &chs = parent->Outgoing->Children; - if(old_parent && old_parent->Outgoing){ - std::vector &old_chs = old_parent->Outgoing->Children; - if(chs.size() == old_chs.size()){ - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = old_chs[i]; - goto matching_done; - } - else - std::cout << "derivation tree does not match old cex" << std::endl; - } - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = 0; - } - matching_done: - Node *old_node = cex_map[node]; - if(!old_node) - unmatched.insert(node); - else if(old_node != node->map) - unmatched.insert(node); - else - matched.insert(node); - } - Heuristic::ChooseExpand(unmatched,best); - } - }; - - /** This proposer class generates conjectures based on the - unwinding generated by a previous solver. The assumption is - that the provious solver was working on a different - abstraction of the same system. The trick is to adapt the - annotations in the old unwinding to the new predicates. We - start by generating a map from predicates and parameters in - the old problem to the new. - - HACK: mapping is done by cheesy name comparison. - */ - - class HistoryProposer : public Proposer - { - Duality *old_solver; - Duality *new_solver; - hash_map > conjectures; - - public: - /** Construct a history solver. */ - HistoryProposer(Duality *_old_solver, Duality *_new_solver) - : old_solver(_old_solver), new_solver(_new_solver) { - - // tricky: names in the axioms may have changed -- map them - hash_set &old_constants = old_solver->unwinding->ls->get_constants(); - hash_set &new_constants = new_solver->rpfp->ls->get_constants(); - hash_map cmap; - for(hash_set::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it) - cmap[GetKey(*it)] = *it; - hash_map bckg_map; - for(hash_set::iterator it = old_constants.begin(), en = old_constants.end(); it != en; ++it){ - func_decl f = new_solver->ctx.translate(*it); // move to new context - if(cmap.find(GetKey(f)) != cmap.end()) - bckg_map[f] = cmap[GetKey(f)]; - // else - // std::cout << "constant not matched\n"; - } - - RPFP *old_unwinding = old_solver->unwinding; - hash_map > pred_match; - - // index all the predicates in the old unwinding - for(unsigned i = 0; i < old_unwinding->nodes.size(); i++){ - Node *node = old_unwinding->nodes[i]; - std::string key = GetKey(node); - pred_match[key].push_back(node); - } - - // match with predicates in the new RPFP - RPFP *rpfp = new_solver->rpfp; - for(unsigned i = 0; i < rpfp->nodes.size(); i++){ - Node *node = rpfp->nodes[i]; - std::string key = GetKey(node); - std::vector &matches = pred_match[key]; - for(unsigned j = 0; j < matches.size(); j++) - MatchNodes(node,matches[j],bckg_map); - } - } - - virtual std::vector &Propose(Node *node){ - return conjectures[node->map]; - } - - virtual ~HistoryProposer(){ - }; - - private: - void MatchNodes(Node *new_node, Node *old_node, hash_map &bckg_map){ - if(old_node->Annotation.IsFull()) - return; // don't conjecture true! - hash_map var_match; - std::vector &new_params = new_node->Annotation.IndParams; - // Index the new parameters by their keys - for(unsigned i = 0; i < new_params.size(); i++) - var_match[GetKey(new_params[i])] = new_params[i]; - RPFP::Transformer &old = old_node->Annotation; - std::vector from_params = old.IndParams; - for(unsigned j = 0; j < from_params.size(); j++) - from_params[j] = new_solver->ctx.translate(from_params[j]); // get in new context - std::vector to_params = from_params; - for(unsigned j = 0; j < to_params.size(); j++){ - std::string key = GetKey(to_params[j]); - if(var_match.find(key) == var_match.end()){ - // std::cout << "unmatched parameter!\n"; - return; - } - to_params[j] = var_match[key]; - } - expr fmla = new_solver->ctx.translate(old.Formula); // get in new context - fmla = new_solver->rpfp->SubstParams(old.IndParams,to_params,fmla); // substitute parameters - hash_map memo; - fmla = new_solver->rpfp->SubstRec(memo,bckg_map,fmla); // substitute background constants - RPFP::Transformer new_annot = new_node->Annotation; - new_annot.Formula = fmla; - conjectures[new_node].push_back(new_annot); - } - - // We match names by removing suffixes beginning with double at sign - - std::string GetKey(Node *node){ - return GetKey(node->Name); - } - - std::string GetKey(const expr &var){ - return GetKey(var.decl()); - } - - std::string GetKey(const func_decl &f){ - std::string name = f.name().str(); - int idx = name.find("@@"); - if(idx >= 0) - name.erase(idx); - return name; - } - }; - }; - - - class StreamReporter : public Reporter { - std::ostream &s; - public: - StreamReporter(RPFP *_rpfp, std::ostream &_s) - : Reporter(_rpfp), s(_s) {event = 0; depth = -1;} - int event; - int depth; - void ev(){ - s << "[" << event++ << "]" ; - } - virtual void Extend(RPFP::Node *node){ - ev(); s << "node " << node->number << ": " << node->Name.name(); - std::vector &rps = node->Outgoing->Children; - for(unsigned i = 0; i < rps.size(); i++) - s << " " << rps[i]->number; - s << std::endl; - } - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ - ev(); s << "update " << node->number << " " << node->Name.name() << ": "; - rpfp->Summarize(update.Formula); - if(depth > 0) s << " (depth=" << depth << ")"; - if(eager) s << " (eager)"; - s << std::endl; - } - virtual void Bound(RPFP::Node *node){ - ev(); s << "check " << node->number << std::endl; - } - virtual void Expand(RPFP::Edge *edge){ - RPFP::Node *node = edge->Parent; - ev(); s << "expand " << node->map->number << " " << node->Name.name(); - if(depth > 0) s << " (depth=" << depth << ")"; - s << std::endl; - } - virtual void Depth(int d){ - depth = d; - } - virtual void AddCover(RPFP::Node *covered, std::vector &covering){ - ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; - for(unsigned i = 0; i < covering.size(); i++) - s << covering[i]->number << " "; - s << std::endl; - } - virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){ - ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; - } - virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){ - ev(); s << "forcing " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; - } - virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){ - ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; - rpfp->Summarize(t.Formula); - s << std::endl; - } - virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ - ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; - } - virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){ - ev(); s << "induction failure: " << edge->Parent->Name.name() << ", children ="; - for(unsigned i = 0; i < children.size(); i++) - s << " " << children[i]->number; - s << std::endl; - } - virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){ - ev(); s << "underapprox " << node->number << " " << node->Name.name() << ": " << update.Formula << std::endl; - } - virtual void Reject(RPFP::Edge *edge, const std::vector &children){ - ev(); s << "reject " << edge->Parent->number << " " << edge->Parent->Name.name() << ": "; - for(unsigned i = 0; i < children.size(); i++) - s << " " << children[i]->number; - s << std::endl; - } - virtual void Message(const std::string &msg){ - ev(); s << "msg " << msg << std::endl; - } - - }; - - Solver *Solver::Create(const std::string &solver_class, RPFP *rpfp){ - Duality *s = alloc(Duality,rpfp); - return s; - } - - Reporter *CreateStdoutReporter(RPFP *rpfp){ - return new StreamReporter(rpfp, std::cout); - } -} +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality_solver.h + +Abstract: + + implements relational post-fixedpoint problem + (RPFP) solver + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + +#ifdef _WINDOWS +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#endif + +#include "duality.h" +#include "duality_profiling.h" + +#include +#include +#include +#include +#include + +// TODO: make these official options or get rid of them + +#define NEW_CAND_SEL +// #define LOCALIZE_CONJECTURES +// #define CANDS_FROM_UPDATES +#define CANDS_FROM_COVER_FAIL +#define DEPTH_FIRST_EXPAND +#define MINIMIZE_CANDIDATES +// #define MINIMIZE_CANDIDATES_HARDER +#define BOUNDED +// #define CHECK_CANDS_FROM_IND_SET +#define UNDERAPPROX_NODES +#define NEW_EXPAND +#define EARLY_EXPAND +// #define TOP_DOWN +// #define EFFORT_BOUNDED_STRAT +#define SKIP_UNDERAPPROX_NODES +// #define KEEP_EXPANSIONS +// #define USE_CACHING_RPFP +// #define PROPAGATE_BEFORE_CHECK +#define NEW_STRATIFIED_INLINING + +#define USE_RPFP_CLONE +#define USE_NEW_GEN_CANDS + +//#define NO_PROPAGATE +//#define NO_GENERALIZE +//#define NO_DECISIONS + +namespace Duality { + + // TODO: must be a better place for this... + static char string_of_int_buffer[20]; + + static const char *string_of_int(int n){ + sprintf(string_of_int_buffer,"%d",n); + return string_of_int_buffer; + } + + /** Generic object for producing diagnostic output. */ + + class Reporter { + protected: + RPFP *rpfp; + public: + Reporter(RPFP *_rpfp){ + rpfp = _rpfp; + } + virtual void Extend(RPFP::Node *node){} + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){} + virtual void Bound(RPFP::Node *node){} + virtual void Expand(RPFP::Edge *edge){} + virtual void AddCover(RPFP::Node *covered, std::vector &covering){} + virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){} + virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){} + virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){} + virtual void Dominates(RPFP::Node *node, RPFP::Node *other){} + virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){} + virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} + virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} + virtual void Message(const std::string &msg){} + virtual void Depth(int){} + virtual ~Reporter(){} + }; + + Reporter *CreateStdoutReporter(RPFP *rpfp); + + /** Object we throw in case of catastrophe. */ + + struct InternalError { + std::string msg; + InternalError(const std::string _msg) + : msg(_msg) {} + }; + + + /** This is the main solver. It takes anarbitrary (possibly cyclic) + RPFP and either annotates it with a solution, or returns a + counterexample derivation in the form of an embedd RPFP tree. */ + + class Duality : public Solver { + + public: + Duality(RPFP *_rpfp) + : ctx(_rpfp->ctx), + slvr(_rpfp->slvr()), + nodes(_rpfp->nodes), + edges(_rpfp->edges) + { + rpfp = _rpfp; + reporter = 0; + heuristic = 0; + unwinding = 0; + FullExpand = false; + NoConj = false; + FeasibleEdges = true; + UseUnderapprox = true; + Report = false; + StratifiedInlining = false; + RecursionBound = -1; + BatchExpand = false; + { + scoped_no_proof no_proofs_please(ctx.m()); +#ifdef USE_RPFP_CLONE + clone_rpfp = new RPFP_caching(rpfp->ls); + clone_rpfp->Clone(rpfp); +#endif +#ifdef USE_NEW_GEN_CANDS + gen_cands_rpfp = new RPFP_caching(rpfp->ls); + gen_cands_rpfp->Clone(rpfp); +#endif + } + } + + ~Duality(){ +#ifdef USE_RPFP_CLONE + delete clone_rpfp; +#endif +#ifdef USE_NEW_GEN_CANDS + delete gen_cands_rpfp; +#endif + if(unwinding) delete unwinding; + } + +#ifdef USE_RPFP_CLONE + RPFP_caching *clone_rpfp; +#endif +#ifdef USE_NEW_GEN_CANDS + RPFP_caching *gen_cands_rpfp; +#endif + + + typedef RPFP::Node Node; + typedef RPFP::Edge Edge; + + /** This struct represents a candidate for extending the + unwinding. It consists of an edge to instantiate + and a vector of children for the new instance. */ + + struct Candidate { + Edge *edge; std::vector + Children; + }; + + /** Comparison operator, allowing us to sort Nodes + by their number field. */ + + struct lnode + { + bool operator()(const Node* s1, const Node* s2) const + { + return s1->number < s2->number; + } + }; + + typedef std::set Unexpanded; // sorted set of Nodes + + /** This class provides a heuristic for expanding a derivation + tree. */ + + class Heuristic { + RPFP *rpfp; + + /** Heuristic score for unwinding nodes. Currently this + counts the number of updates. */ + struct score { + int updates; + score() : updates(0) {} + }; + hash_map scores; + + public: + Heuristic(RPFP *_rpfp){ + rpfp = _rpfp; + } + + virtual ~Heuristic(){} + + virtual void Update(RPFP::Node *node){ + scores[node].updates++; + } + + /** Heuristic choice of nodes to expand. Takes a set "choices" + and returns a subset "best". We currently choose the + nodes with the fewest updates. + */ +#if 0 + virtual void ChooseExpand(const std::set &choices, std::set &best){ + int best_score = INT_MAX; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it)->map; + int score = scores[node].updates; + best_score = std::min(best_score,score); + } + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) + if(scores[(*it)->map].updates == best_score) + best.insert(*it); + } +#else + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false, bool best_only=false){ + if(high_priority) return; + int best_score = INT_MAX; + int worst_score = 0; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it)->map; + int score = scores[node].updates; + best_score = std::min(best_score,score); + worst_score = std::max(worst_score,score); + } + int cutoff = best_only ? best_score : (best_score + (worst_score-best_score)/2); + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) + if(scores[(*it)->map].updates <= cutoff) + best.insert(*it); + } +#endif + + /** Called when done expanding a tree */ + virtual void Done() {} + }; + + /** The Proposer class proposes conjectures eagerly. These can come + from any source, including predicate abstraction, templates, or + previous solver runs. The proposed conjectures are checked + with low effort when the unwinding is expanded. + */ + + class Proposer { + public: + /** Given a node in the unwinding, propose some conjectures */ + virtual std::vector &Propose(Node *node) = 0; + virtual ~Proposer(){}; + }; + + + class Covering; // see below + + // These members represent the state of the algorithm. + + RPFP *rpfp; // the input RPFP + Reporter *reporter; // object for logging + Heuristic *heuristic; // expansion heuristic + context &ctx; // Z3 context + solver &slvr; // Z3 solver + std::vector &nodes; // Nodes of input RPFP + std::vector &edges; // Edges of input RPFP + std::vector leaves; // leaf nodes of unwinding (unused) + Unexpanded unexpanded; // unexpanded nodes + std::list candidates; // candidates for expansion + // maps children to edges in input RPFP + hash_map > edges_by_child; + // maps each node in input RPFP to its expanded instances + hash_map > insts_of_node; + // maps each node in input RPFP to all its instances + hash_map > all_of_node; + RPFP *unwinding; // the unwinding + Covering *indset; // proposed inductive subset + Counterexample cex; // counterexample + std::list to_expand; + hash_set updated_nodes; + hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate + int last_decisions; + hash_set overapproxes; + std::vector proposers; + +#ifdef BOUNDED + struct Counter { + int val; + Counter(){val = 0;} + }; + typedef std::map NodeToCounter; + hash_map back_edges; // counts of back edges +#endif + + /** Solve the problem. */ + virtual bool Solve(){ + reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); +#ifndef LOCALIZE_CONJECTURES + heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); +#else + heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) + : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); +#endif + cex.clear(); // in case we didn't use it for heuristic + if(unwinding) delete unwinding; + unwinding = new RPFP(rpfp->ls); + unwinding->HornClauses = rpfp->HornClauses; + indset = new Covering(this); + last_decisions = 0; + CreateEdgesByChildMap(); +#ifndef TOP_DOWN + CreateInitialUnwinding(); +#else + CreateLeaves(); + for(unsigned i = 0; i < leaves.size(); i++) + if(!SatisfyUpperBound(leaves[i])) + return false; +#endif + StratifiedLeafCount = -1; + timer_start("SolveMain"); + bool res = SolveMain(); // does the actual work + timer_stop("SolveMain"); + // print_profile(std::cout); + delete indset; + delete heuristic; + // delete unwinding; // keep the unwinding for future mining of predicates + delete reporter; + for(unsigned i = 0; i < proposers.size(); i++) + delete proposers[i]; + return res; + } + + void CreateInitialUnwinding(){ + if(!StratifiedInlining){ + CreateLeaves(); + if(FeasibleEdges)NullaryCandidates(); + else InstantiateAllEdges(); + } + else { +#ifdef NEW_STRATIFIED_INLINING + +#else + CreateLeaves(); +#endif + } + + } + + void Cancel(){ + // TODO + } + +#if 0 + virtual void Restart(RPFP *_rpfp){ + rpfp = _rpfp; + delete unwinding; + nodes = _rpfp->nodes; + edges = _rpfp->edges; + leaves.clear(); + unexpanded.clear(); // unexpanded nodes + candidates.clear(); // candidates for expansion + edges_by_child.clear(); + insts_of_node.clear(); + all_of_node.clear(); + to_expand.clear(); + } +#endif + + virtual void LearnFrom(Solver *other_solver){ + // get the counterexample as a guide + cex.swap(other_solver->GetCounterexample()); + + // propose conjectures based on old unwinding + Duality *old_duality = dynamic_cast(other_solver); + if(old_duality) + proposers.push_back(new HistoryProposer(old_duality,this)); + } + + /** Return a reference to the counterexample */ + virtual Counterexample &GetCounterexample(){ + return cex; + } + + // options + bool FullExpand; // do not use partial expansion of derivation tree + bool NoConj; // do not use conjectures (no forced covering) + bool FeasibleEdges; // use only feasible edges in unwinding + bool UseUnderapprox; // use underapproximations + bool Report; // spew on stdout + bool StratifiedInlining; // Do stratified inlining as preprocessing step + int RecursionBound; // Recursion bound for bounded verification + bool BatchExpand; + + bool SetBoolOption(bool &opt, const std::string &value){ + if(value == "0") { + opt = false; + return true; + } + if(value == "1") { + opt = true; + return true; + } + return false; + } + + bool SetIntOption(int &opt, const std::string &value){ + opt = atoi(value.c_str()); + return true; + } + + /** Set options (not currently used) */ + virtual bool SetOption(const std::string &option, const std::string &value){ + if(option == "full_expand"){ + return SetBoolOption(FullExpand,value); + } + if(option == "no_conj"){ + return SetBoolOption(NoConj,value); + } + if(option == "feasible_edges"){ + return SetBoolOption(FeasibleEdges,value); + } + if(option == "use_underapprox"){ + return SetBoolOption(UseUnderapprox,value); + } + if(option == "report"){ + return SetBoolOption(Report,value); + } + if(option == "stratified_inlining"){ + return SetBoolOption(StratifiedInlining,value); + } + if(option == "batch_expand"){ + return SetBoolOption(BatchExpand,value); + } + if(option == "recursion_bound"){ + return SetIntOption(RecursionBound,value); + } + return false; + } + + /** Create an instance of a node in the unwinding. Set its + annotation to true, and mark it unexpanded. */ + Node* CreateNodeInstance(Node *node, int number = 0){ + RPFP::Node *inst = unwinding->CloneNode(node); + inst->Annotation.SetFull(); + if(number < 0) inst->number = number; + unexpanded.insert(inst); + all_of_node[node].push_back(inst); + return inst; + } + + /** Create an instance of an edge in the unwinding, with given + parent and children. */ + void CreateEdgeInstance(Edge *edge, Node *parent, const std::vector &children){ + RPFP::Edge *inst = unwinding->CreateEdge(parent,edge->F,children); + inst->map = edge; + } + + void MakeLeaf(Node *node, bool do_not_expand = false){ + node->Annotation.SetEmpty(); + Edge *e = unwinding->CreateLowerBoundEdge(node); +#ifdef TOP_DOWN + node->Annotation.SetFull(); // allow this node to cover others +#endif + if(StratifiedInlining) + node->Annotation.SetFull(); // allow this node to cover others + else + updated_nodes.insert(node); + e->map = 0; + reporter->Extend(node); +#ifdef EARLY_EXPAND + if(!do_not_expand) + TryExpandNode(node); +#endif + // e->F.SetEmpty(); + } + + void MakeOverapprox(Node *node){ + node->Annotation.SetFull(); + Edge *e = unwinding->CreateLowerBoundEdge(node); + overapproxes.insert(node); + e->map = 0; + } + + /** We start the unwinding with leaves that under-approximate + each relation with false. */ + void CreateLeaves(){ + unexpanded.clear(); + leaves.clear(); + for(unsigned i = 0; i < nodes.size(); i++){ + RPFP::Node *node = CreateNodeInstance(nodes[i]); + if(0 && nodes[i]->Outgoing->Children.size() == 0) + CreateEdgeInstance(nodes[i]->Outgoing,node,std::vector()); + else { + if(!StratifiedInlining) + MakeLeaf(node); + else { + MakeOverapprox(node); + LeafMap[nodes[i]] = node; + } + } + leaves.push_back(node); + } + } + + /** Create the map from children to edges in the input RPFP. This + is used to generate candidates for expansion. */ + void CreateEdgesByChildMap(){ + edges_by_child.clear(); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *e = edges[i]; + std::set done; + for(unsigned j = 0; j < e->Children.size(); j++){ + Node *c = e->Children[j]; + if(done.find(c) == done.end()) // avoid duplicates + edges_by_child[c].push_back(e); + done.insert(c); + } + } + } + + void NullaryCandidates(){ + for(unsigned i = 0; i < edges.size(); i++){ + RPFP::Edge *edge = edges[i]; + if(edge->Children.size() == 0){ + Candidate cand; + cand.edge = edge; + candidates.push_back(cand); + } + } + } + + void InstantiateAllEdges(){ + hash_map leaf_map; + for(unsigned i = 0; i < leaves.size(); i++){ + leaf_map[leaves[i]->map] = leaves[i]; + insts_of_node[leaves[i]->map].push_back(leaves[i]); + } + unexpanded.clear(); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *edge = edges[i]; + Candidate c; c.edge = edge; + c.Children.resize(edge->Children.size()); + for(unsigned j = 0; j < c.Children.size(); j++) + c.Children[j] = leaf_map[edge->Children[j]]; + Node *new_node; + Extend(c,new_node); +#ifdef EARLY_EXPAND + TryExpandNode(new_node); +#endif + } + for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it) + indset->Add(*it); + for(unsigned i = 0; i < leaves.size(); i++){ + std::vector &foo = insts_of_node[leaves[i]->map]; + foo.erase(foo.begin()); + } + } + + bool ProducedBySI(Edge *edge, std::vector &children){ + if(LeafMap.find(edge->Parent) == LeafMap.end()) return false; + Node *other = LeafMap[edge->Parent]; + if(other->Outgoing->map != edge) return false; + std::vector &ochs = other->Outgoing->Children; + for(unsigned i = 0; i < children.size(); i++) + if(ochs[i] != children[i]) return false; + return true; + } + + /** Add a candidate for expansion, but not if Stratified inlining has already + produced it */ + + void AddCandidate(Edge *edge, std::vector &children){ + if(StratifiedInlining && ProducedBySI(edge,children)) + return; + candidates.push_back(Candidate()); + candidates.back().edge = edge; + candidates.back().Children = children; + } + + /** Generate candidates for expansion, given a vector of candidate + sets for each argument position. This recursively produces + the cross product. + */ + void GenCandidatesRec(int pos, Edge *edge, + const std::vector > &vec, + std::vector &children){ + if(pos == (int)vec.size()){ + AddCandidate(edge,children); + } + else { + for(unsigned i = 0; i < vec[pos].size(); i++){ + children[pos] = vec[pos][i]; + GenCandidatesRec(pos+1,edge,vec,children); + } + } + } + + /** Setup for above recursion. */ + void GenCandidates(int pos, Edge *edge, + const std::vector > &vec){ + std::vector children(vec.size()); + GenCandidatesRec(0,edge,vec,children); + } + + /** Expand a node. We find all the candidates for expansion using + this node and other already expanded nodes. This is a little + tricky, since a node may be used for multiple argument + positions of an edge, and we don't want to produce duplicates. + */ + +#ifndef NEW_EXPAND + void ExpandNode(Node *node){ + std::vector &nedges = edges_by_child[node->map]; + for(unsigned i = 0; i < nedges.size(); i++){ + Edge *edge = nedges[i]; + for(unsigned npos = 0; npos < edge->Children.size(); ++npos){ + if(edge->Children[npos] == node->map){ + std::vector > vec(edge->Children.size()); + vec[npos].push_back(node); + for(unsigned j = 0; j < edge->Children.size(); j++){ + if(j != npos){ + std::vector &insts = insts_of_node[edge->Children[j]]; + for(unsigned k = 0; k < insts.size(); k++) + if(indset->Candidate(insts[k])) + vec[j].push_back(insts[k]); + } + if(j < npos && edge->Children[j] == node->map) + vec[j].push_back(node); + } + GenCandidates(0,edge,vec); + } + } + } + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } +#else + /** If the current proposed solution is not inductive, + use the induction failure to generate candidates for extension. */ + void ExpandNode(Node *node){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + timer_start("GenCandIndFailUsing"); + std::vector &nedges = edges_by_child[node->map]; + for(unsigned i = 0; i < nedges.size(); i++){ + Edge *edge = nedges[i]; + slvr.push(); + RPFP *checker = new RPFP(rpfp->ls); + Node *root = CheckerJustForEdge(edge,checker,true); + if(root){ + expr using_cond = ctx.bool_val(false); + for(unsigned npos = 0; npos < edge->Children.size(); ++npos) + if(edge->Children[npos] == node->map) + using_cond = using_cond || checker->Localize(root->Outgoing->Children[npos]->Outgoing,NodeMarker(node)); + slvr.add(using_cond); + if(checker->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,checker,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + } + slvr.pop(1); + delete checker; + } + timer_stop("GenCandIndFailUsing"); + } +#endif + + void ExpandNodeFromOther(Node *node, Node *other){ + std::vector &in = other->Incoming; + for(unsigned i = 0; i < in.size(); i++){ + Edge *edge = in[i]; + Candidate cand; + cand.edge = edge->map; + cand.Children = edge->Children; + for(unsigned j = 0; j < cand.Children.size(); j++) + if(cand.Children[j] == other) + cand.Children[j] = node; + candidates.push_front(cand); + } + // unexpanded.erase(node); + // insts_of_node[node->map].push_back(node); + } + + /** Expand a node based on some uncovered node it dominates. + This pushes cahdidates onto the *front* of the candidate + queue, so these expansions are done depth-first. */ + bool ExpandNodeFromCoverFail(Node *node){ + if(!node->Outgoing || node->Outgoing->Children.size() == 0) + return false; + Node *other = indset->GetSimilarNode(node); + if(!other) + return false; +#ifdef UNDERAPPROX_NODES + Node *under_node = CreateUnderapproxNode(node); + underapprox_map[under_node] = node; + indset->CoverByNode(node,under_node); + ExpandNodeFromOther(under_node,other); + ExpandNode(under_node); +#else + ExpandNodeFromOther(node,other); + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); +#endif + return true; + } + + + /** Make a boolean variable to act as a "marker" for a node. */ + expr NodeMarker(Node *node){ + std::string name = std::string("@m_") + string_of_int(node->number); + return ctx.constant(name.c_str(),ctx.bool_sort()); + } + + /** Union the annotation of dst into src. If with_markers is + true, we conjoin the annotation formula of dst with its + marker. This allows us to discover which disjunct is + true in a satisfying assignment. */ + void UnionAnnotations(RPFP::Transformer &dst, Node *src, bool with_markers = false){ + if(!with_markers) + dst.UnionWith(src->Annotation); + else { + RPFP::Transformer t = src->Annotation; + t.Formula = t.Formula && NodeMarker(src); + dst.UnionWith(t); + } + } + + void GenNodeSolutionFromIndSet(Node *node, RPFP::Transformer &annot, bool with_markers = false){ + annot.SetEmpty(); + std::vector &insts = insts_of_node[node]; + for(unsigned j = 0; j < insts.size(); j++) + if(indset->Contains(insts[j])) + UnionAnnotations(annot,insts[j],with_markers); + annot.Simplify(); + } + + /** Generate a proposed solution of the input RPFP from + the unwinding, by unioning the instances of each node. */ + void GenSolutionFromIndSet(bool with_markers = false){ + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + GenNodeSolutionFromIndSet(node,node->Annotation,with_markers); + } + } + +#ifdef BOUNDED + bool NodePastRecursionBound(Node *node){ + if(RecursionBound < 0) return false; + NodeToCounter &backs = back_edges[node]; + for(NodeToCounter::iterator it = backs.begin(), en = backs.end(); it != en; ++it){ + if(it->second.val > RecursionBound) + return true; + } + return false; + } +#endif + + /** Test whether a given extension candidate actually represents + an induction failure. Right now we approximate this: if + the resulting node in the unwinding could be labeled false, + it clearly is not an induction failure. */ + + bool CandidateFeasible(const Candidate &cand){ + if(!FeasibleEdges) return true; + timer_start("CandidateFeasible"); + RPFP *checker = new RPFP(rpfp->ls); + // std::cout << "Checking feasibility of extension " << cand.edge->Parent->number << std::endl; + checker->Push(); + std::vector chs(cand.Children.size()); + Node *root = checker->CloneNode(cand.edge->Parent); +#ifdef BOUNDED + for(unsigned i = 0; i < cand.Children.size(); i++) + if(NodePastRecursionBound(cand.Children[i])){ + timer_stop("CandidateFeasible"); + return false; + } +#endif +#ifdef NEW_CAND_SEL + GenNodeSolutionFromIndSet(cand.edge->Parent,root->Bound); +#else + root->Bound.SetEmpty(); +#endif + checker->AssertNode(root); + for(unsigned i = 0; i < cand.Children.size(); i++) + chs[i] = checker->CloneNode(cand.Children[i]); + Edge *e = checker->CreateEdge(root,cand.edge->F,chs); + checker->AssertEdge(e,0,true); + // std::cout << "Checking SAT: " << e->dual << std::endl; + bool res = checker->Check(root) != unsat; + // std::cout << "Result: " << res << std::endl; + if(!res)reporter->Reject(cand.edge,cand.Children); + checker->Pop(1); + delete checker; + timer_stop("CandidateFeasible"); + return res; + } + + + hash_map TopoSort; + int TopoSortCounter; + std::vector SortedEdges; + + void DoTopoSortRec(Node *node){ + if(TopoSort.find(node) != TopoSort.end()) + return; + TopoSort[node] = INT_MAX; // just to break cycles + Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge + if(edge){ + std::vector &chs = edge->Children; + for(unsigned i = 0; i < chs.size(); i++) + DoTopoSortRec(chs[i]); + } + TopoSort[node] = TopoSortCounter++; + SortedEdges.push_back(edge); + } + + void DoTopoSort(){ + TopoSort.clear(); + SortedEdges.clear(); + TopoSortCounter = 0; + for(unsigned i = 0; i < nodes.size(); i++) + DoTopoSortRec(nodes[i]); + } + + + int StratifiedLeafCount; + +#ifdef NEW_STRATIFIED_INLINING + + /** Stratified inlining builds an initial layered unwinding before + switching to the LAWI strategy. Currently the number of layers + is one. Only nodes that are the targets of back edges are + consider to be leaves. This assumes we have already computed a + topological sort. + */ + + bool DoStratifiedInlining(){ + DoTopoSort(); + int depth = 1; // TODO: make this an option + std::vector > unfolding_levels(depth+1); + for(int level = 1; level <= depth; level++) + for(unsigned i = 0; i < SortedEdges.size(); i++){ + Edge *edge = SortedEdges[i]; + Node *parent = edge->Parent; + std::vector &chs = edge->Children; + std::vector my_chs(chs.size()); + for(unsigned j = 0; j < chs.size(); j++){ + Node *child = chs[j]; + int ch_level = TopoSort[child] >= TopoSort[parent] ? level-1 : level; + if(unfolding_levels[ch_level].find(child) == unfolding_levels[ch_level].end()){ + if(ch_level == 0) + unfolding_levels[0][child] = CreateLeaf(child); + else + throw InternalError("in levelized unwinding"); + } + my_chs[j] = unfolding_levels[ch_level][child]; + } + Candidate cand; cand.edge = edge; cand.Children = my_chs; + Node *new_node; + bool ok = Extend(cand,new_node); + MarkExpanded(new_node); // we don't expand here -- just mark it done + if(!ok) return false; // got a counterexample + unfolding_levels[level][parent] = new_node; + } + return true; + } + + Node *CreateLeaf(Node *node){ + RPFP::Node *nchild = CreateNodeInstance(node); + MakeLeaf(nchild, /* do_not_expand = */ true); + nchild->Annotation.SetEmpty(); + return nchild; + } + + void MarkExpanded(Node *node){ + if(unexpanded.find(node) != unexpanded.end()){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } + } + +#else + + /** In stratified inlining, we build the unwinding from the bottom + down, trying to satisfy the node bounds. We do this as a pre-pass, + limiting the expansion. If we get a counterexample, we are done, + else we continue as usual expanding the unwinding upward. + */ + + bool DoStratifiedInlining(){ + timer_start("StratifiedInlining"); + DoTopoSort(); + for(unsigned i = 0; i < leaves.size(); i++){ + Node *node = leaves[i]; + bool res = SatisfyUpperBound(node); + if(!res){ + timer_stop("StratifiedInlining"); + return false; + } + } + // don't leave any dangling nodes! +#ifndef EFFORT_BOUNDED_STRAT + for(unsigned i = 0; i < leaves.size(); i++) + if(!leaves[i]->Outgoing) + MakeLeaf(leaves[i],true); +#endif + timer_stop("StratifiedInlining"); + return true; + } + +#endif + + /** Here, we do the downward expansion for stratified inlining */ + + hash_map LeafMap, StratifiedLeafMap; + + Edge *GetNodeOutgoing(Node *node, int last_decs = 0){ + if(overapproxes.find(node) == overapproxes.end()) return node->Outgoing; /* already expanded */ + overapproxes.erase(node); +#ifdef EFFORT_BOUNDED_STRAT + if(last_decs > 5000){ + // RPFP::Transformer save = node->Annotation; + node->Annotation.SetEmpty(); + Edge *e = unwinding->CreateLowerBoundEdge(node); + // node->Annotation = save; + insts_of_node[node->map].push_back(node); + // std::cout << "made leaf: " << node->number << std::endl; + return e; + } +#endif + Edge *edge = node->map->Outgoing; + std::vector &chs = edge->Children; + + // make sure we don't create a covered node in this process! + + for(unsigned i = 0; i < chs.size(); i++){ + Node *child = chs[i]; + if(TopoSort[child] < TopoSort[node->map]){ + Node *leaf = LeafMap[child]; + if(!indset->Contains(leaf)){ + node->Outgoing->F.Formula = ctx.bool_val(false); // make this a proper leaf, else bogus cex + return node->Outgoing; + } + } + } + + std::vector nchs(chs.size()); + for(unsigned i = 0; i < chs.size(); i++){ + Node *child = chs[i]; + if(TopoSort[child] < TopoSort[node->map]){ + Node *leaf = LeafMap[child]; + nchs[i] = leaf; + if(unexpanded.find(leaf) != unexpanded.end()){ + unexpanded.erase(leaf); + insts_of_node[child].push_back(leaf); + } + } + else { + if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ + RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); + MakeLeaf(nchild); + nchild->Annotation.SetEmpty(); + StratifiedLeafMap[child] = nchild; + indset->SetDominated(nchild); + } + nchs[i] = StratifiedLeafMap[child]; + } + } + CreateEdgeInstance(edge,node,nchs); + reporter->Extend(node); + return node->Outgoing; + } + + void SetHeuristicOldNode(Node *node){ + LocalHeuristic *h = dynamic_cast(heuristic); + if(h) + h->SetOldNode(node); + } + + /** This does the actual solving work. We try to generate + candidates for extension. If we succed, we extend the + unwinding. If we fail, we have a solution. */ + bool SolveMain(){ + if(StratifiedInlining && !DoStratifiedInlining()) + return false; +#ifdef BOUNDED + DoTopoSort(); +#endif + while(true){ + timer_start("ProduceCandidatesForExtension"); + ProduceCandidatesForExtension(); + timer_stop("ProduceCandidatesForExtension"); + if(candidates.empty()){ + GenSolutionFromIndSet(); + return true; + } + Candidate cand = candidates.front(); + candidates.pop_front(); + if(CandidateFeasible(cand)){ + Node *new_node; + if(!Extend(cand,new_node)) + return false; +#ifdef EARLY_EXPAND + TryExpandNode(new_node); +#endif + } + } + } + + // hack: put something local into the underapproximation formula + // without this, interpolants can be pretty bad + void AddThing(expr &conj){ + std::string name = "@thing"; + expr thing = ctx.constant(name.c_str(),ctx.bool_sort()); + if(conj.is_app() && conj.decl().get_decl_kind() == And){ + std::vector conjs(conj.num_args()+1); + for(unsigned i = 0; i+1 < conjs.size(); i++) + conjs[i] = conj.arg(i); + conjs[conjs.size()-1] = thing; + conj = rpfp->conjoin(conjs); + } + } + + + Node *CreateUnderapproxNode(Node *node){ + // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); + RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); + under_node->Annotation.IntersectWith(cex.get_root()->Underapprox); + AddThing(under_node->Annotation.Formula); + Edge *e = unwinding->CreateLowerBoundEdge(under_node); + under_node->Annotation.SetFull(); // allow this node to cover others + back_edges[under_node] = back_edges[node]; + e->map = 0; + reporter->Extend(under_node); + return under_node; + } + + /** Try to prove a conjecture about a node. If successful + update the unwinding annotation appropriately. */ + bool ProveConjecture(Node *node, const RPFP::Transformer &t,Node *other = 0, Counterexample *_cex = 0){ + reporter->Conjecture(node,t); + timer_start("ProveConjecture"); + RPFP::Transformer save = node->Bound; + node->Bound.IntersectWith(t); + +#ifndef LOCALIZE_CONJECTURES + bool ok = SatisfyUpperBound(node); +#else + SetHeuristicOldNode(other); + bool ok = SatisfyUpperBound(node); + SetHeuristicOldNode(0); +#endif + + if(ok){ + timer_stop("ProveConjecture"); + return true; + } +#ifdef UNDERAPPROX_NODES + if(UseUnderapprox && last_decisions > 500){ + std::cout << "making an underapprox\n"; + ExpandNodeFromCoverFail(node); + } +#endif + if(_cex) (*_cex).swap(cex); // return the cex if asked + cex.clear(); // throw away the useless cex + node->Bound = save; // put back original bound + timer_stop("ProveConjecture"); + return false; + } + + /** If a node is part of the inductive subset, expand it. + We ask the inductive subset to exclude the node if possible. + */ + void TryExpandNode(RPFP::Node *node){ + if(indset->Close(node)) return; + if(!NoConj && indset->Conjecture(node)){ +#ifdef UNDERAPPROX_NODES + /* TODO: temporary fix. this prevents an infinite loop in case + the node is covered by multiple others. This should be + removed when covering by a set is implemented. + */ + if(indset->Contains(node)){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } +#endif + return; + } +#ifdef UNDERAPPROX_NODES + if(!indset->Contains(node)) + return; // could be covered by an underapprox node +#endif + indset->Add(node); +#if defined(CANDS_FROM_COVER_FAIL) && !defined(UNDERAPPROX_NODES) + if(ExpandNodeFromCoverFail(node)) + return; +#endif + ExpandNode(node); + } + + /** Make the conjunction of markers for all (expanded) instances of + a node in the input RPFP. */ + expr AllNodeMarkers(Node *node){ + expr res = ctx.bool_val(true); + std::vector &insts = insts_of_node[node]; + for(int k = insts.size()-1; k >= 0; k--) + res = res && NodeMarker(insts[k]); + return res; + } + + void RuleOutNodesPastBound(Node *node, RPFP::Transformer &t){ +#ifdef BOUNDED + if(RecursionBound < 0)return; + std::vector &insts = insts_of_node[node]; + for(unsigned i = 0; i < insts.size(); i++) + if(NodePastRecursionBound(insts[i])) + t.Formula = t.Formula && !NodeMarker(insts[i]); +#endif + } + + + void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction){ +#ifdef BOUNDED + if(RecursionBound >= 0 && NodePastRecursionBound(node)) + return; +#endif + RPFP::Transformer temp = node->Annotation; + expr marker = NodeMarker(node); + temp.Formula = (!marker || temp.Formula); + annot.IntersectWith(temp); + marker_disjunction = marker_disjunction || marker; + } + + bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false){ + bool res = false; + annot.SetFull(); + expr marker_disjunction = ctx.bool_val(false); + std::vector &insts = expanded_only ? insts_of_node[node] : all_of_node[node]; + for(unsigned j = 0; j < insts.size(); j++){ + Node *node = insts[j]; + if(indset->Contains(insts[j])){ + GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction); res = true; + } + } + annot.Formula = annot.Formula && marker_disjunction; + annot.Simplify(); + return res; + } + + /** Make a checker to determine if an edge in the input RPFP + is satisfied. */ + Node *CheckerJustForEdge(Edge *edge, RPFP *checker, bool expanded_only = false){ + Node *root = checker->CloneNode(edge->Parent); + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); + if(root->Bound.IsFull()) + return 0; + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + if(!GenNodeSolutionWithMarkers(oc,nc->Annotation,expanded_only)) + return 0; + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } + +#ifndef MINIMIZE_CANDIDATES_HARDER + +#if 0 + /** Make a checker to detheermine if an edge in the input RPFP + is satisfied. */ + Node *CheckerForEdge(Edge *edge, RPFP *checker){ + Node *root = checker->CloneNode(edge->Parent); + root->Bound = edge->Parent->Annotation; + root->Bound.Formula = (!AllNodeMarkers(edge->Parent)) || root->Bound.Formula; + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + nc->Annotation = oc->Annotation; + RuleOutNodesPastBound(oc,nc->Annotation); + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } + +#else + /** Make a checker to determine if an edge in the input RPFP + is satisfied. */ + Node *CheckerForEdge(Edge *edge, RPFP *checker){ + Node *root = checker->CloneNode(edge->Parent); + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); +#if 0 + if(root->Bound.IsFull()) + return = 0; +#endif + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } +#endif + + /** If an edge is not satisfied, produce an extension candidate + using instances of its children that violate the parent annotation. + We find these using the marker predicates. */ + void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ + candidate.edge = edge; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *node = root->Outgoing->Children[j]; + Edge *lb = node->Outgoing; + std::vector &insts = insts_of_node[edge->Children[j]]; +#ifndef MINIMIZE_CANDIDATES + for(int k = insts.size()-1; k >= 0; k--) +#else + for(unsigned k = 0; k < insts.size(); k++) +#endif + { + Node *inst = insts[k]; + if(indset->Contains(inst)){ + if(checker->Empty(node) || + eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst)),ctx.bool_val(true))){ + candidate.Children.push_back(inst); + goto next_child; + } + } + } + throw InternalError("No candidate from induction failure"); + next_child:; + } + } +#else + + + /** Make a checker to determine if an edge in the input RPFP + is satisfied. */ + Node *CheckerForEdge(Edge *edge, RPFP *checker){ + Node *root = checker->CloneNode(edge->Parent); + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); + if(root->Bound.IsFull()) + return = 0; + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } + + /** If an edge is not satisfied, produce an extension candidate + using instances of its children that violate the parent annotation. + We find these using the marker predicates. */ + void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ + candidate.edge = edge; + std::vector assumps; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Edge *lb = root->Outgoing->Children[j]->Outgoing; + std::vector &insts = insts_of_node[edge->Children[j]]; + for(unsigned k = 0; k < insts.size(); k++) + { + Node *inst = insts[k]; + expr marker = NodeMarker(inst); + if(indset->Contains(inst)){ + if(checker->Empty(lb->Parent) || + eq(checker->Eval(lb,marker),ctx.bool_val(true))){ + candidate.Children.push_back(inst); + assumps.push_back(checker->Localize(lb,marker)); + goto next_child; + } + assumps.push_back(checker->Localize(lb,marker)); + if(checker->CheckUpdateModel(root,assumps) != unsat){ + candidate.Children.push_back(inst); + goto next_child; + } + assumps.pop_back(); + } + } + throw InternalError("No candidate from induction failure"); + next_child:; + } + } + +#endif + + + Node *CheckerForEdgeClone(Edge *edge, RPFP_caching *checker){ + Edge *gen_cands_edge = checker->GetEdgeClone(edge); + Node *root = gen_cands_edge->Parent; + root->Outgoing = gen_cands_edge; + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); +#if 0 + if(root->Bound.IsFull()) + return = 0; +#endif + checker->AssertNode(root); + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = gen_cands_edge->Children[j]; + GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + } + checker->AssertEdge(gen_cands_edge,1,true); + return root; + } + + /** If the current proposed solution is not inductive, + use the induction failure to generate candidates for extension. */ + void GenCandidatesFromInductionFailure(bool full_scan = false){ + timer_start("GenCandIndFail"); + GenSolutionFromIndSet(true /* add markers */); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *edge = edges[i]; + if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) + continue; +#ifndef USE_NEW_GEN_CANDS + slvr.push(); + RPFP *checker = new RPFP(rpfp->ls); + Node *root = CheckerForEdge(edge,checker); + if(checker->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,checker,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + slvr.pop(1); + delete checker; +#else + RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); + gen_cands_rpfp->Push(); + Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); + if(gen_cands_rpfp->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + gen_cands_rpfp->Pop(1); +#endif + } + updated_nodes.clear(); + timer_stop("GenCandIndFail"); +#ifdef CHECK_CANDS_FROM_IND_SET + for(std::list::iterator it = candidates.begin(), en = candidates.end(); it != en; ++it){ + if(!CandidateFeasible(*it)) + throw "produced infeasible candidate"; + } +#endif + if(!full_scan && candidates.empty()){ + reporter->Message("No candidates from updates. Trying full scan."); + GenCandidatesFromInductionFailure(true); + } + } + +#ifdef CANDS_FROM_UPDATES + /** If the given edge is not inductive in the current proposed solution, + use the induction failure to generate candidates for extension. */ + void GenCandidatesFromEdgeInductionFailure(RPFP::Edge *edge){ + GenSolutionFromIndSet(true /* add markers */); + for(unsigned i = 0; i < edges.size(); i++){ + slvr.push(); + Edge *edge = edges[i]; + RPFP *checker = new RPFP(rpfp->ls); + Node *root = CheckerForEdge(edge,checker); + if(checker->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,checker,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + slvr.pop(1); + delete checker; + } + } +#endif + + /** Find the unexpanded nodes in the inductive subset. */ + void FindNodesToExpand(){ + for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it){ + Node *node = *it; + if(indset->Candidate(node)) + to_expand.push_back(node); + } + } + + /** Try to create some extension candidates from the unexpanded + nodes. */ + void ProduceSomeCandidates(){ + while(candidates.empty() && !to_expand.empty()){ + Node *node = to_expand.front(); + to_expand.pop_front(); + TryExpandNode(node); + } + } + + std::list postponed_candidates; + + /** Try to produce some extension candidates, first from unexpanded + nides, and if this fails, from induction failure. */ + void ProduceCandidatesForExtension(){ + if(candidates.empty()) + ProduceSomeCandidates(); + while(candidates.empty()){ + FindNodesToExpand(); + if(to_expand.empty()) break; + ProduceSomeCandidates(); + } + if(candidates.empty()){ +#ifdef DEPTH_FIRST_EXPAND + if(postponed_candidates.empty()){ + GenCandidatesFromInductionFailure(); + postponed_candidates.swap(candidates); + } + if(!postponed_candidates.empty()){ + candidates.push_back(postponed_candidates.front()); + postponed_candidates.pop_front(); + } +#else + GenCandidatesFromInductionFailure(); +#endif + } + } + + bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){ + if(!node->Annotation.SubsetEq(fact)){ + reporter->Update(node,fact,eager); + indset->Update(node,fact); + updated_nodes.insert(node->map); + node->Annotation.IntersectWith(fact); + return true; + } + return false; + } + + bool UpdateNodeToNode(Node *node, Node *top){ + return Update(node,top->Annotation); + } + + /** Update the unwinding solution, using an interpolant for the + derivation tree. */ + void UpdateWithInterpolant(Node *node, RPFP *tree, Node *top){ + if(top->Outgoing) + for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) + UpdateWithInterpolant(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); + UpdateNodeToNode(node, top); + heuristic->Update(node); + } + + /** Update unwinding lower bounds, using a counterexample. */ + + void UpdateWithCounterexample(Node *node, RPFP *tree, Node *top){ + if(top->Outgoing) + for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) + UpdateWithCounterexample(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); + if(!top->Underapprox.SubsetEq(node->Underapprox)){ + reporter->UpdateUnderapprox(node,top->Underapprox); + // indset->Update(node,top->Annotation); + node->Underapprox.UnionWith(top->Underapprox); + heuristic->Update(node); + } + } + + /** Try to update the unwinding to satisfy the upper bound of a + node. */ + bool SatisfyUpperBound(Node *node){ + if(node->Bound.IsFull()) return true; +#ifdef PROPAGATE_BEFORE_CHECK + Propagate(); +#endif + reporter->Bound(node); + int start_decs = rpfp->CumulativeDecisions(); + DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); + DerivationTree &dt = *dtp; + bool res = dt.Derive(unwinding,node,UseUnderapprox); + int end_decs = rpfp->CumulativeDecisions(); + // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; + last_decisions = end_decs - start_decs; + if(res){ + cex.set(dt.tree,dt.top); // note tree is now owned by cex + if(UseUnderapprox){ + UpdateWithCounterexample(node,dt.tree,dt.top); + } + } + else { + UpdateWithInterpolant(node,dt.tree,dt.top); + delete dt.tree; + } + delete dtp; + return !res; + } + + /* For a given nod in the unwinding, get conjectures from the + proposers and check them locally. Update the node with any true + conjectures. + */ + + void DoEagerDeduction(Node *node){ + for(unsigned i = 0; i < proposers.size(); i++){ + const std::vector &conjectures = proposers[i]->Propose(node); + for(unsigned j = 0; j < conjectures.size(); j++){ + const RPFP::Transformer &conjecture = conjectures[j]; + RPFP::Transformer bound(conjecture); + std::vector conj_vec; + unwinding->CollectConjuncts(bound.Formula,conj_vec); + for(unsigned k = 0; k < conj_vec.size(); k++){ + bound.Formula = conj_vec[k]; + if(CheckEdgeCaching(node->Outgoing,bound) == unsat) + Update(node,bound, /* eager = */ true); + //else + //std::cout << "conjecture failed\n"; + } + } + } + } + + + check_result CheckEdge(RPFP *checker, Edge *edge){ + Node *root = edge->Parent; + checker->Push(); + checker->AssertNode(root); + checker->AssertEdge(edge,1,true); + check_result res = checker->Check(root); + checker->Pop(1); + return res; + } + + check_result CheckEdgeCaching(Edge *unwinding_edge, const RPFP::Transformer &bound){ + + // use a dedicated solver for this edge + // TODO: can this mess be hidden somehow? + + RPFP_caching *checker = gen_cands_rpfp; // TODO: a good choice? + Edge *edge = unwinding_edge->map; // get the edge in the original RPFP + RPFP_caching::scoped_solver_for_edge ssfe(checker,edge,true /* models */, true /*axioms*/); + Edge *checker_edge = checker->GetEdgeClone(edge); + + // copy the annotations and bound to the clone + Node *root = checker_edge->Parent; + root->Bound = bound; + for(unsigned j = 0; j < checker_edge->Children.size(); j++){ + Node *oc = unwinding_edge->Children[j]; + Node *nc = checker_edge->Children[j]; + nc->Annotation = oc->Annotation; + } + + return CheckEdge(checker,checker_edge); + } + + + /* If the counterexample derivation is partial due to + use of underapproximations, complete it. */ + + void BuildFullCex(Node *node){ + DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); + bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree + if(!res) throw "Duality internal error in BuildFullCex"; + cex.set(dt.tree,dt.top); + } + + void UpdateBackEdges(Node *node){ +#ifdef BOUNDED + std::vector &chs = node->Outgoing->Children; + for(unsigned i = 0; i < chs.size(); i++){ + Node *child = chs[i]; + bool is_back = TopoSort[child->map] >= TopoSort[node->map]; + NodeToCounter &nov = back_edges[node]; + NodeToCounter chv = back_edges[child]; + if(is_back) + chv[child->map].val++; + for(NodeToCounter::iterator it = chv.begin(), en = chv.end(); it != en; ++it){ + Node *back = it->first; + Counter &c = nov[back]; + c.val = std::max(c.val,it->second.val); + } + } +#endif + } + + /** Extend the unwinding, keeping it solved. */ + bool Extend(Candidate &cand, Node *&node){ + timer_start("Extend"); + node = CreateNodeInstance(cand.edge->Parent); + CreateEdgeInstance(cand.edge,node,cand.Children); + UpdateBackEdges(node); + reporter->Extend(node); + DoEagerDeduction(node); // first be eager... + bool res = SatisfyUpperBound(node); // then be lazy + if(res) indset->CloseDescendants(node); + else { +#ifdef UNDERAPPROX_NODES + ExpandUnderapproxNodes(cex.get_tree(), cex.get_root()); +#endif + if(UseUnderapprox) BuildFullCex(node); + timer_stop("Extend"); + return res; + } + timer_stop("Extend"); + return res; + } + + void ExpandUnderapproxNodes(RPFP *tree, Node *root){ + Node *node = root->map; + if(underapprox_map.find(node) != underapprox_map.end()){ + RPFP::Transformer cnst = root->Annotation; + tree->EvalNodeAsConstraint(root, cnst); + cnst.Complement(); + Node *orig = underapprox_map[node]; + RPFP::Transformer save = orig->Bound; + orig->Bound = cnst; + DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); + bool res = dt.Derive(unwinding,orig,UseUnderapprox,true,tree); + if(!res){ + UpdateWithInterpolant(orig,dt.tree,dt.top); + throw "bogus underapprox!"; + } + ExpandUnderapproxNodes(tree,dt.top); + } + else if(root->Outgoing){ + std::vector &chs = root->Outgoing->Children; + for(unsigned i = 0; i < chs.size(); i++) + ExpandUnderapproxNodes(tree,chs[i]); + } + } + + // Propagate conjuncts up the unwinding + void Propagate(){ + reporter->Message("beginning propagation"); + timer_start("Propagate"); + std::vector sorted_nodes = unwinding->nodes; + std::sort(sorted_nodes.begin(),sorted_nodes.end(),std::less()); // sorts by sequence number + hash_map > facts; + for(unsigned i = 0; i < sorted_nodes.size(); i++){ + Node *node = sorted_nodes[i]; + std::set &node_facts = facts[node->map]; + if(!(node->Outgoing && indset->Contains(node))) + continue; + std::vector conj_vec; + unwinding->CollectConjuncts(node->Annotation.Formula,conj_vec); + std::set conjs; + std::copy(conj_vec.begin(),conj_vec.end(),std::inserter(conjs,conjs.begin())); + if(!node_facts.empty()){ + RPFP *checker = new RPFP(rpfp->ls); + slvr.push(); + Node *root = checker->CloneNode(node); + Edge *edge = node->Outgoing; + // checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + nc->Annotation = oc->Annotation; // is this needed? + cs.push_back(nc); + } + Edge *checker_edge = checker->CreateEdge(root,edge->F,cs); + checker->AssertEdge(checker_edge, 0, true, false); + std::vector propagated; + for(std::set ::iterator it = node_facts.begin(), en = node_facts.end(); it != en;){ + const expr &fact = *it; + if(conjs.find(fact) == conjs.end()){ + root->Bound.Formula = fact; + slvr.push(); + checker->AssertNode(root); + check_result res = checker->Check(root); + slvr.pop(); + if(res != unsat){ + std::set ::iterator victim = it; + ++it; + node_facts.erase(victim); // if it ain't true, nix it + continue; + } + propagated.push_back(fact); + } + ++it; + } + slvr.pop(); + for(unsigned i = 0; i < propagated.size(); i++){ + root->Annotation.Formula = propagated[i]; + UpdateNodeToNode(node,root); + } + delete checker; + } + for(std::set ::iterator it = conjs.begin(), en = conjs.end(); it != en; ++it){ + expr foo = *it; + node_facts.insert(foo); + } + } + timer_stop("Propagate"); + } + + /** This class represents a derivation tree. */ + class DerivationTree { + public: + + virtual ~DerivationTree(){} + + DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) + : slvr(rpfp->slvr()), + ctx(rpfp->ctx) + { + duality = _duality; + reporter = _reporter; + heuristic = _heuristic; + full_expand = _full_expand; + } + + Duality *duality; + Reporter *reporter; + Heuristic *heuristic; + solver &slvr; + context &ctx; + RPFP *tree; + RPFP::Node *top; + std::list leaves; + bool full_expand; + bool underapprox; + bool constrained; + bool false_approx; + std::vector underapprox_core; + int start_decs, last_decs; + + /* We build derivation trees in one of three modes: + + 1) In normal mode, we build the full tree without considering + underapproximations. + + 2) In underapprox mode, we use underapproximations to cut off + the tree construction. THis means the resulting tree may not + be complete. + + 3) In constrained mode, we build the full tree but use + underapproximations as upper bounds. This mode is used to + complete the partial derivation constructed in underapprox + mode. + */ + + bool Derive(RPFP *rpfp, RPFP::Node *root, bool _underapprox, bool _constrained = false, RPFP *_tree = 0){ + underapprox = _underapprox; + constrained = _constrained; + false_approx = true; + timer_start("Derive"); +#ifndef USE_CACHING_RPFP + tree = _tree ? _tree : new RPFP(rpfp->ls); +#else + RPFP::LogicSolver *cache_ls = new RPFP::iZ3LogicSolver(ctx); + cache_ls->slvr->push(); + tree = _tree ? _tree : new RPFP_caching(cache_ls); +#endif + tree->HornClauses = rpfp->HornClauses; + tree->Push(); // so we can clear out the solver later when finished + top = CreateApproximatedInstance(root); + tree->AssertNode(top); // assert the negation of the top-level spec + timer_start("Build"); + bool res = Build(); + heuristic->Done(); + timer_stop("Build"); + timer_start("Pop"); + tree->Pop(1); + timer_stop("Pop"); +#ifdef USE_CACHING_RPFP + cache_ls->slvr->pop(1); + delete cache_ls; + tree->ls = rpfp->ls; +#endif + timer_stop("Derive"); + return res; + } + +#define WITH_CHILDREN + + void InitializeApproximatedInstance(RPFP::Node *to){ + to->Annotation = to->map->Annotation; +#ifndef WITH_CHILDREN + tree->CreateLowerBoundEdge(to); +#endif + leaves.push_back(to); + } + + Node *CreateApproximatedInstance(RPFP::Node *from){ + Node *to = tree->CloneNode(from); + InitializeApproximatedInstance(to); + return to; + } + + bool CheckWithUnderapprox(){ + timer_start("CheckWithUnderapprox"); + std::vector leaves_vector(leaves.size()); + std::copy(leaves.begin(),leaves.end(),leaves_vector.begin()); + check_result res = tree->Check(top,leaves_vector); + timer_stop("CheckWithUnderapprox"); + return res != unsat; + } + + virtual bool Build(){ +#ifdef EFFORT_BOUNDED_STRAT + start_decs = tree->CumulativeDecisions(); +#endif + while(ExpandSomeNodes(true)); // do high-priority expansions + while (true) + { +#ifndef WITH_CHILDREN + timer_start("asserting leaves"); + timer_start("pushing"); + tree->Push(); + timer_stop("pushing"); + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) + tree->AssertEdge((*it)->Outgoing,1); // assert the overapproximation, and keep it past pop + timer_stop("asserting leaves"); + lbool res = tree->Solve(top, 2); // incremental solve, keep interpolants for two pops + timer_start("popping leaves"); + tree->Pop(1); + timer_stop("popping leaves"); +#else + lbool res; + if((underapprox || false_approx) && top->Outgoing && CheckWithUnderapprox()){ + if(constrained) goto expand_some_nodes; // in constrained mode, keep expanding + goto we_are_sat; // else if underapprox is sat, we stop + } + // tree->Check(top); + res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop +#endif + if (res == l_false) + return false; + + expand_some_nodes: + if(ExpandSomeNodes()) + continue; + + we_are_sat: + if(underapprox && !constrained){ + timer_start("ComputeUnderapprox"); + tree->ComputeUnderapprox(top,1); + timer_stop("ComputeUnderapprox"); + } + else { +#ifdef UNDERAPPROX_NODES +#ifndef SKIP_UNDERAPPROX_NODES + timer_start("ComputeUnderapprox"); + tree->ComputeUnderapprox(top,1); + timer_stop("ComputeUnderapprox"); +#endif +#endif + } + return true; + } + } + + virtual void ExpandNode(RPFP::Node *p){ + // tree->RemoveEdge(p->Outgoing); + Edge *ne = p->Outgoing; + if(ne) { + // reporter->Message("Recycling edge..."); + std::vector &cs = ne->Children; + for(unsigned i = 0; i < cs.size(); i++) + InitializeApproximatedInstance(cs[i]); + // ne->dual = expr(); + } + else { + Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); + std::vector &cs = edge->Children; + std::vector children(cs.size()); + for(unsigned i = 0; i < cs.size(); i++) + children[i] = CreateApproximatedInstance(cs[i]); + ne = tree->CreateEdge(p, p->map->Outgoing->F, children); + ne->map = p->map->Outgoing->map; + } +#ifndef WITH_CHILDREN + tree->AssertEdge(ne); // assert the edge in the solver +#else + tree->AssertEdge(ne,0,!full_expand,(underapprox || false_approx)); // assert the edge in the solver +#endif + reporter->Expand(ne); + } + +#define UNDERAPPROXCORE +#ifndef UNDERAPPROXCORE + void ExpansionChoices(std::set &best){ + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) + if (!tree->Empty(*it)) // if used in the counter-model + choices.insert(*it); + heuristic->ChooseExpand(choices, best); + } +#else +#if 0 + + void ExpansionChoices(std::set &best){ + std::vector unused_set, used_set; + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ + Node *n = *it; + if (!tree->Empty(n)) + used_set.push_back(n); + else + unused_set.push_back(n); + } + if(tree->Check(top,unused_set) == unsat) + throw "error in ExpansionChoices"; + for(unsigned i = 0; i < used_set.size(); i++){ + Node *n = used_set[i]; + unused_set.push_back(n); + if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ + unused_set.pop_back(); + choices.insert(n); + } + else + std::cout << "Using underapprox of " << n->number << std::endl; + } + heuristic->ChooseExpand(choices, best); + } +#else + void ExpansionChoicesFull(std::set &best, bool high_priority, bool best_only = false){ + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) + if (high_priority || !tree->Empty(*it)) // if used in the counter-model + choices.insert(*it); + heuristic->ChooseExpand(choices, best, high_priority, best_only); + } + + void ExpansionChoicesRec(std::vector &unused_set, std::vector &used_set, + std::set &choices, int from, int to){ + if(from == to) return; + int orig_unused = unused_set.size(); + unused_set.resize(orig_unused + (to - from)); + std::copy(used_set.begin()+from,used_set.begin()+to,unused_set.begin()+orig_unused); + if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ + unused_set.resize(orig_unused); + if(to - from == 1){ +#if 1 + std::cout << "Not using underapprox of " << used_set[from] ->number << std::endl; +#endif + choices.insert(used_set[from]); + } + else { + int mid = from + (to - from)/2; + ExpansionChoicesRec(unused_set, used_set, choices, from, mid); + ExpansionChoicesRec(unused_set, used_set, choices, mid, to); + } + } + else { +#if 1 + std::cout << "Using underapprox of "; + for(int i = from; i < to; i++){ + std::cout << used_set[i]->number << " "; + if(used_set[i]->map->Underapprox.IsEmpty()) + std::cout << "(false!) "; + } + std::cout << std::endl; +#endif + } + } + + std::set old_choices; + + void ExpansionChoices(std::set &best, bool high_priority, bool best_only = false){ + if(!underapprox || constrained || high_priority){ + ExpansionChoicesFull(best, high_priority,best_only); + return; + } + std::vector unused_set, used_set; + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ + Node *n = *it; + if (!tree->Empty(n)){ + if(old_choices.find(n) != old_choices.end() || n->map->Underapprox.IsEmpty()) + choices.insert(n); + else + used_set.push_back(n); + } + else + unused_set.push_back(n); + } + if(tree->Check(top,unused_set) == unsat) + throw "error in ExpansionChoices"; + ExpansionChoicesRec(unused_set, used_set, choices, 0, used_set.size()); + old_choices = choices; + heuristic->ChooseExpand(choices, best, high_priority); + } +#endif +#endif + + bool ExpandSomeNodes(bool high_priority = false, int max = INT_MAX){ +#ifdef EFFORT_BOUNDED_STRAT + last_decs = tree->CumulativeDecisions() - start_decs; +#endif + timer_start("ExpandSomeNodes"); + timer_start("ExpansionChoices"); + std::set choices; + ExpansionChoices(choices,high_priority,max != INT_MAX); + timer_stop("ExpansionChoices"); + std::list leaves_copy = leaves; // copy so can modify orig + leaves.clear(); + int count = 0; + for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ + if(choices.find(*it) != choices.end() && count < max){ + count++; + ExpandNode(*it); + } + else leaves.push_back(*it); + } + timer_stop("ExpandSomeNodes"); + return !choices.empty(); + } + + void RemoveExpansion(RPFP::Node *p){ + Edge *edge = p->Outgoing; + Node *parent = edge->Parent; +#ifndef KEEP_EXPANSIONS + std::vector cs = edge->Children; + tree->DeleteEdge(edge); + for(unsigned i = 0; i < cs.size(); i++) + tree->DeleteNode(cs[i]); +#endif + leaves.push_back(parent); + } + + // remove all the descendants of tree root (but not root itself) + void RemoveTree(RPFP *tree, RPFP::Node *root){ + Edge *edge = root->Outgoing; + std::vector cs = edge->Children; + tree->DeleteEdge(edge); + for(unsigned i = 0; i < cs.size(); i++){ + RemoveTree(tree,cs[i]); + tree->DeleteNode(cs[i]); + } + } + }; + + class DerivationTreeSlow : public DerivationTree { + public: + + struct stack_entry { + unsigned level; // SMT solver stack level + std::vector expansions; + }; + + std::vector stack; + + hash_map updates; + + DerivationTreeSlow(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) + : DerivationTree(_duality, rpfp, _reporter, _heuristic, _full_expand) { + stack.push_back(stack_entry()); + } + + struct DoRestart {}; + + virtual bool Build(){ + while (true) { + try { + return BuildMain(); + } + catch (const DoRestart &) { + // clear the statck and try again + updated_nodes.clear(); + while(stack.size() > 1) + PopLevel(); + reporter->Message("restarted"); + } + } + } + + bool BuildMain(){ + + stack.back().level = tree->slvr().get_scope_level(); + bool was_sat = true; + int update_failures = 0; + + while (true) + { + lbool res; + + unsigned slvr_level = tree->slvr().get_scope_level(); + if(slvr_level != stack.back().level) + throw "stacks out of sync!"; + reporter->Depth(stack.size()); + + // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop + check_result foo = tree->Check(top); + res = foo == unsat ? l_false : l_true; + + if (res == l_false) { + if (stack.empty()) // should never happen + return false; + + { + std::vector &expansions = stack.back().expansions; + int update_count = 0; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + tree->SolveSingleNode(top,node); +#ifdef NO_GENERALIZE + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); +#else + try { + if(expansions.size() == 1 && NodeTooComplicated(node)) + SimplifyNode(node); + else + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); + Generalize(node); + } + catch(const RPFP::Bad &){ + // bad interpolants can get us here + throw DoRestart(); + } +#endif + if(RecordUpdate(node)) + update_count++; + else + heuristic->Update(node->map); // make it less likely to expand this node in future + } + if(update_count == 0){ + if(was_sat){ + update_failures++; + if(update_failures > 10) + throw Incompleteness(); + } + reporter->Message("backtracked without learning"); + } + else update_failures = 0; + } + tree->ComputeProofCore(); // need to compute the proof core before popping solver + bool propagated = false; + while(1) { + bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop + PopLevel(); + if(stack.size() == 1)break; + if(prev_level_used){ + Node *node = stack.back().expansions[0]; +#ifndef NO_PROPAGATE + if(!Propagate(node)) break; +#endif + if(!RecordUpdate(node)) break; // shouldn't happen! + RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list + propagated = true; + continue; + } + if(propagated) break; // propagation invalidates the proof core, so disable non-chron backtrack + RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list + std::vector &unused_ex = stack.back().expansions; + for(unsigned i = 0; i < unused_ex.size(); i++) + heuristic->Update(unused_ex[i]->map); // make it less likely to expand this node in future + } + HandleUpdatedNodes(); + if(stack.size() == 1){ + if(top->Outgoing) + tree->DeleteEdge(top->Outgoing); // in case we kept the tree + return false; + } + was_sat = false; + } + else { + was_sat = true; + tree->Push(); + std::vector &expansions = stack.back().expansions; +#ifndef NO_DECISIONS + for(unsigned i = 0; i < expansions.size(); i++){ + tree->FixCurrentState(expansions[i]->Outgoing); + } +#endif +#if 0 + if(tree->slvr().check() == unsat) + throw "help!"; +#endif + int expand_max = 1; + if(0&&duality->BatchExpand){ + int thing = stack.size() * 0.1; + expand_max = std::max(1,thing); + if(expand_max > 1) + std::cout << "foo!\n"; + } + + if(ExpandSomeNodes(false,expand_max)) + continue; + tree->Pop(1); + while(stack.size() > 1){ + tree->Pop(1); + stack.pop_back(); + } + return true; + } + } + } + + void PopLevel(){ + std::vector &expansions = stack.back().expansions; + tree->Pop(1); + hash_set leaves_to_remove; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + // if(node != top) + // tree->ConstrainParent(node->Incoming[0],node); + std::vector &cs = node->Outgoing->Children; + for(unsigned i = 0; i < cs.size(); i++){ + leaves_to_remove.insert(cs[i]); + UnmapNode(cs[i]); + if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) + throw "help!"; + } + } + RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + RemoveExpansion(node); + } + stack.pop_back(); + } + + bool NodeTooComplicated(Node *node){ + int ops = tree->CountOperators(node->Annotation.Formula); + if(ops > 10) return true; + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); + return tree->CountOperators(node->Annotation.Formula) > 3; + } + + void SimplifyNode(Node *node){ + // have to destroy the old proof to get a new interpolant + timer_start("SimplifyNode"); + tree->PopPush(); + try { + tree->InterpolateByCases(top,node); + } + catch(const RPFP::Bad&){ + timer_stop("SimplifyNode"); + throw RPFP::Bad(); + } + timer_stop("SimplifyNode"); + } + + bool LevelUsedInProof(unsigned level){ + std::vector &expansions = stack[level].expansions; + for(unsigned i = 0; i < expansions.size(); i++) + if(tree->EdgeUsedInProof(expansions[i]->Outgoing)) + return true; + return false; + } + + void RemoveUpdateNodesAtCurrentLevel() { + for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ + Node *node = *it; + if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ + std::list::iterator victim = it; + ++it; + updated_nodes.erase(victim); + } + else + ++it; + } + } + + void RemoveLeaves(hash_set &leaves_to_remove){ + std::list leaves_copy; + leaves_copy.swap(leaves); + for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ + if(leaves_to_remove.find(*it) == leaves_to_remove.end()) + leaves.push_back(*it); + } + } + + hash_map > node_map; + std::list updated_nodes; + + virtual void ExpandNode(RPFP::Node *p){ + stack.push_back(stack_entry()); + stack.back().level = tree->slvr().get_scope_level(); + stack.back().expansions.push_back(p); + DerivationTree::ExpandNode(p); + std::vector &new_nodes = p->Outgoing->Children; + for(unsigned i = 0; i < new_nodes.size(); i++){ + Node *n = new_nodes[i]; + node_map[n->map].push_back(n); + } + } + + bool RecordUpdate(Node *node){ + bool res = duality->UpdateNodeToNode(node->map,node); + if(res){ + std::vector to_update = node_map[node->map]; + for(unsigned i = 0; i < to_update.size(); i++){ + Node *node2 = to_update[i]; + // maintain invariant that no nodes on updated list are created at current stack level + if(node2 == node || !(node->Incoming.size() > 0 && AtCurrentStackLevel(node2->Incoming[0]->Parent))){ + updated_nodes.push_back(node2); + if(node2 != node) + node2->Annotation = node->Annotation; + } + } + } + return res; + } + + void HandleUpdatedNodes(){ + for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ + Node *node = *it; + node->Annotation = node->map->Annotation; + if(node->Incoming.size() > 0) + tree->ConstrainParent(node->Incoming[0],node); + if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ + std::list::iterator victim = it; + ++it; + updated_nodes.erase(victim); + } + else + ++it; + } + } + + bool AtCurrentStackLevel(Node *node){ + std::vector vec = stack.back().expansions; + for(unsigned i = 0; i < vec.size(); i++) + if(vec[i] == node) + return true; + return false; + } + + void UnmapNode(Node *node){ + std::vector &vec = node_map[node->map]; + for(unsigned i = 0; i < vec.size(); i++){ + if(vec[i] == node){ + std::swap(vec[i],vec.back()); + vec.pop_back(); + return; + } + } + throw "can't unmap node"; + } + + void Generalize(Node *node){ +#ifndef USE_RPFP_CLONE + tree->Generalize(top,node); +#else + RPFP_caching *clone_rpfp = duality->clone_rpfp; + if(!node->Outgoing->map) return; + Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); + Node *clone_node = clone_edge->Parent; + clone_node->Annotation = node->Annotation; + for(unsigned i = 0; i < clone_edge->Children.size(); i++) + clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; + clone_rpfp->GeneralizeCache(clone_edge); + node->Annotation = clone_node->Annotation; +#endif + } + + bool Propagate(Node *node){ +#ifdef USE_RPFP_CLONE + RPFP_caching *clone_rpfp = duality->clone_rpfp; + Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); + Node *clone_node = clone_edge->Parent; + clone_node->Annotation = node->map->Annotation; + for(unsigned i = 0; i < clone_edge->Children.size(); i++) + clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; + bool res = clone_rpfp->PropagateCache(clone_edge); + if(res) + node->Annotation = clone_node->Annotation; + return res; +#else + return false; +#endif + } + + }; + + + class Covering { + + struct cover_info { + Node *covered_by; + std::list covers; + bool dominated; + std::set dominates; + cover_info(){ + covered_by = 0; + dominated = false; + } + }; + + typedef hash_map cover_map; + cover_map cm; + Duality *parent; + bool some_updates; + +#define NO_CONJ_ON_SIMPLE_LOOPS +#ifdef NO_CONJ_ON_SIMPLE_LOOPS + hash_set simple_loops; +#endif + + Node *&covered_by(Node *node){ + return cm[node].covered_by; + } + + std::list &covers(Node *node){ + return cm[node].covers; + } + + std::vector &insts_of_node(Node *node){ + return parent->insts_of_node[node]; + } + + Reporter *reporter(){ + return parent->reporter; + } + + std::set &dominates(Node *x){ + return cm[x].dominates; + } + + bool dominates(Node *x, Node *y){ + std::set &d = cm[x].dominates; + return d.find(y) != d.end(); + } + + bool &dominated(Node *x){ + return cm[x].dominated; + } + + public: + + Covering(Duality *_parent){ + parent = _parent; + some_updates = false; + +#ifdef NO_CONJ_ON_SIMPLE_LOOPS + hash_map > outgoing; + for(unsigned i = 0; i < parent->rpfp->edges.size(); i++) + outgoing[parent->rpfp->edges[i]->Parent].push_back(parent->rpfp->edges[i]); + for(unsigned i = 0; i < parent->rpfp->nodes.size(); i++){ + Node * node = parent->rpfp->nodes[i]; + std::vector &outs = outgoing[node]; + if(outs.size() == 2){ + for(int j = 0; j < 2; j++){ + Edge *loop_edge = outs[j]; + if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent) + simple_loops.insert(node); + } + } + } +#endif + + } + + bool IsCoveredRec(hash_set &memo, Node *node){ + if(memo.find(node) != memo.end()) + return false; + memo.insert(node); + if(covered_by(node)) return true; + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) + if(IsCoveredRec(memo,node->Outgoing->Children[i])) + return true; + return false; + } + + bool IsCovered(Node *node){ + hash_set memo; + return IsCoveredRec(memo,node); + } + +#ifndef UNDERAPPROX_NODES + void RemoveCoveringsBy(Node *node){ + std::list &cs = covers(node); + for(std::list::iterator it = cs.begin(), en = cs.end(); it != en; it++){ + covered_by(*it) = 0; + reporter()->RemoveCover(*it,node); + } + cs.clear(); + } +#else + void RemoveCoveringsBy(Node *node){ + std::vector &cs = parent->all_of_node[node->map]; + for(std::vector::iterator it = cs.begin(), en = cs.end(); it != en; it++){ + Node *other = *it; + if(covered_by(other) && CoverOrder(node,other)){ + covered_by(other) = 0; + reporter()->RemoveCover(*it,node); + } + } + } +#endif + + void RemoveAscendantCoveringsRec(hash_set &memo, Node *node){ + if(memo.find(node) != memo.end()) + return; + memo.insert(node); + RemoveCoveringsBy(node); + for(std::vector::iterator it = node->Incoming.begin(), en = node->Incoming.end(); it != en; ++it) + RemoveAscendantCoveringsRec(memo,(*it)->Parent); + } + + void RemoveAscendantCoverings(Node *node){ + hash_set memo; + RemoveAscendantCoveringsRec(memo,node); + } + + bool CoverOrder(Node *covering, Node *covered){ +#ifdef UNDERAPPROX_NODES + if(parent->underapprox_map.find(covered) != parent->underapprox_map.end()) + return false; + if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) + return covering->number < covered->number || parent->underapprox_map[covering] == covered; +#endif + return covering->number < covered->number; + } + + bool CheckCover(Node *covered, Node *covering){ + return + CoverOrder(covering,covered) + && covered->Annotation.SubsetEq(covering->Annotation) + && !IsCovered(covering); + } + + bool CoverByNode(Node *covered, Node *covering){ + if(CheckCover(covered,covering)){ + covered_by(covered) = covering; + covers(covering).push_back(covered); + std::vector others; others.push_back(covering); + reporter()->AddCover(covered,others); + RemoveAscendantCoverings(covered); + return true; + } + else + return false; + } + +#ifdef UNDERAPPROX_NODES + bool CoverByAll(Node *covered){ + RPFP::Transformer all = covered->Annotation; + all.SetEmpty(); + std::vector &insts = parent->insts_of_node[covered->map]; + std::vector others; + for(unsigned i = 0; i < insts.size(); i++){ + Node *covering = insts[i]; + if(CoverOrder(covering,covered) && !IsCovered(covering)){ + others.push_back(covering); + all.UnionWith(covering->Annotation); + } + } + if(others.size() && covered->Annotation.SubsetEq(all)){ + covered_by(covered) = covered; // anything non-null will do + reporter()->AddCover(covered,others); + RemoveAscendantCoverings(covered); + return true; + } + else + return false; + } +#endif + + bool Close(Node *node){ + if(covered_by(node)) + return true; +#ifndef UNDERAPPROX_NODES + std::vector &insts = insts_of_node(node->map); + for(unsigned i = 0; i < insts.size(); i++) + if(CoverByNode(node,insts[i])) + return true; +#else + if(CoverByAll(node)) + return true; +#endif + return false; + } + + bool CloseDescendantsRec(hash_set &memo, Node *node){ + if(memo.find(node) != memo.end()) + return false; + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) + if(CloseDescendantsRec(memo,node->Outgoing->Children[i])) + return true; + if(Close(node)) + return true; + memo.insert(node); + return false; + } + + bool CloseDescendants(Node *node){ + timer_start("CloseDescendants"); + hash_set memo; + bool res = CloseDescendantsRec(memo,node); + timer_stop("CloseDescendants"); + return res; + } + + bool Contains(Node *node){ + timer_start("Contains"); + bool res = !IsCovered(node); + timer_stop("Contains"); + return res; + } + + bool Candidate(Node *node){ + timer_start("Candidate"); + bool res = !IsCovered(node) && !dominated(node); + timer_stop("Candidate"); + return res; + } + + void SetDominated(Node *node){ + dominated(node) = true; + } + + bool CouldCover(Node *covered, Node *covering){ +#ifdef NO_CONJ_ON_SIMPLE_LOOPS + // Forsimple loops, we rely on propagation, not covering + if(simple_loops.find(covered->map) != simple_loops.end()) + return false; +#endif +#ifdef UNDERAPPROX_NODES + // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) + // return parent->underapprox_map[covering] == covered; +#endif + if(CoverOrder(covering,covered) + && !IsCovered(covering)){ + RPFP::Transformer f(covering->Annotation); f.SetEmpty(); +#if defined(TOP_DOWN) || defined(EFFORT_BOUNDED_STRAT) + if(parent->StratifiedInlining) + return true; +#endif + return !covering->Annotation.SubsetEq(f); + } + return false; + } + + bool ContainsCex(Node *node, Counterexample &cex){ + expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula); + return eq(val,parent->ctx.bool_val(true)); + } + + /** We conjecture that the annotations of similar nodes may be + true of this one. We start with later nodes, on the + principle that their annotations are likely weaker. We save + a counterexample -- if annotations of other nodes are true + in this counterexample, we don't need to check them. + */ + +#ifndef UNDERAPPROX_NODES + bool Conjecture(Node *node){ + std::vector &insts = insts_of_node(node->map); + Counterexample cex; + for(int i = insts.size() - 1; i >= 0; i--){ + Node *other = insts[i]; + if(CouldCover(node,other)){ + reporter()->Forcing(node,other); + if(cex.get_tree() && !ContainsCex(other,cex)) + continue; + cex.clear(); + if(parent->ProveConjecture(node,other->Annotation,other,&cex)) + if(CloseDescendants(node)) + return true; + } + } + cex.clear(); + return false; + } +#else + bool Conjecture(Node *node){ + std::vector &insts = insts_of_node(node->map); + Counterexample cex; + RPFP::Transformer Bound = node->Annotation; + Bound.SetEmpty(); + bool some_other = false; + for(int i = insts.size() - 1; i >= 0; i--){ + Node *other = insts[i]; + if(CouldCover(node,other)){ + reporter()->Forcing(node,other); + Bound.UnionWith(other->Annotation); + some_other = true; + } + } + if(some_other && parent->ProveConjecture(node,Bound)){ + CloseDescendants(node); + return true; + } + return false; + } +#endif + + void Update(Node *node, const RPFP::Transformer &update){ + RemoveCoveringsBy(node); + some_updates = true; + } + +#ifndef UNDERAPPROX_NODES + Node *GetSimilarNode(Node *node){ + if(!some_updates) + return 0; + std::vector &insts = insts_of_node(node->map); + for(int i = insts.size()-1; i >= 0; i--){ + Node *other = insts[i]; + if(dominates(node,other)) + if(CoverOrder(other,node) + && !IsCovered(other)) + return other; + } + return 0; + } +#else + Node *GetSimilarNode(Node *node){ + if(!some_updates) + return 0; + std::vector &insts = insts_of_node(node->map); + for(int i = insts.size() - 1; i >= 0; i--){ + Node *other = insts[i]; + if(CoverOrder(other,node) + && !IsCovered(other)) + return other; + } + return 0; + } +#endif + + bool Dominates(Node * node, Node *other){ + if(node == other) return false; + if(other->Outgoing->map == 0) return true; + if(node->Outgoing->map == other->Outgoing->map){ + assert(node->Outgoing->Children.size() == other->Outgoing->Children.size()); + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ + Node *nc = node->Outgoing->Children[i]; + Node *oc = other->Outgoing->Children[i]; + if(!(nc == oc || oc->Outgoing->map ==0 || dominates(nc,oc))) + return false; + } + return true; + } + return false; + } + + void Add(Node *node){ + std::vector &insts = insts_of_node(node->map); + for(unsigned i = 0; i < insts.size(); i++){ + Node *other = insts[i]; + if(Dominates(node,other)){ + cm[node].dominates.insert(other); + cm[other].dominated = true; + reporter()->Dominates(node, other); + } + } + } + + }; + + /* This expansion heuristic makes use of a previuosly obtained + counterexample as a guide. This is for use in abstraction + refinement schemes.*/ + + class ReplayHeuristic : public Heuristic { + + Counterexample old_cex; + public: + ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) + : Heuristic(_rpfp) + { + old_cex.swap(_old_cex); // take ownership from caller + } + + ~ReplayHeuristic(){ + } + + // Maps nodes of derivation tree into old cex + hash_map cex_map; + + void Done() { + cex_map.clear(); + old_cex.clear(); + } + + void ShowNodeAndChildren(Node *n){ + std::cout << n->Name.name() << ": "; + std::vector &chs = n->Outgoing->Children; + for(unsigned i = 0; i < chs.size(); i++) + std::cout << chs[i]->Name.name() << " " ; + std::cout << std::endl; + } + + // HACK: When matching relation names, we drop suffixes used to + // make the names unique between runs. For compatibility + // with boggie, we drop suffixes beginning with @@ + std::string BaseName(const std::string &name){ + int pos = name.find("@@"); + if(pos >= 1) + return name.substr(0,pos); + return name; + } + + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ + if(!high_priority || !old_cex.get_tree()){ + Heuristic::ChooseExpand(choices,best,false); + return; + } + // first, try to match the derivatino tree nodes to the old cex + std::set matched, unmatched; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it); + if(cex_map.empty()) + cex_map[node] = old_cex.get_root(); // match the root nodes + if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node + Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! + if(cex_map.find(parent) == cex_map.end()) + throw "catastrophe in ReplayHeuristic::ChooseExpand"; + Node *old_parent = cex_map[parent]; + std::vector &chs = parent->Outgoing->Children; + if(old_parent && old_parent->Outgoing){ + std::vector &old_chs = old_parent->Outgoing->Children; + for(unsigned i = 0, j=0; i < chs.size(); i++){ + if(j < old_chs.size() && BaseName(chs[i]->Name.name().str()) == BaseName(old_chs[j]->Name.name().str())) + cex_map[chs[i]] = old_chs[j++]; + else { + std::cerr << "WARNING: duality: unmatched child: " << chs[i]->Name.name() << std::endl; + cex_map[chs[i]] = 0; + } + } + goto matching_done; + } + for(unsigned i = 0; i < chs.size(); i++) + cex_map[chs[i]] = 0; + } + matching_done: + Node *old_node = cex_map[node]; + if(!old_node) + unmatched.insert(node); + else if(old_cex.get_tree()->Empty(old_node)) + unmatched.insert(node); + else + matched.insert(node); + } + if (matched.empty() && !high_priority) + Heuristic::ChooseExpand(unmatched,best,false); + else + Heuristic::ChooseExpand(matched,best,false); + } + }; + + + class LocalHeuristic : public Heuristic { + + RPFP::Node *old_node; + public: + LocalHeuristic(RPFP *_rpfp) + : Heuristic(_rpfp) + { + old_node = 0; + } + + void SetOldNode(RPFP::Node *_old_node){ + old_node = _old_node; + cex_map.clear(); + } + + // Maps nodes of derivation tree into old subtree + hash_map cex_map; + + virtual void ChooseExpand(const std::set &choices, std::set &best){ + if(old_node == 0){ + Heuristic::ChooseExpand(choices,best); + return; + } + // first, try to match the derivatino tree nodes to the old cex + std::set matched, unmatched; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it); + if(cex_map.empty()) + cex_map[node] = old_node; // match the root nodes + if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node + Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! + if(cex_map.find(parent) == cex_map.end()) + throw "catastrophe in ReplayHeuristic::ChooseExpand"; + Node *old_parent = cex_map[parent]; + std::vector &chs = parent->Outgoing->Children; + if(old_parent && old_parent->Outgoing){ + std::vector &old_chs = old_parent->Outgoing->Children; + if(chs.size() == old_chs.size()){ + for(unsigned i = 0; i < chs.size(); i++) + cex_map[chs[i]] = old_chs[i]; + goto matching_done; + } + else + std::cout << "derivation tree does not match old cex" << std::endl; + } + for(unsigned i = 0; i < chs.size(); i++) + cex_map[chs[i]] = 0; + } + matching_done: + Node *old_node = cex_map[node]; + if(!old_node) + unmatched.insert(node); + else if(old_node != node->map) + unmatched.insert(node); + else + matched.insert(node); + } + Heuristic::ChooseExpand(unmatched,best); + } + }; + + /** This proposer class generates conjectures based on the + unwinding generated by a previous solver. The assumption is + that the provious solver was working on a different + abstraction of the same system. The trick is to adapt the + annotations in the old unwinding to the new predicates. We + start by generating a map from predicates and parameters in + the old problem to the new. + + HACK: mapping is done by cheesy name comparison. + */ + + class HistoryProposer : public Proposer + { + Duality *old_solver; + Duality *new_solver; + hash_map > conjectures; + + public: + /** Construct a history solver. */ + HistoryProposer(Duality *_old_solver, Duality *_new_solver) + : old_solver(_old_solver), new_solver(_new_solver) { + + // tricky: names in the axioms may have changed -- map them + hash_set &old_constants = old_solver->unwinding->ls->get_constants(); + hash_set &new_constants = new_solver->rpfp->ls->get_constants(); + hash_map cmap; + for(hash_set::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it) + cmap[GetKey(*it)] = *it; + hash_map bckg_map; + for(hash_set::iterator it = old_constants.begin(), en = old_constants.end(); it != en; ++it){ + func_decl f = new_solver->ctx.translate(*it); // move to new context + if(cmap.find(GetKey(f)) != cmap.end()) + bckg_map[f] = cmap[GetKey(f)]; + // else + // std::cout << "constant not matched\n"; + } + + RPFP *old_unwinding = old_solver->unwinding; + hash_map > pred_match; + + // index all the predicates in the old unwinding + for(unsigned i = 0; i < old_unwinding->nodes.size(); i++){ + Node *node = old_unwinding->nodes[i]; + std::string key = GetKey(node); + pred_match[key].push_back(node); + } + + // match with predicates in the new RPFP + RPFP *rpfp = new_solver->rpfp; + for(unsigned i = 0; i < rpfp->nodes.size(); i++){ + Node *node = rpfp->nodes[i]; + std::string key = GetKey(node); + std::vector &matches = pred_match[key]; + for(unsigned j = 0; j < matches.size(); j++) + MatchNodes(node,matches[j],bckg_map); + } + } + + virtual std::vector &Propose(Node *node){ + return conjectures[node->map]; + } + + virtual ~HistoryProposer(){ + }; + + private: + void MatchNodes(Node *new_node, Node *old_node, hash_map &bckg_map){ + if(old_node->Annotation.IsFull()) + return; // don't conjecture true! + hash_map var_match; + std::vector &new_params = new_node->Annotation.IndParams; + // Index the new parameters by their keys + for(unsigned i = 0; i < new_params.size(); i++) + var_match[GetKey(new_params[i])] = new_params[i]; + RPFP::Transformer &old = old_node->Annotation; + std::vector from_params = old.IndParams; + for(unsigned j = 0; j < from_params.size(); j++) + from_params[j] = new_solver->ctx.translate(from_params[j]); // get in new context + std::vector to_params = from_params; + for(unsigned j = 0; j < to_params.size(); j++){ + std::string key = GetKey(to_params[j]); + if(var_match.find(key) == var_match.end()){ + // std::cout << "unmatched parameter!\n"; + return; + } + to_params[j] = var_match[key]; + } + expr fmla = new_solver->ctx.translate(old.Formula); // get in new context + fmla = new_solver->rpfp->SubstParams(old.IndParams,to_params,fmla); // substitute parameters + hash_map memo; + fmla = new_solver->rpfp->SubstRec(memo,bckg_map,fmla); // substitute background constants + RPFP::Transformer new_annot = new_node->Annotation; + new_annot.Formula = fmla; + conjectures[new_node].push_back(new_annot); + } + + // We match names by removing suffixes beginning with double at sign + + std::string GetKey(Node *node){ + return GetKey(node->Name); + } + + std::string GetKey(const expr &var){ + return GetKey(var.decl()); + } + + std::string GetKey(const func_decl &f){ + std::string name = f.name().str(); + int idx = name.find("@@"); + if(idx >= 0) + name.erase(idx); + return name; + } + }; + }; + + + class StreamReporter : public Reporter { + std::ostream &s; + public: + StreamReporter(RPFP *_rpfp, std::ostream &_s) + : Reporter(_rpfp), s(_s) {event = 0; depth = -1;} + int event; + int depth; + void ev(){ + s << "[" << event++ << "]" ; + } + virtual void Extend(RPFP::Node *node){ + ev(); s << "node " << node->number << ": " << node->Name.name(); + std::vector &rps = node->Outgoing->Children; + for(unsigned i = 0; i < rps.size(); i++) + s << " " << rps[i]->number; + s << std::endl; + } + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ + ev(); s << "update " << node->number << " " << node->Name.name() << ": "; + rpfp->Summarize(update.Formula); + if(depth > 0) s << " (depth=" << depth << ")"; + if(eager) s << " (eager)"; + s << std::endl; + } + virtual void Bound(RPFP::Node *node){ + ev(); s << "check " << node->number << std::endl; + } + virtual void Expand(RPFP::Edge *edge){ + RPFP::Node *node = edge->Parent; + ev(); s << "expand " << node->map->number << " " << node->Name.name(); + if(depth > 0) s << " (depth=" << depth << ")"; + s << std::endl; + } + virtual void Depth(int d){ + depth = d; + } + virtual void AddCover(RPFP::Node *covered, std::vector &covering){ + ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; + for(unsigned i = 0; i < covering.size(); i++) + s << covering[i]->number << " "; + s << std::endl; + } + virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){ + ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; + } + virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){ + ev(); s << "forcing " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; + } + virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){ + ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; + rpfp->Summarize(t.Formula); + s << std::endl; + } + virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ + ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; + } + virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){ + ev(); s << "induction failure: " << edge->Parent->Name.name() << ", children ="; + for(unsigned i = 0; i < children.size(); i++) + s << " " << children[i]->number; + s << std::endl; + } + virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){ + ev(); s << "underapprox " << node->number << " " << node->Name.name() << ": " << update.Formula << std::endl; + } + virtual void Reject(RPFP::Edge *edge, const std::vector &children){ + ev(); s << "reject " << edge->Parent->number << " " << edge->Parent->Name.name() << ": "; + for(unsigned i = 0; i < children.size(); i++) + s << " " << children[i]->number; + s << std::endl; + } + virtual void Message(const std::string &msg){ + ev(); s << "msg " << msg << std::endl; + } + + }; + + Solver *Solver::Create(const std::string &solver_class, RPFP *rpfp){ + Duality *s = alloc(Duality,rpfp); + return s; + } + + Reporter *CreateStdoutReporter(RPFP *rpfp){ + return new StreamReporter(rpfp, std::cout); + } +} diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 2a699a99502..604192ebcc5 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -340,6 +340,12 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st params p; return simplify(p); } + + expr context::make_var(int idx, const sort &s){ + ::sort * a = to_sort(s.raw()); + return cook(m().mk_var(idx,a)); + } + expr expr::qe_lite() const { ::qe_lite qe(m()); @@ -374,6 +380,12 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st return q.ctx().cook(q.m().update_quantifier(thing, is_forall, num_patterns, &_patterns[0], to_expr(b.raw()))); } + expr clone_quantifier(decl_kind dk, const expr &q, const expr &b){ + quantifier *thing = to_quantifier(q.raw()); + bool is_forall = dk == Forall; + return q.ctx().cook(q.m().update_quantifier(thing, is_forall, to_expr(b.raw()))); + } + void expr::get_patterns(std::vector &pats) const { quantifier *thing = to_quantifier(raw()); unsigned num_patterns = thing->get_num_patterns(); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h old mode 100755 new mode 100644 index 17b960477cd..a0975a719fe --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1,1475 +1,1486 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - duality_wrapper.h - -Abstract: - - wrap various Z3 classes in the style expected by duality - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ -#ifndef __DUALITY_WRAPPER_H_ -#define __DUALITY_WRAPPER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include"version.h" -#include - -#include "iz3hash.h" -#include "model.h" -#include "solver.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"scoped_proof.h" - -namespace Duality { - - class exception; - class config; - class context; - class symbol; - class params; - class ast; - class sort; - class func_decl; - class expr; - class solver; - class goal; - class tactic; - class probe; - class model; - class func_interp; - class func_entry; - class statistics; - class apply_result; - class fixedpoint; - class literals; - - /** - Duality global configuration object. - */ - - class config { - params_ref m_params; - config & operator=(config const & s); - public: - config(config const & s) : m_params(s.m_params) {} - config(const params_ref &_params) : m_params(_params) {} - config() { } // TODO: create a new params object here - ~config() { } - void set(char const * param, char const * value) { m_params.set_str(param,value); } - void set(char const * param, bool value) { m_params.set_bool(param,value); } - void set(char const * param, int value) { m_params.set_uint(param,value); } - params_ref &get() {return m_params;} - const params_ref &get() const {return m_params;} - }; - - enum decl_kind { - 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, - OtherBasic, - OtherArith, - OtherArray, - Other - }; - - enum sort_kind {BoolSort,IntSort,RealSort,ArraySort,UninterpretedSort,UnknownSort}; - - /** - A context has an ast manager global configuration options, etc. - */ - - class context { - protected: - ast_manager &mgr; - config m_config; - - 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; - - public: - context(ast_manager &_manager, const config &_config) : mgr(_manager), m_config(_config), m_arith_util(_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"); - } - ~context() { } - - ast_manager &m() const {return *(ast_manager *)&mgr;} - - void set(char const * param, char const * value) { m_config.set(param,value); } - void set(char const * param, bool value) { m_config.set(param,value); } - void set(char const * param, int value) { m_config.set(param,value); } - config &get_config() {return m_config;} - - symbol str_symbol(char const * s); - symbol int_symbol(int n); - - sort bool_sort(); - sort int_sort(); - sort real_sort(); - sort bv_sort(unsigned sz); - sort array_sort(sort d, sort r); - - 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(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); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); - func_decl fresh_func_decl(char const * name, const std::vector &domain, sort const & range); - func_decl fresh_func_decl(char const * name, sort const & range); - - expr constant(symbol const & name, sort const & s); - expr constant(char const * name, sort const & s); - expr constant(const std::string &name, sort const & s); - expr bool_const(char const * name); - expr int_const(char const * name); - expr real_const(char const * name); - expr bv_const(char const * name, unsigned sz); - - expr bool_val(bool b); - - expr int_val(int n); - expr int_val(unsigned n); - expr int_val(char const * n); - - expr real_val(int n, int d); - expr real_val(int n); - expr real_val(unsigned n); - expr real_val(char const * n); - - expr bv_val(int n, unsigned sz); - expr bv_val(unsigned n, unsigned sz); - expr bv_val(char const * n, unsigned sz); - - expr num_val(int n, sort const & s); - - expr mki(family_id fid, ::decl_kind dk, int n, ::expr **args); - expr make(decl_kind op, int n, ::expr **args); - expr make(decl_kind op, const std::vector &args); - expr make(decl_kind op); - expr make(decl_kind op, const expr &arg0); - expr make(decl_kind op, const expr &arg0, const expr &arg1); - expr make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2); - - expr make_quant(decl_kind op, const std::vector &bvs, const expr &body); - expr make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body); - - - decl_kind get_decl_kind(const func_decl &t); - - sort_kind get_sort_kind(const sort &s); - - expr translate(const expr &e); - func_decl translate(const func_decl &); - - void print_expr(std::ostream &s, const ast &e); - - fixedpoint mk_fixedpoint(); - - expr cook(::expr *a); - std::vector cook(ptr_vector< ::expr> v); - ::expr *uncook(const expr &a); - }; - - template - class z3array { - T * m_array; - unsigned m_size; - z3array(z3array const & s); - z3array & operator=(z3array const & s); - public: - z3array(unsigned sz):m_size(sz) { m_array = new T[sz]; } - ~z3array() { delete[] m_array; } - unsigned size() const { return m_size; } - T & operator[](unsigned i) { assert(i < m_size); return m_array[i]; } - T const & operator[](unsigned i) const { assert(i < m_size); return m_array[i]; } - T const * ptr() const { return m_array; } - T * ptr() { return m_array; } - }; - - class object { - protected: - context * m_ctx; - public: - object(): m_ctx((context *)0) {} - object(context & c):m_ctx(&c) {} - object(object const & s):m_ctx(s.m_ctx) {} - context & ctx() const { return *m_ctx; } - friend void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } - ast_manager &m() const {return m_ctx->m();} - }; - - class symbol : public object { - ::symbol m_sym; - public: - symbol(context & c, ::symbol s):object(c), m_sym(s) {} - symbol(symbol const & s):object(s), m_sym(s.m_sym) {} - symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } - operator ::symbol() const {return m_sym;} - std::string str() const { - if (m_sym.is_numerical()) { - std::ostringstream buffer; - buffer << m_sym.get_num(); - return buffer.str(); - } - else { - return m_sym.bare_str(); - } - } - friend std::ostream & operator<<(std::ostream & out, symbol const & s){ - return out << s.str(); - } - friend bool operator==(const symbol &x, const symbol &y){ - return x.m_sym == y.m_sym; - } - }; - - class params : public config {}; - - /** Wrapper around an ast pointer */ - class ast_i : public object { - protected: - ::ast *_ast; - public: - ::ast * const &raw() const {return _ast;} - ast_i(context & c, ::ast *a = 0) : object(c) {_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 : public ast_i { - public: - operator ::ast*() const { return raw(); } - friend bool eq(ast const & a, ast const & b) { return a.raw() == b.raw(); } - - - ast(context &c, ::ast *a = 0) : ast_i(c,a) { - if(_ast) - m().inc_ref(a); - } - - ast() {} - - ast(const ast &other) : ast_i(other) { - if(_ast) - m().inc_ref(_ast); - } - - ast &operator=(const ast &other) { - if(_ast) - m().dec_ref(_ast); - _ast = other._ast; - m_ctx = other.m_ctx; - if(_ast) - m().inc_ref(_ast); - return *this; - } - - ~ast(){ - if(_ast) - m().dec_ref(_ast); - } - - void show() const; - - }; - - class sort : public ast { - public: - sort(context & c):ast(c) {} - sort(context & c, ::sort *s):ast(c, s) {} - sort(sort const & s):ast(s) {} - operator ::sort*() const { return to_sort(raw()); } - sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } - - bool is_bool() const { return m().is_bool(*this); } - bool is_int() const { return ctx().get_sort_kind(*this) == IntSort; } - bool is_real() const { return ctx().get_sort_kind(*this) == RealSort; } - bool is_arith() const; - bool is_array() const { return ctx().get_sort_kind(*this) == ArraySort; } - bool is_datatype() const; - bool is_relation() const; - bool is_finite_domain() const; - - - sort array_domain() const; - sort array_range() const; - }; - - - class func_decl : public ast { - public: - func_decl() : ast() {} - func_decl(context & c):ast(c) {} - func_decl(context & c, ::func_decl *n):ast(c, n) {} - func_decl(func_decl const & s):ast(s) {} - operator ::func_decl*() const { return to_func_decl(*this); } - func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } - - unsigned arity() const; - sort domain(unsigned i) const; - sort range() const; - symbol name() const {return symbol(ctx(),to_func_decl(raw())->get_name());} - decl_kind get_decl_kind() const; - - bool is_const() const { return arity() == 0; } - - expr operator()(unsigned n, expr const * args) const; - expr operator()(const std::vector &args) const; - expr operator()() const; - expr operator()(expr const & a) const; - expr operator()(int a) const; - expr operator()(expr const & a1, expr const & a2) const; - expr operator()(expr const & a1, int a2) const; - expr operator()(int a1, expr const & a2) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; - - func_decl get_func_decl_parameter(unsigned idx){ - return func_decl(ctx(),to_func_decl(to_func_decl(raw())->get_parameters()[idx].get_ast())); - } - - }; - - class expr : public ast { - public: - expr() : ast() {} - expr(context & c):ast(c) {} - expr(context & c, ::ast *n):ast(c, n) {} - expr(expr const & n):ast(n) {} - expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } - operator ::expr*() const { return to_expr(raw()); } - unsigned get_id() const {return to_expr(raw())->get_id();} - - sort get_sort() const { return sort(ctx(),m().get_sort(to_expr(raw()))); } - - bool is_bool() const { return get_sort().is_bool(); } - bool is_int() const { return get_sort().is_int(); } - bool is_real() const { return get_sort().is_real(); } - bool is_arith() const { return get_sort().is_arith(); } - bool is_array() const { return get_sort().is_array(); } - bool is_datatype() const { return get_sort().is_datatype(); } - bool is_relation() const { return get_sort().is_relation(); } - bool is_finite_domain() const { return get_sort().is_finite_domain(); } - bool is_true() const {return is_app() && decl().get_decl_kind() == True; } - - bool is_numeral() const { - return is_app() && decl().get_decl_kind() == OtherArith && m().is_unique_value(to_expr(raw())); - } - bool is_app() const {return raw()->get_kind() == AST_APP;} - bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} - bool is_var() const {return raw()->get_kind() == AST_VAR;} - bool is_label (bool &pos,std::vector &names) const ; - bool is_ground() const {return to_app(raw())->is_ground();} - bool has_quantifiers() const {return to_app(raw())->has_quantifiers();} - - // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } - func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} - unsigned num_args() const { - ast_kind dk = raw()->get_kind(); - switch(dk){ - case AST_APP: - return to_app(raw())->get_num_args(); - case AST_QUANTIFIER: - return 1; - case AST_VAR: - return 0; - default:; - } - SASSERT(0); - return 0; - } - expr arg(unsigned i) const { - ast_kind dk = raw()->get_kind(); - switch(dk){ - case AST_APP: - return ctx().cook(to_app(raw())->get_arg(i)); - case AST_QUANTIFIER: - return ctx().cook(to_quantifier(raw())->get_expr()); - default:; - } - assert(0); - return expr(); - } - - expr body() const { - return ctx().cook(to_quantifier(raw())->get_expr()); - } - - friend expr operator!(expr const & a) { - // ::expr *e = a; - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_NOT,a)); - } - - friend expr operator&&(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_AND,a,b)); - } - - friend expr operator||(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_OR,a,b)); - } - - friend expr implies(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_IMPLIES,a,b)); - } - - friend expr operator==(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_EQ,a,b)); - } - - friend expr operator!=(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DISTINCT,a,b)); - } - - friend expr operator+(expr const & a, expr const & b) { - return a.ctx().make(Plus,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); - } - - friend expr operator*(expr const & a, expr const & b) { - return a.ctx().make(Times,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); - } - - friend expr operator/(expr const & a, expr const & b) { - return a.ctx().make(Div,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); - } - - friend expr operator-(expr const & a) { - return a.ctx().make(Uminus,a); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); - } - - friend expr operator-(expr const & a, expr const & b) { - return a.ctx().make(Sub,a,b); // expr(a.ctx(),a.m().mk_app(a.ctx().m_arith_fid,OP_SUB,a,b)); - } - - friend expr operator<=(expr const & a, expr const & b) { - return a.ctx().make(Leq,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); - } - - friend expr operator>=(expr const & a, expr const & b) { - return a.ctx().make(Geq,a,b); //expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); - } - - friend expr operator<(expr const & a, expr const & b) { - return a.ctx().make(Lt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); - } - - friend expr operator>(expr const & a, expr const & b) { - return a.ctx().make(Gt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); - } - - expr simplify() const; - - expr simplify(params const & p) const; - - expr qe_lite() const; - - expr qe_lite(const std::set &idxs, bool index_of_bound) const; - - friend expr clone_quantifier(const expr &, const expr &); - - friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); - - friend std::ostream & operator<<(std::ostream & out, expr const & m){ - m.ctx().print_expr(out,m); - return out; - } - - void get_patterns(std::vector &pats) const ; - - unsigned get_quantifier_num_bound() const { - return to_quantifier(raw())->get_num_decls(); - } - - unsigned get_index_value() const { - var* va = to_var(raw()); - return va->get_idx(); - } - - bool is_quantifier_forall() const { - return to_quantifier(raw())->is_forall(); - } - - sort get_quantifier_bound_sort(unsigned n) const { - return sort(ctx(),to_quantifier(raw())->get_decl_sort(n)); - } - - symbol get_quantifier_bound_name(unsigned n) const { - return symbol(ctx(),to_quantifier(raw())->get_decl_names()[n]); - } - - friend expr forall(const std::vector &quants, const expr &body); - - friend expr exists(const std::vector &quants, const expr &body); - - }; - - - typedef ::decl_kind pfrule; - - class proof : public ast { - public: - proof(context & c):ast(c) {} - proof(context & c, ::proof *s):ast(c, s) {} - proof(proof const & s):ast(s) {} - operator ::proof*() const { return to_app(raw()); } - proof & operator=(proof const & s) { return static_cast(ast::operator=(s)); } - - pfrule rule() const { - ::func_decl *d = to_app(raw())->get_decl(); - return d->get_decl_kind(); - } - - unsigned num_prems() const { - return to_app(raw())->get_num_args() - 1; - } - - expr conc() const { - return ctx().cook(to_app(raw())->get_arg(num_prems())); - } - - proof prem(unsigned i) const { - return proof(ctx(),to_app(to_app(raw())->get_arg(i))); - } - - void get_assumptions(std::vector &assumps); - }; - -#if 0 - -#if Z3_MAJOR_VERSION > 4 || Z3_MAJOR_VERSION == 4 && Z3_MINOR_VERSION >= 3 - template - class ast_vector_tpl : public object { - Z3_ast_vector m_vector; - void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } - public: - ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } - ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } - ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } - ~ast_vector_tpl() { /* Z3_ast_vector_dec_ref(ctx(), m_vector); */ } - operator Z3_ast_vector() const { return m_vector; } - unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } - T operator[](unsigned i) const { Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } - void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } - void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } - T back() const { return operator[](size() - 1); } - void pop_back() { assert(size() > 0); resize(size() - 1); } - bool empty() const { return size() == 0; } - ast_vector_tpl & operator=(ast_vector_tpl const & s) { - Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); - // Z3_ast_vector_dec_ref(ctx(), m_vector); - m_ctx = s.m_ctx; - m_vector = s.m_vector; - return *this; - } - friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } - }; - - typedef ast_vector_tpl ast_vector; - typedef ast_vector_tpl expr_vector; - typedef ast_vector_tpl sort_vector; - typedef ast_vector_tpl func_decl_vector; - -#endif - -#endif - - class func_interp : public object { - ::func_interp * m_interp; - void init(::func_interp * e) { - m_interp = e; - } - public: - func_interp(context & c, ::func_interp * e):object(c) { init(e); } - func_interp(func_interp const & s):object(s) { init(s.m_interp); } - ~func_interp() { } - operator ::func_interp *() const { return m_interp; } - func_interp & operator=(func_interp const & s) { - m_ctx = s.m_ctx; - m_interp = s.m_interp; - return *this; - } - unsigned num_entries() const { return m_interp->num_entries(); } - expr get_arg(unsigned ent, unsigned arg) const { - return expr(ctx(),m_interp->get_entry(ent)->get_arg(arg)); - } - expr get_value(unsigned ent) const { - return expr(ctx(),m_interp->get_entry(ent)->get_result()); - } - expr else_value() const { - return expr(ctx(),m_interp->get_else()); - } - }; - - - - class model : public object { - model_ref m_model; - void init(::model *m) { - m_model = m; - } - public: - model(context & c, ::model * m = 0):object(c), m_model(m) { } - model(model const & s):object(s), m_model(s.m_model) { } - ~model() { } - operator ::model *() const { return m_model.get(); } - model & operator=(model const & s) { - // ::model *_inc_ref(s.ctx(), s.m_model); - // ::model *_dec_ref(ctx(), m_model); - m_ctx = s.m_ctx; - m_model = s.m_model.get(); - return *this; - } - model & operator=(::model *s) { - m_model = s; - return *this; - } - bool null() const {return !m_model;} - - expr eval(expr const & n, bool model_completion=true) const { - ::model * _m = m_model.get(); - expr_ref result(ctx().m()); - _m->eval(n, result, model_completion); - return expr(ctx(), result); - } - - void show() const; - void show_hash() const; - - unsigned num_consts() const {return m_model.get()->get_num_constants();} - unsigned num_funcs() const {return m_model.get()->get_num_functions();} - func_decl get_const_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_constant(i));} - func_decl get_func_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_function(i));} - unsigned size() const; - func_decl operator[](unsigned i) const; - - expr get_const_interp(func_decl f) const { - return ctx().cook(m_model->get_const_interp(to_func_decl(f.raw()))); - } - - func_interp get_func_interp(func_decl f) const { - return func_interp(ctx(),m_model->get_func_interp(to_func_decl(f.raw()))); - } - -#if 0 - friend std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } -#endif - }; - -#if 0 - class stats : public object { - Z3_stats m_stats; - void init(Z3_stats e) { - m_stats = e; - Z3_stats_inc_ref(ctx(), m_stats); - } - public: - stats(context & c):object(c), m_stats(0) {} - stats(context & c, Z3_stats e):object(c) { init(e); } - stats(stats const & s):object(s) { init(s.m_stats); } - ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } - operator Z3_stats() const { return m_stats; } - stats & operator=(stats const & s) { - Z3_stats_inc_ref(s.ctx(), s.m_stats); - if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); - m_ctx = s.m_ctx; - m_stats = s.m_stats; - return *this; - } - unsigned size() const { return Z3_stats_size(ctx(), m_stats); } - std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } - bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } - bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } - unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } - double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } - friend std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } - }; -#endif - - enum check_result { - unsat, sat, unknown - }; - - class fixedpoint : public object { - - public: - void get_rules(std::vector &rules); - void get_assertions(std::vector &rules); - void register_relation(const func_decl &rela); - void add_rule(const expr &clause, const symbol &name); - void assert_cnst(const expr &cnst); - }; - - inline std::ostream & operator<<(std::ostream & out, check_result r) { - if (r == unsat) out << "unsat"; - else if (r == sat) out << "sat"; - else out << "unknown"; - return out; - } - - inline check_result to_check_result(lbool l) { - if (l == l_true) return sat; - else if (l == l_false) return unsat; - return unknown; - } - - class solver : public object { - protected: - ::solver *m_solver; - model the_model; - bool canceled; - proof_gen_mode m_mode; - bool extensional; - public: - solver(context & c, bool extensional = false, bool models = true); - solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} - solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} - ~solver() { - if(m_solver) - dealloc(m_solver); - } - operator ::solver*() const { return m_solver; } - solver & operator=(solver const & s) { - m_ctx = s.m_ctx; - m_solver = s.m_solver; - the_model = s.the_model; - m_mode = s.m_mode; - return *this; - } - struct cancel_exception {}; - void checkpoint(){ - if(canceled) - throw(cancel_exception()); - } - // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } - void push() { scoped_proof_mode spm(m(),m_mode); m_solver->push(); } - void pop(unsigned n = 1) { scoped_proof_mode spm(m(),m_mode); m_solver->pop(n); } - // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } - void add(expr const & e) { scoped_proof_mode spm(m(),m_mode); m_solver->assert_expr(e); } - check_result check() { - scoped_proof_mode spm(m(),m_mode); - checkpoint(); - lbool r = m_solver->check_sat(0,0); - model_ref m; - m_solver->get_model(m); - the_model = m.get(); - return to_check_result(r); - } - check_result check_keep_model(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { - scoped_proof_mode spm(m(),m_mode); - model old_model(the_model); - check_result res = check(n,assumptions,core_size,core); - if(the_model == 0) - the_model = old_model; - return res; - } - check_result check(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { - scoped_proof_mode spm(m(),m_mode); - checkpoint(); - std::vector< ::expr *> _assumptions(n); - for (unsigned i = 0; i < n; i++) { - _assumptions[i] = to_expr(assumptions[i]); - } - the_model = 0; - lbool r = m_solver->check_sat(n,&_assumptions[0]); - - if(core_size && core){ - ptr_vector< ::expr> _core; - m_solver->get_unsat_core(_core); - *core_size = _core.size(); - for(unsigned i = 0; i < *core_size; i++) - core[i] = expr(ctx(),_core[i]); - } - - model_ref m; - m_solver->get_model(m); - the_model = m.get(); - - return to_check_result(r); - } -#if 0 - check_result check(expr_vector assumptions) { - scoped_proof_mode spm(m(),m_mode); - unsigned n = assumptions.size(); - z3array _assumptions(n); - for (unsigned i = 0; i < n; i++) { - check_context(*this, assumptions[i]); - _assumptions[i] = assumptions[i]; - } - Z3_lbool r = Z3_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); - check_error(); - return to_check_result(r); - } -#endif - model get_model() const { return model(ctx(), the_model); } - // std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } - // stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } -#if 0 - expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } - expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } -#endif - // expr proof() const { Z3_ast r = Z3_solver_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } - // friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } - - int get_num_decisions(); - - void cancel(){ - scoped_proof_mode spm(m(),m_mode); - canceled = true; - if(m_solver) - m_solver->cancel(); - } - - unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} - - void show(); - void print(const char *filename); - void show_assertion_ids(); - - proof get_proof(){ - scoped_proof_mode spm(m(),m_mode); - return proof(ctx(),m_solver->get_proof()); - } - - bool extensional_array_theory() {return extensional;} - }; - -#if 0 - class goal : public object { - Z3_goal m_goal; - void init(Z3_goal s) { - m_goal = s; - Z3_goal_inc_ref(ctx(), s); - } - public: - goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } - goal(context & c, Z3_goal s):object(c) { init(s); } - goal(goal const & s):object(s) { init(s.m_goal); } - ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } - operator Z3_goal() const { return m_goal; } - goal & operator=(goal const & s) { - Z3_goal_inc_ref(s.ctx(), s.m_goal); - Z3_goal_dec_ref(ctx(), m_goal); - m_ctx = s.m_ctx; - m_goal = s.m_goal; - return *this; - } - void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } - unsigned size() const { return Z3_goal_size(ctx(), m_goal); } - expr operator[](unsigned i) const { Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } - Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } - bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } - unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } - void reset() { Z3_goal_reset(ctx(), m_goal); } - unsigned num_exprs() const { Z3_goal_num_exprs(ctx(), m_goal); } - bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } - bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } - friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } - }; - - class apply_result : public object { - Z3_apply_result m_apply_result; - void init(Z3_apply_result s) { - m_apply_result = s; - Z3_apply_result_inc_ref(ctx(), s); - } - public: - apply_result(context & c, Z3_apply_result s):object(c) { init(s); } - apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } - ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } - operator Z3_apply_result() const { return m_apply_result; } - apply_result & operator=(apply_result const & s) { - Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); - Z3_apply_result_dec_ref(ctx(), m_apply_result); - m_ctx = s.m_ctx; - m_apply_result = s.m_apply_result; - return *this; - } - unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } - goal operator[](unsigned i) const { Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } - goal operator[](int i) const { assert(i >= 0); return this->operator[](static_cast(i)); } - model convert_model(model const & m, unsigned i = 0) const { - check_context(*this, m); - Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); - check_error(); - return model(ctx(), new_m); - } - friend std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } - }; - - class tactic : public object { - Z3_tactic m_tactic; - void init(Z3_tactic s) { - m_tactic = s; - Z3_tactic_inc_ref(ctx(), s); - } - public: - tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } - tactic(context & c, Z3_tactic s):object(c) { init(s); } - tactic(tactic const & s):object(s) { init(s.m_tactic); } - ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } - operator Z3_tactic() const { return m_tactic; } - tactic & operator=(tactic const & s) { - Z3_tactic_inc_ref(s.ctx(), s.m_tactic); - Z3_tactic_dec_ref(ctx(), m_tactic); - m_ctx = s.m_ctx; - m_tactic = s.m_tactic; - return *this; - } - solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } - apply_result apply(goal const & g) const { - check_context(*this, g); - Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); - check_error(); - return apply_result(ctx(), r); - } - apply_result operator()(goal const & g) const { - return apply(g); - } - std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } - friend tactic operator&(tactic const & t1, tactic const & t2) { - check_context(t1, t2); - Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - friend tactic operator|(tactic const & t1, tactic const & t2) { - check_context(t1, t2); - Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - friend tactic repeat(tactic const & t, unsigned max=UINT_MAX) { - Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic with(tactic const & t, params const & p) { - Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic try_for(tactic const & t, unsigned ms) { - Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); - t.check_error(); - return tactic(t.ctx(), r); - } - }; - - class probe : public object { - Z3_probe m_probe; - void init(Z3_probe s) { - m_probe = s; - Z3_probe_inc_ref(ctx(), s); - } - public: - probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } - probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } - probe(context & c, Z3_probe s):object(c) { init(s); } - probe(probe const & s):object(s) { init(s.m_probe); } - ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } - operator Z3_probe() const { return m_probe; } - probe & operator=(probe const & s) { - Z3_probe_inc_ref(s.ctx(), s.m_probe); - Z3_probe_dec_ref(ctx(), m_probe); - m_ctx = s.m_ctx; - m_probe = s.m_probe; - return *this; - } - double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } - double operator()(goal const & g) const { return apply(g); } - friend probe operator<=(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } - friend probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } - friend probe operator>=(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } - friend probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } - friend probe operator<(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } - friend probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } - friend probe operator>(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } - friend probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } - friend probe operator==(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } - friend probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } - friend probe operator&&(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator||(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator!(probe const & p) { - Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); - } - }; - - inline tactic fail_if(probe const & p) { - Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); - p.check_error(); - return tactic(p.ctx(), r); - } - inline tactic when(probe const & p, tactic const & t) { - check_context(p, t); - Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); - t.check_error(); - return tactic(t.ctx(), r); - } - inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { - check_context(p, t1); check_context(p, t2); - Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - -#endif - - inline expr context::bool_val(bool b){return b ? make(True) : make(False);} - - inline symbol context::str_symbol(char const * s) { ::symbol r = ::symbol(s); return symbol(*this, r); } - inline symbol context::int_symbol(int n) { ::symbol r = ::symbol(n); return symbol(*this, r); } - - inline sort context::bool_sort() { - ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); - return sort(*this, s); - } - inline sort context::int_sort() { - ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); - return sort(*this, s); - } - inline sort context::real_sort() { - ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); - return sort(*this, s); - } - inline sort context::array_sort(sort d, sort r) { - parameter params[2] = { parameter(d), parameter(to_sort(r)) }; - ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); - return sort(*this, s); - } - - - inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { - 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 func_decl(*this,d); - } - - inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { - return function(str_symbol(name), arity, domain, range); - } - - inline func_decl context::function(char const * name, sort const & domain, sort const & range) { - sort args[1] = { domain }; - return function(name, 1, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { - sort args[2] = { d1, d2 }; - return function(name, 2, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { - sort args[3] = { d1, d2, d3 }; - return function(name, 3, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { - sort args[4] = { d1, d2, d3, d4 }; - return function(name, 4, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { - sort args[5] = { d1, d2, d3, d4, d5 }; - return function(name, 5, args, range); - } - - - inline expr context::constant(symbol const & name, sort const & s) { - ::expr *r = m().mk_const(m().mk_const_decl(name, s)); - return expr(*this, r); - } - inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } - inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } - inline expr context::int_const(char const * name) { return constant(name, int_sort()); } - inline expr context::real_const(char const * name) { return constant(name, real_sort()); } - inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } - - inline expr func_decl::operator()(const std::vector &args) const { - return operator()(static_cast(args.size()),&args[0]); - } - inline expr func_decl::operator()() const { - return operator()(0,0); - } - inline expr func_decl::operator()(expr const & a) const { - return operator()(1,&a); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2) const { - expr args[2] = {a1,a2}; - return operator()(2,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { - expr args[3] = {a1,a2,a3}; - return operator()(3,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { - expr args[4] = {a1,a2,a3,a4}; - return operator()(4,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { - expr args[5] = {a1,a2,a3,a4,a5}; - return operator()(5,args); - } - - - inline expr select(expr const & a, expr const & i) { return a.ctx().make(Select,a,i); } - inline expr store(expr const & a, expr const & i, expr const & v) { return a.ctx().make(Store,a,i,v); } - - inline expr forall(const std::vector &quants, const expr &body){ - return body.ctx().make_quant(Forall,quants,body); - } - - inline expr exists(const std::vector &quants, const expr &body){ - return body.ctx().make_quant(Exists,quants,body); - } - - inline expr context::int_val(int n){ - :: sort *r = m().mk_sort(m_arith_fid, INT_SORT); - return cook(m_arith_util.mk_numeral(rational(n),r)); - } - - - class literals : public object { - }; - - class TermTree { - public: - - TermTree(expr _term){ - term = _term; - } - - TermTree(expr _term, const std::vector &_children){ - term = _term; - children = _children; - } - - inline expr getTerm(){return term;} - - inline std::vector &getTerms(){return terms;} - - inline std::vector &getChildren(){ - return children; - } - - inline int number(int from){ - for(unsigned i = 0; i < children.size(); i++) - from = children[i]->number(from); - num = from; - return from + 1; - } - - inline int getNumber(){ - return num; - } - - inline void setTerm(expr t){term = t;} - - inline void addTerm(expr t){terms.push_back(t);} - - inline void setChildren(const std::vector & _children){ - children = _children; - } - - inline void setNumber(int _num){ - num = _num; - } - - ~TermTree(){ - for(unsigned i = 0; i < children.size(); i++) - delete children[i]; - } - - private: - expr term; - std::vector terms; - std::vector children; - int num; - }; - - typedef context interpolating_context; - - class interpolating_solver : public solver { - public: - interpolating_solver(context &ctx, bool models = true) - : solver(ctx, true, models) - { - weak_mode = false; - } - - public: - lbool interpolate(const std::vector &assumptions, - std::vector &interpolants, - model &_model, - literals &lits, - bool incremental - ); - - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - literals &lits, - bool incremental - ); - - bool read_interpolation_problem(const std::string &file_name, - std::vector &assumptions, - std::vector &theory, - std::string &error_message - ); - - void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ); - - void AssertInterpolationAxiom(const expr &expr); - void RemoveInterpolationAxiom(const expr &expr); - - void SetWeakInterpolants(bool weak); - void SetPrintToFile(const std::string &file_name); - - const std::vector &GetInterpolationAxioms() {return theory;} - const char *profile(); - - private: - bool weak_mode; - std::string print_filename; - std::vector theory; - }; - - - inline expr context::cook(::expr *a) {return expr(*this,a);} - - inline std::vector context::cook(ptr_vector< ::expr> v) { - std::vector _v(v.size()); - for(unsigned i = 0; i < v.size(); i++) - _v[i] = cook(v[i]); - return _v; - } - - inline ::expr *context::uncook(const expr &a) { - m().inc_ref(a.raw()); - return to_expr(a.raw()); - } - - inline expr context::translate(const expr &e) { - ::expr *f = to_expr(e.raw()); - if(&e.ctx().m() != &m()) // same ast manager -> no translation - throw "ast manager mismatch"; - return cook(f); - } - - inline func_decl context::translate(const func_decl &e) { - ::func_decl *f = to_func_decl(e.raw()); - if(&e.ctx().m() != &m()) // same ast manager -> no translation - throw "ast manager mismatch"; - return func_decl(*this,f); - } - - typedef double clock_t; - clock_t current_time(); - inline void output_time(std::ostream &os, clock_t time){os << time;} - - template class uptr { - public: - X *ptr; - uptr(){ptr = 0;} - void set(X *_ptr){ - if(ptr) delete ptr; - ptr = _ptr; - } - X *get(){ return ptr;} - ~uptr(){ - if(ptr) delete ptr; - } - }; - -}; - -// to make Duality::ast hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::ast &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make Duality::ast usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::ast &s, const Duality::ast &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// to make Duality::ast usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::expr &s, const Duality::expr &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// to make Duality::func_decl hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::func_decl &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make Duality::func_decl usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::func_decl &s, const Duality::func_decl &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -#endif - +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality_wrapper.h + +Abstract: + + wrap various Z3 classes in the style expected by duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ +#ifndef __DUALITY_WRAPPER_H_ +#define __DUALITY_WRAPPER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include"version.h" +#include + +#include "iz3hash.h" +#include "model.h" +#include "solver.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"scoped_proof.h" + +namespace Duality { + + class exception; + class config; + class context; + class symbol; + class params; + class ast; + class sort; + class func_decl; + class expr; + class solver; + class goal; + class tactic; + class probe; + class model; + class func_interp; + class func_entry; + class statistics; + class apply_result; + class fixedpoint; + class literals; + + /** + Duality global configuration object. + */ + + class config { + params_ref m_params; + config & operator=(config const & s); + public: + config(config const & s) : m_params(s.m_params) {} + config(const params_ref &_params) : m_params(_params) {} + config() { } // TODO: create a new params object here + ~config() { } + void set(char const * param, char const * value) { m_params.set_str(param,value); } + void set(char const * param, bool value) { m_params.set_bool(param,value); } + void set(char const * param, int value) { m_params.set_uint(param,value); } + params_ref &get() {return m_params;} + const params_ref &get() const {return m_params;} + }; + + enum decl_kind { + 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, + OtherBasic, + OtherArith, + OtherArray, + Other + }; + + enum sort_kind {BoolSort,IntSort,RealSort,ArraySort,UninterpretedSort,UnknownSort}; + + /** + A context has an ast manager global configuration options, etc. + */ + + class context { + protected: + ast_manager &mgr; + config m_config; + + 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; + + public: + context(ast_manager &_manager, const config &_config) : mgr(_manager), m_config(_config), m_arith_util(_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"); + } + ~context() { } + + ast_manager &m() const {return *(ast_manager *)&mgr;} + + void set(char const * param, char const * value) { m_config.set(param,value); } + void set(char const * param, bool value) { m_config.set(param,value); } + void set(char const * param, int value) { m_config.set(param,value); } + config &get_config() {return m_config;} + + symbol str_symbol(char const * s); + symbol int_symbol(int n); + + sort bool_sort(); + sort int_sort(); + sort real_sort(); + sort bv_sort(unsigned sz); + sort array_sort(sort d, sort r); + + 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(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); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); + func_decl fresh_func_decl(char const * name, const std::vector &domain, sort const & range); + func_decl fresh_func_decl(char const * name, sort const & range); + + expr constant(symbol const & name, sort const & s); + expr constant(char const * name, sort const & s); + expr constant(const std::string &name, sort const & s); + expr bool_const(char const * name); + expr int_const(char const * name); + expr real_const(char const * name); + expr bv_const(char const * name, unsigned sz); + + expr bool_val(bool b); + + expr int_val(int n); + expr int_val(unsigned n); + expr int_val(char const * n); + + expr real_val(int n, int d); + expr real_val(int n); + expr real_val(unsigned n); + expr real_val(char const * n); + + expr bv_val(int n, unsigned sz); + expr bv_val(unsigned n, unsigned sz); + expr bv_val(char const * n, unsigned sz); + + expr num_val(int n, sort const & s); + + expr mki(family_id fid, ::decl_kind dk, int n, ::expr **args); + expr make(decl_kind op, int n, ::expr **args); + expr make(decl_kind op, const std::vector &args); + expr make(decl_kind op); + expr make(decl_kind op, const expr &arg0); + expr make(decl_kind op, const expr &arg0, const expr &arg1); + expr make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2); + + expr make_quant(decl_kind op, const std::vector &bvs, const expr &body); + expr make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body); + expr make_var(int idx, const sort &s); + + decl_kind get_decl_kind(const func_decl &t); + + sort_kind get_sort_kind(const sort &s); + + expr translate(const expr &e); + func_decl translate(const func_decl &); + + void print_expr(std::ostream &s, const ast &e); + + fixedpoint mk_fixedpoint(); + + expr cook(::expr *a); + std::vector cook(ptr_vector< ::expr> v); + ::expr *uncook(const expr &a); + }; + + template + class z3array { + T * m_array; + unsigned m_size; + z3array(z3array const & s); + z3array & operator=(z3array const & s); + public: + z3array(unsigned sz):m_size(sz) { m_array = new T[sz]; } + ~z3array() { delete[] m_array; } + unsigned size() const { return m_size; } + T & operator[](unsigned i) { assert(i < m_size); return m_array[i]; } + T const & operator[](unsigned i) const { assert(i < m_size); return m_array[i]; } + T const * ptr() const { return m_array; } + T * ptr() { return m_array; } + }; + + class object { + protected: + context * m_ctx; + public: + object(): m_ctx((context *)0) {} + object(context & c):m_ctx(&c) {} + object(object const & s):m_ctx(s.m_ctx) {} + context & ctx() const { return *m_ctx; } + friend void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } + ast_manager &m() const {return m_ctx->m();} + }; + + class symbol : public object { + ::symbol m_sym; + public: + symbol(context & c, ::symbol s):object(c), m_sym(s) {} + symbol(symbol const & s):object(s), m_sym(s.m_sym) {} + symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } + operator ::symbol() const {return m_sym;} + std::string str() const { + if (m_sym.is_numerical()) { + std::ostringstream buffer; + buffer << m_sym.get_num(); + return buffer.str(); + } + else { + return m_sym.bare_str(); + } + } + friend std::ostream & operator<<(std::ostream & out, symbol const & s){ + return out << s.str(); + } + friend bool operator==(const symbol &x, const symbol &y){ + return x.m_sym == y.m_sym; + } + }; + + class params : public config {}; + + /** Wrapper around an ast pointer */ + class ast_i : public object { + protected: + ::ast *_ast; + public: + ::ast * const &raw() const {return _ast;} + ast_i(context & c, ::ast *a = 0) : object(c) {_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 : public ast_i { + public: + operator ::ast*() const { return raw(); } + friend bool eq(ast const & a, ast const & b) { return a.raw() == b.raw(); } + + + ast(context &c, ::ast *a = 0) : ast_i(c,a) { + if(_ast) + m().inc_ref(a); + } + + ast() {} + + ast(const ast &other) : ast_i(other) { + if(_ast) + m().inc_ref(_ast); + } + + ast &operator=(const ast &other) { + if(_ast) + m().dec_ref(_ast); + _ast = other._ast; + m_ctx = other.m_ctx; + if(_ast) + m().inc_ref(_ast); + return *this; + } + + ~ast(){ + if(_ast) + m().dec_ref(_ast); + } + + void show() const; + + }; + + class sort : public ast { + public: + sort(context & c):ast(c) {} + sort(context & c, ::sort *s):ast(c, s) {} + sort(sort const & s):ast(s) {} + operator ::sort*() const { return to_sort(raw()); } + sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } + + bool is_bool() const { return m().is_bool(*this); } + bool is_int() const { return ctx().get_sort_kind(*this) == IntSort; } + bool is_real() const { return ctx().get_sort_kind(*this) == RealSort; } + bool is_arith() const; + bool is_array() const { return ctx().get_sort_kind(*this) == ArraySort; } + bool is_datatype() const; + bool is_relation() const; + bool is_finite_domain() const; + + + sort array_domain() const; + sort array_range() const; + }; + + + class func_decl : public ast { + public: + func_decl() : ast() {} + func_decl(context & c):ast(c) {} + func_decl(context & c, ::func_decl *n):ast(c, n) {} + func_decl(func_decl const & s):ast(s) {} + operator ::func_decl*() const { return to_func_decl(*this); } + func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } + + unsigned arity() const; + sort domain(unsigned i) const; + sort range() const; + symbol name() const {return symbol(ctx(),to_func_decl(raw())->get_name());} + decl_kind get_decl_kind() const; + + bool is_const() const { return arity() == 0; } + + expr operator()(unsigned n, expr const * args) const; + expr operator()(const std::vector &args) const; + expr operator()() const; + expr operator()(expr const & a) const; + expr operator()(int a) const; + expr operator()(expr const & a1, expr const & a2) const; + expr operator()(expr const & a1, int a2) const; + expr operator()(int a1, expr const & a2) const; + expr operator()(expr const & a1, expr const & a2, expr const & a3) const; + expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; + expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; + + func_decl get_func_decl_parameter(unsigned idx){ + return func_decl(ctx(),to_func_decl(to_func_decl(raw())->get_parameters()[idx].get_ast())); + } + + }; + + class expr : public ast { + public: + expr() : ast() {} + expr(context & c):ast(c) {} + expr(context & c, ::ast *n):ast(c, n) {} + expr(expr const & n):ast(n) {} + expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } + operator ::expr*() const { return to_expr(raw()); } + unsigned get_id() const {return to_expr(raw())->get_id();} + + sort get_sort() const { return sort(ctx(),m().get_sort(to_expr(raw()))); } + + bool is_bool() const { return get_sort().is_bool(); } + bool is_int() const { return get_sort().is_int(); } + bool is_real() const { return get_sort().is_real(); } + bool is_arith() const { return get_sort().is_arith(); } + bool is_array() const { return get_sort().is_array(); } + bool is_datatype() const { return get_sort().is_datatype(); } + bool is_relation() const { return get_sort().is_relation(); } + bool is_finite_domain() const { return get_sort().is_finite_domain(); } + bool is_true() const {return is_app() && decl().get_decl_kind() == True; } + + bool is_numeral() const { + return is_app() && decl().get_decl_kind() == OtherArith && m().is_unique_value(to_expr(raw())); + } + bool is_app() const {return raw()->get_kind() == AST_APP;} + bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} + bool is_var() const {return raw()->get_kind() == AST_VAR;} + bool is_label (bool &pos,std::vector &names) const ; + bool is_ground() const {return to_app(raw())->is_ground();} + bool has_quantifiers() const {return to_app(raw())->has_quantifiers();} + bool has_free(int idx) const { + used_vars proc; + proc.process(to_expr(raw())); + return proc.contains(idx); + } + unsigned get_max_var_idx_plus_1() const { + used_vars proc; + proc.process(to_expr(raw())); + return proc.get_max_found_var_idx_plus_1(); + } + + // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } + func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} + unsigned num_args() const { + ast_kind dk = raw()->get_kind(); + switch(dk){ + case AST_APP: + return to_app(raw())->get_num_args(); + case AST_QUANTIFIER: + return 1; + case AST_VAR: + return 0; + default:; + } + SASSERT(0); + return 0; + } + expr arg(unsigned i) const { + ast_kind dk = raw()->get_kind(); + switch(dk){ + case AST_APP: + return ctx().cook(to_app(raw())->get_arg(i)); + case AST_QUANTIFIER: + return ctx().cook(to_quantifier(raw())->get_expr()); + default:; + } + assert(0); + return expr(); + } + + expr body() const { + return ctx().cook(to_quantifier(raw())->get_expr()); + } + + friend expr operator!(expr const & a) { + // ::expr *e = a; + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_NOT,a)); + } + + friend expr operator&&(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_AND,a,b)); + } + + friend expr operator||(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_OR,a,b)); + } + + friend expr implies(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_IMPLIES,a,b)); + } + + friend expr operator==(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_EQ,a,b)); + } + + friend expr operator!=(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DISTINCT,a,b)); + } + + friend expr operator+(expr const & a, expr const & b) { + return a.ctx().make(Plus,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); + } + + friend expr operator*(expr const & a, expr const & b) { + return a.ctx().make(Times,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); + } + + friend expr operator/(expr const & a, expr const & b) { + return a.ctx().make(Div,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); + } + + friend expr operator-(expr const & a) { + return a.ctx().make(Uminus,a); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); + } + + friend expr operator-(expr const & a, expr const & b) { + return a.ctx().make(Sub,a,b); // expr(a.ctx(),a.m().mk_app(a.ctx().m_arith_fid,OP_SUB,a,b)); + } + + friend expr operator<=(expr const & a, expr const & b) { + return a.ctx().make(Leq,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); + } + + friend expr operator>=(expr const & a, expr const & b) { + return a.ctx().make(Geq,a,b); //expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); + } + + friend expr operator<(expr const & a, expr const & b) { + return a.ctx().make(Lt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); + } + + friend expr operator>(expr const & a, expr const & b) { + return a.ctx().make(Gt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); + } + + expr simplify() const; + + expr simplify(params const & p) const; + + expr qe_lite() const; + + expr qe_lite(const std::set &idxs, bool index_of_bound) const; + + friend expr clone_quantifier(const expr &, const expr &); + + friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); + + friend expr clone_quantifier(decl_kind, const expr &, const expr &); + + friend std::ostream & operator<<(std::ostream & out, expr const & m){ + m.ctx().print_expr(out,m); + return out; + } + + void get_patterns(std::vector &pats) const ; + + unsigned get_quantifier_num_bound() const { + return to_quantifier(raw())->get_num_decls(); + } + + unsigned get_index_value() const { + var* va = to_var(raw()); + return va->get_idx(); + } + + bool is_quantifier_forall() const { + return to_quantifier(raw())->is_forall(); + } + + sort get_quantifier_bound_sort(unsigned n) const { + return sort(ctx(),to_quantifier(raw())->get_decl_sort(n)); + } + + symbol get_quantifier_bound_name(unsigned n) const { + return symbol(ctx(),to_quantifier(raw())->get_decl_names()[n]); + } + + friend expr forall(const std::vector &quants, const expr &body); + + friend expr exists(const std::vector &quants, const expr &body); + + }; + + + typedef ::decl_kind pfrule; + + class proof : public ast { + public: + proof(context & c):ast(c) {} + proof(context & c, ::proof *s):ast(c, s) {} + proof(proof const & s):ast(s) {} + operator ::proof*() const { return to_app(raw()); } + proof & operator=(proof const & s) { return static_cast(ast::operator=(s)); } + + pfrule rule() const { + ::func_decl *d = to_app(raw())->get_decl(); + return d->get_decl_kind(); + } + + unsigned num_prems() const { + return to_app(raw())->get_num_args() - 1; + } + + expr conc() const { + return ctx().cook(to_app(raw())->get_arg(num_prems())); + } + + proof prem(unsigned i) const { + return proof(ctx(),to_app(to_app(raw())->get_arg(i))); + } + + void get_assumptions(std::vector &assumps); + }; + +#if 0 + +#if Z3_MAJOR_VERSION > 4 || Z3_MAJOR_VERSION == 4 && Z3_MINOR_VERSION >= 3 + template + class ast_vector_tpl : public object { + Z3_ast_vector m_vector; + void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } + public: + ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } + ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } + ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } + ~ast_vector_tpl() { /* Z3_ast_vector_dec_ref(ctx(), m_vector); */ } + operator Z3_ast_vector() const { return m_vector; } + unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } + T operator[](unsigned i) const { Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } + void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } + void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } + T back() const { return operator[](size() - 1); } + void pop_back() { assert(size() > 0); resize(size() - 1); } + bool empty() const { return size() == 0; } + ast_vector_tpl & operator=(ast_vector_tpl const & s) { + Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); + // Z3_ast_vector_dec_ref(ctx(), m_vector); + m_ctx = s.m_ctx; + m_vector = s.m_vector; + return *this; + } + friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } + }; + + typedef ast_vector_tpl ast_vector; + typedef ast_vector_tpl expr_vector; + typedef ast_vector_tpl sort_vector; + typedef ast_vector_tpl func_decl_vector; + +#endif + +#endif + + class func_interp : public object { + ::func_interp * m_interp; + void init(::func_interp * e) { + m_interp = e; + } + public: + func_interp(context & c, ::func_interp * e):object(c) { init(e); } + func_interp(func_interp const & s):object(s) { init(s.m_interp); } + ~func_interp() { } + operator ::func_interp *() const { return m_interp; } + func_interp & operator=(func_interp const & s) { + m_ctx = s.m_ctx; + m_interp = s.m_interp; + return *this; + } + unsigned num_entries() const { return m_interp->num_entries(); } + expr get_arg(unsigned ent, unsigned arg) const { + return expr(ctx(),m_interp->get_entry(ent)->get_arg(arg)); + } + expr get_value(unsigned ent) const { + return expr(ctx(),m_interp->get_entry(ent)->get_result()); + } + expr else_value() const { + return expr(ctx(),m_interp->get_else()); + } + }; + + + + class model : public object { + model_ref m_model; + void init(::model *m) { + m_model = m; + } + public: + model(context & c, ::model * m = 0):object(c), m_model(m) { } + model(model const & s):object(s), m_model(s.m_model) { } + ~model() { } + operator ::model *() const { return m_model.get(); } + model & operator=(model const & s) { + // ::model *_inc_ref(s.ctx(), s.m_model); + // ::model *_dec_ref(ctx(), m_model); + m_ctx = s.m_ctx; + m_model = s.m_model.get(); + return *this; + } + model & operator=(::model *s) { + m_model = s; + return *this; + } + bool null() const {return !m_model;} + + expr eval(expr const & n, bool model_completion=true) const { + ::model * _m = m_model.get(); + expr_ref result(ctx().m()); + _m->eval(n, result, model_completion); + return expr(ctx(), result); + } + + void show() const; + void show_hash() const; + + unsigned num_consts() const {return m_model.get()->get_num_constants();} + unsigned num_funcs() const {return m_model.get()->get_num_functions();} + func_decl get_const_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_constant(i));} + func_decl get_func_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_function(i));} + unsigned size() const; + func_decl operator[](unsigned i) const; + + expr get_const_interp(func_decl f) const { + return ctx().cook(m_model->get_const_interp(to_func_decl(f.raw()))); + } + + func_interp get_func_interp(func_decl f) const { + return func_interp(ctx(),m_model->get_func_interp(to_func_decl(f.raw()))); + } + +#if 0 + friend std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } +#endif + }; + +#if 0 + class stats : public object { + Z3_stats m_stats; + void init(Z3_stats e) { + m_stats = e; + Z3_stats_inc_ref(ctx(), m_stats); + } + public: + stats(context & c):object(c), m_stats(0) {} + stats(context & c, Z3_stats e):object(c) { init(e); } + stats(stats const & s):object(s) { init(s.m_stats); } + ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } + operator Z3_stats() const { return m_stats; } + stats & operator=(stats const & s) { + Z3_stats_inc_ref(s.ctx(), s.m_stats); + if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); + m_ctx = s.m_ctx; + m_stats = s.m_stats; + return *this; + } + unsigned size() const { return Z3_stats_size(ctx(), m_stats); } + std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } + bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } + bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } + unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } + double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } + friend std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } + }; +#endif + + enum check_result { + unsat, sat, unknown + }; + + class fixedpoint : public object { + + public: + void get_rules(std::vector &rules); + void get_assertions(std::vector &rules); + void register_relation(const func_decl &rela); + void add_rule(const expr &clause, const symbol &name); + void assert_cnst(const expr &cnst); + }; + + inline std::ostream & operator<<(std::ostream & out, check_result r) { + if (r == unsat) out << "unsat"; + else if (r == sat) out << "sat"; + else out << "unknown"; + return out; + } + + inline check_result to_check_result(lbool l) { + if (l == l_true) return sat; + else if (l == l_false) return unsat; + return unknown; + } + + class solver : public object { + protected: + ::solver *m_solver; + model the_model; + bool canceled; + proof_gen_mode m_mode; + bool extensional; + public: + solver(context & c, bool extensional = false, bool models = true); + solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} + solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} + ~solver() { + if(m_solver) + dealloc(m_solver); + } + operator ::solver*() const { return m_solver; } + solver & operator=(solver const & s) { + m_ctx = s.m_ctx; + m_solver = s.m_solver; + the_model = s.the_model; + m_mode = s.m_mode; + return *this; + } + struct cancel_exception {}; + void checkpoint(){ + if(canceled) + throw(cancel_exception()); + } + // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } + void push() { scoped_proof_mode spm(m(),m_mode); m_solver->push(); } + void pop(unsigned n = 1) { scoped_proof_mode spm(m(),m_mode); m_solver->pop(n); } + // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } + void add(expr const & e) { scoped_proof_mode spm(m(),m_mode); m_solver->assert_expr(e); } + check_result check() { + scoped_proof_mode spm(m(),m_mode); + checkpoint(); + lbool r = m_solver->check_sat(0,0); + model_ref m; + m_solver->get_model(m); + the_model = m.get(); + return to_check_result(r); + } + check_result check_keep_model(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { + scoped_proof_mode spm(m(),m_mode); + model old_model(the_model); + check_result res = check(n,assumptions,core_size,core); + if(the_model == 0) + the_model = old_model; + return res; + } + check_result check(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { + scoped_proof_mode spm(m(),m_mode); + checkpoint(); + std::vector< ::expr *> _assumptions(n); + for (unsigned i = 0; i < n; i++) { + _assumptions[i] = to_expr(assumptions[i]); + } + the_model = 0; + lbool r = m_solver->check_sat(n,&_assumptions[0]); + + if(core_size && core){ + ptr_vector< ::expr> _core; + m_solver->get_unsat_core(_core); + *core_size = _core.size(); + for(unsigned i = 0; i < *core_size; i++) + core[i] = expr(ctx(),_core[i]); + } + + model_ref m; + m_solver->get_model(m); + the_model = m.get(); + + return to_check_result(r); + } +#if 0 + check_result check(expr_vector assumptions) { + scoped_proof_mode spm(m(),m_mode); + unsigned n = assumptions.size(); + z3array _assumptions(n); + for (unsigned i = 0; i < n; i++) { + check_context(*this, assumptions[i]); + _assumptions[i] = assumptions[i]; + } + Z3_lbool r = Z3_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); + check_error(); + return to_check_result(r); + } +#endif + model get_model() const { return model(ctx(), the_model); } + // std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } + // stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } +#if 0 + expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } + expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } +#endif + // expr proof() const { Z3_ast r = Z3_solver_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } + // friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } + + int get_num_decisions(); + + void cancel(){ + scoped_proof_mode spm(m(),m_mode); + canceled = true; + if(m_solver) + m_solver->cancel(); + } + + unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} + + void show(); + void print(const char *filename); + void show_assertion_ids(); + + proof get_proof(){ + scoped_proof_mode spm(m(),m_mode); + return proof(ctx(),m_solver->get_proof()); + } + + bool extensional_array_theory() {return extensional;} + }; + +#if 0 + class goal : public object { + Z3_goal m_goal; + void init(Z3_goal s) { + m_goal = s; + Z3_goal_inc_ref(ctx(), s); + } + public: + goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } + goal(context & c, Z3_goal s):object(c) { init(s); } + goal(goal const & s):object(s) { init(s.m_goal); } + ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } + operator Z3_goal() const { return m_goal; } + goal & operator=(goal const & s) { + Z3_goal_inc_ref(s.ctx(), s.m_goal); + Z3_goal_dec_ref(ctx(), m_goal); + m_ctx = s.m_ctx; + m_goal = s.m_goal; + return *this; + } + void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } + unsigned size() const { return Z3_goal_size(ctx(), m_goal); } + expr operator[](unsigned i) const { Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } + Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } + bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } + unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } + void reset() { Z3_goal_reset(ctx(), m_goal); } + unsigned num_exprs() const { Z3_goal_num_exprs(ctx(), m_goal); } + bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } + bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } + friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } + }; + + class apply_result : public object { + Z3_apply_result m_apply_result; + void init(Z3_apply_result s) { + m_apply_result = s; + Z3_apply_result_inc_ref(ctx(), s); + } + public: + apply_result(context & c, Z3_apply_result s):object(c) { init(s); } + apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } + ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } + operator Z3_apply_result() const { return m_apply_result; } + apply_result & operator=(apply_result const & s) { + Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); + Z3_apply_result_dec_ref(ctx(), m_apply_result); + m_ctx = s.m_ctx; + m_apply_result = s.m_apply_result; + return *this; + } + unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } + goal operator[](unsigned i) const { Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } + goal operator[](int i) const { assert(i >= 0); return this->operator[](static_cast(i)); } + model convert_model(model const & m, unsigned i = 0) const { + check_context(*this, m); + Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); + check_error(); + return model(ctx(), new_m); + } + friend std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } + }; + + class tactic : public object { + Z3_tactic m_tactic; + void init(Z3_tactic s) { + m_tactic = s; + Z3_tactic_inc_ref(ctx(), s); + } + public: + tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } + tactic(context & c, Z3_tactic s):object(c) { init(s); } + tactic(tactic const & s):object(s) { init(s.m_tactic); } + ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } + operator Z3_tactic() const { return m_tactic; } + tactic & operator=(tactic const & s) { + Z3_tactic_inc_ref(s.ctx(), s.m_tactic); + Z3_tactic_dec_ref(ctx(), m_tactic); + m_ctx = s.m_ctx; + m_tactic = s.m_tactic; + return *this; + } + solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } + apply_result apply(goal const & g) const { + check_context(*this, g); + Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); + check_error(); + return apply_result(ctx(), r); + } + apply_result operator()(goal const & g) const { + return apply(g); + } + std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } + friend tactic operator&(tactic const & t1, tactic const & t2) { + check_context(t1, t2); + Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); + t1.check_error(); + return tactic(t1.ctx(), r); + } + friend tactic operator|(tactic const & t1, tactic const & t2) { + check_context(t1, t2); + Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); + t1.check_error(); + return tactic(t1.ctx(), r); + } + friend tactic repeat(tactic const & t, unsigned max=UINT_MAX) { + Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); + t.check_error(); + return tactic(t.ctx(), r); + } + friend tactic with(tactic const & t, params const & p) { + Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); + t.check_error(); + return tactic(t.ctx(), r); + } + friend tactic try_for(tactic const & t, unsigned ms) { + Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); + t.check_error(); + return tactic(t.ctx(), r); + } + }; + + class probe : public object { + Z3_probe m_probe; + void init(Z3_probe s) { + m_probe = s; + Z3_probe_inc_ref(ctx(), s); + } + public: + probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } + probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } + probe(context & c, Z3_probe s):object(c) { init(s); } + probe(probe const & s):object(s) { init(s.m_probe); } + ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } + operator Z3_probe() const { return m_probe; } + probe & operator=(probe const & s) { + Z3_probe_inc_ref(s.ctx(), s.m_probe); + Z3_probe_dec_ref(ctx(), m_probe); + m_ctx = s.m_ctx; + m_probe = s.m_probe; + return *this; + } + double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } + double operator()(goal const & g) const { return apply(g); } + friend probe operator<=(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } + friend probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } + friend probe operator>=(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } + friend probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } + friend probe operator<(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } + friend probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } + friend probe operator>(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } + friend probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } + friend probe operator==(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } + friend probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } + friend probe operator&&(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator||(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator!(probe const & p) { + Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); + } + }; + + inline tactic fail_if(probe const & p) { + Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); + p.check_error(); + return tactic(p.ctx(), r); + } + inline tactic when(probe const & p, tactic const & t) { + check_context(p, t); + Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); + t.check_error(); + return tactic(t.ctx(), r); + } + inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { + check_context(p, t1); check_context(p, t2); + Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); + t1.check_error(); + return tactic(t1.ctx(), r); + } + +#endif + + inline expr context::bool_val(bool b){return b ? make(True) : make(False);} + + inline symbol context::str_symbol(char const * s) { ::symbol r = ::symbol(s); return symbol(*this, r); } + inline symbol context::int_symbol(int n) { ::symbol r = ::symbol(n); return symbol(*this, r); } + + inline sort context::bool_sort() { + ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); + return sort(*this, s); + } + inline sort context::int_sort() { + ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); + return sort(*this, s); + } + inline sort context::real_sort() { + ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); + return sort(*this, s); + } + inline sort context::array_sort(sort d, sort r) { + parameter params[2] = { parameter(d), parameter(to_sort(r)) }; + ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); + return sort(*this, s); + } + + + inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { + 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 func_decl(*this,d); + } + + inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { + return function(str_symbol(name), arity, domain, range); + } + + inline func_decl context::function(char const * name, sort const & domain, sort const & range) { + sort args[1] = { domain }; + return function(name, 1, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { + sort args[2] = { d1, d2 }; + return function(name, 2, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { + sort args[3] = { d1, d2, d3 }; + return function(name, 3, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { + sort args[4] = { d1, d2, d3, d4 }; + return function(name, 4, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { + sort args[5] = { d1, d2, d3, d4, d5 }; + return function(name, 5, args, range); + } + + + inline expr context::constant(symbol const & name, sort const & s) { + ::expr *r = m().mk_const(m().mk_const_decl(name, s)); + return expr(*this, r); + } + inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } + inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } + inline expr context::int_const(char const * name) { return constant(name, int_sort()); } + inline expr context::real_const(char const * name) { return constant(name, real_sort()); } + inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } + + inline expr func_decl::operator()(const std::vector &args) const { + return operator()(args.size(),&args[0]); + } + inline expr func_decl::operator()() const { + return operator()(0,0); + } + inline expr func_decl::operator()(expr const & a) const { + return operator()(1,&a); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2) const { + expr args[2] = {a1,a2}; + return operator()(2,args); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { + expr args[3] = {a1,a2,a3}; + return operator()(3,args); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { + expr args[4] = {a1,a2,a3,a4}; + return operator()(4,args); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { + expr args[5] = {a1,a2,a3,a4,a5}; + return operator()(5,args); + } + + + inline expr select(expr const & a, expr const & i) { return a.ctx().make(Select,a,i); } + inline expr store(expr const & a, expr const & i, expr const & v) { return a.ctx().make(Store,a,i,v); } + + inline expr forall(const std::vector &quants, const expr &body){ + return body.ctx().make_quant(Forall,quants,body); + } + + inline expr exists(const std::vector &quants, const expr &body){ + return body.ctx().make_quant(Exists,quants,body); + } + + inline expr context::int_val(int n){ + :: sort *r = m().mk_sort(m_arith_fid, INT_SORT); + return cook(m_arith_util.mk_numeral(rational(n),r)); + } + + + class literals : public object { + }; + + class TermTree { + public: + + TermTree(expr _term){ + term = _term; + } + + TermTree(expr _term, const std::vector &_children){ + term = _term; + children = _children; + } + + inline expr getTerm(){return term;} + + inline std::vector &getTerms(){return terms;} + + inline std::vector &getChildren(){ + return children; + } + + inline int number(int from){ + for(unsigned i = 0; i < children.size(); i++) + from = children[i]->number(from); + num = from; + return from + 1; + } + + inline int getNumber(){ + return num; + } + + inline void setTerm(expr t){term = t;} + + inline void addTerm(expr t){terms.push_back(t);} + + inline void setChildren(const std::vector & _children){ + children = _children; + } + + inline void setNumber(int _num){ + num = _num; + } + + ~TermTree(){ + for(unsigned i = 0; i < children.size(); i++) + delete children[i]; + } + + private: + expr term; + std::vector terms; + std::vector children; + int num; + }; + + typedef context interpolating_context; + + class interpolating_solver : public solver { + public: + interpolating_solver(context &ctx, bool models = true) + : solver(ctx, true, models) + { + weak_mode = false; + } + + public: + lbool interpolate(const std::vector &assumptions, + std::vector &interpolants, + model &_model, + literals &lits, + bool incremental + ); + + lbool interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + literals &lits, + bool incremental + ); + + bool read_interpolation_problem(const std::string &file_name, + std::vector &assumptions, + std::vector &theory, + std::string &error_message + ); + + void write_interpolation_problem(const std::string &file_name, + const std::vector &assumptions, + const std::vector &theory + ); + + void AssertInterpolationAxiom(const expr &expr); + void RemoveInterpolationAxiom(const expr &expr); + + void SetWeakInterpolants(bool weak); + void SetPrintToFile(const std::string &file_name); + + const std::vector &GetInterpolationAxioms() {return theory;} + const char *profile(); + + private: + bool weak_mode; + std::string print_filename; + std::vector theory; + }; + + + inline expr context::cook(::expr *a) {return expr(*this,a);} + + inline std::vector context::cook(ptr_vector< ::expr> v) { + std::vector _v(v.size()); + for(unsigned i = 0; i < v.size(); i++) + _v[i] = cook(v[i]); + return _v; + } + + inline ::expr *context::uncook(const expr &a) { + m().inc_ref(a.raw()); + return to_expr(a.raw()); + } + + inline expr context::translate(const expr &e) { + ::expr *f = to_expr(e.raw()); + if(&e.ctx().m() != &m()) // same ast manager -> no translation + throw "ast manager mismatch"; + return cook(f); + } + + inline func_decl context::translate(const func_decl &e) { + ::func_decl *f = to_func_decl(e.raw()); + if(&e.ctx().m() != &m()) // same ast manager -> no translation + throw "ast manager mismatch"; + return func_decl(*this,f); + } + + typedef double clock_t; + clock_t current_time(); + inline void output_time(std::ostream &os, clock_t time){os << time;} + + template class uptr { + public: + X *ptr; + uptr(){ptr = 0;} + void set(X *_ptr){ + if(ptr) delete ptr; + ptr = _ptr; + } + X *get(){ return ptr;} + ~uptr(){ + if(ptr) delete ptr; + } + }; + +}; + +// to make Duality::ast hashable +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::ast &s) const { + return s.raw()->get_id(); + } + }; +} + + +// to make Duality::ast usable in ordered collections +namespace std { + template <> + class less { + public: + bool operator()(const Duality::ast &s, const Duality::ast &t) const { + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + +// to make Duality::ast usable in ordered collections +namespace std { + template <> + class less { + public: + bool operator()(const Duality::expr &s, const Duality::expr &t) const { + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + +// to make Duality::func_decl hashable +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::func_decl &s) const { + return s.raw()->get_id(); + } + }; +} + + +// to make Duality::func_decl usable in ordered collections +namespace std { + template <> + class less { + public: + bool operator()(const Duality::func_decl &s, const Duality::func_decl &t) const { + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + +#endif diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 6bf09bb85da..d82e721ae97 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -1,195 +1,195 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - iz3base.h - -Abstract: - - Base class for interpolators. Includes an AST manager and a scoping - object as bases. - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - ---*/ - -#ifndef IZ3BASE_H -#define IZ3BASE_H - -#include "iz3mgr.h" -#include "iz3scopes.h" - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(func_decl * const &s) const { - return (size_t) s; - } - }; -} - -/* Base class for interpolators. Includes an AST manager and a scoping - object as bases. */ - -class iz3base : public iz3mgr, public scopes { - - public: - - /** Get the range in which an expression occurs. This is the - smallest subtree containing all occurrences of the - expression. */ - range &ast_range(ast); - - /** Get the scope of an expression. This is the set of tree nodes in - which all of the expression's symbols are in scope. */ - range &ast_scope(ast); - - /** Get the range of a symbol. This is the smallest subtree containing - all occurrences of the symbol. */ - range &sym_range(symb); - - /** Is an expression local (in scope in some frame)? */ - - bool is_local(ast node){ - return !range_is_empty(ast_scope(node)); - } - - /** Simplify an expression */ - - ast simplify(ast); - - /** Constructor */ - - iz3base(ast_manager &_m_manager, - const std::vector &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(_m_manager), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other, - const std::vector &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(other), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other, - const std::vector > &_cnsts, - const std::vector &_parents, - const std::vector &_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 &itps, std::vector &theory); - - /** For convenience -- is this formula SAT? */ - bool is_sat(const std::vector &consts, ast &_proof); - - /** Interpolator for clauses, to be implemented */ - virtual void interpolate_clause(std::vector &lits, std::vector &itps){ - throw "no interpolator"; - } - - ast get_proof_check_assump(range &rng){ - std::vector cs(theory); - cs.push_back(cnsts[rng.hi]); - return make(And,cs); - } - - int frame_of_assertion(const ast &ass){ - stl_ext::hash_map::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 &_cnsts, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &theory, - std::vector &pos_map, - bool merge = false - ); - - protected: - std::vector cnsts; - std::vector theory; - - private: - - struct ranges { - range rng; - range scp; - bool scope_computed; - ranges(){scope_computed = false;} - }; - - stl_ext::hash_map sym_range_hash; - stl_ext::hash_map ast_ranges_hash; - stl_ext::hash_map simplify_memo; - stl_ext::hash_map 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 &_parts, const std::vector &_parents, const std::vector &_theory); - - void initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory); - - bool is_literal(ast n); - void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); - void gather_conjuncts(ast n, std::vector &conjuncts); - ast simplify_and(std::vector &conjuncts); - ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); - ast simplify_with_lit(ast n, ast lit); - void find_children(const stl_ext::hash_set &cnsts_set, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &conjuncts, - std::vector &children, - std::vector &pos_map, - bool merge - ); - bool weak; - -}; - - - -#endif +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3base.h + +Abstract: + + Base class for interpolators. Includes an AST manager and a scoping + object as bases. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#ifndef IZ3BASE_H +#define IZ3BASE_H + +#include "iz3mgr.h" +#include "iz3scopes.h" + +namespace hash_space { + template <> + class hash { + public: + size_t operator()(func_decl * const &s) const { + return (size_t) s; + } + }; +} + +/* Base class for interpolators. Includes an AST manager and a scoping + object as bases. */ + +class iz3base : public iz3mgr, public scopes { + + public: + + /** Get the range in which an expression occurs. This is the + smallest subtree containing all occurrences of the + expression. */ + range &ast_range(ast); + + /** Get the scope of an expression. This is the set of tree nodes in + which all of the expression's symbols are in scope. */ + range &ast_scope(ast); + + /** Get the range of a symbol. This is the smallest subtree containing + all occurrences of the symbol. */ + range &sym_range(symb); + + /** Is an expression local (in scope in some frame)? */ + + bool is_local(ast node){ + return !range_is_empty(ast_scope(node)); + } + + /** Simplify an expression */ + + ast simplify(ast); + + /** Constructor */ + + iz3base(ast_manager &_m_manager, + const std::vector &_cnsts, + const std::vector &_parents, + const std::vector &_theory) + : iz3mgr(_m_manager), scopes(_parents) { + initialize(_cnsts,_parents,_theory); + weak = false; + } + + iz3base(const iz3mgr& other, + const std::vector &_cnsts, + const std::vector &_parents, + const std::vector &_theory) + : iz3mgr(other), scopes(_parents) { + initialize(_cnsts,_parents,_theory); + weak = false; + } + + iz3base(const iz3mgr& other, + const std::vector > &_cnsts, + const std::vector &_parents, + const std::vector &_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 &itps, std::vector &theory); + + /** For convenience -- is this formula SAT? */ + bool is_sat(const std::vector &consts, ast &_proof); + + /** Interpolator for clauses, to be implemented */ + virtual void interpolate_clause(std::vector &lits, std::vector &itps){ + throw "no interpolator"; + } + + ast get_proof_check_assump(range &rng){ + std::vector cs(theory); + cs.push_back(cnsts[rng.hi]); + return make(And,cs); + } + + int frame_of_assertion(const ast &ass){ + stl_ext::hash_map::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 &_cnsts, + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &theory, + std::vector &pos_map, + bool merge = false + ); + + protected: + std::vector cnsts; + std::vector theory; + + private: + + struct ranges { + range rng; + range scp; + bool scope_computed; + ranges(){scope_computed = false;} + }; + + stl_ext::hash_map sym_range_hash; + stl_ext::hash_map ast_ranges_hash; + stl_ext::hash_map simplify_memo; + stl_ext::hash_map 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 &_parts, const std::vector &_parents, const std::vector &_theory); + + void initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory); + + bool is_literal(ast n); + void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); + void gather_conjuncts(ast n, std::vector &conjuncts); + ast simplify_and(std::vector &conjuncts); + ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); + ast simplify_with_lit(ast n, ast lit); + void find_children(const stl_ext::hash_set &cnsts_set, + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &conjuncts, + std::vector &children, + std::vector &pos_map, + bool merge + ); + bool weak; + +}; + + + +#endif diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index d1bfd9f7bea..7616b8664b0 100644 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -464,7 +464,9 @@ namespace hash_space { Value &operator[](const Key& key) { std::pair kvp(key,Value()); - return this->lookup(kvp,true)->val.second; + return + hashtable,Key,HashFun,proj1,EqFun>:: + lookup(kvp,true)->val.second; } }; diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 2a4c4bd85fb..5fe3338dcb4 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -814,6 +814,10 @@ class iz3proof_itp_impl : public iz3proof_itp { ast equa = sep_cond(arg(pf,0),cond); if(is_equivrel_chain(equa)){ ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove + if(!rewrites_from_to(equa,lhs,rhs)){ + lhs = arg(arg(neg_equality,0),0); // the equality proved is ambiguous, sadly + rhs = arg(arg(neg_equality,0),1); + } LitType lhst = get_term_type(lhs), rhst = get_term_type(rhs); if(lhst != LitMixed && rhst != LitMixed) return unmixed_eq2ineq(lhs, rhs, op(arg(neg_equality,0)), equa, cond); @@ -1671,9 +1675,20 @@ class iz3proof_itp_impl : public iz3proof_itp { return head; } - // split a rewrite chain into head and tail at last non-mixed term + bool has_mixed_summands(const ast &e){ + if(op(e) == Plus){ + int nargs = num_args(e); + for(int i = 0; i < nargs; i++) + if(has_mixed_summands(arg(e,i))) + return true; + return false; + } + return get_term_type(e) == LitMixed; + } + + // split a rewrite chain into head and tail at last sum with no mixed sumands ast get_right_movers(const ast &chain, const ast &rhs, ast &tail, ast &mid){ - if(is_true(chain) || get_term_type(rhs) != LitMixed){ + if(is_true(chain) || !has_mixed_summands(rhs)){ mid = rhs; tail = mk_true(); return chain; @@ -1686,11 +1701,11 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } - // split a rewrite chain into head and tail at first non-mixed term + // split a rewrite chain into head and tail at first sum with no mixed sumands ast get_left_movers(const ast &chain, const ast &lhs, ast &tail, ast &mid){ if(is_true(chain)){ mid = lhs; - if(get_term_type(lhs) != LitMixed){ + if(!has_mixed_summands(lhs)){ tail = mk_true(); return chain; } @@ -1790,10 +1805,21 @@ class iz3proof_itp_impl : public iz3proof_itp { } + bool rewrites_from_to(const ast &chain, const ast &lhs, const ast &rhs){ + if(is_true(chain)) + return lhs == rhs; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); + return rewrites_from_to(rest,lhs,mid); + } + + struct bad_ineq_inference {}; + ast chain_ineqs(opr comp_op, LitType t, const ast &chain, const ast &lhs, const ast &rhs){ if(is_true(chain)){ if(lhs != rhs) - throw "bad ineq inference"; + throw bad_ineq_inference(); return make(Leq,make_int(rational(0)),make_int(rational(0))); } ast last = chain_last(chain); @@ -2656,9 +2682,11 @@ class iz3proof_itp_impl : public iz3proof_itp { pf = make_refl(e); // proof that e = e prover::range erng = pv->ast_scope(e); +#if 0 if(!(erng.lo > erng.hi) && pv->ranges_intersect(pv->ast_scope(e),rng)){ return e; // this term occurs in range, so it's O.K. } +#endif hash_map::iterator it = localization_map.find(e); diff --git a/src/interp/iz3scopes.cpp b/src/interp/iz3scopes.cpp index 198ecffe359..389a64b6c5c 100755 --- a/src/interp/iz3scopes.cpp +++ b/src/interp/iz3scopes.cpp @@ -1,321 +1,321 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - iz3scopes.cpp - -Abstract: - - Calculations with scopes, for both sequence and tree interpolation. - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - ---*/ - -#include - -#include - -#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 { - public: - size_t operator()(const scopes::range_lo &p) const { - return p.lo + (size_t)p.next; - } - }; - } - - template <> inline - size_t stdext::hash_value(const scopes::range_lo& p) - { - std::hash h; - return h(p); - } - - namespace std { - template <> - class less { - 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 { - 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(const range_op& p) - { - std::hash h; - return h(p); - } - - namespace std { - template <> - class less { - 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 unique; - hash_map lub; - hash_map glb; - }; - - - scopes::range_lo *scopes::find_range_lo(int lo, range_lo *next){ - range_lo foo(lo,next); - std::pair baz(foo,(range_lo *)0); - std::pair::iterator,bool> bar = rt->unique.insert(baz); - if(bar.second) - bar.first->second = new range_lo(lo,next); - return bar.first->second; - //std::pair::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 foo(range_op(rng1,rng2,0),(range_lo *)0); - std::pair::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 cand(range_op(rng1,rng2,hi),(range_lo *)0); - std::pair::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 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 - - +/*++ +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 + +#include + +#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 { + public: + size_t operator()(const scopes::range_lo &p) const { + return p.lo + (size_t)p.next; + } + }; + } + + template <> inline + size_t stdext::hash_value(const scopes::range_lo& p) + { + std::hash h; + return h(p); + } + + namespace std { + template <> + class less { + 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 { + 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(const range_op& p) + { + std::hash h; + return h(p); + } + + namespace std { + template <> + class less { + 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 unique; + hash_map lub; + hash_map glb; + }; + + + scopes::range_lo *scopes::find_range_lo(int lo, range_lo *next){ + range_lo foo(lo,next); + std::pair baz(foo,(range_lo *)0); + std::pair::iterator,bool> bar = rt->unique.insert(baz); + if(bar.second) + bar.first->second = new range_lo(lo,next); + return bar.first->second; + //std::pair::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 foo(range_op(rng1,rng2,0),(range_lo *)0); + std::pair::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 cand(range_op(rng1,rng2,hi),(range_lo *)0); + std::pair::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 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 + + diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h index 54cf89aba18..cd7203eeb14 100755 --- a/src/interp/iz3scopes.h +++ b/src/interp/iz3scopes.h @@ -1,197 +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 -#include - -class scopes { - - public: - /** Construct from parents vector. */ - scopes(const std::vector &_parents){ - parents = _parents; - } - - scopes(){ - } - - void initialize(const std::vector &_parents){ - parents = _parents; - } - - /** The parents vector defining the tree structure */ - std::vector 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 +/*++ +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 +#include + +class scopes { + + public: + /** Construct from parents vector. */ + scopes(const std::vector &_parents){ + parents = _parents; + } + + scopes(){ + } + + void initialize(const std::vector &_parents){ + parents = _parents; + } + + /** The parents vector defining the tree structure */ + std::vector 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 diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 2c4a2272486..a7c2125e7b9 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -464,7 +464,7 @@ class iz3translation_full : public iz3translation { for(int i = 0; i < 2; i++){ // try the second equality both ways if(match_op(eq_ops_r[0],Select,sel_ops,2)) if(match_op(sel_ops[0],Store,sto_ops,3)) - if(match_op(eq_ops_r[1],Select,sel_ops2,2)) + if(match_op(eq_ops_r[1],Select,sel_ops2,2)) for(int j = 0; j < 2; j++){ // try the first equality both ways if(eq_ops_l[0] == sto_ops[1] && eq_ops_l[1] == sel_ops[1] @@ -482,8 +482,8 @@ class iz3translation_full : public iz3translation { // int frame = range_min(ast_scope(res)); TODO // antes.push_back(std::pair(res,frame)); return; - } - std::swap(eq_ops_l[0],eq_ops_l[1]); + } + std::swap(eq_ops_l[0],eq_ops_l[1]); } std::swap(eq_ops_r[0],eq_ops_r[1]); } diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index e5b8a5fcacf..9cef3a6864e 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -484,7 +484,7 @@ class iz3translation_direct : public iz3translation { for(int i = 0; i < 2; i++){ // try the second equality both ways if(match_op(eq_ops_r[0],Select,sel_ops,2)) if(match_op(sel_ops[0],Store,sto_ops,3)) - if(match_op(eq_ops_r[1],Select,sel_ops2,2)) + if(match_op(eq_ops_r[1],Select,sel_ops2,2)) for(int j = 0; j < 2; j++){ // try the first equality both ways if(eq_ops_l[0] == sto_ops[1] && eq_ops_l[1] == sel_ops[1] @@ -502,8 +502,8 @@ class iz3translation_direct : public iz3translation { int frame = range_min(ast_scope(res)); antes.push_back(std::pair(res,frame)); return; - } - std::swap(eq_ops_l[0],eq_ops_l[1]); + } + std::swap(eq_ops_l[0],eq_ops_l[1]); } std::swap(eq_ops_r[0],eq_ops_r[1]); } diff --git a/src/math/polynomial/upolynomial_factorization.cpp b/src/math/polynomial/upolynomial_factorization.cpp index 230d352f362..7d5a23d996c 100644 --- a/src/math/polynomial/upolynomial_factorization.cpp +++ b/src/math/polynomial/upolynomial_factorization.cpp @@ -530,7 +530,7 @@ bool check_hansel_lift(z_manager & upm, numeral_vector const & C, upm.mul(A_lifted.size(), A_lifted.c_ptr(), B_lifted.size(), B_lifted.c_ptr(), test1); upm.sub(C.size(), C.c_ptr(), test1.size(), test1.c_ptr(), test1); to_zp_manager(br_upm, test1); - if (!test1.size() == 0) { + if (test1.size() != 0) { TRACE("polynomial::factorization::bughunt", tout << "sage: R. = ZZ['x']" << endl; tout << "sage: A = "; upm.display(tout, A); tout << endl; diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp new file mode 100644 index 00000000000..147d874961a --- /dev/null +++ b/src/smt/theory_fpa.cpp @@ -0,0 +1,46 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + theory_fpa.cpp + +Abstract: + + Floating-Point Theory Plugin + +Author: + + Christoph (cwinter) 2014-04-23 + +Revision History: + +--*/ +#include"ast_smt2_pp.h" +#include"theory_fpa.h" + +namespace smt { + + bool theory_fpa::internalize_atom(app * atom, bool gate_ctx) { + TRACE("bv", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << "\n";); + SASSERT(atom->get_family_id() == get_family_id()); + NOT_IMPLEMENTED_YET(); + return true; + } + + void theory_fpa::new_eq_eh(theory_var, theory_var) { + NOT_IMPLEMENTED_YET(); + } + + void theory_fpa::new_diseq_eh(theory_var, theory_var) { + NOT_IMPLEMENTED_YET(); + } + + void theory_fpa::push_scope_eh() { + NOT_IMPLEMENTED_YET(); + } + + void theory_fpa::pop_scope_eh(unsigned num_scopes) { + NOT_IMPLEMENTED_YET(); + } +}; diff --git a/src/smt/theory_fpa.h b/src/smt/theory_fpa.h new file mode 100644 index 00000000000..3716247d398 --- /dev/null +++ b/src/smt/theory_fpa.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + theory_fpa.h + +Abstract: + + Floating-Point Theory Plugin + +Author: + + Christoph (cwinter) 2014-04-23 + +Revision History: + +--*/ +#ifndef _THEORY_FPA_H_ +#define _THEORY_FPA_H_ + +#include"smt_theory.h" +#include"fpa2bv_converter.h" + +namespace smt { + class theory_fpa : public theory { + fpa2bv_converter m_converter; + + virtual final_check_status final_check_eh() { return FC_DONE; } + virtual bool internalize_atom(app*, bool); + virtual bool internalize_term(app*) { return internalize_atom(0, false); } + virtual void new_eq_eh(theory_var, theory_var); + virtual void new_diseq_eh(theory_var, theory_var); + virtual void push_scope_eh(); + virtual void pop_scope_eh(unsigned num_scopes); + virtual theory* mk_fresh(context*) { return alloc(theory_fpa, get_manager()); } + virtual char const * get_name() const { return "fpa"; } + public: + theory_fpa(ast_manager& m) : theory(m.mk_family_id("fpa")), m_converter(m) {} + }; + +}; + +#endif /* _THEORY_FPA_H_ */ + diff --git a/src/tactic/fpa/fpa2bv_model_converter.cpp b/src/tactic/fpa/fpa2bv_model_converter.cpp new file mode 100644 index 00000000000..9b4b5a70f6a --- /dev/null +++ b/src/tactic/fpa/fpa2bv_model_converter.cpp @@ -0,0 +1,232 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + fpa2bv_model_converter.h + +Abstract: + + Model conversion for fpa2bv_converter + +Author: + + Christoph (cwinter) 2012-02-09 + +Notes: + +--*/ +#include"ast_smt2_pp.h" +#include"fpa2bv_model_converter.h" + +void fpa2bv_model_converter::display(std::ostream & out) { + out << "(fpa2bv-model-converter"; + for (obj_map::iterator it = m_const2bv.begin(); + it != m_const2bv.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_rm_const2bv.begin(); + it != m_rm_const2bv.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value.f_sgn, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_sig, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_exp, m, indent) << " ; " << + ")"; + } + out << ")" << std::endl; +} + +model_converter * fpa2bv_model_converter::translate(ast_translation & translator) { + fpa2bv_model_converter * res = alloc(fpa2bv_model_converter, translator.to()); + for (obj_map::iterator it = m_const2bv.begin(); + it != m_const2bv.end(); + it++) + { + func_decl * k = translator(it->m_key); + expr * v = translator(it->m_value); + res->m_const2bv.insert(k, v); + translator.to().inc_ref(k); + translator.to().inc_ref(v); + } + for (obj_map::iterator it = m_rm_const2bv.begin(); + it != m_rm_const2bv.end(); + it++) + { + func_decl * k = translator(it->m_key); + expr * v = translator(it->m_value); + res->m_rm_const2bv.insert(k, v); + translator.to().inc_ref(k); + translator.to().inc_ref(v); + } + return res; +} + +void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { + float_util fu(m); + bv_util bu(m); + mpf fp_val; + unsynch_mpz_manager & mpzm = fu.fm().mpz_manager(); + unsynch_mpq_manager & mpqm = fu.fm().mpq_manager(); + + TRACE("fpa2bv_mc", tout << "BV Model: " << std::endl; + for (unsigned i = 0; i < bv_mdl->get_num_constants(); i++) + tout << bv_mdl->get_constant(i)->get_name() << " --> " << + mk_ismt2_pp(bv_mdl->get_const_interp(bv_mdl->get_constant(i)), m) << std::endl; + ); + + obj_hashtable seen; + + for (obj_map::iterator it = m_const2bv.begin(); + it != m_const2bv.end(); + it++) + { + func_decl * var = it->m_key; + app * a = to_app(it->m_value); + SASSERT(fu.is_float(var->get_range())); + SASSERT(var->get_range()->get_num_parameters() == 2); + + unsigned ebits = fu.get_ebits(var->get_range()); + unsigned sbits = fu.get_sbits(var->get_range()); + + expr_ref sgn(m), sig(m), exp(m); + sgn = bv_mdl->get_const_interp(to_app(a->get_arg(0))->get_decl()); + sig = bv_mdl->get_const_interp(to_app(a->get_arg(1))->get_decl()); + exp = bv_mdl->get_const_interp(to_app(a->get_arg(2))->get_decl()); + + seen.insert(to_app(a->get_arg(0))->get_decl()); + seen.insert(to_app(a->get_arg(1))->get_decl()); + seen.insert(to_app(a->get_arg(2))->get_decl()); + + if (!sgn && !sig && !exp) + continue; + + unsigned sgn_sz = bu.get_bv_size(m.get_sort(a->get_arg(0))); + unsigned sig_sz = bu.get_bv_size(m.get_sort(a->get_arg(1))) - 1; + unsigned exp_sz = bu.get_bv_size(m.get_sort(a->get_arg(2))); + + rational sgn_q(0), sig_q(0), exp_q(0); + + if (sgn) bu.is_numeral(sgn, sgn_q, sgn_sz); + if (sig) bu.is_numeral(sig, sig_q, sig_sz); + if (exp) bu.is_numeral(exp, exp_q, exp_sz); + + // un-bias exponent + rational exp_unbiased_q; + exp_unbiased_q = exp_q - fu.fm().m_powers2.m1(ebits - 1); + + mpz sig_z; mpf_exp_t exp_z; + mpzm.set(sig_z, sig_q.to_mpq().numerator()); + exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator()); + + TRACE("fpa2bv_mc", tout << var->get_name() << " == [" << sgn_q.to_string() << " " << + mpzm.to_string(sig_z) << " " << exp_z << "(" << exp_q.to_string() << ")]" << std::endl;); + + fu.fm().set(fp_val, ebits, sbits, !mpqm.is_zero(sgn_q.to_mpq()), sig_z, exp_z); + + float_mdl->register_decl(var, fu.mk_value(fp_val)); + + mpzm.del(sig_z); + } + + for (obj_map::iterator it = m_rm_const2bv.begin(); + it != m_rm_const2bv.end(); + it++) + { + func_decl * var = it->m_key; + app * a = to_app(it->m_value); + SASSERT(fu.is_rm(var->get_range())); + rational val(0); + unsigned sz = 0; + if (a && bu.is_numeral(a, val, sz)) { + TRACE("fpa2bv_mc", tout << var->get_name() << " == " << val.to_string() << std::endl;); + SASSERT(val.is_uint64()); + switch (val.get_uint64()) + { + case BV_RM_TIES_TO_AWAY: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_away()); break; + case BV_RM_TIES_TO_EVEN: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_even()); break; + case BV_RM_TO_NEGATIVE: float_mdl->register_decl(var, fu.mk_round_toward_negative()); break; + case BV_RM_TO_POSITIVE: float_mdl->register_decl(var, fu.mk_round_toward_positive()); break; + case BV_RM_TO_ZERO: + default: float_mdl->register_decl(var, fu.mk_round_toward_zero()); + } + seen.insert(var); + } + } + + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) + seen.insert(it->m_value); + + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) + { + seen.insert(it->m_value.f_sgn); + seen.insert(it->m_value.f_sig); + seen.insert(it->m_value.f_exp); + } + + fu.fm().del(fp_val); + + // Keep all the non-float constants. + unsigned sz = bv_mdl->get_num_constants(); + for (unsigned i = 0; i < sz; i++) + { + func_decl * c = bv_mdl->get_constant(i); + if (!seen.contains(c)) + float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); + } + + // And keep everything else + sz = bv_mdl->get_num_functions(); + for (unsigned i = 0; i < sz; i++) + { + func_decl * f = bv_mdl->get_function(i); + if (!seen.contains(f)) + { + TRACE("fpa2bv_mc", tout << "Keeping: " << mk_ismt2_pp(f, m) << std::endl;); + func_interp * val = bv_mdl->get_func_interp(f); + float_mdl->register_decl(f, val); + } + } + + sz = bv_mdl->get_num_uninterpreted_sorts(); + for (unsigned i = 0; i < sz; i++) + { + sort * s = bv_mdl->get_uninterpreted_sort(i); + ptr_vector u = bv_mdl->get_universe(s); + float_mdl->register_usort(s, u.size(), u.c_ptr()); + } +} + +model_converter * mk_fpa2bv_model_converter(ast_manager & m, + obj_map const & const2bv, + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) { + return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv, uf2bvuf, uf23bvuf); +} diff --git a/src/tactic/fpa/fpa2bv_model_converter.h b/src/tactic/fpa/fpa2bv_model_converter.h new file mode 100644 index 00000000000..7b95987402d --- /dev/null +++ b/src/tactic/fpa/fpa2bv_model_converter.h @@ -0,0 +1,106 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + fpa2bv_model_converter.h + +Abstract: + + Model conversion for fpa2bv_converter + +Author: + + Christoph (cwinter) 2012-02-09 + +Notes: + +--*/ +#ifndef _FPA2BV_MODEL_CONVERTER_H_ +#define _FPA2BV_MODEL_CONVERTER_H_ + +#include"fpa2bv_converter.h" +#include"model_converter.h" + +class fpa2bv_model_converter : public model_converter { + ast_manager & m; + obj_map m_const2bv; + obj_map m_rm_const2bv; + obj_map m_uf2bvuf; + obj_map m_uf23bvuf; + +public: + fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) : + m(m) { + // Just create a copy? + for (obj_map::iterator it = const2bv.begin(); + it != const2bv.end(); + it++) + { + m_const2bv.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = rm_const2bv.begin(); + it != rm_const2bv.end(); + it++) + { + m_rm_const2bv.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = uf2bvuf.begin(); + it != uf2bvuf.end(); + it++) + { + m_uf2bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = uf23bvuf.begin(); + it != uf23bvuf.end(); + it++) + { + m_uf23bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + } + } + + virtual ~fpa2bv_model_converter() { + dec_ref_map_key_values(m, m_const2bv); + dec_ref_map_key_values(m, m_rm_const2bv); + } + + virtual void operator()(model_ref & md, unsigned goal_idx) { + SASSERT(goal_idx == 0); + model * new_model = alloc(model, m); + obj_hashtable bits; + convert(md.get(), new_model); + md = new_model; + } + + virtual void operator()(model_ref & md) { + operator()(md, 0); + } + + void display(std::ostream & out); + + virtual model_converter * translate(ast_translation & translator); + +protected: + fpa2bv_model_converter(ast_manager & m) : m(m) { } + + void convert(model * bv_mdl, model * float_mdl); +}; + + +model_converter * mk_fpa2bv_model_converter(ast_manager & m, + obj_map const & const2bv, + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf); + +#endif \ No newline at end of file diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index 9bb35eea691..3a6b7ea456c 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -20,6 +20,7 @@ Module Name: #include"fpa2bv_rewriter.h" #include"simplify_tactic.h" #include"fpa2bv_tactic.h" +#include"fpa2bv_model_converter.h" class fpa2bv_tactic : public tactic { struct imp {