diff --git a/cmake/target_arch_detect.cpp b/cmake/target_arch_detect.cpp index 8053e3532..0a2d0f3e6 100644 --- a/cmake/target_arch_detect.cpp +++ b/cmake/target_arch_detect.cpp @@ -5,6 +5,8 @@ #error CMAKE_TARGET_ARCH_i686 #elif defined(__x86_64__) || defined(_M_X64) #error CMAKE_TARGET_ARCH_x86_64 +#elif defined(__ARM_ARCH) +#error CMAKE_TARGET_ARCH_arm #else #error CMAKE_TARGET_ARCH_unknown #endif diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index bd705456f..150efd545 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -1868,7 +1868,7 @@ class JavaExample } } else { - System.out.println("BUG, the constraints are satisfiable."); + System.out.println("BUG, the constraints are not satisfiable."); } } diff --git a/examples/python/efsmt.py b/examples/python/efsmt.py index 53c83c02e..e519ed8c0 100644 --- a/examples/python/efsmt.py +++ b/examples/python/efsmt.py @@ -5,43 +5,34 @@ from z3.z3util import get_vars Modified from the example in pysmt https://github.com/pysmt/pysmt/blob/97088bf3b0d64137c3099ef79a4e153b10ccfda7/examples/efsmt.py ''' -def efsmt(y, phi, maxloops=None): - """Solving exists x. forall y. phi(x, y)""" - vars = get_vars(phi) - x = [item for item in vars if item not in y] - esolver = Solver() - fsolver = Solver() - esolver.add(BoolVal(True)) + +def efsmt(ys, phi, maxloops = None): + """Solving exists xs. forall ys. phi(x, y)""" + xs = [x for x in get_vars(phi) if x not in ys] + E = Solver() + F = Solver() + E.add(BoolVal(True)) loops = 0 while maxloops is None or loops <= maxloops: loops += 1 - eres = esolver.check() - if eres == unsat: - return unsat - else: - emodel = esolver.model() - tau = [emodel.eval(var, True) for var in x] - sub_phi = phi - for i in range(len(x)): - sub_phi = simplify(substitute(sub_phi, (x[i], tau[i]))) - fsolver.add(Not(sub_phi)) - if fsolver.check() == sat: - fmodel = fsolver.model() - sigma = [fmodel.eval(v, True) for v in y] - sub_phi = phi - for j in range(len(y)): - sub_phi = simplify(substitute(sub_phi, (y[j], sigma[j]))) - esolver.add(sub_phi) + eres = E.check() + if eres == sat: + emodel = E.model() + sub_phi = substitute(phi, [(x, emodel.eval(x, True)) for x in xs]) + F.push() + F.add(Not(sub_phi)) + fres = F.check() + if fres == sat: + fmodel = F.model() + sub_phi = substitute(phi, [(y, fmodel.eval(y, True)) for y in ys]) + E.add(sub_phi) else: - return sat + return fres, [(x, emodel.eval(x, True)) for x in xs] + F.pop() + else: + return eres return unknown - -def test(): - x, y, z = Reals("x y z") - fmla = Implies(And(y > 0, y < 10), y - 2 * x < 7) - fmlb = And(y > 3, x == 1) - print(efsmt([y], fmla)) - print(efsmt([y], fmlb)) - -test() +x, y, z = Reals("x y z") +print(efsmt([y], Implies(And(y > 0, y < 10), y - 2 * x < 7))) +print(efsmt([y], And(y > 3, x == 1))) diff --git a/examples/python/visitor.py b/examples/python/visitor.py index 504e2acc8..78ec824fb 100644 --- a/examples/python/visitor.py +++ b/examples/python/visitor.py @@ -17,13 +17,71 @@ def visitor(e, seen): yield e return -x, y = Ints('x y') -fml = x + x + y > 2 -seen = {} -for e in visitor(fml, seen): - if is_const(e) and e.decl().kind() == Z3_OP_UNINTERPRETED: - print("Variable", e) - else: - print(e) - +def modify(e, fn): + seen = {} + def visit(e): + if e in seen: + pass + elif fn(e) is not None: + seen[e] = fn(e) + elif is_and(e): + chs = [visit(ch) for ch in e.children()] + seen[e] = And(chs) + elif is_or(e): + chs = [visit(ch) for ch in e.children()] + seen[e] = Or(chs) + elif is_app(e): + chs = [visit(ch) for ch in e.children()] + seen[e] = e.decl()(chs) + elif is_quantifier(e): + # Note: does not work for Lambda that requires a separate case + body = visit(e.body()) + is_forall = e.is_forall() + num_pats = e.num_patterns() + pats = (Pattern * num_pats)() + for i in range(num_pats): + pats[i] = e.pattern(i).ast + + num_decls = e.num_vars() + sorts = (Sort * num_decls)() + names = (Symbol * num_decls)() + for i in range(num_decls): + sorts[i] = e.var_sort(i).ast + names[i] = to_symbol(e.var_name(i), e.ctx) + r = QuantifierRef(Z3_mk_quantifier(e.ctx_ref(), is_forall, e.weight(), num_pats, pats, num_decls, sorts, names, body.ast), e.ctx) + seen[e] = r + else: + seen[e] = e + return seen[e] + return visit(e) + +if __name__ == "__main__": + x, y = Ints('x y') + fml = x + x + y > 2 + seen = {} + for e in visitor(fml, seen): + if is_const(e) and e.decl().kind() == Z3_OP_UNINTERPRETED: + print("Variable", e) + else: + print(e) + + s = SolverFor("HORN") + inv = Function('inv', IntSort(), IntSort(), BoolSort()) + i, ip, j, jp = Ints('i ip j jp') + s.add(ForAll([i, j], Implies(i == 0, inv(i, j)))) + s.add(ForAll([i, ip, j, jp], Implies(And(inv(i, j), i < 10, ip == i + 1), inv(ip, jp)))) + s.add(ForAll([i, j], Implies(And(inv(i, j), i >= 10), i == 10))) + + a0, a1, a2 = Ints('a0 a1 a2') + b0, b1, b2 = Ints('b0 b1 b2') + x = Var(0, IntSort()) + y = Var(1, IntSort()) + template = And(a0 + a1*x + a2*y >= 0, b0 + b1*x + b2*y >= 0) + def update(e): + if is_app(e) and eq(e.decl(), inv): + return substitute_vars(template, (e.arg(0)), e.arg(1)) + return None + for f in s.assertions(): + f_new = modify(f, update) + print(f_new) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index ade4fe92f..20ebb0137 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -4,7 +4,7 @@ variables: Minor: '8' Patch: '16' NightlyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId)-$(Build.DefinitionName) - MacFlags: 'CXXFLAGS="-arch arm64 -arch x86_64" LINK_EXTRA_FLAGS="-arch arm64 -arch x86_64" SLINK_EXTRA_FLAGS="-arch arm64 -arch x86_64" FPMATH_ENABLED=False' + MacFlags: 'CXXFLAGS="-arch x86_64" LINK_EXTRA_FLAGS="-arch x86_64" SLINK_EXTRA_FLAGS="-arch x86_64"' stages: - stage: Build @@ -24,6 +24,7 @@ stages: artifactName: 'Mac' targetPath: $(Build.ArtifactStagingDirectory) + - job: Ubuntu displayName: "Ubuntu build" pool: @@ -193,6 +194,7 @@ stages: patchVersion: $(Patch) arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.sym.nuspec -Version $(NightlyVersion) -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' - task: EsrpCodeSigning@1 + continueOnError: true displayName: 'Sign Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' @@ -220,6 +222,7 @@ stages: MaxConcurrency: '50' MaxRetryAttempts: '5' - task: EsrpCodeSigning@1 + continueOnError: true displayName: 'Sign Symbol Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' @@ -296,6 +299,7 @@ stages: patchVersion: $(Patch) arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.x86.sym.nuspec -Version $(NightlyVersion) -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' - task: EsrpCodeSigning@1 + continueOnError: true displayName: 'Sign Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' @@ -323,6 +327,7 @@ stages: MaxConcurrency: '50' MaxRetryAttempts: '5' - task: EsrpCodeSigning@1 + continueOnError: true displayName: 'Sign Symbol Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' @@ -392,6 +397,7 @@ stages: artifactName: 'Python packages' targetPath: src/api/python/dist + - stage: Deployment jobs: - job: Deploy diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index b00a33ad7..4d60de433 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -2333,7 +2333,7 @@ namespace z3 { inline expr pble(expr_vector const& es, int const* coeffs, int bound) { assert(es.size() > 0); - context& ctx = es[0].ctx(); + context& ctx = es[0u].ctx(); array _es(es); Z3_ast r = Z3_mk_pble(ctx, _es.size(), _es.ptr(), coeffs, bound); ctx.check_error(); @@ -2341,7 +2341,7 @@ namespace z3 { } inline expr pbge(expr_vector const& es, int const* coeffs, int bound) { assert(es.size() > 0); - context& ctx = es[0].ctx(); + context& ctx = es[0u].ctx(); array _es(es); Z3_ast r = Z3_mk_pbge(ctx, _es.size(), _es.ptr(), coeffs, bound); ctx.check_error(); @@ -2349,7 +2349,7 @@ namespace z3 { } inline expr pbeq(expr_vector const& es, int const* coeffs, int bound) { assert(es.size() > 0); - context& ctx = es[0].ctx(); + context& ctx = es[0u].ctx(); array _es(es); Z3_ast r = Z3_mk_pbeq(ctx, _es.size(), _es.ptr(), coeffs, bound); ctx.check_error(); @@ -2357,7 +2357,7 @@ namespace z3 { } inline expr atmost(expr_vector const& es, unsigned bound) { assert(es.size() > 0); - context& ctx = es[0].ctx(); + context& ctx = es[0u].ctx(); array _es(es); Z3_ast r = Z3_mk_atmost(ctx, _es.size(), _es.ptr(), bound); ctx.check_error(); @@ -2365,7 +2365,7 @@ namespace z3 { } inline expr atleast(expr_vector const& es, unsigned bound) { assert(es.size() > 0); - context& ctx = es[0].ctx(); + context& ctx = es[0u].ctx(); array _es(es); Z3_ast r = Z3_mk_atleast(ctx, _es.size(), _es.ptr(), bound); ctx.check_error(); @@ -2373,7 +2373,7 @@ namespace z3 { } inline expr sum(expr_vector const& args) { assert(args.size() > 0); - context& ctx = args[0].ctx(); + context& ctx = args[0u].ctx(); array _args(args); Z3_ast r = Z3_mk_add(ctx, _args.size(), _args.ptr()); ctx.check_error(); @@ -2382,7 +2382,7 @@ namespace z3 { inline expr distinct(expr_vector const& args) { assert(args.size() > 0); - context& ctx = args[0].ctx(); + context& ctx = args[0u].ctx(); array _args(args); Z3_ast r = Z3_mk_distinct(ctx, _args.size(), _args.ptr()); ctx.check_error(); @@ -2411,14 +2411,14 @@ namespace z3 { Z3_ast r; assert(args.size() > 0); if (args.size() == 1) { - return args[0]; + return args[0u]; } - context& ctx = args[0].ctx(); + context& ctx = args[0u].ctx(); array _args(args); - if (Z3_is_seq_sort(ctx, args[0].get_sort())) { + if (Z3_is_seq_sort(ctx, args[0u].get_sort())) { r = Z3_mk_seq_concat(ctx, _args.size(), _args.ptr()); } - else if (Z3_is_re_sort(ctx, args[0].get_sort())) { + else if (Z3_is_re_sort(ctx, args[0u].get_sort())) { r = Z3_mk_re_concat(ctx, _args.size(), _args.ptr()); } else { @@ -2448,7 +2448,7 @@ namespace z3 { inline expr mk_xor(expr_vector const& args) { if (args.empty()) return args.ctx().bool_val(false); - expr r = args[0]; + expr r = args[0u]; for (unsigned i = 1; i < args.size(); ++i) r = r ^ args[i]; return r; @@ -2771,7 +2771,7 @@ namespace z3 { assert(!m_end && !m_empty); m_cube = m_solver.cube(m_vars, m_cutoff); m_cutoff = 0xFFFFFFFF; - if (m_cube.size() == 1 && m_cube[0].is_false()) { + if (m_cube.size() == 1 && m_cube[0u].is_false()) { m_cube = z3::expr_vector(m_solver.ctx()); m_end = true; } @@ -3005,7 +3005,7 @@ namespace z3 { } array buffer(n); for (unsigned i = 0; i < n; ++i) buffer[i] = tactics[i]; - return tactic(tactics[0].ctx(), Z3_tactic_par_or(tactics[0].ctx(), n, buffer.ptr())); + return tactic(tactics[0u].ctx(), Z3_tactic_par_or(tactics[0u].ctx(), n, buffer.ptr())); } inline tactic par_and_then(tactic const & t1, tactic const & t2) { @@ -3804,7 +3804,7 @@ namespace z3 { } inline expr re_intersect(expr_vector const& args) { assert(args.size() > 0); - context& ctx = args[0].ctx(); + context& ctx = args[0u].ctx(); array _args(args); Z3_ast r = Z3_mk_re_intersect(ctx, _args.size(), _args.ptr()); ctx.check_error(); diff --git a/src/api/dotnet/NativeFuncInterp.cs b/src/api/dotnet/NativeFuncInterp.cs index 86bb27c55..0f446fa6f 100644 --- a/src/api/dotnet/NativeFuncInterp.cs +++ b/src/api/dotnet/NativeFuncInterp.cs @@ -85,13 +85,14 @@ namespace Microsoft.Z3 for (uint j = 0; j < numEntries; ++j) { - var entry = Native.Z3_func_interp_get_entry(nCtx, fi, j); - Native.Z3_func_entry_inc_ref(nCtx, entry); + var ntvEntry = Native.Z3_func_interp_get_entry(nCtx, fi, j); + Entries[j] = new Entry(); + Native.Z3_func_entry_inc_ref(nCtx, ntvEntry); Entries[j].Arguments = new Z3_ast[numArgs]; for (uint i = 0; i < numArgs; ++i) - Entries[j].Arguments[i] = Native.Z3_func_entry_get_arg(nCtx, entry, i); - Entries[j].Result = Native.Z3_func_entry_get_value(nCtx, entry); - Native.Z3_func_entry_dec_ref(nCtx, entry); + Entries[j].Arguments[i] = Native.Z3_func_entry_get_arg(nCtx, ntvEntry, i); + Entries[j].Result = Native.Z3_func_entry_get_value(nCtx, ntvEntry); + Native.Z3_func_entry_dec_ref(nCtx, ntvEntry); } Native.Z3_func_interp_dec_ref(nCtx, fi); diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index d7ba4d604..073885c21 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -37,6 +37,7 @@ void bv_rewriter::updt_local_params(params_ref const & _p) { m_extract_prop = p.bv_extract_prop(); m_ite2id = p.bv_ite2id(); m_le_extra = p.bv_le_extra(); + m_le2extract = p.bv_le2extract(); set_sort_sums(p.bv_sort_ac()); } @@ -577,7 +578,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref result = m().mk_eq(a, m_util.mk_numeral(numeral(0), bv_sz)); return BR_REWRITE1; } - else if (first_non_zero < bv_sz - 1) { + else if (first_non_zero < bv_sz - 1 && m_le2extract) { result = m().mk_and(m().mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), m_util.mk_numeral(numeral(0), bv_sz - first_non_zero - 1)), m_util.mk_ule(m_mk_extract(first_non_zero, 0, a), m_mk_extract(first_non_zero, 0, b))); return BR_REWRITE3; diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 7b734dca1..7f0c67540 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -62,6 +62,7 @@ class bv_rewriter : public poly_rewriter { bool m_extract_prop; bool m_bvnot_simpl; bool m_le_extra; + bool m_le2extract; bool is_zero_bit(expr * x, unsigned idx); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 968dcacd8..89efc439f 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1815,6 +1815,9 @@ void cmd_context::display_model(model_ref& mdl) { } void cmd_context::add_declared_functions(model& mdl) { + model_params p; + if (!p.user_functions()) + return; for (auto const& kv : m_func_decls) { func_decl* f = kv.m_value.first(); if (f->get_family_id() == null_family_id && !mdl.has_interpretation(f)) { diff --git a/src/math/lp/lp_dual_simplex_def.h b/src/math/lp/lp_dual_simplex_def.h index 34079cd94..c21429c88 100644 --- a/src/math/lp/lp_dual_simplex_def.h +++ b/src/math/lp/lp_dual_simplex_def.h @@ -217,14 +217,14 @@ template void lp_dual_simplex::fill_costs_bounds_ m_can_enter_basis[j] = true; this->set_scaled_cost(j); this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] =numeric_traits::one(); + this->m_upper_bounds[j] = numeric_traits::one(); break; } case column_type::free_column: { m_can_enter_basis[j] = true; this->set_scaled_cost(j); - this->m_upper_bounds[j] = free_bound; - this->m_lower_bounds[j] = -free_bound; + this->m_upper_bounds[j] = free_bound; + this->m_lower_bounds[j] = -free_bound; break; } case column_type::boxed: diff --git a/src/model/datatype_factory.cpp b/src/model/datatype_factory.cpp index b77512cb8..e58812a1f 100644 --- a/src/model/datatype_factory.cpp +++ b/src/model/datatype_factory.cpp @@ -48,10 +48,8 @@ expr * datatype_factory::get_some_value(sort * s) { */ expr * datatype_factory::get_last_fresh_value(sort * s) { expr * val = nullptr; - if (m_last_fresh_value.find(s, val)) { - TRACE("datatype", tout << "cached fresh value: " << mk_pp(val, m_manager) << "\n";); + if (m_last_fresh_value.find(s, val)) return val; - } value_set * set = get_value_set(s); if (set->empty()) val = get_some_value(s); @@ -200,7 +198,7 @@ expr * datatype_factory::get_fresh_value(sort * s) { if (m_util.is_recursive(s)) { while (true) { ++num_iterations; - TRACE("datatype", tout << mk_pp(get_last_fresh_value(s), m_manager) << "\n";); + TRACE("datatype", tout << num_iterations << " " << mk_pp(get_last_fresh_value(s), m_manager) << "\n";); ptr_vector const & constructors = *m_util.get_datatype_constructors(s); for (func_decl * constructor : constructors) { expr_ref_vector args(m_manager); @@ -219,7 +217,7 @@ expr * datatype_factory::get_fresh_value(sort * s) { expr * maybe_new_arg = nullptr; if (!m_util.is_datatype(s_arg)) maybe_new_arg = m_model.get_fresh_value(s_arg); - else if (num_iterations <= 1) + else if (num_iterations <= 1 || m_util.is_recursive(s_arg)) maybe_new_arg = get_almost_fresh_value(s_arg); else maybe_new_arg = get_fresh_value(s_arg); diff --git a/src/model/model_params.pyg b/src/model/model_params.pyg index 7e370cb3a..59899644e 100644 --- a/src/model/model_params.pyg +++ b/src/model/model_params.pyg @@ -5,6 +5,7 @@ def_module_params('model', ('v2', BOOL, False, 'use Z3 version 2.x (x <= 16) pretty printer'), ('compact', BOOL, True, 'try to compact function graph (i.e., function interpretations that are lookup tables)'), ('inline_def', BOOL, False, 'inline local function definitions ignoring possible expansion'), + ('user_functions', BOOL, True, 'include user defined functions in model'), ('completion', BOOL, False, 'enable/disable model completion'), )) diff --git a/src/params/bv_rewriter_params.pyg b/src/params/bv_rewriter_params.pyg index b439f2924..04c582485 100644 --- a/src/params/bv_rewriter_params.pyg +++ b/src/params/bv_rewriter_params.pyg @@ -11,5 +11,6 @@ def_module_params(module_name='rewriter', ("bv_extract_prop", BOOL, False, "attempt to partially propagate extraction inwards"), ("bv_not_simpl", BOOL, False, "apply simplifications for bvnot"), ("bv_ite2id", BOOL, False, "rewrite ite that can be simplified to identity"), - ("bv_le_extra", BOOL, False, "additional bu_(u/s)le simplifications") + ("bv_le_extra", BOOL, False, "additional bu_(u/s)le simplifications"), + ("bv_le2extract", BOOL, True, "disassemble bvule to extract") )) diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index da0ea8cf7..509b73835 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -1484,13 +1484,11 @@ namespace qe { tout << "free: " << m_free_vars << "\n";); free_vars.append(m_free_vars); - if (!m_free_vars.empty() || m_solver.inconsistent()) { - if (m_fml.get() != m_subfml.get()) { - scoped_ptr rp = mk_default_expr_replacer(m, false); - rp->apply_substitution(to_app(m_subfml.get()), fml, m_fml); - fml = m_fml; - } + if (m_fml.get() != m_subfml.get()) { + scoped_ptr rp = mk_default_expr_replacer(m, false); + rp->apply_substitution(to_app(m_subfml.get()), fml, m_fml); + fml = m_fml; } reset(); m_solver.pop(1); diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index a9910852e..b3521452b 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -652,6 +652,7 @@ namespace sat { inline void simplifier::propagate_unit(literal l) { unsigned old_trail_sz = s.m_trail.size(); + unsigned num_clauses = s.m_clauses.size(); s.assign_scoped(l); s.propagate_core(false); // must not use propagate(), since s.m_clauses is not in a consistent state. if (s.inconsistent()) @@ -672,6 +673,8 @@ namespace sat { } cs.reset(); } + for (unsigned i = num_clauses; i < s.m_clauses.size(); ++i) + m_use_list.insert(*s.m_clauses[i]); } void simplifier::elim_lit(clause & c, literal l) { @@ -1806,6 +1809,8 @@ namespace sat { */ bool simplifier::resolve(clause_wrapper const & c1, clause_wrapper const & c2, literal l, literal_vector & r) { CTRACE("resolve_bug", !c1.contains(l), tout << c1 << "\n" << c2 << "\nl: " << l << "\n";); + if (m_visited.size() <= 2*s.num_vars()) + m_visited.resize(2*s.num_vars(), false); SASSERT(c1.contains(l)); SASSERT(c2.contains(~l)); bool res = true; @@ -1825,6 +1830,10 @@ namespace sat { literal l2 = c2[i]; if (not_l == l2) continue; + if ((~l2).index() >= m_visited.size()) { + s.display(std::cout << l2 << " " << s.num_vars() << " " << m_visited.size() << "\n"); + exit(0); + } if (m_visited[(~l2).index()]) { res = false; break; diff --git a/src/sat/smt/array_model.cpp b/src/sat/smt/array_model.cpp index 1f1066121..c437629f5 100644 --- a/src/sat/smt/array_model.cpp +++ b/src/sat/smt/array_model.cpp @@ -24,27 +24,32 @@ namespace array { void solver::init_model() { collect_defaults(); + collect_selects(); + } + + void solver::finalize_model(model& mdl) { + std::for_each(m_selects_range.begin(), m_selects_range.end(), delete_proc()); } - bool solver::add_dep(euf::enode* n, top_sort& dep) { + bool solver::add_dep(euf::enode* n, top_sort& dep) { if (!a.is_array(n->get_expr())) { dep.insert(n, nullptr); return true; - } - for (euf::enode* p : euf::enode_parents(n->get_root())) { - if (a.is_default(p->get_expr())) { + } + if (a.is_array(n->get_expr())) { + for (euf::enode* p : euf::enode_parents(n->get_root())) + if (a.is_default(p->get_expr())) + dep.add(n, p); + + for (euf::enode* p : *get_select_set(n)) { dep.add(n, p); - continue; + for (unsigned i = 1; i < p->num_args(); ++i) + dep.add(n, p->get_arg(i)); } - if (!a.is_select(p->get_expr())) - continue; - dep.add(n, p); - for (unsigned i = 1; i < p->num_args(); ++i) - dep.add(n, p->get_arg(i)); } for (euf::enode* k : euf::enode_class(n)) if (a.is_const(k->get_expr())) - dep.add(n, k->get_arg(0)); + dep.add(n, k->get_arg(0)); theory_var v = get_th_var(n); euf::enode* d = get_default(v); if (d) @@ -103,17 +108,27 @@ namespace array { if (!get_else(v) && fi->get_else()) set_else(v, fi->get_else()); - - for (euf::enode* p : euf::enode_parents(n)) { - if (a.is_select(p->get_expr()) && p->get_arg(0)->get_root() == n) { - expr* value = values.get(p->get_root_id(), nullptr); - if (!value || value == fi->get_else()) - continue; - args.reset(); - for (unsigned i = 1; i < p->num_args(); ++i) - args.push_back(values.get(p->get_arg(i)->get_root_id())); - fi->insert_entry(args.data(), value); + + if (!get_else(v)) { + expr* else_value = mdl.get_some_value(get_array_range(srt)); + fi->set_else(else_value); + set_else(v, else_value); + } + + for (euf::enode* p : *get_select_set(n)) { + expr* value = values.get(p->get_root_id(), nullptr); + if (!value || value == fi->get_else()) + continue; + args.reset(); + for (unsigned i = 1; i < p->num_args(); ++i) { + if (!values.get(p->get_arg(i)->get_root_id())) { + TRACE("array", tout << ctx.bpp(p->get_arg(i)) << "\n"); + } + SASSERT(values.get(p->get_arg(i)->get_root_id())); } + for (unsigned i = 1; i < p->num_args(); ++i) + args.push_back(values.get(p->get_arg(i)->get_root_id())); + fi->insert_entry(args.data(), value); } TRACE("array", tout << "array-as-function " << ctx.bpp(n) << " := " << mk_pp(f, m) << "\n" << "default " << mk_pp(fi->get_else(), m) << "\n";); @@ -135,52 +150,103 @@ namespace array { return true; return false; -#if 0 - struct eq { - solver& s; - eq(solver& s) :s(s) {} - bool operator()(euf::enode* n1, euf::enode* n2) const { - SASSERT(s.a.is_select(n1->get_expr())); - SASSERT(s.a.is_select(n2->get_expr())); - for (unsigned i = n1->num_args(); i-- > 1; ) - if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) - return false; - return true; - } - }; - struct hash { - solver& s; - hash(solver& s) :s(s) {} - unsigned operator()(euf::enode* n) const { - SASSERT(s.a.is_select(n->get_expr())); - unsigned h = 33; - for (unsigned i = n->num_args(); i-- > 1; ) - h = hash_u_u(h, n->get_arg(i)->get_root_id()); - return h; - } - }; - eq eq_proc(*this); - hash hash_proc(*this); - hashtable table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, hash_proc, eq_proc); - euf::enode* p2 = nullptr; - auto maps_diff = [&](euf::enode* p, euf::enode* else_, euf::enode* r) { - return table.find(p, p2) ? p2->get_root() != r : (else_ && else_ != r); - }; - auto table_diff = [&](euf::enode* r1, euf::enode* r2, euf::enode* else1) { - table.reset(); - for (euf::enode* p : euf::enode_parents(r1)) - if (a.is_select(p->get_expr()) && r1 == p->get_arg(0)->get_root()) - table.insert(p); - for (euf::enode* p : euf::enode_parents(r2)) - if (a.is_select(p->get_expr()) && r2 == p->get_arg(0)->get_root()) - if (maps_diff(p, else1, p->get_root())) - return true; - return false; - }; - - return table_diff(r1, r2, else1) || table_diff(r2, r1, else2); + } -#endif + unsigned solver::sel_hash::operator()(euf::enode * n) const { + return get_composite_hash(n, n->num_args() - 1, sel_khasher(), sel_chasher()); + } + + bool solver::sel_eq::operator()(euf::enode * n1, euf::enode * n2) const { + SASSERT(n1->num_args() == n2->num_args()); + unsigned num_args = n1->num_args(); + for (unsigned i = 1; i < num_args; i++) + if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) + return false; + return true; + } + + + void solver::collect_selects() { + int num_vars = get_num_vars(); + + m_selects.reset(); + m_selects_domain.reset(); + m_selects_range.reset(); + + for (theory_var v = 0; v < num_vars; ++v) { + euf::enode * r = var2enode(v)->get_root(); + if (is_representative(v) && ctx.is_relevant(r)) { + for (euf::enode * parent : euf::enode_parents(r)) { + if (parent->get_cg() == parent && + ctx.is_relevant(parent) && + a.is_select(parent->get_expr()) && + parent->get_arg(0)->get_root() == r) { + select_set * s = get_select_set(r); + SASSERT(!s->contains(parent) || (*(s->find(parent)))->get_root() == parent->get_root()); + s->insert(parent); + } + } + } + } + euf::enode_pair_vector todo; + for (euf::enode * r : m_selects_domain) + for (euf::enode* sel : *get_select_set(r)) + propagate_select_to_store_parents(r, sel, todo); + for (unsigned qhead = 0; qhead < todo.size(); qhead++) { + euf::enode_pair & pair = todo[qhead]; + euf::enode * r = pair.first; + euf::enode * sel = pair.second; + propagate_select_to_store_parents(r, sel, todo); + } + } + + void solver::propagate_select_to_store_parents(euf::enode* r, euf::enode* sel, euf::enode_pair_vector& todo) { + SASSERT(r->get_root() == r); + SASSERT(a.is_select(sel->get_expr())); + if (!ctx.is_relevant(r)) + return; + + for (euf::enode * parent : euf::enode_parents(r)) { + if (ctx.is_relevant(parent) && + a.is_store(parent->get_expr()) && + parent->get_arg(0)->get_root() == r) { + // propagate upward + select_set * parent_sel_set = get_select_set(parent); + euf::enode * parent_root = parent->get_root(); + + if (parent_sel_set->contains(sel)) + continue; + + SASSERT(sel->num_args() + 1 == parent->num_args()); + + // check whether the sel idx was overwritten by the store + unsigned num_args = sel->num_args(); + unsigned i = 1; + for (; i < num_args; i++) { + if (sel->get_arg(i)->get_root() != parent->get_arg(i)->get_root()) + break; + } + + if (i < num_args) { + SASSERT(!parent_sel_set->contains(sel) || (*(parent_sel_set->find(sel)))->get_root() == sel->get_root()); + parent_sel_set->insert(sel); + todo.push_back(std::make_pair(parent_root, sel)); + } + } + } + } + + solver::select_set* solver::get_select_set(euf::enode* n) { + euf::enode * r = n->get_root(); + select_set * set = nullptr; + m_selects.find(r, set); + if (set == nullptr) { + set = alloc(select_set); + m_selects.insert(r, set); + m_selects_domain.push_back(r); + m_selects_range.push_back(set); + } + return set; } void solver::collect_defaults() { diff --git a/src/sat/smt/array_solver.h b/src/sat/smt/array_solver.h index 31bdba4a1..511f971a3 100644 --- a/src/sat/smt/array_solver.h +++ b/src/sat/smt/array_solver.h @@ -218,11 +218,39 @@ namespace array { void pop_core(unsigned n) override; // models + // I need a set of select enodes where select(A,i) = select(B,j) if i->get_root() == j->get_root() + struct sel_khasher { + unsigned operator()(euf::enode const * n) const { return 0; } + }; + + struct sel_chasher { + unsigned operator()(euf::enode const * n, unsigned idx) const { + return n->get_arg(idx+1)->get_root()->hash(); + } + }; + + struct sel_hash { + unsigned operator()(euf::enode * n) const; + }; + + struct sel_eq { + bool operator()(euf::enode * n1, euf::enode * n2) const; + }; + + typedef ptr_hashtable select_set; euf::enode_vector m_defaults; // temporary field for model construction ptr_vector m_else_values; // svector m_parents; // temporary field for model construction + obj_map m_selects; // mapping from array -> relevant selects + ptr_vector m_selects_domain; + ptr_vector m_selects_range; + bool must_have_different_model_values(theory_var v1, theory_var v2); + select_set* get_select_set(euf::enode* n); void collect_defaults(); + void collect_selects(); // mapping from array -> relevant selects + void propagate_select_to_store_parents(euf::enode* r, euf::enode* sel, euf::enode_pair_vector& todo); + void mg_merge(theory_var u, theory_var v); theory_var mg_find(theory_var n); void set_default(theory_var v, euf::enode* n); @@ -254,6 +282,7 @@ namespace array { void new_diseq_eh(euf::th_eq const& eq) override; bool unit_propagate() override; void init_model() override; + void finalize_model(model& mdl) override; bool include_func_interp(func_decl* f) const override { return a.is_ext(f); } void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override; bool add_dep(euf::enode* n, top_sort& dep) override; diff --git a/src/sat/smt/euf_model.cpp b/src/sat/smt/euf_model.cpp index fff7e5989..9f7f3b938 100644 --- a/src/sat/smt/euf_model.cpp +++ b/src/sat/smt/euf_model.cpp @@ -63,9 +63,17 @@ namespace euf { } }; + void solver::save_model(model_ref& mdl) { + m_qmodel = mdl; + } + void solver::update_model(model_ref& mdl) { TRACE("model", tout << "create model\n";); - mdl->reset_eval_cache(); + if (m_qmodel) { + mdl = m_qmodel; + return; + } + mdl->reset_eval_cache(); for (auto* mb : m_solvers) mb->init_model(); m_values.reset(); diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 669eb1616..63439625b 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -154,6 +154,7 @@ namespace euf { // model building expr_ref_vector m_values; obj_map m_values2root; + model_ref m_qmodel; bool include_func_interp(func_decl* f); void register_macros(model& mdl); void dependencies2values(user_sort& us, deps_t& deps, model_ref& mdl); @@ -395,6 +396,7 @@ namespace euf { relevancy& get_relevancy() { return m_relevancy; } // model construction + void save_model(model_ref& mdl); void update_model(model_ref& mdl); obj_map const& values2root(); void model_updated(model_ref& mdl); diff --git a/src/sat/smt/q_mam.cpp b/src/sat/smt/q_mam.cpp index b029b0618..d860b3c3c 100644 --- a/src/sat/smt/q_mam.cpp +++ b/src/sat/smt/q_mam.cpp @@ -1893,7 +1893,8 @@ namespace q { } void recycle_enode_vector(enode_vector * v) { - m_pool.recycle(v); + if (v) + m_pool.recycle(v); } void update_max_generation(enode * n, enode * prev) { @@ -2197,8 +2198,10 @@ namespace q { if (curr->num_args() == expected_num_args && ctx.is_relevant(curr)) break; } - if (bp.m_it == bp.m_end) + if (bp.m_it == bp.m_end) { + recycle_enode_vector(bp.m_to_recycle); return nullptr; + } m_top++; update_max_generation(*(bp.m_it), nullptr); return *(bp.m_it); diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index e7b7c2a01..af49f4eda 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -48,6 +48,7 @@ namespace q { lbool mbqi::operator()() { lbool result = l_true; m_model = nullptr; + ctx.save_model(m_model); m_instantiations.reset(); for (sat::literal lit : m_qs.m_universal) { quantifier* q = to_quantifier(ctx.bool_var2expr(lit.var())); @@ -73,6 +74,9 @@ namespace q { m_qs.add_clause(~qlit, ~lit); } m_instantiations.reset(); + if (result != l_true) + m_model = nullptr; + ctx.save_model(m_model); return result; } diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index b2d8d85b7..dbd042e98 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -188,6 +188,7 @@ namespace euf { enode* expr2enode(expr* e) const; enode* var2enode(theory_var v) const { return m_var2enode[v]; } expr* var2expr(theory_var v) const { return var2enode(v)->get_expr(); } + bool is_representative(theory_var v) const { return v == get_representative(v); } expr* bool_var2expr(sat::bool_var v) const; expr_ref literal2expr(sat::literal lit) const; enode* bool_var2enode(sat::bool_var v) const { expr* e = bool_var2expr(v); return e ? expr2enode(e) : nullptr; } diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index faaee95f8..af07001e3 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -405,6 +405,8 @@ struct goal2sat::imp : public sat::sat_internalizer { m_result_stack.shrink(old_sz); } else { + if (process_cached(t, root, sign)) + return; SASSERT(num <= m_result_stack.size()); sat::bool_var k = add_var(false, t); sat::literal l(k, false); @@ -454,6 +456,8 @@ struct goal2sat::imp : public sat::sat_internalizer { m_result_stack.shrink(old_sz); } else { + if (process_cached(t, root, sign)) + return; SASSERT(num <= m_result_stack.size()); sat::bool_var k = add_var(false, t); sat::literal l(k, false); @@ -507,6 +511,8 @@ struct goal2sat::imp : public sat::sat_internalizer { } } else { + if (process_cached(n, root, sign)) + return; sat::bool_var k = add_var(false, n); sat::literal l(k, false); cache(n, l); @@ -537,6 +543,8 @@ struct goal2sat::imp : public sat::sat_internalizer { mk_root_clause(sign ? lit : ~lit); } else { + if (process_cached(t, root, sign)) + return; sat::bool_var k = add_var(false, t); sat::literal l(k, false); cache(t, l); @@ -567,6 +575,8 @@ struct goal2sat::imp : public sat::sat_internalizer { } } else { + if (process_cached(t, root, sign)) + return; sat::bool_var k = add_var(false, t); sat::literal l(k, false); cache(t, l); @@ -603,6 +613,8 @@ struct goal2sat::imp : public sat::sat_internalizer { } } else { + if (process_cached(t, root, sign)) + return; sat::bool_var k = add_var(false, t); sat::literal l(k, false); if (m.is_xor(t)) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 2eba2c2a2..377ad1f2b 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -29,6 +29,7 @@ Revision History: #include "ast/proofs/proof_checker.h" #include "ast/ast_util.h" #include "ast/well_sorted.h" +#include "model/model_params.hpp" #include "model/model.h" #include "model/model_pp.h" #include "smt/smt_context.h" @@ -205,7 +206,7 @@ namespace smt { ast_translation tr(src_ctx.m, m, false); for (unsigned i = 0; i < src_ctx.m_user_propagator->get_num_vars(); ++i) { app* e = src_ctx.m_user_propagator->get_expr(i); - m_user_propagator->add_expr(tr(e)); + m_user_propagator->add_expr(tr(e), true); } } @@ -4638,7 +4639,8 @@ namespace smt { } void context::add_rec_funs_to_model() { - if (m_model) + model_params p; + if (m_model && p.user_functions()) m_model->add_rec_funs(); } diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index f1b2514b1..ac2b0cfc8 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1729,7 +1729,7 @@ namespace smt { void user_propagate_register_expr(expr* e) { if (!m_user_propagator) throw default_exception("user propagator must be initialized"); - m_user_propagator->add_expr(e); + m_user_propagator->add_expr(e, true); } void user_propagate_register_created(user_propagator::created_eh_t& r) { diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 31acc2be0..6e1d77dd4 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -602,9 +602,11 @@ namespace smt { void add_row_entry(unsigned r_id, numeral const & coeff, theory_var v); uint_set& row_vars(); class scoped_row_vars; - + + void check_app(expr* e, expr* n); void internalize_internal_monomial(app * m, unsigned r_id); theory_var internalize_add(app * n); + theory_var internalize_sub(app * n); theory_var internalize_mul_core(app * m); theory_var internalize_mul(app * m); theory_var internalize_div(app * n); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 4a2963656..0168652cb 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -302,6 +302,44 @@ namespace smt { } } + template + void theory_arith::check_app(expr* e, expr* n) { + if (is_app(e)) + return; + std::ostringstream strm; + strm << mk_pp(n, m) << " contains a " << (is_var(e) ? "free variable":"quantifier"); + throw default_exception(strm.str()); + } + + + template + theory_var theory_arith::internalize_sub(app * n) { + VERIFY(m_util.is_sub(n)); + bool first = true; + unsigned r_id = mk_row(); + scoped_row_vars _sc(m_row_vars, m_row_vars_top); + theory_var v; + for (expr* arg : *n) { + check_app(arg, n); + v = internalize_term_core(to_app(arg)); + if (first) + add_row_entry(r_id, numeral::one(), v); + else + add_row_entry(r_id, numeral::one(), v); + first = false; + } + enode * e = mk_enode(n); + v = e->get_th_var(get_id()); + if (v == null_theory_var) { + v = mk_var(e); + add_row_entry(r_id, numeral::one(), v); + init_row(r_id); + } + else + del_row(r_id); + return v; + } + /** \brief Internalize a polynomial (+ h t). Return an alias for the monomial, that is, a variable v such that v = (+ h t) is a new row in the tableau. @@ -314,11 +352,7 @@ namespace smt { unsigned r_id = mk_row(); scoped_row_vars _sc(m_row_vars, m_row_vars_top); for (expr* arg : *n) { - if (is_var(arg)) { - std::ostringstream strm; - strm << mk_pp(n, m) << " contains a free variable"; - throw default_exception(strm.str()); - } + check_app(arg, n); internalize_internal_monomial(to_app(arg), r_id); } enode * e = mk_enode(n); @@ -383,11 +417,7 @@ namespace smt { } unsigned r_id = mk_row(); scoped_row_vars _sc(m_row_vars, m_row_vars_top); - if (is_var(arg1)) { - std::ostringstream strm; - strm << mk_pp(m, get_manager()) << " contains a free variable"; - throw default_exception(strm.str()); - } + check_app(arg1, m); if (reflection_enabled()) internalize_term_core(to_app(arg0)); theory_var v = internalize_mul_core(to_app(arg1)); @@ -749,7 +779,6 @@ namespace smt { return e->get_th_var(get_id()); } - SASSERT(!m_util.is_sub(n)); SASSERT(!m_util.is_uminus(n)); if (m_util.is_add(n)) @@ -770,6 +799,8 @@ namespace smt { return internalize_to_int(n); else if (m_util.is_numeral(n)) return internalize_numeral(n); + else if (m_util.is_sub(n)) + return internalize_sub(n); if (m_util.is_power(n)) { // unsupported found_unsupported_op(n); diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index c86db1aa5..d09069ce4 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -1498,8 +1498,8 @@ void theory_seq::add_length(expr* l) { TRACE("seq", tout << mk_bounded_pp(e, m, 2) << "\n";); m_length.push_back(l); m_has_length.insert(e); - m_trail_stack.push(insert_obj_trail(m_has_length, e)); m_trail_stack.push(push_back_vector(m_length)); + m_trail_stack.push(insert_obj_trail(m_has_length, e)); } /** @@ -1542,8 +1542,8 @@ bool theory_seq::add_length_to_eqc(expr* e) { expr* o = n->get_expr(); if (!has_length(o)) { expr_ref len(m_util.str.mk_length(o), m); - ensure_enode(len); add_length(len); + ensure_enode(len); change = true; } n = n->get_next(); diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index 3f5310ce5..7c53aa8eb 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -24,7 +24,8 @@ using namespace smt; theory_user_propagator::theory_user_propagator(context& ctx): theory(ctx, ctx.get_manager().mk_family_id(user_propagator::plugin::name())), - m_var2expr(ctx.get_manager()) + m_var2expr(ctx.get_manager()), + m_to_add(ctx.get_manager()) {} theory_user_propagator::~theory_user_propagator() { @@ -33,13 +34,15 @@ theory_user_propagator::~theory_user_propagator() { void theory_user_propagator::force_push() { for (; m_num_scopes > 0; --m_num_scopes) { + flet _pushing(m_push_popping, true); theory::push_scope_eh(); - m_push_eh(m_user_context); m_prop_lim.push_back(m_prop.size()); + m_to_add_lim.push_back(m_to_add.size()); + m_push_eh(m_user_context); } } -void theory_user_propagator::add_expr(expr* term) { +void theory_user_propagator::add_expr(expr* term, bool ensure_enode) { force_push(); expr_ref r(m); expr* e = term; @@ -52,7 +55,7 @@ void theory_user_propagator::add_expr(expr* term) { e = r; ctx.mark_as_relevant(eq.get()); } - enode* n = ensure_enode(e); + enode* n = ensure_enode ? this->ensure_enode(e) : ctx.get_enode(e); if (is_attached_to_var(n)) return; @@ -82,6 +85,7 @@ void theory_user_propagator::propagate_cb( expr* conseq) { CTRACE("user_propagate", ctx.lit_internalized(conseq) && ctx.get_assignment(ctx.get_literal(conseq)) == l_true, ctx.display(tout << "redundant consequence: " << mk_pp(conseq, m) << "\n")); + expr_ref _conseq(conseq, m); ctx.get_rewriter()(conseq, _conseq); if (ctx.lit_internalized(_conseq) && ctx.get_assignment(ctx.get_literal(_conseq)) == l_true) @@ -90,7 +94,10 @@ void theory_user_propagator::propagate_cb( } void theory_user_propagator::register_cb(expr* e) { - add_expr(e); + if (m_push_popping) + m_to_add.push_back(e); + else + add_expr(e, true); } theory * theory_user_propagator::mk_fresh(context * new_ctx) { @@ -144,25 +151,29 @@ void theory_user_propagator::new_fixed_eh(theory_var v, expr* value, unsigned nu } } -void theory_user_propagator::push_scope_eh() { +void theory_user_propagator::push_scope_eh() { ++m_num_scopes; } void theory_user_propagator::pop_scope_eh(unsigned num_scopes) { + flet _popping(m_push_popping, true); unsigned n = std::min(num_scopes, m_num_scopes); m_num_scopes -= n; num_scopes -= n; if (num_scopes == 0) return; - m_pop_eh(m_user_context, num_scopes); theory::pop_scope_eh(num_scopes); unsigned old_sz = m_prop_lim.size() - num_scopes; m_prop.shrink(m_prop_lim[old_sz]); m_prop_lim.shrink(old_sz); + old_sz = m_to_add_lim.size() - num_scopes; + m_to_add.shrink(m_to_add_lim[old_sz]); + m_to_add_lim.shrink(old_sz); + m_pop_eh(m_user_context, num_scopes); } bool theory_user_propagator::can_propagate() { - return m_qhead < m_prop.size(); + return m_qhead < m_prop.size() || !m_to_add.empty(); } void theory_user_propagator::propagate_consequence(prop_info const& prop) { @@ -215,10 +226,19 @@ void theory_user_propagator::propagate_new_fixed(prop_info const& prop) { void theory_user_propagator::propagate() { TRACE("user_propagate", tout << "propagating queue head: " << m_qhead << " prop queue: " << m_prop.size() << "\n"); - if (m_qhead == m_prop.size()) + if (m_qhead == m_prop.size() && m_to_add_qhead == m_to_add.size()) return; force_push(); - unsigned qhead = m_qhead; + + unsigned qhead = m_to_add_qhead; + if (qhead < m_to_add.size()) { + for (; qhead < m_to_add.size(); ++qhead) + add_expr(m_to_add.get(qhead), true); + ctx.push_trail(value_trail(m_to_add_qhead)); + m_to_add_qhead = qhead; + } + + qhead = m_qhead; while (qhead < m_prop.size() && !ctx.inconsistent()) { auto const& prop = m_prop[qhead]; if (prop.m_var == null_theory_var) @@ -243,7 +263,7 @@ bool theory_user_propagator::internalize_term(app* term) { if (term->get_family_id() == get_id() && !ctx.e_internalized(term)) ctx.mk_enode(term, true, false, true); - add_expr(term); + add_expr(term, false); if (!m_created_eh) throw default_exception("You have to register a created event handler for new terms if you track them"); diff --git a/src/smt/theory_user_propagator.h b/src/smt/theory_user_propagator.h index 1045feb0a..9b271e9c3 100644 --- a/src/smt/theory_user_propagator.h +++ b/src/smt/theory_user_propagator.h @@ -78,6 +78,10 @@ namespace smt { stats m_stats; expr_ref_vector m_var2expr; unsigned_vector m_expr2var; + bool m_push_popping; + expr_ref_vector m_to_add; + unsigned_vector m_to_add_lim; + unsigned m_to_add_qhead = 0; expr* var2expr(theory_var v) { return m_var2expr.get(v); } theory_var expr2var(expr* e) { check_defined(e); return m_expr2var[e->get_id()]; } @@ -110,7 +114,7 @@ namespace smt { m_fresh_eh = fresh_eh; } - void add_expr(expr* e); + void add_expr(expr* e, bool ensure_enode); void register_final(user_propagator::final_eh_t& final_eh) { m_final_eh = final_eh; } void register_fixed(user_propagator::fixed_eh_t& fixed_eh) { m_fixed_eh = fixed_eh; } diff --git a/src/tactic/arith/bv2real_rewriter.cpp b/src/tactic/arith/bv2real_rewriter.cpp index bb8c17f33..03acc8161 100644 --- a/src/tactic/arith/bv2real_rewriter.cpp +++ b/src/tactic/arith/bv2real_rewriter.cpp @@ -17,6 +17,7 @@ Notes: --*/ #include "tactic/arith/bv2real_rewriter.h" +#include "tactic/tactic_exception.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/for_each_expr.h" @@ -40,6 +41,7 @@ bv2real_util::bv2real_util(ast_manager& m, rational const& default_root, rationa m_pos_le = m.mk_fresh_func_decl("<=","",2,domain,m.mk_bool_sort()); m_decls.push_back(m_pos_lt); m_decls.push_back(m_pos_le); + m_max_memory = std::max((1ull << 31ull), 3*memory::get_allocation_size()); } bool bv2real_util::is_bv2real(func_decl* f) const { @@ -178,12 +180,10 @@ void bv2real_util::align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr expr* bv2real_util::mk_bv_mul(expr* s, expr* t) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); - if (is_zero(s)) { + if (is_zero(s)) return s; - } - if (is_zero(t)) { + if (is_zero(t)) return t; - } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); unsigned n = m_bv.get_bv_size(t1); @@ -343,6 +343,10 @@ bool bv2real_util::mk_is_divisible_by(expr_ref& s, rational const& _overflow) { } +bool bv2real_util::memory_exceeded() const { + return m_max_memory <= memory::get_allocation_size(); +} + // --------------------------------------------------------------------- // bv2real_rewriter @@ -362,6 +366,11 @@ br_status bv2real_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * tout << mk_pp(args[i], m()) << " "; } tout << "\n";); + + if (u().memory_exceeded()) { + std::cout << "tactic exception\n"; + throw tactic_exception("bv2real-memory exceeded"); + } if(f->get_family_id() == m_arith.get_family_id()) { switch (f->get_decl_kind()) { case OP_NUM: return BR_FAILED; diff --git a/src/tactic/arith/bv2real_rewriter.h b/src/tactic/arith/bv2real_rewriter.h index 7b3915105..4c3c63c2a 100644 --- a/src/tactic/arith/bv2real_rewriter.h +++ b/src/tactic/arith/bv2real_rewriter.h @@ -65,6 +65,7 @@ class bv2real_util { rational m_default_divisor; rational m_max_divisor; unsigned m_max_num_bits; + uint64_t m_max_memory; class contains_bv2real_proc; @@ -81,6 +82,8 @@ public: bool contains_bv2real(expr* e) const; + bool memory_exceeded() const; + bool mk_bv2real(expr* s, expr* t, rational& d, rational& r, expr_ref& result); expr* mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r); expr* mk_bv2real(expr* n, expr* m) { return mk_bv2real_c(n, m, default_divisor(), default_root()); } diff --git a/src/tactic/arith/nla2bv_tactic.cpp b/src/tactic/arith/nla2bv_tactic.cpp index 887cc9e31..560b7b265 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -100,20 +100,19 @@ class nla2bv_tactic : public tactic { return; } substitute_vars(g); - TRACE("nla2bv", g.display(tout << "substitute vars\n");); + TRACE("nla2bv", g.display(tout << "substitute vars\n")); reduce_bv2int(g); reduce_bv2real(g); - TRACE("nla2bv", g.display(tout << "after reduce\n");); + TRACE("nla2bv", g.display(tout << "after reduce\n")); mc = m_fmc.get(); - for (unsigned i = 0; i < m_vars.size(); ++i) { - m_fmc->add(m_vars[i].get(), m_defs[i].get()); - } + for (unsigned i = 0; i < m_vars.size(); ++i) + m_fmc->add(m_vars.get(i), m_defs.get(i)); for (unsigned i = 0; i < m_bv2real.num_aux_decls(); ++i) { m_fmc->hide(m_bv2real.get_aux_decl(i)); } IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(nla->bv :sat-preserving " << m_is_sat_preserving << ")\n";); - TRACE("nla2bv_verbose", g.display(tout);); - TRACE("nla2bv", tout << "Muls: " << count_mul(g) << "\n";); + TRACE("nla2bv_verbose", g.display(tout)); + TRACE("nla2bv", tout << "Muls: " << count_mul(g) << "\n"); g.inc_depth(); if (!is_sat_preserving()) g.updt_prec(goal::UNDER); diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 2d14a5eaa..9167650ad 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -336,6 +336,9 @@ public: catch (tactic_exception &) { result.reset(); } + catch (rewriter_exception&) { + result.reset(); + } catch (z3_error & ex) { IF_VERBOSE(10, verbose_stream() << "z3 error: " << ex.error_code() << " in or-else\n"); throw; @@ -1019,7 +1022,6 @@ public: void operator()(goal_ref const & in, goal_ref_buffer& result) override { cancel_eh eh(in->m().limit()); { - // Warning: scoped_timer is not thread safe in Linux. scoped_timer timer(m_timeout, &eh); m_t->operator()(in, result); }