diff --git a/.github/workflows/cross-build.yml b/.github/workflows/cross-build.yml new file mode 100644 index 000000000..5efd00329 --- /dev/null +++ b/.github/workflows/cross-build.yml @@ -0,0 +1,30 @@ +name: RISC V and PowerPC 64 + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + container: ubuntu:jammy + + strategy: + fail-fast: false + matrix: + arch: [ aarch64, riscv64, powerpc64 ] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install cross build tools + run: apt update && apt install -y ninja-build cmake python3 g++-11-${{ matrix.arch }}-linux-gnu + env: + DEBIAN_FRONTEND: noninteractive + + - name: Configure CMake and build + run: | + mkdir build && cd build + cmake -DCMAKE_CXX_COMPILER=${{ matrix.arch }}-linux-gnu-g++-11 ../ + make -j$(nproc) diff --git a/.gitignore b/.gitignore index ac83a2775..051580bb3 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,7 @@ src/api/js/build/ src/api/js/**/*.d.ts !src/api/js/scripts/*.js !src/api/js/src/*.js +debug/* out/** *.bak diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a265bb6c..35046e60b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.4) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") -project(Z3 VERSION 4.8.15.0 LANGUAGES CXX C) +project(Z3 VERSION 4.8.16.0 LANGUAGES CXX C) ################################################################################ # Project version @@ -198,7 +198,9 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "GNU") message(STATUS "Platform: GNU/Hurd") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_HURD_") elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") - # Does macOS really not need any special flags? + if (TARGET_ARCHITECTURE STREQUAL "arm64") + set(CMAKE_OSX_ARCHITECTURES "arm64") + endif() message(STATUS "Platform: Darwin") elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") message(STATUS "Platform: FreeBSD") @@ -431,6 +433,11 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") string(APPEND CMAKE_SHARED_LINKER_FLAGS " /RELEASE") endif() +################################################################################ +# Check atomic linking as needed +################################################################################ +include(${PROJECT_SOURCE_DIR}/cmake/check_link_atomic.cmake) + ################################################################################ # Report default CMake flags ################################################################################ diff --git a/README.md b/README.md index 643e49823..4849c33d8 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ A 32 bit build should work similarly (but is untested); the same is true for 32/ By default, it will install z3 executable at ``PREFIX/bin``, libraries at ``PREFIX/lib``, and include files at ``PREFIX/include``, where ``PREFIX`` -installation prefix if inferred by the ``mk_make.py`` script. It is usually +installation prefix is inferred by the ``mk_make.py`` script. It is usually ``/usr`` for most Linux distros, and ``/usr/local`` for FreeBSD and macOS. Use the ``--prefix=`` command line option to change the install prefix. For example: @@ -158,7 +158,7 @@ You can install the Python wrapper for Z3 for the latest release from pypi using Use the ``--python`` command line flag with ``mk_make.py`` to enable building these. -Note that is required on certain platforms that the Python package directory +Note that it is required on certain platforms that the Python package directory (``site-packages`` on most distributions and ``dist-packages`` on Debian based distributions) live under the install prefix. If you use a non standard prefix you can use the ``--pypkgdir`` option to change the Python package directory @@ -199,6 +199,11 @@ The Julia package [Z3.jl](https://github.com/ahumenberger/Z3.jl) wraps the C++ A A WebAssembly build with associated TypeScript typings is published on npm as [z3-solver](https://www.npmjs.com/package/z3-solver). Information about building these bindings can be found in [src/api/js](src/api/js). +### Smalltalk (``Pharo`` / ``Smalltalk/X``) + +Project [MachineArithmetic](https://github.com/shingarov/MachineArithmetic) provides Smalltalk interface +to Z3's C API. For more information, see [MachineArithmetic/README.md](https://github.com/shingarov/MachineArithmetic/blob/pure-z3/MachineArithmetic/README.md) + ## System Overview ![System Diagram](https://github.com/Z3Prover/doc/blob/master/programmingz3/images/Z3Overall.jpg) @@ -215,5 +220,6 @@ A WebAssembly build with associated TypeScript typings is published on npm as [z * C * OCaml * [Julia](https://github.com/ahumenberger/Z3.jl) +* [Smalltalk](https://github.com/shingarov/MachineArithmetic/blob/pure-z3/MachineArithmetic/README.md) (supports Pharo and Smalltalk/X) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 78cd70fb0..8e61f55bd 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -10,6 +10,10 @@ Version 4.8.next - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. +Version 4.8.15 +============== + - elaborate user propagator API. Change id based scheme to expressions + - includes a Web Assembly ffi API thanks to Kevin Gibbons Version 4.8.14 ============== diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bff70c859..d29a3dd51 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -217,12 +217,12 @@ jobs: $(setupCmd1) $(setupCmd2) $(setupCmd3) - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" $(arch) + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" $(arch) cmake $(bindings) -G "NMake Makefiles" ../ nmake cd .. - script: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" $(arch) + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" $(arch) pushd build\python python z3test.py z3 python z3test.py z3num diff --git a/cmake/check_link_atomic.cmake b/cmake/check_link_atomic.cmake new file mode 100644 index 000000000..d462191a0 --- /dev/null +++ b/cmake/check_link_atomic.cmake @@ -0,0 +1,23 @@ +set(ATOMIC_TEST_SOURCE " +#include +std::atomic x; +std::atomic y; +std::atomic z; +std::atomic w; +int main() { + ++z; + ++y; + ++w; + return ++x; +}") +CHECK_CXX_SOURCE_COMPILES("${ATOMIC_TEST_SOURCE}" BUILTIN_ATOMIC) +if (NOT BUILTIN_ATOMIC) + set(CMAKE_REQUIRED_LIBRARIES atomic) + CHECK_CXX_SOURCE_COMPILES("${ATOMIC_TEST_SOURCE}" ATOMICS_REQUIRE_LIBATOMIC) + unset(CMAKE_REQUIRED_LIBRARIES) + if (ATOMICS_REQUIRE_LIBATOMIC) + list(APPEND Z3_DEPENDENT_LIBS atomic) + else() + message(FATAL_ERROR "Host compiler must support std::atomic!") + endif() +endif() diff --git a/cmake/target_arch_detect.cpp b/cmake/target_arch_detect.cpp index 8053e3532..379b5817e 100644 --- a/cmake/target_arch_detect.cpp +++ b/cmake/target_arch_detect.cpp @@ -5,6 +5,10 @@ #error CMAKE_TARGET_ARCH_i686 #elif defined(__x86_64__) || defined(_M_X64) #error CMAKE_TARGET_ARCH_x86_64 +#elif defined(__ARM_ARCH_ISA_A64) +#error CMAKE_TARGET_ARCH_arm64 +#elif defined(__ARM_ARCH) +#error CMAKE_TARGET_ARCH_arm #else #error CMAKE_TARGET_ARCH_unknown #endif diff --git a/doc/website.dox.in b/doc/website.dox.in index 411520a17..f4caa1277 100644 --- a/doc/website.dox.in +++ b/doc/website.dox.in @@ -4,11 +4,10 @@ Z3 is a high-performance theorem prover being developed at Microsoft Research. - The Z3 website is at http://github.com/z3prover.. + The Z3 website is at http://github.com/z3prover. This website hosts the automatically generated documentation for the Z3 APIs. - \ref @C_API@ - \ref @CPP_API@ @DOTNET_API@ @JAVA_API@ @PYTHON_API@ @OCAML_API@ - - Try Z3 online at RiSE4Fun. */ diff --git a/doc/z3api.cfg.in b/doc/z3api.cfg.in index 9b7e61e34..41624b255 100644 --- a/doc/z3api.cfg.in +++ b/doc/z3api.cfg.in @@ -270,8 +270,7 @@ ALIASES = "beginfaq=
    " \ "emph{1}=\1" \ "extdoc{2}=\2" \ "nicebox{1}=
    \1
    " \ - "ccode{1}=\1" \ - "zframe=" + "ccode{1}=\1" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" diff --git a/doc/z3code.dox b/doc/z3code.dox index 205dd059b..b6a7fb088 100644 --- a/doc/z3code.dox +++ b/doc/z3code.dox @@ -64,8 +64,7 @@ ALIASES = "beginfaq=
      " \ "emph{1}=\1" \ "extdoc{2}=\2" \ "nicebox{1}=
      \1
      " \ - "ccode{1}=\1" \ - "zframe=" + "ccode{1}=\1" OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO 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/examples/userPropagator/CMakeLists.txt b/examples/userPropagator/CMakeLists.txt index 9ed916d46..384d257dc 100644 --- a/examples/userPropagator/CMakeLists.txt +++ b/examples/userPropagator/CMakeLists.txt @@ -24,7 +24,15 @@ message(STATUS "Z3_FOUND: ${Z3_FOUND}") message(STATUS "Found Z3 ${Z3_VERSION_STRING}") message(STATUS "Z3_DIR: ${Z3_DIR}") -add_executable(user_propagator_example example.cpp) +add_executable(user_propagator_example + example.cpp + common.h + user_propagator.h + user_propagator_with_theory.h + user_propagator_subquery_maximisation.h + user_propagator_internal_maximisation.h + user_propagator_created_maximisation.h) + target_include_directories(user_propagator_example PRIVATE ${Z3_CXX_INCLUDE_DIRS}) target_link_libraries(user_propagator_example PRIVATE ${Z3_LIBRARIES}) diff --git a/examples/userPropagator/common.h b/examples/userPropagator/common.h new file mode 100644 index 000000000..3c9f3b299 --- /dev/null +++ b/examples/userPropagator/common.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z3++.h" + +using std::to_string; + +#define SIZE(x) std::extent::value + +// #define VERBOSE // Log events +#ifdef VERBOSE +#define WriteEmptyLine std::cout << std::endl +#define WriteLine(x) std::cout << (x) << std::endl +#define Write(x) std::cout << x +#else +#define WriteEmptyLine +#define WriteLine(x) +#define Write(x) +#endif + +int log2i(unsigned n) { + if (n <= 0) { + return 0; + } + if (n <= 2) { + return 1; + } + unsigned l = 1; + int i = 0; + while (l < n) { + l <<= 1; + i++; + } + return i; +} + +typedef std::vector simple_model; + +// For putting z3 expressions in hash-tables +namespace std { + + template<> + struct hash { + std::size_t operator()(const simple_model &m) const { + size_t hash = 0; + for (unsigned i = 0; i < m.size(); i++) { + hash *= m.size(); + hash += m[i]; + } + return hash; + } + }; + + template<> + struct hash { + std::size_t operator()(const z3::expr &k) const { + return k.hash(); + } + }; + + // Do not use Z3's == operator in the hash table + template<> + struct equal_to { + bool operator()(const z3::expr &lhs, const z3::expr &rhs) const { + return z3::eq(lhs, rhs); + } + }; +} \ No newline at end of file diff --git a/examples/userPropagator/example.cpp b/examples/userPropagator/example.cpp index b66e3bb0e..9c3e7cf3e 100644 --- a/examples/userPropagator/example.cpp +++ b/examples/userPropagator/example.cpp @@ -1,13 +1,8 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "z3++.h" +#include "user_propagator.h" +#include "user_propagator_with_theory.h" +#include "user_propagator_subquery_maximisation.h" +#include "user_propagator_internal_maximisation.h" +#include "user_propagator_created_maximisation.h" /** * The program solves the n-queens problem (number of solutions) with 4 different approaches @@ -20,206 +15,57 @@ */ using namespace std::chrono; -using std::to_string; -#define QUEEN #define REPETITIONS 5 -#define SIZE(x) std::extent::value +#define MIN_BOARD 4 +#define MAX_BOARD1 12 +#define MAX_BOARD2 12 -#ifdef LOG -#define WriteEmptyLine std::cout << std::endl -#define WriteLine(x) std::cout << (x) << std::endl -#define Write(x) std::cout << x -#else -#define WriteEmptyLine -#define WriteLine(x) -#define Write(x) -#endif - -typedef std::vector model; - -struct model_hash_function { - std::size_t operator()(const model &m) const { - size_t hash = 0; - for (unsigned i = 0; i < m.size(); i++) { - hash *= m.size(); - hash += m[i]; - } - return hash; - } -}; - -class user_propagator : public z3::user_propagator_base { - -protected: - - unsigned board; - std::unordered_map& id_mapping; - model currentModel; - std::unordered_set modelSet; - std::vector fixedValues; - std::stack fixedCnt; - - int solutionId = 1; - -public: - - int getModelCount() const { - return solutionId - 1; - } - - void final() final { - this->conflict((unsigned) fixedValues.size(), fixedValues.data()); - if (modelSet.find(currentModel) != modelSet.end()) { - WriteLine("Got already computed model"); - return; - } - Write("Model #" << solutionId << ":\n"); - solutionId++; -#ifdef LOG - for (unsigned i = 0; i < fixedValues.size(); i++) { - unsigned id = fixedValues[i]; - WriteLine("q" + to_string(id_mapping[id]) + " = " + to_string(currentModel[id])); - } -#endif - modelSet.insert(currentModel); - WriteEmptyLine; - } - - static unsigned bvToInt(z3::expr e) { - return (unsigned)e.get_numeral_int(); - } - - void fixed(unsigned id, z3::expr const &e) override { - fixedValues.push_back(id); - unsigned value = bvToInt(e); - currentModel[id_mapping[id]] = value; - } - - user_propagator(z3::solver *s, std::unordered_map& idMapping, unsigned board) - : user_propagator_base(s), board(board), id_mapping(idMapping), currentModel(board, (unsigned)-1) { - - this->register_fixed(); - this->register_final(); - } - - virtual ~user_propagator() = default; - - void push() override { - fixedCnt.push((unsigned) fixedValues.size()); - } - - void pop(unsigned num_scopes) override { - for (unsigned i = 0; i < num_scopes; i++) { - unsigned lastCnt = fixedCnt.top(); - fixedCnt.pop(); - for (auto j = fixedValues.size(); j > lastCnt; j--) { - currentModel[fixedValues[j - 1]] = (unsigned)-1; - } - fixedValues.resize(lastCnt); - } - } - - user_propagator_base *fresh(Z3_context) override { return this; } -}; - -class user_propagator_with_theory : public user_propagator { - -public: - - void fixed(unsigned id, z3::expr const &e) override { - unsigned queenId = id_mapping[id]; - unsigned queenPos = bvToInt(e); - - if (queenPos >= board) { - this->conflict(1, &id); - return; - } - - for (unsigned fixed : fixedValues) { - unsigned otherId = id_mapping[fixed]; - unsigned otherPos = currentModel[fixed]; - - if (queenPos == otherPos) { - const unsigned conflicting[] = {id, fixed}; - this->conflict(2, conflicting); - continue; - } -#ifdef QUEEN - int diffY = abs((int)queenId - (int)otherId); - int diffX = abs((int)queenPos - (int)otherPos); - if (diffX == diffY) { - const unsigned conflicting[] = {id, fixed}; - this->conflict(2, conflicting); - } -#endif - } - - fixedValues.push_back(id); - currentModel[id_mapping[id]] = queenPos; - } - - user_propagator_with_theory(z3::solver *s, std::unordered_map& idMapping, unsigned board) - : user_propagator(s, idMapping, board) {} -}; - -int log2i(unsigned n) { - if (n <= 0) { - return 0; - } - if (n <= 2) { - return 1; - } - unsigned l = 1; - int i = 0; - while (l < n) { - l <<= 1; - i++; - } - return i; -} - -std::vector createQueens(z3::context &context, unsigned num) { - std::vector queens; - int bits = log2i(num) + 1 /*to detect potential overflow in the diagonal*/; +z3::expr_vector createQueens(z3::context &context, unsigned num, int bits, std::string prefix) { + z3::expr_vector queens(context); for (unsigned i = 0; i < num; i++) { - queens.push_back(context.bv_const((std::string("q") + to_string(i)).c_str(), bits)); + queens.push_back(context.bv_const((prefix + "q" + to_string(i)).c_str(), bits)); } return queens; } -void createConstraints(z3::context &context, z3::solver &solver, const std::vector &queens) { +z3::expr_vector createQueens(z3::context &context, unsigned num) { + return createQueens(context, num, log2i(num) + 1, ""); +} + +z3::expr createConstraints(z3::context &context, const z3::expr_vector &queens) { + z3::expr_vector assertions(context); for (unsigned i = 0; i < queens.size(); i++) { // assert column range - solver.add(z3::uge(queens[i], 0)); - solver.add(z3::ule(queens[i], (int) (queens.size() - 1))); + assertions.push_back(z3::uge(queens[i], 0)); + assertions.push_back(z3::ule(queens[i], (int) (queens.size() - 1))); } z3::expr_vector distinct(context); - for (const z3::expr &queen : queens) { + for (const z3::expr &queen: queens) { distinct.push_back(queen); } - solver.add(z3::distinct(distinct)); + assertions.push_back(z3::distinct(distinct)); -#ifdef QUEEN for (unsigned i = 0; i < queens.size(); i++) { for (unsigned j = i + 1; j < queens.size(); j++) { - solver.add((int)(j - i) != (queens[j] - queens[i])); - solver.add((int)(j - i) != (queens[i] - queens[j])); + assertions.push_back((int) (j - i) != (queens[j] - queens[i])); + assertions.push_back((int) (j - i) != (queens[i] - queens[j])); } } -#endif + + return z3::mk_and(assertions); } int test01(unsigned num, bool simple) { z3::context context; z3::solver solver(context, !simple ? Z3_mk_solver(context) : Z3_mk_simple_solver(context)); - std::vector queens = createQueens(context, num); + z3::expr_vector queens = createQueens(context, num); - createConstraints(context, solver, queens); + solver.add(createConstraints(context, queens)); int solutionId = 1; @@ -260,8 +106,8 @@ inline int test1(unsigned num) { int test23(unsigned num, bool withTheory) { z3::context context; - z3::solver solver(context, Z3_mk_simple_solver(context)); - std::unordered_map idMapping; + z3::solver solver(context, z3::solver::simple()); + std::unordered_map idMapping; user_propagator *propagator; if (!withTheory) { @@ -271,15 +117,15 @@ int test23(unsigned num, bool withTheory) { propagator = new user_propagator_with_theory(&solver, idMapping, num); } - std::vector queens = createQueens(context, num); + z3::expr_vector queens = createQueens(context, num); for (unsigned i = 0; i < queens.size(); i++) { - unsigned id = propagator->add(queens[i]); - idMapping[id] = i; + propagator->add(queens[i]); + idMapping[queens[i]] = i; } if (!withTheory) { - createConstraints(context, solver, queens); + solver.add(createConstraints(context, queens)); } solver.check(); @@ -296,50 +142,246 @@ inline int test3(unsigned num) { return test23(num, true); } +int test4(unsigned num) { + z3::context context; + z3::solver solver(context, z3::solver::simple()); + + z3::expr_vector queens1 = createQueens(context, num, log2i(num * num), ""); // square to avoid overflow during summation + + z3::expr valid1 = createConstraints(context, queens1); + + z3::expr_vector queens2 = createQueens(context, num, log2i(num * num), "forall_"); + + z3::expr valid2 = createConstraints(context, queens2); + + z3::expr manhattanSum1 = context.bv_val(0, queens1[0].get_sort().bv_size()); + z3::expr manhattanSum2 = context.bv_val(0, queens2[0].get_sort().bv_size()); + + for (int i = 1; i < queens1.size(); i++) { + manhattanSum1 = manhattanSum1 + z3::ite(z3::uge(queens1[i], queens1[i - 1]), queens1[i] - queens1[i - 1], queens1[i - 1] - queens1[i]); + manhattanSum2 = manhattanSum2 + z3::ite(z3::uge(queens2[i], queens2[i - 1]), queens2[i] - queens2[i - 1], queens2[i - 1] - queens2[i]); + } + + + solver.add(valid1 && z3::forall(queens2, z3::implies(valid2, manhattanSum1 >= manhattanSum2))); + + solver.check(); + z3::model model = solver.get_model(); + + int max = 0; + + int prev, curr; + curr = model.eval(queens1[0]).get_numeral_int(); + + for (unsigned i = 1; i < num; i++) { + prev = curr; + curr = model.eval(queens1[i]).get_numeral_int(); + max += abs(curr - prev); + } + + return max; +} + +int test5(unsigned num) { + z3::context context; + z3::solver solver(context, z3::solver::simple()); + std::unordered_map idMapping; + + z3::expr_vector queens = createQueens(context, num, log2i(num * num), ""); + + solver.add(createConstraints(context, queens)); + + user_propagator_subquery_maximisation propagator(&solver, idMapping, num, queens); + + for (unsigned i = 0; i < queens.size(); i++) { + propagator.add(queens[i]); + idMapping[queens[i]] = i; + } + + solver.check(); + z3::model model = solver.get_model(); + + int max = 0; + + int prev, curr; + curr = model.eval(queens[0]).get_numeral_int(); + for (unsigned i = 1; i < num; i++) { + prev = curr; + curr = model.eval(queens[i]).get_numeral_int(); + max += abs(curr - prev); + } + + return max; +} + +int test6(unsigned num) { + z3::context context; + z3::solver solver(context, z3::solver::simple()); + std::unordered_map idMapping; + + z3::expr_vector queens = createQueens(context, num, log2i(num * num), ""); + + solver.add(createConstraints(context, queens)); + + user_propagator_internal_maximisation propagator(&solver, idMapping, num, queens); + + for (unsigned i = 0; i < queens.size(); i++) { + propagator.add(queens[i]); + idMapping[queens[i]] = i; + } + + solver.check(); + return propagator.best; +} + +int test7(unsigned num) { + z3::context context; + z3::solver solver(context, z3::solver::simple()); + + z3::expr_vector queens1 = createQueens(context, num, log2i(num * num), ""); + z3::expr_vector queens2 = createQueens(context, num, log2i(num * num), "forall_"); + + z3::expr manhattanSum1 = context.bv_val(0, queens1[0].get_sort().bv_size()); + z3::expr manhattanSum2 = context.bv_val(0, queens2[0].get_sort().bv_size()); + + for (int i = 1; i < queens1.size(); i++) { + manhattanSum1 = manhattanSum1 + z3::ite(z3::uge(queens1[i], queens1[i - 1]), queens1[i] - queens1[i - 1], queens1[i - 1] - queens1[i]); + manhattanSum2 = manhattanSum2 + z3::ite(z3::uge(queens2[i], queens2[i - 1]), queens2[i] - queens2[i - 1], queens2[i - 1] - queens2[i]); + } + + z3::sort_vector domain(context); + for (int i = 0; i < queens1.size(); i++) { + domain.push_back(queens1[i].get_sort()); + } + z3::func_decl validFunc = context.user_propagate_function(context.str_symbol("valid"), domain, context.bool_sort()); + + solver.add(validFunc(queens1) && z3::forall(queens2, z3::implies(validFunc(queens2), manhattanSum1 >= manhattanSum2))); + user_propagator_created_maximisation propagator(&solver, num); + + solver.check(); + z3::model model = solver.get_model(); + + int max = 0; + + int prev, curr; + curr = model.eval(queens1[0]).get_numeral_int(); + + for (unsigned i = 1; i < num; i++) { + prev = curr; + curr = model.eval(queens1[i]).get_numeral_int(); + max += abs(curr - prev); + } + + return max; +} + int main() { - for (int num = 4; num <= 11; num++) { + for (int num = MIN_BOARD; num <= MAX_BOARD1; num++) { + + std::cout << "num = " << num << ":\n" << std::endl; + + unsigned seed = (unsigned) high_resolution_clock::now().time_since_epoch().count(); + const char *testName[] = + { + "BV + Blocking clauses (Default solver)", + "BV + Blocking clauses (Simple solver)", + "BV + Adding conflicts", + "Custom theory + conflicts", + }; + int permutation[4] = {0, 1, 2, 3,}; + double timeResults[REPETITIONS * SIZE(permutation)]; + + for (int rep = 0; rep < REPETITIONS; rep++) { + // Execute strategies in a randomised order + std::shuffle(&permutation[0], &permutation[SIZE(permutation) - 1], std::default_random_engine(seed)); + + for (int i : permutation) { + int modelCount = -1; + + auto now1 = high_resolution_clock::now(); + + switch (i) { + case 0: + modelCount = test0(num); + break; + case 1: + modelCount = test1(num); + break; + case 2: + modelCount = test2(num); + break; + case 3: + modelCount = test3(num); + break; + default: + WriteLine("Unknown case"); + break; + } + auto now2 = high_resolution_clock::now(); + duration ms = now2 - now1; + std::cout << testName[i] << " took " << ms.count() << "ms (" << modelCount << " models)" << std::endl; + timeResults[rep * SIZE(permutation) + i] = ms.count(); + WriteLine("-------------"); + } + } + + std::cout << "\n" << std::endl; + + for (unsigned i = 0; i < SIZE(permutation); i++) { + std::cout << testName[i]; + double sum = 0; + for (int j = 0; j < REPETITIONS; j++) { + std::cout << " " << timeResults[j * SIZE(permutation) + i] << "ms"; + sum += timeResults[j * SIZE(permutation) + i]; + } + std::cout << " | avg: " << sum / REPETITIONS << "ms" << std::endl; + } + + std::cout << std::endl; + } + + z3::set_param("smt.ematching", "false"); + z3::set_param("smt.mbqi", "true"); + + std::cout << "\nMaximal distance:" << std::endl; + + for (int num = MIN_BOARD; num <= MAX_BOARD2; num++) { std::cout << "num = " << num << ":\n" << std::endl; unsigned seed = (unsigned) high_resolution_clock::now().time_since_epoch().count(); const char *testName[] = { - "BV + Blocking clauses (Default solver)", - "BV + Blocking clauses (Simple solver)", - "BV + Adding conflicts", - "Custom theory + conflicts", - }; - int permutation[4] = - { - 0, - 1, - 2, - 3, + "Ordinary/Direct Encoding", + "SubQuery in final", + "Assert Smaller in final", + "created", }; + int permutation[4] = {0, 1, 2, 3,}; double timeResults[REPETITIONS * SIZE(permutation)]; for (int rep = 0; rep < REPETITIONS; rep++) { // Execute strategies in a randomised order std::shuffle(&permutation[0], &permutation[SIZE(permutation) - 1], std::default_random_engine(seed)); - for (int i : permutation) { - int modelCount = -1; + for (int i: permutation) { + int max = -1; auto now1 = high_resolution_clock::now(); - switch (i) { - case 0: - modelCount = test0(num); + switch (i + 4) { + case 4: + max = test4(num); break; - case 1: - modelCount = test1(num); + case 5: + max = test5(num); break; - case 2: - modelCount = test2(num); + case 6: + max = test6(num); break; - case 3: - modelCount = test3(num); + case 7: + max = test7(num); break; default: WriteLine("Unknown case"); @@ -347,7 +389,7 @@ int main() { } auto now2 = high_resolution_clock::now(); duration ms = now2 - now1; - std::cout << testName[i] << " took " << ms.count() << "ms (" << modelCount << " models)" << std::endl; + std::cout << testName[i] << " took " << ms.count() << "ms. Max: " << max << std::endl; timeResults[rep * SIZE(permutation) + i] = ms.count(); WriteLine("-------------"); } @@ -367,4 +409,4 @@ int main() { std::cout << std::endl; } -} +} \ No newline at end of file diff --git a/examples/userPropagator/example.pdf b/examples/userPropagator/example.pdf index 2e802259c..eaf3c9952 100644 Binary files a/examples/userPropagator/example.pdf and b/examples/userPropagator/example.pdf differ diff --git a/examples/userPropagator/user_propagator.h b/examples/userPropagator/user_propagator.h new file mode 100644 index 000000000..6c12ee2f3 --- /dev/null +++ b/examples/userPropagator/user_propagator.h @@ -0,0 +1,87 @@ +#pragma once + +#include "common.h" + +class user_propagator : public z3::user_propagator_base { + +protected: + + unsigned board; + std::unordered_map &queenToY; + simple_model currentModel; + std::unordered_set modelSet; + z3::expr_vector fixedValues; + std::stack fixedCnt; + + int solutionNr = 1; + +public: + + int getModelCount() const { + return solutionNr - 1; + } + + void final() override { + this->conflict(fixedValues); + if (modelSet.find(currentModel) != modelSet.end()) { + WriteLine("Got already computed model"); + return; + } + Write("Model #" << solutionNr << ":\n"); + solutionNr++; +#ifdef VERBOSE + for (unsigned i = 0; i < fixedValues.size(); i++) { + z3::expr fixed = fixedValues[i]; + WriteLine("q" + to_string(queenToY[fixed]) + " = " + to_string(currentModel[queenToY[fixed]])); + } +#endif + modelSet.insert(currentModel); + WriteEmptyLine; + } + + static unsigned bvToInt(z3::expr const &e) { + return (unsigned) e.get_numeral_int(); + } + + void fixed(z3::expr const &ast, z3::expr const &value) override { + fixedValues.push_back(ast); + unsigned valueBv = bvToInt(value); + currentModel[queenToY[ast]] = valueBv; + } + + user_propagator(z3::context &c, std::unordered_map &queenToY, unsigned board) + : user_propagator_base(c), board(board), queenToY(queenToY), fixedValues(c), currentModel(board, (unsigned) -1) { + + this->register_fixed(); + this->register_final(); + } + + user_propagator(z3::solver *s, std::unordered_map &idMapping, unsigned board) + : user_propagator_base(s), board(board), queenToY(idMapping), fixedValues(s->ctx()), currentModel(board, (unsigned) -1) { + + this->register_fixed(); + this->register_final(); + } + + ~user_propagator() = default; + + void push() override { + fixedCnt.push((unsigned) fixedValues.size()); + } + + void pop(unsigned num_scopes) override { + for (unsigned i = 0; i < num_scopes; i++) { + unsigned lastCnt = fixedCnt.top(); + fixedCnt.pop(); + // Remove fixed values from model + for (unsigned j = fixedValues.size(); j > lastCnt; j--) { + currentModel[queenToY[fixedValues[j - 1]]] = (unsigned) -1; + } + fixedValues.resize(lastCnt); + } + } + + user_propagator_base *fresh(z3::context &) override { + return this; + } +}; \ No newline at end of file diff --git a/examples/userPropagator/user_propagator_created_maximisation.h b/examples/userPropagator/user_propagator_created_maximisation.h new file mode 100644 index 000000000..7ef93b8fe --- /dev/null +++ b/examples/userPropagator/user_propagator_created_maximisation.h @@ -0,0 +1,338 @@ +#pragma once + +#include "common.h" + +class user_propagator_created_maximisation : public z3::user_propagator_base { + + + std::unordered_map argToFcts; + std::unordered_map fctToArgs; + + std::unordered_map currentModel; + z3::expr_vector fixedValues; + std::vector fixedCnt; + + user_propagator_created_maximisation* childPropagator = nullptr; + user_propagator_created_maximisation* parentPropagator = nullptr; + + int board; + int nesting; // Just for logging (0 ... main solver; 1 ... sub-solver) + +public: + + user_propagator_created_maximisation(z3::context &c, user_propagator_created_maximisation* parentPropagator, unsigned board, int nesting) : + z3::user_propagator_base(c), fixedValues(c), parentPropagator(parentPropagator), board(board), nesting(nesting) { + + this->register_fixed(); + this->register_final(); + this->register_created(); + } + + user_propagator_created_maximisation(z3::solver *s, unsigned board) : + z3::user_propagator_base(s), fixedValues(s->ctx()), board(board), nesting(0) { + + this->register_fixed(); + this->register_final(); + this->register_created(); + } + + ~user_propagator_created_maximisation() { + delete childPropagator; + } + + void final() override { + WriteLine("Final (" + to_string(nesting) + ")"); + } + + void push() override { + WriteLine("Push (" + to_string(nesting) + ")"); + fixedCnt.push_back((unsigned) fixedValues.size()); + } + + void pop(unsigned num_scopes) override { + WriteLine("Pop (" + to_string(nesting) + ")"); + for (unsigned i = 0; i < num_scopes; i++) { + unsigned lastCnt = fixedCnt.back(); + fixedCnt.pop_back(); + for (auto j = fixedValues.size(); j > lastCnt; j--) { + currentModel.erase(fixedValues[j - 1]); + } + fixedValues.resize(lastCnt); + } + } + + void checkValidPlacement(std::vector &conflicts, const z3::expr &fct, const z3::expr_vector &args, const std::vector &argValues, int pos) { + unsigned queenId = pos; + unsigned queenPos = argValues[pos]; + z3::expr queenPosExpr = args[pos]; + + if (queenPos >= board) { + z3::expr_vector conflicting(ctx()); + conflicting.push_back(fct); + conflicting.push_back(queenPosExpr); + conflicts.push_back(conflicting); + return; + } + + for (unsigned otherId = 0; otherId < argValues.size(); otherId++) { + if (otherId == pos) + continue; + + unsigned otherPos = argValues[otherId]; + z3::expr otherPosExpr = args[otherId]; + + if (otherPos == (unsigned)-1) + continue; // We apparently do not have this value + + if (queenPos == otherPos) { + z3::expr_vector conflicting(ctx()); + conflicting.push_back(fct); + conflicting.push_back(queenPosExpr); + conflicting.push_back(otherPosExpr); + conflicts.push_back(conflicting); + } + int diffY = abs((int) queenId - (int) otherId); + int diffX = abs((int) queenPos - (int) otherPos); + if (diffX == diffY) { + z3::expr_vector conflicting(ctx()); + conflicting.push_back(fct); + conflicting.push_back(queenPosExpr); + conflicting.push_back(otherPosExpr); + conflicts.push_back(conflicting); + } + } + } + + unsigned getValues(const z3::expr &fct, std::vector &argValues) const { + z3::expr_vector args = fctToArgs.at(fct); + unsigned fixed = 0; + for (const z3::expr &arg: args) { + if (currentModel.contains(arg)) { + argValues.push_back(currentModel.at(arg)); + fixed++; + } + else + argValues.push_back((unsigned) -1); // no value so far + } + return fixed; + } + + + user_propagator_base *fresh(z3::context &ctx) override { + WriteLine("Fresh context"); + childPropagator = new user_propagator_created_maximisation(ctx, this, board, nesting + 1); + return childPropagator; + } + + void fixed(const z3::expr &expr, const z3::expr &value) override { + // Could be optimized! + WriteLine("Fixed (" + to_string(nesting) + ") " + expr.to_string() + " to " + value.to_string()); + unsigned v = value.is_true() ? 1 : (value.is_false() ? 0 : value.get_numeral_uint()); + currentModel[expr] = v; + fixedValues.push_back(expr); + + z3::expr_vector effectedFcts(ctx()); + bool fixedFct = fctToArgs.contains(expr); + + if (fixedFct) { + // fixed the value of a function + effectedFcts.push_back(expr); + } + else { + // fixed the value of a function's argument + effectedFcts = argToFcts.at(expr); + } + + for (const z3::expr& fct : effectedFcts) { + if (!currentModel.contains(fct)) + // we do not know yet whether to expect a valid or invalid placement + continue; + + std::vector values; + unsigned fixedArgsCnt = getValues(fct, values); + bool fctValue = currentModel[fct]; + z3::expr_vector args = fctToArgs.at(fct); + + if (!fctValue) { + // expect invalid placement ... + if (fixedArgsCnt != board) + // we expect an invalid placement, but not all queen positions have been placed yet + return; + std::vector conflicts; + for (unsigned i = 0; i < args.size(); i++) { + if (values[i] != (unsigned)-1) + checkValidPlacement(conflicts, expr, args, values, i); + } + + if (conflicts.empty()) { + // ... but we got a valid one + z3::expr_vector conflicting(ctx()); + conflicting.push_back(fct); + for (const z3::expr &arg: args) { + if (!arg.is_numeral()) + conflicting.push_back(arg); + } + this->conflict(conflicting); + } + else { + // ... and everything is fine; we have at least one conflict + } + } + else { + // expect valid placement ... + std::vector conflicts; + if (fixedFct){ + for (unsigned i = 0; i < args.size(); i++) { + if (values[i] != (unsigned)-1) // check all set queens + checkValidPlacement(conflicts, expr, args, values, i); + } + } + else { + for (unsigned i = 0; i < args.size(); i++) { + if (z3::eq(args[i], expr)) // only check newly fixed values + checkValidPlacement(conflicts, fct, args, values, i); + } + } + if (conflicts.size() > 0) { + // ... but we got an invalid one + for (const z3::expr_vector &conflicting: conflicts) + this->conflict(conflicting); + } + else { + // ... and everything is fine; no conflict + } + } + } + } + +// void fixed(const z3::expr &expr, const z3::expr &value) override { +// WriteLine("Fixed (" + to_string(nesting) + ") " + expr.to_string() + " to " + value.to_string()); +// unsigned v = value.is_true() ? 1 : (value.is_false() ? 0 : value.get_numeral_uint()); +// currentModel[expr] = v; +// fixedValues.push_back(expr); +// +// if (fctToArgs.contains(expr)) { +// // fixed the value of a function +// +// std::vector values; +// unsigned fixedArgsCnt = getValues(expr, values); +// +// if (!v && fixedArgsCnt != board) +// // we expect an invalid placement, but not all queen positions have been placed yet +// return; +// +// z3::expr_vector args = fctToArgs.at(expr); +// +// std::vector conflicts; +// for (unsigned i = 0; i < args.size(); i++) { +// if (values[i] != (unsigned)-1) +// checkValidPlacement(conflicts, expr, args, values, i); +// } +// if (v) { +// //we expected a valid queen placement +// if (conflicts.size() > 0) { +// // ... but we got an invalid one +// for (const z3::expr_vector &conflicting: conflicts) +// this->conflict(conflicting); +// } +// else { +// // everything fine; no conflict +// } +// } +// else { +// // we expect an invalid queen placement +// if (conflicts.empty()) { +// // ... but we got a valid one +// z3::expr_vector conflicting(ctx()); +// conflicting.push_back(expr); +// for (const z3::expr &arg: args) { +// if (!arg.is_numeral()) +// conflicting.push_back(arg); +// } +// this->conflict(conflicting); +// } +// else { +// // everything fine; we have at least one conflict +// } +// } +// } +// else { +// // fixed the value of a function argument +// +// z3::expr_vector effectedFcts = argToFcts.at(expr); +// +// for (const z3::expr& fct : effectedFcts) { +// if (!currentModel.contains(fct)) +// // we do not know yet whether to expect a valid or invalid placement +// continue; +// +// std::vector values; +// unsigned fixedArgsCnt = getValues(fct, values); +// bool fctValue = currentModel[fct]; +// z3::expr_vector args = fctToArgs.at(fct); +// +// if (!fctValue) { +// // expect invalid placement +// if (fixedArgsCnt != board) +// // we expect an invalid placement, but not all queen positions have been placed yet +// return; +// std::vector conflicts; +// for (unsigned i = 0; i < args.size(); i++) { +// if (values[i] != (unsigned)-1) +// checkValidPlacement(conflicts, expr, args, values, i); +// } +// +// if (conflicts.empty()) { +// // ... but we got a valid one +// z3::expr_vector conflicting(ctx()); +// conflicting.push_back(fct); +// for (const z3::expr &arg: args) { +// if (!arg.is_numeral()) +// conflicting.push_back(arg); +// } +// this->conflict(conflicting); +// } +// else { +// // everything fine; we have at least one conflict +// } +// } +// else { +// // expect valid placement +// std::vector conflicts; +// for (unsigned i = 0; i < args.size(); i++) { +// if (z3::eq(args[i], expr)) // only check newly fixed values +// checkValidPlacement(conflicts, fct, args, values, i); +// } +// if (conflicts.size() > 0) { +// // ... but we got an invalid one +// for (const z3::expr_vector &conflicting: conflicts) +// this->conflict(conflicting); +// } +// else { +// // everything fine; no conflict +// } +// } +// } +// } +// } + + void created(const z3::expr &func) override { + WriteLine("Created (" + to_string(nesting) + "): " + func.to_string()); + z3::expr_vector args = func.args(); + for (unsigned i = 0; i < args.size(); i++) { + z3::expr arg = args[i]; + + if (!arg.is_numeral()) { + WriteLine("Registered " + arg.to_string()); + this->add(arg); + } + else { + currentModel[arg] = arg.get_numeral_uint(); + // Skip registering as argument is a fixed BV; + } + + argToFcts.try_emplace(arg, ctx()).first->second.push_back(func); + } + fctToArgs.emplace(std::make_pair(func, args)); + } +}; \ No newline at end of file diff --git a/examples/userPropagator/user_propagator_internal_maximisation.h b/examples/userPropagator/user_propagator_internal_maximisation.h new file mode 100644 index 000000000..7a22270eb --- /dev/null +++ b/examples/userPropagator/user_propagator_internal_maximisation.h @@ -0,0 +1,30 @@ +#pragma once + +#include "user_propagator_with_theory.h" + +class user_propagator_internal_maximisation : public user_propagator_with_theory { + + z3::expr manhattanSum; + +public: + + int best = -1; + + user_propagator_internal_maximisation(z3::solver *s, std::unordered_map &idMapping, unsigned board, z3::expr_vector queens) + : user_propagator_with_theory(s, idMapping, board), + manhattanSum(s->ctx().bv_val(0, queens[0].get_sort().bv_size())) { + for (int i = 1; i < queens.size(); i++) { + manhattanSum = manhattanSum + z3::ite(z3::uge(queens[i], queens[i - 1]), queens[i] - queens[i - 1], queens[i - 1] - queens[i]); + } + } + + void final() override { + + int current = 0; + for (unsigned i = 1; i < board; i++) { + current += abs((signed) currentModel[i] - (signed) currentModel[i - 1]); + } + best = std::max(current, best); + this->propagate(z3::expr_vector(ctx()), z3::ugt(manhattanSum, best)); + } +}; \ No newline at end of file diff --git a/examples/userPropagator/user_propagator_subquery_maximisation.h b/examples/userPropagator/user_propagator_subquery_maximisation.h new file mode 100644 index 000000000..47382c435 --- /dev/null +++ b/examples/userPropagator/user_propagator_subquery_maximisation.h @@ -0,0 +1,51 @@ +#pragma once + +#include "user_propagator.h" + +class user_propagator_subquery_maximisation : public user_propagator { + + z3::expr assertion; + z3::expr_vector queens; + z3::expr manhattanSum; + +public: + + user_propagator_subquery_maximisation(z3::solver *s, std::unordered_map &idMapping, unsigned board, z3::expr_vector queens) + : user_propagator(s, idMapping, board), + assertion(mk_and(s->assertions())), + queens(queens), manhattanSum(s->ctx().bv_val(0, queens[0].get_sort().bv_size())) { + + for (int i = 1; i < queens.size(); i++) { + manhattanSum = manhattanSum + z3::ite(z3::uge(queens[i], queens[i - 1]), queens[i] - queens[i - 1], queens[i - 1] - queens[i]); + } + } + + void final() override { + + int max1 = 0; + for (unsigned i = 1; i < board; i++) { + max1 += abs((signed) currentModel[i] - (signed) currentModel[i - 1]); + } + z3::expr_vector vec(ctx()); + + int max2 = 0; + z3::solver subquery(ctx(), z3::solver::simple()); + + subquery.add(assertion); + subquery.add(z3::ugt(manhattanSum, max1)); + if (subquery.check() == z3::unsat) + return; // model is already maximal + + z3::model counterExample = subquery.get_model(); + + int prev, curr = -1; + + for (int i = 0; i < queens.size(); i++) { + prev = curr; + curr = counterExample.eval(queens[i]).get_numeral_int(); + if (i == 0) continue; + max2 += abs(curr - prev); + } + this->propagate(vec, z3::uge(manhattanSum, max2)); + } +}; \ No newline at end of file diff --git a/examples/userPropagator/user_propagator_with_theory.h b/examples/userPropagator/user_propagator_with_theory.h new file mode 100644 index 000000000..cd3e6f273 --- /dev/null +++ b/examples/userPropagator/user_propagator_with_theory.h @@ -0,0 +1,50 @@ +#pragma once + +#include "user_propagator.h" + +class user_propagator_with_theory : public user_propagator { + +public: + + user_propagator_with_theory(z3::context &c, std::unordered_map &idMapping, unsigned board) + : user_propagator(c, idMapping, board) {} + + user_propagator_with_theory(z3::solver *s, std::unordered_map &idMapping, unsigned board) + : user_propagator(s, idMapping, board) {} + + void fixed(z3::expr const &ast, z3::expr const &value) override { + unsigned queenId = queenToY[ast]; + unsigned queenPos = bvToInt(value); + + if (queenPos >= board) { + z3::expr_vector conflicting(ast.ctx()); + conflicting.push_back(ast); + this->conflict(conflicting); + return; + } + + for (const z3::expr &fixed: fixedValues) { + unsigned otherId = queenToY[fixed]; + unsigned otherPos = currentModel[queenToY[fixed]]; + + if (queenPos == otherPos) { + z3::expr_vector conflicting(ast.ctx()); + conflicting.push_back(ast); + conflicting.push_back(fixed); + this->conflict(conflicting); + continue; + } + int diffY = abs((int) queenId - (int) otherId); + int diffX = abs((int) queenPos - (int) otherPos); + if (diffX == diffY) { + z3::expr_vector conflicting(ast.ctx()); + conflicting.push_back(ast); + conflicting.push_back(fixed); + this->conflict(conflicting); + } + } + + fixedValues.push_back(ast); + currentModel[queenToY[ast]] = queenPos; + } +}; \ No newline at end of file diff --git a/scripts/build-win-signed.yml b/scripts/build-win-signed.yml index bd96889a3..1b0bb111a 100644 --- a/scripts/build-win-signed.yml +++ b/scripts/build-win-signed.yml @@ -18,7 +18,7 @@ jobs: displayName: Build inputs: script: - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{parameters.BuildArchitecture}} && + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{parameters.BuildArchitecture}} && python scripts\mk_win_dist.py --${{parameters.BuildArchitecture}}-only --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index ef73e5f3f..f4b54cba0 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -644,7 +644,7 @@ def mk_gparams_register_modules_internal(h_files_full_path, path): for code in cmds: fout.write('{ param_descrs d; %s(d); gparams::register_global(d); }\n' % code) for (mod, code) in mod_cmds: - fout.write('{ std::function f = []() { auto* d = alloc(param_descrs); %s(*d); return d; }; gparams::register_module("%s", f); }\n' % (code, mod)) + fout.write('{ auto f = []() { auto* d = alloc(param_descrs); %s(*d); return d; }; gparams::register_module("%s", f); }\n' % (code, mod)) for (mod, descr) in mod_descrs: fout.write('gparams::register_module_descr("%s", "%s");\n' % (mod, descr)) fout.write('}\n') diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 7ede9e881..a40bbf869 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 8, 15, 0) + set_version(4, 8, 16, 0) # Z3 Project definition def init_project_def(): diff --git a/scripts/mk_unix_dist.py b/scripts/mk_unix_dist.py index 28d68a01b..97de725e9 100644 --- a/scripts/mk_unix_dist.py +++ b/scripts/mk_unix_dist.py @@ -56,6 +56,7 @@ def display_help(): print(" -f, --force force script to regenerate Makefiles.") print(" --nodotnet do not include .NET bindings in the binary distribution files.") print(" --dotnet-key= sign the .NET assembly with the private key in .") + print(" --arch= set architecture (to arm64) to force arm64 build") print(" --nojava do not include Java bindings in the binary distribution files.") print(" --nopython do not include Python bindings in the binary distribution files.") print(" --githash include git hash in the Zip file.") @@ -72,6 +73,7 @@ def parse_options(): 'nojava', 'nodotnet', 'dotnet-key=', + 'arch=', 'githash', 'nopython' ]) @@ -96,6 +98,11 @@ def parse_options(): JAVA_ENABLED = False elif opt == '--githash': GIT_HASH = True + elif opt == '--arch': + if arg == "arm64": + mk_util.IS_ARCH_ARM64 = True + else: + raise MKException("Invalid architecture directive '%s'. Legal directives: arm64" % arg) else: raise MKException("Invalid command line option '%s'" % opt) set_build_dir(path) @@ -119,6 +126,8 @@ def mk_build_dir(path): opts.append('--git-describe') if PYTHON_ENABLED: opts.append('--python') + if mk_util.IS_ARCH_ARM64: + opts.append('--arm64=true') if subprocess.call(opts) != 0: raise MKException("Failed to generate build directory at '%s'" % path) @@ -172,7 +181,9 @@ def get_os_name(): def get_z3_name(): major, minor, build, revision = get_version() - if sys.maxsize >= 2**32: + if mk_util.IS_ARCH_ARM64: + platform = "arm64" + elif sys.maxsize >= 2**32: platform = "x64" else: platform = "x86" diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 042e6af46..761de8b26 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -69,6 +69,7 @@ IS_WINDOWS=False IS_LINUX=False IS_HURD=False IS_OSX=False +IS_ARCH_ARM64=False IS_FREEBSD=False IS_NETBSD=False IS_OPENBSD=False @@ -114,6 +115,7 @@ ALWAYS_DYNAMIC_BASE=False FPMATH="Default" FPMATH_FLAGS="-mfpmath=sse -msse -msse2" +FPMATH_ENABLED=getenv("FPMATH_ENABLED", "True") def check_output(cmd): @@ -277,7 +279,13 @@ def test_gmp(cc): def test_fpmath(cc): - global FPMATH_FLAGS + global FPMATH_FLAGS, IS_ARCH_ARM64, IS_OSX + if FPMATH_ENABLED == "False": + FPMATH_FLAGS="" + return "Disabled" + if IS_ARCH_ARM64 and IS_OSX: + FPMATH_FLAGS = "" + return "Disabled-ARM64" if is_verbose(): print("Testing floating point support...") t = TempFile('tstsse.cpp') @@ -616,8 +624,12 @@ elif os.name == 'posix': LINUX_X64=True else: LINUX_X64=False - + +if os.name == 'posix' and os.uname()[4] == 'arm64': + IS_ARCH_ARM64 = True + + def display_help(exit_code): print("mk_make.py: Z3 Makefile generator\n") print("This script generates the Makefile for the Z3 theorem prover.") @@ -640,6 +652,7 @@ def display_help(exit_code): print(" -x, --x64 create 64 binary when using Visual Studio.") else: print(" --x86 force 32-bit x86 build on x64 systems.") + print(" --arm64= forcearm64 bit build on/off (supported for Darwin).") print(" -m, --makefiles generate only makefiles.") if IS_WINDOWS: print(" -v, --vsproj generate Visual Studio Project Files.") @@ -682,11 +695,11 @@ def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM global DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, JAVA_ENABLED, ML_ENABLED, STATIC_LIB, STATIC_BIN, PREFIX, GMP, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH, GIT_DESCRIBE, PYTHON_INSTALL_ENABLED, PYTHON_ENABLED global LINUX_X64, SLOW_OPTIMIZE, LOG_SYNC, SINGLE_THREADED - global GUARD_CF, ALWAYS_DYNAMIC_BASE + global GUARD_CF, ALWAYS_DYNAMIC_BASE, IS_ARCH_ARM64 try: options, remainder = getopt.gnu_getopt(sys.argv[1:], - 'b:df:sxhmcvtnp:gj', - ['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', 'guardcf', + 'b:df:sxa:hmcvtnp:gj', + ['build=', 'debug', 'silent', 'x64', 'arm64=', 'help', 'makefiles', 'showcpp', 'vsproj', 'guardcf', 'trace', 'dotnet', 'dotnet-key=', 'staticlib', 'prefix=', 'gmp', 'java', 'parallel=', 'gprof', 'js', 'githash=', 'git-describe', 'x86', 'ml', 'optimize', 'pypkgdir=', 'python', 'staticbin', 'log-sync', 'single-threaded']) except: @@ -709,6 +722,8 @@ def parse_options(): VS_X64 = True elif opt in ('--x86'): LINUX_X64=False + elif opt in ('--arm64'): + IS_ARCH_ARM64 = arg in ('true','on','True','TRUE') elif opt in ('-h', '--help'): display_help(0) elif opt in ('-m', '--makefiles'): @@ -1678,6 +1693,7 @@ class DotNetDLLComponent(Component): netstandard1.4 + 8.0 $(DefineConstants);DOTNET_CORE portable Microsoft.Z3 @@ -1995,7 +2011,7 @@ class MLComponent(Component): LIBZ3 = LIBZ3 + ' ' + ' '.join(map(lambda x: '-cclib ' + x, LDFLAGS.split())) - stubs_install_path = '$$(%s printconf path)/stublibs' % OCAMLFIND + stubs_install_path = '$$(%s printconf destdir)/stublibs' % OCAMLFIND if not STATIC_LIB: loadpath = '-ccopt -L' + stubs_install_path dllpath = '-dllpath ' + stubs_install_path @@ -2076,7 +2092,7 @@ class MLComponent(Component): out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml.cmxa')))) out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml.cmxs')))) out.write(' %s' % ((os.path.join(self.sub_dir, 'dllz3ml')))) - if is_windows() or is_cygwin_mingw(): + if is_windows() or is_cygwin_mingw() or is_msys2(): out.write('.dll') else: out.write('.so') # .so also on OSX! @@ -2426,7 +2442,7 @@ def mk_config(): if ONLY_MAKEFILES: return config = open(os.path.join(BUILD_DIR, 'config.mk'), 'w') - global CXX, CC, GMP, GUARD_CF, STATIC_BIN, GIT_HASH, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS, LOG_SYNC, SINGLE_THREADED + global CXX, CC, GMP, GUARD_CF, STATIC_BIN, GIT_HASH, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS, LOG_SYNC, SINGLE_THREADED, IS_ARCH_ARM64 if IS_WINDOWS: CXXFLAGS = '/nologo /Zi /D WIN32 /D _WINDOWS /EHsc /GS /Gd /std:c++17' config.write( @@ -2624,13 +2640,18 @@ def mk_config(): SLIBFLAGS = '%s -m32' % SLIBFLAGS if TRACE or DEBUG_MODE: CPPFLAGS = '%s -D_TRACE' % CPPFLAGS - if is_cygwin_mingw(): + if is_cygwin_mingw() or is_msys2(): # when cross-compiling with MinGW, we need to statically link its standard libraries # and to make it create an import library. SLIBEXTRAFLAGS = '%s -static-libgcc -static-libstdc++ -Wl,--out-implib,libz3.dll.a' % SLIBEXTRAFLAGS LDFLAGS = '%s -static-libgcc -static-libstdc++' % LDFLAGS if sysname == 'Linux' and machine.startswith('armv7') or machine.startswith('armv8'): CXXFLAGS = '%s -fpic' % CXXFLAGS + if IS_OSX and IS_ARCH_ARM64: + print("Setting arm64") + CXXFLAGS = '%s -arch arm64' % CXXFLAGS + LDFLAGS = '%s -arch arm64' % LDFLAGS + SLIBEXTRAFLAGS = '%s -arch arm64' % SLIBEXTRAFLAGS config.write('PREFIX=%s\n' % PREFIX) config.write('CC=%s\n' % CC) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 08b3501a0..0add769b9 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -1,5 +1,9 @@ variables: - ReleaseVersion: '4.8.15' + + Major: '4' + Minor: '8' + Patch: '16' + NightlyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId)-$(Build.DefinitionName) stages: - stage: Build @@ -19,6 +23,21 @@ stages: artifactName: 'Mac' targetPath: $(Build.ArtifactStagingDirectory) + + - job: MacArm64 + displayName: "Mac ARM64 Build" + pool: + vmImage: "macOS-latest" + steps: + - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 + - script: git clone https://github.com/z3prover/z3test z3test + - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. + - task: PublishPipelineArtifact@1 + inputs: + artifactName: 'MacArm64' + targetPath: $(Build.ArtifactStagingDirectory) + + - job: Ubuntu displayName: "Ubuntu build" pool: @@ -92,7 +111,7 @@ stages: - task: CmdLine@2 inputs: script: - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86 & + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86 & python scripts\mk_win_dist.py --x86-only --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk @@ -115,7 +134,7 @@ stages: - task: CmdLine@2 inputs: script: - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 & + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 & python scripts\mk_win_dist.py --x64-only --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk @@ -130,6 +149,8 @@ stages: targetPath: $(Build.ArtifactStagingDirectory) artifactName: 'Windows64' + + - stage: Package jobs: - job: NuGet64 @@ -158,6 +179,11 @@ stages: inputs: artifact: 'Mac' path: $(Agent.TempDirectory)\package + - task: DownloadPipelineArtifact@2 + displayName: 'Download macOS Arm64 Build' + inputs: + artifact: 'MacArm64' + path: $(Agent.TempDirectory)\package - task: NuGetToolInstaller@0 inputs: versionSpec: 5.x @@ -170,7 +196,7 @@ stages: workingDirectory: $(Agent.TempDirectory)\package arguments: $(Agent.TempDirectory)\package - $(ReleaseVersion) + $(NightlyVersion) $(Build.Repository.Uri) $(Build.SourceBranchName) $(Build.SourceVersion) @@ -180,13 +206,18 @@ stages: displayName: 'NuGet Pack Symbols' inputs: command: custom - arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.sym.nuspec -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' + versioningScheme: byPrereleaseNumber + majorVersion: $(Major) + minorVersion: $(Minor) + 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' FolderPath: $(Build.ArtifactStagingDirectory) - Pattern: Microsoft.Z3.$(ReleaseVersion).nupkg + Pattern: Microsoft.Z3.$(NightlyVersion).nupkg signConfigType: 'inlineSignParams' inlineOperation: | [ @@ -209,11 +240,12 @@ stages: MaxConcurrency: '50' MaxRetryAttempts: '5' - task: EsrpCodeSigning@1 + continueOnError: true displayName: 'Sign Symbol Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' FolderPath: $(Build.ArtifactStagingDirectory) - Pattern: Microsoft.Z3.$(ReleaseVersion).snupkg + Pattern: Microsoft.Z3.$(NightlyVersion).snupkg signConfigType: 'inlineSignParams' inlineOperation: | [ @@ -268,7 +300,7 @@ stages: workingDirectory: $(Agent.TempDirectory)\package arguments: $(Agent.TempDirectory)\package - $(ReleaseVersion) + $(NightlyVersion) $(Build.Repository.Uri) $(Build.SourceBranchName) $(Build.SourceVersion) @@ -279,13 +311,18 @@ stages: displayName: 'NuGet Pack Symbols' inputs: command: custom - arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.x86.sym.nuspec -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' + versioningScheme: byPrereleaseNumber + majorVersion: $(Major) + minorVersion: $(Minor) + 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' FolderPath: $(Build.ArtifactStagingDirectory) - Pattern: Microsoft.Z3.x86.$(ReleaseVersion).nupkg + Pattern: Microsoft.Z3.x86.$(NightlyVersion).nupkg signConfigType: 'inlineSignParams' inlineOperation: | [ @@ -308,11 +345,12 @@ stages: MaxConcurrency: '50' MaxRetryAttempts: '5' - task: EsrpCodeSigning@1 + continueOnError: true displayName: 'Sign Symbol Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' FolderPath: $(Build.ArtifactStagingDirectory) - Pattern: Microsoft.Z3.x86.$(ReleaseVersion).snupkg + Pattern: Microsoft.Z3.x86.$(NightlyVersion).snupkg signConfigType: 'inlineSignParams' inlineOperation: | [ @@ -361,7 +399,12 @@ stages: inputs: artifactName: 'Mac' targetPath: $(Agent.TempDirectory) - - script: cd $(Agent.TempDirectory); mkdir osx-bin; cd osx-bin; unzip ../*osx*.zip + - task: DownloadPipelineArtifact@2 + inputs: + artifactName: 'MacArm64' + targetPath: $(Agent.TempDirectory) + - script: cd $(Agent.TempDirectory); mkdir osx-x64-bin; cd osx-x64-bin; unzip ../*x64-osx*.zip + - script: cd $(Agent.TempDirectory); mkdir osx-arm64-bin; cd osx-arm64-bin; unzip ../*arm64-osx*.zip - script: cd $(Agent.TempDirectory); mkdir linux-bin; cd linux-bin; unzip ../*glibc*.zip - script: cd $(Agent.TempDirectory); mkdir win32-bin; cd win32-bin; unzip ../*x86-win*.zip - script: cd $(Agent.TempDirectory); mkdir win64-bin; cd win64-bin; unzip ../*x64-win*.zip @@ -371,16 +414,19 @@ stages: - script: cd src/api/python; echo $(Agent.TempDirectory)/linux-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/win32-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/win64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel - - script: cd src/api/python; echo $(Agent.TempDirectory)/osx-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel + - script: cd src/api/python; echo $(Agent.TempDirectory)/osx-x64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel + - script: cd src/api/python; echo $(Agent.TempDirectory)/osx-arm64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel - task: PublishPipelineArtifact@0 inputs: artifactName: 'Python packages' targetPath: src/api/python/dist + - stage: Deployment jobs: - job: Deploy displayName: "Deploy into GitHub" + continueOnError: true pool: vmImage: "ubuntu-latest" steps: @@ -399,6 +445,11 @@ stages: inputs: artifactName: 'Mac' targetPath: tmp + - task: DownloadPipelineArtifact@2 + displayName: "Download MacArm64" + inputs: + artifactName: 'MacArm64' + targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download Ubuntu" inputs: @@ -425,6 +476,7 @@ stages: artifactName: 'NuGet32' targetPath: tmp - task: GitHubRelease@0 + continueOnError: true inputs: gitHubConnection: Z3GitHub repositoryName: 'Z3Prover/z3' @@ -433,6 +485,7 @@ stages: tagSource: 'manual' tag: 'Nightly' - task: GitHubRelease@0 + continueOnError: true inputs: gitHubConnection: Z3GitHub repositoryName: 'Z3Prover/z3' @@ -448,5 +501,41 @@ stages: isDraft: false isPreRelease: true +- stage: NugetPublishNightly + jobs: + # Publish to nightly feed on Azure + - job: NuGetPublishNightly + displayName: "Push nuget packages to Azure Feed" + steps: + - task: NuGetAuthenticate@0 + displayName: 'NuGet Authenticate' + - task: NuGetToolInstaller@0 + inputs: + versionSpec: 5.x + checkLatest: false + - task: DownloadPipelineArtifact@2 + displayName: 'Download NuGet x86 Package' + inputs: + artifact: 'NuGet32' + path: $(Agent.TempDirectory)/x86 + - task: DownloadPipelineArtifact@2 + displayName: 'Download NuGet x64 Package' + inputs: + artifact: 'NuGet' + path: $(Agent.TempDirectory)/x64 + - task: NuGetCommand@2 + displayName: 'NuGet Nightly x64 push' + inputs: + command: push + publishVstsFeed: 'Z3Nightly' + packagesToPush: $(Agent.TempDirectory)/x64/*.nupkg + allowPackageConflicts: true + - task: NuGetCommand@2 + displayName: 'NuGet Nightly x86 push' + inputs: + command: push + publishVstsFeed: 'Z3Nightly' + packagesToPush: $(Agent.TempDirectory)/x86/*.nupkg + allowPackageConflicts: true # TBD: run regression tests on generated binaries. diff --git a/scripts/release.yml b/scripts/release.yml index 8e69be9b0..cd860d177 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -6,7 +6,7 @@ trigger: none variables: - ReleaseVersion: '4.8.15' + ReleaseVersion: '4.8.16' stages: @@ -155,8 +155,8 @@ stages: - stage: Package jobs: - - job: NuGetPackage - displayName: "NuGet packaging" + - job: NuGet64 + displayName: "NuGet 64 packaging" pool: vmImage: "windows-latest" steps: @@ -171,11 +171,6 @@ stages: inputs: artifact: 'WindowsBuild-x64' path: $(Agent.TempDirectory)\package - - task: DownloadPipelineArtifact@2 - displayName: 'Download Win32 Build' - inputs: - artifact: 'WindowsBuild-x86' - path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download Ubuntu Build' inputs: @@ -186,6 +181,10 @@ stages: inputs: artifact: 'macOSBuild' path: $(Agent.TempDirectory)\package + - task: NuGetToolInstaller@0 + inputs: + versionSpec: 5.x + checkLatest: false - task: PythonScript@0 displayName: 'Python: assemble files' inputs: @@ -200,10 +199,6 @@ stages: $(Build.SourceVersion) $(Build.SourcesDirectory) symbols - - task: NuGetToolInstaller@0 - inputs: - versionSpec: 5.x - checkLatest: false - task: NugetCommand@2 displayName: 'NuGet Pack Symbols' inputs: @@ -266,7 +261,107 @@ stages: - task: PublishPipelineArtifact@1 inputs: targetPath: $(Build.ArtifactStagingDirectory) - artifactName: 'NuGetPackage' + artifactName: 'NuGet' + + - job: NuGet32 + displayName: "NuGet 32 packaging" + pool: + vmImage: "windows-latest" + steps: + - powershell: write-host $(System.DefinitionId) + displayName: 'System.DefinitionId' + - powershell: write-host $(Build.BuildId) + displayName: 'Build.BuildId' + - powershell: write-host $(System.TeamProjectId) + displayName: 'System.TeamProjectId' + - task: DownloadPipelineArtifact@2 + displayName: 'Download Win32 Build' + inputs: + artifact: 'WindowsBuild-x86' + path: $(Agent.TempDirectory)\package + - task: NuGetToolInstaller@0 + inputs: + versionSpec: 5.x + checkLatest: false + - task: PythonScript@0 + displayName: 'Python: assemble files' + inputs: + scriptSource: 'filepath' + scriptPath: scripts\mk_nuget_task.py + workingDirectory: $(Agent.TempDirectory)\package + arguments: + $(Agent.TempDirectory)\package + $(ReleaseVersion) + $(Build.Repository.Uri) + $(Build.SourceBranchName) + $(Build.SourceVersion) + $(Build.SourcesDirectory) + symbols + x86 + - task: NugetCommand@2 + displayName: 'NuGet Pack Symbols' + inputs: + command: custom + arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.x86.sym.nuspec -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' + - task: EsrpCodeSigning@1 + displayName: 'Sign Package' + inputs: + ConnectedServiceName: 'z3-esrp-signing-2' + FolderPath: $(Build.ArtifactStagingDirectory) + Pattern: Microsoft.Z3.x86.$(ReleaseVersion).nupkg + signConfigType: 'inlineSignParams' + inlineOperation: | + [ + { + "KeyCode" : "CP-401405", + "OperationCode" : "NuGetSign", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-401405", + "OperationCode" : "NuGetVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + SessionTimeout: '60' + MaxConcurrency: '50' + MaxRetryAttempts: '5' + - task: EsrpCodeSigning@1 + displayName: 'Sign Symbol Package' + inputs: + ConnectedServiceName: 'z3-esrp-signing-2' + FolderPath: $(Build.ArtifactStagingDirectory) + Pattern: Microsoft.Z3.x86.$(ReleaseVersion).snupkg + signConfigType: 'inlineSignParams' + inlineOperation: | + [ + { + "KeyCode" : "CP-401405", + "OperationCode" : "NuGetSign", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-401405", + "OperationCode" : "NuGetVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + SessionTimeout: '60' + MaxConcurrency: '50' + MaxRetryAttempts: '5' + - task: PublishPipelineArtifact@1 + inputs: + targetPath: $(Build.ArtifactStagingDirectory) + artifactName: 'NuGet32' + - job: PythonPackage displayName: "Python packaging" @@ -350,9 +445,14 @@ stages: artifact: 'WindowsBuild-x64' path: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 - displayName: 'Download NuGet Package' + displayName: 'Download NuGet64 Package' inputs: - artifact: 'NuGetPackage' + artifact: 'NuGet' + path: $(Agent.TempDirectory) + - task: DownloadPipelineArtifact@2 + displayName: 'Download NuGet32 Package' + inputs: + artifact: 'NuGet32' path: $(Agent.TempDirectory) - task: GitHubRelease@0 inputs: diff --git a/scripts/update_api.py b/scripts/update_api.py index 2daf0a7a7..d4d1ab0e0 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1820,13 +1820,13 @@ _error_handler_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_uint) _lib.Z3_set_error_handler.restype = None _lib.Z3_set_error_handler.argtypes = [ContextObj, _error_handler_type] -push_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p) -pop_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_uint) +push_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p) +pop_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint) fresh_eh_type = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) -fixed_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p) +fixed_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) final_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p) -eq_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint) +eq_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) _lib.Z3_solver_propagate_init.restype = None _lib.Z3_solver_propagate_init.argtypes = [ContextObj, SolverObj, ctypes.c_void_p, push_eh_type, pop_eh_type, fresh_eh_type] diff --git a/src/api/api_array.cpp b/src/api/api_array.cpp index c1ea4729e..7aa3a87bf 100644 --- a/src/api/api_array.cpp +++ b/src/api/api_array.cpp @@ -309,6 +309,22 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_sort Z3_API Z3_get_array_sort_domain_n(Z3_context c, Z3_sort t, unsigned idx) { + Z3_TRY; + LOG_Z3_get_array_sort_domain_n(c, t, idx); + RESET_ERROR_CODE(); + CHECK_VALID_AST(t, nullptr); + if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && + to_sort(t)->get_decl_kind() == ARRAY_SORT && + get_array_arity(to_sort(t)) > idx) { + Z3_sort r = reinterpret_cast(get_array_domain(to_sort(t), idx)); + RETURN_Z3(r); + } + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); + RETURN_Z3(nullptr); + Z3_CATCH_RETURN(nullptr); + } + Z3_sort Z3_API Z3_get_array_sort_range(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_array_sort_range(c, t); diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 87e218d99..9f9039378 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -164,7 +164,7 @@ extern "C" { return; } recfun_replace replace(m); - p.set_definition(replace, pd, true, n, _vars.data(), abs_body); + p.set_definition(replace, pd, false, n, _vars.data(), abs_body); Z3_CATCH; } @@ -355,7 +355,7 @@ extern "C" { return mk_c(c)->mk_external_string(buffer.str()); } else { - return mk_c(c)->mk_external_string(_s.bare_str()); + return mk_c(c)->mk_external_string(_s.str()); } Z3_CATCH_RETURN(""); } @@ -1224,15 +1224,21 @@ extern "C" { case OP_RE_PLUS: return Z3_OP_RE_PLUS; case OP_RE_STAR: return Z3_OP_RE_STAR; case OP_RE_OPTION: return Z3_OP_RE_OPTION; + case OP_RE_RANGE: return Z3_OP_RE_RANGE; case OP_RE_CONCAT: return Z3_OP_RE_CONCAT; case OP_RE_UNION: return Z3_OP_RE_UNION; case OP_RE_DIFF: return Z3_OP_RE_DIFF; - case OP_RE_POWER: return Z3_OP_RE_POWER; case OP_RE_INTERSECT: return Z3_OP_RE_INTERSECT; case OP_RE_LOOP: return Z3_OP_RE_LOOP; - case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET; - //case OP_RE_FULL_CHAR_SET: return Z3_OP_RE_FULL_SET; + case OP_RE_POWER: return Z3_OP_RE_POWER; + case OP_RE_COMPLEMENT: return Z3_OP_RE_COMPLEMENT; case OP_RE_EMPTY_SET: return Z3_OP_RE_EMPTY_SET; + + case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET; + case OP_RE_FULL_CHAR_SET: return Z3_OP_RE_FULL_CHAR_SET; + case OP_RE_OF_PRED: return Z3_OP_RE_OF_PRED; + case OP_RE_REVERSE: return Z3_OP_RE_REVERSE; + case OP_RE_DERIVATIVE: return Z3_OP_RE_DERIVATIVE; default: return Z3_OP_INTERNAL; } @@ -1323,6 +1329,9 @@ extern "C" { } } + if (mk_c(c)->recfun().get_family_id() == _d->get_family_id()) + return Z3_OP_RECURSIVE; + return Z3_OP_UNINTERPRETED; Z3_CATCH_RETURN(Z3_OP_UNINTERPRETED); } diff --git a/src/api/api_log.cpp b/src/api/api_log.cpp index c1864ae8c..ed5f68e8a 100644 --- a/src/api/api_log.cpp +++ b/src/api/api_log.cpp @@ -88,7 +88,7 @@ void Sy(Z3_symbol sym) { *g_z3_log << "# " << s.get_num(); } else { - *g_z3_log << "$ |" << ll_escaped{s.bare_str()} << '|'; + *g_z3_log << "$ |" << ll_escaped{s.str().c_str()} << '|'; } *g_z3_log << std::endl; } diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index a7c42df4f..d64589cde 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -315,6 +315,17 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_ast Z3_API Z3_mk_re_power(Z3_context c, Z3_ast r, unsigned n) { + Z3_TRY; + LOG_Z3_mk_re_power(c, r, n); + RESET_ERROR_CODE(); + app* a = mk_c(c)->sutil().re.mk_power(to_expr(r), n); + mk_c(c)->save_ast_trail(a); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(nullptr); + } + + MK_UNARY(Z3_mk_re_plus, mk_c(c)->get_seq_fid(), OP_RE_PLUS, SKIP); MK_UNARY(Z3_mk_re_star, mk_c(c)->get_seq_fid(), OP_RE_STAR, SKIP); MK_UNARY(Z3_mk_re_option, mk_c(c)->get_seq_fid(), OP_RE_OPTION, SKIP); diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 8e8360cd3..05b125546 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -114,7 +114,7 @@ extern "C" { } solver2smt2_pp::solver2smt2_pp(ast_manager& m, const std::string& file): - m_pp_util(m), m_out(file), m_tracked(m) { + m_pp_util(m), m_out(file, std::ofstream::trunc | std::ofstream::out), m_tracked(m) { if (!m_out) { throw default_exception("could not open " + file + " for output"); } @@ -564,7 +564,7 @@ extern "C" { init_solver(c, s); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); - expr_ref_vector trail = to_solver_ref(s)->get_trail(); + expr_ref_vector trail = to_solver_ref(s)->get_trail(UINT_MAX); for (expr* f : trail) { v->m_ast_vector.push_back(f); } @@ -883,8 +883,8 @@ extern "C" { Z3_TRY; RESET_ERROR_CODE(); init_solver(c, s); - user_propagator::push_eh_t _push = push_eh; - user_propagator::pop_eh_t _pop = pop_eh; + user_propagator::push_eh_t _push = (void(*)(void*,user_propagator::callback*)) push_eh; + user_propagator::pop_eh_t _pop = (void(*)(void*,user_propagator::callback*,unsigned)) pop_eh; user_propagator::fresh_eh_t _fresh = [=](void * user_ctx, ast_manager& m, user_propagator::context_obj*& _ctx) { ast_context_params params; params.set_foreign_manager(&m); @@ -902,7 +902,7 @@ extern "C" { Z3_fixed_eh fixed_eh) { Z3_TRY; RESET_ERROR_CODE(); - user_propagator::fixed_eh_t _fixed = (void(*)(void*,user_propagator::callback*,unsigned,expr*))fixed_eh; + user_propagator::fixed_eh_t _fixed = (void(*)(void*,user_propagator::callback*,expr*,expr*))fixed_eh; to_solver_ref(s)->user_propagate_register_fixed(_fixed); Z3_CATCH; } @@ -924,7 +924,7 @@ extern "C" { Z3_eq_eh eq_eh) { Z3_TRY; RESET_ERROR_CODE(); - user_propagator::eq_eh_t _eq = (void(*)(void*,user_propagator::callback*,unsigned,unsigned))eq_eh; + user_propagator::eq_eh_t _eq = (void(*)(void*,user_propagator::callback*,expr*,expr*))eq_eh; to_solver_ref(s)->user_propagate_register_eq(_eq); Z3_CATCH; } @@ -935,43 +935,54 @@ extern "C" { Z3_eq_eh diseq_eh) { Z3_TRY; RESET_ERROR_CODE(); - user_propagator::eq_eh_t _diseq = (void(*)(void*,user_propagator::callback*,unsigned,unsigned))diseq_eh; + user_propagator::eq_eh_t _diseq = (void(*)(void*,user_propagator::callback*,expr*,expr*))diseq_eh; to_solver_ref(s)->user_propagate_register_diseq(_diseq); Z3_CATCH; } - unsigned Z3_API Z3_solver_propagate_register(Z3_context c, Z3_solver s, Z3_ast e) { + void Z3_API Z3_solver_propagate_register(Z3_context c, Z3_solver s, Z3_ast e) { Z3_TRY; LOG_Z3_solver_propagate_register(c, s, e); RESET_ERROR_CODE(); - return to_solver_ref(s)->user_propagate_register_expr(to_expr(e)); - Z3_CATCH_RETURN(0); + to_solver_ref(s)->user_propagate_register_expr(to_expr(e)); + Z3_CATCH; } - unsigned Z3_API Z3_solver_propagate_register_cb(Z3_context c, Z3_solver_callback s, Z3_ast e) { + void Z3_API Z3_solver_propagate_register_cb(Z3_context c, Z3_solver_callback s, Z3_ast e) { Z3_TRY; LOG_Z3_solver_propagate_register_cb(c, s, e); RESET_ERROR_CODE(); - return reinterpret_cast(s)->register_cb(to_expr(e)); - Z3_CATCH_RETURN(0); + reinterpret_cast(s)->register_cb(to_expr(e)); + Z3_CATCH; } - void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback s, unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, Z3_ast conseq) { + void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback s, unsigned num_fixed, Z3_ast const* fixed_ids, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq) { Z3_TRY; LOG_Z3_solver_propagate_consequence(c, s, num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, conseq); RESET_ERROR_CODE(); - reinterpret_cast(s)->propagate_cb(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, to_expr(conseq)); + expr* const * _fixed_ids = (expr* const*) fixed_ids; + expr* const * _eq_lhs = (expr*const*) eq_lhs; + expr* const * _eq_rhs = (expr*const*) eq_rhs; + reinterpret_cast(s)->propagate_cb(num_fixed, _fixed_ids, num_eqs, _eq_lhs, _eq_rhs, to_expr(conseq)); Z3_CATCH; } void Z3_API Z3_solver_propagate_created(Z3_context c, Z3_solver s, Z3_created_eh created_eh) { Z3_TRY; RESET_ERROR_CODE(); - user_propagator::created_eh_t c = (void(*)(void*, user_propagator::callback*, expr*, unsigned))created_eh; + user_propagator::created_eh_t c = (void(*)(void*, user_propagator::callback*, expr*))created_eh; to_solver_ref(s)->user_propagate_register_created(c); Z3_CATCH; } + void Z3_API Z3_solver_propagate_decide(Z3_context c, Z3_solver s, Z3_decide_eh decide_eh) { + Z3_TRY; + RESET_ERROR_CODE(); + user_propagator::decide_eh_t c = (void(*)(void*, user_propagator::callback*, expr**, unsigned*, lbool*))decide_eh; + to_solver_ref(s)->user_propagate_register_decide(c); + Z3_CATCH; + } + Z3_func_decl Z3_API Z3_solver_propagate_declare(Z3_context c, Z3_symbol name, unsigned n, Z3_sort* domain, Z3_sort range) { Z3_TRY; LOG_Z3_solver_propagate_declare(c, name, n, domain, range); diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 45073cdb1..f67a373dd 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -331,8 +331,8 @@ extern "C" { if (idx >= mk_c(c)->num_tactics()) { SET_ERROR_CODE(Z3_IOB, nullptr); return ""; - } - return mk_c(c)->get_tactic(idx)->get_name().bare_str(); + } + return mk_c(c)->mk_external_string(mk_c(c)->get_tactic(idx)->get_name().str().c_str()); Z3_CATCH_RETURN(""); } @@ -352,7 +352,7 @@ extern "C" { SET_ERROR_CODE(Z3_IOB, nullptr); return ""; } - return mk_c(c)->get_probe(idx)->get_name().bare_str(); + return mk_c(c)->mk_external_string(mk_c(c)->get_probe(idx)->get_name().str().c_str()); Z3_CATCH_RETURN(""); } diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 80432450d..04e0b78e2 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -25,6 +25,7 @@ Notes: #include #include #include +#include #include #include #include @@ -155,9 +156,10 @@ namespace z3 { class context { private: + friend class user_propagator_base; bool m_enable_exceptions; rounding_mode m_rounding_mode; - Z3_context m_ctx; + Z3_context m_ctx = nullptr; void init(config & c) { set_context(Z3_mk_context_rc(c)); } @@ -173,7 +175,6 @@ namespace z3 { context(context const &) = delete; context & operator=(context const &) = delete; - friend class scoped_context; context(Z3_context c) { set_context(c); } void detach() { m_ctx = nullptr; } public: @@ -394,14 +395,6 @@ namespace z3 { expr_vector parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls); }; - class scoped_context final { - context m_ctx; - public: - scoped_context(Z3_context c): m_ctx(c) {} - ~scoped_context() { m_ctx.detach(); } - context& operator()() { return m_ctx; } - }; - template class array { @@ -509,7 +502,7 @@ namespace z3 { ast(context & c):object(c), m_ast(0) {} ast(context & c, Z3_ast n):object(c), m_ast(n) { Z3_inc_ref(ctx(), m_ast); } ast(ast const & s) :object(s), m_ast(s.m_ast) { Z3_inc_ref(ctx(), m_ast); } - ~ast() { if (m_ast) Z3_dec_ref(*m_ctx, m_ast); } + ~ast() { if (m_ast) { Z3_dec_ref(*m_ctx, m_ast); } } operator Z3_ast() const { return m_ast; } operator bool() const { return m_ast != 0; } ast & operator=(ast const & s) { @@ -550,7 +543,7 @@ namespace z3 { ~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[](int i) const { assert(0 <= i); Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } + 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); } @@ -1157,6 +1150,19 @@ namespace z3 { \pre i < num_args() */ expr arg(unsigned i) const { Z3_ast r = Z3_get_app_arg(ctx(), *this, i); check_error(); return expr(ctx(), r); } + /** + \brief Return a vector of all the arguments of this application. + This method assumes the expression is an application. + + \pre is_app() + */ + expr_vector args() const { + expr_vector vec(ctx()); + unsigned argCnt = num_args(); + for (unsigned i = 0; i < argCnt; i++) + vec.push_back(arg(i)); + return vec; + } /** \brief Return the 'body' of this quantifier. @@ -2327,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(); @@ -2335,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(); @@ -2343,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(); @@ -2351,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(); @@ -2359,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(); @@ -2367,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(); @@ -2376,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(); @@ -2405,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 { @@ -2442,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; @@ -2581,9 +2587,9 @@ namespace z3 { friend std::ostream & operator<<(std::ostream & out, model const & m); - std::string to_string() const { return std::string(Z3_model_to_string(ctx(), m_model)); } + std::string to_string() const { return m_model ? std::string(Z3_model_to_string(ctx(), m_model)) : "null"; } }; - inline std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } + inline std::ostream & operator<<(std::ostream & out, model const & m) { return out << m.to_string(); } class stats : public object { Z3_stats m_stats; @@ -2627,7 +2633,8 @@ namespace z3 { Z3_solver m_solver; void init(Z3_solver s) { m_solver = s; - Z3_solver_inc_ref(ctx(), s); + if (s) + Z3_solver_inc_ref(ctx(), s); } public: struct simple {}; @@ -2636,7 +2643,7 @@ namespace z3 { solver(context & c, simple):object(c) { init(Z3_mk_simple_solver(c)); } solver(context & c, Z3_solver s):object(c) { init(s); } solver(context & c, char const * logic):object(c) { init(Z3_mk_solver_for_logic(c, c.str_symbol(logic))); } - solver(context & c, solver const& src, translate): object(c) { init(Z3_solver_translate(src.ctx(), src, c)); } + solver(context & c, solver const& src, translate): object(c) { Z3_solver s = Z3_solver_translate(src.ctx(), src, c); check_error(); init(s); } solver(solver const & s):object(s) { init(s.m_solver); } ~solver() { Z3_solver_dec_ref(ctx(), m_solver); } operator Z3_solver() const { return m_solver; } @@ -2764,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; } @@ -2998,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) { @@ -3797,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(); @@ -3932,56 +3939,65 @@ namespace z3 { class user_propagator_base { - typedef std::function fixed_eh_t; + typedef std::function fixed_eh_t; typedef std::function final_eh_t; - typedef std::function eq_eh_t; - typedef std::function created_eh_t; + typedef std::function eq_eh_t; + typedef std::function created_eh_t; + typedef std::function decide_eh_t; final_eh_t m_final_eh; eq_eh_t m_eq_eh; fixed_eh_t m_fixed_eh; created_eh_t m_created_eh; + decide_eh_t m_decide_eh; solver* s; - Z3_context c; - Z3_solver_callback cb { nullptr }; + context* c; + std::vector subcontexts; - Z3_context ctx() { - return c ? c : (Z3_context)s->ctx(); - } + Z3_solver_callback cb { nullptr }; struct scoped_cb { user_propagator_base& p; scoped_cb(void* _p, Z3_solver_callback cb):p(*static_cast(_p)) { p.cb = cb; } - ~scoped_cb() { - p.cb = nullptr; + ~scoped_cb() { + p.cb = nullptr; } }; - static void push_eh(void* p) { + static void push_eh(void* _p, Z3_solver_callback cb) { + user_propagator_base* p = static_cast(_p); + scoped_cb _cb(p, cb); static_cast(p)->push(); } - static void pop_eh(void* p, unsigned num_scopes) { - static_cast(p)->pop(num_scopes); - } - - static void* fresh_eh(void* p, Z3_context ctx) { - return static_cast(p)->fresh(ctx); - } - - static void fixed_eh(void* _p, Z3_solver_callback cb, unsigned id, Z3_ast _value) { + static void pop_eh(void* _p, Z3_solver_callback cb, unsigned num_scopes) { user_propagator_base* p = static_cast(_p); scoped_cb _cb(p, cb); - scoped_context ctx(p->ctx()); - expr value(ctx(), _value); - static_cast(p)->m_fixed_eh(id, value); + static_cast(_p)->pop(num_scopes); } - static void eq_eh(void* p, Z3_solver_callback cb, unsigned x, unsigned y) { + static void* fresh_eh(void* _p, Z3_context ctx) { + user_propagator_base* p = static_cast(_p); + context* c = new context(ctx); + p->subcontexts.push_back(c); + return p->fresh(*c); + } + + static void fixed_eh(void* _p, Z3_solver_callback cb, Z3_ast _var, Z3_ast _value) { + user_propagator_base* p = static_cast(_p); scoped_cb _cb(p, cb); - static_cast(p)->m_eq_eh(x, y); + expr value(p->ctx(), _value); + expr var(p->ctx(), _var); + p->m_fixed_eh(var, value); + } + + static void eq_eh(void* _p, Z3_solver_callback cb, Z3_ast _x, Z3_ast _y) { + user_propagator_base* p = static_cast(_p); + scoped_cb _cb(p, cb); + expr x(p->ctx(), _x), y(p->ctx(), _y); + p->m_eq_eh(x, y); } static void final_eh(void* p, Z3_solver_callback cb) { @@ -3989,69 +4005,89 @@ namespace z3 { static_cast(p)->m_final_eh(); } - static void created_eh(void* _p, Z3_solver_callback cb, Z3_ast _e, unsigned id) { + static void created_eh(void* _p, Z3_solver_callback cb, Z3_ast _e) { user_propagator_base* p = static_cast(_p); scoped_cb _cb(p, cb); - scoped_context ctx(p->ctx()); - expr e(ctx(), _e); - static_cast(p)->m_created_eh(id, e); + expr e(p->ctx(), _e); + p->m_created_eh(e); } - - + + static void decide_eh(void* _p, Z3_solver_callback cb, Z3_ast* _val, unsigned* bit, Z3_lbool* is_pos) { + user_propagator_base* p = static_cast(_p); + scoped_cb _cb(p, cb); + expr val(p->ctx(), *_val); + p->m_decide_eh(val, *bit, *is_pos); + // TBD: life time of val is within the scope of this callback. + *_val = val; + } + public: - user_propagator_base(Z3_context c) : s(nullptr), c(c) {} + user_propagator_base(context& c) : s(nullptr), c(&c) {} user_propagator_base(solver* s): s(s), c(nullptr) { - Z3_solver_propagate_init(ctx(), *s, this, push_eh, pop_eh, fresh_eh); + Z3_solver_propagate_init(ctx(), *s, this, push_eh, pop_eh, fresh_eh); } virtual void push() = 0; virtual void pop(unsigned num_scopes) = 0; - virtual ~user_propagator_base() = default; + virtual ~user_propagator_base() { + for (auto& subcontext : subcontexts) { + subcontext->detach(); // detach first; the subcontexts will be freed internally! + delete subcontext; + } + } + + context& ctx() { + return c ? *c : s->ctx(); + } /** - \brief user_propagators created using \c fresh() are created during + \brief user_propagators created using \c fresh() are created during search and their lifetimes are restricted to search time. They should be garbage collected by the propagator used to invoke \c fresh(). The life-time of the Z3_context object can only be assumed valid during callbacks, such as \c fixed(), which contains expressions based on the context. */ - virtual user_propagator_base* fresh(Z3_context ctx) = 0; + virtual user_propagator_base* fresh(context& ctx) = 0; /** \brief register callbacks. Callbacks can only be registered with user_propagators - that were created using a solver. + that were created using a solver. */ - void register_fixed(fixed_eh_t& f) { - assert(s); - m_fixed_eh = f; - Z3_solver_propagate_fixed(ctx(), *s, fixed_eh); + void register_fixed(fixed_eh_t& f) { + m_fixed_eh = f; + if (s) { + Z3_solver_propagate_fixed(ctx(), *s, fixed_eh); + } } void register_fixed() { - assert(s); - m_fixed_eh = [this](unsigned id, expr const& e) { + m_fixed_eh = [this](expr const &id, expr const &e) { fixed(id, e); }; - Z3_solver_propagate_fixed(ctx(), *s, fixed_eh); + if (s) { + Z3_solver_propagate_fixed(ctx(), *s, fixed_eh); + } } - void register_eq(eq_eh_t& f) { - assert(s); - m_eq_eh = f; - Z3_solver_propagate_eq(ctx(), *s, eq_eh); + void register_eq(eq_eh_t& f) { + m_eq_eh = f; + if (s) { + Z3_solver_propagate_eq(ctx(), *s, eq_eh); + } } void register_eq() { - assert(s); - m_eq_eh = [this](unsigned x, unsigned y) { + m_eq_eh = [this](expr const& x, expr const& y) { eq(x, y); }; - Z3_solver_propagate_eq(ctx(), *s, eq_eh); + if (s) { + Z3_solver_propagate_eq(ctx(), *s, eq_eh); + } } /** @@ -4059,51 +4095,74 @@ namespace z3 { During the final check stage, all propagations have been processed. This is an opportunity for the user-propagator to delay some analysis that could be expensive to perform incrementally. It is also an opportunity - for the propagator to implement branch and bound optimization. + for the propagator to implement branch and bound optimization. */ - void register_final(final_eh_t& f) { - assert(s); - m_final_eh = f; - Z3_solver_propagate_final(ctx(), *s, final_eh); + void register_final(final_eh_t& f) { + m_final_eh = f; + if (s) { + Z3_solver_propagate_final(ctx(), *s, final_eh); + } } - - void register_final() { - assert(s); + + void register_final() { m_final_eh = [this]() { final(); }; - Z3_solver_propagate_final(ctx(), *s, final_eh); + if (s) { + Z3_solver_propagate_final(ctx(), *s, final_eh); + } } void register_created(created_eh_t& c) { - assert(s); m_created_eh = c; - Z3_solver_propagate_created(ctx(), *s, created_eh); + if (s) { + Z3_solver_propagate_created(ctx(), *s, created_eh); + } } void register_created() { - m_created_eh = [this](unsigned id, expr const& e) { - created(id, e); + m_created_eh = [this](expr const& e) { + created(e); }; - Z3_solver_propagate_created(ctx(), *s, created_eh); + if (s) { + Z3_solver_propagate_created(ctx(), *s, created_eh); + } + } + + void register_decide(decide_eh_t& c) { + m_decide_eh = c; + if (s) { + Z3_solver_propagate_decide(ctx(), *s, decide_eh); + } } - virtual void fixed(unsigned /*id*/, expr const& /*e*/) { } + void register_decide() { + m_decide_eh = [this](expr& val, unsigned& bit, Z3_lbool& is_pos) { + decide(val, bit, is_pos); + }; + if (s) { + Z3_solver_propagate_decide(ctx(), *s, decide_eh); + } + } - virtual void eq(unsigned /*x*/, unsigned /*y*/) { } + virtual void fixed(expr const& /*id*/, expr const& /*e*/) { } + + virtual void eq(expr const& /*x*/, expr const& /*y*/) { } virtual void final() { } - virtual void created(unsigned /*id*/, expr const& /*e*/) {} + virtual void created(expr const& /*e*/) {} + + virtual void decide(expr& /*val*/, unsigned& /*bit*/, Z3_lbool& /*is_pos*/) {} /** \brief tracks \c e by a unique identifier that is returned by the call. - If the \c fixed() callback is registered and if \c e is a Boolean or Bit-vector, + If the \c fixed() callback is registered and if \c e is a Boolean or Bit-vector, the \c fixed() callback gets invoked when \c e is bound to a value. If the \c eq() callback is registered, then equalities between registered expressions - are reported. + are reported. A consumer can use the \c propagate or \c conflict functions to invoke propagations or conflicts as a consequence of these callbacks. These functions take a list of identifiers for registered expressions that have been fixed. The list of identifiers must correspond to @@ -4111,40 +4170,43 @@ namespace z3 { correspond to equalities that have been registered during a callback. */ - unsigned add(expr const& e) { + void add(expr const& e) { if (cb) - return Z3_solver_propagate_register_cb(ctx(), cb, e); - if (s) - return Z3_solver_propagate_register(ctx(), *s, e); - assert(false); - return 0; + Z3_solver_propagate_register_cb(ctx(), cb, e); + else if (s) + Z3_solver_propagate_register(ctx(), *s, e); + else + assert(false); } - void conflict(unsigned num_fixed, unsigned const* fixed) { + void conflict(expr_vector const& fixed) { assert(cb); - scoped_context _ctx(ctx()); - expr conseq = _ctx().bool_val(false); - Z3_solver_propagate_consequence(ctx(), cb, num_fixed, fixed, 0, nullptr, nullptr, conseq); + expr conseq = ctx().bool_val(false); + array _fixed(fixed); + Z3_solver_propagate_consequence(ctx(), cb, fixed.size(), _fixed.ptr(), 0, nullptr, nullptr, conseq); } - void propagate(unsigned num_fixed, unsigned const* fixed, expr const& conseq) { + void propagate(expr_vector const& fixed, expr const& conseq) { assert(cb); - assert(conseq.ctx() == ctx()); - Z3_solver_propagate_consequence(ctx(), cb, num_fixed, fixed, 0, nullptr, nullptr, conseq); + assert((Z3_context)conseq.ctx() == (Z3_context)ctx()); + array _fixed(fixed); + Z3_solver_propagate_consequence(ctx(), cb, _fixed.size(), _fixed.ptr(), 0, nullptr, nullptr, conseq); } - void propagate(unsigned num_fixed, unsigned const* fixed, - unsigned num_eqs, unsigned const* lhs, unsigned const * rhs, + void propagate(expr_vector const& fixed, + expr_vector const& lhs, expr_vector const& rhs, expr const& conseq) { assert(cb); - assert(conseq.ctx() == ctx()); - Z3_solver_propagate_consequence(ctx(), cb, num_fixed, fixed, num_eqs, lhs, rhs, conseq); + assert((Z3_context)conseq.ctx() == (Z3_context)ctx()); + assert(lhs.size() == rhs.size()); + array _fixed(fixed); + array _lhs(lhs); + array _rhs(rhs); + + Z3_solver_propagate_consequence(ctx(), cb, _fixed.size(), _fixed.ptr(), lhs.size(), _lhs.ptr(), _rhs.ptr(), conseq); } }; - - - } /**@}*/ diff --git a/src/api/dotnet/ASTMap.cs b/src/api/dotnet/ASTMap.cs index f678f71c3..0dde04411 100644 --- a/src/api/dotnet/ASTMap.cs +++ b/src/api/dotnet/ASTMap.cs @@ -100,7 +100,7 @@ namespace Microsoft.Z3 { get { - ASTVector res = new ASTVector(Context, Native.Z3_ast_map_keys(Context.nCtx, NativeObject)); + using ASTVector res = new ASTVector(Context, Native.Z3_ast_map_keys(Context.nCtx, NativeObject)); return res.ToArray(); } } diff --git a/src/api/dotnet/ArithExpr.cs b/src/api/dotnet/ArithExpr.cs index 53b9db21d..bd1aedc80 100644 --- a/src/api/dotnet/ArithExpr.cs +++ b/src/api/dotnet/ArithExpr.cs @@ -22,7 +22,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; - namespace Microsoft.Z3 { /// @@ -41,24 +40,48 @@ namespace Microsoft.Z3 #region Operators - private static ArithExpr MkNum(ArithExpr e, int i) { return (ArithExpr)e.Context.MkNumeral(i, e.Context.MkIntSort()); } + private static ArithExpr MkNum(ArithExpr e, int i) + { + using var sort = e.Context.MkIntSort(); + return (ArithExpr)e.Context.MkNumeral(i, sort); + } - private static ArithExpr MkNum(ArithExpr e, double d) { return (ArithExpr)e.Context.MkNumeral(d.ToString(), e.Context.MkRealSort()); } + private static ArithExpr MkNum(ArithExpr e, double d) + { + using var sort = e.Context.MkRealSort(); + return (ArithExpr)e.Context.MkNumeral(d.ToString(), sort); + } /// Operator overloading for arithmetical division operator (over reals) public static ArithExpr operator /(ArithExpr a, ArithExpr b) { return a.Context.MkDiv(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator /(ArithExpr a, int b) { return a / MkNum(a, b); } + public static ArithExpr operator /(ArithExpr a, int b) + { + using var denominator = MkNum(a, b); + return a / denominator; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator /(ArithExpr a, double b) { return a / MkNum(a, b); } + public static ArithExpr operator /(ArithExpr a, double b) + { + using var denominator = MkNum(a, b); + return a / denominator; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator /(int a, ArithExpr b) { return MkNum(b, a) / b; } + public static ArithExpr operator /(int a, ArithExpr b) + { + using var numerator = MkNum(b, a); + return numerator / b; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator /(double a, ArithExpr b) { return MkNum(b, a) / b; } + public static ArithExpr operator /(double a, ArithExpr b) + { + using var numerator = MkNum(b, a); + return numerator / b; + } /// Operator overloading for arithmetical operator public static ArithExpr operator -(ArithExpr a) { return a.Context.MkUnaryMinus(a); } @@ -67,106 +90,218 @@ namespace Microsoft.Z3 public static ArithExpr operator -(ArithExpr a, ArithExpr b) { return a.Context.MkSub(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator -(ArithExpr a, int b) { return a - MkNum(a, b); } + public static ArithExpr operator -(ArithExpr a, int b) + { + using var rhs = MkNum(a, b); + return a - rhs; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator -(ArithExpr a, double b) { return a - MkNum(a, b); } + public static ArithExpr operator -(ArithExpr a, double b) + { + using var rhs = MkNum(a, b); + return a - rhs; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator -(int a, ArithExpr b) { return MkNum(b, a) - b; } + public static ArithExpr operator -(int a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs - b; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator -(double a, ArithExpr b) { return MkNum(b, a) - b; } + public static ArithExpr operator -(double a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs - b; + } /// Operator overloading for arithmetical operator public static ArithExpr operator +(ArithExpr a, ArithExpr b) { return a.Context.MkAdd(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator +(ArithExpr a, int b) { return a + MkNum(a, b); } + public static ArithExpr operator +(ArithExpr a, int b) + { + using var rhs = MkNum(a, b); + return a + rhs; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator +(ArithExpr a, double b) { return a + MkNum(a, b); } + public static ArithExpr operator +(ArithExpr a, double b) + { + using var rhs = MkNum(a, b); + return a + rhs; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator +(int a, ArithExpr b) { return MkNum(b, a) + b; } + public static ArithExpr operator +(int a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs + b; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator +(double a, ArithExpr b) { return MkNum(b, a) + b; } + public static ArithExpr operator +(double a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs + b; + } /// Operator overloading for arithmetical operator public static ArithExpr operator *(ArithExpr a, ArithExpr b) { return a.Context.MkMul(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator *(ArithExpr a, int b) { return a * MkNum(a, b); } + public static ArithExpr operator *(ArithExpr a, int b) + { + using var rhs = MkNum(a, b); + return a * rhs; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator *(ArithExpr a, double b) { return a * MkNum(a, b); } + public static ArithExpr operator *(ArithExpr a, double b) + { + using var rhs = MkNum(a, b); + return a * rhs; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator *(int a, ArithExpr b) { return MkNum(b, a) * b; } + public static ArithExpr operator *(int a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs * b; + } /// Operator overloading for arithmetical operator - public static ArithExpr operator *(double a, ArithExpr b) { return MkNum(b, a) * b; } + public static ArithExpr operator *(double a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs * b; + } /// Operator overloading for arithmetical operator public static BoolExpr operator <=(ArithExpr a, ArithExpr b) { return a.Context.MkLe(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(ArithExpr a, int b) { return a <= MkNum(a, b); } + public static BoolExpr operator <=(ArithExpr a, int b) + { + using var rhs = MkNum(a, b); + return a <= rhs; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(ArithExpr a, double b) { return a <= MkNum(a, b); } + public static BoolExpr operator <=(ArithExpr a, double b) + { + using var rhs = MkNum(a, b); + return a <= rhs; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(int a, ArithExpr b) { return MkNum(b, a) <= b; } + public static BoolExpr operator <=(int a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs <= b; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(double a, ArithExpr b) { return MkNum(b, a) <= b; } + public static BoolExpr operator <=(double a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs <= b; + } /// Operator overloading for arithmetical operator public static BoolExpr operator <(ArithExpr a, ArithExpr b) { return a.Context.MkLt(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator <(ArithExpr a, int b) { return a < MkNum(a, b); } + public static BoolExpr operator <(ArithExpr a, int b) + { + using var rhs = MkNum(a, b); + return a < rhs; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator <(ArithExpr a, double b) { return a < MkNum(a, b); } + public static BoolExpr operator <(ArithExpr a, double b) + { + using var rhs = MkNum(a, b); + return a < rhs; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator <(int a, ArithExpr b) { return MkNum(b, a) < b; } + public static BoolExpr operator <(int a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs < b; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator <(double a, ArithExpr b) { return MkNum(b, a) < b; } + public static BoolExpr operator <(double a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs < b; + } /// Operator overloading for arithmetical operator public static BoolExpr operator >(ArithExpr a, ArithExpr b) { return a.Context.MkGt(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator >(ArithExpr a, int b) { return a > MkNum(a, b); } + public static BoolExpr operator >(ArithExpr a, int b) + { + using var rhs = MkNum(a, b); + return a > rhs; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator >(ArithExpr a, double b) { return a > MkNum(a, b); } + public static BoolExpr operator >(ArithExpr a, double b) + { + using var rhs = MkNum(a, b); + return a > rhs; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator >(int a, ArithExpr b) { return MkNum(b, a) > b; } + public static BoolExpr operator >(int a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs > b; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator >(double a, ArithExpr b) { return MkNum(b, a) > b; } + public static BoolExpr operator >(double a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs > b; + } /// Operator overloading for arithmetical operator public static BoolExpr operator >=(ArithExpr a, ArithExpr b) { return a.Context.MkGe(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(ArithExpr a, int b) { return a >= MkNum(a, b); } + public static BoolExpr operator >=(ArithExpr a, int b) + { + using var rhs = MkNum(a, b); + return a >= rhs; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(ArithExpr a, double b) { return a >= MkNum(a, b); } + public static BoolExpr operator >=(ArithExpr a, double b) + { + using var rhs = MkNum(a, b); + return a >= rhs; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(int a, ArithExpr b) { return MkNum(b, a) >= b; } + public static BoolExpr operator >=(int a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs >= b; + } /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(double a, ArithExpr b) { return MkNum(b, a) >= b; } + public static BoolExpr operator >=(double a, ArithExpr b) + { + using var lhs = MkNum(b, a); + return lhs >= b; + } #endregion } diff --git a/src/api/dotnet/CMakeLists.txt b/src/api/dotnet/CMakeLists.txt index bab8ac982..e2055cd7a 100644 --- a/src/api/dotnet/CMakeLists.txt +++ b/src/api/dotnet/CMakeLists.txt @@ -85,6 +85,10 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE ListSort.cs Log.cs Model.cs + NativeContext.cs + NativeFuncInterp.cs + NativeModel.cs + NativeSolver.cs Optimize.cs ParamDescrs.cs Params.cs diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 214411053..a0676b4e2 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -18,10 +18,10 @@ Notes: --*/ using System; -using System.Diagnostics; using System.Collections.Generic; -using System.Runtime.InteropServices; +using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; namespace Microsoft.Z3 { @@ -202,8 +202,8 @@ namespace Microsoft.Z3 /// public UninterpretedSort MkUninterpretedSort(string str) { - - return MkUninterpretedSort(MkSymbol(str)); + using var sym = MkSymbol(str); + return MkUninterpretedSort(sym); } /// @@ -231,7 +231,6 @@ namespace Microsoft.Z3 return new BitVecSort(this, Native.Z3_mk_bv_sort(nCtx, size)); } - /// /// Create a new sequence sort. /// @@ -314,7 +313,17 @@ namespace Microsoft.Z3 { Debug.Assert(enumNames != null); - return new EnumSort(this, MkSymbol(name), MkSymbols(enumNames)); + var enumSymbols = MkSymbols(enumNames); + try + { + using var symbol = MkSymbol(name); + return new EnumSort(this, symbol, enumSymbols); + } + finally + { + foreach (var enumSymbol in enumSymbols) + enumSymbol.Dispose(); + } } /// @@ -338,7 +347,8 @@ namespace Microsoft.Z3 Debug.Assert(elemSort != null); CheckContextMatch(elemSort); - return new ListSort(this, MkSymbol(name), elemSort); + using var symbol = MkSymbol(name); + return new ListSort(this, symbol, elemSort); } /// @@ -365,8 +375,8 @@ namespace Microsoft.Z3 /// The size of the sort public FiniteDomainSort MkFiniteDomainSort(string name, ulong size) { - - return new FiniteDomainSort(this, MkSymbol(name), size); + using var symbol = MkSymbol(name); + return new FiniteDomainSort(this, symbol, size); } @@ -401,7 +411,18 @@ namespace Microsoft.Z3 public Constructor MkConstructor(string name, string recognizer, string[] fieldNames = null, Sort[] sorts = null, uint[] sortRefs = null) { - return new Constructor(this, MkSymbol(name), MkSymbol(recognizer), MkSymbols(fieldNames), sorts, sortRefs); + using var nameSymbol = MkSymbol(name); + using var recognizerSymbol = MkSymbol(recognizer); + var fieldSymbols = MkSymbols(fieldNames); + try + { + return new Constructor(this, nameSymbol, recognizerSymbol, fieldSymbols, sorts, sortRefs); + } + finally + { + foreach (var fieldSymbol in fieldSymbols) + fieldSymbol.Dispose(); + } } /// @@ -428,7 +449,8 @@ namespace Microsoft.Z3 Debug.Assert(constructors.All(c => c != null)); CheckContextMatch(constructors); - return new DatatypeSort(this, MkSymbol(name), constructors); + using var symbol = MkSymbol(name); + return new DatatypeSort(this, symbol, constructors); } /// @@ -477,7 +499,16 @@ namespace Microsoft.Z3 //Debug.Assert(Contract.ForAll(0, c.Length, j => c[j] != null)); //Debug.Assert(names.All(name => name != null)); - return MkDatatypeSorts(MkSymbols(names), c); + var symbols = MkSymbols(names); + try + { + return MkDatatypeSorts(symbols, c); + } + finally + { + foreach (var symbol in symbols) + symbol.Dispose(); + } } /// @@ -485,7 +516,7 @@ namespace Microsoft.Z3 /// The function performs a record update at t. The field /// that is passed in as argument is updated with value v, /// the remaining fields of t are unchanged. - /// + /// public Expr MkUpdateField(FuncDecl field, Expr t, Expr v) { return Expr.Create(this, Native.Z3_datatype_update_field( @@ -538,7 +569,8 @@ namespace Microsoft.Z3 CheckContextMatch(domain); CheckContextMatch(range); - return new FuncDecl(this, MkSymbol(name), domain, range); + using var symbol = MkSymbol(name); + return new FuncDecl(this, symbol, domain, range); } /// @@ -551,7 +583,8 @@ namespace Microsoft.Z3 CheckContextMatch(domain); CheckContextMatch(range); - return new FuncDecl(this, MkSymbol(name), domain, range, true); + using var symbol = MkSymbol(name); + return new FuncDecl(this, symbol, domain, range, true); } /// @@ -560,14 +593,14 @@ namespace Microsoft.Z3 /// MkRecFuncDecl. The body may contain recursive uses of the function or /// other mutually recursive functions. /// - public void AddRecDef(FuncDecl f, Expr[] args, Expr body) - { - CheckContextMatch(f); - CheckContextMatch(args); - CheckContextMatch(body); + public void AddRecDef(FuncDecl f, Expr[] args, Expr body) + { + CheckContextMatch(f); + CheckContextMatch(args); + CheckContextMatch(body); IntPtr[] argsNative = AST.ArrayToNative(args); - Native.Z3_add_rec_def(nCtx, f.NativeObject, (uint)args.Length, argsNative, body.NativeObject); - } + Native.Z3_add_rec_def(nCtx, f.NativeObject, (uint)args.Length, argsNative, body.NativeObject); + } /// /// Creates a new function declaration. @@ -579,8 +612,9 @@ namespace Microsoft.Z3 CheckContextMatch(domain); CheckContextMatch(range); + using var symbol = MkSymbol(name); Sort[] q = new Sort[] { domain }; - return new FuncDecl(this, MkSymbol(name), q, range); + return new FuncDecl(this, symbol, q, range); } /// @@ -619,7 +653,8 @@ namespace Microsoft.Z3 Debug.Assert(range != null); CheckContextMatch(range); - return new FuncDecl(this, MkSymbol(name), null, range); + using var symbol = MkSymbol(name); + return new FuncDecl(this, symbol, null, range); } /// @@ -687,7 +722,8 @@ namespace Microsoft.Z3 { Debug.Assert(range != null); - return MkConst(MkSymbol(name), range); + using var symbol = MkSymbol(name); + return MkConst(symbol, range); } /// @@ -728,8 +764,8 @@ namespace Microsoft.Z3 /// public BoolExpr MkBoolConst(string name) { - - return (BoolExpr)MkConst(MkSymbol(name), BoolSort); + using var symbol = MkSymbol(name); + return (BoolExpr)MkConst(symbol, BoolSort); } /// @@ -778,7 +814,8 @@ namespace Microsoft.Z3 { Debug.Assert(name != null); - return (BitVecExpr)MkConst(name, MkBitVecSort(size)); + using var sort = MkBitVecSort(size); + return (BitVecExpr)MkConst(name, sort); } /// @@ -786,8 +823,8 @@ namespace Microsoft.Z3 /// public BitVecExpr MkBVConst(string name, uint size) { - - return (BitVecExpr)MkConst(name, MkBitVecSort(size)); + using var sort = MkBitVecSort(size); + return (BitVecExpr)MkConst(name, sort); } #endregion @@ -811,7 +848,7 @@ namespace Microsoft.Z3 public Expr MkApp(FuncDecl f, IEnumerable args) { Debug.Assert(f != null); - Debug.Assert(args == null || args.All( a => a != null)); + Debug.Assert(args == null || args.All(a => a != null)); CheckContextMatch(f); CheckContextMatch(args); @@ -948,16 +985,12 @@ namespace Microsoft.Z3 Debug.Assert(ts != null); Debug.Assert(ts.All(a => a != null)); CheckContextMatch(ts); - BoolExpr r = null; - foreach (var t in ts) { - if (r == null) - r = t; - else - r = MkXor(r, t); - } - if (r == null) - r = MkTrue(); - return r; + + return ts.Aggregate(MkFalse(), (r, t) => + { + using (r) + return MkXor(r, t); + }); } /// @@ -2032,7 +2065,8 @@ namespace Microsoft.Z3 Debug.Assert(domain != null); Debug.Assert(range != null); - return (ArrayExpr)MkConst(name, MkArraySort(domain, range)); + using var sort = MkArraySort(domain, range); + return (ArrayExpr)MkConst(name, sort); } /// @@ -2043,7 +2077,9 @@ namespace Microsoft.Z3 Debug.Assert(domain != null); Debug.Assert(range != null); - return (ArrayExpr)MkConst(MkSymbol(name), MkArraySort(domain, range)); + using var symbol = MkSymbol(name); + using var sort = MkArraySort(domain, range); + return (ArrayExpr)MkConst(symbol, sort); } @@ -2343,7 +2379,7 @@ namespace Microsoft.Z3 CheckContextMatch(elem); CheckContextMatch(set); - return (BoolExpr) Expr.Create(this, Native.Z3_mk_set_member(nCtx, elem.NativeObject, set.NativeObject)); + return (BoolExpr)Expr.Create(this, Native.Z3_mk_set_member(nCtx, elem.NativeObject, set.NativeObject)); } /// @@ -2356,7 +2392,7 @@ namespace Microsoft.Z3 CheckContextMatch(arg1); CheckContextMatch(arg2); - return (BoolExpr) Expr.Create(this, Native.Z3_mk_set_subset(nCtx, arg1.NativeObject, arg2.NativeObject)); + return (BoolExpr)Expr.Create(this, Native.Z3_mk_set_subset(nCtx, arg1.NativeObject, arg2.NativeObject)); } #endregion @@ -2366,7 +2402,7 @@ namespace Microsoft.Z3 /// /// Create the empty sequence. /// - public SeqExpr MkEmptySeq(Sort s) + public SeqExpr MkEmptySeq(Sort s) { Debug.Assert(s != null); return new SeqExpr(this, Native.Z3_mk_seq_empty(nCtx, s.NativeObject)); @@ -2375,7 +2411,7 @@ namespace Microsoft.Z3 /// /// Create the singleton sequence. /// - public SeqExpr MkUnit(Expr elem) + public SeqExpr MkUnit(Expr elem) { Debug.Assert(elem != null); return new SeqExpr(this, Native.Z3_mk_seq_unit(nCtx, elem.NativeObject)); @@ -2384,7 +2420,7 @@ namespace Microsoft.Z3 /// /// Create a string constant. /// - public SeqExpr MkString(string s) + public SeqExpr MkString(string s) { Debug.Assert(s != null); return new SeqExpr(this, Native.Z3_mk_string(nCtx, s)); @@ -2393,7 +2429,7 @@ namespace Microsoft.Z3 /// /// Convert an integer expression to a string. /// - public SeqExpr IntToString(Expr e) + public SeqExpr IntToString(Expr e) { Debug.Assert(e != null); Debug.Assert(e is ArithExpr); @@ -2413,7 +2449,8 @@ namespace Microsoft.Z3 /// /// Convert a bit-vector expression, represented as an signed number, to a string. /// - public SeqExpr SbvToString(Expr e) { + public SeqExpr SbvToString(Expr e) + { Debug.Assert(e != null); Debug.Assert(e is ArithExpr); return new SeqExpr(this, Native.Z3_mk_sbv_to_str(nCtx, e.NativeObject)); @@ -2422,7 +2459,7 @@ namespace Microsoft.Z3 /// /// Convert an integer expression to a string. /// - public IntExpr StringToInt(Expr e) + public IntExpr StringToInt(Expr e) { Debug.Assert(e != null); Debug.Assert(e is SeqExpr); @@ -2449,13 +2486,13 @@ namespace Microsoft.Z3 public IntExpr MkLength(SeqExpr s) { Debug.Assert(s != null); - return (IntExpr) Expr.Create(this, Native.Z3_mk_seq_length(nCtx, s.NativeObject)); + return (IntExpr)Expr.Create(this, Native.Z3_mk_seq_length(nCtx, s.NativeObject)); } /// /// Check for sequence prefix. /// - public BoolExpr MkPrefixOf(SeqExpr s1, SeqExpr s2) + public BoolExpr MkPrefixOf(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); @@ -2466,7 +2503,7 @@ namespace Microsoft.Z3 /// /// Check for sequence suffix. /// - public BoolExpr MkSuffixOf(SeqExpr s1, SeqExpr s2) + public BoolExpr MkSuffixOf(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); @@ -2477,7 +2514,7 @@ namespace Microsoft.Z3 /// /// Check for sequence containment of s2 in s1. /// - public BoolExpr MkContains(SeqExpr s1, SeqExpr s2) + public BoolExpr MkContains(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); @@ -2488,7 +2525,7 @@ namespace Microsoft.Z3 /// /// Check if the string s1 is lexicographically strictly less than s2. /// - public BoolExpr MkStringLt(SeqExpr s1, SeqExpr s2) + public BoolExpr MkStringLt(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); @@ -2497,9 +2534,9 @@ namespace Microsoft.Z3 } /// - /// Check if the string s1 is lexicographically strictly less than s2. + /// Check if the string s1 is lexicographically less or equal to s2. /// - public BoolExpr MkStringLe(SeqExpr s1, SeqExpr s2) + public BoolExpr MkStringLe(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); @@ -2568,10 +2605,10 @@ namespace Microsoft.Z3 /// /// Convert a regular expression that accepts sequence s. /// - public ReExpr MkToRe(SeqExpr s) + public ReExpr MkToRe(SeqExpr s) { Debug.Assert(s != null); - return new ReExpr(this, Native.Z3_mk_seq_to_re(nCtx, s.NativeObject)); + return new ReExpr(this, Native.Z3_mk_seq_to_re(nCtx, s.NativeObject)); } @@ -2583,7 +2620,7 @@ namespace Microsoft.Z3 Debug.Assert(s != null); Debug.Assert(re != null); CheckContextMatch(s, re); - return new BoolExpr(this, Native.Z3_mk_seq_in_re(nCtx, s.NativeObject, re.NativeObject)); + return new BoolExpr(this, Native.Z3_mk_seq_in_re(nCtx, s.NativeObject, re.NativeObject)); } /// @@ -2592,7 +2629,7 @@ namespace Microsoft.Z3 public ReExpr MkStar(ReExpr re) { Debug.Assert(re != null); - return new ReExpr(this, Native.Z3_mk_re_star(nCtx, re.NativeObject)); + return new ReExpr(this, Native.Z3_mk_re_star(nCtx, re.NativeObject)); } /// @@ -2601,7 +2638,7 @@ namespace Microsoft.Z3 public ReExpr MkLoop(ReExpr re, uint lo, uint hi = 0) { Debug.Assert(re != null); - return new ReExpr(this, Native.Z3_mk_re_loop(nCtx, re.NativeObject, lo, hi)); + return new ReExpr(this, Native.Z3_mk_re_loop(nCtx, re.NativeObject, lo, hi)); } /// @@ -2610,7 +2647,7 @@ namespace Microsoft.Z3 public ReExpr MkPlus(ReExpr re) { Debug.Assert(re != null); - return new ReExpr(this, Native.Z3_mk_re_plus(nCtx, re.NativeObject)); + return new ReExpr(this, Native.Z3_mk_re_plus(nCtx, re.NativeObject)); } /// @@ -2619,7 +2656,7 @@ namespace Microsoft.Z3 public ReExpr MkOption(ReExpr re) { Debug.Assert(re != null); - return new ReExpr(this, Native.Z3_mk_re_option(nCtx, re.NativeObject)); + return new ReExpr(this, Native.Z3_mk_re_option(nCtx, re.NativeObject)); } /// @@ -2628,7 +2665,7 @@ namespace Microsoft.Z3 public ReExpr MkComplement(ReExpr re) { Debug.Assert(re != null); - return new ReExpr(this, Native.Z3_mk_re_complement(nCtx, re.NativeObject)); + return new ReExpr(this, Native.Z3_mk_re_complement(nCtx, re.NativeObject)); } /// @@ -2670,7 +2707,7 @@ namespace Microsoft.Z3 /// /// Create a difference regular expression. /// - public ReExpr MkDiff(ReExpr a, ReExpr b) + public ReExpr MkDiff(ReExpr a, ReExpr b) { Debug.Assert(a != null); Debug.Assert(b != null); @@ -2682,7 +2719,7 @@ namespace Microsoft.Z3 /// Create the empty regular expression. /// The sort s should be a regular expression. /// - public ReExpr MkEmptyRe(Sort s) + public ReExpr MkEmptyRe(Sort s) { Debug.Assert(s != null); return new ReExpr(this, Native.Z3_mk_re_empty(nCtx, s.NativeObject)); @@ -2692,7 +2729,7 @@ namespace Microsoft.Z3 /// Create the full regular expression. /// The sort s should be a regular expression. /// - public ReExpr MkFullRe(Sort s) + public ReExpr MkFullRe(Sort s) { Debug.Assert(s != null); return new ReExpr(this, Native.Z3_mk_re_full(nCtx, s.NativeObject)); @@ -2702,7 +2739,7 @@ namespace Microsoft.Z3 /// /// Create a range expression. /// - public ReExpr MkRange(SeqExpr lo, SeqExpr hi) + public ReExpr MkRange(SeqExpr lo, SeqExpr hi) { Debug.Assert(lo != null); Debug.Assert(hi != null); @@ -2713,7 +2750,7 @@ namespace Microsoft.Z3 /// /// Create less than or equal to between two characters. /// - public BoolExpr MkCharLe(Expr ch1, Expr ch2) + public BoolExpr MkCharLe(Expr ch1, Expr ch2) { Debug.Assert(ch1 != null); Debug.Assert(ch2 != null); @@ -2723,7 +2760,7 @@ namespace Microsoft.Z3 /// /// Create an integer (code point) from character. /// - public IntExpr CharToInt(Expr ch) + public IntExpr CharToInt(Expr ch) { Debug.Assert(ch != null); return new IntExpr(this, Native.Z3_mk_char_to_int(nCtx, ch.NativeObject)); @@ -2732,7 +2769,7 @@ namespace Microsoft.Z3 /// /// Create a bit-vector (code point) from character. /// - public BitVecExpr CharToBV(Expr ch) + public BitVecExpr CharToBV(Expr ch) { Debug.Assert(ch != null); return new BitVecExpr(this, Native.Z3_mk_char_to_bv(nCtx, ch.NativeObject)); @@ -2741,7 +2778,7 @@ namespace Microsoft.Z3 /// /// Create a character from a bit-vector (code point). /// - public Expr CharFromBV(BitVecExpr bv) + public Expr CharFromBV(BitVecExpr bv) { Debug.Assert(bv != null); return new Expr(this, Native.Z3_mk_char_from_bv(nCtx, bv.NativeObject)); @@ -2750,7 +2787,7 @@ namespace Microsoft.Z3 /// /// Create a check if the character is a digit. /// - public BoolExpr MkIsDigit(Expr ch) + public BoolExpr MkIsDigit(Expr ch) { Debug.Assert(ch != null); return new BoolExpr(this, Native.Z3_mk_char_is_digit(nCtx, ch.NativeObject)); @@ -2768,7 +2805,7 @@ namespace Microsoft.Z3 Debug.Assert(args != null); CheckContextMatch(args); var ts = args.ToArray(); - return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) ts.Length, + return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint)ts.Length, AST.ArrayToNative(ts), k)); } @@ -2780,7 +2817,7 @@ namespace Microsoft.Z3 Debug.Assert(args != null); CheckContextMatch(args); var ts = args.ToArray(); - return new BoolExpr(this, Native.Z3_mk_atleast(nCtx, (uint) ts.Length, + return new BoolExpr(this, Native.Z3_mk_atleast(nCtx, (uint)ts.Length, AST.ArrayToNative(ts), k)); } @@ -2789,13 +2826,13 @@ namespace Microsoft.Z3 /// public BoolExpr MkPBLe(int[] coeffs, BoolExpr[] args, int k) { - Debug.Assert(args != null); - Debug.Assert(coeffs != null); - Debug.Assert(args.Length == coeffs.Length); - CheckContextMatch(args); - return new BoolExpr(this, Native.Z3_mk_pble(nCtx, (uint) args.Length, - AST.ArrayToNative(args), - coeffs, k)); + Debug.Assert(args != null); + Debug.Assert(coeffs != null); + Debug.Assert(args.Length == coeffs.Length); + CheckContextMatch(args); + return new BoolExpr(this, Native.Z3_mk_pble(nCtx, (uint)args.Length, + AST.ArrayToNative(args), + coeffs, k)); } /// @@ -2803,26 +2840,26 @@ namespace Microsoft.Z3 /// public BoolExpr MkPBGe(int[] coeffs, BoolExpr[] args, int k) { - Debug.Assert(args != null); - Debug.Assert(coeffs != null); - Debug.Assert(args.Length == coeffs.Length); - CheckContextMatch(args); - return new BoolExpr(this, Native.Z3_mk_pbge(nCtx, (uint) args.Length, - AST.ArrayToNative(args), - coeffs, k)); + Debug.Assert(args != null); + Debug.Assert(coeffs != null); + Debug.Assert(args.Length == coeffs.Length); + CheckContextMatch(args); + return new BoolExpr(this, Native.Z3_mk_pbge(nCtx, (uint)args.Length, + AST.ArrayToNative(args), + coeffs, k)); } /// /// Create a pseudo-Boolean equal constraint. /// public BoolExpr MkPBEq(int[] coeffs, BoolExpr[] args, int k) { - Debug.Assert(args != null); - Debug.Assert(coeffs != null); - Debug.Assert(args.Length == coeffs.Length); - CheckContextMatch(args); - return new BoolExpr(this, Native.Z3_mk_pbeq(nCtx, (uint) args.Length, - AST.ArrayToNative(args), - coeffs, k)); + Debug.Assert(args != null); + Debug.Assert(coeffs != null); + Debug.Assert(args.Length == coeffs.Length); + CheckContextMatch(args); + return new BoolExpr(this, Native.Z3_mk_pbeq(nCtx, (uint)args.Length, + AST.ArrayToNative(args), + coeffs, k)); } #endregion @@ -3040,8 +3077,8 @@ namespace Microsoft.Z3 /// the size of the bit-vector public BitVecNum MkBV(string v, uint size) { - - return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); + using var sort = MkBitVecSort(size); + return (BitVecNum)MkNumeral(v, sort); } /// @@ -3051,8 +3088,8 @@ namespace Microsoft.Z3 /// the size of the bit-vector public BitVecNum MkBV(int v, uint size) { - - return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); + using var sort = MkBitVecSort(size); + return (BitVecNum)MkNumeral(v, sort); } /// @@ -3062,8 +3099,8 @@ namespace Microsoft.Z3 /// the size of the bit-vector public BitVecNum MkBV(uint v, uint size) { - - return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); + using var sort = MkBitVecSort(size); + return (BitVecNum)MkNumeral(v, sort); } /// @@ -3073,8 +3110,8 @@ namespace Microsoft.Z3 /// the size of the bit-vector public BitVecNum MkBV(long v, uint size) { - - return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); + using var sort = MkBitVecSort(size); + return (BitVecNum)MkNumeral(v, sort); } /// @@ -3084,8 +3121,8 @@ namespace Microsoft.Z3 /// the size of the bit-vector public BitVecNum MkBV(ulong v, uint size) { - - return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); + using var sort = MkBitVecSort(size); + return (BitVecNum)MkNumeral(v, sort); } /// @@ -3332,7 +3369,7 @@ namespace Microsoft.Z3 uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); - ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_string(nCtx, str, + using ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_string(nCtx, str, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); return assertions.ToBoolExprArray(); @@ -3351,7 +3388,7 @@ namespace Microsoft.Z3 uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); - ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_file(nCtx, fileName, + using ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_file(nCtx, fileName, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); return assertions.ToBoolExprArray(); @@ -3836,8 +3873,8 @@ namespace Microsoft.Z3 /// public Solver MkSolver(string logic) { - - return MkSolver(MkSymbol(logic)); + using var symbol = MkSymbol(logic); + return MkSolver(symbol); } /// @@ -4085,7 +4122,7 @@ namespace Microsoft.Z3 /// indicates whether the result should be negative. public FPNum MkFPZero(FPSort s, bool negative) { - return new FPNum(this, Native.Z3_mk_fpa_zero(nCtx, s.NativeObject, (byte)(negative ? 1 : 0))); + return new FPNum(this, Native.Z3_mk_fpa_zero(nCtx, s.NativeObject, (byte)(negative ? 1 : 0))); } /// @@ -4127,7 +4164,7 @@ namespace Microsoft.Z3 /// FloatingPoint sort. public FPNum MkFPNumeral(bool sgn, uint sig, int exp, FPSort s) { - return new FPNum(this, Native.Z3_mk_fpa_numeral_int_uint(nCtx, (byte)(sgn ? 1 : 0), exp, sig, s.NativeObject)); + return new FPNum(this, Native.Z3_mk_fpa_numeral_int_uint(nCtx, (byte)(sgn ? 1 : 0), exp, sig, s.NativeObject)); } /// @@ -4139,7 +4176,7 @@ namespace Microsoft.Z3 /// FloatingPoint sort. public FPNum MkFPNumeral(bool sgn, Int64 exp, UInt64 sig, FPSort s) { - return new FPNum(this, Native.Z3_mk_fpa_numeral_int64_uint64(nCtx, (byte)(sgn ? 1 : 0), exp, sig, s.NativeObject)); + return new FPNum(this, Native.Z3_mk_fpa_numeral_int64_uint64(nCtx, (byte)(sgn ? 1 : 0), exp, sig, s.NativeObject)); } /// @@ -4825,12 +4862,12 @@ namespace Microsoft.Z3 /// /// ASTVector DRQ /// - public IDecRefQueue ASTVector_DRQ { get { return m_ASTVector_DRQ; } } + public IDecRefQueue ASTVector_DRQ { get { return m_ASTVector_DRQ; } } /// /// ApplyResult DRQ /// - public IDecRefQueue ApplyResult_DRQ { get { return m_ApplyResult_DRQ; } } + public IDecRefQueue ApplyResult_DRQ { get { return m_ApplyResult_DRQ; } } /// /// FuncEntry DRQ @@ -4926,10 +4963,16 @@ namespace Microsoft.Z3 Fixedpoint_DRQ.Clear(this); Optimize_DRQ.Clear(this); + if (m_boolSort != null) m_boolSort.Dispose(); + if (m_intSort != null) m_intSort.Dispose(); + if (m_realSort != null) m_realSort.Dispose(); + if (m_stringSort != null) m_stringSort.Dispose(); + if (m_charSort != null) m_charSort.Dispose(); m_boolSort = null; m_intSort = null; m_realSort = null; m_stringSort = null; + m_charSort = null; if (refCount == 0 && m_ctx != IntPtr.Zero) { m_n_err_handler = null; @@ -4937,7 +4980,7 @@ namespace Microsoft.Z3 m_ctx = IntPtr.Zero; Native.Z3_del_context(ctx); } - else + else GC.ReRegisterForFinalize(this); } #endregion diff --git a/src/api/dotnet/DatatypeSort.cs b/src/api/dotnet/DatatypeSort.cs index 943d3753f..5ad66bfa4 100644 --- a/src/api/dotnet/DatatypeSort.cs +++ b/src/api/dotnet/DatatypeSort.cs @@ -79,7 +79,7 @@ namespace Microsoft.Z3 FuncDecl[][] res = new FuncDecl[n][]; for (uint i = 0; i < n; i++) { - FuncDecl fd = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, i)); + using FuncDecl fd = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, i)); uint ds = fd.DomainSize; FuncDecl[] tmp = new FuncDecl[ds]; for (uint j = 0; j < ds; j++) diff --git a/src/api/dotnet/EnumSort.cs b/src/api/dotnet/EnumSort.cs index 08c85361e..8abef3154 100644 --- a/src/api/dotnet/EnumSort.cs +++ b/src/api/dotnet/EnumSort.cs @@ -74,7 +74,8 @@ namespace Microsoft.Z3 /// public Expr Const(uint inx) { - return Context.MkApp(ConstDecl(inx)); + using var decl = ConstDecl(inx); + return Context.MkApp(decl); } /// diff --git a/src/api/dotnet/Fixedpoint.cs b/src/api/dotnet/Fixedpoint.cs index dc4de8925..15560d829 100644 --- a/src/api/dotnet/Fixedpoint.cs +++ b/src/api/dotnet/Fixedpoint.cs @@ -255,7 +255,7 @@ namespace Microsoft.Z3 get { - ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject)); + using ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject)); return av.ToBoolExprArray(); } } @@ -268,7 +268,7 @@ namespace Microsoft.Z3 get { - ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject)); + using ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject)); return av.ToBoolExprArray(); } } @@ -292,7 +292,7 @@ namespace Microsoft.Z3 /// public BoolExpr[] ParseFile(string file) { - ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_file(Context.nCtx, NativeObject, file)); + using ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_file(Context.nCtx, NativeObject, file)); return av.ToBoolExprArray(); } @@ -301,7 +301,7 @@ namespace Microsoft.Z3 /// public BoolExpr[] ParseString(string s) { - ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_string(Context.nCtx, NativeObject, s)); + using ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_string(Context.nCtx, NativeObject, s)); return av.ToBoolExprArray(); } diff --git a/src/api/dotnet/Goal.cs b/src/api/dotnet/Goal.cs index c31f649f7..c096c0755 100644 --- a/src/api/dotnet/Goal.cs +++ b/src/api/dotnet/Goal.cs @@ -203,8 +203,8 @@ namespace Microsoft.Z3 /// Essentially invokes the `simplify' tactic on the goal. public Goal Simplify(Params p = null) { - Tactic t = Context.MkTactic("simplify"); - ApplyResult res = t.Apply(this, p); + using Tactic t = Context.MkTactic("simplify"); + using ApplyResult res = t.Apply(this, p); if (res.NumSubgoals == 0) throw new Z3Exception("No subgoals"); diff --git a/src/api/dotnet/Microsoft.Z3.csproj.in b/src/api/dotnet/Microsoft.Z3.csproj.in index 157334242..85ab98b38 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj.in +++ b/src/api/dotnet/Microsoft.Z3.csproj.in @@ -32,6 +32,8 @@ Microsoft Microsoft + + 8.0 diff --git a/src/api/dotnet/Model.cs b/src/api/dotnet/Model.cs index 0238ff974..c35c0a727 100644 --- a/src/api/dotnet/Model.cs +++ b/src/api/dotnet/Model.cs @@ -87,7 +87,8 @@ namespace Microsoft.Z3 if (Native.Z3_is_as_array(Context.nCtx, n) == 0) throw new Z3Exception("Argument was not an array constant"); IntPtr fd = Native.Z3_get_as_array_func_decl(Context.nCtx, n); - return FuncInterp(new FuncDecl(Context, fd)); + using var decl = new FuncDecl(Context, fd); + return FuncInterp(decl); } } else @@ -241,7 +242,7 @@ namespace Microsoft.Z3 /// Evaluate expression to a double, assuming it is a numeral already. /// public double Double(Expr t) { - var r = Eval(t, true); + using var r = Eval(t, true); return Native.Z3_get_numeral_double(Context.nCtx, r.NativeObject); } @@ -283,7 +284,7 @@ namespace Microsoft.Z3 { Debug.Assert(s != null); - ASTVector av = new ASTVector(Context, Native.Z3_model_get_sort_universe(Context.nCtx, NativeObject, s.NativeObject)); + using ASTVector av = new ASTVector(Context, Native.Z3_model_get_sort_universe(Context.nCtx, NativeObject, s.NativeObject)); return av.ToExprArray(); } diff --git a/src/api/dotnet/NativeContext.cs b/src/api/dotnet/NativeContext.cs new file mode 100644 index 000000000..9f285ec84 --- /dev/null +++ b/src/api/dotnet/NativeContext.cs @@ -0,0 +1,1405 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + NativeContext.cs + +Abstract: + + Z3 Managed API: Native Context + +Author: + + Christoph Wintersteiger (cwinter) 2012-03-22 + John Fleisher, Nikolaj Bjorner (nbjorner) 2022-03-01 + +--*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Microsoft.Z3 +{ + using Z3_app = System.IntPtr; + using Z3_ast = System.IntPtr; + using Z3_ast_vector = System.IntPtr; + using Z3_func_decl = System.IntPtr; + using Z3_pattern = System.IntPtr; + using Z3_solver = System.IntPtr; + using Z3_sort = System.IntPtr; + using Z3_stats = System.IntPtr; + using Z3_symbol = System.IntPtr; + + /// + /// The main interaction with Z3 happens via the Context. + /// NativeContext allows for efficient wrapper-reduced interaction with Z3 + /// expressions. + /// + public class NativeContext : IDisposable + { + /// + /// Constructor. + /// + /// + /// The following parameters can be set: + /// - proof (Boolean) Enable proof generation + /// - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting + /// - trace (Boolean) Tracing support for VCC + /// - trace_file_name (String) Trace out file for VCC traces + /// - timeout (unsigned) default timeout (in milliseconds) used for solvers + /// - well_sorted_check type checker + /// - auto_config use heuristics to automatically select solver and configure it + /// - model model generation for solvers, this parameter can be overwritten when creating a solver + /// - model_validate validate models produced by solvers + /// - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver + /// Note that in previous versions of Z3, this constructor was also used to set global and module parameters. + /// For this purpose we should now use + /// + public NativeContext(Dictionary settings) + : base() + { + Debug.Assert(settings != null); + + lock (creation_lock) + { + IntPtr cfg = Native.Z3_mk_config(); + foreach (KeyValuePair kv in settings) + Native.Z3_set_param_value(cfg, kv.Key, kv.Value); + m_ctx = Native.Z3_mk_context(cfg); + Native.Z3_del_config(cfg); + InitContext(); + } + } + + #region Arithmetic + /// + /// Create an expression representing t[0] + t[1] + .... + /// + + public Z3_ast MkAdd(params Z3_ast[] t) + { + Debug.Assert(t != null); + Debug.Assert(t.All(a => a != IntPtr.Zero)); + + return Native.Z3_mk_add(nCtx, (uint)(t?.Length ?? 0), t); + } + + /// + /// Create an expression representing t[0] * t[1] * .... + /// + public Z3_ast MkMul(params Z3_ast[] t) + { + Debug.Assert(t != null); + Debug.Assert(t.All(a => a != IntPtr.Zero)); + + var ts = t.ToArray(); + return Native.Z3_mk_mul(nCtx, (uint)(ts?.Length ?? 0), ts); + } + + /// + /// Create an expression representing t1 / t2. + /// + public Z3_ast MkDiv(Z3_ast t1, Z3_ast t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_div(nCtx, t1, t2); + } + + /// + /// Create an expression representing t1 <= t2 + /// + public Z3_ast MkLe(Z3_ast t1, Z3_ast t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_le(nCtx, t1, t2); + } + + /// + /// Create an expression representing t1 < t2 + /// + public Z3_ast MkLt(Z3_ast t1, Z3_ast t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_lt(nCtx, t1, t2); + } + + /// + /// Create an expression representing t1 >= t2 + /// + public Z3_ast MkGe(Z3_ast t1, Z3_ast t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_ge(nCtx, t1, t2); + } + + /// + /// Create an expression representing t1 > t2 + /// + public Z3_ast MkGt(Z3_ast t1, Z3_ast t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_gt(nCtx, t1, t2); + } + + /// + /// Unsigned less-than + /// + /// + /// The arguments must have the same bit-vector sort. + /// + public Z3_ast MkBvUlt(Z3_ast t1, Z3_ast t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvult(nCtx, t1, t2); + } + + /// + /// Unsigned less-than-equal + /// + /// + /// The arguments must have the same bit-vector sort. + /// + public Z3_ast MkBvUle(Z3_ast t1, Z3_ast t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvule(nCtx, t1, t2); + } + + /// + /// Creates the equality = . + /// + public Z3_ast MkEq(Z3_ast x, Z3_ast y) + { + Debug.Assert(x != IntPtr.Zero); + Debug.Assert(y != IntPtr.Zero); + + return Native.Z3_mk_eq(nCtx, x, y); + } + + /// + /// Mk an expression representing not(a). + /// + public Z3_ast MkNot(Z3_ast a) + { + Debug.Assert(a != IntPtr.Zero); + + return Native.Z3_mk_not(nCtx, a); + } + + /// + /// Create an expression representing t[0] and t[1] and .... + /// + public Z3_ast MkAnd(params Z3_ast[] t) + { + Debug.Assert(t != null); + Debug.Assert(t.All(a => a != IntPtr.Zero)); + + return Native.Z3_mk_and(nCtx, (uint)(t?.Length ?? 0), t); + } + + /// + /// Create an expression representing t[0] or t[1] or .... + /// + public Z3_ast MkOr(params Z3_ast[] t) + { + Debug.Assert(t != null); + Debug.Assert(t.All(a => a != IntPtr.Zero)); + + return Native.Z3_mk_or(nCtx, (uint)(t?.Length ?? 0), t); + } + + + /// + /// Create a real numeral. + /// + /// A string representing the Term value in decimal notation. + /// A Term with value and sort Real + public Z3_ast MkReal(string v) + { + Debug.Assert(!string.IsNullOrEmpty(v)); + return Native.Z3_mk_numeral(nCtx, v, RealSort); + } + + /// + /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. + /// + /// Value of the numeral + /// Sort of the numeral + public Z3_ast MkNumeral(int v, Z3_sort sort) + { + Debug.Assert(sort != IntPtr.Zero); + + return Native.Z3_mk_int(nCtx, v, sort); + } + + /// + /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. + /// + /// Value of the numeral + /// Sort of the numeral + public Z3_ast MkNumeral(uint v, Z3_sort sort) + { + Debug.Assert(sort != null); + + return Native.Z3_mk_unsigned_int(nCtx, v, sort); + } + + /// + /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. + /// + /// Value of the numeral + /// Sort of the numeral + public Z3_ast MkNumeral(long v, Z3_sort sort) + { + Debug.Assert(sort != null); + + return Native.Z3_mk_int64(nCtx, v, sort); + } + + /// + /// Create an expression representing an if-then-else: ite(t1, t2, t3). + /// + /// An expression with Boolean sort + /// An expression + /// An expression with the same sort as + public Z3_ast MkIte(Z3_ast t1, Z3_ast t2, Z3_ast t3) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + Debug.Assert(t3 != IntPtr.Zero); + + return Native.Z3_mk_ite(nCtx, t1, t2, t3); + } + + /// + /// Create an expression representing t1 -> t2. + /// + public Z3_ast MkImplies(Z3_ast t1, Z3_ast t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_implies(nCtx, t1, t2); + } + + #endregion + + #region Sort + + /// + /// Integer Sort + /// + public Z3_sort IntSort => Native.Z3_mk_int_sort(nCtx); + + /// + /// Returns the "singleton" BoolSort for this NativeContext + /// + public Z3_sort BoolSort => Native.Z3_mk_bool_sort(nCtx); + + /// + /// Returns the "singleton" RealSort for this NativeContext + /// + public Z3_sort RealSort => Native.Z3_mk_real_sort(nCtx); + + + /// + /// Returns the BvSort for size in this NativeContext + /// + public Z3_sort MkBvSort(uint size) => Native.Z3_mk_bv_sort(nCtx, size); + + /// + /// returns ListSort + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// The list algebraic datatype + + public Z3_sort MkListSort(string name, Z3_sort elemSort, + out Z3_func_decl inil, out Z3_func_decl iisnil, + out Z3_func_decl icons, out Z3_func_decl iiscons, + out Z3_func_decl ihead, out Z3_func_decl itail) + { + Debug.Assert(!string.IsNullOrEmpty(name)); + Debug.Assert(elemSort != IntPtr.Zero); + + IntPtr nil = IntPtr.Zero, isnil = IntPtr.Zero, + cons = IntPtr.Zero, iscons = IntPtr.Zero, + head = IntPtr.Zero, tail = IntPtr.Zero; + + var symbol = Native.Z3_mk_string_symbol(nCtx, name); + var sort = Native.Z3_mk_list_sort(nCtx, symbol, elemSort, + ref nil, ref isnil, ref cons, ref iscons, ref head, ref tail); + + inil = nil; + iisnil = isnil; + icons = cons; + iiscons = iscons; + ihead = head; + itail = tail; + + return sort; + } + + /// + /// Create a new array sort. + /// + public Z3_sort MkArraySort(Z3_sort domain, Z3_sort range) + { + Debug.Assert(domain != IntPtr.Zero); + Debug.Assert(range != IntPtr.Zero); + + return Native.Z3_mk_array_sort(nCtx, domain, range); + } + + /// + /// Create a new tuple sort. + /// + public Z3_sort MkTupleSort(Z3_symbol name, Z3_symbol[] fieldNames, Z3_sort[] fieldSorts, out Z3_func_decl constructor, Z3_func_decl[] projections) + { + Debug.Assert(name != IntPtr.Zero); + Debug.Assert(fieldNames != null); + Debug.Assert(fieldNames.All(fn => fn != IntPtr.Zero)); + Debug.Assert(fieldSorts == null || fieldSorts.All(fs => fs != IntPtr.Zero)); + + var numFields = (uint)(fieldNames?.Length ?? 0); + constructor = IntPtr.Zero; + return Native.Z3_mk_tuple_sort(nCtx, name, numFields, fieldNames, fieldSorts, ref constructor, projections); + } + + #endregion + + #region Propositional + /// + /// The true Term. + /// + public Z3_ast MkTrue() => Native.Z3_mk_true(nCtx); + + /// + /// The false Term. + /// + public Z3_ast MkFalse() => Native.Z3_mk_false(nCtx); + + /// + /// Creates a Boolean value. + /// + public Z3_ast MkBool(bool value) => value ? MkTrue() : MkFalse(); + + /// + /// Create an expression representing t1 iff t2. + /// + public Z3_ast MkIff(Z3_ast t1, Z3_ast t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_iff(nCtx, t1, t2); + } + #endregion + + #region Constants + /// + /// Creates a new Constant of sort and named . + /// + public Z3_ast MkConst(string name, Z3_sort range) + { + Debug.Assert(!string.IsNullOrEmpty(name)); + Debug.Assert(range != IntPtr.Zero); + + return Native.Z3_mk_const(nCtx, MkStringSymbol(name), range); + } + + #endregion + + #region Symbol + /// + /// Return a ptr to symbol for string + /// + /// + /// + public Z3_symbol MkStringSymbol(string name) + { + Debug.Assert(!string.IsNullOrEmpty(name)); + + return Native.Z3_mk_string_symbol(nCtx, name); + } + #endregion + + #region Terms + /// + /// Create a new function application. + /// + public Z3_ast MkApp(Z3_func_decl f, params Z3_ast[] args) + { + Debug.Assert(f != IntPtr.Zero); + Debug.Assert(args == null || args.All(a => a != IntPtr.Zero)); + + return Native.Z3_mk_app(nCtx, f, (uint)(args?.Length ?? 0), args); + } + + #endregion + + #region Bound Variables + /// + /// Creates a new bound variable. + /// + /// The de-Bruijn index of the variable + /// The sort of the variable + public Z3_ast MkBound(uint index, Z3_sort sort) + { + Debug.Assert(sort != IntPtr.Zero); + + return Native.Z3_mk_bound(nCtx, index, sort); + } + #endregion + + #region Bit-vectors + /// + /// Bitwise conjunction. + /// + /// The arguments must have a bit-vector sort. + public Z3_ast_vector MkBvAnd(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvand(nCtx, t1, t2); + } + + /// + /// Bitwise negation. + /// + /// The argument must have a bit-vector sort. + public Z3_ast_vector MkBvNot(Z3_ast_vector t) + { + Debug.Assert(t != IntPtr.Zero); + + return Native.Z3_mk_bvnot(nCtx, t); + } + + /// + /// Standard two's complement unary minus. + /// + /// The arguments must have a bit-vector sort. + public Z3_ast_vector MkBvNeg(Z3_ast_vector t) + { + Debug.Assert(t != IntPtr.Zero); + + return Native.Z3_mk_bvneg(nCtx, t); + } + + /// + /// Standard two's complement unary minus. + /// + /// The arguments must have a bit-vector sort. + public Z3_ast_vector MkBVNeg(Z3_ast_vector t) + { + Debug.Assert(t != IntPtr.Zero); + + return Native.Z3_mk_bvneg(nCtx, t); + } + + /// + /// Two's complement addition. + /// + /// The arguments must have the same bit-vector sort. + public Z3_ast_vector MkBvAdd(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvadd(nCtx, t1, t2); + } + + /// + /// Bit-vector extraction. + /// + /// + /// Extract the bits down to from a bitvector of + /// size m to yield a new bitvector of size n, where + /// n = high - low + 1. + /// The argument must have a bit-vector sort. + /// + public Z3_ast_vector MkBvExtract(uint high, uint low, Z3_ast_vector t) + { + Debug.Assert(t != IntPtr.Zero); + + return Native.Z3_mk_extract(nCtx, high, low, t); + } + + /// + /// Bit-vector sign extension. + /// + /// + /// Sign-extends the given bit-vector to the (signed) equivalent bitvector of + /// size m+i, where \c m is the size of the given bit-vector. + /// The argument must have a bit-vector sort. + /// + public Z3_ast_vector MkBvSignExt(uint i, Z3_ast_vector t) + { + Debug.Assert(t != IntPtr.Zero); + + return Native.Z3_mk_sign_ext(nCtx, i, t); + } + + /// + /// Bit-vector zero extension. + /// + /// + /// Extend the given bit-vector with zeros to the (unsigned) equivalent + /// bitvector of size m+i, where \c m is the size of the + /// given bit-vector. + /// The argument must have a bit-vector sort. + /// + public Z3_ast_vector MkBvZeroExt(uint i, Z3_ast_vector t) + { + Debug.Assert(t != IntPtr.Zero); + + return Native.Z3_mk_zero_ext(nCtx, i, t); + } + + /// + /// Shift left. + /// + /// + /// It is equivalent to multiplication by 2^x where \c x is the value of . + /// + /// NB. The semantics of shift operations varies between environments. This + /// definition does not necessarily capture directly the semantics of the + /// programming language or assembly architecture you are modeling. + /// + /// The arguments must have a bit-vector sort. + /// + public Z3_ast_vector MkBvShl(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvshl(nCtx, t1, t2); + } + + /// + /// Logical shift right + /// + /// + /// It is equivalent to unsigned division by 2^x where \c x is the value of . + /// + /// NB. The semantics of shift operations varies between environments. This + /// definition does not necessarily capture directly the semantics of the + /// programming language or assembly architecture you are modeling. + /// + /// The arguments must have a bit-vector sort. + /// + public Z3_ast_vector MkBvLshr(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvlshr(nCtx, t1, t2); + } + + /// + /// Arithmetic shift right + /// + /// + /// It is like logical shift right except that the most significant + /// bits of the result always copy the most significant bit of the + /// second argument. + /// + /// NB. The semantics of shift operations varies between environments. This + /// definition does not necessarily capture directly the semantics of the + /// programming language or assembly architecture you are modeling. + /// + /// The arguments must have a bit-vector sort. + /// + public Z3_ast_vector MkBvAshr(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvashr(nCtx, t1, t2); + } + + /// + /// Two's complement signed less-than + /// + /// + /// The arguments must have the same bit-vector sort. + /// + public Z3_ast MkBvSlt(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvslt(nCtx, t1, t2); + } + + /// + /// Two's complement multiplication. + /// + /// The arguments must have the same bit-vector sort. + public Z3_ast_vector MkBvMul(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvmul(nCtx, t1, t2); + } + + /// + /// Unsigned division. + /// + /// + /// It is defined as the floor of t1/t2 if \c t2 is + /// different from zero. If t2 is zero, then the result + /// is undefined. + /// The arguments must have the same bit-vector sort. + /// + public Z3_ast_vector MkBvUdiv(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvudiv(nCtx, t1, t2); + } + + /// + /// Signed division. + /// + /// + /// It is defined in the following way: + /// + /// - The \c floor of t1/t2 if \c t2 is different from zero, and t1*t2 >= 0. + /// + /// - The \c ceiling of t1/t2 if \c t2 is different from zero, and t1*t2 < 0. + /// + /// If t2 is zero, then the result is undefined. + /// The arguments must have the same bit-vector sort. + /// + public Z3_ast_vector MkBvSdiv(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvsdiv(nCtx, t1, t2); + } + + /// + /// Unsigned remainder. + /// + /// + /// It is defined as t1 - (t1 /u t2) * t2, where /u represents unsigned division. + /// If t2 is zero, then the result is undefined. + /// The arguments must have the same bit-vector sort. + /// + public Z3_ast_vector MkBvUrem(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvurem(nCtx, t1, t2); + } + + /// + /// Signed remainder. + /// + /// + /// It is defined as t1 - (t1 /s t2) * t2, where /s represents signed division. + /// The most significant bit (sign) of the result is equal to the most significant bit of \c t1. + /// + /// If t2 is zero, then the result is undefined. + /// The arguments must have the same bit-vector sort. + /// + public Z3_ast_vector MkBvSrem(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvsrem(nCtx, t1, t2); + } + + /// + /// Two's complement subtraction. + /// + /// The arguments must have the same bit-vector sort. + public Z3_ast_vector MkBvSub(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvsub(nCtx, t1, t2); + } + + /// + /// Bitwise disjunction. + /// + /// The arguments must have a bit-vector sort. + public Z3_ast_vector MkBvOr(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != IntPtr.Zero); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvor(nCtx, t1, t2); + } + + /// + /// Bitwise XOR. + /// + /// The arguments must have a bit-vector sort. + public Z3_ast_vector MkBvXor(Z3_ast_vector t1, Z3_ast_vector t2) + { + Debug.Assert(t1 != null); + Debug.Assert(t2 != IntPtr.Zero); + + return Native.Z3_mk_bvxor(nCtx, t1, t2); + } + #endregion + + #region Quantifiers + /// + /// Create a universal Quantifier. + /// + /// + /// Creates a forall formula, where is the weight, + /// is an array of patterns, is an array + /// with the sorts of the bound variables, is an array with the + /// 'names' of the bound variables, and is the body of the + /// quantifier. Quantifiers are associated with weights indicating the importance of + /// using the quantifier during instantiation. + /// Note that the bound variables are de-Bruijn indices created using . + /// Z3 applies the convention that the last element in and + /// refers to the variable with index 0, the second to last element + /// of and refers to the variable + /// with index 1, etc. + /// + /// the sorts of the bound variables. + /// names of the bound variables + /// the body of the quantifier. + /// quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. + /// array containing the patterns created using MkPattern. + /// array containing the anti-patterns created using MkPattern. + /// optional symbol to track quantifier. + /// optional symbol to track skolem constants. + public Z3_ast MkForall(Z3_sort[] sorts, Z3_symbol[] names, Z3_ast body, uint weight = 1, Z3_ast[] patterns = null, Z3_ast[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) + { + return MkQuantifier(true, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); + } + + /// + /// Same as MkForAll but defaults to "forall" = false + /// Create an existential Quantifier. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Z3_ast MkExists(Z3_sort[] sorts, Z3_symbol[] names, Z3_ast body, uint weight = 1, Z3_ast[] patterns = null, Z3_ast[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) + { + return MkQuantifier(false, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); + } + + /// + /// Create a quantified expression either forall or exists + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private Z3_ast MkQuantifier(bool is_forall, Z3_sort[] sorts, Z3_symbol[] names, Z3_ast body, uint weight, Z3_ast[] patterns, Z3_ast[] noPatterns, Symbol quantifierID, Symbol skolemID) + { + Debug.Assert(sorts != null); + Debug.Assert(names != null); + Debug.Assert(body != null); + Debug.Assert(sorts.Length == names.Length); + Debug.Assert(sorts.All(s => s != IntPtr.Zero)); + Debug.Assert(names.All(n => n != IntPtr.Zero)); + Debug.Assert(patterns == null || patterns.All(p => p != IntPtr.Zero)); + Debug.Assert(noPatterns == null || noPatterns.All(np => np != IntPtr.Zero)); + + if (noPatterns == null && quantifierID == null && skolemID == null) + { + return Native.Z3_mk_quantifier(nCtx, (byte)(is_forall ? 1 : 0), weight, + (uint)(patterns?.Length ?? 0), patterns, + (uint)(sorts?.Length ?? 0), sorts, + names, + body); + } + else + { + return Native.Z3_mk_quantifier_ex(nCtx, (byte)(is_forall ? 1 : 0), weight, + AST.GetNativeObject(quantifierID), AST.GetNativeObject(skolemID), + (uint)(patterns?.Length ?? 0), patterns, + (uint)(noPatterns?.Length ?? 0), noPatterns, + (uint)(sorts?.Length ?? 0), sorts, + names, + body); + } + } + + #endregion + + #region Options + /// + /// Selects the format used for pretty-printing expressions. + /// + /// + /// The default mode for pretty printing expressions is to produce + /// SMT-LIB style output where common subexpressions are printed + /// at each occurrence. The mode is called Z3_PRINT_SMTLIB_FULL. + /// To print shared common subexpressions only once, + /// use the Z3_PRINT_LOW_LEVEL mode. + /// To print in way that conforms to SMT-LIB standards and uses let + /// expressions to share common sub-expressions use Z3_PRINT_SMTLIB_COMPLIANT. + /// + /// + /// + /// + /// + public Z3_ast_print_mode PrintMode + { + set { Native.Z3_set_ast_print_mode(nCtx, (uint)value); } + } + + #endregion + + #region Arrays + + + /// + /// Create a constant array. + /// + /// + /// The resulting term is an array, such that a selecton an arbitrary index + /// produces the value v. + /// + public Z3_ast MkConstArray(Z3_sort domain, Z3_ast v) + { + Debug.Assert(domain != IntPtr.Zero); + Debug.Assert(v != IntPtr.Zero); + + return Native.Z3_mk_const_array(nCtx, domain, v); + } + + /// + /// Array update. + /// + /// + /// The node a must have an array sort [domain -> range], + /// i must have sort domain, + /// v must have sort range. The sort of the result is [domain -> range]. + /// The semantics of this function is given by the theory of arrays described in the SMT-LIB + /// standard. See http://smtlib.org for more details. + /// The result of this function is an array that is equal to a + /// (with respect to select) + /// on all indices except for i, where it maps to v + /// (and the select of a with + /// respect to i may be a different value). + /// + public Z3_ast MkStore(Z3_ast a, Z3_ast i, Z3_ast v) + { + Debug.Assert(a != IntPtr.Zero); + Debug.Assert(i != IntPtr.Zero); + Debug.Assert(v != IntPtr.Zero); + + return Native.Z3_mk_store(nCtx, a, i, v); + } + + /// + /// Array read. + /// + /// + /// The argument array is the array and index is the index + /// of the array that gets read. + /// + /// The node array must have an array sort [domain -> range], + /// and index must have the sort domain. + /// The sort of the result is range. + /// + public Z3_ast MkSelect(Z3_ast array, Z3_ast index) + { + Debug.Assert(array != IntPtr.Zero); + Debug.Assert(index != IntPtr.Zero); + + return Native.Z3_mk_select(nCtx, array, index); + } + + /// + /// Access the array default value. + /// + /// + /// Produces the default range value, for arrays that can be represented as + /// finite maps with a default range value. + /// + public Z3_ast MkDefault(Z3_ast a) + { + Debug.Assert(a != null); + + return Native.Z3_mk_array_default(nCtx, a); + } + + #endregion + + #region Function Declarations + + /// + /// Creates a new function declaration. + /// + public Z3_func_decl MkFuncDecl(string name, Z3_sort[] domain, Z3_sort range) + { + Debug.Assert(!string.IsNullOrEmpty(name)); + Debug.Assert(range != IntPtr.Zero); + Debug.Assert(domain != null); + Debug.Assert(domain.All(d => d != IntPtr.Zero)); + + var symbol = Native.Z3_mk_string_symbol(nCtx, name); + return Native.Z3_mk_func_decl(nCtx, symbol, (uint)(domain?.Length ?? 0), domain, range); + } + + /// + /// Creates a new function declaration. + /// + public Z3_func_decl MkFuncDecl(string name, Z3_sort domain, Z3_sort range) + { + Debug.Assert(!string.IsNullOrEmpty(name)); + Debug.Assert(range != IntPtr.Zero); + Debug.Assert(domain != IntPtr.Zero); + + var symbol = Native.Z3_mk_string_symbol(nCtx, name); + var q = new Z3_sort[] { domain }; + return Native.Z3_mk_func_decl(nCtx, symbol, (uint)q.Length, q, range); + } + + /// + /// Creates a fresh function declaration with a name prefixed with . + /// + public Z3_func_decl MkFreshFuncDecl(string prefix, Z3_sort[] domain, Z3_sort range) + { + Debug.Assert(domain != null); + Debug.Assert(range != IntPtr.Zero); + Debug.Assert(domain.All(d => d != IntPtr.Zero)); + + return Native.Z3_mk_fresh_func_decl(nCtx, prefix, (uint)(domain?.Length ?? 0), domain, range); + } + + /// + /// Creates a new constant function declaration. + /// + public Z3_func_decl MkConstDecl(string name, Z3_sort range) + { + Debug.Assert(range != IntPtr.Zero); + + var symbol = Native.Z3_mk_string_symbol(nCtx, name); + return Native.Z3_mk_func_decl(nCtx, symbol, 0, new IntPtr[0], range); + } + + /// + /// Get domain for a funcdecl + /// + /// + /// + public Z3_sort[] GetDomain(Z3_func_decl fdecl) + { + Debug.Assert(fdecl != IntPtr.Zero); + + var sz = Native.Z3_get_domain_size(nCtx, fdecl); + var domain = new Z3_sort[sz]; + for (uint i = 0; i < sz; i++) + { + domain[i] = Native.Z3_get_domain(nCtx, fdecl, i); + } + return domain; + } + + /// + /// Get range for a funcdecl + /// + /// + /// + public Z3_sort GetRange(Z3_func_decl fdecl) + { + Debug.Assert(fdecl != IntPtr.Zero); + + return Native.Z3_get_range(nCtx, fdecl); + } + + #endregion + + #region Quantifier Patterns + /// + /// Create a quantifier pattern. + /// + public Z3_pattern MkPattern(params Z3_ast[] terms) + { + Debug.Assert(terms != null); + if (terms == null || terms.Length == 0) + throw new Z3Exception("Cannot create a pattern from zero terms"); + + return Native.Z3_mk_pattern(nCtx, (uint)terms.Length, terms); + } + #endregion + + #region Solver + + /// + /// Creates a new (incremental) solver. + /// + public NativeSolver MkSimpleSolver() + { + Z3_solver nSolver = Native.Z3_mk_simple_solver(nCtx); + return new NativeSolver(this, nSolver); + } + + #endregion + + #region Utilities + /// + /// Get the sort kind from IntPtr + /// + public Z3_sort_kind GetSortKind(Z3_sort sort) + { + Debug.Assert(sort != IntPtr.Zero); + + return (Z3_sort_kind)Native.Z3_get_sort_kind(nCtx, sort); + } + + /// + /// Get the AST kind from IntPtr + /// + public Z3_ast_kind GetAstKind(Z3_ast ast) + { + Debug.Assert(ast != IntPtr.Zero); + + return (Z3_ast_kind)Native.Z3_get_ast_kind(nCtx, ast); + } + + /// + /// Get the Decl kind from IntPtr + /// + public Z3_decl_kind GetDeclKind(Z3_func_decl decl) + { + Debug.Assert(decl != IntPtr.Zero); + + return (Z3_decl_kind)Native.Z3_get_decl_kind(nCtx, decl); + } + + /// + /// Get Sort for AST + /// + public Z3_sort GetSort(Z3_ast ast) + { + Debug.Assert(ast != IntPtr.Zero); + + return Native.Z3_get_sort(nCtx, ast); + } + + /// + /// Get the arguments for app + /// + /// + /// + public Z3_ast[] GetAppArgs(Z3_app app) + { + var numArgs = GetNumArgs(app); + var args = new Z3_ast[numArgs]; + for (uint i = 0; i < numArgs; i++) + { + args[i] = GetAppArg(app, i); + } + return args; + } + + /// + /// Return number of arguments for app + /// + /// + /// + public uint GetNumArgs(Z3_app app) + { + Debug.Assert(app != IntPtr.Zero); + + return Native.Z3_get_app_num_args(nCtx, app); + } + + internal Z3_ast GetAppArg(Z3_app app, uint i) => Native.Z3_get_app_arg(nCtx, app, i); + + /// + /// Get App Decl from IntPtr + /// + public Z3_func_decl GetAppDecl(Z3_ast ast) + { + Debug.Assert(ast != IntPtr.Zero); + + return Native.Z3_get_app_decl(nCtx, ast); + } + + /// + /// Get string name for Decl + /// + /// + /// + public string GetDeclName(Z3_func_decl decl) + { + Debug.Assert(decl != IntPtr.Zero); + + var namePtr = Native.Z3_get_decl_name(nCtx, decl); + return Marshal.PtrToStringAnsi(namePtr); + } + + /// + /// Get size of BitVector Sort + /// + public uint GetBvSortSize(Z3_sort bvSort) + { + Debug.Assert(bvSort != IntPtr.Zero); + + return Native.Z3_get_bv_sort_size(nCtx, bvSort); + } + + /// + /// Get the domain IntPtr for Sort + /// + public Z3_sort GetArraySortDomain(Z3_ast array) + { + Debug.Assert(array != IntPtr.Zero); + + return Native.Z3_get_array_sort_domain(nCtx, array); + } + + /// + /// Get the range IntPtr for Sort + /// + public Z3_sort GetArraySortRange(Z3_ast array) + { + Debug.Assert(array != IntPtr.Zero); + + return Native.Z3_get_array_sort_range(nCtx, array); + } + + /// + /// Try to get integer from AST + /// + /// + /// + /// + public bool TryGetNumeralInt(Z3_ast v, out int i) + { + Debug.Assert(v != IntPtr.Zero); + + int result = i = 0; + if (Native.Z3_get_numeral_int(nCtx, v, ref result) == 0) + { + return false; + } + i = result; + return true; + } + + /// + /// Try to get uint from AST + /// + /// + /// + /// + public bool TryGetNumeralUInt(Z3_ast v, out uint u) + { + Debug.Assert(v != IntPtr.Zero); + + uint result = u = 0; + if (Native.Z3_get_numeral_uint(nCtx, v, ref result) == 0) + { + return false; + } + u = result; + return true; + } + + /// + /// Try to get long from AST + /// + /// + /// + /// + public bool TryGetNumeralInt64(Z3_ast v, out long i) + { + Debug.Assert(v != IntPtr.Zero); + + long result = i = 0; + if (Native.Z3_get_numeral_int64(nCtx, v, ref result) == 0) + { + return false; + } + i = result; + return true; + } + + /// + /// Try get ulong from AST + /// + /// + /// + /// + public bool TryGetNumeralUInt64(Z3_ast v, out ulong u) + { + Debug.Assert(v != IntPtr.Zero); + + ulong result = u = 0; + if (Native.Z3_get_numeral_uint64(nCtx, v, ref result) == 0) + { + return false; + } + u = result; + return true; + } + + /// + /// Get string for numeral ast + /// + /// + /// + public string GetNumeralString(Z3_ast v) + { + Debug.Assert(v != IntPtr.Zero); + return Native.Z3_get_numeral_string(nCtx, v); + } + + /// + /// Get printable string representing Z3_ast + /// + /// + /// + public string ToString(Z3_ast ast) + { + Debug.Assert(ast != IntPtr.Zero); + + return Native.Z3_ast_to_string(nCtx, ast); + } + + /// + /// Enable or disable warning messages + /// + /// + public void ToggleWarningMessages(bool turnOn) + => Native.Z3_toggle_warning_messages(turnOn ? (byte)1 : (byte)0); + + #endregion + + #region Internal + internal static Object creation_lock = new Object(); + internal IntPtr m_ctx = IntPtr.Zero; + internal Native.Z3_error_handler m_n_err_handler = null; + internal IntPtr nCtx { get { return m_ctx; } } + + internal void NativeErrorHandler(IntPtr ctx, Z3_error_code errorCode) + { + // Do-nothing error handler. The wrappers in Z3.Native will throw exceptions upon errors. + } + + internal void InitContext() + { + PrintMode = Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT; + m_n_err_handler = new Native.Z3_error_handler(NativeErrorHandler); // keep reference so it doesn't get collected. + Native.Z3_set_error_handler(m_ctx, m_n_err_handler); + + GC.SuppressFinalize(this); + } + + #endregion + + #region Tracing + /// + /// Enable tracint to file + /// + /// + public void TraceToFile(string file) + { + Debug.Assert(!string.IsNullOrEmpty(file)); + Native.Z3_enable_trace(file); + } + + #endregion + + #region Dispose + + /// + /// Disposes of the context. + /// + public void Dispose() + { + if (m_ctx != IntPtr.Zero) + { + m_n_err_handler = null; + IntPtr ctx = m_ctx; + m_ctx = IntPtr.Zero; + Native.Z3_del_context(ctx); + } + else + GC.ReRegisterForFinalize(this); + } + #endregion + + + /// + /// Utility to convert a vector object of ast to a .Net array + /// + /// + /// + public Z3_ast[] ToArray(Z3_ast_vector vec) + { + Native.Z3_ast_vector_inc_ref(nCtx, vec); + var sz = Native.Z3_ast_vector_size(nCtx, vec); + var result = new Z3_ast[sz]; + for (uint i = 0; i < sz; ++i) + result[i] = Native.Z3_ast_vector_get(nCtx, vec, i); + Native.Z3_ast_vector_dec_ref(nCtx, vec); + return result; + } + + /// + /// Retrieve statistics as an array of entries + /// + /// + /// + public Statistics.Entry[] GetStatistics(Z3_stats stats) + { + Native.Z3_stats_inc_ref(nCtx, stats); + var result = Statistics.NativeEntries(nCtx, stats); + Native.Z3_stats_dec_ref(nCtx, stats); + return result; + } + + } +} \ No newline at end of file diff --git a/src/api/dotnet/NativeFuncInterp.cs b/src/api/dotnet/NativeFuncInterp.cs new file mode 100644 index 000000000..0f446fa6f --- /dev/null +++ b/src/api/dotnet/NativeFuncInterp.cs @@ -0,0 +1,104 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + NativeFuncInterp.cs + +Abstract: + + Z3 Managed API: Function Interpretations + +Author: + + Christoph Wintersteiger (cwinter) 2012-03-21 + +Notes: + +--*/ + +using System.Diagnostics; +using System; + +namespace Microsoft.Z3 +{ + + using Z3_context = System.IntPtr; + using Z3_ast = System.IntPtr; + using Z3_app = System.IntPtr; + using Z3_sort = System.IntPtr; + using Z3_func_decl = System.IntPtr; + using Z3_model = System.IntPtr; + using Z3_func_interp = System.IntPtr; + using Z3_func_entry = System.IntPtr; + + /// + /// A function interpretation is represented as a finite map and an 'else' value. + /// Each entry in the finite map represents the value of a function given a set of arguments. + /// + public class NativeFuncInterp + { + + /// + /// Evaluation entry of a function + /// + public class Entry + { + /// + /// Argument values that define entry + /// + public Z3_ast[] Arguments; + + /// + /// Result of applying function to Arguments in the interpretation + /// + public Z3_ast Result; + } + + /// + /// Function that is interpreted + /// + public Z3_func_decl Declaration; + + /// + /// Set of non-default entries defining the function graph + /// + public Entry[] Entries; + + /// + /// Default cause of the function interpretation + /// + public Z3_ast Else; + + #region Internal + internal NativeFuncInterp(NativeContext ctx, NativeModel mdl, Z3_func_decl decl, Z3_func_interp fi) + { + Debug.Assert(ctx != null); + Z3_context nCtx = ctx.nCtx; + Native.Z3_func_interp_inc_ref(nCtx, fi); + + Declaration = decl; + Else = Native.Z3_func_interp_get_else(nCtx, fi); + uint numEntries = Native.Z3_func_interp_get_num_entries(nCtx, fi); + uint numArgs = Native.Z3_func_interp_get_arity(nCtx, fi); + Entries = new Entry[numEntries]; + + for (uint j = 0; j < numEntries; ++j) + { + 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, 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); + } + + + #endregion + } +} diff --git a/src/api/dotnet/NativeModel.cs b/src/api/dotnet/NativeModel.cs new file mode 100644 index 000000000..3b9eb0950 --- /dev/null +++ b/src/api/dotnet/NativeModel.cs @@ -0,0 +1,383 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + NativeModel.cs + +Abstract: + + Z3 Managed API: Models + Native interface to model objects. + +Author: + + Christoph Wintersteiger (cwinter) 2012-03-21 + Nikolaj Bjorner (nbjorner) 2022-03-01 + +Notes: + +--*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; + +namespace Microsoft.Z3 +{ + using Z3_ast = System.IntPtr; + using Z3_func_decl = System.IntPtr; + using Z3_sort = System.IntPtr; + + /// + /// A Model contains interpretations (assignments) of constants and functions. + /// + public class NativeModel : IDisposable + { + + /// + /// Retrieves the interpretation (the assignment) of in the model. + /// + /// A Constant + /// An expression if the constant has an interpretation in the model, null otherwise. + public Z3_ast ConstInterp(Z3_ast a) => ConstFuncInterp(Native.Z3_get_app_decl(ntvContext.nCtx, a)); + + /// + /// Retrieves the interpretation (the assignment) of in the model. + /// + /// A function declaration of zero arity + /// An expression if the function has an interpretation in the model, null otherwise. + public Z3_ast ConstFuncInterp(Z3_func_decl f) + { + if (Native.Z3_get_arity(ntvContext.nCtx, f) != 0) + throw new Z3Exception("Non-zero arity functions have FunctionInterpretations as a model. Use FuncInterp."); + + return Native.Z3_model_get_const_interp(ntvContext.nCtx, NativeObject, f); + } + + /// + /// Retrieves the interpretation (the assignment) of a non-constant in the model. + /// + /// A function declaration of non-zero arity + /// A FunctionInterpretation if the function has an interpretation in the model, null otherwise. + public NativeFuncInterp FuncInterp(Z3_func_decl f) + { + Z3_sort_kind sk = (Z3_sort_kind)Native.Z3_get_sort_kind(ntvContext.nCtx, Native.Z3_get_range(ntvContext.nCtx, f)); + + if (Native.Z3_get_arity(ntvContext.nCtx, f) == 0) + { + IntPtr n = Native.Z3_model_get_const_interp(ntvContext.nCtx, NativeObject, f); + + if (sk == Z3_sort_kind.Z3_ARRAY_SORT) + { + if (n == IntPtr.Zero) + return null; + else + { + if (Native.Z3_is_as_array(ntvContext.nCtx, n) == 0) + throw new Z3Exception("Argument was not an array constant"); + var fd = Native.Z3_get_as_array_func_decl(ntvContext.nCtx, n); + return new NativeFuncInterp(ntvContext, this, f, fd); + } + } + else + { + throw new Z3Exception("Constant functions do not have a function interpretation; use ConstInterp"); + } + } + else + { + IntPtr n = Native.Z3_model_get_func_interp(ntvContext.nCtx, NativeObject, f); + if (n == IntPtr.Zero) + return null; + else + return new NativeFuncInterp(ntvContext, this, f, n); + } + } + + + + /// + /// The number of constants that have an interpretation in the model. + /// + public uint NumConsts + { + get { return Native.Z3_model_get_num_consts(ntvContext.nCtx, NativeObject); } + } + + + /// + /// The function declarations of the constants in the model. + /// + public Z3_func_decl[] ConstDecls + { + get + { + + uint n = NumConsts; + Z3_func_decl[] res = new Z3_func_decl[n]; + for (uint i = 0; i < n; i++) + res[i] = Native.Z3_model_get_const_decl(ntvContext.nCtx, NativeObject, i); + return res; + } + } + + + /// + /// Enumerate constants in model. + /// + public IEnumerable> Consts + { + get + { + uint nc = NumConsts; + for (uint i = 0; i < nc; ++i) + { + var f = Native.Z3_model_get_const_decl(ntvContext.nCtx, NativeObject, i); + IntPtr n = Native.Z3_model_get_const_interp(ntvContext.nCtx, NativeObject, f); + if (n == IntPtr.Zero) continue; + yield return new KeyValuePair(f, n); + } + } + } + + /// + /// The number of function interpretations in the model. + /// + public uint NumFuncs + { + get { return Native.Z3_model_get_num_funcs(ntvContext.nCtx, NativeObject); } + } + + /// + /// The function declarations of the function interpretations in the model. + /// + public Z3_func_decl[] FuncDecls + { + get + { + + uint n = NumFuncs; + Z3_func_decl[] res = new Z3_func_decl[n]; + for (uint i = 0; i < n; i++) + res[i] = Native.Z3_model_get_func_decl(ntvContext.nCtx, NativeObject, i); + return res; + } + } + + /// + /// All symbols that have an interpretation in the model. + /// + public Z3_func_decl[] Decls + { + get + { + + uint nFuncs = NumFuncs; + uint nConsts = NumConsts; + uint n = nFuncs + nConsts; + Z3_func_decl[] res = new Z3_func_decl[n]; + for (uint i = 0; i < nConsts; i++) + res[i] = Native.Z3_model_get_const_decl(ntvContext.nCtx, NativeObject, i); + for (uint i = 0; i < nFuncs; i++) + res[nConsts + i] = Native.Z3_model_get_func_decl(ntvContext.nCtx, NativeObject, i); + return res; + } + } + + /// + /// A ModelEvaluationFailedException is thrown when an expression cannot be evaluated by the model. + /// + public class ModelEvaluationFailedException : Z3Exception + { + /// + /// An exception that is thrown when model evaluation fails. + /// + public ModelEvaluationFailedException() : base() { } + } + + /// + /// Evaluates the expression in the current model. + /// + /// + /// This function may fail if contains quantifiers, + /// is partial (MODEL_PARTIAL enabled), or if is not well-sorted. + /// In this case a ModelEvaluationFailedException is thrown. + /// + /// An expression + /// + /// When this flag is enabled, a model value will be assigned to any constant + /// or function that does not have an interpretation in the model. + /// + /// The evaluation of in the model. + public Z3_ast Eval(Z3_ast t, bool completion = false) + { + + IntPtr v = IntPtr.Zero; + if (Native.Z3_model_eval(ntvContext.nCtx, NativeObject, t, (byte)(completion ? 1 : 0), ref v) == (byte)0) + throw new ModelEvaluationFailedException(); + else + return v; + } + + /// + /// Alias for Eval. + /// + public Z3_ast Evaluate(Z3_ast t, bool completion = false) => Eval(t, completion); + + /// + /// Evaluate expression to a double, assuming it is a numeral already. + /// + public double Double(Z3_ast t) + { + var r = Eval(t, true); + return Native.Z3_get_numeral_double(ntvContext.nCtx, r); + } + + /// + /// An array value obtained by untangling a model assignment. + /// + public class ArrayValue + { + /// + /// One dimensional array of indices where the array is updated + /// + public KeyValuePair[] Updates; + + /// + /// default Else case + /// + public Z3_ast Else; + + /// + /// Domain for array + /// Updates.Keys + /// + public Z3_ast[] Domain; + + /// + /// Range for array + /// Updates.Values + /// + public Z3_ast[] Range; + } + + /// + /// Convert the interpretation of t into a sequence of array updates + /// + /// + /// + /// null if the argument does evaluate to a sequence of stores to an array + public bool TryGetArrayValue(Z3_ast t, out ArrayValue result) + { + var r = Eval(t, true); + // check that r is a sequence of store over a constant default array. + var updates = new Dictionary(); + result = null; + while (true) + { + if (ntvContext.GetAstKind(r) != Z3_ast_kind.Z3_APP_AST) + return false; + Z3_func_decl f = ntvContext.GetAppDecl(r); + var kind = ntvContext.GetDeclKind(f); + if (kind == Z3_decl_kind.Z3_OP_CONST_ARRAY) + { + result = new ArrayValue(); + result.Else = ntvContext.GetAppArg(r, 0); + result.Updates = updates.ToArray(); + result.Domain = updates.Keys.ToArray(); + result.Range = updates.Values.ToArray(); + return true; + } + else if (kind == Z3_decl_kind.Z3_OP_STORE) + { + Debug.Assert(ntvContext.GetNumArgs(r) == 3); + updates[ntvContext.GetAppArg(r, 1)] = ntvContext.GetAppArg(r, 2); + r = ntvContext.GetAppArg(r, 0); + } + else + { + return false; + } + } + } + + /// + /// The number of uninterpreted sorts that the model has an interpretation for. + /// + public uint NumSorts { get { return Native.Z3_model_get_num_sorts(ntvContext.nCtx, NativeObject); } } + + + /// + /// The uninterpreted sorts that the model has an interpretation for. + /// + /// + /// Z3 also provides an interpretation for uninterpreted sorts used in a formula. + /// The interpretation for a sort is a finite set of distinct values. We say this finite set is + /// the "universe" of the sort. + /// + /// + public Z3_sort[] Sorts + { + get + { + + uint n = NumSorts; + Z3_sort[] res = new Z3_sort[n]; + for (uint i = 0; i < n; i++) + res[i] = Native.Z3_model_get_sort(ntvContext.nCtx, NativeObject, i); + return res; + } + } + + + /// + /// Conversion of models to strings. + /// + /// A string representation of the model. + public override string ToString() + { + return Native.Z3_model_to_string(ntvContext.nCtx, NativeObject); + } + + + IntPtr NativeObject; + NativeContext ntvContext; + + internal NativeModel(NativeContext ctx, IntPtr obj) + { + ntvContext = ctx; + NativeObject = obj; + Debug.Assert(ctx != null); + Native.Z3_model_inc_ref(ctx.nCtx, obj); + } + + + /// + /// Finalizer. + /// + ~NativeModel() + { + Dispose(); + } + + /// + /// Disposes of the underlying native Z3 object. + /// + public void Dispose() + { + if (NativeObject != IntPtr.Zero) + { + Native.Z3_model_dec_ref(ntvContext.nCtx, NativeObject); + NativeObject = IntPtr.Zero; + } + GC.SuppressFinalize(this); + } + + + } +} + + + diff --git a/src/api/dotnet/NativeSolver.cs b/src/api/dotnet/NativeSolver.cs new file mode 100644 index 000000000..7dc937234 --- /dev/null +++ b/src/api/dotnet/NativeSolver.cs @@ -0,0 +1,451 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + NativeSolver.cs + +Abstract: + + Z3 Managed API: Native Solver + +Author: + + Christoph Wintersteiger (cwinter) 2012-03-22 + Nikolaj Bjorner (nbjorner) 2022-03-01 + +Notes: + +--*/ + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Z3 +{ + + using Z3_ast = System.IntPtr; + using Z3_context = System.IntPtr; + using Z3_func_decl = System.IntPtr; + using Z3_params = System.IntPtr; + using Z3_solver = System.IntPtr; + using Z3_sort = System.IntPtr; + using Z3_symbol = System.IntPtr; + + /// + /// Solvers. + /// + public class NativeSolver : IDisposable + { + /// + /// A string that describes all available solver parameters. + /// + public string Help => Native.Z3_solver_get_help(nCtx, z3solver); + + private void SetParam(Action setter) + { + Z3_params p = Native.Z3_mk_params(nCtx); + Native.Z3_params_inc_ref(nCtx, p); + setter(p); + Native.Z3_solver_set_params(nCtx, z3solver, p); + Native.Z3_params_dec_ref(nCtx, p); + } + + /// + /// Sets parameter on the solver + /// + public void Set(string name, bool value) + { + SetParam((Z3_params p) => Native.Z3_params_set_bool(nCtx, p, Native.Z3_mk_string_symbol(nCtx, name), (byte)(value ? 1 : 0))); + } + + /// + /// Sets parameter on the solver + /// + public void Set(string name, uint value) + { + SetParam((Z3_params p) => Native.Z3_params_set_uint(nCtx, p, Native.Z3_mk_string_symbol(nCtx, name), value)); + } + + /// + /// Sets parameter on the solver + /// + public void Set(string name, double value) + { + SetParam((Z3_params p) => Native.Z3_params_set_double(nCtx, p, Native.Z3_mk_string_symbol(nCtx, name), value)); + } + + /// + /// Sets parameter on the solver + /// + public void Set(string name, string value) + { + var value_sym = Native.Z3_mk_string_symbol(nCtx, value); + SetParam((Z3_params p) => Native.Z3_params_set_symbol(nCtx, p, Native.Z3_mk_string_symbol(nCtx, name), value_sym)); + } + +#if false + /// + /// Sets parameter on the solver + /// + public void Set(string name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } + /// + /// Sets parameter on the solver + /// + public void Set(Symbol name, bool value) { Parameters = Context.MkParams().Add(name, value); } + /// + /// Sets parameter on the solver + /// + public void Set(Symbol name, uint value) { Parameters = Context.MkParams().Add(name, value); } + /// + /// Sets parameter on the solver + /// + public void Set(Symbol name, double value) { Parameters = Context.MkParams().Add(name, value); } + /// + /// Sets parameter on the solver + /// + public void Set(Symbol name, string value) { Parameters = Context.MkParams().Add(name, value); } + /// + /// Sets parameter on the solver + /// + public void Set(Symbol name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } + + /// + /// Retrieves parameter descriptions for solver. + /// + public ParamDescrs ParameterDescriptions + { + get { return new ParamDescrs(Context, Native.Z3_solver_get_param_descrs(nCtx, NativeObject)); } + } +#endif + + /// + /// The current number of backtracking points (scopes). + /// + /// + /// + public uint NumScopes => Native.Z3_solver_get_num_scopes(nCtx, z3solver); + + /// + /// Creates a backtracking point. + /// + /// + public void Push() => Native.Z3_solver_push(nCtx, z3solver); + + /// + /// Backtracks backtracking points. + /// + /// Note that an exception is thrown if is not smaller than NumScopes + /// + public void Pop(uint n = 1) => Native.Z3_solver_pop(nCtx, z3solver, n); + + /// + /// Resets the Solver. + /// + /// This removes all assertions from the solver. + public void Reset() => Native.Z3_solver_reset(nCtx, z3solver); + + /// + /// Assert a constraint (or multiple) into the solver. + /// + public void Assert(params Z3_ast[] constraints) + { + Debug.Assert(constraints != null); + Debug.Assert(constraints.All(c => c != IntPtr.Zero)); + + foreach (Z3_ast a in constraints) + { + Native.Z3_solver_assert(nCtx, z3solver, a); + } + } + + /// + /// Alias for Assert. + /// + public void Add(params Z3_ast[] constraints) => Assert(constraints); + + /// + /// Alias for Assert. + /// + public void Add(IEnumerable constraints) => Assert(constraints.ToArray()); + + /// + /// Add constraints to ensure the function f can only be injective. + /// Example: + /// for function f : D1 x D2 -> R + /// assert axioms + /// forall (x1 : D1, x2 : D2) x1 = inv1(f(x1,x2)) + /// forall (x1 : D1, x2 : D2) x2 = inv2(f(x1,x2)) + /// + /// + public void AssertInjective(Z3_func_decl f) + { + uint arity = Native.Z3_get_arity(nCtx, f); + Z3_sort range = Native.Z3_get_range(nCtx, f); + Z3_ast[] vars = new Z3_ast[arity]; + Z3_sort[] sorts = new Z3_sort[arity]; + Z3_symbol[] names = new Z3_symbol[arity]; + for (uint i = 0; i < arity; ++i) + { + Z3_sort domain = Native.Z3_get_domain(nCtx, f, i); + vars[i] = ntvContext.MkBound(arity - i - 1, domain); + sorts[i] = domain; + names[i] = Native.Z3_mk_int_symbol(nCtx, (int)i); + } + Z3_ast app_f = IntPtr.Zero; // Context.MkApp(f, vars); + for (uint i = 0; i < arity; ++i) + { + Z3_sort domain = Native.Z3_get_domain(nCtx, f, i); + Z3_func_decl proj = ntvContext.MkFreshFuncDecl("inv", new Z3_sort[] { range }, domain); + Z3_ast body = ntvContext.MkEq(vars[i], ntvContext.MkApp(proj, app_f)); + Z3_ast q = ntvContext.MkForall(names, sorts, body); + Assert(q); + } + } + + /// + /// Assert multiple constraints into the solver, and track them (in the unsat) core + /// using the Boolean constants in 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 + /// provided using with assumptions. + /// + public void AssertAndTrack(Z3_ast[] constraints, Z3_ast[] ps) + { + Debug.Assert(constraints != null); + Debug.Assert(constraints.All(c => c != IntPtr.Zero)); + Debug.Assert(ps.All(c => c != IntPtr.Zero)); + if (constraints.Length != ps.Length) + throw new Z3Exception("Argument size mismatch"); + + for (int i = 0; i < constraints.Length; i++) + Native.Z3_solver_assert_and_track(nCtx, z3solver, constraints[i], ps[i]); + } + + /// + /// Assert a constraint into the solver, and track it (in the unsat) core + /// using the Boolean constant p. + /// + /// + /// 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 + /// provided using with assumptions. + /// + public void AssertAndTrack(Z3_ast constraint, Z3_ast p) + { + Debug.Assert(constraint != null); + Debug.Assert(p != null); + + Native.Z3_solver_assert_and_track(nCtx, z3solver, constraint, p); + } + + /// + /// Load solver assertions from a file. + /// + public void FromFile(string file) + => Native.Z3_solver_from_file(nCtx, z3solver, file); + + /// + /// Load solver assertions from a string. + /// + public void FromString(string str) + => Native.Z3_solver_from_string(nCtx, z3solver, str); + + /// + /// The number of assertions in the solver. + /// + public uint NumAssertions + => (uint)ntvContext.ToArray(Native.Z3_solver_get_assertions(nCtx, z3solver)).Length; + + /// + /// The set of asserted formulas. + /// + public Z3_ast[] Assertions + => ntvContext.ToArray(Native.Z3_solver_get_assertions(nCtx, z3solver)); + + /// + /// Currently inferred units. + /// + public Z3_ast[] Units + => ntvContext.ToArray(Native.Z3_solver_get_units(nCtx, z3solver)); + + /// + /// Checks whether the assertions in the solver are consistent or not. + /// + /// + /// + /// + /// + /// + public Status Check(params Z3_ast[] assumptions) + { + Z3_lbool r; + if (assumptions == null || assumptions.Length == 0) + r = (Z3_lbool)Native.Z3_solver_check(nCtx, z3solver); + else + r = (Z3_lbool)Native.Z3_solver_check_assumptions(nCtx, z3solver, (uint)assumptions.Length, assumptions); + return lboolToStatus(r); + } + + /// + /// Checks whether the assertions in the solver are consistent or not. + /// + /// + /// + /// + /// + /// + public Status Check(IEnumerable assumptions) + { + Z3_lbool r; + Z3_ast[] asms = assumptions.ToArray(); + if (asms.Length == 0) + r = (Z3_lbool)Native.Z3_solver_check(nCtx, z3solver); + else + r = (Z3_lbool)Native.Z3_solver_check_assumptions(nCtx, z3solver, (uint)asms.Length, asms); + return lboolToStatus(r); + } + + /// + /// The model of the last Check(params Expr[] assumptions). + /// + /// + /// The result is null if Check(params Expr[] assumptions) was not invoked before, + /// if its results was not SATISFIABLE, or if model production is not enabled. + /// + public NativeModel Model + { + get + { + IntPtr x = Native.Z3_solver_get_model(nCtx, z3solver); + return x == IntPtr.Zero + ? null + : new NativeModel(ntvContext, x); + } + } + + /// + /// The proof of the last Check(params Expr[] assumptions). + /// + /// + /// The result is null if Check(params Expr[] assumptions) was not invoked before, + /// if its results was not UNSATISFIABLE, or if proof production is disabled. + /// + public Z3_ast Proof + => Native.Z3_solver_get_proof(nCtx, z3solver); + + /// + /// The unsat core of the last Check. + /// + /// + /// The unsat core is a subset of Assertions + /// The result is empty if Check was not invoked before, + /// if its results was not UNSATISFIABLE, or if core production is disabled. + /// + public Z3_ast[] UnsatCore + => ntvContext.ToArray(Native.Z3_solver_get_unsat_core(nCtx, z3solver)); + + /// + /// A brief justification of why the last call to Check returned UNKNOWN. + /// + public string ReasonUnknown + => Native.Z3_solver_get_reason_unknown(nCtx, z3solver); + + /// + /// Create a clone of the current solver with respect to ctx. + /// + public NativeSolver Translate(NativeContext ctx) + { + Debug.Assert(ctx != null); + return new NativeSolver(ctx, Native.Z3_solver_translate(nCtx, z3solver, ctx.nCtx)); + } + + /// + /// Import model converter from other solver. + /// + public void ImportModelConverter(NativeSolver src) + { + Debug.Assert(src != null); + + Native.Z3_solver_import_model_converter(nCtx, src.z3solver, z3solver); + } + + /// + /// Solver statistics. + /// + public Statistics.Entry[] Statistics + { + get + { + var stats = Native.Z3_solver_get_statistics(nCtx, z3solver); + return ntvContext.GetStatistics(stats); + } + } + + /// + /// A string representation of the solver. + /// + public override string ToString() + { + return Native.Z3_solver_to_string(nCtx, z3solver); + } + + #region Internal + readonly NativeContext ntvContext; + Z3_solver z3solver; + Z3_context nCtx => ntvContext.nCtx; + + internal NativeSolver(NativeContext nativeCtx, Z3_solver z3solver) + { + Debug.Assert(nativeCtx != null); + Debug.Assert(z3solver != IntPtr.Zero); + + this.ntvContext = nativeCtx; + this.z3solver = z3solver; + + Native.Z3_solver_inc_ref(nCtx, z3solver); + } + + /// + /// Finalizer. + /// + ~NativeSolver() + { + Dispose(); + } + + /// + /// Disposes of the underlying native Z3 object. + /// + public void Dispose() + { + if (z3solver != IntPtr.Zero) + { + Native.Z3_solver_dec_ref(nCtx, z3solver); + z3solver = IntPtr.Zero; + } + GC.SuppressFinalize(this); + } + + + private Status lboolToStatus(Z3_lbool r) + { + switch (r) + { + case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; + case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; + default: return Status.UNKNOWN; + } + } + + #endregion + } +} diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index 66b0df82c..ecd5e8e82 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -212,7 +212,7 @@ namespace Microsoft.Z3 public Handle AssertSoft(BoolExpr constraint, uint weight, string group) { Context.CheckContextMatch(constraint); - Symbol s = Context.MkSymbol(group); + using Symbol s = Context.MkSymbol(group); return new Handle(this, Native.Z3_optimize_assert_soft(Context.nCtx, NativeObject, constraint.NativeObject, weight.ToString(), s.NativeObject)); } @@ -289,7 +289,7 @@ namespace Microsoft.Z3 get { - ASTVector core = new ASTVector(Context, Native.Z3_optimize_get_unsat_core(Context.nCtx, NativeObject)); + using ASTVector core = new ASTVector(Context, Native.Z3_optimize_get_unsat_core(Context.nCtx, NativeObject)); return core.ToBoolExprArray(); } } @@ -337,7 +337,7 @@ namespace Microsoft.Z3 /// private Expr[] GetLowerAsVector(uint index) { - ASTVector v = new ASTVector(Context, Native.Z3_optimize_get_lower_as_vector(Context.nCtx, NativeObject, index)); + using ASTVector v = new ASTVector(Context, Native.Z3_optimize_get_lower_as_vector(Context.nCtx, NativeObject, index)); return v.ToExprArray(); } @@ -347,7 +347,7 @@ namespace Microsoft.Z3 /// private Expr[] GetUpperAsVector(uint index) { - ASTVector v = new ASTVector(Context, Native.Z3_optimize_get_upper_as_vector(Context.nCtx, NativeObject, index)); + using ASTVector v = new ASTVector(Context, Native.Z3_optimize_get_upper_as_vector(Context.nCtx, NativeObject, index)); return v.ToExprArray(); } @@ -396,7 +396,7 @@ namespace Microsoft.Z3 get { - ASTVector assertions = new ASTVector(Context, Native.Z3_optimize_get_assertions(Context.nCtx, NativeObject)); + using ASTVector assertions = new ASTVector(Context, Native.Z3_optimize_get_assertions(Context.nCtx, NativeObject)); return assertions.ToBoolExprArray(); } } @@ -409,7 +409,7 @@ namespace Microsoft.Z3 get { - ASTVector objectives = new ASTVector(Context, Native.Z3_optimize_get_objectives(Context.nCtx, NativeObject)); + using ASTVector objectives = new ASTVector(Context, Native.Z3_optimize_get_objectives(Context.nCtx, NativeObject)); return objectives.ToExprArray(); } } diff --git a/src/api/dotnet/RatNum.cs b/src/api/dotnet/RatNum.cs index 1d485a347..a1326d7a8 100644 --- a/src/api/dotnet/RatNum.cs +++ b/src/api/dotnet/RatNum.cs @@ -62,7 +62,7 @@ namespace Microsoft.Z3 { get { - IntNum n = Numerator; + using IntNum n = Numerator; return BigInteger.Parse(n.ToString()); } } @@ -74,7 +74,7 @@ namespace Microsoft.Z3 { get { - IntNum n = Denominator; + using IntNum n = Denominator; return BigInteger.Parse(n.ToString()); } } diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index ec53b14de..52f2ad993 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -58,43 +58,92 @@ namespace Microsoft.Z3 /// /// Sets parameter on the solver /// - public void Set(string name, bool value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(string name, bool value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } + /// /// Sets parameter on the solver /// - public void Set(string name, uint value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(string name, uint value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } + /// /// Sets parameter on the solver /// - public void Set(string name, double value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(string name, double value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } + /// /// Sets parameter on the solver /// - public void Set(string name, string value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(string name, string value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } + /// /// Sets parameter on the solver /// - public void Set(string name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(string name, Symbol value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } + /// /// Sets parameter on the solver /// - public void Set(Symbol name, bool value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(Symbol name, bool value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } + /// /// Sets parameter on the solver /// - public void Set(Symbol name, uint value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(Symbol name, uint value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } + /// /// Sets parameter on the solver /// - public void Set(Symbol name, double value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(Symbol name, double value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } + /// /// Sets parameter on the solver /// - public void Set(Symbol name, string value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(Symbol name, string value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } + /// /// Sets parameter on the solver /// - public void Set(Symbol name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } + public void Set(Symbol name, Symbol value) + { + using var parameters = Context.MkParams().Add(name, value); + Parameters = parameters; + } @@ -245,7 +294,7 @@ namespace Microsoft.Z3 { get { - ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); + using ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); return assertions.Size; } } @@ -258,7 +307,7 @@ namespace Microsoft.Z3 get { - ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); + using ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); return assertions.ToBoolExprArray(); } } @@ -271,7 +320,7 @@ namespace Microsoft.Z3 get { - ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_units(Context.nCtx, NativeObject)); + using ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_units(Context.nCtx, NativeObject)); return assertions.ToBoolExprArray(); } } @@ -330,9 +379,9 @@ namespace Microsoft.Z3 /// public Status Consequences(IEnumerable assumptions, IEnumerable variables, out BoolExpr[] consequences) { - ASTVector result = new ASTVector(Context); - ASTVector asms = new ASTVector(Context); - ASTVector vars = new ASTVector(Context); + using ASTVector result = new ASTVector(Context); + using ASTVector asms = new ASTVector(Context); + using ASTVector vars = new ASTVector(Context); foreach (var asm in assumptions) asms.Push(asm); foreach (var v in variables) vars.Push(v); Z3_lbool r = (Z3_lbool)Native.Z3_solver_get_consequences(Context.nCtx, NativeObject, asms.NativeObject, vars.NativeObject, result.NativeObject); @@ -391,7 +440,7 @@ namespace Microsoft.Z3 get { - ASTVector core = new ASTVector(Context, Native.Z3_solver_get_unsat_core(Context.nCtx, NativeObject)); + using ASTVector core = new ASTVector(Context, Native.Z3_solver_get_unsat_core(Context.nCtx, NativeObject)); return core.ToBoolExprArray(); } } @@ -424,14 +473,14 @@ namespace Microsoft.Z3 /// public IEnumerable Cube() { - ASTVector cv = new ASTVector(Context); + using ASTVector cv = new ASTVector(Context); if (CubeVariables != null) foreach (var b in CubeVariables) cv.Push(b); while (true) { var lvl = BacktrackLevel; BacktrackLevel = uint.MaxValue; - ASTVector r = new ASTVector(Context, Native.Z3_solver_cube(Context.nCtx, NativeObject, cv.NativeObject, lvl)); + using ASTVector r = new ASTVector(Context, Native.Z3_solver_cube(Context.nCtx, NativeObject, cv.NativeObject, lvl)); var v = r.ToBoolExprArray(); CubeVariables = cv.ToBoolExprArray(); if (v.Length == 1 && v[0].IsFalse) { diff --git a/src/api/dotnet/Statistics.cs b/src/api/dotnet/Statistics.cs index 8b664913a..f6d72e0c7 100644 --- a/src/api/dotnet/Statistics.cs +++ b/src/api/dotnet/Statistics.cs @@ -23,6 +23,9 @@ using System.Diagnostics; namespace Microsoft.Z3 { + + using Z3_context = System.IntPtr; + using Z3_stats = System.IntPtr; /// /// Objects of this class track statistical information about solvers. /// @@ -123,25 +126,29 @@ namespace Microsoft.Z3 { get { - - uint n = Size; - Entry[] res = new Entry[n]; - for (uint i = 0; i < n; i++) - { - Entry e; - string k = Native.Z3_stats_get_key(Context.nCtx, NativeObject, i); - if (Native.Z3_stats_is_uint(Context.nCtx, NativeObject, i) != 0) - e = new Entry(k, Native.Z3_stats_get_uint_value(Context.nCtx, NativeObject, i)); - else if (Native.Z3_stats_is_double(Context.nCtx, NativeObject, i) != 0) - e = new Entry(k, Native.Z3_stats_get_double_value(Context.nCtx, NativeObject, i)); - else - throw new Z3Exception("Unknown data entry value"); - res[i] = e; - } - return res; + return NativeEntries(Context.nCtx, NativeObject); } } + internal static Entry[] NativeEntries(Z3_context ctx, Z3_stats stats) + { + uint n = Native.Z3_stats_size(ctx, stats); + Entry[] res = new Entry[n]; + for (uint i = 0; i < n; i++) + { + Entry e; + string k = Native.Z3_stats_get_key(ctx, stats, i); + if (Native.Z3_stats_is_uint(ctx, stats, i) != 0) + e = new Entry(k, Native.Z3_stats_get_uint_value(ctx, stats, i)); + else if (Native.Z3_stats_is_double(ctx, stats, i) != 0) + e = new Entry(k, Native.Z3_stats_get_double_value(ctx, stats, i)); + else + throw new Z3Exception("Unknown data entry value"); + res[i] = e; + } + return res; + } + /// /// The statistical counters. /// diff --git a/src/api/java/Context.java b/src/api/java/Context.java index b22bedef7..7b5f8a936 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2106,6 +2106,26 @@ public class Context implements AutoCloseable { return (BoolExpr) Expr.create(this, Native.mkSeqContains(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } + /** + * Check if the string s1 is lexicographically strictly less than s2. + */ + + public BoolExpr MkStringLt(SeqSort s1, SeqSort s2) + { + checkContextMatch(s1, s2); + return new BoolExpr(this, Native.mkStrLt(nCtx(), s1.getNativeObject(), s2.getNativeObject())); + } + + /** + * Check if the string s1 is lexicographically less or equal to s2. + */ + public BoolExpr MkStringLe(SeqSort s1, SeqSort s2) + { + checkContextMatch(s1, s2); + return new BoolExpr(this, Native.mkStrLe(nCtx(), s1.getNativeObject(), s2.getNativeObject())); + } + + /** * Retrieve sequence of length one at index. */ @@ -2180,6 +2200,14 @@ public class Context implements AutoCloseable { return (ReExpr) Expr.create(this, Native.mkReStar(nCtx(), re.getNativeObject())); } + /** + * Create power regular expression. + */ + public ReExpr mkPower(Expr> re, int n) + { + return (ReExpr) Expr.create(this, Native.mkRePower(nCtx(), re.getNativeObject(), n)); + } + /** * Take the lower and upper-bounded Kleene star of a regular expression. */ @@ -4038,6 +4066,37 @@ public class Context implements AutoCloseable { return new BitVecExpr(this, Native.mkFpaToFpIntReal(nCtx(), rm.getNativeObject(), exp.getNativeObject(), sig.getNativeObject(), s.getNativeObject())); } + /** + * Creates or a linear order. + * @param index The index of the order. + * @param sort The sort of the order. + */ + public FuncDecl mkLinearOrder(R sort, int index) { + return (FuncDecl) FuncDecl.create( + this, + Native.mkLinearOrder( + nCtx(), + sort.getNativeObject(), + index + ) + ); + } + + /** + * Creates or a partial order. + * @param index The index of the order. + * @param sort The sort of the order. + */ + public FuncDecl mkPartialOrder(R sort, int index) { + return (FuncDecl) FuncDecl.create( + this, + Native.mkPartialOrder( + nCtx(), + sort.getNativeObject(), + index + ) + ); + } /** * Wraps an AST. diff --git a/src/api/ml/z3native_stubs.c.pre b/src/api/ml/z3native_stubs.c.pre index 895e20235..0efaa110f 100644 --- a/src/api/ml/z3native_stubs.c.pre +++ b/src/api/ml/z3native_stubs.c.pre @@ -294,7 +294,8 @@ static struct custom_operations Z3_ast_plus_custom_ops = { Z3_ast_compare_ext }; -MK_CTX_OF(ast, 16) // let's say 16 bytes per ast +// FUDGE +MK_CTX_OF(ast, 8) // let's say 16 bytes per ast #define MK_PLUS_OBJ_NO_REF(X, USED) \ typedef struct { \ @@ -410,25 +411,26 @@ MK_CTX_OF(ast, 16) // let's say 16 bytes per ast \ MK_CTX_OF(X, USED) -MK_PLUS_OBJ_NO_REF(symbol, 32) -MK_PLUS_OBJ_NO_REF(constructor, 32) -MK_PLUS_OBJ_NO_REF(constructor_list, 32) -MK_PLUS_OBJ_NO_REF(rcf_num, 32) -MK_PLUS_OBJ(params, 128) -MK_PLUS_OBJ(param_descrs, 128) -MK_PLUS_OBJ(model, 512) -MK_PLUS_OBJ(func_interp, 128) -MK_PLUS_OBJ(func_entry, 128) -MK_PLUS_OBJ(goal, 128) -MK_PLUS_OBJ(tactic, 128) -MK_PLUS_OBJ(probe, 128) -MK_PLUS_OBJ(apply_result, 128) -MK_PLUS_OBJ(solver, 20 * 1000 * 1000) // pretend a solver is 20MB -MK_PLUS_OBJ(stats, 128) -MK_PLUS_OBJ(ast_map, 1024 * 2) -MK_PLUS_OBJ(ast_vector, 128) -MK_PLUS_OBJ(fixedpoint, 20 * 1000 * 1000) -MK_PLUS_OBJ(optimize, 20 * 1000 * 1000) +// FUDGE +MK_PLUS_OBJ_NO_REF(symbol, 16) +MK_PLUS_OBJ_NO_REF(constructor, 16) +MK_PLUS_OBJ_NO_REF(constructor_list, 16) +MK_PLUS_OBJ_NO_REF(rcf_num, 16) +MK_PLUS_OBJ(params, 64) +MK_PLUS_OBJ(param_descrs, 64) +MK_PLUS_OBJ(model, 64) +MK_PLUS_OBJ(func_interp, 32) +MK_PLUS_OBJ(func_entry, 32) +MK_PLUS_OBJ(goal, 64) +MK_PLUS_OBJ(tactic, 64) +MK_PLUS_OBJ(probe, 64) +MK_PLUS_OBJ(apply_result, 32) +MK_PLUS_OBJ(solver, 20 * 1000) +MK_PLUS_OBJ(stats, 32) +MK_PLUS_OBJ(ast_map, 32) +MK_PLUS_OBJ(ast_vector, 32) +MK_PLUS_OBJ(fixedpoint, 20 * 1000) +MK_PLUS_OBJ(optimize, 20 * 1000) #ifdef __cplusplus extern "C" { diff --git a/src/api/python/CMakeLists.txt b/src/api/python/CMakeLists.txt index e791a2f11..067a25e8c 100644 --- a/src/api/python/CMakeLists.txt +++ b/src/api/python/CMakeLists.txt @@ -42,8 +42,6 @@ add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3/z3core.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} - # FIXME: When update_api.py no longer uses ``mk_util`` drop this dependency - "${PROJECT_SOURCE_DIR}/scripts/mk_util.py" COMMENT "Generating z3core.py" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} ) diff --git a/src/api/python/MANIFEST.in b/src/api/python/MANIFEST.in index 35a0627e5..7317d7b21 100644 --- a/src/api/python/MANIFEST.in +++ b/src/api/python/MANIFEST.in @@ -5,3 +5,4 @@ recursive-include core *.cmake recursive-include core/src * recursive-include core/cmake * recursive-include core/scripts * +include pyproject.toml diff --git a/src/api/python/pyproject.toml b/src/api/python/pyproject.toml new file mode 100644 index 000000000..a9f2676a7 --- /dev/null +++ b/src/api/python/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=46.4.0", "wheel", "cmake"] +build-backend = "setuptools.build_meta" diff --git a/src/api/python/setup.py b/src/api/python/setup.py index 8c73c29e6..6179422df 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -154,16 +154,10 @@ def _copy_bins(): _clean_bins() - python_dir = None - if RELEASE_DIR is not None: - python_dir = os.path.join(RELEASE_DIR, 'bin', 'python') - elif SRC_DIR == SRC_DIR_LOCAL: - python_dir = os.path.join(SRC_DIR, 'src', 'api', 'python') - if python_dir is not None: - py_z3_build_dir = os.path.join(BUILD_DIR, 'python', 'z3') - root_z3_dir = os.path.join(ROOT_DIR, 'z3') - shutil.copy(os.path.join(py_z3_build_dir, 'z3core.py'), root_z3_dir) - shutil.copy(os.path.join(py_z3_build_dir, 'z3consts.py'), root_z3_dir) + py_z3_build_dir = os.path.join(BUILD_DIR, 'python', 'z3') + root_z3_dir = os.path.join(ROOT_DIR, 'z3') + shutil.copy(os.path.join(py_z3_build_dir, 'z3core.py'), root_z3_dir) + shutil.copy(os.path.join(py_z3_build_dir, 'z3consts.py'), root_z3_dir) # STEP 2: Copy the shared library, the executable and the headers @@ -184,6 +178,20 @@ def _copy_bins(): continue shutil.copy(os.path.join(header_dir, fname), os.path.join(HEADERS_DIR, fname)) + # This hack lets z3 installed libs link on M1 macs; it is a hack, not a proper fix + # @TODO: Linked issue: https://github.com/Z3Prover/z3/issues/5926 + major_minor = '.'.join(_z3_version().split('.')[:2]) + link_name = None + if BUILD_PLATFORM in ('win32', 'cygwin', 'win'): + pass # TODO: When windows VMs work on M1, fill this in + elif BUILD_PLATFORM in ('darwin', 'osx'): + split = LIBRARY_FILE.split('.') + link_name = split[0] + '.' + major_minor + '.' + split[1] + else: + link_name = LIBRARY_FILE + '.' + major_minor + if link_name: + os.symlink(LIBRARY_FILE, os.path.join(LIBS_DIR, link_name), True) + def _copy_sources(): """ Prepare for a source distribution by assembling a minimal set of source files needed @@ -281,6 +289,8 @@ if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: osver = '.'.join(osver.split('.')[:2]) if arch == 'x64': plat_name ='macosx_%s_x86_64' % osver.replace('.', '_') + elif arch == 'arm64': + plat_name ='macosx_%s_arm64' % osver.replace('.', '_') else: raise Exception(f"idk how os {distos} {osver} works. what goes here?") else: @@ -291,7 +301,6 @@ if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: sys.argv.insert(idx + 1, plat_name) sys.argv.insert(idx + 2, '--universal') # supports py2+py3. if --plat-name is not specified this will also mean that the package can be installed on any machine regardless of architecture, so watch out! - setup( name='z3-solver', version=_z3_version(), diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 13c922f85..ed9c7f0a8 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -12,8 +12,6 @@ Z3 is used in many applications such as: software/hardware verification and test constraint solving, analysis of hybrid systems, security, biology (in silico analysis), and geometrical problems. -Several online tutorials for Z3Py are available at: -http://rise4fun.com/Z3Py/tutorial/guide Please send feedback, comments and/or corrections on the Issue tracker for https://github.com/Z3prover/z3.git. Your comments are very valuable. @@ -103,9 +101,6 @@ def get_version(): def get_full_version(): return Z3_get_full_version() -# We use _z3_assert instead of the assert command because we want to -# produce nice error messages in Z3Py at rise4fun.com - def _z3_assert(cond, msg): if not cond: @@ -1771,7 +1766,7 @@ def Xor(a, b, ctx=None): >>> Xor(p, q) Xor(p, q) >>> simplify(Xor(p, q)) - Not(p) == q + Not(p == q) """ ctx = _get_ctx(_ctx_from_ast_arg_list([a, b], ctx)) s = BoolSort(ctx) @@ -2015,8 +2010,7 @@ class QuantifierRef(BoolRef): """ if z3_debug(): _z3_assert(self.is_lambda(), "quantifier should be a lambda expression") - arg = self.sort().domain().cast(arg) - return _to_expr_ref(Z3_mk_select(self.ctx_ref(), self.as_ast(), arg.as_ast()), self.ctx) + return _array_select(self, arg) def weight(self): """Return the weight annotation of `self`. @@ -2285,6 +2279,9 @@ class ArithSortRef(SortRef): """ return self.kind() == Z3_INT_SORT + def is_bool(self): + return False + def subsort(self, other): """Return `True` if `self` is a subsort of `other`.""" return self.is_int() and is_arith_sort(other) and other.is_real() @@ -4496,6 +4493,11 @@ class ArraySortRef(SortRef): """ return _to_sort_ref(Z3_get_array_sort_domain(self.ctx_ref(), self.ast), self.ctx) + def domain_n(self, i): + """Return the domain of the array sort `self`. + """ + return _to_sort_ref(Z3_get_array_sort_domain_n(self.ctx_ref(), self.ast, i), self.ctx) + def range(self): """Return the range of the array sort `self`. @@ -4527,6 +4529,10 @@ class ArrayRef(ExprRef): """ return self.sort().domain() + def domain_n(self, i): + """Shorthand for self.sort().domain_n(i)`.""" + return self.sort().domain_n(i) + def range(self): """Shorthand for `self.sort().range()`. @@ -4546,13 +4552,21 @@ class ArrayRef(ExprRef): >>> a[i].sexpr() '(select a i)' """ - arg = self.domain().cast(arg) - return _to_expr_ref(Z3_mk_select(self.ctx_ref(), self.as_ast(), arg.as_ast()), self.ctx) + return _array_select(self, arg) def default(self): return _to_expr_ref(Z3_mk_array_default(self.ctx_ref(), self.as_ast()), self.ctx) +def _array_select(ar, arg): + if isinstance(arg, tuple): + args = [ar.domain_n(i).cast(arg[i]) for i in range(len(arg))] + _args, sz = _to_ast_array(args) + return _to_expr_ref(Z3_mk_select_n(ar.ctx_ref(), ar.as_ast(), sz, _args), ar.ctx) + arg = ar.domain().cast(arg) + return _to_expr_ref(Z3_mk_select(ar.ctx_ref(), ar.as_ast(), arg.as_ast()), ar.ctx) + + def is_array_sort(a): return Z3_get_sort_kind(a.ctx.ref(), Z3_get_sort(a.ctx.ref(), a.ast)) == Z3_ARRAY_SORT @@ -4679,7 +4693,7 @@ def ArraySort(*sig): return ArraySortRef(Z3_mk_array_sort_n(ctx.ref(), arity, dom, r.ast), ctx) -def Array(name, dom, rng): +def Array(name, *sorts): """Return an array constant named `name` with the given domain and range sorts. >>> a = Array('a', IntSort(), IntSort()) @@ -4688,12 +4702,12 @@ def Array(name, dom, rng): >>> a[0] a[0] """ - s = ArraySort(dom, rng) + s = ArraySort(sorts) ctx = s.ctx return ArrayRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), s.ast), ctx) -def Update(a, i, v): +def Update(a, *args): """Return a Z3 store array expression. >>> a = Array('a', IntSort(), IntSort()) @@ -4709,10 +4723,20 @@ def Update(a, i, v): """ if z3_debug(): _z3_assert(is_array_sort(a), "First argument must be a Z3 array expression") - i = a.sort().domain().cast(i) - v = a.sort().range().cast(v) + args = _get_args(args) ctx = a.ctx - return _to_expr_ref(Z3_mk_store(ctx.ref(), a.as_ast(), i.as_ast(), v.as_ast()), ctx) + if len(args) <= 1: + raise Z3Exception("array update requires index and value arguments") + if len(args) == 2: + i = args[0] + v = args[1] + i = a.sort().domain().cast(i) + v = a.sort().range().cast(v) + return _to_expr_ref(Z3_mk_store(ctx.ref(), a.as_ast(), i.as_ast(), v.as_ast()), ctx) + v = a.sort().range().cast(args[-1]) + idxs = [a.sort().domain_n(i).cast(args[i]) for i in range(len(args)-1)] + _args, sz = _to_ast_array(idxs) + return _to_expr_ref(Z3_mk_store_n(ctx.ref(), a.as_ast(), sz, _args, v.as_ast()), ctx) def Default(a): @@ -4726,7 +4750,7 @@ def Default(a): return a.default() -def Store(a, i, v): +def Store(a, *args): """Return a Z3 store array expression. >>> a = Array('a', IntSort(), IntSort()) @@ -4740,10 +4764,10 @@ def Store(a, i, v): >>> prove(Implies(i != j, s[j] == a[j])) proved """ - return Update(a, i, v) + return Update(a, args) -def Select(a, i): +def Select(a, *args): """Return a Z3 select array expression. >>> a = Array('a', IntSort(), IntSort()) @@ -4753,9 +4777,10 @@ def Select(a, i): >>> eq(Select(a, i), a[i]) True """ + args = _get_args(args) if z3_debug(): _z3_assert(is_array_sort(a), "First argument must be a Z3 array expression") - return a[i] + return a[args] def Map(f, *args): @@ -6569,6 +6594,19 @@ class ModelRef(Z3PPObject): """Update the interpretation of a constant""" if is_expr(x): x = x.decl() + if is_func_decl(x) and x.arity() != 0 and isinstance(value, FuncInterp): + fi1 = value.f + fi2 = Z3_add_func_interp(x.ctx_ref(), self.model, x.ast, value.else_value().ast); + fi2 = FuncInterp(fi2, x.ctx) + for i in range(value.num_entries()): + e = value.entry(i) + n = Z3_func_entry_get_num_args(x.ctx_ref(), e.entry) + v = AstVector() + for j in range(n): + v.push(entry.arg_value(j)) + val = Z3_func_entry_get_value(x.ctx_ref(), e.entry) + Z3_func_interp_add_entry(x.ctx_ref(), fi2.f, v.vector, val) + return if not is_func_decl(x) or x.arity() != 0: raise Z3Exception("Expecting 0-ary function or constant expression") value = _py2expr(value) @@ -8856,7 +8894,7 @@ def _pb_args_coeffs(args, default_ctx=None): for i in range(len(coeffs)): _z3_check_cint_overflow(coeffs[i], "coefficient") _coeffs[i] = coeffs[i] - return ctx, sz, _args, _coeffs + return ctx, sz, _args, _coeffs, args def PbLe(args, k): @@ -8866,7 +8904,7 @@ def PbLe(args, k): >>> f = PbLe(((a,1),(b,3),(c,2)), 3) """ _z3_check_cint_overflow(k, "k") - ctx, sz, _args, _coeffs = _pb_args_coeffs(args) + ctx, sz, _args, _coeffs, args = _pb_args_coeffs(args) return BoolRef(Z3_mk_pble(ctx.ref(), sz, _args, _coeffs, k), ctx) @@ -8877,7 +8915,7 @@ def PbGe(args, k): >>> f = PbGe(((a,1),(b,3),(c,2)), 3) """ _z3_check_cint_overflow(k, "k") - ctx, sz, _args, _coeffs = _pb_args_coeffs(args) + ctx, sz, _args, _coeffs, args = _pb_args_coeffs(args) return BoolRef(Z3_mk_pbge(ctx.ref(), sz, _args, _coeffs, k), ctx) @@ -8888,7 +8926,7 @@ def PbEq(args, k, ctx=None): >>> f = PbEq(((a,1),(b,3),(c,2)), 3) """ _z3_check_cint_overflow(k, "k") - ctx, sz, _args, _coeffs = _pb_args_coeffs(args) + ctx, sz, _args, _coeffs, args = _pb_args_coeffs(args) return BoolRef(Z3_mk_pbeq(ctx.ref(), sz, _args, _coeffs, k), ctx) @@ -8982,7 +9020,7 @@ def prove(claim, show=False, **keywords): def _solve_html(*args, **keywords): - """Version of function `solve` used in RiSE4Fun.""" + """Version of function `solve` that renders HTML output.""" show = keywords.pop("show", False) s = Solver() s.set(**keywords) @@ -9006,7 +9044,7 @@ def _solve_html(*args, **keywords): def _solve_using_html(s, *args, **keywords): - """Version of function `solve_using` used in RiSE4Fun.""" + """Version of function `solve_using` that renders HTML.""" show = keywords.pop("show", False) if z3_debug(): _z3_assert(isinstance(s, Solver), "Solver object expected") @@ -9031,7 +9069,7 @@ def _solve_using_html(s, *args, **keywords): def _prove_html(claim, show=False, **keywords): - """Version of function `prove` used in RiSE4Fun.""" + """Version of function `prove` that renders HTML.""" if z3_debug(): _z3_assert(is_bool(claim), "Z3 Boolean expression expected") s = Solver() @@ -11215,12 +11253,16 @@ def ensure_prop_closures(): _prop_closures = PropClosures() -def user_prop_push(ctx): - _prop_closures.get(ctx).push() +def user_prop_push(ctx, cb): + prop = _prop_closures.get(ctx) + prop.cb = cb + prop.push() -def user_prop_pop(ctx, num_scopes): - _prop_closures.get(ctx).pop(num_scopes) +def user_prop_pop(ctx, cb, num_scopes): + prop = _prop_closures.get(ctx) + prop.cb = cb + pop(num_scopes) def user_prop_fresh(id, ctx): @@ -11230,31 +11272,38 @@ def user_prop_fresh(id, ctx): _prop_closures.set(new_prop.id, new_prop) return ctypes.c_void_p(new_prop.id) +def to_Ast(ptr,): + ast = Ast(ptr) + super(ctypes.c_void_p, ast).__init__(ptr) + return ast def user_prop_fixed(ctx, cb, id, value): prop = _prop_closures.get(ctx) prop.cb = cb - prop.fixed(id, _to_expr_ref(ctypes.c_void_p(value), prop.ctx())) + id = _to_expr_ref(to_Ast(id), prop.ctx()) + value = _to_expr_ref(to_Ast(value), prop.ctx()) + prop.fixed(id, value) prop.cb = None - def user_prop_final(ctx, cb): prop = _prop_closures.get(ctx) prop.cb = cb prop.final() prop.cb = None - def user_prop_eq(ctx, cb, x, y): prop = _prop_closures.get(ctx) prop.cb = cb + x = _to_expr_ref(to_Ast(x), prop.ctx()) + y = _to_expr_ref(to_Ast(y), prop.ctx()) prop.eq(x, y) prop.cb = None - def user_prop_diseq(ctx, cb, x, y): prop = _prop_closures.get(ctx) prop.cb = cb + x = _to_expr_ref(to_Ast(x), prop.ctx()) + y = _to_expr_ref(to_Ast(y), prop.ctx()) prop.diseq(x, y) prop.cb = None @@ -11352,24 +11401,18 @@ class UserPropagateBase: def add(self, e): assert self.solver assert not self._ctx - return Z3_solver_propagate_register(self.ctx_ref(), self.solver.solver, e.ast) + Z3_solver_propagate_register(self.ctx_ref(), self.solver.solver, e.ast) # # Propagation can only be invoked as during a fixed or final callback. # def propagate(self, e, ids, eqs=[]): - num_fixed = len(ids) - _ids = (ctypes.c_uint * num_fixed)() - for i in range(num_fixed): - _ids[i] = ids[i] + _ids, num_fixed = _to_ast_array(ids) num_eqs = len(eqs) - _lhs = (ctypes.c_uint * num_eqs)() - _rhs = (ctypes.c_uint * num_eqs)() - for i in range(num_eqs): - _lhs[i] = eqs[i][0] - _rhs[i] = eqs[i][1] + _lhs, _num_lhs = _to_ast_array([x for x, y in eqs]) + _rhs, _num_rhs = _to_ast_array([y for x, y in eqs]) Z3_solver_propagate_consequence(e.ctx.ref(), ctypes.c_void_p( self.cb), num_fixed, _ids, num_eqs, _lhs, _rhs, e.ast) - def conflict(self, ids): - self.propagate(BoolVal(False, self.ctx()), ids, eqs=[]) + def conflict(self, deps): + self.propagate(BoolVal(False, self.ctx()), deps, eqs=[]) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 0b0d286d1..2680b6f82 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -996,6 +996,8 @@ typedef enum information is exposed. Tools may use the string representation of the function declaration to obtain more information. + - Z3_OP_RECURSIVE: function declared as recursive + - Z3_OP_UNINTERPRETED: kind used for uninterpreted symbols. */ typedef enum { @@ -1220,13 +1222,17 @@ typedef enum { Z3_OP_RE_CONCAT, Z3_OP_RE_UNION, Z3_OP_RE_RANGE, + Z3_OP_RE_DIFF, + Z3_OP_RE_INTERSECT, Z3_OP_RE_LOOP, Z3_OP_RE_POWER, - Z3_OP_RE_INTERSECT, - Z3_OP_RE_DIFF, + Z3_OP_RE_COMPLEMENT, Z3_OP_RE_EMPTY_SET, Z3_OP_RE_FULL_SET, - Z3_OP_RE_COMPLEMENT, + Z3_OP_RE_FULL_CHAR_SET, + Z3_OP_RE_OF_PRED, + Z3_OP_RE_REVERSE, + Z3_OP_RE_DERIVATIVE, // char Z3_OP_CHAR_CONST, @@ -1316,6 +1322,7 @@ typedef enum { Z3_OP_FPA_BV2RM, Z3_OP_INTERNAL, + Z3_OP_RECURSIVE, Z3_OP_UNINTERPRETED } Z3_decl_kind; @@ -1430,13 +1437,14 @@ Z3_DECLARE_CLOSURE(Z3_error_handler, void, (Z3_context c, Z3_error_code e)); /** \brief callback functions for user propagator. */ -Z3_DECLARE_CLOSURE(Z3_push_eh, void, (void* ctx)); -Z3_DECLARE_CLOSURE(Z3_pop_eh, void, (void* ctx, unsigned num_scopes)); +Z3_DECLARE_CLOSURE(Z3_push_eh, void, (void* ctx, Z3_solver_callback cb)); +Z3_DECLARE_CLOSURE(Z3_pop_eh, void, (void* ctx, Z3_solver_callback cb, unsigned num_scopes)); Z3_DECLARE_CLOSURE(Z3_fresh_eh, void*, (void* ctx, Z3_context new_context)); -Z3_DECLARE_CLOSURE(Z3_fixed_eh, void, (void* ctx, Z3_solver_callback cb, unsigned id, Z3_ast value)); -Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, unsigned x, unsigned y)); +Z3_DECLARE_CLOSURE(Z3_fixed_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t, Z3_ast value)); +Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast s, Z3_ast t)); Z3_DECLARE_CLOSURE(Z3_final_eh, void, (void* ctx, Z3_solver_callback cb)); -Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast e, unsigned id)); +Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t)); +Z3_DECLARE_CLOSURE(Z3_decide_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast*, unsigned*, Z3_lbool*)); /** @@ -3817,6 +3825,13 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_re_loop(Z3_context c, Z3_ast r, unsigned lo, unsigned hi); + /** + \brief Create a power regular expression. + + def_API('Z3_mk_re_power', AST, (_in(CONTEXT), _in(AST), _in(UINT))) + */ + Z3_ast Z3_API Z3_mk_re_power(Z3_context c, Z3_ast, unsigned n); + /** \brief Create the intersection of the regular languages. @@ -4358,11 +4373,27 @@ extern "C" { \sa Z3_mk_array_sort \sa Z3_get_sort_kind + \sa Z3_get_array_sort_domain_n def_API('Z3_get_array_sort_domain', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_get_array_sort_domain(Z3_context c, Z3_sort t); + + /** + \brief Return the i'th domain sort of an n-dimensional array. + + \pre Z3_get_sort_kind(c, t) == Z3_ARRAY_SORT + + \sa Z3_mk_array_sort + \sa Z3_get_sort_kind + \sa Z3_get_array_sort_domain + + def_API('Z3_get_array_sort_domain_n', SORT, (_in(CONTEXT), _in(SORT), _in(UINT))) + + */ + Z3_sort Z3_API Z3_get_array_sort_domain_n(Z3_context c, Z3_sort t, unsigned idx); + /** \brief Return the range of the given array sort. @@ -6672,6 +6703,13 @@ extern "C" { /** \brief register a user-properator with the solver. + + \param c - context. + \param s - solver object. + \param user_context - a context used to maintain state for callbacks. + \param push_eh - a callback invoked when scopes are pushed + \param pop_eh - a callback invoked when scopes are poped + \param fresh_eh - a solver may spawn new solvers internally. This callback is used to produce a fresh user_context to be associated with fresh solvers. */ void Z3_API Z3_solver_propagate_init( @@ -6694,11 +6732,15 @@ extern "C" { /** \brief register a callback on final check. This provides freedom to the propagator to delay actions or implement a branch-and bound solver. + The final check is invoked when all decision variables have been assigned by the solver. - The final_eh callback takes as argument the original user_context that was used - when calling \c Z3_solver_propagate_init, and it takes a callback context for propagations. - If may use the callback context to invoke the \c Z3_solver_propagate_consequence function. - If the callback context gets used, the solver continues. + The \c final_eh callback takes as argument the original user_context that was used + when calling \c Z3_solver_propagate_init, and it takes a callback context with the + opaque type \c Z3_solver_callback. + The callback context is passed as argument to invoke the \c Z3_solver_propagate_consequence function. + The callback context can only be accessed (for propagation and for dynamically registering expressions) within a callback. + If the callback context gets used for propagation or conflicts, those propagations take effect and + may trigger new decision variables to be set. */ void Z3_API Z3_solver_propagate_final(Z3_context c, Z3_solver s, Z3_final_eh final_eh); @@ -6717,6 +6759,14 @@ extern "C" { * The registered function appears at the top level and is created using \ref Z3_propagate_solver_declare. */ void Z3_API Z3_solver_propagate_created(Z3_context c, Z3_solver s, Z3_created_eh created_eh); + + /** + * \brief register a callback when a the solver decides to split on a registered expression + * The callback may set passed expression to another registered expression which will be selected instead. + * In case the expression is a bitvector the bit to split on is determined by the bit argument and the + * truth-value to try first is given by is_pos + */ + void Z3_API Z3_solver_propagate_decide(Z3_context c, Z3_solver s, Z3_decide_eh decide_eh); /** Create uninterpreted function declaration for the user propagator. @@ -6734,10 +6784,10 @@ extern "C" { \brief register an expression to propagate on with the solver. Only expressions of type Bool and type Bit-Vector can be registered for propagation. - def_API('Z3_solver_propagate_register', UINT, (_in(CONTEXT), _in(SOLVER), _in(AST))) + def_API('Z3_solver_propagate_register', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST))) */ - unsigned Z3_API Z3_solver_propagate_register(Z3_context c, Z3_solver s, Z3_ast e); + void Z3_API Z3_solver_propagate_register(Z3_context c, Z3_solver s, Z3_ast e); /** \brief register an expression to propagate on with the solver. @@ -6745,9 +6795,9 @@ extern "C" { Unlike \ref Z3_solver_propagate_register, this function takes a solver callback context as argument. It can be invoked during a callback to register new expressions. - def_API('Z3_solver_propagate_register_cb', UINT, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(AST))) + def_API('Z3_solver_propagate_register_cb', VOID, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(AST))) */ - unsigned Z3_API Z3_solver_propagate_register_cb(Z3_context c, Z3_solver_callback cb, Z3_ast e); + void Z3_API Z3_solver_propagate_register_cb(Z3_context c, Z3_solver_callback cb, Z3_ast e); /** \brief propagate a consequence based on fixed values. @@ -6755,10 +6805,10 @@ extern "C" { The callback adds a propagation consequence based on the fixed values of the \c ids. - def_API('Z3_solver_propagate_consequence', VOID, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(UINT), _in_array(2, UINT), _in(UINT), _in_array(4, UINT), _in_array(4, UINT), _in(AST))) + def_API('Z3_solver_propagate_consequence', VOID, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(UINT), _in_array(2, AST), _in(UINT), _in_array(4, AST), _in_array(4, AST), _in(AST))) */ - void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback, unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, Z3_ast conseq); + void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback, unsigned num_fixed, Z3_ast const* fixed, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq); /** \brief Check whether the assertions in a given solver are consistent or not. diff --git a/src/api/z3_optimization.h b/src/api/z3_optimization.h index 3cdacc46d..889db94ea 100644 --- a/src/api/z3_optimization.h +++ b/src/api/z3_optimization.h @@ -82,7 +82,7 @@ extern "C" { \param c - context \param o - optimization context \param a - formula - \param weight - a positive weight, penalty for violating soft constraint + \param weight - a penalty for violating soft constraint. Negative weights convert into rewards. \param id - optional identifier to group soft constraints \sa Z3_optimize_assert diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 6ef012836..65e2a5d1a 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -27,6 +27,7 @@ Revision History: #include "ast/ast_util.h" #include "ast/ast_smt2_pp.h" #include "ast/array_decl_plugin.h" +#include "ast/arith_decl_plugin.h" #include "ast/ast_translation.h" #include "util/z3_version.h" @@ -233,8 +234,7 @@ std::ostream& operator<<(std::ostream& out, sort_size const & ss) { // ----------------------------------- std::ostream & operator<<(std::ostream & out, sort_info const & info) { operator<<(out, static_cast(info)); - out << " :size " << info.get_num_elements(); - return out; + return out << " :size " << info.get_num_elements(); } // ----------------------------------- @@ -1758,13 +1758,13 @@ ast * ast_manager::register_node_core(ast * n) { switch (n->get_kind()) { case AST_SORT: if (to_sort(n)->m_info != nullptr) { - to_sort(n)->m_info = alloc(sort_info, *(to_sort(n)->get_info())); + to_sort(n)->m_info = alloc(sort_info, std::move(*(to_sort(n)->get_info()))); to_sort(n)->m_info->init_eh(*this); } break; case AST_FUNC_DECL: if (to_func_decl(n)->m_info != nullptr) { - to_func_decl(n)->m_info = alloc(func_decl_info, *(to_func_decl(n)->get_info())); + to_func_decl(n)->m_info = alloc(func_decl_info, std::move(*(to_func_decl(n)->get_info()))); to_func_decl(n)->m_info->init_eh(*this); } inc_array_ref(to_func_decl(n)->get_arity(), to_func_decl(n)->get_domain()); @@ -1992,7 +1992,7 @@ sort * ast_manager::substitute(sort* s, unsigned n, sort * const * src, sort * c return s; } decl_info dinfo(s->get_family_id(), s->get_decl_kind(), ps.size(), ps.data(), s->private_parameters()); - sort_info sinfo(dinfo, s->get_num_elements()); + sort_info sinfo(std::move(dinfo), s->get_num_elements()); return mk_sort(s->get_name(), &sinfo); } @@ -2131,12 +2131,17 @@ bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * co expr* ast_manager::coerce_to(expr* e, sort* s) { sort* se = e->get_sort(); if (s != se && s->get_family_id() == arith_family_id && se->get_family_id() == arith_family_id) { - if (s->get_decl_kind() == REAL_SORT) { + if (s->get_decl_kind() == REAL_SORT) return mk_app(arith_family_id, OP_TO_REAL, e); - } - else { - return mk_app(arith_family_id, OP_TO_INT, e); - } + else + return mk_app(arith_family_id, OP_TO_INT, e); + } + if (s != se && s->get_family_id() == arith_family_id && is_bool(e)) { + arith_util au(*this); + if (s->get_decl_kind() == REAL_SORT) + return mk_ite(e, au.mk_real(1), au.mk_real(0)); + else + return mk_ite(e, au.mk_int(1), au.mk_int(0)); } else { return e; @@ -2231,7 +2236,7 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar std::ostringstream buffer; buffer << "Wrong number of arguments (" << num_args << ") passed to function " << mk_pp(decl, *this); - throw ast_exception(buffer.str()); + throw ast_exception(std::move(buffer).str()); } app * r = nullptr; if (num_args == 1 && decl->is_chainable() && decl->get_arity() == 2) { diff --git a/src/ast/ast.h b/src/ast/ast.h index 13898c2a6..3cb90193f 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -367,8 +367,8 @@ public: decl_info(family_id, k, num_parameters, parameters, private_parameters), m_num_elements(num_elements) { } - sort_info(decl_info const& di, sort_size const& num_elements) : - decl_info(di), m_num_elements(num_elements) {} + sort_info(decl_info && di, sort_size const& num_elements) : + decl_info(std::move(di)), m_num_elements(num_elements) {} bool is_infinite() const { return m_num_elements.is_infinite(); } bool is_very_big() const { return m_num_elements.is_very_big(); } diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 9854195a4..59f9ecda6 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -47,18 +47,14 @@ format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len, bo len = static_cast(str.length()); return mk_string(m, str); } - else if (s.is_numerical()) { - std::string str = s.str(); - len = static_cast(str.length()); - return mk_string(m, str); - } - else if (!s.bare_str()) { + else if (s.is_null()) { len = 4; return mk_string(m, "null"); } else { - len = static_cast(strlen(s.bare_str())); - return mk_string(m, s.bare_str()); + std::string str = s.str(); + len = static_cast(str.length()); + return mk_string(m, str); } } diff --git a/src/ast/ast_translation.cpp b/src/ast/ast_translation.cpp index c4e32a303..781593b38 100644 --- a/src/ast/ast_translation.cpp +++ b/src/ast/ast_translation.cpp @@ -170,12 +170,20 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) { new_fi.set_injective(fi->is_injective()); new_fi.set_skolem(fi->is_skolem()); new_fi.set_idempotent(fi->is_idempotent()); + new_fi.set_lambda(fi->is_lambda()); new_f = m_to_manager.mk_func_decl(f->get_name(), f->get_arity(), new_domain, new_range, new_fi); + + if (new_fi.is_lambda()) { + quantifier* q = from().is_lambda_def(f); + ast_translation tr(from(), to()); + quantifier* new_q = tr(q); + to().add_lambda_def(new_f, new_q); + } } TRACE("ast_translation", tout << f->get_name() << " "; if (fi) tout << *fi; tout << "\n"; diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 645088e3e..74002bb1b 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -886,8 +886,8 @@ app * bv_util::mk_numeral(rational const & val, unsigned bv_size) const { } sort * bv_util::mk_sort(unsigned bv_size) { - parameter p[1] = { parameter(bv_size) }; - return m_manager.mk_sort(get_fid(), BV_SORT, 1, p); + parameter p(bv_size); + return m_manager.mk_sort(get_fid(), BV_SORT, 1, &p); } unsigned bv_util::get_int2bv_size(parameter const& p) { diff --git a/src/ast/char_decl_plugin.cpp b/src/ast/char_decl_plugin.cpp index 2ab4def9a..029312ba3 100644 --- a/src/ast/char_decl_plugin.cpp +++ b/src/ast/char_decl_plugin.cpp @@ -16,6 +16,7 @@ Author: --*/ #include "util/gparams.h" +#include "ast/bv_decl_plugin.h" #include "ast/char_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" @@ -164,6 +165,14 @@ app* char_decl_plugin::mk_le(expr* a, expr* b) { unsigned v1 = 0, v2 = 0; if (a == b) return m_manager->mk_true(); + bv_util bv(*m_manager); + if (bv.is_bv(a)) + return bv.mk_ule(a, b); + arith_util arith(*m_manager); + if (arith.is_int_real(a)) + return arith.mk_le(a, b); + if (a->get_sort() != char_sort()) + throw default_exception("range comparison is only supported for bit-vectors, int, real and characters"); bool c1 = is_const_char(a, v1); bool c2 = is_const_char(b, v2); if (c1 && c2) diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index ace0cb567..bc574fc1c 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -69,8 +69,8 @@ namespace datatype { domain.push_back(a->instantiate(ps)->get_range()); } sort_ref range = get_def().instantiate(ps); - parameter pas[1] = { parameter(name()) }; - return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_CONSTRUCTOR, 1, pas, domain.size(), domain.data(), range), m); + parameter pas(name()); + return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_CONSTRUCTOR, 1, &pas, domain.size(), domain.data(), range), m); } func_decl_ref constructor::instantiate(sort* dt) const { @@ -1052,8 +1052,8 @@ namespace datatype { func_decl * util::get_constructor_is(func_decl * con) { SASSERT(is_constructor(con)); sort * datatype = con->get_range(); - parameter ps[1] = { parameter(con)}; - return m.mk_func_decl(fid(), OP_DT_IS, 1, ps, 1, &datatype); + parameter ps(con); + return m.mk_func_decl(fid(), OP_DT_IS, 1, &ps, 1, &datatype); } func_decl * util::get_constructor_recognizer(func_decl * con) { diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 8a3a4e002..5d0bd4b17 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -293,11 +293,12 @@ namespace euf { VERIFY(n->num_args() == 0 || !n->merge_enabled() || m_table.contains(n)); } - void egraph::set_value(enode* n, lbool value) { + void egraph::set_value(enode* n, lbool value, justification j) { if (n->value() == l_undef) { force_push(); TRACE("euf", tout << bpp(n) << " := " << value << "\n";); n->set_value(value); + n->m_lit_justification = j; m_updates.push_back(update_record(n, update_record::value_assignment())); } } @@ -657,6 +658,7 @@ namespace euf { push_lca(n1->get_arg(1), n2->get_arg(0)); return; } + TRACE("euf_verbose", tout << bpp(n1) << " " << bpp(n2) << "\n"); for (unsigned i = 0; i < n1->num_args(); ++i) push_lca(n1->get_arg(i), n2->get_arg(i)); @@ -713,6 +715,15 @@ namespace euf { explain_todo(justifications); } + template + void egraph::explain_eq(ptr_vector& justifications, enode* a, enode* b, justification const& j) { + if (j.is_external()) + justifications.push_back(j.ext()); + else if (j.is_congruence()) + push_congruence(a, b, j.is_commutative()); + } + + template void egraph::explain_eq(ptr_vector& justifications, enode* a, enode* b) { SASSERT(a->get_root() == b->get_root()); @@ -746,11 +757,21 @@ namespace euf { void egraph::explain_todo(ptr_vector& justifications) { for (unsigned i = 0; i < m_todo.size(); ++i) { enode* n = m_todo[i]; - if (n->m_target && !n->is_marked1()) { + if (n->is_marked1()) + continue; + if (n->m_target) { n->mark1(); CTRACE("euf_verbose", m_display_justification, n->m_justification.display(tout << n->get_expr_id() << " = " << n->m_target->get_expr_id() << " ", m_display_justification) << "\n";); explain_eq(justifications, n, n->m_target, n->m_justification); } + else if (!n->is_marked1() && n->value() != l_undef) { + n->mark1(); + if (m.is_true(n->get_expr()) || m.is_false(n->get_expr())) + continue; + justification j = n->m_lit_justification; + SASSERT(j.is_external()); + justifications.push_back(j.ext()); + } } } diff --git a/src/ast/euf/euf_egraph.h b/src/ast/euf/euf_egraph.h index a91dbf4a4..55f94f0f2 100644 --- a/src/ast/euf/euf_egraph.h +++ b/src/ast/euf/euf_egraph.h @@ -226,12 +226,8 @@ namespace euf { void erase_from_table(enode* p); template - void explain_eq(ptr_vector& justifications, enode* a, enode* b, justification const& j) { - if (j.is_external()) - justifications.push_back(j.ext()); - else if (j.is_congruence()) - push_congruence(a, b, j.is_commutative()); - } + void explain_eq(ptr_vector& justifications, enode* a, enode* b, justification const& j); + template void explain_todo(ptr_vector& justifications); @@ -295,7 +291,7 @@ namespace euf { void add_th_var(enode* n, theory_var v, theory_id id); void set_th_propagates_diseqs(theory_id id); void set_merge_enabled(enode* n, bool enable_merge); - void set_value(enode* n, lbool value); + void set_value(enode* n, lbool value, justification j); void set_bool_var(enode* n, unsigned v) { n->set_bool_var(v); } void set_relevant(enode* n); void set_default_relevant(bool b) { m_default_relevant = b; } diff --git a/src/ast/euf/euf_enode.h b/src/ast/euf/euf_enode.h index 850e183e8..7014223be 100644 --- a/src/ast/euf/euf_enode.h +++ b/src/ast/euf/euf_enode.h @@ -63,6 +63,7 @@ namespace euf { enode* m_cg = nullptr; th_var_list m_th_vars; justification m_justification; + justification m_lit_justification; unsigned m_num_args = 0; signed char m_lbl_hash = -1; // It is different from -1, if enode is used in a pattern approx_set m_lbls; @@ -133,6 +134,7 @@ namespace euf { void del_th_var(theory_id id) { m_th_vars.del_var(id); } void set_merge_enabled(bool m) { m_merge_enabled = m; } void set_value(lbool v) { m_value = v; } + void set_justification(justification j) { m_justification = j; } void set_is_equality() { m_is_equality = true; } void set_bool_var(sat::bool_var v) { m_bool_var = v; } diff --git a/src/ast/pb_decl_plugin.cpp b/src/ast/pb_decl_plugin.cpp index 71722c7d0..10e6c694c 100644 --- a/src/ast/pb_decl_plugin.cpp +++ b/src/ast/pb_decl_plugin.cpp @@ -33,11 +33,9 @@ func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p unsigned arity, sort * const * domain, sort * range) { SASSERT(m_manager); ast_manager& m = *m_manager; - for (unsigned i = 0; i < arity; ++i) { - if (!m.is_bool(domain[i])) { - m.raise_exception("invalid non-Boolean sort applied to 'at-most'"); - } - } + for (unsigned i = 0; i < arity; ++i) + if (!m.is_bool(domain[i])) + m.raise_exception("invalid non-Boolean sort applied to Pseudo-Boolean relation"); symbol sym; switch(k) { case OP_AT_LEAST_K: sym = m_at_least_sym; break; @@ -50,9 +48,8 @@ func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p switch(k) { case OP_AT_LEAST_K: case OP_AT_MOST_K: { - if (num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() < 0) { + if (num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() < 0) m.raise_exception("function expects one non-negative integer parameter"); - } func_decl_info info(m_family_id, k, 1, parameters); return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info); } @@ -93,11 +90,11 @@ func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p void pb_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { if (logic == symbol::null || logic == "QF_FD" || logic == "ALL" || logic == "HORN") { - op_names.push_back(builtin_name(m_at_most_sym.bare_str(), OP_AT_MOST_K)); - op_names.push_back(builtin_name(m_at_least_sym.bare_str(), OP_AT_LEAST_K)); - op_names.push_back(builtin_name(m_pble_sym.bare_str(), OP_PB_LE)); - op_names.push_back(builtin_name(m_pbge_sym.bare_str(), OP_PB_GE)); - op_names.push_back(builtin_name(m_pbeq_sym.bare_str(), OP_PB_EQ)); + op_names.push_back(builtin_name(m_at_most_sym.str(), OP_AT_MOST_K)); + op_names.push_back(builtin_name(m_at_least_sym.str(), OP_AT_LEAST_K)); + op_names.push_back(builtin_name(m_pble_sym.str(), OP_PB_LE)); + op_names.push_back(builtin_name(m_pbge_sym.str(), OP_PB_GE)); + op_names.push_back(builtin_name(m_pbeq_sym.str(), OP_PB_EQ)); } } diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 9a8c59ae9..0e9c27655 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -225,6 +225,11 @@ namespace recfun { m_vars.append(n_vars, vars); m_rhs = rhs; + if (!is_macro) + for (expr* e : subterms::all(m_rhs)) + if (is_lambda(e)) + throw default_exception("recursive definitions with lambdas are not supported"); + expr_ref_vector conditions(m); // is the function a macro (unconditional body)? @@ -233,6 +238,8 @@ namespace recfun { add_case(name, 0, conditions, rhs); return; } + + // analyze control flow of `rhs`, accumulating guards and // rebuilding a `ite`-free RHS on the fly for each path in `rhs`. diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt index d2b80ea9c..4bd6d07c4 100644 --- a/src/ast/rewriter/CMakeLists.txt +++ b/src/ast/rewriter/CMakeLists.txt @@ -9,6 +9,7 @@ z3_add_component(rewriter bv_elim.cpp bv_rewriter.cpp cached_var_subst.cpp + char_rewriter.cpp datatype_rewriter.cpp der.cpp distribute_forall.cpp diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 356e30d1a..1fdd7bd14 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()); } @@ -196,11 +197,11 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons SASSERT(num_args == 1); return mk_bit2bool(args[0], f->get_parameter(0).get_int(), result); case OP_BSMUL_NO_OVFL: - return mk_bvsmul_no_overflow(num_args, args, result); + return mk_bvsmul_no_overflow(num_args, args, true, result); + case OP_BSMUL_NO_UDFL: + return mk_bvsmul_no_overflow(num_args, args, false, result); case OP_BUMUL_NO_OVFL: return mk_bvumul_no_overflow(num_args, args, result); - case OP_BSMUL_NO_UDFL: - return mk_bvsmul_no_underflow(num_args, args, result); default: return BR_FAILED; } @@ -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; @@ -2802,30 +2803,51 @@ br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & resu return BR_FAILED; } -br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args, expr_ref & result) { +br_status bv_rewriter::mk_distinct(unsigned num_args, expr * const * args, expr_ref & result) { + if (num_args <= 1) { + result = m().mk_true(); + return BR_DONE; + } + unsigned sz = get_bv_size(args[0]); + // check if num_args > 2^sz + if (sz >= 32) + return BR_FAILED; + if (num_args <= 1u << sz) + return BR_FAILED; + result = m().mk_false(); + return BR_DONE; +} + +br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args, bool is_overflow, expr_ref & result) { SASSERT(num == 2); unsigned bv_sz; rational a0_val, a1_val; bool is_num1 = is_numeral(args[0], a0_val, bv_sz); bool is_num2 = is_numeral(args[1], a1_val, bv_sz); - if (is_num1 && (a0_val.is_zero() || a0_val.is_one())) { + + if (is_num1 && (a0_val.is_zero() || (bv_sz != 1 && a0_val.is_one()))) { result = m().mk_true(); return BR_DONE; } - if (is_num2 && (a1_val.is_zero() || a1_val.is_one())) { + if (is_num2 && (a1_val.is_zero() || (bv_sz != 1 && a1_val.is_one()))) { result = m().mk_true(); return BR_DONE; } if (!is_num1 || !is_num2) return BR_FAILED; - - rational lim = rational::power_of_two(bv_sz); + + bool sign0 = m_util.has_sign_bit(a0_val, bv_sz); + bool sign1 = m_util.has_sign_bit(a1_val, bv_sz); + if (sign0) a0_val = rational::power_of_two(bv_sz) - a0_val; + if (sign1) a1_val = rational::power_of_two(bv_sz) - a1_val; + rational lim = rational::power_of_two(bv_sz-1); rational r = a0_val * a1_val; - bool sign1 = m_util.has_sign_bit(a0_val, bv_sz); - bool sign2 = m_util.has_sign_bit(a1_val, bv_sz); - result = m().mk_bool_val((sign1 != sign2) || r < lim); + if (is_overflow) + result = m().mk_bool_val(sign0 != sign1 || r < lim); + else + result = m().mk_bool_val(sign0 == sign1 || r <= lim); return BR_DONE; } @@ -2855,36 +2877,5 @@ br_status bv_rewriter::mk_bvumul_no_overflow(unsigned num, expr * const * args, return BR_FAILED; } -br_status bv_rewriter::mk_bvsmul_no_underflow(unsigned num, expr * const * args, expr_ref & result) { - SASSERT(num == 2); - unsigned bv_sz; - rational a0_val, a1_val; - - bool is_num1 = is_numeral(args[0], a0_val, bv_sz); - bool is_num2 = is_numeral(args[1], a1_val, bv_sz); - if (is_num1 && (a0_val.is_zero() || a0_val.is_one())) { - result = m().mk_true(); - return BR_DONE; - } - if (is_num2 && (a1_val.is_zero() || a1_val.is_one())) { - result = m().mk_true(); - return BR_DONE; - } - - if (is_num1 && is_num2) { - rational ul = rational::power_of_two(bv_sz); - rational lim = rational::power_of_two(bv_sz-1); - if (a0_val >= lim) a0_val -= ul; - if (a1_val >= lim) a1_val -= ul; - rational mr = a0_val * a1_val; - rational neg_lim = -lim; - TRACE("bv_rewriter_bvsmul_no_underflow", tout << "a0:" << a0_val << " a1:" << a1_val << " mr:" << mr << " neg_lim:" << neg_lim << std::endl;); - result = m().mk_bool_val(mr >= neg_lim); - return BR_DONE; - } - - return BR_FAILED; -} - template class poly_rewriter; diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index e9596256f..88d952c06 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); @@ -134,9 +135,8 @@ class bv_rewriter : public poly_rewriter { br_status mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & result); br_status mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result); br_status mk_mkbv(unsigned num, expr * const * args, expr_ref & result); - br_status mk_bvsmul_no_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsmul_no_overflow(unsigned num, expr * const * args, bool is_overflow, expr_ref & result); br_status mk_bvumul_no_overflow(unsigned num, expr * const * args, expr_ref & result); - br_status mk_bvsmul_no_underflow(unsigned num, expr * const * args, expr_ref & result); bool is_minus_one_times_t(expr * arg); void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result); @@ -180,7 +180,8 @@ public: bool is_urem_any(expr * e, expr * & dividend, expr * & divisor); br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); - br_status mk_ite_core(expr * c, expr * t, expr * e, expr_ref & resul); + br_status mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result); + br_status mk_distinct(unsigned num_args, expr * const * args, expr_ref & result); bool hi_div0() const { return m_hi_div0; } diff --git a/src/ast/rewriter/char_rewriter.cpp b/src/ast/rewriter/char_rewriter.cpp new file mode 100644 index 000000000..14b86773a --- /dev/null +++ b/src/ast/rewriter/char_rewriter.cpp @@ -0,0 +1,73 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + char_rewriter.cpp + +Abstract: + + Basic rewriting rules for character constraints + +Author: + + Nikolaj Bjorner (nbjorner) 2015-12-5 + +--*/ + +#include "util/debug.h" +#include "ast/rewriter/char_rewriter.h" +#include "ast/bv_decl_plugin.h" +#include "ast/arith_decl_plugin.h" + + +char_rewriter::char_rewriter(ast_manager& m): + m(m) { + m_char = static_cast(m.get_plugin(m.mk_family_id("char"))); +} + +family_id char_rewriter::get_fid() { + return m_char->get_family_id(); +} + +br_status char_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(f->get_family_id() == get_fid()); + br_status st = BR_FAILED; + switch (f->get_decl_kind()) { + case OP_CHAR_CONST: + break; + case OP_CHAR_LE: + break; + case OP_CHAR_TO_INT: + st = mk_char_to_int(args[0], result); + break; + case OP_CHAR_TO_BV: + break; + case OP_CHAR_FROM_BV: + st = mk_char_from_bv(args[0], result); + break; + case OP_CHAR_IS_DIGIT: + break; + } + return st; +} + +br_status char_rewriter::mk_char_from_bv(expr* e, expr_ref& result) { + bv_util bv(m); + rational n; + if (bv.is_numeral(e, n) && n.is_unsigned() && n <= m_char->max_char()) { + result = m_char->mk_char(n.get_unsigned()); + return BR_DONE; + } + return BR_FAILED; +} + +br_status char_rewriter::mk_char_to_int(expr* e, expr_ref& result) { + unsigned n = 0; + if (m_char->is_const_char(e, n)) { + arith_util arith(m); + result = arith.mk_int(n); + return BR_DONE; + } + return BR_FAILED; +} diff --git a/src/ast/rewriter/char_rewriter.h b/src/ast/rewriter/char_rewriter.h new file mode 100644 index 000000000..f67695bd8 --- /dev/null +++ b/src/ast/rewriter/char_rewriter.h @@ -0,0 +1,57 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + char_rewriter.h + +Abstract: + + Basic rewriting rules for characters constraints. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-03-10 + +Notes: + +--*/ +#pragma once + +#include "ast/char_decl_plugin.h" +#include "ast/rewriter/rewriter_types.h" +#include "util/params.h" +#include "util/lbool.h" + + +/** + \brief Cheap rewrite rules for character constraints +*/ +class char_rewriter { + ast_manager& m; + char_decl_plugin* m_char; + + br_status mk_char_from_bv(expr* e, expr_ref& result); + + br_status mk_char_to_int(expr* e, expr_ref& result); + +public: + + char_rewriter(ast_manager& m); + + family_id get_fid(); + + br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); + + expr_ref mk_app(func_decl* f, expr_ref_vector const& args) { return mk_app(f, args.size(), args.data()); } + + expr_ref mk_app(func_decl* f, unsigned n, expr* const* args) { + expr_ref result(m); + if (f->get_family_id() != get_fid() || + BR_FAILED == mk_app_core(f, n, args, result)) + result = m.mk_app(f, n, args); + return result; + } + +}; + diff --git a/src/ast/rewriter/func_decl_replace.cpp b/src/ast/rewriter/func_decl_replace.cpp index 512a831af..022afc538 100644 --- a/src/ast/rewriter/func_decl_replace.cpp +++ b/src/ast/rewriter/func_decl_replace.cpp @@ -23,7 +23,8 @@ Revision History: expr_ref func_decl_replace::operator()(expr* e) { m_todo.push_back(e); - + m_refs.push_back(e); + while (!m_todo.empty()) { expr* a = m_todo.back(), *b; if (m_cache.contains(a)) { diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index b9ac64749..eb72ddd67 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -979,9 +979,8 @@ expr* poly_rewriter::merge_muls(expr* x, expr* y) { template bool poly_rewriter::hoist_ite(expr_ref& e) { - if (!m_hoist_ite) { + if (!m_hoist_ite) return false; - } obj_hashtable shared; ptr_buffer adds; expr_ref_vector bs(m()), pinned(m()); diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index adaf3ec0f..9fcc35b2d 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -936,22 +936,13 @@ expr_ref seq_rewriter::mk_seq_last(expr* t) { } /* -* In general constructs substring(t,0,|t|-1) but if t = substring(s,0,k) then simplifies to substring(s,0,k-1) -* This method assumes that |t| > 0, thus, if t = substring(s,0,k) then k > 0 so substring(s,0,k-1) is correct. +* In general constructs substring(t,0,|t|-1) +* Incorrect comment: "but if t = substring(s,0,k) then simplifies to substring(s,0,k-1). +* This method assumes that |t| > 0, thus, if t = substring(s,0,k) then k > 0 so substring(s,0,k-1) is correct." +* No: if k > |s| then substring(s,0,k) = substring(s,0,k-1) */ expr_ref seq_rewriter::mk_seq_butlast(expr* t) { - expr_ref result(m()); - expr* s, * j, * k; - rational v; - if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, v) && v.is_zero()) { - expr_ref_vector k_min_1(m()); - k_min_1.push_back(k); - k_min_1.push_back(minus_one()); - result = str().mk_substr(s, j, m_autil.mk_add_simplify(k_min_1)); - } - else - result = str().mk_substr(t, zero(), m_autil.mk_sub(str().mk_length(t), one())); - return result; + return expr_ref(str().mk_substr(t, zero(), m_autil.mk_sub(str().mk_length(t), one())), m()); } /* @@ -3128,8 +3119,8 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref expr_ref range(m()); expr_ref psi(m().mk_false(), m()); if (str().is_unit_string(r1, c1) && str().is_unit_string(r2, c2)) { - SASSERT(u().is_char(c1)); - SASSERT(u().is_char(c2)); + // SASSERT(u().is_char(c1)); + // SASSERT(u().is_char(c2)); // case: c1 <= e <= c2 range = simplify_path(e, m().mk_and(u().mk_le(c1, e), u().mk_le(e, c2))); psi = simplify_path(e, m().mk_and(path, range)); @@ -5531,44 +5522,37 @@ lbool seq_rewriter::eq_length(expr* x, expr* y) { maximal length (the sequence is bounded). */ -bool seq_rewriter::min_length(expr* e, unsigned& len) { +bool seq_rewriter::min_length(unsigned sz, expr* const* ss, unsigned& len) { + ptr_buffer es; + for (unsigned i = 0; i < sz; ++i) + es.push_back(ss[i]); zstring s; len = 0; - if (str().is_unit(e)) { - len = 1; - return true; + bool bounded = true; + while (!es.empty()) { + expr* e = es.back(); + es.pop_back(); + if (str().is_unit(e)) + len += 1; + else if (str().is_empty(e)) + continue; + else if (str().is_string(e, s)) + len += s.length(); + else if (str().is_concat(e)) + for (expr* arg : *to_app(e)) + es.push_back(arg); + else + bounded = false; } - else if (str().is_empty(e)) { - len = 0; - return true; - } - else if (str().is_string(e, s)) { - len = s.length(); - return true; - } - else if (str().is_concat(e)) { - unsigned min_l = 0; - bool bounded = true; - for (expr* arg : *to_app(e)) { - if (!min_length(arg, min_l)) - bounded = false; - len += min_l; - } - return bounded; - } - return false; + return bounded; +} + +bool seq_rewriter::min_length(expr* e, unsigned& len) { + return min_length(1, &e, len); } bool seq_rewriter::min_length(expr_ref_vector const& es, unsigned& len) { - unsigned min_l = 0; - bool bounded = true; - len = 0; - for (expr* arg : es) { - if (!min_length(arg, min_l)) - bounded = false; - len += min_l; - } - return bounded; + return min_length(es.size(), es.data(), len); } bool seq_rewriter::max_length(expr* e, rational& len) { diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 0d8ac029c..f10532572 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -322,6 +322,7 @@ class seq_rewriter { bool reduce_eq_empty(expr* l, expr* r, expr_ref& result); bool min_length(expr_ref_vector const& es, unsigned& len); bool min_length(expr* e, unsigned& len); + bool min_length(unsigned sz, expr* const* es, unsigned& len); bool max_length(expr* e, rational& len); lbool eq_length(expr* x, expr* y); expr* concat_non_empty(expr_ref_vector& es); diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 2aaf4626c..9cf9fc810 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -21,6 +21,7 @@ Notes: #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/bv_rewriter.h" +#include "ast/rewriter/char_rewriter.h" #include "ast/rewriter/datatype_rewriter.h" #include "ast/rewriter/array_rewriter.h" #include "ast/rewriter/fpa_rewriter.h" @@ -48,6 +49,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { dl_rewriter m_dl_rw; pb_rewriter m_pb_rw; seq_rewriter m_seq_rw; + char_rewriter m_char_rw; recfun_rewriter m_rec_rw; arith_util m_a_util; bv_util m_bv_util; @@ -58,7 +60,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr_substitution * m_subst = nullptr; unsigned long long m_max_memory; // in bytes bool m_new_subst = false; - unsigned m_max_steps = UINT_MAX; + unsigned m_max_steps = UINT_MAX; bool m_pull_cheap_ite = true; bool m_flat = true; bool m_cache_all = false; @@ -178,7 +180,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { // theory dispatch for = SASSERT(num == 2); family_id s_fid = args[0]->get_sort()->get_family_id(); - if (s_fid == m_a_rw.get_fid()) + if (s_fid == m_a_rw.get_fid()) st = m_a_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_bv_rw.get_fid()) st = m_bv_rw.mk_eq_core(args[0], args[1], result); @@ -191,10 +193,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { else if (s_fid == m_seq_rw.get_fid()) st = m_seq_rw.mk_eq_core(args[0], args[1], result); if (st != BR_FAILED) - return st; - } - if (k == OP_EQ) { - SASSERT(num == 2); + return st; st = apply_tamagotchi(args[0], args[1], result); if (st != BR_FAILED) return st; @@ -208,16 +207,21 @@ struct th_rewriter_cfg : public default_rewriter_cfg { return st; } if ((k == OP_AND || k == OP_OR) && m_seq_rw.u().has_re()) { - st = m_seq_rw.mk_bool_app(f, num, args, result); + st = m_seq_rw.mk_bool_app(f, num, args, result); if (st != BR_FAILED) return st; } - if (k == OP_EQ && m_seq_rw.u().has_seq() && is_app(args[0]) && + if (k == OP_EQ && m_seq_rw.u().has_seq() && is_app(args[0]) && to_app(args[0])->get_family_id() == m_seq_rw.get_fid()) { st = m_seq_rw.mk_eq_core(args[0], args[1], result); if (st != BR_FAILED) return st; } + if (k == OP_DISTINCT && num > 0 && m_bv_rw.is_bv(args[0])) { + st = m_bv_rw.mk_distinct(num, args, result); + if (st != BR_FAILED) + return st; + } return m_b_rw.mk_app_core(f, num, args, result); } @@ -247,6 +251,8 @@ struct th_rewriter_cfg : public default_rewriter_cfg { return m_pb_rw.mk_app_core(f, num, args, result); if (fid == m_seq_rw.get_fid()) return m_seq_rw.mk_app_core(f, num, args, result); + if (fid == m_char_rw.get_fid()) + return m_char_rw.mk_app_core(f, num, args, result); if (fid == m_rec_rw.get_fid()) return m_rec_rw.mk_app_core(f, num, args, result); return BR_FAILED; @@ -295,6 +301,8 @@ struct th_rewriter_cfg : public default_rewriter_cfg { bool is_ite_value_tree(expr * t) { if (!m().is_ite(t)) return false; + if (t->get_ref_count() != 1) + return false; ptr_buffer todo; todo.push_back(to_app(t)); while (!todo.empty()) { @@ -303,12 +311,12 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr * arg1 = ite->get_arg(1); expr * arg2 = ite->get_arg(2); - if (m().is_ite(arg1) && arg1->get_ref_count() == 1) // do not apply on shared terms, since it may blowup + if (m().is_ite(arg1) && arg1->get_ref_count() == 1) // do not apply on shared terms, since it may blow up todo.push_back(to_app(arg1)); else if (!m().is_value(arg1)) return false; - if (m().is_ite(arg2) && arg2->get_ref_count() == 1) // do not apply on shared terms, since it may blowup + if (m().is_ite(arg2) && arg2->get_ref_count() == 1) // do not apply on shared terms, since it may blow up todo.push_back(to_app(arg2)); else if (!m().is_value(arg2)) return false; @@ -319,7 +327,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { br_status pull_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { if (num == 2 && m().is_bool(f->get_range()) && !m().is_bool(args[0])) { if (m().is_ite(args[0])) { - if (m().is_value(args[1])) + if (m().is_value(args[1]) && args[0]->get_ref_count() == 1) return pull_ite_core(f, to_app(args[0]), to_app(args[1]), result); if (m().is_ite(args[1]) && to_app(args[0])->get_arg(0) == to_app(args[1])->get_arg(0)) { // (p (ite C A1 B1) (ite C A2 B2)) --> (ite (p A1 A2) (p B1 B2)) @@ -329,17 +337,17 @@ struct th_rewriter_cfg : public default_rewriter_cfg { return BR_REWRITE2; } } - if (m().is_ite(args[1]) && m().is_value(args[0])) + if (m().is_ite(args[1]) && m().is_value(args[0]) && args[1]->get_ref_count() == 1) return pull_ite_core(f, to_app(args[1]), to_app(args[0]), result); } family_id fid = f->get_family_id(); if (num == 2 && (fid == m().get_basic_family_id() || fid == m_a_rw.get_fid() || fid == m_bv_rw.get_fid())) { // (f v3 (ite c v1 v2)) --> (ite v (f v3 v1) (f v3 v2)) - if (m().is_value(args[0]) && is_ite_value_tree(args[1])) + if (m().is_value(args[0]) && is_ite_value_tree(args[1])) return pull_ite_core(f, to_app(args[1]), to_app(args[0]), result); // (f (ite c v1 v2) v3) --> (ite v (f v1 v3) (f v2 v3)) - if (m().is_value(args[1]) && is_ite_value_tree(args[0])) + if (m().is_value(args[1]) && is_ite_value_tree(args[0])) return pull_ite_core(f, to_app(args[0]), to_app(args[1]), result); } return BR_FAILED; @@ -800,6 +808,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { m_dl_rw(m), m_pb_rw(m), m_seq_rw(m), + m_char_rw(m), m_rec_rw(m), m_a_util(m), m_bv_util(m), diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 091f77f5c..f4cf2ecaa 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -1053,6 +1053,12 @@ sort* seq_util::rex::to_seq(sort* re) { return to_sort(re->get_parameter(0).get_ast()); } +app* seq_util::rex::mk_power(expr* r, unsigned n) { + parameter param(n); + return m.mk_app(m_fid, OP_RE_POWER, 1, ¶m, 1, &r); +} + + app* seq_util::rex::mk_loop(expr* r, unsigned lo) { parameter param(lo); return m.mk_app(m_fid, OP_RE_LOOP, 1, ¶m, 1, &r); diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 9c76298b0..ddde7fa6a 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -502,6 +502,7 @@ public: app* mk_star(expr* r) { return m.mk_app(m_fid, OP_RE_STAR, r); } app* mk_plus(expr* r) { return m.mk_app(m_fid, OP_RE_PLUS, r); } app* mk_opt(expr* r) { return m.mk_app(m_fid, OP_RE_OPTION, r); } + app* mk_power(expr* r, unsigned n); app* mk_loop(expr* r, unsigned lo); app* mk_loop(expr* r, unsigned lo, unsigned hi); expr* mk_loop_proper(expr* r, unsigned lo, unsigned hi); diff --git a/src/ast/special_relations_decl_plugin.cpp b/src/ast/special_relations_decl_plugin.cpp index 5dc5f32fe..7ed5e8346 100644 --- a/src/ast/special_relations_decl_plugin.cpp +++ b/src/ast/special_relations_decl_plugin.cpp @@ -61,11 +61,11 @@ func_decl * special_relations_decl_plugin::mk_func_decl( void special_relations_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { if (logic == symbol::null) { - op_names.push_back(builtin_name(m_po.bare_str(), OP_SPECIAL_RELATION_PO)); - op_names.push_back(builtin_name(m_lo.bare_str(), OP_SPECIAL_RELATION_LO)); - op_names.push_back(builtin_name(m_plo.bare_str(), OP_SPECIAL_RELATION_PLO)); - op_names.push_back(builtin_name(m_to.bare_str(), OP_SPECIAL_RELATION_TO)); - op_names.push_back(builtin_name(m_tc.bare_str(), OP_SPECIAL_RELATION_TC)); + op_names.push_back(builtin_name(m_po.str(), OP_SPECIAL_RELATION_PO)); + op_names.push_back(builtin_name(m_lo.str(), OP_SPECIAL_RELATION_LO)); + op_names.push_back(builtin_name(m_plo.str(), OP_SPECIAL_RELATION_PLO)); + op_names.push_back(builtin_name(m_to.str(), OP_SPECIAL_RELATION_TO)); + op_names.push_back(builtin_name(m_tc.str(), OP_SPECIAL_RELATION_TC)); } } diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 823f48e01..f3bd0ed57 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -58,7 +58,7 @@ public: cmd * c = ctx.find_cmd(s); if (c == nullptr) { std::string err_msg("unknown command '"); - err_msg = err_msg + s.bare_str() + "'"; + err_msg = err_msg + s.str() + "'"; throw cmd_exception(std::move(err_msg)); } m_cmds.push_back(s); @@ -817,9 +817,9 @@ public: sort_ref range(ctx.m()); array_sort_args.push_back(m_f->get_range()); range = array_sort->instantiate(ctx.pm(), array_sort_args.size(), array_sort_args.data()); - parameter p[1] = { parameter(m_f) }; + parameter p(m_f); func_decl_ref new_map(ctx.m()); - new_map = ctx.m().mk_func_decl(get_array_fid(ctx), OP_ARRAY_MAP, 1, p, domain.size(), domain.data(), range.get()); + new_map = ctx.m().mk_func_decl(get_array_fid(ctx), OP_ARRAY_MAP, 1, &p, domain.size(), domain.data(), range.get()); if (new_map == 0) throw cmd_exception("invalid array map operator"); ctx.insert(m_name, new_map); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 968dcacd8..580cc2de4 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1636,6 +1636,7 @@ void cmd_context::pop(unsigned n) { restore_aux_pdecls(s.m_aux_pdecls_lim); restore_assertions(s.m_assertions_lim); restore_psort_inst(s.m_psort_inst_stack_lim); + m_dt_eh.get()->reset(); m_mcs.shrink(m_mcs.size() - n); m_scopes.shrink(new_lvl); if (!m_global_decls) @@ -1815,6 +1816,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/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 4f9d80a8d..60a6e930b 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -267,6 +267,7 @@ protected: cmd_context & m_owner; datatype_util m_dt_util; public: + void reset() { m_dt_util.reset(); } dt_eh(cmd_context & owner); ~dt_eh() override; void operator()(sort * dt, pdecl* pd) override; diff --git a/src/cmd_context/eval_cmd.cpp b/src/cmd_context/eval_cmd.cpp index 8819eb584..70a48da5f 100644 --- a/src/cmd_context/eval_cmd.cpp +++ b/src/cmd_context/eval_cmd.cpp @@ -57,6 +57,8 @@ public: void execute(cmd_context & ctx) override { model_ref md; + if (ctx.ignore_check()) + return; if (!ctx.is_model_available(md)) throw cmd_exception("model is not available"); if (!m_target) diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index 7a93382e9..2bf21de3a 100644 --- a/src/cmd_context/pdecl.cpp +++ b/src/cmd_context/pdecl.cpp @@ -156,8 +156,8 @@ public: return false; return m_sort == static_cast(other)->m_sort; } - void display(std::ostream & out) const override { - out << m_sort->get_name(); + std::ostream& display(std::ostream & out) const override { + return out << m_sort->get_name(); } }; @@ -180,8 +180,8 @@ public: get_num_params() == other->get_num_params() && m_idx == static_cast(other)->m_idx; } - void display(std::ostream & out) const override { - out << "s_" << m_idx; + std::ostream& display(std::ostream & out) const override { + return out << "s_" << m_idx; } unsigned idx() const { return m_idx; } }; @@ -254,7 +254,7 @@ public: } return true; } - void display(std::ostream & out) const override { + std::ostream& display(std::ostream & out) const override { if (m_args.empty()) { out << m_decl->get_name(); } @@ -267,6 +267,7 @@ public: } out << ")"; } + return out; } }; @@ -342,12 +343,12 @@ void display_sort_args(std::ostream & out, unsigned num_params) { out << ") "; } -void psort_user_decl::display(std::ostream & out) const { +std::ostream& psort_user_decl::display(std::ostream & out) const { out << "(declare-sort " << m_name; display_sort_args(out, m_num_params); if (m_def) m_def->display(out); - out << ")"; + return out << ")"; } // ------------------- @@ -364,8 +365,8 @@ sort * psort_dt_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * return m.instantiate_datatype(this, m_name, n, s); } -void psort_dt_decl::display(std::ostream & out) const { - out << "(datatype-sort " << m_name << ")"; +std::ostream& psort_dt_decl::display(std::ostream & out) const { + return out << "(datatype-sort " << m_name << ")"; } // ------------------- @@ -410,8 +411,8 @@ sort * psort_builtin_decl::instantiate(pdecl_manager & m, unsigned n, unsigned c } } -void psort_builtin_decl::display(std::ostream & out) const { - out << "(declare-builtin-sort " << m_name << ")"; +std::ostream& psort_builtin_decl::display(std::ostream & out) const { + return out << "(declare-builtin-sort " << m_name << ")"; } void ptype::display(std::ostream & out, pdatatype_decl const * const * dts) const { @@ -615,7 +616,7 @@ sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * } -void pdatatype_decl::display(std::ostream & out) const { +std::ostream& pdatatype_decl::display(std::ostream & out) const { out << "(declare-datatype " << m_name; display_sort_args(out, m_num_params); bool first = true; @@ -631,7 +632,7 @@ void pdatatype_decl::display(std::ostream & out) const { } first = false; } - out << ")"; + return out << ")"; } bool pdatatype_decl::commit(pdecl_manager& m) { @@ -645,9 +646,11 @@ bool pdatatype_decl::commit(pdecl_manager& m) { datatype_decl * d_ptr = dts.m_buffer[0]; sort_ref_vector sorts(m.m()); bool is_ok = m.get_dt_plugin()->mk_datatypes(1, &d_ptr, m_num_params, ps.data(), sorts); + m.notify_mk_datatype(m_name); if (is_ok && m_num_params == 0) { m.notify_new_dt(sorts.get(0), this); } + return is_ok; } @@ -722,6 +725,7 @@ void pdecl_manager::notify_datatype(sort *r, psort_decl* p, unsigned n, sort* co void pdecl_manager::push() { m_notified_lim.push_back(m_notified_trail.size()); + m_datatypes_lim.push_back(m_datatypes_trail.size()); } void pdecl_manager::pop(unsigned n) { @@ -732,6 +736,16 @@ void pdecl_manager::pop(unsigned n) { } m_notified_trail.shrink(new_sz); m_notified_lim.shrink(m_notified_lim.size() - n); + + new_sz = m_datatypes_lim[m_datatypes_lim.size() - n]; + if (new_sz != m_datatypes_trail.size()) { + datatype_util util(m()); + for (unsigned i = m_datatypes_trail.size(); i-- > new_sz; ) + util.plugin().remove(m_datatypes_trail[i]); + } + m_datatypes_trail.shrink(new_sz); + m_datatypes_lim.shrink(m_datatypes_lim.size() - n); + } bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) { @@ -751,16 +765,24 @@ bool pdatatypes_decl::commit(pdecl_manager& m) { sort_ref_vector sorts(m.m()); bool is_ok = m.get_dt_plugin()->mk_datatypes(m_datatypes.size(), dts.m_buffer.data(), 0, nullptr, sorts); if (is_ok) { + for (pdatatype_decl* d : m_datatypes) { + m.notify_mk_datatype(d->get_name()); + } for (unsigned i = 0; i < m_datatypes.size(); ++i) { pdatatype_decl* d = m_datatypes[i]; - if (d->get_num_params() == 0) { + if (d->get_num_params() == 0) m.notify_new_dt(sorts.get(i), this); - } } } + return is_ok; } +void pdecl_manager::notify_mk_datatype(symbol const& name) { + m_datatypes_trail.push_back(name); +} + + struct pdecl_manager::sort_info { psort_decl * m_decl; @@ -985,16 +1007,19 @@ void pdecl_manager::del_decl_core(pdecl * p) { } void pdecl_manager::del_decl(pdecl * p) { - TRACE("pdecl_manager", tout << "del psort "; p->display(tout); tout << "\n";); + TRACE("pdecl_manager", tout << "del psort "; p->display(tout); tout << "\n";); if (p->is_psort()) { psort * _p = static_cast(p); if (_p->is_sort_wrapper()) { - m_sort2psort.erase(static_cast(_p)->get_sort()); + sort* s = static_cast(_p)->get_sort(); + m_sort2psort.erase(s); } else { m_table.erase(_p); } + } + del_decl_core(p); } diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h index 3a1db06c1..4f9c56825 100644 --- a/src/cmd_context/pdecl.h +++ b/src/cmd_context/pdecl.h @@ -45,7 +45,7 @@ public: unsigned get_id() const { return m_id; } unsigned get_ref_count() const { return m_ref_count; } unsigned hash() const { return m_id; } - virtual void display(std::ostream & out) const {} + virtual std::ostream& display(std::ostream & out) const { return out;} virtual void reset_cache(pdecl_manager& m) {} }; @@ -123,7 +123,7 @@ protected: ~psort_user_decl() override {} public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; - void display(std::ostream & out) const override; + std::ostream& display(std::ostream & out) const override; }; class psort_builtin_decl : public psort_decl { @@ -137,7 +137,7 @@ protected: public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; sort * instantiate(pdecl_manager & m, unsigned n, unsigned const * s) override; - void display(std::ostream & out) const override; + std::ostream& display(std::ostream & out) const override; }; class psort_dt_decl : public psort_decl { @@ -148,7 +148,7 @@ protected: ~psort_dt_decl() override {} public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; - void display(std::ostream & out) const override; + std::ostream& display(std::ostream & out) const override; }; @@ -198,7 +198,7 @@ class paccessor_decl : public pdecl { ptype const & get_type() const { return m_type; } ~paccessor_decl() override {} public: - void display(std::ostream & out) const override { pdecl::display(out); } + std::ostream& display(std::ostream & out) const override { pdecl::display(out); return out; } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; @@ -219,7 +219,7 @@ class pconstructor_decl : public pdecl { constructor_decl * instantiate_decl(pdecl_manager & m, unsigned n, sort * const * s); ~pconstructor_decl() override {} public: - void display(std::ostream & out) const override { pdecl::display(out); } + std::ostream& display(std::ostream & out) const override { pdecl::display(out); return out; } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; @@ -237,7 +237,7 @@ class pdatatype_decl : public psort_decl { ~pdatatype_decl() override {} public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; - void display(std::ostream & out) const override; + std::ostream& display(std::ostream & out) const override; bool has_missing_refs(symbol & missing) const; bool has_duplicate_accessors(symbol & repeated) const; bool commit(pdecl_manager& m); @@ -289,6 +289,8 @@ class pdecl_manager { obj_hashtable m_notified; ptr_vector m_notified_trail; unsigned_vector m_notified_lim; + svector m_datatypes_trail; + unsigned_vector m_datatypes_lim; void init_list(); void del_decl_core(pdecl * p); @@ -319,6 +321,7 @@ public: sort * instantiate_datatype(psort_decl* p, symbol const& name, unsigned n, sort * const* s); sort * instantiate(psort * s, unsigned num, sort * const * args); void notify_datatype(sort *r, psort_decl* p, unsigned n, sort* const* s); + void notify_mk_datatype(symbol const& name); void push(); void pop(unsigned n); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 1cd776cc2..0c61bdcb2 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -223,7 +223,7 @@ class lar_solver : public column_namer { void insert_row_with_changed_bounds(unsigned rid); void detect_rows_with_changed_bounds_for_column(unsigned j); void detect_rows_with_changed_bounds(); - void set_value_for_nbasic_column(unsigned j, const impq & new_val); + void update_x_and_inf_costs_for_columns_with_changed_bounds(); void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); @@ -355,6 +355,9 @@ public: bp.consume(a, witness); } } + + void set_value_for_nbasic_column(unsigned j, const impq& new_val); + // lp_assert(implied_bound_is_correctly_explained(ib, explanation)); } constraint_index mk_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side); void activate_check_on_equal(constraint_index, var_index&); 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/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index 5244de82c..57ce00e2c 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -2013,6 +2013,11 @@ namespace algebraic_numbers { } else { algebraic_cell * c = a.to_algebraic(); + if (c->m_i == 0) { + // undefined + c->m_i = upm().get_root_id(c->m_p_sz, c->m_p, lower(c)) + 1; + } + SASSERT(c->m_i > 0); return c->m_i; } } diff --git a/src/model/array_factory.cpp b/src/model/array_factory.cpp index d3ec56d39..9c4aa816e 100644 --- a/src/model/array_factory.cpp +++ b/src/model/array_factory.cpp @@ -45,8 +45,8 @@ expr * array_factory::mk_array_interp(sort * s, func_interp * & fi) { func_decl * f = mk_aux_decl_for_array_sort(m_manager, s); fi = alloc(func_interp, m_manager, get_array_arity(s)); m_model.register_decl(f, fi); - parameter p[1] = { parameter(f) }; - expr * val = m_manager.mk_app(get_family_id(), OP_AS_ARRAY, 1, p); + parameter p(f); + expr * val = m_manager.mk_app(get_family_id(), OP_AS_ARRAY, 1, &p); register_value(val); return val; } 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/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index 5114cb1c3..03a8f1c99 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -281,14 +281,19 @@ namespace datalog { return get_max_var(has_var); } - void del_rule(horn_subsume_model_converter* mc, rule& r, bool unreachable) { + void del_rule(horn_subsume_model_converter* mc, rule& r, lbool unreachable) { if (mc) { ast_manager& m = mc->get_manager(); expr_ref_vector body(m); - if (unreachable) { + TRACE("dl", tout << "unreachable: " << unreachable << " " << r.get_decl()->get_name() << "\n"); + switch (unreachable) { + case l_true: + body.push_back(m.mk_true()); + break; + case l_false: body.push_back(m.mk_false()); - } - else { + break; + default: for (unsigned i = 0; i < r.get_tail_size(); ++i) { if (r.is_neg_tail(i)) { body.push_back(m.mk_not(r.get_tail(i))); @@ -297,11 +302,12 @@ namespace datalog { body.push_back(r.get_tail(i)); } } + break; } - TRACE("dl_dr", + TRACE("dl", tout << mk_pp(r.get_head(), m) << " :- \n"; for (unsigned i = 0; i < body.size(); ++i) { - tout << mk_pp(body[i].get(), m) << "\n"; + tout << mk_pp(body.get(i), m) << "\n"; }); mc->insert(r.get_head(), body.size(), body.data()); diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 1b69a2733..565b58c84 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -353,7 +353,7 @@ namespace datalog { unsigned get_max_rule_var(const rule& r); }; - void del_rule(horn_subsume_model_converter* mc, rule& r, bool unreachable); + void del_rule(horn_subsume_model_converter* mc, rule& r, lbool unreachable); void resolve_rule(rule_manager& rm, replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp index 991ca716e..7632a0c2f 100644 --- a/src/muz/base/rule_properties.cpp +++ b/src/muz/base/rule_properties.cpp @@ -210,19 +210,35 @@ bool rule_properties::check_accessor(app* n) { SASSERT(m_dt.is_datatype(s)); if (m_dt.get_datatype_constructors(s)->size() <= 1) return true; - func_decl* f = n->get_decl(); - func_decl * c = m_dt.get_accessor_constructor(f); + func_decl* c = m_dt.get_accessor_constructor(f); unsigned ut_size = m_rule->get_uninterpreted_tail_size(); unsigned t_size = m_rule->get_tail_size(); + ptr_vector ctors; + // add recognizer constructor to ctors + auto add_recognizer = [&](expr* r) { + if (!m_dt.is_recognizer(r)) + return; + if (n->get_arg(0) != to_app(r)->get_arg(0)) + return; + auto* c2 = m_dt.get_recognizer_constructor(to_app(r)->get_decl()); + if (c == c2) + return; + ctors.push_back(c2); + }; + auto add_not_recognizer = [&](expr* r) { + if (m.is_not(r, r)) + add_recognizer(r); + }; + + // t is a recognizer for n auto is_recognizer_base = [&](expr* t) { return m_dt.is_recognizer(t) && to_app(t)->get_arg(0) == n->get_arg(0) && m_dt.get_recognizer_constructor(to_app(t)->get_decl()) == c; }; - auto is_recognizer = [&](expr* t) { if (m.is_and(t)) for (expr* arg : *to_app(t)) @@ -231,43 +247,78 @@ bool rule_properties::check_accessor(app* n) { return is_recognizer_base(t); }; - - for (unsigned i = ut_size; i < t_size; ++i) - if (is_recognizer(m_rule->get_tail(i))) + for (unsigned i = ut_size; i < t_size; ++i) { + auto* tail = m_rule->get_tail(i); + if (is_recognizer(tail)) return true; - + add_not_recognizer(tail); + } // create parent use list for every sub-expression in the rule obj_map> use_list; for (unsigned i = ut_size; i < t_size; ++i) { app* t = m_rule->get_tail(i); use_list.insert_if_not_there(t, ptr_vector()).push_back(nullptr); // add marker for top-level expression. - for (expr* sub : subterms::all(expr_ref(t, m))) + for (expr* sub : subterms::all(expr_ref(t, m))) if (is_app(sub)) for (expr* arg : *to_app(sub)) use_list.insert_if_not_there(arg, ptr_vector()).push_back(sub); } - // walk parents of n to check that each path is guarded by a recognizer. - ptr_vector todo; - todo.push_back(n); - for (unsigned i = 0; i < todo.size(); ++i) { - expr* e = todo[i]; + // walk parents of n depth first to check that each path is guarded by a recognizer. + vector> todo; + todo.push_back({n, ctors.size(), false}); + while(!todo.empty()) { + auto [e, ctors_size, visited] = todo.back(); + if (visited) { + todo.pop_back(); + while (ctors.size() > ctors_size) ctors.pop_back(); + continue; + } + std::get<2>(todo.back()) = true; // set visited + if (!use_list.contains(e)) return false; for (expr* parent : use_list[e]) { - if (!parent) - return false; // top-level expressions are not guarded - if (is_recognizer(parent)) + if (!parent) { // top-level expression + // check if n is an unguarded "else" branch + ptr_vector diff; + for (auto* dtc : *m_dt.get_datatype_constructors(s)) + if (!ctors.contains(dtc)) + diff.push_back(dtc); + // the only unguarded constructor for s is c: + // all the others are guarded and we are in an "else" branch so the accessor is safe + if (diff.size() == 1 && diff[0] == c) + continue; + return false; // the accessor is not safe + } + if (is_recognizer(parent)) continue; - if (m.is_ite(parent) && to_app(parent)->get_arg(1) == e && is_recognizer(to_app(parent)->get_arg(0))) - continue; - todo.push_back(parent); + + expr *cnd, *thn, *els; + if (m.is_ite(parent, cnd, thn, els)) { + if (thn == e) { + if (is_recognizer(cnd) && els != e) + continue; // e is guarded + } + add_recognizer(cnd); + } + if (m.is_and(parent)) + for (expr* arg : *to_app(parent)) + add_not_recognizer(arg); + if (m.is_or(parent)) + for (expr* arg : *to_app(parent)) { + add_recognizer(arg); + // if one branch is not(recognizer) then the accessor is safe + if (m.is_not(arg, arg) && is_recognizer(arg)) + goto _continue; + } + todo.push_back({parent, ctors.size(), false}); + _continue:; } } - + return true; - } void rule_properties::operator()(app* n) { diff --git a/src/muz/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index 899ec497c..030d88d71 100644 --- a/src/muz/fp/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -777,6 +777,7 @@ protected: // Sym ::= String | NUM | Var // dtoken parse_infix(dtoken tok1, char const* td, app_ref& pred) { + std::string td1_(td); symbol td1(td); expr_ref v1(m), v2(m); sort* s = nullptr; @@ -793,12 +794,12 @@ protected: if (tok1 == TK_ID) { expr* _v1 = nullptr; - m_vars.find(td1.bare_str(), _v1); + m_vars.find(td1_, _v1); v1 = _v1; } if (tok3 == TK_ID) { expr* _v2 = nullptr; - m_vars.find(td2.bare_str(), _v2); + m_vars.find(td, _v2); v2 = _v2; } if (!v1 && !v2) { @@ -950,18 +951,19 @@ protected: break; } case TK_ID: { - symbol data (m_lexer->get_token_data()); - if (is_var(data.bare_str())) { + char const* d = m_lexer->get_token_data(); + symbol data (d); + if (is_var(d)) { unsigned idx = 0; expr* v = nullptr; - if (!m_vars.find(data.bare_str(), v)) { + if (!m_vars.find(d, v)) { idx = m_num_vars++; v = m.mk_var(idx, s); - m_vars.insert(data.bare_str(), v); + m_vars.insert(d, v); } else if (s != v->get_sort()) { throw default_exception(default_exception::fmt(), "sort: %s expected, but got: %s\n", - s->get_name().bare_str(), v->get_sort()->get_name().bare_str()); + s->get_name().str().c_str(), v->get_sort()->get_name().str().c_str()); } args.push_back(v); } @@ -1075,21 +1077,21 @@ protected: } sort * register_finite_sort(symbol name, uint64_t domain_size, context::sort_kind k) { - if(m_sort_dict.contains(name.bare_str())) { - throw default_exception(default_exception::fmt(), "sort %s already declared", name.bare_str()); + if(m_sort_dict.contains(name.str().c_str())) { + throw default_exception(default_exception::fmt(), "sort %s already declared", name.str().c_str()); } sort * s = m_decl_util.mk_sort(name, domain_size); m_context.register_finite_sort(s, k); - m_sort_dict.insert(name.bare_str(), s); + m_sort_dict.insert(name.str(), s); return s; } sort * register_int_sort(symbol name) { - if(m_sort_dict.contains(name.bare_str())) { - throw default_exception(default_exception::fmt(), "sort %s already declared", name.bare_str()); + if(m_sort_dict.contains(name.str().c_str())) { + throw default_exception(default_exception::fmt(), "sort %s already declared", name.str().c_str()); } sort * s = m_arith.mk_int(); - m_sort_dict.insert(name.bare_str(), s); + m_sort_dict.insert(name.str(), s); return s; } @@ -1105,8 +1107,8 @@ protected: app * res; if(m_arith.is_int(s)) { uint64_t val; - if (!string_to_uint64(name.bare_str(), val)) { - throw default_exception(default_exception::fmt(), "Invalid integer: \"%s\"", name.bare_str()); + if (!string_to_uint64(name.str().c_str(), val)) { + throw default_exception(default_exception::fmt(), "Invalid integer: \"%s\"", name.str().c_str()); } res = m_arith.mk_numeral(rational(val, rational::ui64()), s); } @@ -1288,7 +1290,7 @@ private: uint64_set & sort_content = *e->get_data().m_value; if(!sort_content.contains(num)) { warning_msg("symbol number %I64u on line %d in file %s does not belong to sort %s", - num, m_current_line, m_current_file.c_str(), s->get_name().bare_str()); + num, m_current_line, m_current_file.c_str(), s->get_name().str().c_str()); return false; } if(!m_use_map_names) { @@ -1366,7 +1368,7 @@ private: func_decl * pred = m_context.try_get_predicate_decl(predicate_name); if(!pred) { throw default_exception(default_exception::fmt(), "tuple file %s for undeclared predicate %s", - m_current_file.c_str(), predicate_name.bare_str()); + m_current_file.c_str(), predicate_name.str().c_str()); } unsigned pred_arity = pred->get_arity(); sort * const * arg_sorts = pred->get_domain(); @@ -1531,9 +1533,9 @@ private: if(m_use_map_names) { auto const & value = m_number_names.insert_if_not_there(num, el_name); - if (value!=el_name) { + if (value != el_name) { warning_msg("mismatch of number names on line %d in file %s. old: \"%s\" new: \"%s\"", - m_current_line, fname.c_str(), value.bare_str(), el_name.bare_str()); + m_current_line, fname.c_str(), value.str().c_str(), el_name.str().c_str()); } } } diff --git a/src/muz/fp/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp index 6ffd0f745..560202ab3 100644 --- a/src/muz/fp/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -241,7 +241,7 @@ class horn_tactic : public tactic { verify(q, g, result, mc, pc); } g->set(pc.get()); - g->set(mc.get()); + g->add(mc.get()); } void verify(expr* q, @@ -282,12 +282,11 @@ class horn_tactic : public tactic { } case l_false: { // goal is sat - mc = concat(g->mc(), mc.get()); g->reset(); if (produce_models) { model_ref md = m_ctx.get_model(); model_converter_ref mc2 = model2model_converter(md.get()); - mc = concat(mc.get(), mc2.get()); + mc = mc2.get(); TRACE("dl", mc->display(tout << *md << "\n");); } break; @@ -345,6 +344,7 @@ class horn_tactic : public tactic { g->assert_expr(fml); } g->set_prec(goal::UNDER_OVER); + mc = g->mc(); } void check_parameters() { diff --git a/src/muz/rel/dl_finite_product_relation.cpp b/src/muz/rel/dl_finite_product_relation.cpp index 8a4b86e6d..07921a3be 100644 --- a/src/muz/rel/dl_finite_product_relation.cpp +++ b/src/muz/rel/dl_finite_product_relation.cpp @@ -64,7 +64,7 @@ namespace datalog { } symbol finite_product_relation_plugin::get_name(relation_plugin & inner_plugin) { - std::string str = std::string("fpr_")+inner_plugin.get_name().bare_str(); + std::string str = std::string("fpr_")+inner_plugin.get_name().str(); return symbol(str.c_str()); } diff --git a/src/muz/rel/dl_instruction.cpp b/src/muz/rel/dl_instruction.cpp index 0df03172f..63846e7d5 100644 --- a/src/muz/rel/dl_instruction.cpp +++ b/src/muz/rel/dl_instruction.cpp @@ -213,10 +213,10 @@ namespace datalog { return true; } void make_annotations(execution_context & ctx) override { - ctx.set_register_annotation(m_reg, m_pred->get_name().bare_str()); + ctx.set_register_annotation(m_reg, m_pred->get_name().str().c_str()); } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { - const char * rel_name = m_pred->get_name().bare_str(); + auto rel_name = m_pred->get_name(); if (m_store) { return out << "store " << m_reg << " into " << rel_name; } @@ -378,7 +378,7 @@ namespace datalog { if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported join operation on relations of kinds %s and %s", - r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str()); + r1.get_plugin().get_name().str().c_str(), r2.get_plugin().get_name().str().c_str()); } store_fn(r1, r2, fn); } @@ -441,7 +441,7 @@ namespace datalog { if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_equal operation on a relation of kind %s", - r.get_plugin().get_name().bare_str()); + r.get_plugin().get_name().str().c_str()); } store_fn(r, fn); } @@ -490,7 +490,7 @@ namespace datalog { if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_identical operation on a relation of kind %s", - r.get_plugin().get_name().bare_str()); + r.get_plugin().get_name().str().c_str()); } store_fn(r, fn); } @@ -537,7 +537,7 @@ namespace datalog { if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_interpreted operation on a relation of kind %s", - r.get_plugin().get_name().bare_str()); + r.get_plugin().get_name().str().c_str()); } store_fn(r, fn); } @@ -594,7 +594,7 @@ namespace datalog { if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_interpreted_and_project operation on a relation of kind %s", - reg.get_plugin().get_name().bare_str()); + reg.get_plugin().get_name().str().c_str()); } store_fn(reg, fn); } @@ -837,7 +837,7 @@ namespace datalog { if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported join-project operation on relations of kinds %s and %s", - r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str()); + r1.get_plugin().get_name().str().c_str(), r2.get_plugin().get_name().str().c_str()); } store_fn(r1, r2, fn); } @@ -910,7 +910,7 @@ namespace datalog { if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported select_equal_and_project operation on a relation of kind %s", - r.get_plugin().get_name().bare_str()); + r.get_plugin().get_name().str().c_str()); } store_fn(r, fn); } @@ -1076,7 +1076,7 @@ namespace datalog { return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { - return out << "mark_saturated " << m_pred->get_name().bare_str(); + return out << "mark_saturated " << m_pred->get_name(); } void make_annotations(execution_context & ctx) override { } diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp index 11e366f90..3fa52ae2f 100644 --- a/src/muz/rel/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -370,7 +370,7 @@ namespace datalog { rule * one_parent = inf.m_rules.back(); func_decl* parent_head = one_parent->get_decl(); - const char * one_parent_name = parent_head->get_name().bare_str(); + std::string one_parent_name = parent_head->get_name().str(); std::string parent_name; if (inf.m_rules.size() > 1) { parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1); diff --git a/src/muz/rel/dl_table_relation.cpp b/src/muz/rel/dl_table_relation.cpp index de55998f8..8a69f8f85 100644 --- a/src/muz/rel/dl_table_relation.cpp +++ b/src/muz/rel/dl_table_relation.cpp @@ -33,7 +33,7 @@ namespace datalog { // ----------------------------------- symbol table_relation_plugin::create_plugin_name(const table_plugin &p) { - std::string name = std::string("tr_") + p.get_name().bare_str(); + std::string name = std::string("tr_") + p.get_name().str(); return symbol(name.c_str()); } diff --git a/src/muz/rel/rel_context.cpp b/src/muz/rel/rel_context.cpp index 957c85adb..76411a290 100644 --- a/src/muz/rel/rel_context.cpp +++ b/src/muz/rel/rel_context.cpp @@ -157,7 +157,7 @@ namespace datalog { //IF_VERBOSE(3, m_context.display_smt2(0,0,verbose_stream());); if (m_context.print_aig().is_non_empty_string()) { - const char *filename = m_context.print_aig().bare_str(); + std::string filename = m_context.print_aig().str(); aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts); std::ofstream strm(filename, std::ios_base::binary); aig(strm); diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index fa9f76311..739588c6b 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -126,7 +126,7 @@ public: void move_to_front(expr* e) override { m_solver.move_to_front(e); } expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { m_solver.get_levels(vars, depth); } - expr_ref_vector get_trail() override { return m_solver.get_trail(); } + expr_ref_vector get_trail(unsigned max_level) override { return m_solver.get_trail(max_level); } void push() override; void pop(unsigned n) override; diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index 841ab4637..ba85e569a 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -118,7 +118,7 @@ namespace datalog { } rule_set * mk_coi_filter::top_down(rule_set const & source) { - func_decl_set pruned_preds; + func_decl_set pruned_preds, seen; dataflow_engine engine(source.get_manager(), source); engine.run_top_down(); scoped_ptr res = alloc(rule_set, m_context); @@ -126,37 +126,51 @@ namespace datalog { for (rule * r : source) { func_decl * pred = r->get_decl(); - if (engine.get_fact(pred).is_reachable()) { - res->add_rule(r); - } + bool should_keep = false; + if (seen.contains(pred)) + continue; + seen.insert(pred); + if (engine.get_fact(pred).is_reachable()) + should_keep = true; else if (m_context.get_model_converter()) { - pruned_preds.insert(pred); + for (rule* pr : source.get_predicate_rules(pred)) + for (unsigned i = 0; i < pr->get_uninterpreted_tail_size(); ++i) + if (pr->get_tail(i)->get_decl() != pred) + // don't try to eliminate across predicates + return nullptr; } + else + continue; + + if (should_keep) + for (rule* pr : source.get_predicate_rules(pred)) + res->add_rule(pr); + else + pruned_preds.insert(pred); } if (res->get_num_rules() == source.get_num_rules()) { TRACE("dl", tout << "No transformation\n";); res = nullptr; } - if (res && m_context.get_model_converter()) { - generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl_coi"); - for (func_decl* f : pruned_preds) { + if (res && m_context.get_model_converter() && !pruned_preds.empty()) { + auto* mc0 = alloc(generic_model_converter, m, "dl_coi"); + horn_subsume_model_converter hmc(m); + + for (func_decl* f : pruned_preds) { const rule_vector& rules = source.get_predicate_rules(f); expr_ref_vector fmls(m); - for (rule * r : rules) { - app* head = r->get_head(); - expr_ref_vector conj(m); - for (unsigned j = 0; j < head->get_num_args(); ++j) { - expr* arg = head->get_arg(j); - if (!is_var(arg)) { - conj.push_back(m.mk_eq(m.mk_var(j, arg->get_sort()), arg)); - } - } - fmls.push_back(mk_and(conj)); + for (rule* r : rules) { + expr_ref_vector constraints(m); + expr_ref body_res(m); + func_decl_ref pred(m); + for (unsigned i = r->get_uninterpreted_tail_size(); i < r->get_tail_size(); ++i) + constraints.push_back(r->get_tail(i)); + expr_ref body = mk_and(constraints); + VERIFY(hmc.mk_horn(r->get_head(), body, pred, body_res)); + fmls.push_back(body_res); } - expr_ref fml(m); - fml = m.mk_or(fmls.size(), fmls.data()); - mc0->add(f, fml); + mc0->add(f, mk_or(fmls)); } m_context.add_model_converter(mc0); } diff --git a/src/muz/transforms/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp index 1d4f05155..518a63a0a 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -44,6 +44,11 @@ Subsumption transformation (remove rule): P(x) := P(x) or (exists y . Q(y) & phi(x,y)) +For plan_inlining: + TODO: order of rule inlining would affect model converter? + so shouldn't model converter process inlined rules in a specific (topopologial) order? + + --*/ @@ -377,18 +382,15 @@ namespace datalog { return something_forbidden; } - void mk_rule_inliner::plan_inlining(rule_set const & orig) - { + void mk_rule_inliner::plan_inlining(rule_set const & orig) { count_pred_occurrences(orig); scoped_ptr candidate_inlined_set = create_allowed_rule_set(orig); - while (forbid_preds_from_cycles(*candidate_inlined_set)) { + while (forbid_preds_from_cycles(*candidate_inlined_set)) candidate_inlined_set = create_allowed_rule_set(orig); - } - if (forbid_multiple_multipliers(orig, *candidate_inlined_set)) { + if (forbid_multiple_multipliers(orig, *candidate_inlined_set)) candidate_inlined_set = create_allowed_rule_set(orig); - } TRACE("dl", tout<<"rules to be inlined:\n" << (*candidate_inlined_set); ); @@ -402,16 +404,13 @@ namespace datalog { for (rule_stratifier::item_set * stratum : comps) { SASSERT(stratum->size() == 1); func_decl * pred = *stratum->begin(); - for (rule * r : candidate_inlined_set->get_predicate_rules(pred)) { + for (rule * r : candidate_inlined_set->get_predicate_rules(pred)) transform_rule(orig, r, m_inlined_rules); - } } - TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; ); + for (rule * r : m_inlined_rules) + datalog::del_rule(m_mc, *r, l_undef); - for (rule * r : m_inlined_rules) { - datalog::del_rule(m_mc, *r, false); - } } bool mk_rule_inliner::transform_rule(rule_set const& orig, rule * r0, rule_set& tgt) { @@ -437,20 +436,19 @@ namespace datalog { tgt.add_rule(r); continue; } + modified = true; func_decl * pred = r->get_decl(i); const rule_vector& pred_rules = m_inlined_rules.get_predicate_rules(pred); for (rule * inl_rule : pred_rules) { rule_ref inl_result(m_rm); - if (try_to_inline_rule(*r.get(), *inl_rule, i, inl_result)) { + if (try_to_inline_rule(*r.get(), *inl_rule, i, inl_result)) todo.push_back(inl_result); - } } } - if (modified) { - datalog::del_rule(m_mc, *r0, true); - } + if (modified) + datalog::del_rule(m_mc, *r0, l_undef); return modified; } @@ -473,7 +471,7 @@ namespace datalog { if (something_done && m_mc) { for (rule* r : orig) { if (inlining_allowed(orig, r->get_decl())) { - datalog::del_rule(m_mc, *r, true); + datalog::del_rule(m_mc, *r, l_undef); } } } @@ -558,7 +556,7 @@ namespace datalog { // nothing unifies with the tail atom, therefore the rule is unsatisfiable // (we can say this because relation pred doesn't have any ground facts either) res = nullptr; - datalog::del_rule(m_mc, *r, false); + datalog::del_rule(m_mc, *r, l_false); return true; } if (!is_oriented_rewriter(inlining_candidate, strat)) { @@ -568,7 +566,7 @@ namespace datalog { goto process_next_tail; } if (!try_to_inline_rule(*r, *inlining_candidate, ti, res)) { - datalog::del_rule(m_mc, *r, false); + datalog::del_rule(m_mc, *r, l_false); res = nullptr; } return true; @@ -768,7 +766,7 @@ namespace datalog { break; } - rule* r2 = acc[j].get(); + rule* r2 = acc.get(j); // check that the head of r2 only unifies with this single body position. TRACE("dl", output_predicate(m_context, r2->get_head(), tout << "unify head: "); tout << "\n";); @@ -801,7 +799,7 @@ namespace datalog { if (num_tail_unifiers == 1) { TRACE("dl", tout << "setting invalid: " << j << "\n";); valid.set(j, false); - datalog::del_rule(m_mc, *r2, true); + datalog::del_rule(m_mc, *r2, l_undef); del_rule(r2, j); } diff --git a/src/opt/CMakeLists.txt b/src/opt/CMakeLists.txt index d88d11c0f..c652bcaea 100644 --- a/src/opt/CMakeLists.txt +++ b/src/opt/CMakeLists.txt @@ -8,6 +8,7 @@ z3_add_component(opt opt_lns.cpp opt_pareto.cpp opt_parse.cpp + opt_preprocess.cpp optsmt.cpp opt_solver.cpp pb_sls.cpp diff --git a/src/opt/maxlex.cpp b/src/opt/maxlex.cpp index 46c7104d5..fa97359d0 100644 --- a/src/opt/maxlex.cpp +++ b/src/opt/maxlex.cpp @@ -26,15 +26,15 @@ Author: namespace opt { - bool is_maxlex(weights_t & _ws) { - vector ws(_ws); - std::sort(ws.begin(), ws.end()); + bool is_maxlex(vector const & _ws) { + vector ws(_ws); + std::sort(ws.begin(), ws.end(), [&](soft const& s1, soft const& s2) { return s1.weight < s2.weight; }); ws.reverse(); rational sum(0); - for (rational const& w : ws) { + for (auto const& [e, w, t] : ws) { sum += w; } - for (rational const& w : ws) { + for (auto const& [e, w, t] : ws) { if (sum > w + w) return false; sum -= w; } @@ -185,8 +185,8 @@ namespace opt { public: - maxlex(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& s): - maxsmt_solver_base(c, ws, s), + maxlex(maxsat_context& c, unsigned id, vector& s): + maxsmt_solver_base(c, s, id), m(c.get_manager()), m_c(c) { // ensure that soft constraints are sorted with largest soft constraints first. @@ -210,8 +210,8 @@ namespace opt { } }; - maxsmt_solver_base* mk_maxlex(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& soft) { - return alloc(maxlex, c, id, ws, soft); + maxsmt_solver_base* mk_maxlex(maxsat_context& c, unsigned id, vector& soft) { + return alloc(maxlex, c, id, soft); } } diff --git a/src/opt/maxlex.h b/src/opt/maxlex.h index b5c1a6e20..fc30b1fd8 100644 --- a/src/opt/maxlex.h +++ b/src/opt/maxlex.h @@ -21,9 +21,9 @@ Notes: namespace opt { - bool is_maxlex(weights_t & ws); + bool is_maxlex(vector const & ws); - maxsmt_solver_base* mk_maxlex(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& soft); + maxsmt_solver_base* mk_maxlex(maxsat_context& c, unsigned id, vector& soft); }; diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index 27b3b7260..10ab3c77b 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -72,7 +72,9 @@ class maxres : public maxsmt_solver_base { public: enum strategy_t { s_primal, - s_primal_dual + s_primal_dual, + s_primal_binary, + s_primal_binary_delay }; private: struct stats { @@ -95,7 +97,6 @@ private: expr_ref_vector const& soft() override { return i.m_asms; } }; - unsigned m_index; stats m_stats; expr_ref_vector m_B; expr_ref_vector m_asms; @@ -129,11 +130,10 @@ private: typedef ptr_vector exprs; public: - maxres(maxsat_context& c, unsigned index, - weights_t& ws, expr_ref_vector const& soft, + maxres(maxsat_context& c, unsigned index, + vector& soft, strategy_t st): - maxsmt_solver_base(c, ws, soft), - m_index(index), + maxsmt_solver_base(c, soft, index), m_B(m), m_asms(m), m_defs(m), m_new_core(m), m_mus(c.get_solver()), @@ -159,6 +159,12 @@ public: case s_primal_dual: m_trace_id = "pd-maxres"; break; + case s_primal_binary: + m_trace_id = "maxres-bin"; + break; + case s_primal_binary_delay: + m_trace_id = "maxres-bin-delay"; + break; } } @@ -359,6 +365,8 @@ public: m_defs.reset(); switch(m_st) { case s_primal: + case s_primal_binary: + case s_primal_binary_delay: return mus_solver(); case s_primal_dual: return primal_dual_solver(); @@ -534,8 +542,18 @@ public: expr_ref fml(m); SASSERT(!core.empty()); TRACE("opt", display_vec(tout << "minimized core: ", core);); - IF_VERBOSE(10, display_vec(verbose_stream() << "core: ", core);); - max_resolve(core, w); + IF_VERBOSE(10, display_vec(verbose_stream() << "core: ", core);); + switch (m_st) { + case strategy_t::s_primal_binary: + bin_max_resolve(core, w); + break; + case strategy_t::s_primal_binary_delay: + bin_delay_max_resolve(core, w); + break; + default: + max_resolve(core, w); + break; + } fml = mk_not(m, mk_and(m, core.size(), core.data())); add(fml); // save small cores such that lex-combinations of maxres can reuse these cores. @@ -651,6 +669,7 @@ public: } } + void max_resolve(exprs const& core, rational const& w) { SASSERT(!core.empty()); expr_ref fml(m), asum(m); @@ -701,6 +720,107 @@ public: } } + + void bin_max_resolve(exprs const& _core, rational const& w) { + expr_ref_vector core(m, _core.size(), _core.data()); + expr_ref fml(m), cls(m); + for (unsigned i = 0; i + 1 < core.size(); i += 2) { + expr* a = core.get(i); + expr* b = core.get(i + 1); + expr* u = mk_fresh_bool("u"); + expr* v = mk_fresh_bool("v"); + // u = a or b + // v = a and b + cls = m.mk_or(a, b); + fml = m.mk_implies(u, cls); + add(fml); + update_model(u, cls); + m_defs.push_back(fml); + cls = m.mk_and(a, b); + fml = m.mk_implies(v, cls); + add(fml); + update_model(v, cls); + m_defs.push_back(fml); + new_assumption(u, w); + core.push_back(v); + } + s().assert_expr(m.mk_not(core.back())); + } + + + struct unfold_record { + ptr_vector ws; + rational weight; + }; + + obj_map m_unfold; + rational m_unfold_upper; + + void bin_delay_max_resolve(exprs const& _core, rational const& weight) { + expr_ref_vector core(m, _core.size(), _core.data()), partial(m); + expr_ref fml(m), cls(m); + for (expr* c : core) { + unfold_record r; + if (!m_unfold.find(c, r)) + continue; + IF_VERBOSE(2, verbose_stream() << "to unfold " << mk_pp(c, m) << "\n"); + for (expr* f : r.ws) { + IF_VERBOSE(2, verbose_stream() << "unfold " << mk_pp(f, m) << "\n"); + new_assumption(f, r.weight); + } + m_unfold_upper -= r.weight * rational(r.ws.size() - 1); + m_unfold.remove(c); + } + + for (expr* _ : core) + partial.push_back(nullptr); + + std::cout << "Core size " << core.size() << "\n"; + + if (core.size() > 2) + m_unfold_upper += rational(core.size()-2)*weight; + + expr* w = nullptr; + for (unsigned i = 0; i + 1 < core.size(); i += 2) { + expr* a = core.get(i); + expr* b = core.get(i + 1); + expr* u = mk_fresh_bool("u"); + expr* v = mk_fresh_bool("v"); + // u = a or b + // v = a and b + cls = m.mk_or(a, b); + fml = m.mk_implies(u, cls); + add(fml); + update_model(u, cls); + m_defs.push_back(fml); + cls = m.mk_and(a, b); + fml = m.mk_implies(v, cls); + add(fml); + update_model(v, cls); + m_defs.push_back(fml); + core.push_back(v); + + // w = u and w1 and w2 + unfold_record r; + r.ws.push_back(u); + if (partial.get(i)) + r.ws.push_back(partial.get(i)); + if (partial.get(i + 1)) + r.ws.push_back(partial.get(i + 1)); + m_trail.append(r.ws.size(), r.ws.data()); + w = mk_fresh_bool("w"); + cls = m.mk_and(r.ws); + fml = m.mk_implies(w, cls); + partial.push_back(w); + add(fml); + update_model(w, cls); + m_defs.push_back(fml); + m_unfold.insert(w, r); + } + new_assumption(w, weight); + s().assert_expr(m.mk_not(core.back())); + } + // cs is a correction set (a complement of a (maximal) satisfying assignment). void cs_max_resolve(exprs const& cs, rational const& w) { if (cs.empty()) return; @@ -780,7 +900,7 @@ public: } rational cost(model& mdl) { - rational upper(0); + rational upper = m_unfold_upper; for (soft& s : m_soft) if (!mdl.is_true(s.s)) upper += s.weight; @@ -791,11 +911,10 @@ public: improve_model(mdl); mdl->set_model_completion(true); unsigned correction_set_size = 0; - for (expr* a : m_asms) { - if (mdl->is_false(a)) { + for (expr* a : m_asms) + if (mdl->is_false(a)) ++correction_set_size; - } - } + if (!m_csmodel.get() || correction_set_size < m_correction_set_size) { m_csmodel = mdl; m_correction_set_size = correction_set_size; @@ -810,22 +929,22 @@ public: return; } - if (!m_c.verify_model(m_index, mdl.get(), upper)) { + if (!m_c.verify_model(m_index, mdl.get(), upper)) return; - } + unsigned num_assertions = s().get_num_assertions(); m_model = mdl; m_c.model_updated(mdl.get()); TRACE("opt", tout << "updated upper: " << upper << "\n";); - for (soft& s : m_soft) { + for (soft& s : m_soft) s.set_value(m_model->is_true(s.s)); - } verify_assignment(); - m_upper = upper; + if (num_assertions == s().get_num_assertions()) + m_upper = upper; trace(); @@ -876,23 +995,18 @@ public: } lbool init_local() { - m_lower.reset(); m_trail.reset(); lbool is_sat = l_true; - obj_map new_soft; - is_sat = find_mutexes(new_soft); - if (is_sat != l_true) { - return is_sat; - } - for (auto const& kv : new_soft) { - add_soft(kv.m_key, kv.m_value); - } + for (auto const& [e, w, t] : m_soft) + add_soft(e, w); m_max_upper = m_upper; m_found_feasible_optimum = false; m_last_index = 0; add_upper_bound_block(); m_csmodel = nullptr; m_correction_set_size = 0; + m_unfold.reset(); + m_unfold_upper = 0; return l_true; } @@ -954,12 +1068,22 @@ public: }; opt::maxsmt_solver_base* opt::mk_maxres( - maxsat_context& c, unsigned id, weights_t& ws, expr_ref_vector const& soft) { - return alloc(maxres, c, id, ws, soft, maxres::s_primal); + maxsat_context& c, unsigned id, vector& soft) { + return alloc(maxres, c, id, soft, maxres::s_primal); +} + +opt::maxsmt_solver_base* opt::mk_maxres_binary( + maxsat_context& c, unsigned id, vector& soft) { + return alloc(maxres, c, id, soft, maxres::s_primal_binary); +} + +opt::maxsmt_solver_base* opt::mk_maxres_binary_delay( + maxsat_context& c, unsigned id, vector& soft) { + return alloc(maxres, c, id, soft, maxres::s_primal_binary_delay); } opt::maxsmt_solver_base* opt::mk_primal_dual_maxres( - maxsat_context& c, unsigned id, weights_t& ws, expr_ref_vector const& soft) { - return alloc(maxres, c, id, ws, soft, maxres::s_primal_dual); + maxsat_context& c, unsigned id, vector& soft) { + return alloc(maxres, c, id, soft, maxres::s_primal_dual); } diff --git a/src/opt/maxres.h b/src/opt/maxres.h index 85c83efba..947b60492 100644 --- a/src/opt/maxres.h +++ b/src/opt/maxres.h @@ -21,9 +21,13 @@ Notes: namespace opt { - maxsmt_solver_base* mk_maxres(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& soft); + maxsmt_solver_base* mk_maxres(maxsat_context& c, unsigned id, vector& soft); - maxsmt_solver_base* mk_primal_dual_maxres(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& soft); + maxsmt_solver_base* mk_maxres_binary(maxsat_context& c, unsigned id, vector& soft); + + maxsmt_solver_base* mk_maxres_binary_delay(maxsat_context& c, unsigned id, vector& soft); + + maxsmt_solver_base* mk_primal_dual_maxres(maxsat_context& c, unsigned id, vector& soft); }; diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 6a8a2ee35..f346a8980 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -28,29 +28,34 @@ Notes: #include "opt/wmax.h" #include "opt/opt_params.hpp" #include "opt/opt_context.h" +#include "opt/opt_preprocess.h" #include "smt/theory_wmaxsat.h" #include "smt/theory_pb.h" namespace opt { - maxsmt_solver_base::maxsmt_solver_base( - maxsat_context& c, vector const& ws, expr_ref_vector const& softs): + maxsmt_solver_base::maxsmt_solver_base(maxsat_context& c, vector& s, unsigned index): m(c.get_manager()), m_c(c), + m_index(index), + m_soft(s), m_assertions(m), m_trail(m) { c.get_base_model(m_model); SASSERT(m_model); updt_params(c.params()); - for (unsigned i = 0; i < ws.size(); ++i) { - m_soft.push_back(soft(expr_ref(softs.get(i), m), ws[i], false)); - } } void maxsmt_solver_base::updt_params(params_ref& p) { m_params.copy(p); - } + } + + void maxsmt_solver_base::reset_upper() { + m_upper = m_lower; + for (soft& s : m_soft) + m_upper += s.weight; + } solver& maxsmt_solver_base::s() { return m_c.get_solver(); @@ -82,14 +87,24 @@ namespace opt { m_upper.reset(); for (soft& s : m_soft) { s.set_value(m.is_true(s.s)); - if (!s.is_true()) m_upper += s.weight; + if (!s.is_true()) + m_upper += s.weight; } + + // return true; + + preprocess pp(s()); + rational lower(0); + bool r = pp(m_soft, lower); + + m_c.add_offset(m_index, lower); + m_upper -= lower; TRACE("opt", - tout << "upper: " << m_upper << " assignments: "; + tout << "lower " << lower << " upper: " << m_upper << " assignments: "; for (soft& s : m_soft) tout << (s.is_true()?"T":"F"); tout << "\n";); - return true; + return r; } void maxsmt_solver_base::set_mus(bool f) { @@ -153,80 +168,15 @@ namespace opt { void maxsmt_solver_base::trace_bounds(char const * solver) { IF_VERBOSE(1, - rational l = m_adjust_value(m_lower); - rational u = m_adjust_value(m_upper); + rational l = m_c.adjust(m_index, m_lower); + rational u = m_c.adjust(m_index, m_upper); if (l > u) std::swap(l, u); verbose_stream() << "(opt." << solver << " [" << l << ":" << u << "])\n";); } - lbool maxsmt_solver_base::find_mutexes(obj_map& new_soft) { - m_lower.reset(); - expr_ref_vector fmls(m); - for (soft& s : m_soft) { - new_soft.insert(s.s, s.weight); - fmls.push_back(s.s); - } - vector mutexes; - lbool is_sat = s().find_mutexes(fmls, mutexes); - if (is_sat != l_true) { - return is_sat; - } - for (auto& mux : mutexes) { - process_mutex(mux, new_soft); - } - return l_true; - } - - struct maxsmt_compare_soft { - obj_map const& m_soft; - maxsmt_compare_soft(obj_map const& soft): m_soft(soft) {} - bool operator()(expr* a, expr* b) const { - return m_soft.find(a) > m_soft.find(b); - } - }; - - void maxsmt_solver_base::process_mutex(expr_ref_vector& mutex, obj_map& new_soft) { - TRACE("opt", - for (expr* e : mutex) { - tout << mk_pp(e, m) << " |-> " << new_soft.find(e) << "\n"; - }); - if (mutex.size() <= 1) { - return; - } - maxsmt_compare_soft cmp(new_soft); - ptr_vector _mutex(mutex.size(), mutex.data()); - std::sort(_mutex.begin(), _mutex.end(), cmp); - mutex.reset(); - mutex.append(_mutex.size(), _mutex.data()); - - rational weight(0), sum1(0), sum2(0); - vector weights; - for (expr* e : mutex) { - rational w = new_soft.find(e); - weights.push_back(w); - sum1 += w; - new_soft.remove(e); - } - for (unsigned i = mutex.size(); i-- > 0; ) { - expr_ref soft(m.mk_or(i+1, mutex.data()), m); - m_trail.push_back(soft); - rational w = weights[i]; - weight = w - weight; - m_lower += weight*rational(i); - IF_VERBOSE(1, verbose_stream() << "(opt.maxsat mutex size: " << i + 1 << " weight: " << weight << ")\n";); - sum2 += weight*rational(i+1); - new_soft.insert(soft, weight); - for (; i > 0 && weights[i-1] == w; --i) {} - weight = w; - } - SASSERT(sum1 == sum2); - } - - maxsmt::maxsmt(maxsat_context& c, unsigned index): - m(c.get_manager()), m_c(c), m_index(index), - m_soft_constraints(m), m_answer(m) {} + m(c.get_manager()), m_c(c), m_index(index), m_answer(m) {} lbool maxsmt::operator()() { lbool is_sat = l_undef; @@ -235,30 +185,35 @@ namespace opt { symbol const& maxsat_engine = m_c.maxsat_engine(); IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); TRACE("opt_verbose", s().display(tout << "maxsmt\n") << "\n";); - if (optp.maxlex_enable() && is_maxlex(m_weights)) { - m_msolver = mk_maxlex(m_c, m_index, m_weights, m_soft_constraints); + if (optp.maxlex_enable() && is_maxlex(m_soft)) { + m_msolver = mk_maxlex(m_c, m_index, m_soft); } - else if (m_soft_constraints.empty() || maxsat_engine == symbol("maxres") || maxsat_engine == symbol::null) { - m_msolver = mk_maxres(m_c, m_index, m_weights, m_soft_constraints); + else if (m_soft.empty() || maxsat_engine == symbol("maxres") || maxsat_engine == symbol::null) { + m_msolver = mk_maxres(m_c, m_index, m_soft); + } + else if (maxsat_engine == symbol("maxres-bin")) { + m_msolver = mk_maxres_binary(m_c, m_index, m_soft); + } + else if (maxsat_engine == symbol("maxres-bin-delay")) { + m_msolver = mk_maxres_binary_delay(m_c, m_index, m_soft); } else if (maxsat_engine == symbol("pd-maxres")) { - m_msolver = mk_primal_dual_maxres(m_c, m_index, m_weights, m_soft_constraints); + m_msolver = mk_primal_dual_maxres(m_c, m_index, m_soft); } else if (maxsat_engine == symbol("wmax")) { - m_msolver = mk_wmax(m_c, m_weights, m_soft_constraints); + m_msolver = mk_wmax(m_c, m_soft, m_index); } else if (maxsat_engine == symbol("sortmax")) { - m_msolver = mk_sortmax(m_c, m_weights, m_soft_constraints); + m_msolver = mk_sortmax(m_c, m_soft, m_index); } else { auto str = maxsat_engine.str(); warning_msg("solver %s is not recognized, using default 'maxres'", str.c_str()); - m_msolver = mk_maxres(m_c, m_index, m_weights, m_soft_constraints); + m_msolver = mk_maxres(m_c, m_index, m_soft); } if (m_msolver) { m_msolver->updt_params(m_params); - m_msolver->set_adjust_value(m_adjust_value); is_sat = l_undef; try { is_sat = (*m_msolver)(); @@ -282,14 +237,13 @@ namespace opt { return is_sat; } - void maxsmt::set_adjust_value(adjust_value& adj) { - m_adjust_value = adj; + void maxsmt::reset_upper() { if (m_msolver) { - m_msolver->set_adjust_value(m_adjust_value); + m_msolver->reset_upper(); + m_upper = m_msolver->get_upper(); } } - void maxsmt::verify_assignment() { // TBD: have to use a different solver // because we don't push local scope any longer. @@ -311,7 +265,7 @@ namespace opt { rational q = m_msolver->get_lower(); if (q > r) r = q; } - return m_adjust_value(r); + return m_c.adjust(m_index, r); } rational maxsmt::get_upper() const { @@ -320,7 +274,7 @@ namespace opt { rational q = m_msolver->get_upper(); if (q < r) r = q; } - return m_adjust_value(r); + return m_c.adjust(m_index, r); } void maxsmt::update_lower(rational const& r) { @@ -348,39 +302,32 @@ namespace opt { SASSERT(w.is_pos()); unsigned index = 0; if (m_soft_constraint_index.find(f, index)) { - m_weights[index] += w; + m_soft[index].weight += w; } else { - m_soft_constraint_index.insert(f, m_weights.size()); - m_soft_constraints.push_back(f); - m_weights.push_back(w); + m_soft_constraint_index.insert(f, m_soft.size()); + m_soft.push_back(soft(expr_ref(f, m), w, false)); } m_upper += w; } struct cmp_first { bool operator()(std::pair const& x, std::pair const& y) const { - return x.first < y.first; + return x.second < y.second; } }; void maxsmt::display_answer(std::ostream& out) const { - vector> sorted_weights; - unsigned n = m_weights.size(); - for (unsigned i = 0; i < n; ++i) { - sorted_weights.push_back(std::make_pair(i, m_weights[i])); - } - std::sort(sorted_weights.begin(), sorted_weights.end(), cmp_first()); - sorted_weights.reverse(); - for (unsigned i = 0; i < n; ++i) { - unsigned idx = sorted_weights[i].first; - expr* e = m_soft_constraints[idx]; + + unsigned idx = 0; + for (auto const & [_e, w, t] : m_soft) { + expr* e = _e.get(); bool is_not = m.is_not(e, e); - out << m_weights[idx] << ": " << mk_pp(e, m) + out << w << ": " << mk_pp(e, m) << ((is_not != get_assignment(idx))?" |-> true ":" |-> false ") << "\n"; - - } + ++idx; + } } @@ -420,6 +367,7 @@ namespace opt { model_ref m_model; ref m_fm; symbol m_maxsat_engine; + vector m_offsets; public: solver_maxsat_context(params_ref& p, solver* s, model * m): m_params(p), @@ -444,6 +392,14 @@ namespace opt { bool verify_model(unsigned id, model* mdl, rational const& v) override { return true; }; void set_model(model_ref& _m) override { m_model = _m; } void model_updated(model* mdl) override { } // no-op + rational adjust(unsigned id, rational const& r) override { + m_offsets.reserve(id+1); + return r + m_offsets[id]; + } + void add_offset(unsigned id, rational const& r) override { + m_offsets.reserve(id+1); + m_offsets[id] += r; + } }; lbool maxsmt_wrapper::operator()(vector>& soft) { diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index ad355cc9e..b0ae5eeb1 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -34,8 +34,6 @@ namespace opt { class maxsat_context; class maxsmt_solver { - protected: - adjust_value m_adjust_value; public: virtual ~maxsmt_solver() {} virtual lbool operator()() = 0; @@ -45,28 +43,30 @@ namespace opt { virtual void collect_statistics(statistics& st) const = 0; virtual void get_model(model_ref& mdl, svector& labels) = 0; virtual void updt_params(params_ref& p) = 0; - void set_adjust_value(adjust_value& adj) { m_adjust_value = adj; } }; // --------------------------------------------- // base class with common utilities used // by maxsmt solvers - // + // + + struct soft { + expr_ref s; + rational weight; + lbool value; + void set_value(bool t) { value = t?l_true:l_undef; } + void set_value(lbool t) { value = t; } + bool is_true() const { return value == l_true; } + soft(expr_ref const& s, rational const& w, bool t): s(s), weight(w), value(t?l_true:l_undef) {} + }; + class maxsmt_solver_base : public maxsmt_solver { protected: - struct soft { - expr_ref s; - rational weight; - lbool value; - void set_value(bool t) { value = t?l_true:l_undef; } - void set_value(lbool t) { value = t; } - bool is_true() const { return value == l_true; } - soft(expr_ref const& s, rational const& w, bool t): s(s), weight(w), value(t?l_true:l_undef) {} - }; ast_manager& m; - maxsat_context& m_c; - vector m_soft; + maxsat_context& m_c; + unsigned m_index; + vector& m_soft; expr_ref_vector m_assertions; expr_ref_vector m_trail; rational m_lower; @@ -76,8 +76,8 @@ namespace opt { params_ref m_params; // config public: - maxsmt_solver_base(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); - + maxsmt_solver_base(maxsat_context& c, vector& soft, unsigned index); + ~maxsmt_solver_base() override {} rational get_lower() const override { return m_lower; } rational get_upper() const override { return m_upper; } @@ -102,16 +102,13 @@ namespace opt { smt::theory_wmaxsat& operator()(); }; - lbool find_mutexes(obj_map& new_soft); + void reset_upper(); protected: void enable_sls(bool force); void trace_bounds(char const* solver); - void process_mutex(expr_ref_vector& mutex, obj_map& new_soft); - - }; /** @@ -124,13 +121,11 @@ namespace opt { maxsat_context& m_c; unsigned m_index; scoped_ptr m_msolver; - expr_ref_vector m_soft_constraints; + vector m_soft; obj_map m_soft_constraint_index; expr_ref_vector m_answer; - vector m_weights; rational m_lower; rational m_upper; - adjust_value m_adjust_value; model_ref m_model; svector m_labels; params_ref m_params; @@ -139,10 +134,9 @@ namespace opt { lbool operator()(); void updt_params(params_ref& p); void add(expr* f, rational const& w); - void set_adjust_value(adjust_value& adj); - unsigned size() const { return m_soft_constraints.size(); } - expr* operator[](unsigned idx) const { return m_soft_constraints[idx]; } - rational weight(unsigned idx) const { return m_weights[idx]; } + unsigned size() const { return m_soft.size(); } + expr* operator[](unsigned idx) const { return m_soft[idx].s; } + rational weight(unsigned idx) const { return m_soft[idx].weight; } void commit_assignment(); rational get_lower() const; rational get_upper() const; @@ -153,6 +147,7 @@ namespace opt { void display_answer(std::ostream& out) const; void collect_statistics(statistics& st) const; void model_updated(model* mdl); + void reset_upper(); private: bool is_maxsat_problem(weights_t& ws) const; void verify_assignment(); diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index a8c0d5144..25982e89e 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -185,17 +185,36 @@ namespace opt { } void context::set_hard_constraints(expr_ref_vector const& fmls) { - if (m_scoped_state.set(fmls)) { + if (m_calling_on_model) { + for (expr* f : fmls) + add_hard_constraint(f); + return; + } + if (m_scoped_state.set(fmls)) + clear_state(); + } + + void context::add_hard_constraint(expr* f) { + if (m_calling_on_model) { + get_solver().assert_expr(f); + for (auto const& [k, v] : m_maxsmts) + v->reset_upper(); + for (unsigned i = 0; i < num_objectives(); ++i) { + auto const& o = m_scoped_state.m_objectives[i]; + if (o.m_type != O_MAXSMT) + m_optsmt.update_upper(o.m_index, inf_eps::infinity()); + } + } + else { + m_scoped_state.add(f); clear_state(); } } - - void context::add_hard_constraint(expr* f) { - m_scoped_state.add(f); - clear_state(); - } + void context::add_hard_constraint(expr* f, expr* t) { + if (m_calling_on_model) + throw default_exception("adding soft constraints is not supported during callbacks"); m_scoped_state.m_asms.push_back(t); m_scoped_state.add(m.mk_implies(t, f)); clear_state(); @@ -301,7 +320,7 @@ namespace opt { IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n"); - lbool is_sat = s.check_sat(asms.size(),asms.data()); + lbool is_sat = s.check_sat(asms.size(), asms.data()); TRACE("opt", s.display(tout << "initial search result: " << is_sat << "\n");); if (is_sat != l_false) { @@ -379,7 +398,7 @@ namespace opt { } void context::set_model(model_ref& m) { - m_model = m; + m_model = m; opt_params optp(m_params); if (optp.dump_models() && m) { model_ref md = m->copy(); @@ -389,6 +408,7 @@ namespace opt { model_ref md = m->copy(); if (!m_model_fixed.contains(md.get())) fix_model(md); + flet _calling(m_calling_on_model, true); m_on_model_eh(m_on_model_ctx, md); m_model_fixed.pop_back(); } @@ -910,7 +930,8 @@ namespace opt { bool context::is_maxsat(expr* fml, expr_ref_vector& terms, vector& weights, rational& offset, bool& neg, symbol& id, expr_ref& orig_term, unsigned& index) { - if (!is_app(fml)) return false; + if (!is_app(fml)) + return false; neg = false; orig_term = nullptr; index = 0; @@ -1085,8 +1106,7 @@ namespace opt { obj.m_weights.append(weights); obj.m_adjust_value.set_offset(offset); obj.m_adjust_value.set_negate(neg); - m_maxsmts.find(id)->set_adjust_value(obj.m_adjust_value); - TRACE("opt", tout << "maxsat: " << id << " offset:" << offset << "\n"; + TRACE("opt", tout << "maxsat: " << neg << " " << id << " offset: " << offset << "\n"; tout << terms << "\n";); } else if (is_maximize(fml, tr, orig_term, index)) { @@ -1138,7 +1158,14 @@ namespace opt { #endif } + rational context::adjust(unsigned id, rational const& v) { + return m_objectives[id].m_adjust_value(v); + } + void context::add_offset(unsigned id, rational const& o) { + m_objectives[id].m_adjust_value.add_offset(o); + } + bool context::verify_model(unsigned index, model* md, rational const& _v) { rational r; app_ref term = m_objectives[index].m_term; @@ -1321,24 +1348,21 @@ namespace opt { break; } case O_MAXSMT: { - bool ok = true; - for (unsigned j = 0; ok && j < obj.m_terms.size(); ++j) { + for (unsigned j = 0; j < obj.m_terms.size(); ++j) { val = (*m_model)(obj.m_terms[j]); TRACE("opt", tout << mk_pp(obj.m_terms[j], m) << " " << val << "\n";); - if (!m.is_true(val)) { + if (!m.is_true(val)) r += obj.m_weights[j]; - } } - if (ok) { - maxsmt& ms = *m_maxsmts.find(obj.m_id); - if (is_lower) { - ms.update_upper(r); - TRACE("opt", tout << "update upper from " << r << " to " << ms.get_upper() << "\n";); - } - else { - ms.update_lower(r); - TRACE("opt", tout << "update lower from " << r << " to " << ms.get_lower() << "\n";); - } + + maxsmt& ms = *m_maxsmts.find(obj.m_id); + if (is_lower) { + ms.update_upper(r); + TRACE("opt", tout << "update upper from " << r << " to " << ms.get_upper() << "\n";); + } + else { + ms.update_lower(r); + TRACE("opt", tout << "update lower from " << r << " to " << ms.get_lower() << "\n";); } break; } diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index dd717c392..c02689a38 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -57,6 +57,8 @@ namespace opt { virtual smt::context& smt_context() = 0; // access SMT context for SMT based MaxSMT solver (wmax requires SMT core) virtual unsigned num_objectives() = 0; virtual bool verify_model(unsigned id, model* mdl, rational const& v) = 0; + virtual rational adjust(unsigned id, rational const& v) = 0; + virtual void add_offset(unsigned id, rational const& o) = 0; virtual void set_model(model_ref& _m) = 0; virtual void model_updated(model* mdl) = 0; }; @@ -93,7 +95,7 @@ namespace opt { app_ref m_term; // for maximize, minimize term expr_ref_vector m_terms; // for maxsmt vector m_weights; // for maxsmt - adjust_value m_adjust_value; + adjust_value m_adjust_value; symbol m_id; // for maxsmt unsigned m_index; // for maximize/minimize index @@ -165,6 +167,7 @@ namespace opt { ast_manager& m; on_model_t m_on_model_ctx; std::function m_on_model_eh; + bool m_calling_on_model = false; arith_util m_arith; bv_util m_bv; expr_ref_vector m_hard_constraints; @@ -268,11 +271,14 @@ namespace opt { void model_updated(model* mdl) override; + rational adjust(unsigned id, rational const& v) override; + + void add_offset(unsigned id, rational const& o) override; + void register_on_model(on_model_t& ctx, std::function& on_model) { m_on_model_ctx = ctx; m_on_model_eh = on_model; } - void collect_timer_stats(statistics& st) const { if (m_time != 0) diff --git a/src/opt/opt_mux.h b/src/opt/opt_mux.h new file mode 100644 index 000000000..b093026f0 --- /dev/null +++ b/src/opt/opt_mux.h @@ -0,0 +1,32 @@ +/*++ +Copyright (c) 2021 Microsoft Corporation + +Module Name: + + opt_mux.h + +Abstract: + + Find mutexes - at most 1 constraints and modify soft constraints and bounds. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-04-11 + +--*/ + +#pragma once + +#include "opt/maxsmt.h" + +namespace opt { + + class mux { + ast_manager& m; + solver& s; + + public: + mux(solver& s); + lbool operator()(vector& soft, rational& bound); + }; +}; diff --git a/src/opt/opt_params.pyg b/src/opt/opt_params.pyg index 5106c5492..cb8f1d129 100644 --- a/src/opt/opt_params.pyg +++ b/src/opt/opt_params.pyg @@ -2,7 +2,7 @@ def_module_params('opt', description='optimization parameters', export=True, params=(('optsmt_engine', SYMBOL, 'basic', "select optimization engine: 'basic', 'symba'"), - ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'core_maxsat', 'wmax', 'maxres', 'pd-maxres'"), + ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'core_maxsat', 'wmax', 'maxres', 'pd-maxres', 'maxres-bin', 'maxres-bin-delay'"), ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', 'box'"), ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), ('dump_models', BOOL, False, 'display intermediary models to stdout'), diff --git a/src/opt/opt_preprocess.cpp b/src/opt/opt_preprocess.cpp new file mode 100644 index 000000000..bbf119266 --- /dev/null +++ b/src/opt/opt_preprocess.cpp @@ -0,0 +1,195 @@ +/*++ +Copyright (c) 2021 Microsoft Corporation + +Module Name: + + opt_preprocess.cpp + +Abstract: + + Pre-processing for MaxSMT + + Find mutexes - at most 1 constraints and modify soft constraints and bounds. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-04-11 + +--*/ + +#pragma once + +#include "opt/opt_preprocess.h" +#include "util/max_cliques.h" + +namespace opt { + + expr_ref_vector preprocess::propagate(expr* f, lbool& is_sat) { + expr_ref_vector asms(m); + asms.push_back(f); + is_sat = s.check_sat(asms); + return s.get_trail(1); + } + + bool preprocess::prop_mutexes(vector& softs, rational& lower) { + expr_ref_vector fmls(m); + obj_map new_soft = soft2map(softs, fmls); + + params_ref p; + p.set_uint("max_conflicts", 1); + s.updt_params(p); + + obj_hashtable pfmls, nfmls; + for (expr* f : fmls) + if (m.is_not(f, f)) + nfmls.insert(f); + else + pfmls.insert(f); + + u_map ids; + unsigned_vector ps; + for (expr* f : fmls) { + ids.insert(f->get_id(), f); + ps.push_back(f->get_id()); + } + + u_map conns; + + for (expr* f : fmls) { + lbool is_sat; + expr_ref_vector trail = propagate(f, is_sat); + if (is_sat == l_false) { + rational w = new_soft[f]; + lower += w; + s.assert_expr(m.mk_not(f)); + new_soft.remove(f); + continue; + } + + expr_ref_vector mux(m); + for (expr* g : trail) { + if (m.is_not(g, g)) { + if (pfmls.contains(g)) + mux.push_back(g); + } + else if (nfmls.contains(g)) + mux.push_back(m.mk_not(g)); + } + uint_set reach; + for (expr* g : mux) + reach.insert(g->get_id()); + conns.insert(f->get_id(), reach); + } + + p.set_uint("max_conflicts", UINT_MAX); + s.updt_params(p); + + struct neg_literal { + unsigned negate(unsigned id) { + throw default_exception("unexpected call"); + } + }; + max_cliques mc; + vector mutexes; + mc.cliques(ps, conns, mutexes); + + for (auto& mux : mutexes) { + expr_ref_vector _mux(m); + for (auto p : mux) + _mux.push_back(ids[p]); + process_mutex(_mux, new_soft, lower); + } + + softs.reset(); + for (auto const& [k, v] : new_soft) + softs.push_back(soft(expr_ref(k, m), v, false)); + m_trail.reset(); + return true; + } + + obj_map preprocess::soft2map(vector const& softs, expr_ref_vector& fmls) { + obj_map new_soft; + for (soft const& sf : softs) { + m_trail.push_back(sf.s); + if (new_soft.contains(sf.s)) + new_soft[sf.s] += sf.weight; + else + new_soft.insert(sf.s, sf.weight); + fmls.push_back(sf.s); + } + return new_soft; + } + + bool preprocess::find_mutexes(vector& softs, rational& lower) { + expr_ref_vector fmls(m); + obj_map new_soft = soft2map(softs, fmls); + vector mutexes; + lbool is_sat = s.find_mutexes(fmls, mutexes); + if (is_sat == l_false) + return true; + if (is_sat == l_undef) + return false; + for (auto& mux : mutexes) + process_mutex(mux, new_soft, lower); + softs.reset(); + for (auto const& [k, v] : new_soft) + softs.push_back(soft(expr_ref(k, m), v, false)); + m_trail.reset(); + return true; + } + + struct maxsmt_compare_soft { + obj_map const& m_soft; + maxsmt_compare_soft(obj_map const& soft): m_soft(soft) {} + bool operator()(expr* a, expr* b) const { + return m_soft.find(a) > m_soft.find(b); + } + }; + + void preprocess::process_mutex(expr_ref_vector& mutex, obj_map& new_soft, rational& lower) { + TRACE("opt", + for (expr* e : mutex) { + tout << mk_pp(e, m) << " |-> " << new_soft.find(e) << "\n"; + }); + if (mutex.size() <= 1) + return; + + maxsmt_compare_soft cmp(new_soft); + ptr_vector _mutex(mutex.size(), mutex.data()); + std::sort(_mutex.begin(), _mutex.end(), cmp); + mutex.reset(); + mutex.append(_mutex.size(), _mutex.data()); + + rational weight(0), sum1(0), sum2(0); + vector weights; + for (expr* e : mutex) { + rational w = new_soft.find(e); + weights.push_back(w); + sum1 += w; + new_soft.remove(e); + } + for (unsigned i = mutex.size(); i-- > 0; ) { + expr_ref soft(m.mk_or(i+1, mutex.data()), m); + m_trail.push_back(soft); + rational w = weights[i]; + weight = w - weight; + lower += weight*rational(i); + IF_VERBOSE(1, verbose_stream() << "(opt.maxsat mutex size: " << i + 1 << " weight: " << weight << ")\n";); + sum2 += weight*rational(i+1); + new_soft.insert(soft, weight); + for (; i > 0 && weights[i-1] == w; --i) {} + weight = w; + } + SASSERT(sum1 == sum2); + } + + preprocess::preprocess(solver& s): m(s.get_manager()), s(s), m_trail(m) {} + + bool preprocess::operator()(vector& soft, rational& lower) { + if (!find_mutexes(soft, lower)) + return false; + if (false && !prop_mutexes(soft, lower)) + return false; + return true; + } +}; diff --git a/src/opt/opt_preprocess.h b/src/opt/opt_preprocess.h new file mode 100644 index 000000000..567a750ed --- /dev/null +++ b/src/opt/opt_preprocess.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2021 Microsoft Corporation + +Module Name: + + opt_preprocess.h + +Abstract: + + Pre-processing for MaxSMT + + Find mutexes - at most 1 constraints and modify soft constraints and bounds. + +Author: + + Nikolaj Bjorner (nbjorner) 2022-04-11 + +--*/ + +#pragma once + +#include "opt/maxsmt.h" + +namespace opt { + + class preprocess { + ast_manager& m; + solver& s; + expr_ref_vector m_trail; + + expr_ref_vector propagate(expr* f, lbool& is_sat); + obj_map soft2map(vector const& softs, expr_ref_vector& fmls); + bool find_mutexes(vector& softs, rational& lower); + bool prop_mutexes(vector& softs, rational& lower); + void process_mutex(expr_ref_vector& mutex, obj_map& new_soft, rational& lower); + + public: + preprocess(solver& s); + bool operator()(vector& soft, rational& lower); + }; +}; diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index caac008fd..e71287400 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -48,6 +48,7 @@ namespace opt { void set_offset(rational const& o) { m_offset = o; } void set_negate(bool neg) { m_negate = neg; } rational const& get_offset() const { return m_offset; } + void add_offset(rational const& o) { if (m_negate) m_offset -= o; else m_offset += o; } bool get_negate() { return m_negate; } inf_eps operator()(inf_eps const& r) const { inf_eps result = r; @@ -107,7 +108,7 @@ namespace opt { lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override; lbool preferred_sat(expr_ref_vector const& asms, vector& cores) override; void get_levels(ptr_vector const& vars, unsigned_vector& depth) override; - expr_ref_vector get_trail() override { return m_context.get_trail(); } + expr_ref_vector get_trail(unsigned max_level) override { return m_context.get_trail(max_level); } expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } void set_phase(expr* e) override { m_context.set_phase(e); } phase* get_phase() override { return m_context.get_phase(); } diff --git a/src/opt/sortmax.cpp b/src/opt/sortmax.cpp index c6a2f04a9..962369bf2 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -36,34 +36,27 @@ namespace opt { expr_ref_vector m_trail; func_decl_ref_vector m_fresh; ref m_filter; - sortmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): - maxsmt_solver_base(c, ws, soft), m_sort(*this), m_trail(m), m_fresh(m) {} + sortmax(maxsat_context& c, vector& s, unsigned index): + maxsmt_solver_base(c, s, index), m_sort(*this), m_trail(m), m_fresh(m) {} ~sortmax() override {} lbool operator()() override { - obj_map soft; - if (!init()) { - return l_false; - } - lbool is_sat = find_mutexes(soft); - if (is_sat != l_true) { - return is_sat; - } + if (!init()) + return l_undef; + + lbool is_sat = l_true; m_filter = alloc(generic_model_converter, m, "sortmax"); - rational offset = m_lower; - m_upper = offset; expr_ref_vector in(m); expr_ref tmp(m); ptr_vector out; - obj_map::iterator it = soft.begin(), end = soft.end(); - for (; it != end; ++it) { - if (!it->m_value.is_unsigned()) { + for (auto const & [e, w, t] : m_soft) { + if (!w.is_unsigned()) { throw default_exception("sortmax can only handle unsigned weights. Use a different heuristic."); } - unsigned n = it->m_value.get_unsigned(); + unsigned n = w.get_unsigned(); while (n > 0) { - in.push_back(it->m_key); + in.push_back(e); --n; } } @@ -71,19 +64,15 @@ namespace opt { // initialize sorting network outputs using the initial assignment. unsigned first = 0; - it = soft.begin(); - for (; it != end; ++it) { - if (m_model->is_true(it->m_key)) { - unsigned n = it->m_value.get_unsigned(); + for (auto const & [e, w, t] : m_soft) { + if (t == l_true) { + unsigned n = w.get_unsigned(); while (n > 0) { s().assert_expr(out[first]); ++first; --n; } } - else { - m_upper += it->m_value; - } } while (l_true == is_sat && first < out.size() && m_lower < m_upper) { trace_bounds("sortmax"); @@ -149,8 +138,8 @@ namespace opt { }; - maxsmt_solver_base* mk_sortmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { - return alloc(sortmax, c, ws, soft); + maxsmt_solver_base* mk_sortmax(maxsat_context& c, vector& s, unsigned index) { + return alloc(sortmax, c, s, index); } } diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index 22a660799..1fbd26cb8 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -44,8 +44,8 @@ namespace opt { } public: - wmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): - maxsmt_solver_base(c, ws, soft), + wmax(maxsat_context& c, vector& s, unsigned index): + maxsmt_solver_base(c, s, index), m_trail(m), m_defs(m) {} @@ -54,22 +54,18 @@ namespace opt { lbool operator()() override { TRACE("opt", tout << "weighted maxsat\n";); scoped_ensure_theory wth(*this); - obj_map soft; reset(); - lbool is_sat = find_mutexes(soft); - if (is_sat != l_true) { - return is_sat; - } - m_upper = m_lower; + if (init()) + return l_undef; + + lbool is_sat = l_true; + expr_ref_vector asms(m); vector cores; - for (auto const& kv : soft) { - assert_weighted(wth(), kv.m_key, kv.m_value); - if (!is_true(kv.m_key)) { - m_upper += kv.m_value; - } - } + for (auto const& [k, w, t] : m_soft) + assert_weighted(wth(), k, w); + wth().init_min_cost(m_upper - m_lower); trace_bounds("wmax"); @@ -308,8 +304,8 @@ namespace opt { }; - maxsmt_solver_base* mk_wmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { - return alloc(wmax, c, ws, soft); + maxsmt_solver_base* mk_wmax(maxsat_context& c, vector & s, unsigned index) { + return alloc(wmax, c, s, index); } } diff --git a/src/opt/wmax.h b/src/opt/wmax.h index 6cc3ed46b..7f5b26ac6 100644 --- a/src/opt/wmax.h +++ b/src/opt/wmax.h @@ -22,8 +22,8 @@ Notes: #include "opt/maxsmt.h" namespace opt { - maxsmt_solver_base* mk_wmax(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); + maxsmt_solver_base* mk_wmax(maxsat_context& c, vector& s, unsigned index); - maxsmt_solver_base* mk_sortmax(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); + maxsmt_solver_base* mk_sortmax(maxsat_context& c, vector& s, unsigned index); } 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_scc.cpp b/src/sat/sat_scc.cpp index e7adc33ec..c21f67ce5 100644 --- a/src/sat/sat_scc.cpp +++ b/src/sat/sat_scc.cpp @@ -67,15 +67,8 @@ namespace sat { } }; - unsigned scc::operator()() { - if (m_solver.m_inconsistent) - return 0; - if (!m_scc) - return 0; - CASSERT("scc_bug", m_solver.check_invariant()); - report rpt(*this); - TRACE("scc", m_solver.display(tout);); - TRACE("scc_details", m_solver.display_watches(tout);); + bool scc::extract_roots(literal_vector& roots, bool_var_vector& to_elim) { + literal_vector lits; unsigned_vector index; unsigned_vector lowlink; unsigned_vector s; @@ -84,11 +77,9 @@ namespace sat { index.resize(num_lits, UINT_MAX); lowlink.resize(num_lits, UINT_MAX); in_s.resize(num_lits, false); - literal_vector roots, lits; roots.resize(m_solver.num_vars(), null_literal); unsigned next_index = 0; svector frames; - bool_var_vector to_elim; for (unsigned l_idx = 0; l_idx < num_lits; l_idx++) { if (index[l_idx] != UINT_MAX) @@ -182,7 +173,7 @@ namespace sat { j--; if (to_literal(l2_idx) == ~l) { m_solver.set_conflict(); - return 0; + return false; } if (m_solver.is_external(to_literal(l2_idx).var())) { r = to_literal(l2_idx); @@ -226,6 +217,23 @@ namespace sat { roots[i] = literal(i, false); } } + return true; + } + + + unsigned scc::operator()() { + if (m_solver.m_inconsistent) + return 0; + if (!m_scc) + return 0; + CASSERT("scc_bug", m_solver.check_invariant()); + report rpt(*this); + TRACE("scc", m_solver.display(tout);); + TRACE("scc_details", m_solver.display_watches(tout);); + literal_vector roots; + bool_var_vector to_elim; + if (!extract_roots(roots, to_elim)) + return 0; TRACE("scc", for (unsigned i = 0; i < roots.size(); i++) { tout << i << " -> " << roots[i] << "\n"; } tout << "to_elim: "; for (unsigned v : to_elim) tout << v << " "; tout << "\n";); m_num_elim += to_elim.size(); @@ -234,9 +242,8 @@ namespace sat { TRACE("scc_detail", m_solver.display(tout);); CASSERT("scc_bug", m_solver.check_invariant()); - if (m_scc_tr) { + if (m_scc_tr) reduce_tr(); - } TRACE("scc_detail", m_solver.display(tout);); return to_elim.size(); } diff --git a/src/sat/sat_scc.h b/src/sat/sat_scc.h index 0554e09d4..af239c53d 100644 --- a/src/sat/sat_scc.h +++ b/src/sat/sat_scc.h @@ -41,9 +41,13 @@ namespace sat { void reduce_tr(); unsigned reduce_tr(bool learned); + public: scc(solver & s, params_ref const & p); + + bool extract_roots(literal_vector& roots, bool_var_vector& lits); + unsigned operator()(); void updt_params(params_ref const & p); 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/sat_solver.cpp b/src/sat/sat_solver.cpp index 26cf5c9de..b43b6a10a 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -2968,11 +2968,9 @@ namespace sat { } break; case PS_SAT_CACHING: - if (m_search_state == s_sat) { - for (unsigned i = 0; i < m_phase.size(); ++i) { - m_phase[i] = m_best_phase[i]; - } - } + if (m_search_state == s_sat) + for (unsigned i = 0; i < m_phase.size(); ++i) + m_phase[i] = m_best_phase[i]; break; case PS_RANDOM: for (auto& p : m_phase) p = (m_rand() % 2) == 0; @@ -3823,6 +3821,8 @@ namespace sat { void solver::move_to_front(bool_var b) { if (b >= num_vars()) return; + if (m_case_split_queue.empty()) + return; bool_var next = m_case_split_queue.min_var(); auto next_act = m_activity[next]; set_activity(b, next_act + 1); @@ -4177,7 +4177,7 @@ namespace sat { lbool solver::find_mutexes(literal_vector const& lits, vector & mutexes) { max_cliques mc; m_user_bin_clauses.reset(); - m_binary_clause_graph.reset(); + // m_binary_clause_graph.reset(); collect_bin_clauses(m_user_bin_clauses, true, false); hashtable, default_eq > seen_bc; for (auto const& b : m_user_bin_clauses) { @@ -4192,20 +4192,22 @@ namespace sat { vector _mutexes; literal_vector _lits(lits); if (m_ext) { - // m_ext->find_mutexes(_lits, mutexes); + m_ext->find_mutexes(_lits, mutexes); } unsigned_vector ps; - for (literal lit : _lits) { + for (literal lit : _lits) ps.push_back(lit.index()); - } - mc.cliques(ps, _mutexes); + mc.cliques2(ps, _mutexes); + vector> sorted; for (auto const& mux : _mutexes) { literal_vector clique; - for (auto const& idx : mux) { + sorted.reserve(mux.size() + 1); + for (auto const& idx : mux) clique.push_back(to_literal(idx)); - } - mutexes.push_back(clique); + sorted[mux.size()].push_back(clique); } + for (unsigned i = sorted.size(); i-- > 0; ) + mutexes.append(sorted[i]); return l_true; } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 74bb182e8..0f242bd4d 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -374,7 +374,7 @@ namespace sat { void set_eliminated(bool_var v, bool f) override; bool was_eliminated(literal l) const { return was_eliminated(l.var()); } void set_phase(literal l) override { if (l.var() < num_vars()) m_best_phase[l.var()] = m_phase[l.var()] = !l.sign(); } - bool_var get_phase(bool_var b) { return m_phase.get(b, false); } + bool get_phase(bool_var b) { return m_phase.get(b, false); } void move_to_front(bool_var b); unsigned scope_lvl() const { return m_scope_lvl; } unsigned search_lvl() const { return m_search_lvl; } @@ -755,7 +755,7 @@ namespace sat { u_map m_antecedents; literal_vector m_todo_antecedents; - vector m_binary_clause_graph; + // vector m_binary_clause_graph; bool extract_assumptions(literal lit, index_set& s); diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 86de919dc..af0d9dd1b 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -390,7 +390,7 @@ public: } } - expr_ref_vector get_trail() override { + expr_ref_vector get_trail(unsigned max_level) override { expr_ref_vector result(m); unsigned sz = m_solver.trail_size(); expr_ref_vector lit2expr(m); @@ -398,7 +398,11 @@ public: m_map.mk_inv(lit2expr); for (unsigned i = 0; i < sz; ++i) { sat::literal lit = m_solver.trail_literal(i); - result.push_back(lit2expr[lit.index()].get()); + if (m_solver.lvl(lit) > max_level) + continue; + expr_ref e(lit2expr.get(lit.index()), m); + if (e) + result.push_back(e); } return result; } @@ -683,11 +687,11 @@ public: ensure_euf()->user_propagate_register_diseq(diseq_eh); } - unsigned user_propagate_register_expr(expr* e) override { - return ensure_euf()->user_propagate_register_expr(e); + void user_propagate_register_expr(expr* e) override { + ensure_euf()->user_propagate_register_expr(e); } - void user_propagate_register_created(user_propagator::created_eh_t& r) { + void user_propagate_register_created(user_propagator::created_eh_t& r) override { ensure_euf()->user_propagate_register_created(r); } diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index 6f4963990..732cbfc6f 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -85,7 +85,7 @@ namespace arith { m_nla->settings().grobner_number_of_conflicts_to_report() = prms.arith_nl_grobner_cnfl_to_report(); m_nla->settings().grobner_quota() = prms.arith_nl_gr_q(); m_nla->settings().grobner_frequency() = prms.arith_nl_grobner_frequency(); - m_nla->settings().expensive_patching() = prms.arith_nl_expp(); + m_nla->settings().expensive_patching() = false; } } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 5a102fa14..e02a42979 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -549,13 +549,14 @@ namespace arith { found_compatible = false; for (; it != end; ++it) { api_bound* a2 = *it; - if (a1 == a2) continue; - if (a2->get_bound_kind() != kind) continue; + if (a1 == a2) + continue; + if (a2->get_bound_kind() != kind) + continue; rational const& k2(a2->get_value()); found_compatible = true; - if (k1 < k2) { + if (k1 < k2) return it; - } } return end; } diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index d8405ea10..a1d9c8629 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -419,6 +419,7 @@ namespace arith { void false_case_of_check_nla(const nla::lemma& l); void dbg_finalize_model(model& mdl); + public: solver(euf::solver& ctx, theory_id id); ~solver() override; @@ -426,6 +427,8 @@ namespace arith { void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) override; void asserted(literal l) override; sat::check_result check() override; + void simplify() override {} + void init_search() override {} std::ostream& display(std::ostream& out) const override; std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override; diff --git a/src/sat/smt/array_axioms.cpp b/src/sat/smt/array_axioms.cpp index 296c35b8c..c0f0e9a72 100644 --- a/src/sat/smt/array_axioms.cpp +++ b/src/sat/smt/array_axioms.cpp @@ -556,7 +556,7 @@ namespace array { bool has_default = false; for (euf::enode* p : euf::enode_parents(n)) has_default |= a.is_default(p->get_expr()); - if (has_default) + if (!has_default) propagate_parent_default(v); } 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/atom2bool_var.cpp b/src/sat/smt/atom2bool_var.cpp index 65c91ed79..b12f51fb0 100644 --- a/src/sat/smt/atom2bool_var.cpp +++ b/src/sat/smt/atom2bool_var.cpp @@ -19,6 +19,7 @@ Notes: #include "util/ref_util.h" #include "ast/ast_smt2_pp.h" +#include "ast/ast_util.h" #include "tactic/goal.h" #include "sat/smt/atom2bool_var.h" @@ -27,7 +28,7 @@ void atom2bool_var::mk_inv(expr_ref_vector & lit2expr) const { sat::literal l(static_cast(kv.m_value), false); lit2expr.set(l.index(), kv.m_key); l.neg(); - lit2expr.set(l.index(), m().mk_not(kv.m_key)); + lit2expr.set(l.index(), mk_not(m(), kv.m_key)); } } @@ -40,12 +41,12 @@ void atom2bool_var::mk_var_inv(expr_ref_vector & var2expr) const { sat::bool_var atom2bool_var::to_bool_var(expr * n) const { unsigned idx = m_id2map.get(n->get_id(), UINT_MAX); - if (idx == UINT_MAX) { + if (idx == UINT_MAX) return sat::null_bool_var; - } - else { + else if (idx >= m_mapping.size()) + return sat::null_bool_var; + else return m_mapping[idx].m_value; - } } struct collect_boolean_interface_proc { diff --git a/src/sat/smt/euf_internalize.cpp b/src/sat/smt/euf_internalize.cpp index 6cc72eba5..045b92c99 100644 --- a/src/sat/smt/euf_internalize.cpp +++ b/src/sat/smt/euf_internalize.cpp @@ -190,8 +190,9 @@ namespace euf { m_egraph.set_bool_var(n, v); if (m.is_eq(e) || m.is_or(e) || m.is_and(e) || m.is_not(e)) m_egraph.set_merge_enabled(n, false); - if (s().value(lit) != l_undef) - m_egraph.set_value(n, s().value(lit)); + lbool val = s().value(lit); + if (val != l_undef) + m_egraph.set_value(n, val, justification::external(to_ptr(val == l_true ? lit : ~lit))); return lit; } @@ -311,6 +312,7 @@ namespace euf { } } else if (m.is_distinct(e)) { + // TODO - add lazy case for large values of sz. expr_ref_vector eqs(m); unsigned sz = n->num_args(); for (unsigned i = 0; i < sz; ++i) { 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.cpp b/src/sat/smt/euf_solver.cpp index 3c7342136..2ef77ac6a 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -309,7 +309,7 @@ namespace euf { if (!n) return; bool sign = l.sign(); - m_egraph.set_value(n, sign ? l_false : l_true); + m_egraph.set_value(n, sign ? l_false : l_true, justification::external(to_ptr(l))); for (auto const& th : enode_th_vars(n)) m_id2solver[th.get_id()]->asserted(l); diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 36d68ced0..a2183af22 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); @@ -396,6 +397,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); @@ -435,9 +437,9 @@ namespace euf { check_for_user_propagator(); m_user_propagator->register_created(ceh); } - unsigned user_propagate_register_expr(expr* e) { + void user_propagate_register_expr(expr* e) { check_for_user_propagator(); - return m_user_propagator->add_expr(e); + m_user_propagator->add_expr(e); } // solver factory diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index 5c67a05c5..8340543ec 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -21,6 +21,7 @@ Author: #include "sat/smt/pb_solver.h" #include "sat/smt/euf_solver.h" #include "sat/sat_simplifier_params.hpp" +#include "sat/sat_scc.h" namespace pb { @@ -69,7 +70,7 @@ namespace pb { SASSERT(s().at_base_lvl()); if (p.lit() != sat::null_literal && value(p.lit()) == l_false) { TRACE("ba", tout << "pb: flip sign " << p << "\n";); - IF_VERBOSE(1, verbose_stream() << "sign is flipped " << p << "\n";); + IF_VERBOSE(2, verbose_stream() << "sign is flipped " << p << "\n";); return; } bool nullify = p.lit() != sat::null_literal && value(p.lit()) == l_true; @@ -109,22 +110,21 @@ namespace pb { } } else if (true_val >= p.k()) { - if (p.lit() != sat::null_literal) { - IF_VERBOSE(100, display(verbose_stream() << "assign true literal ", p, true);); + IF_VERBOSE(100, display(verbose_stream() << "assign true literal ", p, true);); + if (p.lit() != sat::null_literal) s().assign_scoped(p.lit()); - } - remove_constraint(p, "is true"); + else + remove_constraint(p, "is true"); } else if (slack + true_val < p.k()) { if (p.lit() != sat::null_literal) { - IF_VERBOSE(100, display(verbose_stream() << "assign false literal ", p, true);); + IF_VERBOSE(3, display(verbose_stream() << "assign false literal ", p, true);); s().assign_scoped(~p.lit()); } else { - IF_VERBOSE(1, verbose_stream() << "unsat during simplification\n";); + IF_VERBOSE(1, verbose_stream() << "unsat during simplification\n"); s().set_conflict(sat::justification(0)); } - remove_constraint(p, "is false"); } else if (slack + true_val == p.k()) { literal_vector lits(p.literals()); @@ -132,14 +132,16 @@ namespace pb { remove_constraint(p, "is tight"); } else { + unsigned sz = p.size(); clear_watch(p); unsigned j = 0; for (unsigned i = 0; i < sz; ++i) { literal l = p.get_lit(i); if (value(l) == l_undef) { - if (i != j) p.swap(i, j); - ++j; + if (i != j) + p.swap(i, j); + ++j; } } sz = j; @@ -167,6 +169,7 @@ namespace pb { _bad_id = 11111111; SASSERT(p.well_formed()); m_simplify_change = true; + } } @@ -1422,7 +1425,7 @@ namespace pb { c->watch_literal(*this, ~lit); } if (!c->well_formed()) - std::cout << *c << "\n"; + IF_VERBOSE(0, verbose_stream() << *c << "\n"); VERIFY(c->well_formed()); if (m_solver && m_solver->get_config().m_drat) { std::function fn = [&](std::ostream& out) { @@ -1457,7 +1460,7 @@ namespace pb { return nullptr; } rational weight(0); - for (auto const [w, l] : wlits) + for (auto const &[w, l] : wlits) weight += w; if (weight < k) { if (lit == sat::null_literal) @@ -2035,7 +2038,7 @@ namespace pb { m_constraint_to_reinit.shrink(sz); } - void solver::simplify() { + void solver::simplify() { if (!s().at_base_lvl()) s().pop_to_base_level(); if (s().inconsistent()) @@ -2192,7 +2195,7 @@ namespace pb { } } - bool solver::set_root(literal l, literal r) { + bool solver::set_root(literal l, literal r) { if (s().is_assumption(l.var())) return false; reserve_roots(); @@ -2205,17 +2208,18 @@ namespace pb { } void solver::flush_roots() { - if (m_roots.empty()) return; + if (m_roots.empty()) + return; reserve_roots(); - // validate(); + DEBUG_CODE(validate();); m_constraint_removed = false; for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) flush_roots(*m_constraints[i]); for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) flush_roots(*m_learned[i]); cleanup_constraints(); - // validate(); - // validate_eliminated(); + DEBUG_CODE(validate();); + DEBUG_CODE(validate_eliminated();); } void solver::validate_eliminated() { @@ -2633,6 +2637,10 @@ namespace pb { * add ~root(~l) to c, k <- k + 1 */ void solver::unit_strengthen() { + return; + + // TODO - this is unsound exposed by 4_21_21_-1.txt + sat::big big(s().m_rand); big.init(s(), true); for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) @@ -2642,7 +2650,8 @@ namespace pb { } void solver::unit_strengthen(sat::big& big, constraint& p) { - if (p.lit() != sat::null_literal) return; + if (p.lit() != sat::null_literal) + return; unsigned sz = p.size(); for (unsigned i = 0; i < sz; ++i) { literal u = p.get_lit(i); @@ -2690,8 +2699,8 @@ namespace pb { } } ++m_stats.m_num_big_strengthenings; + constraint* c = add_pb_ge(sat::null_literal, wlits, b, p.learned()); p.set_removed(); - add_pb_ge(sat::null_literal, wlits, b, p.learned()); return; } } @@ -2764,7 +2773,7 @@ namespace pb { ptr_vector::iterator it2 = it; ptr_vector::iterator end = cs.end(); for (; it != end; ++it) { - constraint& c = *(*it); + constraint& c = *(*it); if (c.was_removed()) { clear_watch(c); c.nullify_tracking_literal(*this); @@ -3133,32 +3142,35 @@ namespace pb { void solver::find_mutexes(literal_vector& lits, vector & mutexes) { sat::literal_set slits(lits); bool change = false; + for (constraint* cp : m_constraints) { - if (!cp->is_card()) continue; + if (!cp->is_card()) + continue; + if (cp->lit() != sat::null_literal) + continue; card const& c = cp->to_card(); - if (c.size() == c.k() + 1) { - literal_vector mux; - for (literal lit : c) { - if (slits.contains(~lit)) { - mux.push_back(~lit); - } - } - if (mux.size() <= 1) { - continue; - } + if (c.size() != c.k() + 1) + continue; - for (literal m : mux) { - slits.remove(m); - } - change = true; - mutexes.push_back(mux); - } + literal_vector mux; + for (literal lit : c) + if (slits.contains(~lit)) + mux.push_back(~lit); + + if (mux.size() <= 1) + continue; + + for (literal m : mux) + slits.remove(m); + + change = true; + mutexes.push_back(mux); } - if (!change) return; + if (!change) + return; lits.reset(); - for (literal l : slits) { + for (literal l : slits) lits.push_back(l); - } } void solver::display(std::ostream& out, ineq const& ineq, bool values) const { @@ -3224,9 +3236,6 @@ namespace pb { display(verbose_stream(), p, true);); return false; } - // if (value(alit) == l_true && lvl(l) == lvl(alit)) { - // std::cout << "same level " << alit << " " << l << "\n"; - // } } // the sum of elements not in r or alit add up to less than k. unsigned sum = 0; @@ -3421,19 +3430,11 @@ namespace pb { ++num_max_level; } } - if (m_overflow) { + if (m_overflow) return nullptr; - } - if (slack >= k) { -#if 0 - return active2constraint(); - active2pb(m_A); - std::cout << "not asserting\n"; - display(std::cout, m_A, true); -#endif + if (slack >= k) return nullptr; - } // produce asserting cardinality constraint literal_vector lits; 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/q_model_fixer.cpp b/src/sat/smt/q_model_fixer.cpp index 456525c42..cdea60d23 100644 --- a/src/sat/smt/q_model_fixer.cpp +++ b/src/sat/smt/q_model_fixer.cpp @@ -184,6 +184,8 @@ namespace q { for (euf::enode* n : ctx.get_egraph().enodes_of(f)) { expr* t = n->get_arg(idx)->get_expr(); values.push_back(mdl(t)); + if (!m.is_value(values.back())) + return expr_ref(m.mk_var(idx, srt), m); md->v2t.insert(values.back(), t); md->t2v.insert(t, values.back()); } @@ -299,6 +301,10 @@ namespace q { auto term = [&](unsigned j) { return md->v2t[md->values[j]]; }; + + for (unsigned j = 0; j < sz; ++j) + std::cout << mk_pp(md->values[j], m) << "\n"; + expr* arg = t->get_arg(i); diff --git a/src/sat/smt/q_solver.cpp b/src/sat/smt/q_solver.cpp index 2bb7e2a30..7c1c19040 100644 --- a/src/sat/smt/q_solver.cpp +++ b/src/sat/smt/q_solver.cpp @@ -262,6 +262,8 @@ namespace q { m_expanded.push_back(r); return true; } + if (r == q) + return false; q = to_quantifier(r); } if (is_forall(q)) 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/smt/user_solver.cpp b/src/sat/smt/user_solver.cpp index febbe9383..d24af253e 100644 --- a/src/sat/smt/user_solver.cpp +++ b/src/sat/smt/user_solver.cpp @@ -28,31 +28,33 @@ namespace user_solver { dealloc(m_api_context); } - unsigned solver::add_expr(expr* e) { + void solver::add_expr(expr* e) { force_push(); ctx.internalize(e, false); euf::enode* n = expr2enode(e); if (is_attached_to_var(n)) - return n->get_th_var(get_id()); + return; euf::theory_var v = mk_var(n); ctx.attach_th_var(n, this, v); expr_ref r(m); sat::literal_vector explain; if (ctx.is_fixed(n, r, explain)) - m_prop.push_back(prop_info(explain, v, r)); - return v; + m_prop.push_back(prop_info(explain, v, r)); } void solver::propagate_cb( - unsigned num_fixed, unsigned const* fixed_ids, - unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, + unsigned num_fixed, expr* const* fixed_ids, + unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, expr* conseq) { - m_prop.push_back(prop_info(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, expr_ref(conseq, m))); + m_fixed_ids.reset(); + for (unsigned i = 0; i < num_fixed; ++i) + m_fixed_ids.push_back(get_th_var(fixed_ids[i])); + m_prop.push_back(prop_info(num_fixed, m_fixed_ids.data(), num_eqs, eq_lhs, eq_rhs, expr_ref(conseq, m))); DEBUG_CODE(validate_propagation();); } - unsigned solver::register_cb(expr* e) { - return add_expr(e); + void solver::register_cb(expr* e) { + add_expr(e); } sat::check_result solver::check() { @@ -68,7 +70,7 @@ namespace user_solver { return; force_push(); m_id2justification.setx(v, sat::literal_vector(num_lits, jlits), sat::literal_vector()); - m_fixed_eh(m_user_context, this, v, value); + m_fixed_eh(m_user_context, this, var2expr(v), value); } void solver::asserted(sat::literal lit) { @@ -80,13 +82,13 @@ namespace user_solver { sat::literal_vector lits; lits.push_back(lit); m_id2justification.setx(v, lits, sat::literal_vector()); - m_fixed_eh(m_user_context, this, v, lit.sign() ? m.mk_false() : m.mk_true()); + m_fixed_eh(m_user_context, this, var2expr(v), lit.sign() ? m.mk_false() : m.mk_true()); } void solver::push_core() { th_euf_solver::push_core(); m_prop_lim.push_back(m_prop.size()); - m_push_eh(m_user_context); + m_push_eh(m_user_context, this); } void solver::pop_core(unsigned num_scopes) { @@ -94,7 +96,7 @@ namespace user_solver { unsigned old_sz = m_prop_lim.size() - num_scopes; m_prop.shrink(m_prop_lim[old_sz]); m_prop_lim.shrink(old_sz); - m_pop_eh(m_user_context, num_scopes); + m_pop_eh(m_user_context, this, num_scopes); } void solver::propagate_consequence(prop_info const& prop) { @@ -141,9 +143,9 @@ namespace user_solver { auto& j = justification::from_index(idx); auto const& prop = m_prop[j.m_propagation_index]; for (unsigned id : prop.m_ids) - r.append(m_id2justification[id]); + r.append(m_id2justification[id]); for (auto const& p : prop.m_eqs) - ctx.add_antecedent(var2enode(p.first), var2enode(p.second)); + ctx.add_antecedent(expr2enode(p.first), expr2enode(p.second)); } /* @@ -156,7 +158,7 @@ namespace user_solver { for (auto lit: m_id2justification[id]) VERIFY(s().value(lit) == l_true); for (auto const& p : prop.m_eqs) - VERIFY(var2enode(p.first)->get_root() == var2enode(p.second)->get_root()); + VERIFY(expr2enode(p.first)->get_root() == expr2enode(p.second)->get_root()); } std::ostream& solver::display(std::ostream& out) const { @@ -171,7 +173,7 @@ namespace user_solver { for (unsigned id : prop.m_ids) out << id << ": " << m_id2justification[id]; for (auto const& p : prop.m_eqs) - out << "v" << p.first << " == v" << p.second << " "; + out << "v" << mk_pp(p.first, m) << " == v" << mk_pp(p.second, m) << " "; return out; } @@ -224,9 +226,9 @@ namespace user_solver { SASSERT(!n || !n->is_attached_to(get_id())); if (!n) n = mk_enode(e, false); - auto v = add_expr(e); + add_expr(e); if (m_created_eh) - m_created_eh(m_user_context, this, e, v); + m_created_eh(m_user_context, this, e); return true; } diff --git a/src/sat/smt/user_solver.h b/src/sat/smt/user_solver.h index a30bc6a6d..13948db81 100644 --- a/src/sat/smt/user_solver.h +++ b/src/sat/smt/user_solver.h @@ -29,13 +29,13 @@ namespace user_solver { class solver : public euf::th_euf_solver, public user_propagator::callback { struct prop_info { - unsigned_vector m_ids; - expr_ref m_conseq; - svector> m_eqs; + unsigned_vector m_ids; + expr_ref m_conseq; + svector> m_eqs; sat::literal_vector m_lits; - euf::theory_var m_var = euf::null_theory_var; + euf::theory_var m_var = euf::null_theory_var; - prop_info(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, expr_ref const& c): + prop_info(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, expr_ref const& c): m_ids(num_fixed, fixed_ids), m_conseq(c) { @@ -72,6 +72,7 @@ namespace user_solver { vector m_id2justification; sat::literal_vector m_lits; euf::enode_pair_vector m_eqs; + unsigned_vector m_fixed_ids; stats m_stats; struct justification { @@ -118,7 +119,7 @@ namespace user_solver { m_fresh_eh = fresh_eh; } - unsigned add_expr(expr* e); + void add_expr(expr* e); 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; } @@ -128,8 +129,8 @@ namespace user_solver { bool has_fixed() const { return (bool)m_fixed_eh; } - void propagate_cb(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* lhs, unsigned const* rhs, expr* conseq) override; - unsigned register_cb(expr* e) override; + void propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* lhs, expr* const* rhs, expr* conseq) override; + void register_cb(expr* e) override; void new_fixed_eh(euf::theory_var v, expr* value, unsigned num_lits, sat::literal const* jlits); diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 30d73a21a..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)) @@ -805,6 +817,7 @@ struct goal2sat::imp : public sat::sat_internalizer { } sat::literal internalize(expr* n, bool redundant) override { + bool is_not = m.is_not(n, n); flet _top(m_top_level, false); unsigned sz = m_result_stack.size(); (void)sz; @@ -820,6 +833,9 @@ struct goal2sat::imp : public sat::sat_internalizer { m_map.insert(n, result.var()); m_solver.set_external(result.var()); } + + if (is_not) + result.neg(); return result; } diff --git a/src/sat/tactic/sat2goal.cpp b/src/sat/tactic/sat2goal.cpp index 482e441ef..7614857cb 100644 --- a/src/sat/tactic/sat2goal.cpp +++ b/src/sat/tactic/sat2goal.cpp @@ -218,7 +218,7 @@ struct sat2goal::imp { } sat::literal lit(l.var(), false); m_lit2expr.set(lit.index(), aux); - m_lit2expr.set((~lit).index(), m.mk_not(aux)); + m_lit2expr.set((~lit).index(), mk_not(m, aux)); } return m_lit2expr.get(l.index()); } diff --git a/src/smt/old_interval.cpp b/src/smt/old_interval.cpp index efdb78754..e719f7e2b 100644 --- a/src/smt/old_interval.cpp +++ b/src/smt/old_interval.cpp @@ -292,7 +292,7 @@ v_dependency * interval::join_opt(v_dependency * d1, v_dependency * d2, v_depend } interval & interval::operator*=(interval const & other) { -#if Z3DEBUG || _TRACE +#if defined(Z3DEBUG) || defined(_TRACE) bool contains_zero1 = contains_zero(); bool contains_zero2 = other.contains_zero(); #endif diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 6167a844b..b90ba760d 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -47,14 +47,14 @@ def_module_params(module_name='smt', ('induction', BOOL, False, 'enable generation of induction lemmas'), ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), ('bv.enable_int2bv', BOOL, True, 'enable support for int2bv and bv2int operators'), - ('bv.eq_axioms', BOOL, True, 'add dynamic equality axioms'), ('bv.watch_diseq', BOOL, False, 'use watch lists instead of eager axioms for bit-vectors'), ('bv.delay', BOOL, True, 'delay internalize expensive bit-vector operations'), ('bv.polysat', BOOL, False, 'use polysat bit-vector solver'), + ('bv.eq_axioms', BOOL, True, 'enable redundant equality axioms for bit-vectors'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), ('arith.solver', UINT, 6, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation, relevant only if smt.arith.solver=2'), - ('arith.nl.nra', BOOL, True, 'call nra_solver when incremental lianirization does not produce a lemma, this option is ignored when arith.nl=false, relevant only if smt.arith.solver=6'), + ('arith.nl.nra', BOOL, True, 'call nra_solver when incremental linearization does not produce a lemma, this option is ignored when arith.nl=false, relevant only if smt.arith.solver=6'), ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters, relevant only if smt.arith.solver=2'), ('arith.nl.rounds', UINT, 1024, 'threshold for number of (nested) final checks for non linear arithmetic, relevant only if smt.arith.solver=2'), ('arith.nl.order', BOOL, True, 'run order lemmas'), diff --git a/src/smt/params/theory_bv_params.cpp b/src/smt/params/theory_bv_params.cpp index ea73702f7..38a6e5fb0 100644 --- a/src/smt/params/theory_bv_params.cpp +++ b/src/smt/params/theory_bv_params.cpp @@ -26,9 +26,9 @@ void theory_bv_params::updt_params(params_ref const & _p) { m_hi_div0 = rp.hi_div0(); m_bv_reflect = p.bv_reflect(); m_bv_enable_int2bv2int = p.bv_enable_int2bv(); - m_bv_eq_axioms = p.bv_eq_axioms(); m_bv_delay = p.bv_delay(); m_bv_polysat = p.bv_polysat(); + m_bv_eq_axioms = p.bv_eq_axioms(); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; @@ -38,8 +38,8 @@ void theory_bv_params::display(std::ostream & out) const { DISPLAY_PARAM(m_hi_div0); DISPLAY_PARAM(m_bv_reflect); DISPLAY_PARAM(m_bv_lazy_le); - DISPLAY_PARAM(m_bv_cc); DISPLAY_PARAM(m_bv_eq_axioms); + DISPLAY_PARAM(m_bv_cc); DISPLAY_PARAM(m_bv_blast_max_size); DISPLAY_PARAM(m_bv_enable_int2bv2int); DISPLAY_PARAM(m_bv_delay); diff --git a/src/smt/smt_consequences.cpp b/src/smt/smt_consequences.cpp index b54e92a70..657e222da 100644 --- a/src/smt/smt_consequences.cpp +++ b/src/smt/smt_consequences.cpp @@ -612,7 +612,7 @@ namespace smt { } } vector _mutexes; - mc.cliques(ps, _mutexes); + mc.cliques2(ps, _mutexes); for (auto const& mux : _mutexes) { expr_ref_vector lits(m); for (unsigned idx : mux) { diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index d659329ca..3e444bec7 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" @@ -171,7 +172,7 @@ namespace smt { dst_ctx.setup_context(dst_ctx.m_fparams.m_auto_config); dst_ctx.internalize_assertions(); - dst_ctx.copy_user_propagator(src_ctx); + dst_ctx.copy_user_propagator(src_ctx, true); TRACE("smt_context", src_ctx.display(tout); @@ -193,16 +194,19 @@ namespace smt { } } - void context::copy_user_propagator(context& src_ctx) { + void context::copy_user_propagator(context& src_ctx, bool copy_registered) { if (!src_ctx.m_user_propagator) return; - ast_translation tr(src_ctx.m, m, false); auto* p = get_theory(m.mk_family_id("user_propagator")); m_user_propagator = reinterpret_cast(p); SASSERT(m_user_propagator); + if (!copy_registered) { + return; + } + 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); } } @@ -211,6 +215,7 @@ namespace smt { new_ctx->m_is_auxiliary = true; new_ctx->set_logic(l == nullptr ? m_setup.get_logic() : *l); copy_plugins(*this, *new_ctx); + new_ctx->copy_user_propagator(*this, false); return new_ctx; } @@ -1761,6 +1766,70 @@ namespace smt { m_bvar_inc *= INV_ACTIVITY_LIMIT; } + /** + \brief Returns a truth value for the given variable + */ + bool context::guess(bool_var var, lbool phase) { + if (is_quantifier(m_bool_var2expr[var])) { + // Overriding any decision on how to assign the quantifier. + // assigning a quantifier to false is equivalent to make it irrelevant. + phase = l_false; + } + literal l(var, false); + + if (phase != l_undef) + return phase == l_true; + + bool_var_data & d = m_bdata[var]; + if (d.try_true_first()) + return true; + switch (m_fparams.m_phase_selection) { + case PS_THEORY: + if (m_phase_cache_on && d.m_phase_available) { + return m_bdata[var].m_phase; + } + if (!m_phase_cache_on && d.is_theory_atom()) { + theory * th = m_theories.get_plugin(d.get_theory()); + lbool th_phase = th->get_phase(var); + if (th_phase != l_undef) { + return th_phase == l_true; + } + } + if (track_occs()) { + if (m_lit_occs[l.index()] == 0) { + return false; + } + if (m_lit_occs[(~l).index()] == 0) { + return true; + } + } + return m_phase_default; + case PS_CACHING: + case PS_CACHING_CONSERVATIVE: + case PS_CACHING_CONSERVATIVE2: + if (m_phase_cache_on && d.m_phase_available) { + TRACE("phase_selection", tout << "using cached value, is_pos: " << m_bdata[var].m_phase << ", var: p" << var << "\n";); + return m_bdata[var].m_phase; + } + else { + TRACE("phase_selection", tout << "setting to false\n";); + return m_phase_default; + } + case PS_ALWAYS_FALSE: + return false; + case PS_ALWAYS_TRUE: + return true; + case PS_RANDOM: + return m_random() % 2 == 0; + case PS_OCCURRENCE: { + return m_lit_occs[l.index()] > m_lit_occs[(~l).index()]; + } + default: + UNREACHABLE(); + return false; + } + } + /** \brief Execute next case split, return false if there are no more case splits to be performed. @@ -1802,81 +1871,15 @@ namespace smt { TRACE("decide", tout << "splitting, lvl: " << m_scope_lvl << "\n";); TRACE("decide_detail", tout << mk_pp(bool_var2expr(var), m) << "\n";); - - bool is_pos; - - if (is_quantifier(m_bool_var2expr[var])) { - // Overriding any decision on how to assign the quantifier. - // assigning a quantifier to false is equivalent to make it irrelevant. - phase = l_false; - } + + bool is_pos = guess(var, phase); literal l(var, false); - if (phase != l_undef) { - is_pos = phase == l_true; - } - else { - bool_var_data & d = m_bdata[var]; - if (d.try_true_first()) { - is_pos = true; - } - else { - switch (m_fparams.m_phase_selection) { - case PS_THEORY: - if (m_phase_cache_on && d.m_phase_available) { - is_pos = m_bdata[var].m_phase; - break; - } - if (!m_phase_cache_on && d.is_theory_atom()) { - theory * th = m_theories.get_plugin(d.get_theory()); - lbool th_phase = th->get_phase(var); - if (th_phase != l_undef) { - is_pos = th_phase == l_true; - break; - } - } - if (track_occs()) { - if (m_lit_occs[l.index()] == 0) { - is_pos = false; - break; - } - if (m_lit_occs[(~l).index()] == 0) { - is_pos = true; - break; - } - } - is_pos = m_phase_default; - break; - case PS_CACHING: - case PS_CACHING_CONSERVATIVE: - case PS_CACHING_CONSERVATIVE2: - if (m_phase_cache_on && d.m_phase_available) { - TRACE("phase_selection", tout << "using cached value, is_pos: " << m_bdata[var].m_phase << ", var: p" << var << "\n";); - is_pos = m_bdata[var].m_phase; - } - else { - TRACE("phase_selection", tout << "setting to false\n";); - is_pos = m_phase_default; - } - break; - case PS_ALWAYS_FALSE: - is_pos = false; - break; - case PS_ALWAYS_TRUE: - is_pos = true; - break; - case PS_RANDOM: - is_pos = (m_random() % 2 == 0); - break; - case PS_OCCURRENCE: { - is_pos = m_lit_occs[l.index()] > m_lit_occs[(~l).index()]; - break; - } - default: - is_pos = false; - UNREACHABLE(); - } - } + bool_var original_choice = var; + + if (decide_user_interference(var, is_pos)) { + m_case_split_queue->unassign_var_eh(original_choice); + l = literal(var, false); } if (!is_pos) l.neg(); @@ -1884,7 +1887,7 @@ namespace smt { assign(l, b_justification::mk_axiom(), true); return true; } - + /** \brief Update counter that is used to enable/disable phase caching. */ @@ -2901,6 +2904,14 @@ namespace smt { return m_user_propagator && m_user_propagator->has_fixed() && n->get_th_var(m_user_propagator->get_family_id()) != null_theory_var; } + bool context::decide_user_interference(bool_var& var, bool& is_pos) { + if (!m_user_propagator || !m_user_propagator->has_decide()) + return false; + bool_var old = var; + m_user_propagator->decide(var, is_pos); + return old != var; + } + void context::assign_fixed(enode* n, expr* val, unsigned sz, literal const* explain) { theory_var v = n->get_th_var(m_user_propagator->get_family_id()); m_user_propagator->new_fixed_eh(v, val, sz, explain); @@ -3037,7 +3048,8 @@ namespace smt { } } } - } else { + } + else { literal_vector new_case_split; for (unsigned i = 0; i < num_lits; ++i) { literal l = lits[i]; @@ -3731,7 +3743,7 @@ namespace smt { if (status == l_false) { return false; } - if (status == l_true && !m_qmanager->has_quantifiers()) { + if (status == l_true && !m_qmanager->has_quantifiers() && !m_has_lambda) { return false; } if (status == l_true && m_qmanager->has_quantifiers()) { @@ -3754,6 +3766,11 @@ namespace smt { break; } } + if (status == l_true && m_has_lambda) { + m_last_search_failure = LAMBDAS; + status = l_undef; + return false; + } inc_limits(); if (status == l_true || !m_fparams.m_restart_adaptive || m_agility < m_fparams.m_restart_agility_threshold) { SASSERT(!inconsistent()); @@ -3765,13 +3782,13 @@ namespace smt { pop_scope(m_scope_lvl - curr_lvl); SASSERT(at_search_level()); } - for (theory* th : m_theory_set) { - if (!inconsistent()) th->restart_eh(); - } + for (theory* th : m_theory_set) + if (!inconsistent()) + th->restart_eh(); + TRACE("mbqi_bug_detail", tout << "before instantiating quantifiers...\n";); - if (!inconsistent()) { + if (!inconsistent()) m_qmanager->restart_eh(); - } if (inconsistent()) { VERIFY(!resolve_conflict()); status = l_false; @@ -3993,6 +4010,10 @@ namespace smt { TRACE("final_check_step", tout << "RESULT final_check: " << result << "\n";); if (result == FC_GIVEUP && f != OK) m_last_search_failure = f; + if (result == FC_DONE && m_has_lambda) { + m_last_search_failure = LAMBDAS; + result = FC_GIVEUP; + } return result; } @@ -4598,18 +4619,36 @@ namespace smt { } } - expr_ref_vector context::get_trail() { + expr_ref_vector context::get_trail(unsigned max_level) { expr_ref_vector result(get_manager()); - get_assignments(result); + for (literal lit : m_assigned_literals) { + if (get_assign_level(lit) > max_level + m_base_lvl) + continue; + expr_ref e(m); + literal2expr(lit, e); + result.push_back(std::move(e)); + } return result; } + void context::get_units(expr_ref_vector& result) { + expr_mark visited; + for (expr* fml : result) + visited.mark(fml); + expr_ref_vector trail = get_trail(0); + for (expr* t : trail) + if (!visited.is_marked(t)) + result.push_back(t); + } + + failure context::get_last_search_failure() const { return m_last_search_failure; } 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 fd6c12482..696a5cc39 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -773,6 +773,7 @@ namespace smt { void internalize_quantifier(quantifier * q, bool gate_ctx); + bool m_has_lambda = false; void internalize_lambda(quantifier * q); void internalize_formula_core(app * n, bool gate_ctx); @@ -1133,6 +1134,8 @@ namespace smt { enode * get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args); + bool guess(bool_var var, lbool phase); + protected: bool decide(); @@ -1575,7 +1578,7 @@ namespace smt { void log_stats(); - void copy_user_propagator(context& src); + void copy_user_propagator(context& src, bool copy_registered); public: context(ast_manager & m, smt_params & fp, params_ref const & p = params_ref()); @@ -1666,7 +1669,7 @@ namespace smt { void get_levels(ptr_vector const& vars, unsigned_vector& depth); - expr_ref_vector get_trail(); + expr_ref_vector get_trail(unsigned max_level); void get_model(model_ref & m); @@ -1690,6 +1693,8 @@ namespace smt { void get_assertions(ptr_vector & result) { m_asserted_formulas.get_assertions(result); } + void get_units(expr_ref_vector& result); + /* * user-propagator */ @@ -1723,10 +1728,10 @@ namespace smt { m_user_propagator->register_diseq(diseq_eh); } - unsigned user_propagate_register_expr(expr* e) { + void user_propagate_register_expr(expr* e) { if (!m_user_propagator) throw default_exception("user propagator must be initialized"); - return m_user_propagator->add_expr(e); + m_user_propagator->add_expr(e, true); } void user_propagate_register_created(user_propagator::created_eh_t& r) { @@ -1735,8 +1740,16 @@ namespace smt { m_user_propagator->register_created(r); } + void user_propagate_register_decide(user_propagator::decide_eh_t& r) { + if (!m_user_propagator) + throw default_exception("user propagator must be initialized"); + m_user_propagator->register_decide(r); + } + bool watches_fixed(enode* n) const; + bool decide_user_interference(bool_var& var, bool& is_pos); + void assign_fixed(enode* n, expr* val, unsigned sz, literal const* explain); void assign_fixed(enode* n, expr* val, literal_vector const& explain) { diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 4499752e5..181d9d529 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -56,6 +56,8 @@ namespace smt { return out; case QUANTIFIERS: return out << "QUANTIFIERS"; + case LAMBDAS: + return out << "LAMBDAS"; } UNREACHABLE(); return out << "?"; @@ -79,6 +81,7 @@ namespace smt { } case RESOURCE_LIMIT: r = "(resource limits reached)"; break; case QUANTIFIERS: r = "(incomplete quantifiers)"; break; + case LAMBDAS: r = "(incomplete lambdas)"; break; case UNKNOWN: r = m_unknown; break; } return r; diff --git a/src/smt/smt_failure.h b/src/smt/smt_failure.h index ab5f0827b..ab706297f 100644 --- a/src/smt/smt_failure.h +++ b/src/smt/smt_failure.h @@ -31,6 +31,7 @@ namespace smt { NUM_CONFLICTS, //!< Maximum number of conflicts was reached THEORY, //!< Theory is incomplete RESOURCE_LIMIT, + LAMBDAS, //!< Logical context contains lambdas. QUANTIFIERS //!< Logical context contains universal quantifiers. }; diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 1c899ef18..fd55d5fd0 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -375,7 +375,6 @@ namespace smt { } else { SASSERT(is_app(n)); - SASSERT(!gate_ctx); internalize_term(to_app(n)); } } @@ -605,6 +604,8 @@ namespace smt { bool_var bv = get_bool_var(fa); assign(literal(bv, false), nullptr); mark_as_relevant(bv); + push_trail(value_trail(m_has_lambda)); + m_has_lambda = true; } /** diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index 4a2c8bae9..8f442596c 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -177,6 +177,10 @@ namespace smt { void kernel::get_assignments(expr_ref_vector & result) { m_imp->m_kernel.get_assignments(result); } + + void kernel::get_units(expr_ref_vector & result) { + m_imp->m_kernel.get_units(result); + } void kernel::get_relevant_labels(expr * cnstr, buffer & result) { m_imp->m_kernel.get_relevant_labels(cnstr, result); @@ -244,8 +248,8 @@ namespace smt { m_imp->m_kernel.get_levels(vars, depth); } - expr_ref_vector kernel::get_trail() { - return m_imp->m_kernel.get_trail(); + expr_ref_vector kernel::get_trail(unsigned max_level) { + return m_imp->m_kernel.get_trail(max_level); } void kernel::user_propagate_init( @@ -272,12 +276,16 @@ namespace smt { m_imp->m_kernel.user_propagate_register_diseq(diseq_eh); } - unsigned kernel::user_propagate_register_expr(expr* e) { - return m_imp->m_kernel.user_propagate_register_expr(e); + void kernel::user_propagate_register_expr(expr* e) { + m_imp->m_kernel.user_propagate_register_expr(e); } void kernel::user_propagate_register_created(user_propagator::created_eh_t& r) { m_imp->m_kernel.user_propagate_register_created(r); } -}; \ No newline at end of file + void kernel::user_propagate_register_decide(user_propagator::decide_eh_t& r) { + m_imp->m_kernel.user_propagate_register_decide(r); + } + +}; diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index 681bdd55b..4fa840f5e 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -202,6 +202,12 @@ namespace smt { \brief Return the set of formulas assigned by the kernel. */ void get_assignments(expr_ref_vector & result); + + + /** + \brief Return units assigned by the kernel. + */ + void get_units(expr_ref_vector& result); /** \brief Return the set of relevant labels in the last check command. @@ -242,7 +248,7 @@ namespace smt { /** \brief retrieve trail of assignment stack. */ - expr_ref_vector get_trail(); + expr_ref_vector get_trail(unsigned max_level); /** \brief (For debubbing purposes) Prints the state of the kernel @@ -301,10 +307,12 @@ namespace smt { void user_propagate_register_diseq(user_propagator::eq_eh_t& diseq_eh); - unsigned user_propagate_register_expr(expr* e); + void user_propagate_register_expr(expr* e); void user_propagate_register_created(user_propagator::created_eh_t& r); + void user_propagate_register_decide(user_propagator::decide_eh_t& r); + /** \brief Return a reference to smt::context. This breaks abstractions. diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 03f55585a..5064ed7ef 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -208,8 +208,8 @@ namespace { m_context.get_levels(vars, depth); } - expr_ref_vector get_trail() override { - return m_context.get_trail(); + expr_ref_vector get_trail(unsigned max_level) override { + return m_context.get_trail(max_level); } void user_propagate_init( @@ -236,14 +236,18 @@ namespace { m_context.user_propagate_register_diseq(diseq_eh); } - unsigned user_propagate_register_expr(expr* e) override { - return m_context.user_propagate_register_expr(e); + void user_propagate_register_expr(expr* e) override { + m_context.user_propagate_register_expr(e); } void user_propagate_register_created(user_propagator::created_eh_t& c) override { m_context.user_propagate_register_created(c); } + void user_propagate_register_decide(user_propagator::decide_eh_t& c) override { + m_context.user_propagate_register_decide(c); + } + struct scoped_minimize_core { smt_solver& s; expr_ref_vector m_assumptions; @@ -318,6 +322,10 @@ namespace { return m_context.get_formula(idx); } + void get_units_core(expr_ref_vector& units) override { + m_context.get_units(units); + } + expr_ref_vector cube(expr_ref_vector& vars, unsigned cutoff) override { ast_manager& m = get_manager(); if (!m_cuber) { diff --git a/src/smt/tactic/smt_tactic_core.cpp b/src/smt/tactic/smt_tactic_core.cpp index bf2ea9bd6..9c5fc1c8e 100644 --- a/src/smt/tactic/smt_tactic_core.cpp +++ b/src/smt/tactic/smt_tactic_core.cpp @@ -40,6 +40,7 @@ class smt_tactic : public tactic { ast_manager& m; smt_params m_params; params_ref m_params_ref; + expr_ref_vector m_vars; statistics m_stats; smt::kernel* m_ctx = nullptr; symbol m_logic; @@ -321,141 +322,22 @@ public: user_propagator::eq_eh_t m_eq_eh; user_propagator::eq_eh_t m_diseq_eh; user_propagator::created_eh_t m_created_eh; - - expr_ref_vector m_vars; - unsigned_vector m_var2internal; - unsigned_vector m_internal2var; - unsigned_vector m_limit; + user_propagator::decide_eh_t m_decide_eh; - user_propagator::push_eh_t i_push_eh; - user_propagator::pop_eh_t i_pop_eh; - user_propagator::fixed_eh_t i_fixed_eh; - user_propagator::final_eh_t i_final_eh; - user_propagator::eq_eh_t i_eq_eh; - user_propagator::eq_eh_t i_diseq_eh; - user_propagator::created_eh_t i_created_eh; - - - struct callback : public user_propagator::callback { - smt_tactic* t = nullptr; - user_propagator::callback* cb = nullptr; - unsigned_vector fixed, lhs, rhs; - void propagate_cb(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, expr* conseq) override { - fixed.reset(); - lhs.reset(); - rhs.reset(); - for (unsigned i = 0; i < num_fixed; ++i) - fixed.push_back(t->m_var2internal[fixed_ids[i]]); - for (unsigned i = 0; i < num_eqs; ++i) { - lhs.push_back(t->m_var2internal[eq_lhs[i]]); - rhs.push_back(t->m_var2internal[eq_rhs[i]]); - } - cb->propagate_cb(num_fixed, fixed.data(), num_eqs, lhs.data(), rhs.data(), conseq); - } - - unsigned register_cb(expr* e) override { - unsigned j = t->m_vars.size(); - t->m_vars.push_back(e); - unsigned i = cb->register_cb(e); - t->m_var2internal.setx(j, i, 0); - t->m_internal2var.setx(i, j, 0); - return j; - } - }; - - callback i_cb; - - void init_i_fixed_eh() { - if (!m_fixed_eh) - return; - i_fixed_eh = [this](void* ctx, user_propagator::callback* cb, unsigned id, expr* value) { - i_cb.t = this; - i_cb.cb = cb; - m_fixed_eh(ctx, &i_cb, m_internal2var[id], value); - }; - m_ctx->user_propagate_register_fixed(i_fixed_eh); - } - - void init_i_final_eh() { - if (!m_final_eh) - return; - i_final_eh = [this](void* ctx, user_propagator::callback* cb) { - i_cb.t = this; - i_cb.cb = cb; - m_final_eh(ctx, &i_cb); - }; - m_ctx->user_propagate_register_final(i_final_eh); - } - - void init_i_eq_eh() { - if (!m_eq_eh) - return; - i_eq_eh = [this](void* ctx, user_propagator::callback* cb, unsigned u, unsigned v) { - i_cb.t = this; - i_cb.cb = cb; - m_eq_eh(ctx, &i_cb, m_internal2var[u], m_internal2var[v]); - }; - m_ctx->user_propagate_register_eq(i_eq_eh); - } - - void init_i_diseq_eh() { - if (!m_diseq_eh) - return; - i_diseq_eh = [this](void* ctx, user_propagator::callback* cb, unsigned u, unsigned v) { - i_cb.t = this; - i_cb.cb = cb; - m_diseq_eh(ctx, &i_cb, m_internal2var[u], m_internal2var[v]); - }; - m_ctx->user_propagate_register_diseq(i_diseq_eh); - } - - void init_i_created_eh() { - if (!m_created_eh) - return; - i_created_eh = [this](void* ctx, user_propagator::callback* cb, expr* e, unsigned i) { - unsigned j = m_vars.size(); - m_vars.push_back(e); - m_internal2var.setx(i, j, 0); - m_var2internal.setx(j, i, 0); - m_created_eh(ctx, cb, e, j); - }; - m_ctx->user_propagate_register_created(i_created_eh); - } - - void init_i_push_pop() { - i_push_eh = [this](void* ctx) { - m_limit.push_back(m_vars.size()); - m_push_eh(ctx); - }; - i_pop_eh = [this](void* ctx, unsigned n) { - unsigned old_sz = m_limit.size() - n; - unsigned num_vars = m_limit[old_sz]; - m_vars.shrink(num_vars); - m_limit.shrink(old_sz); - m_pop_eh(ctx, n); - }; - } - - void user_propagate_delay_init() { if (!m_user_ctx) return; - init_i_push_pop(); - m_ctx->user_propagate_init(m_user_ctx, i_push_eh, i_pop_eh, m_fresh_eh); - init_i_fixed_eh(); - init_i_final_eh(); - init_i_eq_eh(); - init_i_diseq_eh(); - init_i_created_eh(); + m_ctx->user_propagate_init(m_user_ctx, m_push_eh, m_pop_eh, m_fresh_eh); + if (m_fixed_eh) m_ctx->user_propagate_register_fixed(m_fixed_eh); + if (m_final_eh) m_ctx->user_propagate_register_final(m_final_eh); + if (m_eq_eh) m_ctx->user_propagate_register_eq(m_eq_eh); + if (m_diseq_eh) m_ctx->user_propagate_register_diseq(m_diseq_eh); + if (m_created_eh) m_ctx->user_propagate_register_created(m_created_eh); + if (m_decide_eh) m_ctx->user_propagate_register_decide(m_decide_eh); - unsigned i = 0; - for (expr* v : m_vars) { - unsigned j = m_ctx->user_propagate_register_expr(v); - m_var2internal.setx(i, j, 0); - m_internal2var.setx(j, i, 0); - ++i; - } + for (expr* v : m_vars) + m_ctx->user_propagate_register_expr(v); } void user_propagate_clear() override { @@ -496,9 +378,8 @@ public: m_diseq_eh = diseq_eh; } - unsigned user_propagate_register_expr(expr* e) override { + void user_propagate_register_expr(expr* e) override { m_vars.push_back(e); - return m_vars.size() - 1; } void user_propagate_register_created(user_propagator::created_eh_t& created_eh) override { 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_array.cpp b/src/smt/theory_array.cpp index ab1673a9d..2d166c5f6 100644 --- a/src/smt/theory_array.cpp +++ b/src/smt/theory_array.cpp @@ -240,12 +240,16 @@ namespace smt { bool theory_array::internalize_term_core(app * n) { TRACE("array_bug", tout << mk_bounded_pp(n, m) << "\n";); unsigned num_args = n->get_num_args(); - for (unsigned i = 0; i < num_args; i++) - ctx.internalize(n->get_arg(i), false); - if (ctx.e_internalized(n)) { + for (expr* arg : *n) + ctx.internalize(arg, false); + // force merge-tf by re-internalizing expression. + for (expr* arg : *n) + if (m.is_bool(arg)) + ctx.internalize(arg, false); + if (ctx.e_internalized(n)) return false; - } - enode * e = ctx.mk_enode(n, false, false, true); + + enode * e = ctx.mk_enode(n, false, false, true); if (!is_attached_to_var(e)) mk_var(e); diff --git a/src/smt/theory_array.h b/src/smt/theory_array.h index 67ae8a8a5..46922e049 100644 --- a/src/smt/theory_array.h +++ b/src/smt/theory_array.h @@ -28,7 +28,7 @@ namespace smt { unsigned m_num_axiom1, m_num_axiom2a, m_num_axiom2b, m_num_extensionality, m_num_eq_splits; unsigned m_num_map_axiom, m_num_default_map_axiom; unsigned m_num_select_const_axiom, m_num_default_store_axiom, m_num_default_const_axiom, m_num_default_as_array_axiom; - unsigned m_num_select_as_array_axiom; + unsigned m_num_select_as_array_axiom, m_num_default_lambda_axiom; void reset() { memset(this, 0, sizeof(theory_array_stats)); } theory_array_stats() { reset(); } }; diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index 79b380671..9bc0b733f 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -958,8 +958,8 @@ namespace smt { fi->insert_entry(args.data(), result); } - parameter p[1] = { parameter(f) }; - return m.mk_app(m_fid, OP_AS_ARRAY, 1, p); + parameter p(f); + return m.mk_app(m_fid, OP_AS_ARRAY, 1, &p); } }; diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 6e7403737..1a793a116 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -181,6 +181,17 @@ namespace smt { } } + void theory_array_full::add_lambda(theory_var v, enode* lam) { + var_data * d = m_var_data[v]; + unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d); + if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) + set_prop_upward(v, d); + ptr_vector & lambdas = m_var_data_full[v]->m_lambdas; + m_trail_stack.push(push_back_trail(lambdas)); + lambdas.push_back(lam); + instantiate_default_lambda_def_axiom(lam); + } + void theory_array_full::add_as_array(theory_var v, enode* arr) { var_data * d = m_var_data[v]; unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d); @@ -238,6 +249,10 @@ namespace smt { instantiate_default_as_array_axiom(n); d->m_as_arrays.push_back(n); } + else if (m.is_lambda_def(n->get_decl())) { + instantiate_default_lambda_def_axiom(n); + d->m_lambdas.push_back(n); + } return r; } @@ -331,18 +346,16 @@ namespace smt { // v1 is the new root SASSERT(v1 == find(v1)); var_data_full * d2 = m_var_data_full[v2]; - for (enode * n : d2->m_maps) { + for (enode * n : d2->m_maps) add_map(v1, n); - } - for (enode * n : d2->m_parent_maps) { + for (enode * n : d2->m_parent_maps) add_parent_map(v1, n); - } - for (enode * n : d2->m_consts) { + for (enode * n : d2->m_consts) add_const(v1, n); - } - for (enode * n : d2->m_as_arrays) { + for (enode * n : d2->m_as_arrays) add_as_array(v1, n); - } + for (enode* n : d2->m_lambdas) + add_lambda(v1, n); TRACE("array", tout << pp(get_enode(v1), m) << "\n"; tout << pp(get_enode(v2), m) << "\n"; @@ -577,6 +590,23 @@ namespace smt { #endif } + bool theory_array_full::instantiate_default_lambda_def_axiom(enode* arr) { + if (!ctx.add_fingerprint(this, m_default_lambda_fingerprint, 1, &arr)) + return false; + m_stats.m_num_default_lambda_axiom++; + expr* def = mk_default(arr->get_expr()); + quantifier* lam = m.is_lambda_def(arr->get_decl()); + expr_ref_vector args(m); + args.push_back(lam); + for (unsigned i = 0; i < lam->get_num_decls(); ++i) + args.push_back(mk_epsilon(lam->get_decl_sort(i)).first); + expr_ref val(mk_select(args), m); + ctx.internalize(def, false); + ctx.internalize(val.get(), false); + return try_assign_eq(val.get(), def); + } + + bool theory_array_full::has_unitary_domain(app* array_term) { SASSERT(is_array_sort(array_term)); sort* s = array_term->get_sort(); @@ -715,8 +745,8 @@ namespace smt { // // expr_ref_vector args1(m), args2(m); - args1.push_back(store_app->get_arg(0)); - args2.push_back(store_app); + args1.push_back(store_app); + args2.push_back(store_app->get_arg(0)); for (unsigned i = 1; i + 1 < num_args; ++i) { expr* arg = store_app->get_arg(i); @@ -732,40 +762,6 @@ namespace smt { ctx.internalize(def2, false); is_new = try_assign_eq(def1, sel1) || try_assign_eq(def2, sel2); return is_new; - - -#if 0 - // - // This is incorrect, issue #5593. - // - - // let A = store(B, i, v) - // - // Add: - // default(A) = ite(epsilon1 = i, v, default(B)) - // A[diag(i)] = B[diag(i)] - // - expr_ref_vector eqs(m); - expr_ref_vector args1(m), args2(m); - args1.push_back(store_app->get_arg(0)); - args2.push_back(store_app); - - for (unsigned i = 1; i + 1 < num_args; ++i) { - expr* arg = store_app->get_arg(i); - sort* srt = arg->get_sort(); - auto ep = mk_epsilon(srt); - eqs.push_back(m.mk_eq(ep.first, arg)); - args1.push_back(m.mk_app(ep.second, arg)); - args2.push_back(m.mk_app(ep.second, arg)); - } - expr_ref eq(mk_and(eqs), m); - def2 = m.mk_ite(eq, store_app->get_arg(num_args-1), def2); - app_ref sel1(m), sel2(m); - sel1 = mk_select(args1); - sel2 = mk_select(args2); - std::cout << "small domain " << sel1 << " " << sel2 << "\n"; - is_new = try_assign_eq(sel1, sel2); -#endif } ctx.internalize(def1, false); @@ -863,5 +859,6 @@ namespace smt { st.update("array def store", m_stats.m_num_default_store_axiom); st.update("array def as-array", m_stats.m_num_default_as_array_axiom); st.update("array sel as-array", m_stats.m_num_select_as_array_axiom); + st.update("array def lambda", m_stats.m_num_default_lambda_axiom); } } diff --git a/src/smt/theory_array_full.h b/src/smt/theory_array_full.h index 19b0e2f6d..09a6daaec 100644 --- a/src/smt/theory_array_full.h +++ b/src/smt/theory_array_full.h @@ -28,6 +28,7 @@ namespace smt { ptr_vector m_maps; ptr_vector m_consts; ptr_vector m_as_arrays; + ptr_vector m_lambdas; ptr_vector m_parent_maps; }; @@ -41,6 +42,7 @@ namespace smt { static unsigned const m_default_store_fingerprint = UINT_MAX - 113; static unsigned const m_default_const_fingerprint = UINT_MAX - 115; static unsigned const m_default_as_array_fingerprint = UINT_MAX - 116; + static unsigned const m_default_lambda_fingerprint = UINT_MAX - 117; protected: @@ -66,6 +68,7 @@ namespace smt { void add_map(theory_var v, enode* s); void add_parent_map(theory_var v, enode* s); void add_as_array(theory_var v, enode* arr); + void add_lambda(theory_var v, enode* lam); void add_parent_select(theory_var v, enode * s) override; void add_parent_default(theory_var v); @@ -76,6 +79,7 @@ namespace smt { bool instantiate_default_store_axiom(enode* store); bool instantiate_default_map_axiom(enode* map); bool instantiate_default_as_array_axiom(enode* arr); + bool instantiate_default_lambda_def_axiom(enode* arr); bool instantiate_parent_stores_default(theory_var v); bool has_large_domain(app* array_term); diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 28ffebb1d..682f4d6f9 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -224,8 +224,6 @@ namespace smt { }; void theory_bv::add_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx) { - if (!params().m_bv_eq_axioms) - return; m_prop_diseqs.push_back(bv_diseq(v1, v2, idx)); ctx.push_trail(push_back_vector>(m_prop_diseqs)); } @@ -533,7 +531,6 @@ namespace smt { return true; } - bool theory_bv::get_fixed_value(theory_var v, numeral & result) const { result.reset(); unsigned i = 0; @@ -997,7 +994,9 @@ namespace smt { process_args(n); expr_ref_vector arg1_bits(m), arg2_bits(m); get_arg_bits(n, 0, arg1_bits); - get_arg_bits(n, 1, arg2_bits); + get_arg_bits(n, 1, arg2_bits); + if (ctx.b_internalized(n)) + return; expr_ref le(m); if (Signed) m_bb.mk_sle(arg1_bits.size(), arg1_bits.data(), arg2_bits.data(), le); @@ -1321,6 +1320,7 @@ namespace smt { else { ctx.assign(consequent, mk_bit_eq_justification(v1, v2, consequent, antecedent)); if (params().m_bv_eq_axioms) { + literal_vector lits; lits.push_back(~consequent); lits.push_back(antecedent); @@ -1343,7 +1343,7 @@ namespace smt { ctx.mk_th_axiom(get_id(), lits.size(), lits.data()); } } - + if (m_wpos[v2] == idx) find_wpos(v2); // REMARK: bit_eq_justification is marked as a theory_bv justification. @@ -1820,6 +1820,39 @@ namespace smt { st.update("bv dynamic eqs", m_stats.m_num_eq_dynamic); } + theory_bv::var_enode_pos theory_bv::get_bv_with_theory(bool_var v, theory_id id) const { + atom* a = get_bv2a(v); + svector vec; + if (!a->is_bit()) + return var_enode_pos(nullptr, UINT32_MAX); + bit_atom * b = static_cast(a); + var_pos_occ * curr = b->m_occs; + while (curr) { + enode* n = get_enode(curr->m_var); + if (n->get_th_var(id) != null_theory_var) + return var_enode_pos(n, curr->m_idx); + curr = curr->m_next; + } + return var_enode_pos(nullptr, UINT32_MAX); + } + + bool_var theory_bv::get_first_unassigned(unsigned start_bit, enode* n) const { + theory_var v = n->get_th_var(get_family_id()); + auto& bits = m_bits[v]; + unsigned sz = bits.size(); + + for (unsigned i = start_bit; i < sz; ++i) { + if (ctx.get_assignment(bits[i].var()) != l_undef) + return bits[i].var(); + } + for (unsigned i = 0; i < start_bit; ++i) { + if (ctx.get_assignment(bits[i].var()) != l_undef) + return bits[i].var(); + } + + return null_bool_var; + } + bool theory_bv::check_assignment(theory_var v) { if (!is_root(v)) return true; diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h index ebca3fa83..d73b7a008 100644 --- a/src/smt/theory_bv.h +++ b/src/smt/theory_bv.h @@ -260,6 +260,9 @@ namespace smt { smt_params const& params() const; public: + + typedef std::pair var_enode_pos; + theory_bv(context& ctx); ~theory_bv() override; @@ -284,6 +287,9 @@ namespace smt { bool get_fixed_value(app* x, numeral & result) const; bool is_fixed_propagated(theory_var v, expr_ref& val, literal_vector& explain) override; + var_enode_pos get_bv_with_theory(bool_var v, theory_id id) const; + bool_var get_first_unassigned(unsigned start_bit, enode* n) const; + bool check_assignment(theory_var v); bool check_invariant(); bool check_zero_one_bits(theory_var v); diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp index 765513823..035b647dc 100644 --- a/src/smt/theory_datatype.cpp +++ b/src/smt/theory_datatype.cpp @@ -254,6 +254,8 @@ namespace smt { app_ref n_is_con(m.mk_app(rec, own), m); ctx.internalize(n_is_con, false); literal lits[2] = { ~is_con, literal(ctx.get_bool_var(n_is_con)) }; + ctx.mark_as_relevant(lits[0]); + ctx.mark_as_relevant(lits[1]); std::function fn = [&]() { return literal_vector(2, lits); }; scoped_trace_stream _st(*this, fn); ctx.mk_th_axiom(get_id(), 2, lits); @@ -297,7 +299,7 @@ namespace smt { TRACE("datatype", tout << "internalizing term:\n" << mk_pp(term, m) << "\n";); unsigned num_args = term->get_num_args(); for (unsigned i = 0; i < num_args; i++) - ctx.internalize(term->get_arg(i), has_quantifiers(term)); + ctx.internalize(term->get_arg(i), m.is_bool(term) && has_quantifiers(term)); // the internalization of the arguments may trigger the internalization of term. if (ctx.e_internalized(term)) return true; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 42e98e471..cf1d73892 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -65,8 +65,6 @@ class theory_lra::imp { unsigned m_idiv_lim; unsigned m_asserted_qhead; unsigned m_asserted_atoms_lim; - unsigned m_underspecified_lim; - expr* m_not_handled; }; struct delayed_atom { @@ -161,7 +159,7 @@ class theory_lra::imp { svector m_definitions; // asserted rows corresponding to definitions svector m_asserted_atoms; - expr* m_not_handled; + ptr_vector m_not_handled; ptr_vector m_underspecified; ptr_vector m_idiv_terms; vector > m_use_list; // bounds where variables are used. @@ -294,19 +292,20 @@ class theory_lra::imp { m_nla->settings().grobner_number_of_conflicts_to_report() = prms.arith_nl_grobner_cnfl_to_report(); m_nla->settings().grobner_quota() = prms.arith_nl_gr_q(); m_nla->settings().grobner_frequency() = prms.arith_nl_grobner_frequency(); - m_nla->settings().expensive_patching() = prms.arith_nl_expp(); + m_nla->settings().expensive_patching() = false; } } void found_unsupported(expr* n) { - ctx().push_trail(value_trail(m_not_handled)); - TRACE("arith", tout << "unsupported " << mk_pp(n, m) << "\n";); - m_not_handled = n; - } + ctx().push_trail(push_back_vector>(m_not_handled)); + TRACE("arith", tout << "unsupported " << mk_pp(n, m) << "\n"); + m_not_handled.push_back(n); + } void found_underspecified(expr* n) { if (a.is_underspecified(n)) { TRACE("arith", tout << "Unhandled: " << mk_pp(n, m) << "\n";); + ctx().push_trail(push_back_vector>(m_underspecified)); m_underspecified.push_back(to_app(n)); } expr* e = nullptr, *x = nullptr, *y = nullptr; @@ -857,7 +856,6 @@ public: m_zero_var(UINT_MAX), m_rone_var(UINT_MAX), m_rzero_var(UINT_MAX), - m_not_handled(nullptr), m_asserted_qhead(0), m_assume_eq_head(0), m_num_conflicts(0), @@ -1052,8 +1050,6 @@ public: sc.m_asserted_qhead = m_asserted_qhead; sc.m_idiv_lim = m_idiv_terms.size(); sc.m_asserted_atoms_lim = m_asserted_atoms.size(); - sc.m_not_handled = m_not_handled; - sc.m_underspecified_lim = m_underspecified.size(); lp().push(); if (m_nla) m_nla->push(); @@ -1070,8 +1066,6 @@ public: m_idiv_terms.shrink(m_scopes[old_size].m_idiv_lim); m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim); m_asserted_qhead = m_scopes[old_size].m_asserted_qhead; - m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim); - m_not_handled = m_scopes[old_size].m_not_handled; m_scopes.resize(old_size); lp().pop(num_scopes); // VERIFY(l_false != make_feasible()); @@ -1567,9 +1561,10 @@ public: if (assume_eqs()) { ++m_stats.m_assume_eqs; return FC_CONTINUE; - } - if (m_not_handled != nullptr) { - TRACE("arith", tout << "unhandled operator " << mk_pp(m_not_handled, m) << "\n";); + } + for (expr* e : m_not_handled) { + (void) e; // just in case TRACE() is a no-op + TRACE("arith", tout << "unhandled operator " << mk_pp(e, m) << "\n";); st = FC_GIVEUP; } return st; @@ -1689,8 +1684,11 @@ public: if (m_idiv_terms.empty()) { return true; } - bool all_divs_valid = true; - for (unsigned i = 0; i < m_idiv_terms.size(); ++i) { + bool all_divs_valid = true; + unsigned count = 0; + unsigned offset = ctx().get_random_value(); + for (unsigned j = 0; j < m_idiv_terms.size(); ++j) { + unsigned i = (offset + j) % m_idiv_terms.size(); expr* n = m_idiv_terms[i]; expr* p = nullptr, *q = nullptr; VERIFY(a.is_idiv(n, p, q)); @@ -1712,6 +1710,7 @@ public: continue; } + if (a.is_numeral(q, r2) && r2.is_pos()) { if (!a.is_bounded(n)) { TRACE("arith", tout << "unbounded " << expr_ref(n, m) << "\n";); @@ -1720,7 +1719,8 @@ public: if (!is_registered_var(v)) continue; lp::impq val_v = get_ivalue(v); - if (val_v.y.is_zero() && val_v.x == div(r1.x, r2)) continue; + if (val_v.y.is_zero() && val_v.x == div(r1.x, r2)) + continue; TRACE("arith", tout << get_value(v) << " != " << r1 << " div " << r2 << "\n";); rational div_r = div(r1.x, r2); @@ -1738,6 +1738,7 @@ public: hi = floor(hi/mul); lo = ceil(lo/mul); } + std::cout << mk_pp(p, m) << " " << mk_pp(n, m) << " " << hi << " " << lo << " " << div_r << "\n"; literal p_le_r1 = mk_literal(a.mk_le(p, a.mk_numeral(hi, true))); literal p_ge_r1 = mk_literal(a.mk_ge(p, a.mk_numeral(lo, true))); literal n_le_div = mk_literal(a.mk_le(n, a.mk_numeral(div_r, true))); @@ -1752,6 +1753,8 @@ public: } all_divs_valid = false; + ++count; + TRACE("arith", tout << r1 << " div " << r2 << "\n"; @@ -1863,9 +1866,6 @@ public: return l_undef; } lbool lia_check = l_undef; - if (!check_idiv_bounds()) { - return l_false; - } switch (m_lia->check(&m_explanation)) { case lp::lia_move::sat: lia_check = l_true; @@ -1935,6 +1935,9 @@ public: default: UNREACHABLE(); } + if (lia_check != l_false && !check_idiv_bounds()) + return l_false; + return lia_check; } @@ -2020,9 +2023,8 @@ public: Use the set to determine if a variable is shared. */ bool is_shared(theory_var v) const { - if (m_underspecified.empty()) { + if (m_underspecified.empty()) return false; - } enode * n = get_enode(v); enode * r = n->get_root(); unsigned usz = m_underspecified.size(); @@ -2031,19 +2033,15 @@ public: for (unsigned i = 0; i < usz; ++i) { app* u = m_underspecified[i]; unsigned sz = u->get_num_args(); - for (unsigned j = 0; j < sz; ++j) { - if (ctx().get_enode(u->get_arg(j))->get_root() == r) { + for (unsigned j = 0; j < sz; ++j) + if (ctx().get_enode(u->get_arg(j))->get_root() == r) return true; - } - } } } else { - for (enode * parent : r->get_const_parents()) { - if (a.is_underspecified(parent->get_expr())) { + for (enode * parent : r->get_const_parents()) + if (a.is_underspecified(parent->get_expr())) return true; - } - } } return false; } @@ -3199,7 +3197,7 @@ public: m_arith_eq_adapter.reset_eh(); m_solver = nullptr; m_internalize_head = 0; - m_not_handled = nullptr; + m_not_handled.reset(); del_bounds(0); m_unassigned_bounds.reset(); m_asserted_qhead = 0; diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 8cef18ab2..0dc52c1b2 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -1881,12 +1881,13 @@ namespace smt { inc_coeff(conseq, offset); clause& cls = *js.get_clause(); justification* cjs = cls.get_justification(); - if (cjs && !is_proof_justification(*cjs)) { - TRACE("pb", tout << "skipping justification for clause over: " << conseq << " " - << typeid(*cjs).name() << "\n";); + unsigned num_lits = cls.get_num_literals(); + if (cjs && typeid(smt::unit_resolution_justification) == typeid(*cjs)) + ; + else if (cjs && !is_proof_justification(*cjs)) { + TRACE("pb", tout << "not processing justification over: " << conseq << " " << typeid(*cjs).name() << "\n";); break; } - unsigned num_lits = cls.get_num_literals(); if (cls.get_literal(0) == conseq) { process_antecedent(cls.get_literal(1), offset); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 498cfbd1e..a433ffaef 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 5e5bd1e1d..1b0cc429d 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -17,13 +17,17 @@ Author: #include "ast/ast_pp.h" +#include "smt/theory_bv.h" #include "smt/theory_user_propagator.h" #include "smt/smt_context.h" using namespace smt; theory_user_propagator::theory_user_propagator(context& ctx): - theory(ctx, ctx.get_manager().mk_family_id(user_propagator::plugin::name())) + theory(ctx, ctx.get_manager().mk_family_id(user_propagator::plugin::name())), + m_var2expr(ctx.get_manager()), + m_push_popping(false), + m_to_add(ctx.get_manager()) {} theory_user_propagator::~theory_user_propagator() { @@ -32,15 +36,18 @@ 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, this); } } -unsigned theory_user_propagator::add_expr(expr* e) { +void theory_user_propagator::add_expr(expr* term, bool ensure_enode) { force_push(); expr_ref r(m); + expr* e = term; ctx.get_rewriter()(e, r); if (r != e) { r = m.mk_fresh_const("aux-expr", e->get_sort()); @@ -50,10 +57,16 @@ unsigned theory_user_propagator::add_expr(expr* e) { 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 n->get_th_var(get_id()); + return; + + theory_var v = mk_var(n); + m_var2expr.reserve(v + 1); + m_var2expr[v] = term; + m_expr2var.setx(term->get_id(), v, null_theory_var); + if (m.is_bool(e) && !ctx.b_internalized(e)) { bool_var bv = ctx.mk_bool_var(e); ctx.set_var_theory(bv, get_id()); @@ -65,32 +78,46 @@ unsigned theory_user_propagator::add_expr(expr* e) { literal_vector explain; if (ctx.is_fixed(n, r, explain)) m_prop.push_back(prop_info(explain, v, r)); - return v; + } void theory_user_propagator::propagate_cb( - unsigned num_fixed, unsigned const* fixed_ids, - unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, + unsigned num_fixed, expr* const* fixed_ids, + unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, 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")); - if (ctx.lit_internalized(conseq) && ctx.get_assignment(ctx.get_literal(conseq)) == l_true) + + expr_ref _conseq(conseq, m); + ctx.get_rewriter()(conseq, _conseq); + if (ctx.lit_internalized(_conseq) && ctx.get_assignment(ctx.get_literal(_conseq)) == l_true) return; - m_prop.push_back(prop_info(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, expr_ref(conseq, m))); + m_prop.push_back(prop_info(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, _conseq)); } -unsigned theory_user_propagator::register_cb(expr* e) { - return add_expr(e); +void theory_user_propagator::register_cb(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) { - auto* th = alloc(theory_user_propagator, *new_ctx); - void* ctx = m_fresh_eh(m_user_context, new_ctx->get_manager(), th->m_api_context); + auto* th = alloc(theory_user_propagator, *new_ctx); + void* ctx; + try { + ctx = m_fresh_eh(m_user_context, new_ctx->get_manager(), th->m_api_context); + } + catch (...) { + throw default_exception("Exception thrown in \"fresh\"-callback"); + } th->add(ctx, m_push_eh, m_pop_eh, m_fresh_eh); if ((bool)m_fixed_eh) th->register_fixed(m_fixed_eh); if ((bool)m_final_eh) th->register_final(m_final_eh); if ((bool)m_eq_eh) th->register_eq(m_eq_eh); if ((bool)m_diseq_eh) th->register_diseq(m_diseq_eh); + if ((bool)m_created_eh) th->register_created(m_created_eh); + if ((bool)m_decide_eh) th->register_decide(m_decide_eh); return th; } @@ -98,10 +125,17 @@ final_check_status theory_user_propagator::final_check_eh() { if (!(bool)m_final_eh) return FC_DONE; force_push(); - unsigned sz = m_prop.size(); - m_final_eh(m_user_context, this); + unsigned sz1 = m_prop.size(); + unsigned sz2 = m_expr2var.size(); + try { + m_final_eh(m_user_context, this); + } + catch (...) { + throw default_exception("Exception thrown in \"final\"-callback"); + } propagate(); - bool done = (sz == m_prop.size()) && !ctx.inconsistent(); + // check if it became inconsistent or something new was propagated/registered + bool done = (sz1 == m_prop.size()) && (sz2 == m_expr2var.size()) && !ctx.inconsistent(); return done ? FC_DONE : FC_CONTINUE; } @@ -114,40 +148,116 @@ void theory_user_propagator::new_fixed_eh(theory_var v, expr* value, unsigned nu m_fixed.insert(v); ctx.push_trail(insert_map(m_fixed, v)); m_id2justification.setx(v, literal_vector(num_lits, jlits), literal_vector()); - m_fixed_eh(m_user_context, this, v, value); + try { + m_fixed_eh(m_user_context, this, var2expr(v), value); + } + catch (...) { + throw default_exception("Exception thrown in \"fixed\"-callback"); + } } -void theory_user_propagator::push_scope_eh() { +void theory_user_propagator::decide(bool_var& var, bool& is_pos) { + + const bool_var_data& d = ctx.get_bdata(var); + + if (!d.is_theory_atom()) + return; + + theory* th = ctx.get_theory(d.get_theory()); + + bv_util bv(m); + enode* original_enode = nullptr; + unsigned original_bit = 0; + + if (d.is_enode() && th->get_family_id() == get_family_id()) { + // variable is just a registered expression + original_enode = ctx.bool_var2enode(var); + } + else if (th->get_family_id() == bv.get_fid()) { + // it might be a registered bit-vector + auto registered_bv = ((theory_bv*)th)->get_bv_with_theory(var, get_family_id()); + if (!registered_bv.first) + // there is no registered bv associated with the bit + return; + original_enode = registered_bv.first; + original_bit = registered_bv.second; + } + else + return; + + // call the registered callback + unsigned new_bit = original_bit; + lbool phase = is_pos ? l_true : l_false; + + expr* e = var2expr(original_enode->get_th_var(get_family_id())); + m_decide_eh(m_user_context, this, &e, &new_bit, &phase); + enode* new_enode = ctx.get_enode(e); + + // check if the callback changed something + if (original_enode == new_enode && (new_enode->is_bool() || original_bit == new_bit)) { + if (phase != l_undef) + // it only affected the truth value + is_pos = phase == l_true; + return; + } + + bool_var old_var = var; + if (new_enode->is_bool()) { + // expression was set to a boolean + bool_var new_var = ctx.enode2bool_var(new_enode); + if (ctx.get_assignment(new_var) == l_undef) { + var = new_var; + } + } + else { + // expression was set to a bit-vector + auto th_bv = (theory_bv*)ctx.get_theory(bv.get_fid()); + bool_var new_var = th_bv->get_first_unassigned(new_bit, new_enode); + + if (new_var != null_bool_var) { + var = new_var; + } + } + + // in case the callback did not decide on a truth value -> let Z3 decide + is_pos = ctx.guess(var, phase); +} + +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, this, num_scopes); } bool theory_user_propagator::can_propagate() { - return m_qhead < m_prop.size(); + return m_qhead < m_prop.size() || m_to_add_qhead < m_to_add.size(); } void theory_user_propagator::propagate_consequence(prop_info const& prop) { justification* js; m_lits.reset(); m_eqs.reset(); - for (unsigned id : prop.m_ids) - m_lits.append(m_id2justification[id]); + for (expr* id : prop.m_ids) + m_lits.append(m_id2justification[expr2var(id)]); for (auto const& p : prop.m_eqs) - m_eqs.push_back(enode_pair(get_enode(p.first), get_enode(p.second))); + m_eqs.push_back(enode_pair(get_enode(expr2var(p.first)), get_enode(expr2var(p.second)))); DEBUG_CODE(for (auto const& p : m_eqs) VERIFY(p.first->get_root() == p.second->get_root());); - DEBUG_CODE(for (unsigned id : prop.m_ids) VERIFY(m_fixed.contains(id));); + DEBUG_CODE(for (expr* e : prop.m_ids) VERIFY(m_fixed.contains(expr2var(e)));); DEBUG_CODE(for (literal lit : m_lits) VERIFY(ctx.get_assignment(lit) == l_true);); TRACE("user_propagate", tout << "propagating #" << prop.m_conseq->get_id() << ": " << prop.m_conseq << "\n"); @@ -188,10 +298,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) @@ -216,12 +335,18 @@ 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); - unsigned v = add_expr(term); - - if (!m_created_eh && (m_fixed_eh || m_eq_eh || m_diseq_eh)) + 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"); - if (m_created_eh) - m_created_eh(m_user_context, this, term, v); + + try { + m_created_eh(m_user_context, this, term); + } + catch (...) { + throw default_exception("Exception thrown in \"created\"-callback"); + } + return true; } diff --git a/src/smt/theory_user_propagator.h b/src/smt/theory_user_propagator.h index f1e558256..bf82883e4 100644 --- a/src/smt/theory_user_propagator.h +++ b/src/smt/theory_user_propagator.h @@ -30,13 +30,13 @@ namespace smt { class theory_user_propagator : public theory, public user_propagator::callback { struct prop_info { - unsigned_vector m_ids; + ptr_vector m_ids; expr_ref m_conseq; - svector> m_eqs; + svector> m_eqs; literal_vector m_lits; - theory_var m_var = null_theory_var; - prop_info(unsigned num_fixed, unsigned const* fixed_ids, - unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, expr_ref const& c): + theory_var m_var = null_theory_var; + prop_info(unsigned num_fixed, expr* const* fixed_ids, + unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, expr_ref const& c): m_ids(num_fixed, fixed_ids), m_conseq(c) { for (unsigned i = 0; i < num_eqs; ++i) @@ -56,7 +56,7 @@ namespace smt { void reset() { memset(this, 0, sizeof(*this)); } }; - void* m_user_context = nullptr; + void* m_user_context = nullptr; user_propagator::push_eh_t m_push_eh; user_propagator::pop_eh_t m_pop_eh; user_propagator::fresh_eh_t m_fresh_eh; @@ -64,7 +64,8 @@ namespace smt { user_propagator::fixed_eh_t m_fixed_eh; user_propagator::eq_eh_t m_eq_eh; user_propagator::eq_eh_t m_diseq_eh; - user_propagator::created_eh_t m_created_eh; + user_propagator::created_eh_t m_created_eh; + user_propagator::decide_eh_t m_decide_eh; user_propagator::context_obj* m_api_context = nullptr; unsigned m_qhead = 0; @@ -76,6 +77,19 @@ namespace smt { literal_vector m_lits; enode_pair_vector m_eqs; 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()]; } + void check_defined(expr* e) { + if (e->get_id() >= m_expr2var.size() || get_num_vars() <= m_expr2var[e->get_id()]) + throw default_exception("expression is not registered"); + } void force_push(); @@ -101,26 +115,29 @@ namespace smt { m_fresh_eh = fresh_eh; } - unsigned 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; } void register_eq(user_propagator::eq_eh_t& eq_eh) { m_eq_eh = eq_eh; } void register_diseq(user_propagator::eq_eh_t& diseq_eh) { m_diseq_eh = diseq_eh; } void register_created(user_propagator::created_eh_t& created_eh) { m_created_eh = created_eh; } + void register_decide(user_propagator::decide_eh_t& decide_eh) { m_decide_eh = decide_eh; } bool has_fixed() const { return (bool)m_fixed_eh; } - - void propagate_cb(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* lhs, unsigned const* rhs, expr* conseq) override; - unsigned register_cb(expr* e) override; + bool has_decide() const { return (bool)m_decide_eh; } + + void propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* lhs, expr* const* rhs, expr* conseq) override; + void register_cb(expr* e) override; void new_fixed_eh(theory_var v, expr* value, unsigned num_lits, literal const* jlits); + void decide(bool_var& var, bool& is_pos); theory * mk_fresh(context * new_ctx) override; bool internalize_atom(app* atom, bool gate_ctx) override; bool internalize_term(app* term) override; - void new_eq_eh(theory_var v1, theory_var v2) override { if (m_eq_eh) m_eq_eh(m_user_context, this, v1, v2); } - void new_diseq_eh(theory_var v1, theory_var v2) override { if (m_diseq_eh) m_diseq_eh(m_user_context, this, v1, v2); } + void new_eq_eh(theory_var v1, theory_var v2) override { if (m_eq_eh) m_eq_eh(m_user_context, this, var2expr(v1), var2expr(v2)); } + void new_diseq_eh(theory_var v1, theory_var v2) override { if (m_diseq_eh) m_diseq_eh(m_user_context, this, var2expr(v1), var2expr(v2)); } bool use_diseqs() const override { return ((bool)m_diseq_eh); } bool build_models() const override { return false; } final_check_status final_check_eh() override; @@ -133,7 +150,7 @@ namespace smt { void collect_statistics(::statistics & st) const override; model_value_proc * mk_value(enode * n, model_generator & mg) override { return nullptr; } void init_model(model_generator & m) override {} - bool include_func_interp(func_decl* f) override { return true; } + bool include_func_interp(func_decl* f) override { return false; } bool can_propagate() override; void propagate() override; void display(std::ostream& out) const override {} diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index b95d2d7f8..bfe495b6e 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -311,11 +311,11 @@ public: m_solver2->get_levels(vars, depth); } - expr_ref_vector get_trail() override { + expr_ref_vector get_trail(unsigned max_level) override { if (m_use_solver1_results) - return m_solver1->get_trail(); + return m_solver1->get_trail(max_level); else - return m_solver2->get_trail(); + return m_solver2->get_trail(max_level); } proof * get_proof() override { diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 7dcca2ecc..d14648058 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -242,6 +242,7 @@ expr_ref_vector solver::get_units() { ast_manager& m = get_manager(); expr_ref_vector fmls(m), result(m), tmp(m); get_assertions(fmls); + get_units_core(fmls); obj_map units; for (expr* f : fmls) { if (m.is_not(f, f) && is_literal(m, f)) { diff --git a/src/solver/solver.h b/src/solver/solver.h index 090cd9d9b..dde4ccbe0 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -261,9 +261,11 @@ public: */ expr_ref_vector get_units(); + virtual void get_units_core(expr_ref_vector& units) {} + expr_ref_vector get_non_units(); - virtual expr_ref_vector get_trail() = 0; // { return expr_ref_vector(get_manager()); } + virtual expr_ref_vector get_trail(unsigned max_level) = 0; // { return expr_ref_vector(get_manager()); } virtual void get_levels(ptr_vector const& vars, unsigned_vector& depth) = 0; diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 9e897208f..bbc46c9c8 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -127,8 +127,8 @@ public: m_base->get_levels(vars, depth); } - expr_ref_vector get_trail() override { - return m_base->get_trail(); + expr_ref_vector get_trail(unsigned max_level) override { + return m_base->get_trail(max_level); } lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 6ed570297..fe89d6533 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -108,14 +108,18 @@ public: m_tactic->user_propagate_register_diseq(diseq_eh); } - unsigned user_propagate_register_expr(expr* e) override { - return m_tactic->user_propagate_register_expr(e); + void user_propagate_register_expr(expr* e) override { + m_tactic->user_propagate_register_expr(e); } void user_propagate_register_created(user_propagator::created_eh_t& created_eh) override { m_tactic->user_propagate_register_created(created_eh); } + void user_propagate_register_decide(user_propagator::decide_eh_t& created_eh) override { + m_tactic->user_propagate_register_decide(created_eh); + } + void user_propagate_clear() override { if (m_tactic) m_tactic->user_propagate_clear(); @@ -134,7 +138,7 @@ public: throw default_exception("cannot retrieve depth from solvers created using tactics"); } - expr_ref_vector get_trail() override { + expr_ref_vector get_trail(unsigned max_level) override { throw default_exception("cannot retrieve trail from solvers created using tactics"); } }; 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/lia2card_tactic.cpp b/src/tactic/arith/lia2card_tactic.cpp index 5f6edf6ea..97c6f466f 100644 --- a/src/tactic/arith/lia2card_tactic.cpp +++ b/src/tactic/arith/lia2card_tactic.cpp @@ -216,11 +216,10 @@ public: } // IF_VERBOSE(0, verbose_stream() << mk_pp(g->form(i), m) << "\n--->\n" << new_curr << "\n";); g->update(i, new_curr, new_pr, g->dep(i)); - } - for (expr* a : axioms) { + for (expr* a : axioms) g->assert_expr(a); - } + if (m_mc) g->add(m_mc.get()); g->inc_depth(); result.push_back(g.get()); 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/bv/bit_blaster_model_converter.cpp b/src/tactic/bv/bit_blaster_model_converter.cpp index c389e6df4..fe3294e4c 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -21,6 +21,7 @@ Notes: #include "tactic/model_converter.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_smt2_pp.h" +#include "ast/ast_pp.h" #include "ast/ast_util.h" /** @@ -40,9 +41,7 @@ struct bit_blaster_model_converter : public model_converter { obj_map const & const2bits, ptr_vector const& newbits): m_vars(m), m_bits(m), m_newbits(m) { - for (auto const& kv : const2bits) { - func_decl * v = kv.m_key; - expr * bits = kv.m_value; + for (auto const& [v, bits] : const2bits) { SASSERT(!TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_MKBV)); SASSERT(TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_CONCAT)); m_vars.push_back(v); @@ -70,7 +69,7 @@ struct bit_blaster_model_converter : public model_converter { bits.insert(to_app(bit)->get_decl()); } } - TRACE("blaster_mc", + TRACE("model_converter", tout << "bits that should not be included in the model:\n"; for (func_decl* f : bits) { tout << f->get_name() << " "; @@ -85,14 +84,14 @@ struct bit_blaster_model_converter : public model_converter { func_decl * f = old_model->get_constant(i); if (bits.contains(f)) continue; - TRACE("blaster_mc", tout << "non-bit: " << f->get_name() << "\n";); + TRACE("model_converter", tout << "non-bit: " << f->get_name() << "\n";); expr * fi = old_model->get_const_interp(f); new_model->register_decl(f, fi); } - TRACE("blaster_mc", tout << "after copy non bits:\n"; model_pp(tout, *new_model);); + TRACE("model_converter", tout << "after copy non bits:\n"; model_pp(tout, *new_model);); new_model->copy_func_interps(*old_model); new_model->copy_usort_interps(*old_model); - TRACE("blaster_mc", tout << "after copying functions and sorts:\n"; model_pp(tout, *new_model);); + TRACE("model_converter", tout << "after copying functions and sorts:\n"; model_pp(tout, *new_model);); } void mk_bvs(model * old_model, model * new_model) { @@ -121,7 +120,9 @@ struct bit_blaster_model_converter : public model_converter { SASSERT(is_uninterp_const(bit)); func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); - if (bit_val != nullptr && m().is_true(bit_val)) + if (bit_val && !m().is_true(bit_val) && !m().is_false(bit_val)) + goto bail; + if (bit_val && m().is_true(bit_val)) val++; } } @@ -136,11 +137,27 @@ struct bit_blaster_model_converter : public model_converter { func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); // remark: if old_model does not assign bit_val, then assume it is false. - if (bit_val != nullptr && !util.is_zero(bit_val)) + if (bit_val && !util.is_one(bit_val) && !util.is_zero(bit_val)) + goto bail; + if (bit_val && util.is_one(bit_val)) val++; } } - new_val = util.mk_numeral(val, bv_sz); + new_val = util.mk_numeral(val, bv_sz); + new_model->register_decl(m_vars.get(i), new_val); + continue; + bail: + expr_ref_vector vals(m()); + for (expr* bit : *to_app(bs)) { + func_decl * bit_decl = to_app(bit)->get_decl(); + expr * bit_val = old_model->get_const_interp(bit_decl); + SASSERT(bit_val); + vals.push_back(bit_val); + } + if (TO_BOOL) + new_val = util.mk_bv(vals.size(), vals.data()); + else + new_val = util.mk_concat(vals); new_model->register_decl(m_vars.get(i), new_val); } } diff --git a/src/tactic/bv/bv1_blaster_tactic.cpp b/src/tactic/bv/bv1_blaster_tactic.cpp index 0fc38f2db..d3f19d1bd 100644 --- a/src/tactic/bv/bv1_blaster_tactic.cpp +++ b/src/tactic/bv/bv1_blaster_tactic.cpp @@ -37,7 +37,7 @@ class bv1_blaster_tactic : public tactic { bv_util m_util; obj_map m_const2bits; ptr_vector m_newbits; - expr_ref_vector m_saved; + ast_ref_vector m_saved; expr_ref m_bit1; expr_ref m_bit0; @@ -108,9 +108,11 @@ class bv1_blaster_tactic : public tactic { for (unsigned i = 0; i < bv_size; i++) { bits.push_back(m().mk_fresh_const(nullptr, b)); m_newbits.push_back(to_app(bits.back())->get_decl()); + m_saved.push_back(m_newbits.back()); } r = butil().mk_concat(bits.size(), bits.data()); m_saved.push_back(r); + m_saved.push_back(f); m_const2bits.insert(f, r); result = r; } diff --git a/src/tactic/core/collect_statistics_tactic.cpp b/src/tactic/core/collect_statistics_tactic.cpp index 5c7ec827c..dc906cb32 100644 --- a/src/tactic/core/collect_statistics_tactic.cpp +++ b/src/tactic/core/collect_statistics_tactic.cpp @@ -110,7 +110,6 @@ protected: void operator()(quantifier * q) { m_stats["quantifiers"]++; SASSERT(is_app(q->get_expr())); - app * body = to_app(q->get_expr()); switch (q->get_kind()) { case forall_k: m_stats["forall-variables"] += q->get_num_decls(); diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 26a69fd4a..c97fa670e 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -892,9 +892,8 @@ public: m_num_elim_apps = 0; } - unsigned user_propagate_register_expr(expr* e) override { + void user_propagate_register_expr(expr* e) override { m_nonvars.insert(e); - return 0; } void user_propagate_clear() override { diff --git a/src/tactic/core/pb_preprocess_tactic.cpp b/src/tactic/core/pb_preprocess_tactic.cpp index 1d40a91c7..0313899ac 100644 --- a/src/tactic/core/pb_preprocess_tactic.cpp +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -119,42 +119,38 @@ public: } generic_model_converter* pp = alloc(generic_model_converter, m, "pb-preprocess"); - g->add(pp); g->inc_depth(); result.push_back(g.get()); while (simplify(g, *pp)); + g->add(pp); + // decompose(g); } bool simplify(goal_ref const& g, generic_model_converter& mc) { reset(); normalize(g); - if (g->inconsistent()) { + if (g->inconsistent()) return false; - } - for (unsigned i = 0; i < g->size(); ++i) { - process_vars(i, g); - } - - if (m_ge.empty()) { + + for (unsigned i = 0; i < g->size(); ++i) + process_vars(i, g); + + if (m_ge.empty()) return false; - } - for (unsigned i = 0; i < m_ge.size(); ++i) { + for (unsigned i = 0; i < m_ge.size(); ++i) if (!classify_vars(i, to_app(g->form(m_ge[i])))) return false; - } declassifier dcl(m_vars); expr_mark visited; - for (unsigned i = 0; !m_vars.empty() && i < m_other.size(); ++i) { + for (unsigned i = 0; !m_vars.empty() && i < m_other.size(); ++i) for_each_expr(dcl, visited, g->form(m_other[i])); - } - if (m_vars.empty()) { + if (m_vars.empty()) return false; - } // display_annotation(tout, g); m_progress = false; @@ -172,24 +168,23 @@ public: replace(r.pos, e, m.mk_true(), g); set_value(mc, e, true); } - if (g->inconsistent()) return false; + if (g->inconsistent()) + return false; ++it; it = next_resolvent(it); } // now resolve clauses. it = next_resolvent(m_vars.begin()); - while (it != m_vars.end()) { - + while (it != m_vars.end()) { app * e = it->m_key; SASSERT(is_uninterp_const(e)); rec const& r = it->m_value; - if (r.pos.size() == 1 && !r.neg.empty()) { + if (r.pos.size() == 1 && !r.neg.empty()) resolve(mc, r.pos[0], r.neg, e, true, g); - } - else if (r.neg.size() == 1 && !r.pos.empty()) { + else if (r.neg.size() == 1 && !r.pos.empty()) resolve(mc, r.neg[0], r.pos, e, false, g); - } - if (g->inconsistent()) return false; + if (g->inconsistent()) + return false; ++it; it = next_resolvent(it); } @@ -201,20 +196,29 @@ public: vector coeffs1, coeffs2; rational k1, k2; expr* fml = g->form(m_ge[i]); - if (!to_ge(fml, args1, coeffs1, k1)) continue; - if (args1.empty()) continue; + if (!to_ge(fml, args1, coeffs1, k1)) + continue; + if (args1.empty()) + continue; expr* arg = args1.get(0); bool neg = m.is_not(arg, arg); - if (!is_uninterp_const(arg)) continue; - if (!m_vars.contains(to_app(arg))) continue; + if (!is_uninterp_const(arg)) + continue; + if (!m_vars.contains(to_app(arg))) + continue; rec const& r = m_vars.find(to_app(arg)); unsigned_vector const& pos = neg?r.neg:r.pos; for (unsigned j = 0; j < pos.size(); ++j) { unsigned k = pos[j]; - if (k == m_ge[i]) continue; - if (!to_ge(g->form(k), args2, coeffs2, k2)) continue; + if (k == m_ge[i]) + continue; + coeffs2.reset(); + args2.reset(); + if (!to_ge(g->form(k), args2, coeffs2, k2)) + continue; if (subsumes(args1, coeffs1, k1, args2, coeffs2, k2)) { - IF_VERBOSE(3, verbose_stream() << "replace " << mk_pp(g->form(k), m) << "\n";); + IF_VERBOSE(3, verbose_stream() << "subsumes " << mk_pp(fml, m) << "\n"); + IF_VERBOSE(3, verbose_stream() << "replace " << mk_pp(g->form(k), m) << "\n"); g->update(k, m.mk_true(), nullptr, m.mk_join(g->dep(m_ge[i]), g->dep(k))); m_progress = true; } @@ -417,12 +421,10 @@ private: m_trail.push_back(e); m_vars.insert(e, rec()); } - if (pos) { + if (pos) m_vars.find(e).pos.push_back(i); - } - else { + else m_vars.find(e).neg.push_back(i); - } } bool pure_args(app* a) const { @@ -631,16 +633,19 @@ private: vector const& coeffs1, rational const& k1, expr_ref_vector const& args2, vector const& coeffs2, rational const& k2) { - if (k2 > k1) return false; + if (k2 > k1) + return false; for (unsigned i = 0; i < args1.size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < args2.size(); ++j) { if (args1[i] == args2[j]) { - if (coeffs1[i] > coeffs2[j]) return false; + if (coeffs1[i] > coeffs2[j]) + return false; found = true; } } - if (!found) return false; + if (!found) + return false; } return true; } diff --git a/src/tactic/core/reduce_args_tactic.cpp b/src/tactic/core/reduce_args_tactic.cpp index 607928f64..7f0d82f2e 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -78,7 +78,7 @@ public: void operator()(goal_ref const & g, goal_ref_buffer & result) override; void cleanup() override; - unsigned user_propagate_register_expr(expr* e) override; + void user_propagate_register_expr(expr* e) override; void user_propagate_clear() override; }; @@ -502,9 +502,8 @@ void reduce_args_tactic::cleanup() { m_imp->m_vars.append(vars); } -unsigned reduce_args_tactic::user_propagate_register_expr(expr* e) { +void reduce_args_tactic::user_propagate_register_expr(expr* e) { m_imp->m_vars.push_back(e); - return 0; } void reduce_args_tactic::user_propagate_clear() { diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index a73fe88b6..30e4a8c4b 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -65,7 +65,8 @@ class solve_eqs_tactic : public tactic { m_a_util(m), m_num_steps(0), m_num_eliminated_vars(0), - m_marked_candidates(m) { + m_marked_candidates(m), + m_var_trail(m) { updt_params(p); if (m_r == nullptr) m_r = mk_default_expr_replacer(m, true); @@ -524,7 +525,14 @@ class solve_eqs_tactic : public tactic { } } + expr_mark m_compatible_tried; + expr_ref_vector m_var_trail; + bool is_compatible(goal const& g, unsigned idx, vector const & path, expr* v, expr* eq) { + if (m_compatible_tried.is_marked(v)) + return false; + m_compatible_tried.mark(v); + m_var_trail.push_back(v); expr_mark occ; svector cache; mark_occurs(occ, g, v); @@ -649,7 +657,7 @@ class solve_eqs_tactic : public tactic { for (unsigned i = 0; i < args.size(); ++i) { pr = nullptr; expr* arg = args.get(i), *lhs = nullptr, *rhs = nullptr; - if (m().is_eq(arg, lhs, rhs)) { + if (m().is_eq(arg, lhs, rhs) && !m().is_bool(lhs)) { if (trivial_solve1(lhs, rhs, var, def, pr) && is_compatible(g, idx, path, var, arg)) { insert_solution(g, idx, arg, var, def, pr); } diff --git a/src/tactic/fd_solver/bounded_int2bv_solver.cpp b/src/tactic/fd_solver/bounded_int2bv_solver.cpp index e907abc72..bc05a3328 100644 --- a/src/tactic/fd_solver/bounded_int2bv_solver.cpp +++ b/src/tactic/fd_solver/bounded_int2bv_solver.cpp @@ -165,8 +165,8 @@ public: void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { m_solver->get_levels(vars, depth); } - expr_ref_vector get_trail() override { - return m_solver->get_trail(); + expr_ref_vector get_trail(unsigned max_level) override { + return m_solver->get_trail(max_level); } model_converter* external_model_converter() const { diff --git a/src/tactic/fd_solver/enum2bv_solver.cpp b/src/tactic/fd_solver/enum2bv_solver.cpp index 80e265676..cb136ad9f 100644 --- a/src/tactic/fd_solver/enum2bv_solver.cpp +++ b/src/tactic/fd_solver/enum2bv_solver.cpp @@ -189,8 +189,8 @@ public: m_solver->get_levels(vars, depth); } - expr_ref_vector get_trail() override { - return m_solver->get_trail(); + expr_ref_vector get_trail(unsigned max_level) override { + return m_solver->get_trail(max_level); } unsigned get_num_assertions() const override { diff --git a/src/tactic/fd_solver/pb2bv_solver.cpp b/src/tactic/fd_solver/pb2bv_solver.cpp index f5d493af6..cd19b0dca 100644 --- a/src/tactic/fd_solver/pb2bv_solver.cpp +++ b/src/tactic/fd_solver/pb2bv_solver.cpp @@ -105,8 +105,8 @@ public: m_solver->get_levels(vars, depth); } - expr_ref_vector get_trail() override { - return m_solver->get_trail(); + expr_ref_vector get_trail(unsigned max_level) override { + return m_solver->get_trail(max_level); } model_converter* external_model_converter() const{ diff --git a/src/tactic/fd_solver/smtfd_solver.cpp b/src/tactic/fd_solver/smtfd_solver.cpp index c5d67506e..32f9df9af 100644 --- a/src/tactic/fd_solver/smtfd_solver.cpp +++ b/src/tactic/fd_solver/smtfd_solver.cpp @@ -2098,9 +2098,9 @@ namespace smtfd { m_fd_sat_solver->get_levels(vars, depth); } - expr_ref_vector get_trail() override { + expr_ref_vector get_trail(unsigned max_level) override { init(); - return m_fd_sat_solver->get_trail(); + return m_fd_sat_solver->get_trail(max_level); } unsigned get_num_assertions() const override { diff --git a/src/tactic/horn_subsume_model_converter.cpp b/src/tactic/horn_subsume_model_converter.cpp index 79b9a038f..979359a46 100644 --- a/src/tactic/horn_subsume_model_converter.cpp +++ b/src/tactic/horn_subsume_model_converter.cpp @@ -80,8 +80,8 @@ bool horn_subsume_model_converter::mk_horn( if (w >= subst.size()) { subst.resize(w+1); } - if (subst[w].get()) { - conjs.push_back(m.mk_eq(v, subst[w].get())); + if (subst.get(w)) { + conjs.push_back(m.mk_eq(v, subst.get(w))); } else { subst[w] = v; @@ -92,11 +92,11 @@ bool horn_subsume_model_converter::mk_horn( } } expr_ref body_expr(m); - body_expr = m.mk_and(conjs.size(), conjs.data()); + body_expr = m.mk_and(conjs); // substitute variables directly. if (!subst.empty()) { - body_expr = vs(body_expr, subst.size(), subst.data()); + body_expr = vs(body_expr, subst); } if (fv.empty()) { @@ -174,17 +174,16 @@ void horn_subsume_model_converter::operator()(model_ref& mr) { func_decl_ref pred(m); expr_ref body_res(m); for (unsigned i = 0; i < m_delay_head.size(); ++i) { - VERIFY(mk_horn(m_delay_head[i].get(), m_delay_body[i].get(), pred, body_res)); + VERIFY(mk_horn(m_delay_head.get(i), m_delay_body.get(i), pred, body_res)); insert(pred.get(), body_res.get()); } m_delay_head.reset(); m_delay_body.reset(); TRACE("mc", tout << m_funcs.size() << "\n"; model_smt2_pp(tout, m, *mr, 0);); - for (unsigned i = m_funcs.size(); i > 0; ) { - --i; - func_decl* h = m_funcs[i].get(); - expr_ref body(m_bodies[i].get(), m); + for (unsigned i = m_funcs.size(); i-- > 0; ) { + func_decl* h = m_funcs.get(i); + expr_ref body(m_bodies.get(i), m); unsigned arity = h->get_arity(); add_default_false_interpretation(body, mr); SASSERT(m.is_bool(body)); diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 7dc244195..073262edd 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -143,10 +143,9 @@ public: tactic_ref t; if (tp.default_tactic() != symbol::null && !tp.default_tactic().is_numerical() && - tp.default_tactic().bare_str() && - tp.default_tactic().bare_str()[0]) { + tp.default_tactic().str()[0]) { cmd_context ctx(false, &m, l); - std::istringstream is(tp.default_tactic().bare_str()); + std::istringstream is(tp.default_tactic().str()); char const* file_name = ""; sexpr_ref se = parse_sexpr(ctx, is, p, file_name); if (se) { diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 575435904..cc0ab8f5e 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -52,6 +52,7 @@ struct tactic_report::imp { << " :before-memory " << std::fixed << std::setprecision(2) << m_start_memory << " :after-memory " << std::fixed << std::setprecision(2) << end_memory << ")" << std::endl); + IF_VERBOSE(20, m_goal.display(verbose_stream() << m_id << "\n")); SASSERT(m_goal.is_well_formed()); } }; diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index 437c7f804..af8b24b2f 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -85,7 +85,7 @@ public: throw default_exception("tactic does not support user propagation"); } - unsigned user_propagate_register_expr(expr* e) override { return 0; } + void user_propagate_register_expr(expr* e) override { } virtual char const* name() const = 0; protected: diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index c5a9e6984..67a0e3062 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -190,9 +190,9 @@ public: m_t2->user_propagate_register_diseq(diseq_eh); } - unsigned user_propagate_register_expr(expr* e) override { + void user_propagate_register_expr(expr* e) override { m_t1->user_propagate_register_expr(e); - return m_t2->user_propagate_register_expr(e); + m_t2->user_propagate_register_expr(e); } void user_propagate_clear() override { @@ -204,6 +204,10 @@ public: m_t2->user_propagate_register_created(created_eh); } + void user_propagate_register_decide(user_propagator::decide_eh_t& decide_eh) override { + m_t2->user_propagate_register_decide(decide_eh); + } + }; tactic * and_then(tactic * t1, tactic * t2) { @@ -336,6 +340,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; @@ -848,7 +855,7 @@ public: void reset() override { m_t->reset(); } void set_logic(symbol const& l) override { m_t->set_logic(l); } void set_progress_callback(progress_callback * callback) override { m_t->set_progress_callback(callback); } - unsigned user_propagate_register_expr(expr* e) override { return m_t->user_propagate_register_expr(e); } + void user_propagate_register_expr(expr* e) override { m_t->user_propagate_register_expr(e); } void user_propagate_clear() override { m_t->user_propagate_clear(); } protected: @@ -1019,7 +1026,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); } diff --git a/src/tactic/user_propagator_base.h b/src/tactic/user_propagator_base.h index 403df8af5..3f4af0329 100644 --- a/src/tactic/user_propagator_base.h +++ b/src/tactic/user_propagator_base.h @@ -2,14 +2,15 @@ #pragma once #include "ast/ast.h" +#include "util/lbool.h" namespace user_propagator { class callback { public: virtual ~callback() = default; - virtual void propagate_cb(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, expr* conseq) = 0; - virtual unsigned register_cb(expr* e) = 0; + virtual void propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, expr* conseq) = 0; + virtual void register_cb(expr* e) = 0; }; class context_obj { @@ -17,14 +18,14 @@ namespace user_propagator { virtual ~context_obj() = default; }; - typedef std::function final_eh_t; - typedef std::function fixed_eh_t; - typedef std::function eq_eh_t; - typedef std::function fresh_eh_t; - typedef std::function push_eh_t; - typedef std::function pop_eh_t; - typedef std::function created_eh_t; - + typedef std::function final_eh_t; + typedef std::function fixed_eh_t; + typedef std::function eq_eh_t; + typedef std::function fresh_eh_t; + typedef std::function push_eh_t; + typedef std::function pop_eh_t; + typedef std::function created_eh_t; + typedef std::function decide_eh_t; class plugin : public decl_plugin { public: @@ -77,7 +78,7 @@ namespace user_propagator { throw default_exception("user-propagators are only supported on the SMT solver"); } - virtual unsigned user_propagate_register_expr(expr* e) { + virtual void user_propagate_register_expr(expr* e) { throw default_exception("user-propagators are only supported on the SMT solver"); } @@ -85,6 +86,10 @@ namespace user_propagator { throw default_exception("user-propagators are only supported on the SMT solver"); } + virtual void user_propagate_register_decide(decide_eh_t& r) { + throw default_exception("user-propagators are only supported on the SMT solver"); + } + virtual void user_propagate_clear() { } diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 0c679e388..28ce5a867 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -86,13 +86,13 @@ static char const * get_new_param_name(std::string const & p) { template class smap : public map {}; -typedef std::function lazy_descrs_t; +typedef param_descrs* (*lazy_descrs_t)(void); class lazy_param_descrs { param_descrs* m_descrs; - ptr_vector m_mk; + svector m_mk; - void apply(lazy_descrs_t& f) { + void apply(lazy_descrs_t f) { param_descrs* d = f(); if (m_descrs) { m_descrs->copy(*d); @@ -104,18 +104,16 @@ class lazy_param_descrs { } void reset_mk() { - for (auto* f : m_mk) dealloc(f); m_mk.reset(); } public: - lazy_param_descrs(lazy_descrs_t& f): m_descrs(nullptr) { + lazy_param_descrs(lazy_descrs_t f): m_descrs(nullptr) { append(f); } ~lazy_param_descrs() { - dealloc(m_descrs); - reset_mk(); + dealloc(m_descrs); } param_descrs* deref() { @@ -124,8 +122,8 @@ public: return m_descrs; } - void append(lazy_descrs_t& f) { - m_mk.push_back(alloc(lazy_descrs_t, f)); + void append(lazy_descrs_t f) { + m_mk.push_back(f); } }; @@ -204,7 +202,7 @@ public: m_param_descrs.copy(d); } - void register_module(char const * module_name, lazy_descrs_t& f) { + void register_module(char const * module_name, lazy_descrs_t f) { // Don't need synchronization here, this method // is invoked from check_registered that is already protected. @@ -278,20 +276,20 @@ public: strm << "the parameter '" << param_name << "', invoke 'z3 -p' to obtain the new parameter list, and 'z3 -pp:" << new_name << "' for the full description of the parameter"; - throw exception(strm.str()); + throw exception(std::move(strm).str()); } else if (is_old_param_name(param_name)) { std::stringstream strm; strm << "unknown parameter '" << param_name << "', this is an old parameter name, invoke 'z3 -p' to obtain the new parameter list"; - throw default_exception(strm.str()); + throw default_exception(std::move(strm).str()); } else { std::stringstream strm; strm << "unknown parameter '" << param_name << "'\n"; strm << "Legal parameters are:\n"; d.display(strm, 2, false, false); - throw default_exception(strm.str()); + throw default_exception(std::move(strm).str()); } } else { @@ -300,7 +298,7 @@ public: strm << "at module '" << mod_name << "'\n"; strm << "Legal parameters are:\n"; d.display(strm, 2, false, false); - throw default_exception(strm.str()); + throw default_exception(std::move(strm).str()); } } @@ -314,7 +312,7 @@ public: if (!('0' <= *value && *value <= '9')) { strm << "Expected values for parameter " << name << " is an unsigned integer. It was given argument '" << _value << "'"; - throw default_exception(strm.str()); + throw default_exception(std::move(strm).str()); } } break; @@ -323,7 +321,7 @@ public: if (!('0' <= *value && *value <= '9') && *value != '.' && *value != '-' && *value != '/') { strm << "Expected values for parameter " << name << " is a double. It was given argument '" << _value << "'"; - throw default_exception(strm.str()); + throw default_exception(std::move(strm).str()); } } break; @@ -332,7 +330,7 @@ public: if (strcmp(value, "true") != 0 && strcmp(value, "false") != 0) { strm << "Expected values for parameter " << name << " are 'true' or 'false'. It was given argument '" << value << "'"; - throw default_exception(strm.str()); + throw default_exception(std::move(strm).str()); } break; default: @@ -370,7 +368,7 @@ public: if (mod_name[0]) { strm << " at module '" << mod_name << "'"; } - throw default_exception(strm.str()); + throw default_exception(std::move(strm).str()); } } else if (k == CPK_SYMBOL) { @@ -387,7 +385,7 @@ public: if (mod_name[0]) { strm << " at module '" << mod_name << "'"; } - throw exception(strm.str()); + throw exception(std::move(strm).str()); } } @@ -408,7 +406,7 @@ public: else { std::stringstream strm; strm << "invalid parameter, unknown module '" << m << "'"; - throw exception(strm.str()); + throw exception(std::move(strm).str()); } } } @@ -458,7 +456,7 @@ public: } std::stringstream strm; strm << "unknown module '" << m << "'"; - throw exception(strm.str()); + throw exception(std::move(strm).str()); } // unfortunately, params_ref is not thread safe @@ -525,7 +523,7 @@ public: if (!get_module_param_descr(module_name, d)) { std::stringstream strm; strm << "unknown module '" << module_name << "'"; - throw exception(strm.str()); + throw exception(std::move(strm).str()); } out << "[module] " << module_name; char const * descr = nullptr; @@ -550,7 +548,7 @@ public: if (!get_module_param_descr(m, d)) { std::stringstream strm; strm << "unknown module '" << m << "'"; - throw exception(strm.str()); + throw exception(std::move(strm).str()); } } if (!d->contains(sp)) @@ -599,7 +597,7 @@ void gparams::register_global(param_descrs & d) { g_imp->register_global(d); } -void gparams::register_module(char const * module_name, lazy_descrs_t& f) { +void gparams::register_module(char const * module_name, lazy_descrs_t f) { SASSERT(g_imp); g_imp->register_module(module_name, f); } diff --git a/src/util/gparams.h b/src/util/gparams.h index 0efad4a07..0959c20fc 100644 --- a/src/util/gparams.h +++ b/src/util/gparams.h @@ -85,8 +85,8 @@ public: module. */ - typedef std::function lazy_descrs_t; - static void register_module(char const* module_name, lazy_descrs_t& get_descrs); + typedef param_descrs* (*lazy_descrs_t)(void); + static void register_module(char const* module_name, lazy_descrs_t get_descrs); /** \brief Add a (small) description to the given module. diff --git a/src/util/max_cliques.h b/src/util/max_cliques.h index 64a718bd1..979a5b795 100644 --- a/src/util/max_cliques.h +++ b/src/util/max_cliques.h @@ -20,7 +20,8 @@ Notes: #include "util/vector.h" #include "util/uint_set.h" - +#include "util/heap.h" +#include "util/map.h" template class max_cliques : public T { @@ -43,12 +44,9 @@ class max_cliques : public T { m_seen1.insert(p); if (m_seen2.contains(p)) { unsigned_vector const& tc = m_tc[p]; - for (unsigned j = 0; j < tc.size(); ++j) { - unsigned np = tc[j]; - if (goal.contains(np)) { + for (unsigned np : tc) + if (goal.contains(np)) reachable.insert(np); - } - } } else { unsigned np = negate(p); @@ -61,29 +59,53 @@ class max_cliques : public T { for (unsigned i = m_todo.size(); i > 0; ) { --i; p = m_todo[i]; - if (m_seen2.contains(p)) { + if (m_seen2.contains(p)) continue; - } m_seen2.insert(p); unsigned np = negate(p); unsigned_vector& tc = m_tc[p]; - if (goal.contains(np)) { + if (goal.contains(np)) tc.push_back(np); - } - else { - unsigned_vector const& succ = next(np); - for (unsigned j = 0; j < succ.size(); ++j) { - tc.append(m_tc[succ[j]]); - } - } + else + for (unsigned s : next(np)) + tc.append(m_tc[s]); } } - - - - unsigned_vector const& next(unsigned vertex) const { return m_next[vertex]; } + + void init(unsigned_vector const& ps) { + unsigned max = 0; + for (unsigned p : ps) { + unsigned np = negate(p); + max = std::max(max, std::max(np, p) + 1); + } + m_next.reserve(max); + m_tc.reserve(m_next.size()); + } + + struct compare_degree { + u_map& conns; + compare_degree(u_map& conns): conns(conns) {} + bool operator()(unsigned x, unsigned y) const { + return conns[x].num_elems() < conns[y].num_elems(); + } + }; + + + void init(unsigned_vector const& ps, u_map& conns) { + + uint_set vars; + + for (unsigned p : ps) + vars.insert(p); + + for (unsigned v : ps) { + uint_set reach; + get_reachable(v, vars, reach); + conns.insert(v, reach); + } + } public: void add_edge(unsigned src, unsigned dst) { @@ -93,21 +115,12 @@ public: m_next[dst].push_back(src); } - void cliques(unsigned_vector const& ps, vector& cliques) { - unsigned max = 0; - unsigned num_ps = ps.size(); - for (unsigned i = 0; i < num_ps; ++i) { - unsigned p = ps[i]; - unsigned np = negate(p); - max = std::max(max, std::max(np, p) + 1); - } - m_next.reserve(max); - m_tc.reserve(m_next.size()); + void cliques1(unsigned_vector const& ps, vector& cliques) { + init(ps); unsigned_vector clique; uint_set vars; - for (unsigned i = 0; i < num_ps; ++i) { - vars.insert(ps[i]); - } + for (unsigned v : ps) + vars.insert(v); while (!vars.empty()) { clique.reset(); @@ -118,9 +131,8 @@ public: m_reachable[turn].remove(p); vars.remove(p); clique.push_back(p); - if (m_reachable[turn].empty()) { + if (m_reachable[turn].empty()) break; - } m_reachable[!turn].reset(); get_reachable(p, m_reachable[turn], m_reachable[!turn]); turn = !turn; @@ -129,9 +141,75 @@ public: if (clique.size() == 2 && clique[0] == negate(clique[1])) { // no op } - else { + else cliques.push_back(clique); + } + } + } + + // better quality cliques + void cliques2(unsigned_vector const& ps, vector& cs) { + u_map conns; + init(ps); + // compute connections using TC of implication graph + init(ps, conns); + cliques(ps, conns, cs); + } + + // cliques after connections are computed. + void cliques(unsigned_vector const& ps, u_map& conns, vector& cliques) { + + unsigned maxp = 1; + for (unsigned p : ps) + maxp = std::max(p, maxp); + + uint_set todo; + compare_degree lt(conns); + heap heap(maxp + 1, lt); + + for (unsigned p : ps) { + todo.insert(p); + heap.insert(p); + } + + while (!todo.empty()) { + unsigned v = heap.min_value(); + uint_set am1; + unsigned_vector next; + for (unsigned n : conns[v]) + if (todo.contains(n)) + next.push_back(n); + std::sort(next.begin(), next.end(), [&](unsigned a, unsigned b) { return conns[a].num_elems() < conns[b].num_elems(); }); + for (unsigned x : next) { + bool all = true; + for (unsigned y : am1) { + if (!conns[x].contains(y)) { + all = false; + break; + } } + if (all) + am1.insert(x); + } + am1.insert(v); + for (unsigned x : am1) { + todo.remove(x); + for (unsigned y : conns[x]) { + conns[y].remove(x); + heap.decreased(y); + } + } + for (unsigned x : am1) + heap.erase(x); + + if (am1.num_elems() > 1) { + unsigned_vector mux; + for (unsigned x : am1) + mux.push_back(x); + if (mux.size() == 2 && mux[0] == negate(mux[1])) { + continue; + } + cliques.push_back(mux); } } } diff --git a/src/util/params.cpp b/src/util/params.cpp index aefe4e074..1745e56fd 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -24,15 +24,14 @@ Notes: params_ref params_ref::g_empty_params_ref; -std::string norm_param_name(char const * n) { - if (n == nullptr) - return "_"; +std::string norm_param_name(char const* n) { if (*n == ':') n++; std::string r = n; unsigned sz = static_cast(r.size()); if (sz == 0) return "_"; + for (unsigned i = 0; i < sz; i++) { char curr = r[i]; if ('A' <= curr && curr <= 'Z') @@ -44,6 +43,8 @@ std::string norm_param_name(char const * n) { } std::string norm_param_name(symbol const & n) { + if (n.is_null()) + return "_"; return norm_param_name(n.bare_str()); } @@ -156,8 +157,8 @@ struct param_descrs::imp { return m_names[idx]; } - struct lt { - bool operator()(symbol const & s1, symbol const & s2) const { return strcmp(s1.bare_str(), s2.bare_str()) < 0; } + struct symlt { + bool operator()(symbol const & s1, symbol const & s2) const { return ::lt(s1, s2); } }; void display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) const { @@ -165,13 +166,13 @@ struct param_descrs::imp { for (auto const& kv : m_info) { names.push_back(kv.m_key); } - std::sort(names.begin(), names.end(), lt()); + std::sort(names.begin(), names.end(), symlt()); for (symbol const& name : names) { for (unsigned i = 0; i < indent; i++) out << " "; if (smt2_style) out << ':'; - char const * s = name.bare_str(); - unsigned n = static_cast(strlen(s)); + std::string s = name.str(); + unsigned n = static_cast(s.length()); for (unsigned i = 0; i < n; i++) { if (smt2_style && s[i] == '_') out << '-'; diff --git a/src/util/rational.h b/src/util/rational.h index e31d06d52..11e4968ab 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -179,6 +179,10 @@ public: return *this; } + rational& operator-=(int r) { + (*this) -= rational(r); + return *this; + } rational & operator*=(rational const & r) { m().mul(m_val, r.m_val, m_val); diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index a36e762aa..e8a72e245 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -76,56 +76,45 @@ static void thread_func(scoped_timer_state *s) { } -struct scoped_timer::imp { -private: - scoped_timer_state *s; +scoped_timer::scoped_timer(unsigned ms, event_handler * eh) { + if (ms == 0 || ms == UINT_MAX) + return; -public: - imp(unsigned ms, event_handler * eh) { - workers.lock(); - bool new_worker = false; - if (available_workers.empty()) { - workers.unlock(); - s = new scoped_timer_state; - new_worker = true; - ++num_workers; - } - else { - s = available_workers.back(); - available_workers.pop_back(); - workers.unlock(); - } - s->ms = ms; - s->eh = eh; - s->m_mutex.lock(); - s->work = WORKING; - if (new_worker) { - s->m_thread = std::thread(thread_func, s); - } - else { - s->cv.notify_one(); - } + workers.lock(); + bool new_worker = false; + if (available_workers.empty()) { + workers.unlock(); + s = new scoped_timer_state; + new_worker = true; + ++num_workers; } - - ~imp() { - s->m_mutex.unlock(); - while (s->work == WORKING) - std::this_thread::yield(); - workers.lock(); - available_workers.push_back(s); + else { + s = available_workers.back(); + available_workers.pop_back(); workers.unlock(); } -}; - -scoped_timer::scoped_timer(unsigned ms, event_handler * eh) { - if (ms != UINT_MAX && ms != 0) - m_imp = alloc(imp, ms, eh); - else - m_imp = nullptr; + s->ms = ms; + s->eh = eh; + s->m_mutex.lock(); + s->work = WORKING; + if (new_worker) { + s->m_thread = std::thread(thread_func, s); + } + else { + s->cv.notify_one(); + } } scoped_timer::~scoped_timer() { - dealloc(m_imp); + if (!s) + return; + + s->m_mutex.unlock(); + while (s->work == WORKING) + std::this_thread::yield(); + workers.lock(); + available_workers.push_back(s); + workers.unlock(); } void scoped_timer::initialize() { diff --git a/src/util/scoped_timer.h b/src/util/scoped_timer.h index 95920b71f..dfd97810c 100644 --- a/src/util/scoped_timer.h +++ b/src/util/scoped_timer.h @@ -20,9 +20,10 @@ Revision History: #include "util/event_handler.h" +struct scoped_timer_state; + class scoped_timer { - struct imp; - imp * m_imp; + scoped_timer_state *s = nullptr; public: scoped_timer(unsigned ms, event_handler * eh); ~scoped_timer(); diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index e73c4cd52..570510458 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -216,7 +216,7 @@ int zstring::indexofu(zstring const& other, unsigned offset) const { int zstring::last_indexof(zstring const& other) const { if (other.length() == 0) return length(); if (other.length() > length()) return -1; - for (unsigned last = length() - other.length(); last-- > 0; ) { + for (unsigned last = length() - other.length() + 1; last-- > 0; ) { bool suffix = true; for (unsigned j = 0; suffix && j < other.length(); ++j) { suffix = m_buffer[last + j] == other[j];