3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-07-03 11:25:40 +00:00
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2021-11-10 03:03:23 -08:00
commit 733f44d141
151 changed files with 3249 additions and 1504 deletions

35
.github/workflows/android-build.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: Android Build
on:
push:
branches: [ master ]
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
android-abi: [arm64-v8a, armeabi-v7a, x86, x86_64]
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Configure CMake and build
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=21 -DCMAKE_ANDROID_ARCH_ABI=${{ matrix.android-abi }} -DCMAKE_ANDROID_NDK=$ANDROID_NDK_HOME -DZ3_BUILD_JAVA_BINDINGS=TRUE -G "Unix Makefiles" -DJAVA_AWT_LIBRARY=NotNeeded -DJAVA_JVM_LIBRARY=NotNeeded -DJAVA_INCLUDE_PATH2=NotNeeded -DJAVA_AWT_INCLUDE_PATH=NotNeeded ../
make -j $(nproc)
tar -cvf z3-build-${{ matrix.android-abi }}.tar *.jar *.so
- name: Archive production artifacts
uses: actions/upload-artifact@v2
with:
name: android-build-${{ matrix.android-abi }}
path: build/z3-build-${{ matrix.android-abi }}.tar

42
.github/workflows/wasm.yml vendored Normal file
View file

@ -0,0 +1,42 @@
name: WASM Build
on:
push:
branches: [ master ]
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Import emscripten
uses: mymindstorm/setup-emsdk@v9
- name: Configure CMake and build
run: |
mkdir build
cd build
emcmake cmake \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DZ3_BUILD_LIBZ3_SHARED=OFF \
-DZ3_ENABLE_EXAMPLE_TARGETS=OFF \
-DZ3_BUILD_TEST_EXECUTABLES=OFF \
-DZ3_BUILD_EXECUTABLE=OFF \
-DZ3_SINGLE_THREADED=ON \
-DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \
..;
make
tar -cvf z3-build-wasm.tar *.a
- name: Archive production artifacts
uses: actions/upload-artifact@v2
with:
name: z3-build-wasm
path: build/z3-build-wasm.tar

View file

@ -14,9 +14,9 @@ See the [release notes](RELEASE_NOTES) for notes on various stable releases of Z
## Build status ## Build status
| Azure Pipelines | Code Coverage | Open Bugs | | Azure Pipelines | Code Coverage | Open Bugs | Android Build | WASM Build |
| --------------- | --------------|-----------| | --------------- | --------------|-----------|---------------|------------|
| [![Build Status](https://dev.azure.com/Z3Public/Z3/_apis/build/status/Z3Prover.z3?branchName=master)](https://dev.azure.com/Z3Public/Z3/_build/latest?definitionId=1&branchName=master) | [![CodeCoverage](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml) | [![Open Issues](https://github.com/Z3Prover/z3/actions/workflows/wip.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) | | [![Build Status](https://dev.azure.com/Z3Public/Z3/_apis/build/status/Z3Prover.z3?branchName=master)](https://dev.azure.com/Z3Public/Z3/_build/latest?definitionId=1&branchName=master) | [![CodeCoverage](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml) | [![Open Issues](https://github.com/Z3Prover/z3/actions/workflows/wip.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) |[![Android Build](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml) | [![WASM Build](https://github.com/Z3Prover/z3/actions/workflows/wasm.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wasm.yml) |
[1]: #building-z3-on-windows-using-visual-studio-command-prompt [1]: #building-z3-on-windows-using-visual-studio-command-prompt
[2]: #building-z3-using-make-and-gccclang [2]: #building-z3-using-make-and-gccclang

View file

@ -43,15 +43,15 @@ jobs:
- ${{if eq(variables['runRegressions'], 'True')}}: - ${{if eq(variables['runRegressions'], 'True')}}:
- template: scripts/test-regressions.yml - template: scripts/test-regressions.yml
- job: "Ubuntu18Python" - job: "Ubuntu20OCaml"
displayName: "Ubuntu 18 with ocaml" displayName: "Ubuntu 20 with OCaml"
pool: pool:
vmImage: "Ubuntu-18.04" vmImage: "Ubuntu-20.04"
steps: steps:
- script: sudo apt-get install ocaml opam libgmp-dev - script: sudo apt-get install ocaml opam libgmp-dev
- script: opam init -y - script: opam init -y
- script: eval `opam config env`; opam install zarith ocamlfind -y - script: eval `opam config env`; opam install zarith ocamlfind -y
- script: python scripts/mk_make.py --ml --staticlib - script: eval `opam config env`; python scripts/mk_make.py --ml
- script: | - script: |
set -e set -e
cd build cd build
@ -59,13 +59,45 @@ jobs:
make -j3 make -j3
make -j3 examples make -j3 examples
make -j3 test-z3 make -j3 test-z3
./ml_example
cd .. cd ..
- script: eval `opam config env`; ocamlfind install z3 build/api/ml/* -dll build/libz3.*
- template: scripts/test-z3.yml - template: scripts/test-z3.yml
- template: scripts/test-regressions.yml - template: scripts/test-regressions.yml
- template: scripts/generate-doc.yml - template: scripts/generate-doc.yml
- job: "Ubuntu20OCamlStatic"
displayName: "Ubuntu 20 with OCaml on z3-static"
pool:
vmImage: "Ubuntu-20.04"
steps:
- script: sudo apt-get install ocaml opam libgmp-dev
- script: opam init -y
- script: eval `opam config env`; opam install zarith ocamlfind -y
- script: eval `opam config env`; python scripts/mk_make.py --ml --staticlib
- script: |
set -e
cd build
eval `opam config env`
make -j3
make -j3 examples
make -j3 test-z3
cd ..
- script: eval `opam config env`; ocamlfind install z3-static build/api/ml/* build/libz3-static.a
- script: |
set -e
cd build
eval `opam config env`
make -j3
make -j3 _ex_ml_example_post_install
./ml_example_static.byte
./ml_example_static_custom.byte
./ml_example_static
cd ..
- template: scripts/test-z3.yml
- template: scripts/test-regressions.yml
- template: scripts/generate-doc.yml
- job: "LinuxMSan" - job: "LinuxMSan"
displayName: "Ubuntu build - cmake" displayName: "Ubuntu build - cmake"
condition: eq(0,1) condition: eq(0,1)
@ -100,7 +132,7 @@ jobs:
# - template: scripts/test-java-cmake.yml # - template: scripts/test-java-cmake.yml
# - template: scripts/test-regressions.yml # - template: scripts/test-regressions.yml
- job: "Ubuntu16CMake" - job: "UbuntuCMake"
displayName: "Ubuntu build - cmake" displayName: "Ubuntu build - cmake"
pool: pool:
vmImage: "Ubuntu-latest" vmImage: "Ubuntu-latest"
@ -252,3 +284,36 @@ jobs:
# - template: scripts/test-examples-cmake.yml # - template: scripts/test-examples-cmake.yml
- template: scripts/test-regressions.yml - template: scripts/test-regressions.yml
# - template: scripts/test-java-cmake.yml # - template: scripts/test-java-cmake.yml
- job: "MacOSOCaml"
displayName: "MacOS build with OCaml"
pool:
vmImage: "macOS-latest"
steps:
- script: brew install opam
- script: opam init -y
- script: eval `opam config env`; opam install zarith ocamlfind -y
- script: eval `opam config env`; python scripts/mk_make.py --ml
- script: |
set -e
cd build
eval `opam config env`
make -j3
make -j3 examples
make -j3 test-z3
cd ..
- script: eval `opam config env`; ocamlfind install z3 build/api/ml/* -dll build/libz3.*
- script: |
set -e
cd build
eval `opam config env`
make -j3
make -j3 _ex_ml_example_post_install
./ml_example_shared.byte
./ml_example_shared_custom.byte
./ml_example_shared
cd ..
# Skip as dead-slow in debug mode:
# - template: scripts/test-z3.yml
- template: scripts/test-regressions.yml

View file

@ -39,6 +39,27 @@ set(CLANG_WARNINGS_AS_ERRORS
"-Werror=delete-non-virtual-dtor" "-Werror=delete-non-virtual-dtor"
# https://clang.llvm.org/docs/DiagnosticsReference.html#woverloaded-virtual # https://clang.llvm.org/docs/DiagnosticsReference.html#woverloaded-virtual
"-Werror=overloaded-virtual" "-Werror=overloaded-virtual"
# warn the user if a class with virtual functions has a
# non-virtual destructor. This helps catch hard to
# track down memory errors
"-Werror=non-virtual-dtor"
# warn if a null dereference is detected
"-Werror=null-dereference"
# warn for potential performance problem casts
# "-Werror=cast-align"
# warn if float is implicit promoted to double
# "-Werror=double-promotion"
"-Werror=no-unreachable-code-return"
# warn the user if a variable declaration shadows one from a parent context
# "-Werror=shadow"
# warn for c-style casts
# "-Werror=old-style-cast"
# warn on sign conversions
# "-Werror=sign-conversion"
# warn on type conversions that may lose data
# "-Werror=conversion"
# warn on anything being unused
# "-Werror=unused"
) )
################################################################################ ################################################################################

View file

@ -106,6 +106,24 @@ ExternalProject_Add(z3_tptp5
) )
set_target_properties(z3_tptp5 PROPERTIES EXCLUDE_FROM_ALL TRUE) set_target_properties(z3_tptp5 PROPERTIES EXCLUDE_FROM_ALL TRUE)
################################################################################
# Build example user-propagator project using libz3's C++ API as an external project
################################################################################
ExternalProject_Add(userPropagator
DEPENDS libz3
# Configure step
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/userPropagator"
CMAKE_ARGS
"-DZ3_DIR=${PROJECT_BINARY_DIR}"
"${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}"
# Build step
${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG}
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/userPropagator_build_dir"
# Install Step
INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command
)
set_target_properties(userPropagator PROPERTIES EXCLUDE_FROM_ALL TRUE)
################################################################################ ################################################################################
# Build Python examples # Build Python examples
################################################################################ ################################################################################

View file

@ -28,6 +28,8 @@ add_executable(cpp_example example.cpp)
target_include_directories(cpp_example PRIVATE ${Z3_CXX_INCLUDE_DIRS}) target_include_directories(cpp_example PRIVATE ${Z3_CXX_INCLUDE_DIRS})
target_link_libraries(cpp_example PRIVATE ${Z3_LIBRARIES}) target_link_libraries(cpp_example PRIVATE ${Z3_LIBRARIES})
target_compile_options(cpp_example PRIVATE ${Z3_COMPONENT_CXX_FLAGS})
if (CMAKE_SYSTEM_NAME MATCHES "[Ww]indows") if (CMAKE_SYSTEM_NAME MATCHES "[Ww]indows")
# On Windows we need to copy the Z3 libraries # On Windows we need to copy the Z3 libraries
# into the same directory as the executable # into the same directory as the executable

View file

@ -304,7 +304,7 @@ void error_example() {
// The next call fails because x is a Boolean. // The next call fails because x is a Boolean.
expr n = x + 1; expr n = x + 1;
} }
catch (exception ex) { catch (exception & ex) {
std::cout << "failed: " << ex << "\n"; std::cout << "failed: " << ex << "\n";
} }
@ -312,7 +312,7 @@ void error_example() {
try { try {
expr arg = to_expr(c, Z3_get_app_arg(c, x, 0)); expr arg = to_expr(c, Z3_get_app_arg(c, x, 0));
} }
catch (exception ex) { catch (exception & ex) {
std::cout << "failed: " << ex << "\n"; std::cout << "failed: " << ex << "\n";
} }
} }
@ -1249,10 +1249,14 @@ void recfun_example() {
static void string_values() { static void string_values() {
context c; context c;
std::cout << "string_values\n";
expr s = c.string_val("abc\n\n\0\0", 7); expr s = c.string_val("abc\n\n\0\0", 7);
std::cout << s << "\n"; std::cout << s << "\n";
std::string s1 = s.get_string(); std::string s1 = s.get_string();
std::cout << s1 << "\n"; std::cout << s1 << "\n";
std::u32string buffer = s.get_u32string();
for (unsigned ch : buffer)
std::cout << "char: " << ch << "\n";
} }
expr MakeStringConstant(context* context, std::string value) { expr MakeStringConstant(context* context, std::string value) {

View file

@ -22,11 +22,11 @@ Copyright (c) 2015 Microsoft Corporation
/** /**
\defgroup capi_ex C API examples \defgroup capi_ex C API examples
*/ */
/*@{*/ /**@{*/
/** /**
@name Auxiliary Functions @name Auxiliary Functions
*/ */
/*@{*/ /**@{*/
/** /**
\brief exit gracefully in case of error. \brief exit gracefully in case of error.
@ -694,12 +694,12 @@ void display_version()
Z3_get_version(&major, &minor, &build, &revision); Z3_get_version(&major, &minor, &build, &revision);
printf("Z3 %d.%d.%d.%d\n", major, minor, build, revision); printf("Z3 %d.%d.%d.%d\n", major, minor, build, revision);
} }
/*@}*/ /**@}*/
/** /**
@name Examples @name Examples
*/ */
/*@{*/ /**@{*/
/** /**
\brief "Hello world" example: create a Z3 logical context, and delete it. \brief "Hello world" example: create a Z3 logical context, and delete it.
*/ */
@ -2947,8 +2947,8 @@ void mk_model_example() {
Z3_del_context(ctx); Z3_del_context(ctx);
} }
/*@}*/ /**@}*/
/*@}*/ /**@}*/

View file

@ -15,7 +15,7 @@ Copyright (c) 2015 Microsoft Corporation
/** /**
\defgroup maxsat_ex MaxSAT/MaxSMT examples \defgroup maxsat_ex MaxSAT/MaxSMT examples
*/ */
/*@{*/ /**@{*/
/** /**
\brief Exit gracefully in case of error. \brief Exit gracefully in case of error.
@ -638,5 +638,5 @@ int main(int argc, char * argv[]) {
return 0; return 0;
} }
/*@}*/ /**@}*/

47
examples/python/efsmt.py Normal file
View file

@ -0,0 +1,47 @@
from z3 import *
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))
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)
else:
return sat
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()

View file

@ -0,0 +1,45 @@
################################################################################
# Example C++ project
################################################################################
project(Z3_USER_PROPAGATOR_EXAMPLE CXX)
cmake_minimum_required(VERSION 3.4)
find_package(Z3
REQUIRED
CONFIG
# `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3.
# This should prevent us from accidentally picking up an installed
# copy of Z3. This is here to benefit Z3's build system when building
# this project. When making your own project you probably shouldn't
# use this option.
NO_DEFAULT_PATH
)
################################################################################
# Z3 C++ API bindings require C++11
################################################################################
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
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)
target_include_directories(user_propagator_example PRIVATE ${Z3_CXX_INCLUDE_DIRS})
target_link_libraries(user_propagator_example PRIVATE ${Z3_LIBRARIES})
if (CMAKE_SYSTEM_NAME MATCHES "[Ww]indows")
# On Windows we need to copy the Z3 libraries
# into the same directory as the executable
# so that they can be found.
foreach (z3_lib ${Z3_LIBRARIES})
message(STATUS "Adding copy rule for ${z3_lib}")
add_custom_command(TARGET user_propagator_example
POST_BUILD
COMMAND
${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:${z3_lib}>
$<TARGET_FILE_DIR:user_propagator_example>
)
endforeach()
endif()

View file

@ -0,0 +1,10 @@
Small example using the user-propagator.
To build the example execute
make examples
in the build directory.
This command will create the executable user_propagator_example.
On Windows, you can just execute it.
On macOS and Linux, you must install z3 first using
sudo make install
OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library.

View file

@ -0,0 +1,370 @@
#include <algorithm>
#include <chrono>
#include <iostream>
#include <random>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <cstring>
#include "z3++.h"
/**
* The program solves the n-queens problem (number of solutions) with 4 different approaches
* 1) Bit-Vector constraints + Default solver + Blocking Clauses
* 2) Bit-Vector constraints + Simple solver + Blocking Clauses
* 3) Bit-Vector constraints + Simple solver + Adding contradictions in the propagator
* 4) Constraints only implicit via the propagator + Simple solver + Adding contradictions in the propagator
*
* Runs 1 + 2 are done for comparison with 3 + 4
*/
using namespace std::chrono;
using std::to_string;
#define QUEEN
#define REPETITIONS 5
#define SIZE(x) std::extent<decltype(x)>::value
#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<unsigned> 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<unsigned, unsigned>& id_mapping;
model currentModel;
std::unordered_set<model, model_hash_function> modelSet;
std::vector<unsigned> fixedValues;
std::stack<unsigned> 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<unsigned, unsigned>& 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<unsigned, unsigned>& 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<z3::expr> createQueens(z3::context &context, unsigned num) {
std::vector<z3::expr> queens;
int bits = log2i(num) + 1 /*to detect potential overflow in the diagonal*/;
for (unsigned i = 0; i < num; i++) {
queens.push_back(context.bv_const((std::string("q") + to_string(i)).c_str(), bits));
}
return queens;
}
void createConstraints(z3::context &context, z3::solver &solver, const std::vector<z3::expr> &queens) {
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)));
}
z3::expr_vector distinct(context);
for (const z3::expr &queen : queens) {
distinct.push_back(queen);
}
solver.add(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]));
}
}
#endif
}
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<z3::expr> queens = createQueens(context, num);
createConstraints(context, solver, queens);
int solutionId = 1;
while (true) {
z3::check_result res = solver.check();
if (res != z3::check_result::sat) {
break;
}
z3::model model = solver.get_model();
WriteLine("Model #" + to_string(solutionId) + ":");
solutionId++;
z3::expr_vector blocking(context);
for (unsigned i = 0; i < num; i++) {
z3::expr eval = model.eval(queens[i]);
WriteLine(("q" + to_string(i) + " = " + to_string(eval.get_numeral_int())));
blocking.push_back(queens[i] != eval);
}
solver.add(z3::mk_or(blocking));
WriteEmptyLine;
}
return solutionId - 1;
}
inline int test0(unsigned num) {
return test01(num, false);
}
inline int test1(unsigned num) {
return test01(num, true);
}
int test23(unsigned num, bool withTheory) {
z3::context context;
z3::solver solver(context, Z3_mk_simple_solver(context));
std::unordered_map<unsigned, unsigned> idMapping;
user_propagator *propagator;
if (!withTheory) {
propagator = new user_propagator(&solver, idMapping, num);
}
else {
propagator = new user_propagator_with_theory(&solver, idMapping, num);
}
std::vector<z3::expr> queens = createQueens(context, num);
for (unsigned i = 0; i < queens.size(); i++) {
unsigned id = propagator->add(queens[i]);
idMapping[id] = i;
}
if (!withTheory) {
createConstraints(context, solver, queens);
}
solver.check();
int res = propagator->getModelCount();
delete propagator;
return res;
}
inline int test2(unsigned num) {
return test23(num, false);
}
inline int test3(unsigned num) {
return test23(num, true);
}
int main() {
for (int num = 4; num <= 11; 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<double, std::milli> 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;
}
}

Binary file not shown.

View file

@ -2014,21 +2014,34 @@ class MLComponent(Component):
LIBZ3 = LIBZ3 + ' ' + ' '.join(map(lambda x: '-cclib ' + x, LDFLAGS.split())) LIBZ3 = LIBZ3 + ' ' + ' '.join(map(lambda x: '-cclib ' + x, LDFLAGS.split()))
stubs_install_path = '$$(%s printconf path)/stublibs' % OCAMLFIND
if not STATIC_LIB:
loadpath = '-ccopt -L' + stubs_install_path
dllpath = '-dllpath ' + stubs_install_path
LIBZ3 = LIBZ3 + ' ' + loadpath + ' ' + dllpath
if DEBUG_MODE and not(is_cygwin()): if DEBUG_MODE and not(is_cygwin()):
# Some ocamlmklib's don't like -g; observed on cygwin, but may be others as well. # Some ocamlmklib's don't like -g; observed on cygwin, but may be others as well.
OCAMLMKLIB += ' -g' OCAMLMKLIB += ' -g'
z3mls = os.path.join(self.sub_dir, 'z3ml') z3mls = os.path.join(self.sub_dir, 'z3ml')
LIBZ3ML = ''
if STATIC_LIB:
LIBZ3ML = '-oc ' + os.path.join(self.sub_dir, 'z3ml-static')
out.write('%s.cma: %s %s %s\n' % (z3mls, cmos, stubso, z3linkdep)) out.write('%s.cma: %s %s %s\n' % (z3mls, cmos, stubso, z3linkdep))
out.write('\t%s -o %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmos, LIBZ3)) out.write('\t%s -o %s %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, LIBZ3ML, self.sub_dir, stubso, cmos, LIBZ3))
out.write('%s.cmxa: %s %s %s %s.cma\n' % (z3mls, cmxs, stubso, z3linkdep, z3mls)) out.write('%s.cmxa: %s %s %s %s.cma\n' % (z3mls, cmxs, stubso, z3linkdep, z3mls))
out.write('\t%s -o %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmxs, LIBZ3)) out.write('\t%s -o %s %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, LIBZ3ML, self.sub_dir, stubso, cmxs, LIBZ3))
out.write('%s.cmxs: %s.cmxa\n' % (z3mls, z3mls)) out.write('%s.cmxs: %s.cmxa\n' % (z3mls, z3mls))
out.write('\t%s -linkall -shared -o %s.cmxs -I . -I %s %s.cmxa\n' % (OCAMLOPTF, z3mls, self.sub_dir, z3mls)) out.write('\t%s -linkall -shared -o %s.cmxs -I . -I %s %s.cmxa\n' % (OCAMLOPTF, z3mls, self.sub_dir, z3mls))
out.write('\n') out.write('\n')
out.write('ml: %s.cma %s.cmxa %s.cmxs\n' % (z3mls, z3mls, z3mls)) out.write('ml: %s.cma %s.cmxa %s.cmxs\n' % (z3mls, z3mls, z3mls))
if IS_OSX:
out.write('\tinstall_name_tool -id %s/libz3.dylib libz3.dylib\n' % (stubs_install_path))
out.write('\tinstall_name_tool -change libz3.dylib %s/libz3.dylib api/ml/dllz3ml.so\n' % (stubs_install_path))
out.write('\n') out.write('\n')
if IS_WINDOWS: if IS_WINDOWS:
@ -2041,6 +2054,7 @@ class MLComponent(Component):
self.mk_uninstall(out) self.mk_uninstall(out)
out.write('\n') out.write('\n')
# The following three functions may be out of date.
def mk_install_deps(self, out): def mk_install_deps(self, out):
if is_ml_enabled() and self._install_bindings(): if is_ml_enabled() and self._install_bindings():
out.write(get_component(Z3_DLL_COMPONENT).dll_name + '$(SO_EXT) ') out.write(get_component(Z3_DLL_COMPONENT).dll_name + '$(SO_EXT) ')
@ -2286,6 +2300,41 @@ class MLExampleComponent(ExampleComponent):
out.write('\n') out.write('\n')
out.write('_ex_%s: ml_example.byte ml_example$(EXE_EXT)\n\n' % self.name) out.write('_ex_%s: ml_example.byte ml_example$(EXE_EXT)\n\n' % self.name)
debug_opt = '-g ' if DEBUG_MODE else ''
if STATIC_LIB:
opam_z3_opts = '-thread -package z3-static -linkpkg'
ml_post_install_tests = [
(OCAMLC, 'ml_example_static.byte'),
(OCAMLC + ' -custom', 'ml_example_static_custom.byte'),
(OCAMLOPT, 'ml_example_static$(EXE_EXT)')
]
else:
opam_z3_opts = '-thread -package z3 -linkpkg'
ml_post_install_tests = [
(OCAMLC, 'ml_example_shared.byte'),
(OCAMLC + ' -custom', 'ml_example_shared_custom.byte'),
(OCAMLOPT, 'ml_example_shared$(EXE_EXT)')
]
for ocaml_compiler, testname in ml_post_install_tests:
out.write(testname + ':')
for mlfile in get_ml_files(self.ex_dir):
out.write(' %s' % os.path.join(self.to_ex_dir, mlfile))
out.write('\n')
out.write('\tocamlfind %s -o %s %s %s ' % (ocaml_compiler, debug_opt, testname, opam_z3_opts))
for mlfile in get_ml_files(self.ex_dir):
out.write(' %s/%s' % (self.to_ex_dir, mlfile))
out.write('\n')
if STATIC_LIB:
out.write('_ex_ml_example_post_install: ml_example_static.byte ml_example_static_custom.byte ml_example_static$(EXE_EXT)\n')
else:
out.write('_ex_ml_example_post_install: ml_example_shared.byte ml_example_shared_custom.byte ml_example_shared$(EXE_EXT)\n')
out.write('\n')
class PythonExampleComponent(ExampleComponent): class PythonExampleComponent(ExampleComponent):
def __init__(self, name, path): def __init__(self, name, path):
ExampleComponent.__init__(self, name, path) ExampleComponent.__init__(self, name, path)

View file

@ -58,7 +58,7 @@ extern "C" {
Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned sz, Z3_string str) { Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned sz, Z3_string str) {
Z3_TRY; Z3_TRY;
LOG_Z3_mk_string(c, str); LOG_Z3_mk_lstring(c, sz, str);
RESET_ERROR_CODE(); RESET_ERROR_CODE();
unsigned_vector chs; unsigned_vector chs;
for (unsigned i = 0; i < sz; ++i) chs.push_back((unsigned char)str[i]); for (unsigned i = 0; i < sz; ++i) chs.push_back((unsigned char)str[i]);
@ -69,6 +69,17 @@ extern "C" {
Z3_CATCH_RETURN(nullptr); Z3_CATCH_RETURN(nullptr);
} }
Z3_ast Z3_API Z3_mk_u32string(Z3_context c, unsigned sz, unsigned const chars[]) {
Z3_TRY;
LOG_Z3_mk_u32string(c, sz, chars);
RESET_ERROR_CODE();
zstring s(sz, chars);
app* a = mk_c(c)->sutil().str.mk_string(s);
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) { Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) {
Z3_TRY; Z3_TRY;
LOG_Z3_mk_string_sort(c); LOG_Z3_mk_string_sort(c);
@ -187,10 +198,9 @@ extern "C" {
svector<char> buff; svector<char> buff;
for (unsigned i = 0; i < str.length(); ++i) { for (unsigned i = 0; i < str.length(); ++i) {
unsigned ch = str[i]; unsigned ch = str[i];
if (ch <= 32 || ch >= 127) { if (ch == 0 || ch >= 256 || (ch == '\\' && i + 1 < str.length() && str[i+1] == 'u')) {
buff.reset(); buff.reset();
buffer.push_back('\\'); buffer.push_back('\\');
// buffer.push_back('\\'); // possibly replace by native non-escaped version?
buffer.push_back('u'); buffer.push_back('u');
buffer.push_back('{'); buffer.push_back('{');
while (ch > 0) { while (ch > 0) {
@ -215,6 +225,38 @@ extern "C" {
Z3_CATCH_RETURN(""); Z3_CATCH_RETURN("");
} }
unsigned Z3_API Z3_get_string_length(Z3_context c, Z3_ast s) {
Z3_TRY;
LOG_Z3_get_string_length(c, s);
RESET_ERROR_CODE();
zstring str;
if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) {
SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal");
}
return str.length();
Z3_CATCH_RETURN(0);
}
void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned contents[]) {
Z3_TRY;
LOG_Z3_get_string_contents(c, s, length, contents);
RESET_ERROR_CODE();
zstring str;
if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) {
SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal");
return;
}
if (str.length() != length) {
SET_ERROR_CODE(Z3_INVALID_ARG, "string size disagrees with supplied buffer length");
return;
}
for (unsigned i = 0; i < length; ++i)
contents[i] = str[i];
Z3_CATCH;
}
#define MK_SORTED(NAME, FN ) \ #define MK_SORTED(NAME, FN ) \
Z3_ast Z3_API NAME(Z3_context c, Z3_sort s) { \ Z3_ast Z3_API NAME(Z3_context c, Z3_sort s) { \
Z3_TRY; \ Z3_TRY; \

View file

@ -875,7 +875,7 @@ extern "C" {
init_solver(c, s); init_solver(c, s);
solver::push_eh_t _push = push_eh; solver::push_eh_t _push = push_eh;
solver::pop_eh_t _pop = pop_eh; solver::pop_eh_t _pop = pop_eh;
solver::fresh_eh_t _fresh = [&](void * user_ctx, ast_manager& m, solver::context_obj*& _ctx) { solver::fresh_eh_t _fresh = [=](void * user_ctx, ast_manager& m, solver::context_obj*& _ctx) {
ast_context_params params; ast_context_params params;
params.set_foreign_manager(&m); params.set_foreign_manager(&m);
auto* ctx = alloc(api::context, &params, false); auto* ctx = alloc(api::context, &params, false);

View file

@ -36,12 +36,12 @@ Notes:
\defgroup cppapi C++ API \defgroup cppapi C++ API
*/ */
/*@{*/ /**@{*/
/** /**
@name C++ API classes and functions @name C++ API classes and functions
*/ */
/*@{*/ /**@{*/
/** /**
\brief Z3 C++ namespace \brief Z3 C++ namespace
@ -379,6 +379,7 @@ namespace z3 {
expr string_val(char const* s); expr string_val(char const* s);
expr string_val(char const* s, unsigned n); expr string_val(char const* s, unsigned n);
expr string_val(std::string const& s); expr string_val(std::string const& s);
expr string_val(std::u32string const& s);
expr num_val(int n, sort const & s); expr num_val(int n, sort const & s);
@ -1100,23 +1101,29 @@ namespace z3 {
bool is_string_value() const { return Z3_is_string(ctx(), m_ast); } bool is_string_value() const { return Z3_is_string(ctx(), m_ast); }
/** /**
\brief for a string value expression return an escaped or unescaped string value. \brief for a string value expression return an escaped string value.
\pre expression is for a string value. \pre expression is for a string value.
*/ */
std::string get_escaped_string() const { std::string get_string() const {
assert(is_string_value()); assert(is_string_value());
char const* s = Z3_get_string(ctx(), m_ast); char const* s = Z3_get_string(ctx(), m_ast);
check_error(); check_error();
return std::string(s); return std::string(s);
} }
std::string get_string() const { /**
\brief for a string value expression return an unespaced string value.
\pre expression is for a string value.
*/
std::u32string get_u32string() const {
assert(is_string_value()); assert(is_string_value());
unsigned n; unsigned n = Z3_get_string_length(ctx(), m_ast);
char const* s = Z3_get_lstring(ctx(), m_ast, &n); std::u32string s;
check_error(); s.resize(n);
return std::string(s, n); Z3_get_string_contents(ctx(), m_ast, n, (unsigned*)s.data());
return s;
} }
operator Z3_app() const { assert(is_app()); return reinterpret_cast<Z3_app>(m_ast); } operator Z3_app() const { assert(is_app()); return reinterpret_cast<Z3_app>(m_ast); }
@ -1516,7 +1523,7 @@ namespace z3 {
expr substitute(expr_vector const& dst); expr substitute(expr_vector const& dst);
class iterator { class iterator {
expr& e; expr& e;
unsigned i; unsigned i;
public: public:
@ -1912,14 +1919,14 @@ namespace z3 {
Z3_ast r; Z3_ast r;
if (a.is_int()) { if (a.is_int()) {
expr zero = a.ctx().int_val(0); expr zero = a.ctx().int_val(0);
expr ge = a >= zero; expr ge = a >= zero;
expr na = -a; expr na = -a;
r = Z3_mk_ite(a.ctx(), ge, a, na); r = Z3_mk_ite(a.ctx(), ge, a, na);
} }
else if (a.is_real()) { else if (a.is_real()) {
expr zero = a.ctx().real_val(0); expr zero = a.ctx().real_val(0);
expr ge = a >= zero; expr ge = a >= zero;
expr na = -a; expr na = -a;
r = Z3_mk_ite(a.ctx(), ge, a, na); r = Z3_mk_ite(a.ctx(), ge, a, na);
} }
else { else {
@ -3480,6 +3487,7 @@ namespace z3 {
inline expr context::string_val(char const* s, unsigned n) { Z3_ast r = Z3_mk_lstring(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::string_val(char const* s, unsigned n) { Z3_ast r = Z3_mk_lstring(m_ctx, n, s); check_error(); return expr(*this, r); }
inline expr context::string_val(char const* s) { Z3_ast r = Z3_mk_string(m_ctx, s); check_error(); return expr(*this, r); } inline expr context::string_val(char const* s) { Z3_ast r = Z3_mk_string(m_ctx, s); check_error(); return expr(*this, r); }
inline expr context::string_val(std::string const& s) { Z3_ast r = Z3_mk_string(m_ctx, s.c_str()); check_error(); return expr(*this, r); } inline expr context::string_val(std::string const& s) { Z3_ast r = Z3_mk_string(m_ctx, s.c_str()); check_error(); return expr(*this, r); }
inline expr context::string_val(std::u32string const& s) { Z3_ast r = Z3_mk_u32string(m_ctx, (unsigned)s.size(), (unsigned const*)s.c_str()); check_error(); return expr(*this, r); }
inline expr context::num_val(int n, sort const & s) { Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::num_val(int n, sort const & s) { Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); }
@ -3932,6 +3940,8 @@ namespace z3 {
virtual void push() = 0; virtual void push() = 0;
virtual void pop(unsigned num_scopes) = 0; virtual void pop(unsigned num_scopes) = 0;
virtual ~user_propagator_base() = default;
/** /**
\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 search and their lifetimes are restricted to search time. They should
@ -3954,12 +3964,28 @@ namespace z3 {
Z3_solver_propagate_fixed(ctx(), *s, fixed_eh); Z3_solver_propagate_fixed(ctx(), *s, fixed_eh);
} }
void register_fixed() {
assert(s);
m_fixed_eh = [this](unsigned id, expr const& e) {
fixed(id, e);
};
Z3_solver_propagate_fixed(ctx(), *s, fixed_eh);
}
void register_eq(eq_eh_t& f) { void register_eq(eq_eh_t& f) {
assert(s); assert(s);
m_eq_eh = f; m_eq_eh = f;
Z3_solver_propagate_eq(ctx(), *s, eq_eh); Z3_solver_propagate_eq(ctx(), *s, eq_eh);
} }
void register_eq() {
assert(s);
m_eq_eh = [this](unsigned x, unsigned y) {
eq(x, y);
};
Z3_solver_propagate_eq(ctx(), *s, eq_eh);
}
/** /**
\brief register a callback on final-check. \brief register a callback on final-check.
During the final check stage, all propagations have been processed. During the final check stage, all propagations have been processed.
@ -3974,6 +4000,21 @@ namespace z3 {
Z3_solver_propagate_final(ctx(), *s, final_eh); Z3_solver_propagate_final(ctx(), *s, final_eh);
} }
void register_final() {
assert(s);
m_final_eh = [this]() {
final();
};
Z3_solver_propagate_final(ctx(), *s, final_eh);
}
virtual void fixed(unsigned /*id*/, expr const& /*e*/) { }
virtual void eq(unsigned /*x*/, unsigned /*y*/) { }
virtual void final() { }
/** /**
\brief tracks \c e by a unique identifier that is returned by the call. \brief tracks \c e by a unique identifier that is returned by the call.
@ -4020,7 +4061,7 @@ namespace z3 {
} }
/*@}*/ /**@}*/
/*@}*/ /**@}*/
#undef Z3_THROW #undef Z3_THROW

View file

@ -214,7 +214,6 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
.MM(expr, numerator) .MM(expr, numerator)
.MM(expr, denominator) .MM(expr, denominator)
.MM(expr, is_string_value) .MM(expr, is_string_value)
.MM(expr, get_escaped_string)
.MM(expr, get_string) .MM(expr, get_string)
.MM(expr, decl) .MM(expr, decl)
.MM(expr, num_args) .MM(expr, num_args)

View file

@ -76,14 +76,14 @@ int compare_pointers(void* pt1, void* pt2) {
return +1; return +1;
} }
#define MK_CTX_OF(X) \ #define MK_CTX_OF(X, USED) \
CAMLprim DLL_PUBLIC value n_context_of_ ## X(value v) { \ CAMLprim DLL_PUBLIC value n_context_of_ ## X(value v) { \
CAMLparam1(v); \ CAMLparam1(v); \
CAMLlocal1(result); \ CAMLlocal1(result); \
Z3_context_plus cp; \ Z3_context_plus cp; \
Z3_ ## X ## _plus * p = (Z3_ ## X ## _plus *) Data_custom_val(v); \ Z3_ ## X ## _plus * p = (Z3_ ## X ## _plus *) Data_custom_val(v); \
cp = p->cp; \ cp = p->cp; \
result = caml_alloc_custom(&Z3_context_plus_custom_ops, sizeof(Z3_context_plus), 0, 1); \ result = caml_alloc_custom_mem(&Z3_context_plus_custom_ops, sizeof(Z3_context_plus), USED); \
*(Z3_context_plus *)Data_custom_val(result) = cp; \ *(Z3_context_plus *)Data_custom_val(result) = cp; \
/* We increment the usage counter of the context, as we just \ /* We increment the usage counter of the context, as we just \
created a second custom block holding that context */ \ created a second custom block holding that context */ \
@ -102,7 +102,7 @@ int compare_pointers(void* pt1, void* pt2) {
CAMLlocal1(result); \ CAMLlocal1(result); \
Z3_context_plus cp = *(Z3_context_plus*)(Data_custom_val(v)); \ Z3_context_plus cp = *(Z3_context_plus*)(Data_custom_val(v)); \
Z3_ ## X ## _plus a = Z3_ ## X ## _plus_mk(cp, NULL); \ Z3_ ## X ## _plus a = Z3_ ## X ## _plus_mk(cp, NULL); \
result = caml_alloc_custom(&Z3_ ## X ## _plus_custom_ops, sizeof(Z3_ ## X ## _plus), 0, 1); \ result = caml_alloc_custom_mem(&Z3_ ## X ## _plus_custom_ops, sizeof(Z3_ ## X ## _plus), USED); \
*(Z3_ ## X ## _plus*)(Data_custom_val(result)) = a; \ *(Z3_ ## X ## _plus*)(Data_custom_val(result)) = a; \
CAMLreturn(result); \ CAMLreturn(result); \
} }
@ -294,9 +294,9 @@ static struct custom_operations Z3_ast_plus_custom_ops = {
Z3_ast_compare_ext Z3_ast_compare_ext
}; };
MK_CTX_OF(ast) MK_CTX_OF(ast, 16) // let's say 16 bytes per ast
#define MK_PLUS_OBJ_NO_REF(X) \ #define MK_PLUS_OBJ_NO_REF(X, USED) \
typedef struct { \ typedef struct { \
Z3_context_plus cp; \ Z3_context_plus cp; \
Z3_ ## X p; \ Z3_ ## X p; \
@ -349,9 +349,9 @@ MK_CTX_OF(ast)
Z3_ ## X ## _compare_ext \ Z3_ ## X ## _compare_ext \
}; \ }; \
\ \
MK_CTX_OF(X) MK_CTX_OF(X, USED)
#define MK_PLUS_OBJ(X) \ #define MK_PLUS_OBJ(X, USED) \
typedef struct { \ typedef struct { \
Z3_context_plus cp; \ Z3_context_plus cp; \
Z3_ ## X p; \ Z3_ ## X p; \
@ -408,27 +408,27 @@ MK_CTX_OF(ast)
Z3_ ## X ## _compare_ext \ Z3_ ## X ## _compare_ext \
}; \ }; \
\ \
MK_CTX_OF(X) MK_CTX_OF(X, USED)
MK_PLUS_OBJ_NO_REF(symbol) MK_PLUS_OBJ_NO_REF(symbol, 32)
MK_PLUS_OBJ_NO_REF(constructor) MK_PLUS_OBJ_NO_REF(constructor, 32)
MK_PLUS_OBJ_NO_REF(constructor_list) MK_PLUS_OBJ_NO_REF(constructor_list, 32)
MK_PLUS_OBJ_NO_REF(rcf_num) MK_PLUS_OBJ_NO_REF(rcf_num, 32)
MK_PLUS_OBJ(params) MK_PLUS_OBJ(params, 128)
MK_PLUS_OBJ(param_descrs) MK_PLUS_OBJ(param_descrs, 128)
MK_PLUS_OBJ(model) MK_PLUS_OBJ(model, 512)
MK_PLUS_OBJ(func_interp) MK_PLUS_OBJ(func_interp, 128)
MK_PLUS_OBJ(func_entry) MK_PLUS_OBJ(func_entry, 128)
MK_PLUS_OBJ(goal) MK_PLUS_OBJ(goal, 128)
MK_PLUS_OBJ(tactic) MK_PLUS_OBJ(tactic, 128)
MK_PLUS_OBJ(probe) MK_PLUS_OBJ(probe, 128)
MK_PLUS_OBJ(apply_result) MK_PLUS_OBJ(apply_result, 128)
MK_PLUS_OBJ(solver) MK_PLUS_OBJ(solver, 20 * 1000 * 1000) // pretend a solver is 20MB
MK_PLUS_OBJ(stats) MK_PLUS_OBJ(stats, 128)
MK_PLUS_OBJ(ast_map) MK_PLUS_OBJ(ast_map, 1024 * 2)
MK_PLUS_OBJ(ast_vector) MK_PLUS_OBJ(ast_vector, 128)
MK_PLUS_OBJ(fixedpoint) MK_PLUS_OBJ(fixedpoint, 20 * 1000 * 1000)
MK_PLUS_OBJ(optimize) MK_PLUS_OBJ(optimize, 20 * 1000 * 1000)
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View file

@ -11102,33 +11102,34 @@ class PropClosures:
self.bases = {} self.bases = {}
self.lock = None self.lock = None
def set_threaded(): def set_threaded(self):
if self.lock is None: if self.lock is None:
import threading import threading
self.lock = threading.thread.Lock() self.lock = threading.Lock()
def get(self, ctx): def get(self, ctx):
if self.lock: if self.lock:
self.lock.acquire() with self.lock:
r = self.bases[ctx] r = self.bases[ctx]
if self.lock: else:
self.lock.release() r = self.bases[ctx]
return r return r
def set(self, ctx, r): def set(self, ctx, r):
if self.lock: if self.lock:
self.lock.acquire() with self.lock:
self.bases[ctx] = r self.bases[ctx] = r
if self.lock: else:
self.lock.release() self.bases[ctx] = r
def insert(self, r): def insert(self, r):
if self.lock: if self.lock:
self.lock.acquire() with self.lock:
id = len(self.bases) + 3 id = len(self.bases) + 3
self.bases[id] = r self.bases[id] = r
if self.lock: else:
self.lock.release() id = len(self.bases) + 3
self.bases[id] = r
return id return id
@ -11151,8 +11152,9 @@ def user_prop_pop(ctx, num_scopes):
def user_prop_fresh(id, ctx): def user_prop_fresh(id, ctx):
_prop_closures.set_threaded() _prop_closures.set_threaded()
new_prop = UsePropagateBase(None, ctx) prop = _prop_closures.get(id)
_prop_closures.set(new_prop.id, new_prop.fresh()) new_prop = prop.fresh()
_prop_closures.set(new_prop.id, new_prop)
return ctypes.c_void_p(new_prop.id) return ctypes.c_void_p(new_prop.id)
@ -11214,11 +11216,12 @@ class UserPropagateBase:
self.eq = None self.eq = None
self.diseq = None self.diseq = None
if ctx: if ctx:
# TBD fresh is broken: ctx is not of the right type when we reach here.
self._ctx = Context() self._ctx = Context()
Z3_del_context(self._ctx.ctx) #Z3_del_context(self._ctx.ctx)
self._ctx.ctx = ctx #self._ctx.ctx = ctx
self._ctx.eh = Z3_set_error_handler(ctx, z3_error_handler) #self._ctx.eh = Z3_set_error_handler(ctx, z3_error_handler)
Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT) #Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT)
if s: if s:
Z3_solver_propagate_init(self.ctx_ref(), Z3_solver_propagate_init(self.ctx_ref(),
s.solver, s.solver,

View file

@ -25,10 +25,10 @@ extern "C" {
#endif // __cplusplus #endif // __cplusplus
/** \defgroup capi C API */ /** \defgroup capi C API */
/*@{*/ /**@{*/
/** @name Algebraic Numbers */ /** @name Algebraic Numbers */
/*@{*/ /**@{*/
/** /**
\brief Return \c true if \c a can be used as value in the Z3 real algebraic \brief Return \c true if \c a can be used as value in the Z3 real algebraic
number package. number package.
@ -240,8 +240,8 @@ extern "C" {
*/ */
unsigned Z3_API Z3_algebraic_get_i(Z3_context c, Z3_ast a); unsigned Z3_API Z3_algebraic_get_i(Z3_context c, Z3_ast a);
/*@}*/ /**@}*/
/*@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -37,11 +37,11 @@ DEFINE_TYPE(Z3_optimize);
DEFINE_TYPE(Z3_rcf_num); DEFINE_TYPE(Z3_rcf_num);
/** \defgroup capi C API */ /** \defgroup capi C API */
/*@{*/ /**@{*/
/** @name Types */ /** @name Types */
///@{ /**@{*/
/** /**
Most of the types in the C API are opaque pointers. Most of the types in the C API are opaque pointers.
@ -1449,7 +1449,7 @@ typedef enum
Z3_GOAL_UNDER_OVER Z3_GOAL_UNDER_OVER
} Z3_goal_prec; } Z3_goal_prec;
///@} /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -1514,7 +1514,7 @@ extern "C" {
/**@}*/ /**@}*/
/** @name Create configuration */ /** @name Create configuration */
/*@{*/ /**@{*/
/** /**
\brief Create a configuration object for the Z3 context object. \brief Create a configuration object for the Z3 context object.
@ -1569,10 +1569,10 @@ extern "C" {
*/ */
void Z3_API Z3_set_param_value(Z3_config c, Z3_string param_id, Z3_string param_value); void Z3_API Z3_set_param_value(Z3_config c, Z3_string param_id, Z3_string param_value);
/*@}*/ /**@}*/
/** @name Context and AST Reference Counting */ /** @name Context and AST Reference Counting */
/*@{*/ /**@{*/
/** /**
\brief Create a context using the given configuration. \brief Create a context using the given configuration.
@ -1678,10 +1678,10 @@ extern "C" {
void Z3_API Z3_interrupt(Z3_context c); void Z3_API Z3_interrupt(Z3_context c);
/*@}*/ /**@}*/
/** @name Parameters */ /** @name Parameters */
/*@{*/ /**@{*/
/** /**
\brief Create a Z3 (empty) parameter set. \brief Create a Z3 (empty) parameter set.
@ -1754,10 +1754,10 @@ extern "C" {
*/ */
void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d); void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d);
/*@}*/ /**@}*/
/** @name Parameter Descriptions */ /** @name Parameter Descriptions */
/*@{*/ /**@{*/
/** /**
\brief Increment the reference counter of the given parameter description set. \brief Increment the reference counter of the given parameter description set.
@ -1811,10 +1811,10 @@ extern "C" {
*/ */
Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p); Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p);
/*@}*/ /**@}*/
/** @name Symbols */ /** @name Symbols */
/*@{*/ /**@{*/
/** /**
\brief Create a Z3 symbol using an integer. \brief Create a Z3 symbol using an integer.
@ -1843,10 +1843,10 @@ extern "C" {
*/ */
Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, Z3_string s); Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, Z3_string s);
/*@}*/ /**@}*/
/** @name Sorts */ /** @name Sorts */
/*@{*/ /**@{*/
/** /**
\brief Create a free (uninterpreted) type using the given name (symbol). \brief Create a free (uninterpreted) type using the given name (symbol).
@ -2150,10 +2150,10 @@ extern "C" {
Z3_func_decl* tester, Z3_func_decl* tester,
Z3_func_decl accessors[]); Z3_func_decl accessors[]);
/*@}*/ /**@}*/
/** @name Constants and Applications */ /** @name Constants and Applications */
/*@{*/ /**@{*/
/** /**
\brief Declare a constant or function. \brief Declare a constant or function.
@ -2287,10 +2287,10 @@ extern "C" {
*/ */
void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body); void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body);
/*@}*/ /**@}*/
/** @name Propositional Logic and Equality */ /** @name Propositional Logic and Equality */
/*@{*/ /**@{*/
/** /**
\brief Create an AST node representing \c true. \brief Create an AST node representing \c true.
@ -2397,10 +2397,10 @@ extern "C" {
def_API('Z3_mk_or', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) def_API('Z3_mk_or', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST)))
*/ */
Z3_ast Z3_API Z3_mk_or(Z3_context c, unsigned num_args, Z3_ast const args[]); Z3_ast Z3_API Z3_mk_or(Z3_context c, unsigned num_args, Z3_ast const args[]);
/*@}*/ /**@}*/
/** @name Integers and Reals */ /** @name Integers and Reals */
/*@{*/ /**@{*/
/** /**
\brief Create an AST node representing \ccode{args[0] + ... + args[num_args-1]}. \brief Create an AST node representing \ccode{args[0] + ... + args[num_args-1]}.
@ -2573,10 +2573,10 @@ extern "C" {
def_API('Z3_mk_is_int', AST, (_in(CONTEXT), _in(AST))) def_API('Z3_mk_is_int', AST, (_in(CONTEXT), _in(AST)))
*/ */
Z3_ast Z3_API Z3_mk_is_int(Z3_context c, Z3_ast t1); Z3_ast Z3_API Z3_mk_is_int(Z3_context c, Z3_ast t1);
/*@}*/ /**@}*/
/** @name Bit-vectors */ /** @name Bit-vectors */
/*@{*/ /**@{*/
/** /**
\brief Bitwise negation. \brief Bitwise negation.
@ -3097,10 +3097,10 @@ extern "C" {
def_API('Z3_mk_bvmul_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) def_API('Z3_mk_bvmul_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/ */
Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2); Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2);
/*@}*/ /**@}*/
/** @name Arrays */ /** @name Arrays */
/*@{*/ /**@{*/
/** /**
\brief Array read. \brief Array read.
The argument \c a is the array and \c i is the index of the array that gets read. The argument \c a is the array and \c i is the index of the array that gets read.
@ -3212,10 +3212,10 @@ extern "C" {
*/ */
Z3_ast Z3_API Z3_mk_set_has_size(Z3_context c, Z3_ast set, Z3_ast k); Z3_ast Z3_API Z3_mk_set_has_size(Z3_context c, Z3_ast set, Z3_ast k);
/*@}*/ /**@}*/
/** @name Sets */ /** @name Sets */
/*@{*/ /**@{*/
/** /**
\brief Create Set type. \brief Create Set type.
@ -3308,10 +3308,10 @@ extern "C" {
*/ */
Z3_ast Z3_API Z3_mk_array_ext(Z3_context c, Z3_ast arg1, Z3_ast arg2); Z3_ast Z3_API Z3_mk_array_ext(Z3_context c, Z3_ast arg1, Z3_ast arg2);
/*@}*/ /**@}*/
/** @name Numerals */ /** @name Numerals */
/*@{*/ /**@{*/
/** /**
\brief Create a numeral of a given sort. \brief Create a numeral of a given sort.
@ -3400,10 +3400,10 @@ extern "C" {
*/ */
Z3_ast Z3_API Z3_mk_bv_numeral(Z3_context c, unsigned sz, bool const* bits); Z3_ast Z3_API Z3_mk_bv_numeral(Z3_context c, unsigned sz, bool const* bits);
/*@}*/ /**@}*/
/** @name Sequences and regular expressions */ /** @name Sequences and regular expressions */
/*@{*/ /**@{*/
/** /**
\brief Create a sequence sort out of the sort for the elements. \brief Create a sequence sort out of the sort for the elements.
@ -3480,6 +3480,11 @@ extern "C" {
/** /**
\brief Create a string constant out of the string that is passed in \brief Create a string constant out of the string that is passed in
The string may contain escape encoding for non-printable characters
or characters outside of the basic printable ASCII range. For example,
the escape encoding \u{0} represents the character 0 and the encoding
\u{100} represents the character 256.
def_API('Z3_mk_string', AST, (_in(CONTEXT), _in(STRING))) def_API('Z3_mk_string', AST, (_in(CONTEXT), _in(STRING)))
*/ */
Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string s); Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string s);
@ -3487,12 +3492,22 @@ extern "C" {
/** /**
\brief Create a string constant out of the string that is passed in \brief Create a string constant out of the string that is passed in
It takes the length of the string as well to take into account It takes the length of the string as well to take into account
0 characters. The string is unescaped. 0 characters. The string is treated as if it is unescaped so a sequence
of characters \u{0} is treated as 5 characters and not the character 0.
def_API('Z3_mk_lstring', AST, (_in(CONTEXT), _in(UINT), _in(STRING))) def_API('Z3_mk_lstring', AST, (_in(CONTEXT), _in(UINT), _in(STRING)))
*/ */
Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned len, Z3_string s); Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned len, Z3_string s);
/**
\brief Create a string constant out of the string that is passed in
It takes the length of the string as well to take into account
0 characters. The string is unescaped.
def_API('Z3_mk_u32string', AST, (_in(CONTEXT), _in(UINT), _in_array(1, UINT)))
*/
Z3_ast Z3_API Z3_mk_u32string(Z3_context c, unsigned len, unsigned const chars[]);
/** /**
\brief Determine if \c s is a string constant. \brief Determine if \c s is a string constant.
@ -3502,6 +3517,7 @@ extern "C" {
/** /**
\brief Retrieve the string constant stored in \c s. \brief Retrieve the string constant stored in \c s.
Characters outside the basic printiable ASCII range are escaped.
\pre Z3_is_string(c, s) \pre Z3_is_string(c, s)
@ -3510,7 +3526,9 @@ extern "C" {
Z3_string Z3_API Z3_get_string(Z3_context c, Z3_ast s); Z3_string Z3_API Z3_get_string(Z3_context c, Z3_ast s);
/** /**
\brief Retrieve the unescaped string constant stored in \c s. \brief Retrieve the string constant stored in \c s. The string can contain escape sequences.
Characters in the range 1 to 255 are literal.
Characters in the range 0, and 256 above are escaped.
\pre Z3_is_string(c, s) \pre Z3_is_string(c, s)
@ -3518,6 +3536,26 @@ extern "C" {
*/ */
Z3_char_ptr Z3_API Z3_get_lstring(Z3_context c, Z3_ast s, unsigned* length); Z3_char_ptr Z3_API Z3_get_lstring(Z3_context c, Z3_ast s, unsigned* length);
/**
\brief Retrieve the length of the unescaped string constant stored in \c s.
\pre Z3_is_string(c, s)
def_API('Z3_get_string_length', UINT, (_in(CONTEXT), _in(AST)))
*/
unsigned Z3_API Z3_get_string_length(Z3_context c, Z3_ast s);
/**
\brief Retrieve the unescaped string constant stored in \c s.
\pre Z3_is_string(c, s)
\pre length contains the number of characters in s
def_API('Z3_get_string_contents', VOID, (_in(CONTEXT), _in(AST), _in(UINT), _out_array(2, UINT)))
*/
void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned contents[]);
/** /**
\brief Create an empty sequence of the sequence sort \c seq. \brief Create an empty sequence of the sequence sort \c seq.
@ -3821,11 +3859,11 @@ extern "C" {
*/ */
Z3_ast Z3_API Z3_mk_char_is_digit(Z3_context c, Z3_ast ch); Z3_ast Z3_API Z3_mk_char_is_digit(Z3_context c, Z3_ast ch);
/*@}*/ /**@}*/
/** @name Special relations */ /** @name Special relations */
/*@{*/ /**@{*/
/** /**
\brief create a linear ordering relation over signature \c a. \brief create a linear ordering relation over signature \c a.
The relation is identified by the index \c id. The relation is identified by the index \c id.
@ -3866,10 +3904,10 @@ extern "C" {
*/ */
Z3_func_decl Z3_API Z3_mk_transitive_closure(Z3_context c, Z3_func_decl f); Z3_func_decl Z3_API Z3_mk_transitive_closure(Z3_context c, Z3_func_decl f);
/*@}*/ /**@}*/
/** @name Quantifiers */ /** @name Quantifiers */
/*@{*/ /**@{*/
/** /**
\brief Create a pattern for quantifier instantiation. \brief Create a pattern for quantifier instantiation.
@ -4174,10 +4212,10 @@ extern "C" {
Z3_ast body); Z3_ast body);
/*@}*/ /**@}*/
/** @name Accessors */ /** @name Accessors */
/*@{*/ /**@{*/
/** /**
\brief Return \c Z3_INT_SYMBOL if the symbol was constructed \brief Return \c Z3_INT_SYMBOL if the symbol was constructed
using #Z3_mk_int_symbol, and \c Z3_STRING_SYMBOL if the symbol using #Z3_mk_int_symbol, and \c Z3_STRING_SYMBOL if the symbol
@ -5112,10 +5150,10 @@ extern "C" {
def_API('Z3_simplify_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT),)) def_API('Z3_simplify_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT),))
*/ */
Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c); Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c);
/*@}*/ /**@}*/
/** @name Modifiers */ /** @name Modifiers */
/*@{*/ /**@{*/
/** /**
\brief Update the arguments of term \c a using the arguments \c args. \brief Update the arguments of term \c a using the arguments \c args.
The number of arguments \c num_args should coincide The number of arguments \c num_args should coincide
@ -5158,10 +5196,10 @@ extern "C" {
def_API('Z3_translate', AST, (_in(CONTEXT), _in(AST), _in(CONTEXT))) def_API('Z3_translate', AST, (_in(CONTEXT), _in(AST), _in(CONTEXT)))
*/ */
Z3_ast Z3_API Z3_translate(Z3_context source, Z3_ast a, Z3_context target); Z3_ast Z3_API Z3_translate(Z3_context source, Z3_ast a, Z3_context target);
/*@}*/ /**@}*/
/** @name Models */ /** @name Models */
/*@{*/ /**@{*/
/** /**
\brief Create a fresh model object. It has reference count 0. \brief Create a fresh model object. It has reference count 0.
@ -5496,10 +5534,10 @@ extern "C" {
def_API('Z3_func_entry_get_arg', AST, (_in(CONTEXT), _in(FUNC_ENTRY), _in(UINT))) def_API('Z3_func_entry_get_arg', AST, (_in(CONTEXT), _in(FUNC_ENTRY), _in(UINT)))
*/ */
Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i); Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i);
/*@}*/ /**@}*/
/** @name Interaction logging */ /** @name Interaction logging */
/*@{*/ /**@{*/
/** /**
\brief Log interaction to a file. \brief Log interaction to a file.
@ -5534,10 +5572,10 @@ extern "C" {
def_API('Z3_toggle_warning_messages', VOID, (_in(BOOL),)) def_API('Z3_toggle_warning_messages', VOID, (_in(BOOL),))
*/ */
void Z3_API Z3_toggle_warning_messages(bool enabled); void Z3_API Z3_toggle_warning_messages(bool enabled);
/*@}*/ /**@}*/
/** @name String conversion */ /** @name String conversion */
/*@{*/ /**@{*/
/** /**
\brief Select mode for the format used for pretty-printing AST nodes. \brief Select mode for the format used for pretty-printing AST nodes.
@ -5624,10 +5662,10 @@ extern "C" {
Z3_ast const assumptions[], Z3_ast const assumptions[],
Z3_ast formula); Z3_ast formula);
/*@}*/ /**@}*/
/** @name Parser interface */ /** @name Parser interface */
/*@{*/ /**@{*/
/** /**
\brief Parse the given string using the SMT-LIB2 parser. \brief Parse the given string using the SMT-LIB2 parser.
@ -5671,10 +5709,10 @@ extern "C" {
Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str); Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str);
/*@}*/ /**@}*/
/** @name Error Handling */ /** @name Error Handling */
/*@{*/ /**@{*/
#ifndef SAFE_ERRORS #ifndef SAFE_ERRORS
/** /**
\brief Return the error code for the last API call. \brief Return the error code for the last API call.
@ -5717,10 +5755,10 @@ extern "C" {
*/ */
Z3_string Z3_API Z3_get_error_msg(Z3_context c, Z3_error_code err); Z3_string Z3_API Z3_get_error_msg(Z3_context c, Z3_error_code err);
/*@}*/ /**@}*/
/** @name Miscellaneous */ /** @name Miscellaneous */
/*@{*/ /**@{*/
/** /**
\brief Return Z3 version number information. \brief Return Z3 version number information.
@ -5781,10 +5819,10 @@ extern "C" {
def_API('Z3_finalize_memory', VOID, ()) def_API('Z3_finalize_memory', VOID, ())
*/ */
void Z3_API Z3_finalize_memory(void); void Z3_API Z3_finalize_memory(void);
/*@}*/ /**@}*/
/** @name Goals */ /** @name Goals */
/*@{*/ /**@{*/
/** /**
\brief Create a goal (aka problem). A goal is essentially a set \brief Create a goal (aka problem). A goal is essentially a set
of formulas, that can be solved and/or transformed using of formulas, that can be solved and/or transformed using
@ -5934,10 +5972,10 @@ extern "C" {
*/ */
Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g, bool include_names); Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g, bool include_names);
/*@}*/ /**@}*/
/** @name Tactics and Probes */ /** @name Tactics and Probes */
/*@{*/ /**@{*/
/** /**
\brief Return a tactic associated with the given name. \brief Return a tactic associated with the given name.
The complete list of tactics may be obtained using the procedures #Z3_get_num_tactics and #Z3_get_tactic_name. The complete list of tactics may be obtained using the procedures #Z3_get_num_tactics and #Z3_get_tactic_name.
@ -6286,10 +6324,10 @@ extern "C" {
*/ */
Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i); Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i);
/*@}*/ /**@}*/
/** @name Solvers*/ /** @name Solvers*/
/*@{*/ /**@{*/
/** /**
\brief Create a new solver. This solver is a "combined solver" (see \brief Create a new solver. This solver is a "combined solver" (see
combined_solver module) that internally uses a non-incremental (solver1) and an combined_solver module) that internally uses a non-incremental (solver1) and an
@ -6812,10 +6850,10 @@ extern "C" {
*/ */
Z3_string Z3_API Z3_solver_to_dimacs_string(Z3_context c, Z3_solver s, bool include_names); Z3_string Z3_API Z3_solver_to_dimacs_string(Z3_context c, Z3_solver s, bool include_names);
/*@}*/ /**@}*/
/** @name Statistics */ /** @name Statistics */
/*@{*/ /**@{*/
/** /**
\brief Convert a statistics into a string. \brief Convert a statistics into a string.
@ -6897,11 +6935,11 @@ extern "C" {
*/ */
uint64_t Z3_API Z3_get_estimated_alloc_size(void); uint64_t Z3_API Z3_get_estimated_alloc_size(void);
/*@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif // __cplusplus #endif // __cplusplus
/*@}*/ /**@}*/

View file

@ -23,10 +23,10 @@ extern "C" {
#endif // __cplusplus #endif // __cplusplus
/** \defgroup capi C API */ /** \defgroup capi C API */
/*@{*/ /**@{*/
/** @name AST vectors */ /** @name AST vectors */
/*@{*/ /**@{*/
/** /**
\brief Return an empty AST vector. \brief Return an empty AST vector.
@ -104,10 +104,10 @@ extern "C" {
*/ */
Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v); Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v);
/*@}*/ /**@}*/
/** @name AST maps */ /** @name AST maps */
/*@{*/ /**@{*/
/** /**
\brief Return an empty mapping from AST to AST \brief Return an empty mapping from AST to AST
@ -189,8 +189,8 @@ extern "C" {
def_API('Z3_ast_map_to_string', STRING, (_in(CONTEXT), _in(AST_MAP))) def_API('Z3_ast_map_to_string', STRING, (_in(CONTEXT), _in(AST_MAP)))
*/ */
Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m); Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m);
/*@}*/ /**@}*/
/*@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -23,10 +23,10 @@ extern "C" {
#endif // __cplusplus #endif // __cplusplus
/** \defgroup capi C API */ /** \defgroup capi C API */
/*@{*/ /**@{*/
/** @name Fixedpoint facilities */ /** @name Fixedpoint facilities */
/*@{*/ /**@{*/
/** /**
\brief Create a new fixedpoint context. \brief Create a new fixedpoint context.
@ -373,8 +373,8 @@ extern "C" {
void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl); void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl);
/*@}*/ /**@}*/
/*@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -23,10 +23,10 @@ extern "C" {
#endif // __cplusplus #endif // __cplusplus
/** \defgroup capi C API */ /** \defgroup capi C API */
/*@{*/ /**@{*/
/** @name Floating-Point Arithmetic */ /** @name Floating-Point Arithmetic */
/*@{*/ /**@{*/
/** /**
\brief Create the RoundingMode sort. \brief Create the RoundingMode sort.
@ -841,7 +841,7 @@ extern "C" {
/** @name Z3-specific floating-point extensions */ /** @name Z3-specific floating-point extensions */
/*@{*/ /**@{*/
/** /**
\brief Retrieves the number of bits reserved for the exponent in a FloatingPoint sort. \brief Retrieves the number of bits reserved for the exponent in a FloatingPoint sort.
@ -1080,9 +1080,9 @@ extern "C" {
def_API('Z3_mk_fpa_to_fp_int_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(SORT))) def_API('Z3_mk_fpa_to_fp_int_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(SORT)))
*/ */
Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s); Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s);
/*@}*/ /**@}*/
/*@}*/ /**@}*/
/*@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -28,10 +28,10 @@ extern "C" {
#endif // __cplusplus #endif // __cplusplus
/** \defgroup capi C API */ /** \defgroup capi C API */
/*@{*/ /**@{*/
/** @name Optimization facilities */ /** @name Optimization facilities */
/*@{*/ /**@{*/
/** /**
\brief Create a new optimize context. \brief Create a new optimize context.
@ -368,8 +368,8 @@ extern "C" {
Z3_model_eh model_eh); Z3_model_eh model_eh);
/*@}*/ /**@}*/
/*@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -24,11 +24,11 @@ extern "C" {
#endif // __cplusplus #endif // __cplusplus
/** \defgroup capi C API */ /** \defgroup capi C API */
/*@{*/ /**@{*/
/** @name Polynomials */ /** @name Polynomials */
/*@{*/ /**@{*/
/** /**
\brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x. \brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x.
@ -43,8 +43,8 @@ extern "C" {
Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x); Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x);
/*@}*/ /**@}*/
/*@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -26,10 +26,10 @@ extern "C" {
#endif // __cplusplus #endif // __cplusplus
/** \defgroup capi C API */ /** \defgroup capi C API */
/*@{*/ /**@{*/
/** @name Real Closed Fields */ /** @name Real Closed Fields */
/*@{*/ /**@{*/
/** /**
\brief Delete a RCF numeral created using the RCF API. \brief Delete a RCF numeral created using the RCF API.
@ -196,8 +196,8 @@ extern "C" {
*/ */
void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d); void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d);
/*@}*/ /**@}*/
/*@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -23,10 +23,10 @@ extern "C" {
#endif // __cplusplus #endif // __cplusplus
/** \defgroup capi C API */ /** \defgroup capi C API */
/*@{*/ /**@{*/
/** @name Spacer facilities */ /** @name Spacer facilities */
/*@{*/ /**@{*/
/** /**
\brief Pose a query against the asserted rules at the given level. \brief Pose a query against the asserted rules at the given level.
@ -132,8 +132,8 @@ extern "C" {
Z3_ast_vector vars, Z3_ast_vector vars,
Z3_ast body); Z3_ast body);
/*@}*/ /**@}*/
/*@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -1399,6 +1399,7 @@ inline bool has_labels(expr const * n) {
class some_value_proc { class some_value_proc {
public: public:
virtual expr * operator()(sort * s) = 0; virtual expr * operator()(sort * s) = 0;
virtual ~some_value_proc() = default;
}; };
// ----------------------------------- // -----------------------------------

View file

@ -46,6 +46,7 @@ public:
public: public:
virtual bool operator()(func_decl* d) const { return false; } virtual bool operator()(func_decl* d) const { return false; }
virtual bool operator()(sort* s) const { return false; } virtual bool operator()(sort* s) const { return false; }
virtual ~is_declared() = default;
}; };
private: private:
ast_manager& m_manager; ast_manager& m_manager;

View file

@ -406,7 +406,7 @@ namespace datatype {
VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast())); VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
VALIDATE_PARAM(u().is_datatype(domain[0])); VALIDATE_PARAM(u().is_datatype(domain[0]));
VALIDATE_PARAM_PP(domain[0] == to_func_decl(parameters[0].get_ast())->get_range(), "invalid sort argument passed to recognizer"); VALIDATE_PARAM_PP(domain[0] == to_func_decl(parameters[0].get_ast())->get_range(), "invalid sort argument passed to recognizer");
// blindly trust that parameter is a constructor VALIDATE_PARAM_PP(u().is_constructor(to_func_decl(parameters[0].get_ast())), "expecting constructor argument to recognizer");
sort* range = m_manager->mk_bool_sort(); sort* range = m_manager->mk_bool_sort();
func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters); func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters);
info.m_private_parameters = true; info.m_private_parameters = true;

View file

@ -681,6 +681,7 @@ namespace euf {
void egraph::begin_explain() { void egraph::begin_explain() {
SASSERT(m_todo.empty()); SASSERT(m_todo.empty());
m_uses_congruence = false; m_uses_congruence = false;
DEBUG_CODE(for (enode* n : m_nodes) SASSERT(!n->is_marked1()););
} }
void egraph::end_explain() { void egraph::end_explain() {

View file

@ -346,7 +346,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
app * a0 = to_app(val->get_arg(0)); app * a0 = to_app(val->get_arg(0));
expr_ref v0(m), v1(m), v2(m); expr_ref v0(m), v1(m), v2(m);
#ifdef Z3DEBUG #ifdef Z3DEBUG_FPA2BV_NAMES
app * a1 = to_app(val->get_arg(1)); app * a1 = to_app(val->get_arg(1));
app * a2 = to_app(val->get_arg(2)); app * a2 = to_app(val->get_arg(2));
v0 = mc->get_const_interp(a0->get_decl()); v0 = mc->get_const_interp(a0->get_decl());
@ -378,7 +378,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
SASSERT(val->is_app_of(m_fpa_util.get_family_id(), OP_FPA_FP)); SASSERT(val->is_app_of(m_fpa_util.get_family_id(), OP_FPA_FP));
#ifdef Z3DEBUG #ifdef Z3DEBUG_FPA2BV_NAMES
SASSERT(to_app(val->get_arg(0))->get_decl()->get_arity() == 0); SASSERT(to_app(val->get_arg(0))->get_decl()->get_arity() == 0);
SASSERT(to_app(val->get_arg(1))->get_decl()->get_arity() == 0); SASSERT(to_app(val->get_arg(1))->get_decl()->get_arity() == 0);
SASSERT(to_app(val->get_arg(2))->get_decl()->get_arity() == 0); SASSERT(to_app(val->get_arg(2))->get_decl()->get_arity() == 0);
@ -386,9 +386,10 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
seen.insert(to_app(val->get_arg(1))->get_decl()); seen.insert(to_app(val->get_arg(1))->get_decl());
seen.insert(to_app(val->get_arg(2))->get_decl()); seen.insert(to_app(val->get_arg(2))->get_decl());
#else #else
SASSERT(a->get_arg(0)->get_kind() == OP_EXTRACT); SASSERT(is_app(val->get_arg(0)));
SASSERT(to_app(a->get_arg(0))->get_arg(0)->get_kind() == OP_EXTRACT); SASSERT(m_bv_util.is_extract(val->get_arg(0)));
seen.insert(to_app(to_app(val->get_arg(0))->get_arg(0))->get_decl()); seen.insert(to_app(to_app(val->get_arg(0))->get_arg(0))->get_decl());
#endif #endif
if (!sgn && !sig && !exp) if (!sgn && !sig && !exp)

View file

@ -192,7 +192,7 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) {
app_ref sgn(m), s(m), e(m); app_ref sgn(m), s(m), e(m);
#ifdef Z3DEBUG #ifdef Z3DEBUG_FPA2BV_NAMES
std::string p("fpa2bv"); std::string p("fpa2bv");
std::string name = f->get_name().str(); std::string name = f->get_name().str();
@ -326,7 +326,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) {
expr_ref bv3(m); expr_ref bv3(m);
bv3 = m.mk_fresh_const( bv3 = m.mk_fresh_const(
#ifdef Z3DEBUG #ifdef Z3DEBUG_FPA2BV_NAMES
"fpa2bv_rm" "fpa2bv_rm"
#else #else
nullptr nullptr
@ -465,7 +465,7 @@ void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits,
SASSERT(is_well_sorted(m, big_d_sig)); SASSERT(is_well_sorted(m, big_d_sig));
if (ebits > sbits) if (ebits > sbits)
throw default_exception("there is no floating point support for division for representations with non-standard bit representations"); throw default_exception("addition/subtract with ebits > sbits not supported");
expr_ref shifted_big(m), shifted_d_sig(m), sticky_raw(m), sticky(m); expr_ref shifted_big(m), shifted_d_sig(m), sticky_raw(m), sticky(m);
@ -950,7 +950,7 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref &
unsigned ebits = m_util.get_ebits(s); unsigned ebits = m_util.get_ebits(s);
unsigned sbits = m_util.get_sbits(s); unsigned sbits = m_util.get_sbits(s);
if (ebits > sbits) if (ebits > sbits)
throw default_exception("there is no floating point support for division for representations with non-standard bit representations"); throw default_exception("division with ebits > sbits not supported");
SASSERT(ebits <= sbits); SASSERT(ebits <= sbits);
expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m);
@ -2561,9 +2561,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
res_sig = sig; res_sig = sig;
res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder. res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder.
unsigned sig_sz = m_bv_util.get_bv_size(res_sig); SASSERT(m_bv_util.get_bv_size(res_sig) == to_sbits + 4);
(void) sig_sz;
SASSERT(sig_sz == to_sbits + 4);
expr_ref exponent_overflow(m), exponent_underflow(m); expr_ref exponent_overflow(m), exponent_underflow(m);
exponent_overflow = m.mk_false(); exponent_overflow = m.mk_false();
@ -2577,7 +2575,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
lz_ext = m_bv_util.mk_zero_extend(to_ebits - from_ebits + 2, lz); lz_ext = m_bv_util.mk_zero_extend(to_ebits - from_ebits + 2, lz);
res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext);
} }
else if (from_ebits > (to_ebits + 2)) { else if (from_ebits >= (to_ebits + 2)) {
unsigned ebits_diff = from_ebits - (to_ebits + 2); unsigned ebits_diff = from_ebits - (to_ebits + 2);
// subtract lz for subnormal numbers. // subtract lz for subnormal numbers.
@ -2617,9 +2615,6 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
res_exp = m.mk_ite(ovf_cond, max_exp, res_exp); res_exp = m.mk_ite(ovf_cond, max_exp, res_exp);
res_exp = m.mk_ite(udf_cond, min_exp, res_exp); res_exp = m.mk_ite(udf_cond, min_exp, res_exp);
} }
else { // from_ebits == (to_ebits + 2)
res_exp = m_bv_util.mk_bv_sub(exp, lz);
}
SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits + 2); SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits + 2);
SASSERT(is_well_sorted(m, res_exp)); SASSERT(is_well_sorted(m, res_exp));
@ -3839,7 +3834,7 @@ void fpa2bv_converter::mk_rounding_mode(decl_kind k, expr_ref & result)
} }
void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) {
#ifdef Z3DEBUG #ifdef Z3DEBUG_FPA2BV_NAMES
return; return;
// CMW: This works only for quantifier-free formulas. // CMW: This works only for quantifier-free formulas.
if (m_util.is_fp(e)) { if (m_util.is_fp(e)) {

View file

@ -61,7 +61,7 @@ protected:
public: public:
fpa2bv_converter(ast_manager & m); fpa2bv_converter(ast_manager & m);
~fpa2bv_converter(); virtual ~fpa2bv_converter();
fpa_util & fu() { return m_util; } fpa_util & fu() { return m_util; }
bv_util & bu() { return m_bv_util; } bv_util & bu() { return m_bv_util; }

View file

@ -929,16 +929,22 @@ bool fpa_decl_plugin::is_unique_value(app* e) const {
case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_NEGATIVE:
case OP_FPA_RM_TOWARD_ZERO: case OP_FPA_RM_TOWARD_ZERO:
return true; return true;
case OP_FPA_PLUS_INF: /* No; +oo == fp(#b0 #b11 #b00) */ case OP_FPA_PLUS_INF: /* No; +oo == (fp #b0 #b11 #b00) */
case OP_FPA_MINUS_INF: /* No; -oo == fp #b1 #b11 #b00) */ case OP_FPA_MINUS_INF: /* No; -oo == (fp #b1 #b11 #b00) */
case OP_FPA_PLUS_ZERO: /* No; +zero == fp #b0 #b00 #b000) */ case OP_FPA_PLUS_ZERO: /* No; +zero == (fp #b0 #b00 #b000) */
case OP_FPA_MINUS_ZERO: /* No; -zero == fp #b1 #b00 #b000) */ case OP_FPA_MINUS_ZERO: /* No; -zero == (fp #b1 #b00 #b000) */
case OP_FPA_NAN: /* No; NaN == (fp #b0 #b111111 #b0000001) */ case OP_FPA_NAN: /* No; NaN == (fp #b0 #b111111 #b0000001) */
case OP_FPA_NUM: /* see NaN */ case OP_FPA_NUM: /* see NaN */
return false; return false;
case OP_FPA_FP: case OP_FPA_FP: {
return false; /*No; generally not because of clashes with +oo, -oo, +zero, -zero, NaN */ if (m_manager->is_value(e->get_arg(0)) &&
// a refinement would require to return true only if there is no clash with these cases. m_manager->is_value(e->get_arg(1)) &&
m_manager->is_value(e->get_arg(2))) {
bv_util bu(*m_manager);
return !bu.is_allone(e->get_arg(1)) && !bu.is_zero(e->get_arg(1));
}
return false;
}
default: default:
return false; return false;
} }

View file

@ -23,6 +23,7 @@ Revision History:
class is_variable_proc { class is_variable_proc {
public: public:
virtual ~is_variable_proc() = default;
virtual bool operator()(const expr* e) const = 0; virtual bool operator()(const expr* e) const = 0;
}; };

View file

@ -23,6 +23,7 @@ Notes:
class expr_predicate { class expr_predicate {
public: public:
virtual ~expr_predicate() = default;
virtual bool operator()(expr * t) = 0; virtual bool operator()(expr * t) = 0;
}; };

View file

@ -37,6 +37,8 @@ public:
m_ignore_quantifiers(ignore_quantifiers) { m_ignore_quantifiers(ignore_quantifiers) {
} }
virtual ~num_occurs() = default;
void validate(); void validate();
virtual void reset() { m_num_occurs.reset(); } virtual void reset() { m_num_occurs.reset(); }

View file

@ -93,6 +93,7 @@ namespace recfun {
// closure for computing whether a `rhs` expression is immediate // closure for computing whether a `rhs` expression is immediate
struct is_immediate_pred { struct is_immediate_pred {
virtual bool operator()(expr * rhs) = 0; virtual bool operator()(expr * rhs) = 0;
virtual ~is_immediate_pred() = default;
}; };
class def { class def {

View file

@ -703,8 +703,60 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
result = m().update_quantifier(lam, quantifier_kind::forall_k, e); result = m().update_quantifier(lam, quantifier_kind::forall_k, e);
return BR_REWRITE2; return BR_REWRITE2;
} }
expr_ref lh1(m()), rh1(m());
expr_ref_vector fmls(m());
auto has_large_domain = [&](sort* s, unsigned num_stores) {
unsigned sz = get_array_arity(s);
uint64_t dsz = 1;
for (unsigned i = 0; i < sz; ++i) {
sort* d = get_array_domain(s, i);
if (d->is_infinite() || d->is_very_big())
return true;
auto const& n = d->get_num_elements();
if (n.size() > num_stores)
return true;
dsz *= n.size();
if (dsz > num_stores)
return true;
}
return false;
};
if (m_expand_store_eq) {
expr* lhs1 = lhs;
expr* rhs1 = rhs;
unsigned num_lhs = 0, num_rhs = 0;
while (m_util.is_store(lhs1)) {
lhs1 = to_app(lhs1)->get_arg(0);
++num_lhs;
}
while (m_util.is_store(rhs1)) {
rhs1 = to_app(rhs1)->get_arg(0);
++num_rhs;
}
if (lhs1 == rhs1) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
result = m().mk_and(fmls);
return BR_REWRITE_FULL;
}
if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
fmls.push_back(m().mk_eq(v, w));
result = m().mk_and(fmls);
return BR_REWRITE_FULL;
}
}
if (m_expand_nested_stores) { if (m_expand_nested_stores) {
expr_ref lh1(m()), rh1(m());
if (is_expandable_store(lhs)) { if (is_expandable_store(lhs)) {
lh1 = expand_store(lhs); lh1 = expand_store(lhs);
} }
@ -719,10 +771,6 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
} }
} }
if (!m_expand_store_eq) {
return BR_FAILED;
}
expr_ref_vector fmls(m());
#if 0 #if 0
// lambda friendly version of array equality rewriting. // lambda friendly version of array equality rewriting.
@ -744,46 +792,5 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
} }
#endif #endif
expr* lhs1 = lhs;
unsigned num_lhs = 0, num_rhs = 0;
while (m_util.is_store(lhs1)) {
lhs1 = to_app(lhs1)->get_arg(0);
++num_lhs;
}
expr* rhs1 = rhs;
while (m_util.is_store(rhs1)) {
rhs1 = to_app(rhs1)->get_arg(0);
++num_rhs;
}
if (lhs1 == rhs1) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
result = m().mk_and(fmls);
return BR_REWRITE_FULL;
}
auto has_large_domain = [&](sort* s, unsigned num_stores) {
unsigned sz = get_array_arity(s);
uint64_t dsz = 1;
for (unsigned i = 0; i < sz; ++i) {
sort* d = get_array_domain(s, i);
if (d->is_infinite() || d->is_very_big())
return true;
auto const& n = d->get_num_elements();
if (n.size() > num_stores)
return true;
dsz *= n.size();
if (dsz > num_stores)
return true;
}
return false;
};
if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
fmls.push_back(m().mk_eq(v, w));
result = m().mk_and(fmls);
return BR_REWRITE_FULL;
}
return BR_FAILED; return BR_FAILED;
} }

View file

@ -33,6 +33,7 @@ struct push_app_ite_cfg : public default_rewriter_cfg {
virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args); virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr);
push_app_ite_cfg(ast_manager& m): m(m), m_conservative(true) {} push_app_ite_cfg(ast_manager& m): m(m), m_conservative(true) {}
virtual ~push_app_ite_cfg() = default;
void set_conservative(bool c) { m_conservative = c; } void set_conservative(bool c) { m_conservative = c; }
bool rewrite_patterns() const { return false; } bool rewrite_patterns() const { return false; }
}; };

View file

@ -22,10 +22,9 @@ Author:
br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (m_rec.is_defined(f) && num_args > 0) { if (m_rec.is_defined(f) && num_args > 0) {
for (unsigned i = 0; i < num_args; ++i) { for (unsigned i = 0; i < num_args; ++i)
if (!m.is_value(args[i])) if (!m.is_value(args[i]))
return BR_FAILED; return BR_FAILED;
}
if (!m_rec.has_def(f)) if (!m_rec.has_def(f))
return BR_FAILED; return BR_FAILED;
recfun::def const& d = m_rec.get_def(f); recfun::def const& d = m_rec.get_def(f);
@ -35,9 +34,8 @@ br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr *
result = sub(d.get_rhs(), num_args, args); result = sub(d.get_rhs(), num_args, args);
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
} }
else { else
return BR_FAILED; return BR_FAILED;
}
} }

View file

@ -850,7 +850,7 @@ namespace seq {
add_clause(~eq, ge10k); add_clause(~eq, ge10k);
for (unsigned i = 0; i < k; ++i) { for (unsigned i = 0; i < k; ++i) {
expr* ch = seq.str.mk_nth_i(ubvs, i); expr* ch = seq.str.mk_nth_c(ubvs, i);
is_digit = seq.mk_char_is_digit(ch); is_digit = seq.mk_char_is_digit(ch);
add_clause(~ge_len, is_digit); add_clause(~ge_len, is_digit);
} }
@ -1142,8 +1142,8 @@ namespace seq {
/** /**
~contains(a, b) => ~prefix(b, a) ~contains(a, b) => ~prefix(b, a)
~contains(a, b) => ~contains(tail(a), b) or a = empty ~contains(a, b) => ~contains(tail(a), b)
~contains(a, b) & a = empty => b != empty a = empty => tail(a) = empty
~(a = empty) => a = head + tail ~(a = empty) => a = head + tail
*/ */
void axioms::unroll_not_contains(expr* e) { void axioms::unroll_not_contains(expr* e) {

View file

@ -15,6 +15,7 @@ Author:
--*/ --*/
#include "ast/ast_pp.h"
#include "ast/rewriter/seq_eq_solver.h" #include "ast/rewriter/seq_eq_solver.h"
#include "ast/bv_decl_plugin.h" #include "ast/bv_decl_plugin.h"
@ -704,8 +705,9 @@ namespace seq {
bool same = true; bool same = true;
// ls = x ++ rs' && rs = rs' ++ y, diff = |x| // ls = x ++ rs' && rs = rs' ++ y, diff = |x|
if (rs.size() > i) { if (rs.size() > i) {
for (unsigned j = 1; same && j <= i; ++j) for (unsigned j = 1; same && j <= i; ++j) {
same = !m.are_distinct(ls[diff + j], rs[j]); same = !m.are_distinct(ls[diff + j], rs[j]);
}
} }
// ls = x ++ rs ++ y, diff = |x| // ls = x ++ rs ++ y, diff = |x|
else { else {
@ -715,6 +717,7 @@ namespace seq {
if (same) if (same)
return true; return true;
} }
return false; return false;
} }

View file

@ -39,6 +39,7 @@ namespace seq {
class eq_solver_context { class eq_solver_context {
public: public:
virtual ~eq_solver_context() = default;
virtual void add_consequence(bool uses_dep, expr_ref_vector const& clause) = 0; virtual void add_consequence(bool uses_dep, expr_ref_vector const& clause) = 0;
virtual void add_solution(expr* var, expr* term) = 0; virtual void add_solution(expr* var, expr* term) = 0;
virtual expr* expr2rep(expr* e) = 0; virtual expr* expr2rep(expr* e) = 0;

View file

@ -859,13 +859,12 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
// elif offset >= len(s) then 0 // elif offset >= len(s) then 0
// elif offset + length > len(s) then len(s) - offset // elif offset + length > len(s) then len(s) - offset
// else length // else length
expr_ref zero(m_autil.mk_int(0), m());
result = length; result = length;
result = m().mk_ite(m_autil.mk_gt(m_autil.mk_add(offset, length), len_s), result = m().mk_ite(m_autil.mk_gt(m_autil.mk_add(offset, length), len_s),
m_autil.mk_sub(len_s, offset), m_autil.mk_sub(len_s, offset),
result); result);
result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero), m_autil.mk_lt(offset, zero)), result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero()), m_autil.mk_lt(offset, zero())),
zero, zero(),
result); result);
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
} }
@ -883,10 +882,24 @@ expr_ref seq_rewriter::mk_seq_first(expr* t) {
if (str().is_extract(t, s, j, k)) if (str().is_extract(t, s, j, k))
result = str().mk_nth_i(s, j); result = str().mk_nth_i(s, j);
else else
result = str().mk_nth_i(t, m_autil.mk_int(0)); result = str().mk_nth_c(t, 0);
return result; return result;
} }
expr_ref seq_rewriter::mk_sub(expr* a, rational const& n) {
expr* a1, *a2;
SASSERT(n.is_int());
rational k;
if (m_autil.is_sub(a, a1, a2) && m_autil.is_numeral(a2, k))
return expr_ref(m_autil.mk_sub(a1, m_autil.mk_int(k + n)), m());
if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a2, k))
return expr_ref(m_autil.mk_add(a1, m_autil.mk_int(k - n)), m());
if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a1, k))
return expr_ref(m_autil.mk_add(a2, m_autil.mk_int(k - n)), m());
return expr_ref(m_autil.mk_sub(a, m_autil.mk_int(n)), m());
}
/* /*
* In general constructs substring(t,1,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j+1,k-1) * In general constructs substring(t,1,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j+1,k-1)
* This method assumes that |t| > 0. * This method assumes that |t| > 0.
@ -894,41 +907,50 @@ expr_ref seq_rewriter::mk_seq_first(expr* t) {
expr_ref seq_rewriter::mk_seq_rest(expr* t) { expr_ref seq_rewriter::mk_seq_rest(expr* t) {
expr_ref result(m()); expr_ref result(m());
expr* s, * j, * k; expr* s, * j, * k;
expr_ref one(m_autil.mk_int(1), m()); rational jv;
if (str().is_extract(t, s, j, k)) if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, jv) && jv >= 0)
result = str().mk_substr(s, m_autil.mk_add(j, one), m_autil.mk_sub(k, one)); result = str().mk_substr(s, m_autil.mk_int(jv + 1), mk_sub(k, 1));
else else
result = str().mk_substr(t, one, m_autil.mk_sub(str().mk_length(t), one)); result = str().mk_substr(t, one(), mk_sub(str().mk_length(t), 1));
return result; return result;
} }
/* /*
* In general constructs nth(t,|t|-1) but if t = substring(s,j,k) then simplifies to nth(s,j+k-1) * In general constructs nth(t,|t|-1) but if t = substring(s,j,|s|-j) j >= 0, then simplifies to nth(s,|s|-1)
* This method assumes that |t| > 0. * This method assumes that |t| > 0.
*/ */
expr_ref seq_rewriter::mk_seq_last(expr* t) { expr_ref seq_rewriter::mk_seq_last(expr* t) {
expr_ref result(m()); expr_ref result(m());
expr* s, * j, * k; expr* s, * j, * k, * s_, * len_s;
expr_ref one(m_autil.mk_int(1), m()); rational jv, i;
if (str().is_extract(t, s, j, k)) if (str().is_extract(t, s, j, k) &&
result = str().mk_nth_i(s, m_autil.mk_sub(m_autil.mk_add(j, k), one)); m_autil.is_numeral(j, jv) && jv >= 0 &&
str().is_len_sub(k, len_s, s_, i) &&
s == s_ && jv == i) {
expr_ref lastpos = mk_sub(len_s, 1);
result = str().mk_nth_i(s, lastpos);
}
else else
result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one)); result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one()));
return result; return result;
} }
/* /*
* In general constructs substring(t,0,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j,k-1) * 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 holds. * This method assumes that |t| > 0, thus, if t = substring(s,0,k) then k > 0 so substring(s,0,k-1) is correct.
*/ */
expr_ref seq_rewriter::mk_seq_butlast(expr* t) { expr_ref seq_rewriter::mk_seq_butlast(expr* t) {
expr_ref result(m()); expr_ref result(m());
expr* s, * j, * k; expr* s, * j, * k;
expr_ref one(m_autil.mk_int(1), m()); rational v;
if (str().is_extract(t, s, j, k)) if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, v) && v.is_zero()) {
result = str().mk_substr(s, j, m_autil.mk_sub(k, one)); 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 else
result = str().mk_substr(t, m_autil.mk_int(0), m_autil.mk_sub(str().mk_length(t), one)); result = str().mk_substr(t, zero(), m_autil.mk_sub(str().mk_length(t), one()));
return result; return result;
} }
@ -1582,23 +1604,33 @@ br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) {
result = s; result = s;
return BR_DONE; return BR_DONE;
} }
if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1)) { if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1) && pos1 > 0) {
expr_ref_vector lens(m()); expr_ref_vector lens(m());
rational pos2; rational pos2;
/*
* nth(s[k, |s| - k], b) =
* b < 0 -> nth_u(a, b)
* b + k < |s| -> nth(s, b + k)
* k >= |s| -> nth_u(empty, b)
* k < |s| <= b + k -> nth_u(a, b)
*/
if (get_lengths(len, lens, pos2) && (pos1 == -pos2) && (lens.size() == 1) && (lens.get(0) == s)) { if (get_lengths(len, lens, pos2) && (pos1 == -pos2) && (lens.size() == 1) && (lens.get(0) == s)) {
expr_ref idx(m_autil.mk_int(pos1), m()); expr_ref k(m_autil.mk_int(pos1), m());
idx = m_autil.mk_add(b, idx); expr_ref case1(str().mk_nth_i(s, m_autil.mk_add(b, k)), m());
expr* es[2] = { s, idx }; expr_ref case2(str().mk_nth_u(str().mk_empty(s->get_sort()), b), m());
result = m().mk_app(m_util.get_family_id(), OP_SEQ_NTH, 2, es); expr_ref case3(str().mk_nth_u(a, b), m());
result = case3;
result = m().mk_ite(m_autil.mk_lt(m_autil.mk_add(k, b), str().mk_length(s)), case1, result);
result = m().mk_ite(m_autil.mk_ge(k, str().mk_length(s)), case2, result);
result = m().mk_ite(m_autil.mk_lt(b, zero()), case3, result);
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
} }
} }
expr* es[2] = { a, b};
expr* la = str().mk_length(a); expr* la = str().mk_length(a);
result = m().mk_ite(m().mk_and(m_autil.mk_ge(b, zero()), m().mk_not(m_autil.mk_le(la, b))), result = m().mk_ite(m().mk_and(m_autil.mk_ge(b, zero()), m().mk_not(m_autil.mk_le(la, b))),
m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_I, 2, es), str().mk_nth_i(a, b),
m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_U, 2, es)); str().mk_nth_u(a, b));
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
} }
@ -1678,7 +1710,7 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result
return BR_DONE; return BR_DONE;
} }
if (m_autil.is_numeral(c, r) && r.is_neg()) { if (m_autil.is_numeral(c, r) && r.is_neg()) {
result = m_autil.mk_int(-1); result = minus_one();
return BR_DONE; return BR_DONE;
} }
@ -1688,10 +1720,10 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result
} }
if (str().is_empty(b)) { if (str().is_empty(b)) {
result = m().mk_ite(m().mk_and(m_autil.mk_le(m_autil.mk_int(0), c), result = m().mk_ite(m().mk_and(m_autil.mk_le(zero(), c),
m_autil.mk_le(c, str().mk_length(a))), m_autil.mk_le(c, str().mk_length(a))),
c, c,
m_autil.mk_int(-1)); minus_one());
return BR_REWRITE2; return BR_REWRITE2;
} }
@ -2307,7 +2339,7 @@ br_status seq_rewriter::mk_str_to_code(expr* a, expr_ref& result) {
if (s.length() == 1) if (s.length() == 1)
result = m_autil.mk_int(s[0]); result = m_autil.mk_int(s[0]);
else else
result = m_autil.mk_int(-1); result = minus_one();
return BR_DONE; return BR_DONE;
} }
return BR_FAILED; return BR_FAILED;
@ -2448,7 +2480,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
result = m_autil.mk_int(ch - '0'); result = m_autil.mk_int(ch - '0');
} }
else { else {
result = m_autil.mk_int(-1); result = minus_one();
} }
return BR_DONE; return BR_DONE;
} }
@ -2456,7 +2488,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
expr_ref_vector as(m()); expr_ref_vector as(m());
str().get_concat_units(a, as); str().get_concat_units(a, as);
if (as.empty()) { if (as.empty()) {
result = m_autil.mk_int(-1); result = minus_one();
return BR_DONE; return BR_DONE;
} }
if (str().is_unit(as.back())) { if (str().is_unit(as.back())) {
@ -2466,11 +2498,11 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
expr_ref tail(str().mk_stoi(as.back()), m()); expr_ref tail(str().mk_stoi(as.back()), m());
expr_ref head(str().mk_concat(as.size() - 1, as.data(), a->get_sort()), m()); expr_ref head(str().mk_concat(as.size() - 1, as.data(), a->get_sort()), m());
expr_ref stoi_head(str().mk_stoi(head), m()); expr_ref stoi_head(str().mk_stoi(head), m());
result = m().mk_ite(m_autil.mk_ge(stoi_head, m_autil.mk_int(0)), result = m().mk_ite(m_autil.mk_ge(stoi_head, zero()),
m_autil.mk_add(m_autil.mk_mul(m_autil.mk_int(10), stoi_head), tail), m_autil.mk_add(m_autil.mk_mul(m_autil.mk_int(10), stoi_head), tail),
m_autil.mk_int(-1)); minus_one());
result = m().mk_ite(m_autil.mk_ge(tail, m_autil.mk_int(0)), result = m().mk_ite(m_autil.mk_ge(tail, zero()),
result, result,
tail); tail);
result = m().mk_ite(str().mk_is_empty(head), result = m().mk_ite(str().mk_is_empty(head),
@ -2481,7 +2513,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
if (str().is_unit(as.get(0), u) && m_util.is_const_char(u, ch) && '0' == ch) { if (str().is_unit(as.get(0), u) && m_util.is_const_char(u, ch) && '0' == ch) {
result = str().mk_concat(as.size() - 1, as.data() + 1, as[0]->get_sort()); result = str().mk_concat(as.size() - 1, as.data() + 1, as[0]->get_sort());
result = m().mk_ite(str().mk_is_empty(result), result = m().mk_ite(str().mk_is_empty(result),
m_autil.mk_int(0), zero(),
str().mk_stoi(result)); str().mk_stoi(result));
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
} }
@ -2573,7 +2605,7 @@ bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) {
} }
/* /*
s = head + tail where |head| = 1 s = [head] + tail where head is the first element of s
*/ */
bool seq_rewriter::get_head_tail(expr* s, expr_ref& head, expr_ref& tail) { bool seq_rewriter::get_head_tail(expr* s, expr_ref& head, expr_ref& tail) {
expr* h = nullptr, *t = nullptr; expr* h = nullptr, *t = nullptr;
@ -2670,10 +2702,10 @@ expr_ref seq_rewriter::re_predicate(expr* cond, sort* seq_sort) {
expr_ref seq_rewriter::is_nullable(expr* r) { expr_ref seq_rewriter::is_nullable(expr* r) {
STRACE("seq_verbose", tout << "is_nullable: " STRACE("seq_verbose", tout << "is_nullable: "
<< mk_pp(r, m()) << std::endl;); << mk_pp(r, m()) << std::endl;);
expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr), m()); expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr, nullptr), m());
if (!result) { if (!result) {
result = is_nullable_rec(r); result = is_nullable_rec(r);
m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, result); m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, nullptr, result);
} }
STRACE("seq_verbose", tout << "is_nullable result: " STRACE("seq_verbose", tout << "is_nullable result: "
<< result << std::endl;); << result << std::endl;);
@ -2691,7 +2723,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
re().is_intersection(r, r1, r2)) { re().is_intersection(r, r1, r2)) {
m_br.mk_and(is_nullable(r1), is_nullable(r2), result); m_br.mk_and(is_nullable(r1), is_nullable(r2), result);
} }
else if (re().is_union(r, r1, r2)) { else if (re().is_union(r, r1, r2) || re().is_antimorov_union(r, r1, r2)) {
m_br.mk_or(is_nullable(r1), is_nullable(r2), result); m_br.mk_or(is_nullable(r1), is_nullable(r2), result);
} }
else if (re().is_diff(r, r1, r2)) { else if (re().is_diff(r, r1, r2)) {
@ -2701,6 +2733,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
else if (re().is_star(r) || else if (re().is_star(r) ||
re().is_opt(r) || re().is_opt(r) ||
re().is_full_seq(r) || re().is_full_seq(r) ||
re().is_epsilon(r) ||
(re().is_loop(r, r1, lo) && lo == 0) || (re().is_loop(r, r1, lo) && lo == 0) ||
(re().is_loop(r, r1, lo, hi) && lo == 0)) { (re().is_loop(r, r1, lo, hi) && lo == 0)) {
result = m().mk_true(); result = m().mk_true();
@ -2724,7 +2757,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
result = is_nullable(r1); result = is_nullable(r1);
} }
else if (m().is_ite(r, cond, r1, r2)) { else if (m().is_ite(r, cond, r1, r2)) {
result = m().mk_ite(cond, is_nullable(r1), is_nullable(r2)); m_br.mk_ite(cond, is_nullable(r1), is_nullable(r2), result);
} }
else if (m_util.is_re(r, seq_sort)) { else if (m_util.is_re(r, seq_sort)) {
result = is_nullable_symbolic_regex(r, seq_sort); result = is_nullable_symbolic_regex(r, seq_sort);
@ -2881,7 +2914,8 @@ br_status seq_rewriter::mk_re_reverse(expr* r, expr_ref& result) {
br_status seq_rewriter::mk_re_derivative(expr* ele, expr* r, expr_ref& result) { br_status seq_rewriter::mk_re_derivative(expr* ele, expr* r, expr_ref& result) {
result = mk_derivative(ele, r); result = mk_derivative(ele, r);
// TBD: we may even declare BR_DONE here and potentially miss some simplifications // TBD: we may even declare BR_DONE here and potentially miss some simplifications
return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL; // return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL;
return BR_DONE;
} }
/* /*
@ -2976,29 +3010,406 @@ bool seq_rewriter::check_deriv_normal_form(expr* r, int level) {
} }
#endif #endif
/* expr_ref seq_rewriter::mk_derivative(expr* r) {
Memoized, recursive implementation of the symbolic derivative such that sort* seq_sort = nullptr, * ele_sort = nullptr;
the result is in normal form. VERIFY(m_util.is_re(r, seq_sort));
VERIFY(m_util.is_seq(seq_sort, ele_sort));
expr_ref v(m().mk_var(0, ele_sort), m());
return mk_antimirov_deriv(v, r, m().mk_true());
}
Functions without _rec are memoized wrappers, which call the _rec
version if lookup fails.
The main logic is in mk_der_op_rec for combining normal forms.
*/
expr_ref seq_rewriter::mk_derivative(expr* ele, expr* r) { expr_ref seq_rewriter::mk_derivative(expr* ele, expr* r) {
STRACE("seq_verbose", tout << "derivative: " << mk_pp(ele, m()) return mk_antimirov_deriv(ele, r, m().mk_true());
<< "," << mk_pp(r, m()) << std::endl;); }
expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, ele, r), m());
expr_ref seq_rewriter::mk_antimirov_deriv(expr* e, expr* r, expr* path) {
// Ensure references are owned
expr_ref _e(e, m()), _path(path, m()), _r(r, m());
expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, e, r, path), m());
if (!result) { if (!result) {
result = mk_derivative_rec(ele, r); mk_antimirov_deriv_rec(e, r, path, result);
m_op_cache.insert(OP_RE_DERIVATIVE, ele, r, result); m_op_cache.insert(OP_RE_DERIVATIVE, e, r, path, result);
STRACE("seq_regex", tout << "D(" << mk_pp(e, m()) << "," << mk_pp(r, m()) << "," << mk_pp(path, m()) << ")" << std::endl;);
STRACE("seq_regex", tout << "= " << mk_pp(result, m()) << std::endl;);
} }
STRACE("seq_verbose", tout << "derivative result: "
<< mk_pp(result, m()) << std::endl;);
CASSERT("seq_regex", check_deriv_normal_form(r));
return result; return result;
} }
void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result) {
sort* seq_sort = nullptr, * ele_sort = nullptr;
VERIFY(m_util.is_re(r, seq_sort));
VERIFY(m_util.is_seq(seq_sort, ele_sort));
SASSERT(ele_sort == e->get_sort());
expr* r1 = nullptr, * r2 = nullptr, * c = nullptr;
expr_ref c1(m());
expr_ref c2(m());
auto nothing = [&]() { return expr_ref(re().mk_empty(r->get_sort()), m()); };
auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); };
auto dotstar = [&]() { return expr_ref(re().mk_full_seq(r->get_sort()), m()); };
unsigned lo = 0, hi = 0;
if (re().is_empty(r) || re().is_epsilon(r))
// D(e,[]) = D(e,()) = []
result = nothing();
else if (re().is_full_seq(r) || re().is_dot_plus(r))
// D(e,.*) = D(e,.+) = .*
result = dotstar();
else if (re().is_full_char(r))
// D(e,.) = ()
result = epsilon();
else if (re().is_to_re(r, r1)) {
expr_ref h(m());
expr_ref t(m());
// here r1 is a sequence
if (get_head_tail(r1, h, t)) {
if (eq_char(e, h))
result = re().mk_to_re(t);
else if (neq_char(e, h))
result = nothing();
else
result = re().mk_ite_simplify(m().mk_eq(e, h), re().mk_to_re(t), nothing());
}
else {
// observe that the precondition |r1|>0 is is implied by c1 for use of mk_seq_first
m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_first(r1), e), c1);
m_br.mk_and(path, c1, c2);
if (m().is_false(c2))
result = nothing();
else
// observe that the precondition |r1|>0 is implied by c1 for use of mk_seq_rest
result = m().mk_ite(c1, re().mk_to_re(mk_seq_rest(r1)), nothing());
}
}
else if (re().is_reverse(r, r2))
if (re().is_to_re(r2, r1)) {
// here r1 is a sequence
// observe that the precondition |r1|>0 of mk_seq_last is implied by c1
m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_last(r1), e), c1);
m_br.mk_and(path, c1, c2);
if (m().is_false(c2))
result = nothing();
else
// observe that the precondition |r1|>0 of mk_seq_rest is implied by c1
result = re().mk_ite_simplify(c1, re().mk_reverse(re().mk_to_re(mk_seq_butlast(r1))), nothing());
}
else {
result = mk_regex_reverse(r2);
if (result.get() == r)
//r2 is an uninterpreted regex that is stuck
//for example if r = (re.reverse R) where R is a regex variable then
//here result.get() == r
result = re().mk_derivative(e, result);
else
result = mk_antimirov_deriv(e, result, path);
}
else if (re().is_concat(r, r1, r2)) {
expr_ref r1nullable(is_nullable(r1), m());
c1 = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), r2);
expr_ref r1nullable_and_path(m());
m_br.mk_and(r1nullable, path, r1nullable_and_path);
if (m().is_false(r1nullable_and_path))
// D(e,r1)r2
result = c1;
else
// D(e,r1)r2|(ite (r1nullable) (D(e,r2)) [])
// observe that (mk_ite_simplify(true, D(e,r2), []) = D(e,r2)
result = mk_antimirov_deriv_union(c1, re().mk_ite_simplify(r1nullable, mk_antimirov_deriv(e, r2, path), nothing()));
}
else if (m().is_ite(r, c, r1, r2)) {
c1 = simplify_path(m().mk_and(c, path));
c2 = simplify_path(m().mk_and(m().mk_not(c), path));
if (m().is_false(c1))
result = mk_antimirov_deriv(e, r2, c2);
else if (m().is_false(c2))
result = mk_antimirov_deriv(e, r1, c1);
else
result = re().mk_ite_simplify(c, mk_antimirov_deriv(e, r1, c1), mk_antimirov_deriv(e, r2, c2));
}
else if (re().is_range(r, r1, r2)) {
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));
// case: c1 <= e <= c2
range = simplify_path(m().mk_and(u().mk_le(c1, e), u().mk_le(e, c2)));
psi = simplify_path(m().mk_and(path, range));
}
else if (!str().is_string(r1) && str().is_unit_string(r2, c2)) {
SASSERT(u().is_char(c2));
// r1 nonground: |r1|=1 & r1[0] <= e <= c2
expr_ref one(m_autil.mk_int(1), m());
expr_ref zero(m_autil.mk_int(0), m());
expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m());
expr_ref r1_0(str().mk_nth_i(r1, zero), m());
range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, c2))));
psi = simplify_path(m().mk_and(path, range));
}
else if (!str().is_string(r2) && str().is_unit_string(r1, c1)) {
SASSERT(u().is_char(c1));
// r2 nonground: |r2|=1 & c1 <= e <= r2_0
expr_ref one(m_autil.mk_int(1), m());
expr_ref zero(m_autil.mk_int(0), m());
expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m());
expr_ref r2_0(str().mk_nth_i(r2, zero), m());
range = simplify_path(m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(c1, e), u().mk_le(e, r2_0))));
psi = simplify_path(m().mk_and(path, range));
}
else if (!str().is_string(r1) && !str().is_string(r2)) {
// both r1 and r2 nonground: |r1|=1 & |r2|=1 & r1[0] <= e <= r2[0]
expr_ref one(m_autil.mk_int(1), m());
expr_ref zero(m_autil.mk_int(0), m());
expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m());
expr_ref r1_0(str().mk_nth_i(r1, zero), m());
expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m());
expr_ref r2_0(str().mk_nth_i(r2, zero), m());
range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, r2_0)))));
psi = simplify_path(m().mk_and(path, range));
}
if (m().is_false(psi))
result = nothing();
else
result = re().mk_ite_simplify(range, epsilon(), nothing());
}
else if (re().is_union(r, r1, r2))
result = mk_antimirov_deriv_union(mk_antimirov_deriv(e, r1, path), mk_antimirov_deriv(e, r2, path));
else if (re().is_intersection(r, r1, r2))
result = mk_antimirov_deriv_intersection(
mk_antimirov_deriv(e, r1, path),
mk_antimirov_deriv(e, r2, path), m().mk_true());
else if (re().is_star(r, r1) || re().is_plus(r, r1) || (re().is_loop(r, r1, lo) && 0 <= lo && lo <= 1))
result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_star(r1));
else if (re().is_loop(r, r1, lo))
result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, lo - 1));
else if (re().is_loop(r, r1, lo, hi)) {
if ((lo == 0 && hi == 0) || hi < lo)
result = nothing();
else
result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, (lo == 0 ? 0 : lo - 1), hi - 1));
}
else if (re().is_opt(r, r1))
result = mk_antimirov_deriv(e, r1, path);
else if (re().is_complement(r, r1))
// D(e,~r1) = ~D(e,r1)
result = mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r1, path));
else if (re().is_diff(r, r1, r2))
result = mk_antimirov_deriv_intersection(
mk_antimirov_deriv(e, r1, path),
mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r2, path)), m().mk_true());
else if (re().is_of_pred(r, r1)) {
array_util array(m());
expr* args[2] = { r1, e };
result = array.mk_select(2, args);
// Use mk_der_cond to normalize
result = mk_der_cond(result, e, seq_sort);
}
else
// stuck cases
result = re().mk_derivative(e, r);
}
expr_ref seq_rewriter::mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path) {
sort* seq_sort = nullptr, * ele_sort = nullptr;
VERIFY(m_util.is_re(d1, seq_sort));
VERIFY(m_util.is_seq(seq_sort, ele_sort));
expr_ref result(m());
expr* c, * a, * b;
if (d1 == d2 || re().is_full_seq(d2) || re().is_empty(d1))
result = d1;
else if (re().is_full_seq(d1) || re().is_empty(d2))
result = d2;
else if (m().is_ite(d1, c, a, b)) {
expr_ref path_and_c(simplify_path(m().mk_and(path, c)), m());
expr_ref path_and_notc(simplify_path(m().mk_and(path, m().mk_not(c))), m());
if (m().is_false(path_and_c))
result = mk_antimirov_deriv_intersection(b, d2, path);
else if (m().is_false(path_and_notc))
result = mk_antimirov_deriv_intersection(a, d2, path);
else
result = m().mk_ite(c, mk_antimirov_deriv_intersection(a, d2, path_and_c),
mk_antimirov_deriv_intersection(b, d2, path_and_notc));
}
else if (m().is_ite(d2))
// swap d1 and d2
result = mk_antimirov_deriv_intersection(d2, d1, path);
else if (re().is_union(d1, a, b))
// distribute intersection over the union in d1
result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(a, d2, path), mk_antimirov_deriv_intersection(b, d2, path));
else if (re().is_union(d2, a, b))
// distribute intersection over the union in d2
result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(d1, a, path), mk_antimirov_deriv_intersection(d1, b, path));
else
// in all other cases create the intersection regex
// TODO: flatten, order and merge d1 and d2 to maintain equality under similarity
result = (d1->get_id() <= d2->get_id() ? re().mk_inter(d1, d2) : re().mk_inter(d2, d1));
return result;
}
expr_ref seq_rewriter::mk_antimirov_deriv_concat(expr* d, expr* r) {
expr_ref result(m());
// Take reference count of r and d
expr_ref _r(r, m()), _d(d, m());
expr* c, * t, * e;
if (m().is_ite(d, c, t, e))
result = m().mk_ite(c, mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
else if (re().is_union(d, t, e))
result = re().mk_union(mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
else
result = mk_re_append(d, r);
return result;
}
expr_ref seq_rewriter::mk_antimirov_deriv_negate(expr* d) {
sort* seq_sort = nullptr;
VERIFY(m_util.is_re(d, seq_sort));
auto nothing = [&]() { return expr_ref(re().mk_empty(d->get_sort()), m()); };
auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); };
auto dotstar = [&]() { return expr_ref(re().mk_full_seq(d->get_sort()), m()); };
auto dotplus = [&]() { return expr_ref(re().mk_plus(re().mk_full_char(d->get_sort())), m()); };
expr_ref result(m());
expr* c, * t, * e;
if (re().is_empty(d))
result = dotstar();
else if (re().is_epsilon(d))
result = dotplus();
else if (re().is_full_seq(d))
result = nothing();
else if (re().is_dot_plus(d))
result = epsilon();
else if (m().is_ite(d, c, t, e))
result = m().mk_ite(c, mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
else if (re().is_union(d, t, e))
result = re().mk_inter(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
else if (re().is_intersection(d, t, e))
result = re().mk_union(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
else if (re().is_complement(d, t))
result = t;
else
result = re().mk_complement(d);
return result;
}
expr_ref seq_rewriter::mk_antimirov_deriv_union(expr* d1, expr* d2) {
expr_ref result(m());
if (re().is_empty(d1) || re().is_full_seq(d2))
result = d2;
else if (re().is_empty(d2) || re().is_full_seq(d1))
result = d1;
else if (re().is_dot_plus(d1) && re().get_info(d2).min_length > 0)
result = d1;
else if (re().is_dot_plus(d2) && re().get_info(d1).min_length > 0)
result = d2;
else
// TODO: flatten, order and merge d1 and d2 to maintain equality under similarity
result = (d1->get_id() <= d2->get_id() ? re().mk_union(d1, d2) : re().mk_union(d2, d1));
return result;
}
expr_ref seq_rewriter::mk_regex_reverse(expr* r) {
expr* r1 = nullptr, * r2 = nullptr, * c = nullptr;
unsigned lo = 0, hi = 0;
expr_ref result(m());
if (re().is_empty(r) || re().is_range(r) || re().is_epsilon(r) || re().is_full_seq(r) ||
re().is_full_char(r) || re().is_dot_plus(r) || re().is_of_pred(r))
result = r;
else if (re().is_to_re(r))
result = re().mk_reverse(r);
else if (re().is_reverse(r, r1))
result = r1;
else if (re().is_concat(r, r1, r2))
result = mk_regex_concat(mk_regex_reverse(r2), mk_regex_reverse(r1));
else if (m().is_ite(r, c, r1, r2))
result = m().mk_ite(c, mk_regex_reverse(r1), mk_regex_reverse(r2));
else if (re().is_union(r, r1, r2))
result = re().mk_union(mk_regex_reverse(r1), mk_regex_reverse(r2));
else if (re().is_intersection(r, r1, r2))
result = re().mk_inter(mk_regex_reverse(r1), mk_regex_reverse(r2));
else if (re().is_diff(r, r1, r2))
result = re().mk_diff(mk_regex_reverse(r1), mk_regex_reverse(r2));
else if (re().is_star(r, r1))
result = re().mk_star(mk_regex_reverse(r1));
else if (re().is_plus(r, r1))
result = re().mk_plus(mk_regex_reverse(r1));
else if (re().is_loop(r, r1, lo))
result = re().mk_loop(mk_regex_reverse(r1), lo);
else if (re().is_loop(r, r1, lo, hi))
result = re().mk_loop(mk_regex_reverse(r1), lo, hi);
else if (re().is_opt(r, r1))
result = re().mk_opt(mk_regex_reverse(r1));
else if (re().is_complement(r, r1))
result = re().mk_complement(mk_regex_reverse(r1));
else
//stuck cases: such as r being a regex variable
//observe that re().mk_reverse(to_re(s)) is not a stuck case
result = re().mk_reverse(r);
return result;
}
expr_ref seq_rewriter::mk_regex_concat(expr* r, expr* s) {
sort* seq_sort = nullptr;
VERIFY(m_util.is_re(r, seq_sort));
SASSERT(r->get_sort() == s->get_sort());
expr_ref result(m());
expr* r1, * r2;
if (re().is_epsilon(r) || re().is_empty(s))
result = s;
else if (re().is_epsilon(s) || re().is_empty(r))
result = r;
else if (re().is_full_seq(r) && re().is_full_seq(s))
result = r;
else if (re().is_concat(r, r1, r2))
//create the resulting concatenation in right-associative form
result = mk_regex_concat(r1, mk_regex_concat(r2, s));
else {
//TODO: perhaps simplifiy some further cases such as .*. = ..* = .*.+ = .+.* = .+
result = re().mk_concat(r, s);
}
return result;
}
expr_ref seq_rewriter::mk_in_antimirov(expr* s, expr* d){
expr_ref result(mk_in_antimirov_rec(s, d), m());
return result;
}
expr_ref seq_rewriter::mk_in_antimirov_rec(expr* s, expr* d) {
expr* c, * d1, * d2;
expr_ref result(m());
if (re().is_full_seq(d) || (str().min_length(s) > 0 && re().is_dot_plus(d)))
// s in .* <==> true, also: s in .+ <==> true when |s|>0
result = m().mk_true();
else if (re().is_empty(d) || (str().min_length(s) > 0 && re().is_epsilon(d)))
// s in [] <==> false, also: s in () <==> false when |s|>0
result = m().mk_false();
else if (m().is_ite(d, c, d1, d2))
result = re().mk_ite_simplify(c, mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2));
else if (re().is_union(d, d1, d2))
m_br.mk_or(mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2), result);
else
result = re().mk_in_re(s, d);
return result;
}
/*
path is typically a conjunction of (negated) character equations or constraints that can potentially be simplified
the first element of each equation is assumed to be the element parameter, for example x = (:var 0),
for example a constraint x='a' & x='b' is simplified to false
*/
expr_ref seq_rewriter::simplify_path(expr* path) {
//TODO: more systematic simplifications
expr_ref result(path, m());
expr* h = nullptr, * t = nullptr, * lhs = nullptr, * rhs = nullptr, * h1 = nullptr;
if (m().is_and(path, h, t)) {
if (m().is_true(h))
result = simplify_path(t);
else if (m().is_true(t))
result = simplify_path(h);
else if (m().is_eq(h, lhs, rhs) || (m().is_not(h, h1) && m().is_eq(h1, lhs, rhs)))
elim_condition(lhs, result);
}
return result;
}
expr_ref seq_rewriter::mk_der_antimorov_union(expr* r1, expr* r2) { expr_ref seq_rewriter::mk_der_antimorov_union(expr* r1, expr* r2) {
return mk_der_op(_OP_RE_ANTIMOROV_UNION, r1, r2); return mk_der_op(_OP_RE_ANTIMOROV_UNION, r1, r2);
} }
@ -3016,7 +3427,7 @@ expr_ref seq_rewriter::mk_der_concat(expr* r1, expr* r2) {
} }
/* /*
Utility functions to decide char <, ==, and <=. Utility functions to decide char <, ==, !=, and <=.
Return true if deduced, false if unknown. Return true if deduced, false if unknown.
*/ */
bool seq_rewriter::lt_char(expr* ch1, expr* ch2) { bool seq_rewriter::lt_char(expr* ch1, expr* ch2) {
@ -3027,6 +3438,11 @@ bool seq_rewriter::lt_char(expr* ch1, expr* ch2) {
bool seq_rewriter::eq_char(expr* ch1, expr* ch2) { bool seq_rewriter::eq_char(expr* ch1, expr* ch2) {
return ch1 == ch2; return ch1 == ch2;
} }
bool seq_rewriter::neq_char(expr* ch1, expr* ch2) {
unsigned u1, u2;
return u().is_const_char(ch1, u1) &&
u().is_const_char(ch2, u2) && (u1 != u2);
}
bool seq_rewriter::le_char(expr* ch1, expr* ch2) { bool seq_rewriter::le_char(expr* ch1, expr* ch2) {
return eq_char(ch1, ch2) || lt_char(ch1, ch2); return eq_char(ch1, ch2) || lt_char(ch1, ch2);
} }
@ -3257,10 +3673,10 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) {
default: default:
break; break;
} }
result = m_op_cache.find(k, a, b); result = m_op_cache.find(k, a, b, nullptr);
if (!result) { if (!result) {
result = mk_der_op_rec(k, a, b); result = mk_der_op_rec(k, a, b);
m_op_cache.insert(k, a, b, result); m_op_cache.insert(k, a, b, nullptr, result);
} }
CASSERT("seq_regex", check_deriv_normal_form(result)); CASSERT("seq_regex", check_deriv_normal_form(result));
return result; return result;
@ -3269,7 +3685,7 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) {
expr_ref seq_rewriter::mk_der_compl(expr* r) { expr_ref seq_rewriter::mk_der_compl(expr* r) {
STRACE("seq_verbose", tout << "mk_der_compl: " << mk_pp(r, m()) STRACE("seq_verbose", tout << "mk_der_compl: " << mk_pp(r, m())
<< std::endl;); << std::endl;);
expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr), m()); expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr, nullptr), m());
if (!result) { if (!result) {
expr* c = nullptr, * r1 = nullptr, * r2 = nullptr; expr* c = nullptr, * r1 = nullptr, * r2 = nullptr;
if (re().is_antimorov_union(r, r1, r2)) { if (re().is_antimorov_union(r, r1, r2)) {
@ -3285,7 +3701,7 @@ expr_ref seq_rewriter::mk_der_compl(expr* r) {
} }
else if (BR_FAILED == mk_re_complement(r, result)) else if (BR_FAILED == mk_re_complement(r, result))
result = re().mk_complement(r); result = re().mk_complement(r);
m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, result); m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, nullptr, result);
} }
CASSERT("seq_regex", check_deriv_normal_form(result)); CASSERT("seq_regex", check_deriv_normal_form(result));
return result; return result;
@ -3509,7 +3925,7 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
// construct the term (if (r2 != () and (ele = (last r2)) then reverse(to_re (butlast r2)) else [])) // construct the term (if (r2 != () and (ele = (last r2)) then reverse(to_re (butlast r2)) else []))
// hd = first of reverse(r2) i.e. last of r2 // hd = first of reverse(r2) i.e. last of r2
// tl = rest of reverse(r2) i.e. butlast of r2 // tl = rest of reverse(r2) i.e. butlast of r2
//hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), m_autil.mk_int(1))); //hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), one()));
hd = mk_seq_last(r2); hd = mk_seq_last(r2);
m_br.mk_and(m().mk_not(m().mk_eq(r2, str().mk_empty(seq_sort))), m().mk_eq(hd, ele), result); m_br.mk_and(m().mk_not(m().mk_eq(r2, str().mk_empty(seq_sort))), m().mk_eq(hd, ele), result);
tl = re().mk_to_re(mk_seq_butlast(r2)); tl = re().mk_to_re(mk_seq_butlast(r2));
@ -3537,9 +3953,9 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
return mk_empty(); return mk_empty();
} }
} }
expr* e1 = nullptr, *e2 = nullptr; expr* e1 = nullptr, * e2 = nullptr;
if (str().is_unit(r1, e1) && str().is_unit(r2, e2)) { if (str().is_unit(r1, e1) && str().is_unit(r2, e2)) {
SASSERT(u().is_char(e1)); SASSERT(u().is_char(e1));
// Use mk_der_cond to normalize // Use mk_der_cond to normalize
STRACE("seq_verbose", tout << "deriv range str" << std::endl;); STRACE("seq_verbose", tout << "deriv range str" << std::endl;);
expr_ref p1(u().mk_le(e1, ele), m()); expr_ref p1(u().mk_le(e1, ele), m());
@ -3760,7 +4176,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
(re().is_union(b, b1, eps) && re().is_epsilon(eps)) || (re().is_union(b, b1, eps) && re().is_epsilon(eps)) ||
(re().is_union(b, eps, b1) && re().is_epsilon(eps))) (re().is_union(b, eps, b1) && re().is_epsilon(eps)))
{ {
result = m().mk_ite(m().mk_eq(str().mk_length(a), m_autil.mk_int(0)), result = m().mk_ite(m().mk_eq(str().mk_length(a), zero()),
m().mk_true(), m().mk_true(),
re().mk_in_re(a, b1)); re().mk_in_re(a, b1));
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
@ -3775,8 +4191,10 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
expr_ref hd(m()), tl(m()); expr_ref hd(m()), tl(m());
if (get_head_tail(a, hd, tl)) { if (get_head_tail(a, hd, tl)) {
result = re().mk_in_re(tl, re().mk_derivative(hd, b)); //result = re().mk_in_re(tl, re().mk_derivative(hd, b));
return BR_REWRITE2; //result = re().mk_in_re(tl, mk_derivative(hd, b));
result = mk_in_antimirov(tl, mk_antimirov_deriv(hd, b, m().mk_true()));
return BR_REWRITE_FULL;
} }
if (get_head_tail_reversed(a, hd, tl)) { if (get_head_tail_reversed(a, hd, tl)) {
@ -3791,7 +4209,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
expr_ref len_a(str().mk_length(a), m()); expr_ref len_a(str().mk_length(a), m());
expr_ref len_tl(m_autil.mk_sub(len_a, len_hd), m()); expr_ref len_tl(m_autil.mk_sub(len_a, len_hd), m());
result = m().mk_and(m_autil.mk_ge(len_a, len_hd), result = m().mk_and(m_autil.mk_ge(len_a, len_hd),
re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd), re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd),
re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl)); re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl));
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
} }
@ -3802,7 +4220,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
expr_ref len_hd(m_autil.mk_sub(len_a, len_tl), m()); expr_ref len_hd(m_autil.mk_sub(len_a, len_tl), m());
expr* s = nullptr; expr* s = nullptr;
result = m().mk_and(m_autil.mk_ge(len_a, len_tl), result = m().mk_and(m_autil.mk_ge(len_a, len_tl),
re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd), re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd),
(re().is_to_re(tl, s) ? m().mk_eq(s, str().mk_substr(a, len_hd, len_tl)) : (re().is_to_re(tl, s) ? m().mk_eq(s, str().mk_substr(a, len_hd, len_tl)) :
re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl))); re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl)));
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
@ -3912,6 +4330,10 @@ br_status seq_rewriter::mk_re_concat(expr* a, expr* b, expr_ref& result) {
return BR_REWRITE2; return BR_REWRITE2;
} }
expr* a1 = nullptr, *b1 = nullptr; expr* a1 = nullptr, *b1 = nullptr;
if (re().is_to_re(a, a1) && re().is_to_re(b, b1)) {
result = re().mk_to_re(str().mk_concat(a1, b1));
return BR_DONE;
}
if (re().is_star(a, a1) && re().is_star(b, b1) && a1 == b1) { if (re().is_star(a, a1) && re().is_star(b, b1) && a1 == b1) {
result = a; result = a;
return BR_DONE; return BR_DONE;
@ -5151,15 +5573,15 @@ bool seq_rewriter::reduce_eq_empty(expr* l, expr* r, expr_ref& result) {
if (str().is_extract(r, s, offset, len)) { if (str().is_extract(r, s, offset, len)) {
expr_ref len_s(str().mk_length(s), m()); expr_ref len_s(str().mk_length(s), m());
expr_ref_vector fmls(m()); expr_ref_vector fmls(m());
fmls.push_back(m_autil.mk_lt(offset, m_autil.mk_int(0))); fmls.push_back(m_autil.mk_lt(offset, zero()));
fmls.push_back(m().mk_eq(s, l)); fmls.push_back(m().mk_eq(s, l));
fmls.push_back(m_autil.mk_le(len, m_autil.mk_int(0))); fmls.push_back(m_autil.mk_le(len, zero()));
fmls.push_back(m_autil.mk_le(len_s, offset)); fmls.push_back(m_autil.mk_le(len_s, offset));
result = m().mk_or(fmls); result = m().mk_or(fmls);
return true; return true;
} }
if (str().is_itos(r, s)) { if (str().is_itos(r, s)) {
result = m_autil.mk_lt(s, m_autil.mk_int(0)); result = m_autil.mk_lt(s, zero());
return true; return true;
} }
return false; return false;
@ -5275,19 +5697,20 @@ seq_rewriter::op_cache::op_cache(ast_manager& m):
m_trail(m) m_trail(m)
{} {}
expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b) { expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b, expr* c) {
op_entry e(op, a, b, nullptr); op_entry e(op, a, b, c, nullptr);
m_table.find(e, e); m_table.find(e, e);
return e.r; return e.r;
} }
void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* r) { void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* c, expr* r) {
cleanup(); cleanup();
if (a) m_trail.push_back(a); if (a) m_trail.push_back(a);
if (b) m_trail.push_back(b); if (b) m_trail.push_back(b);
if (c) m_trail.push_back(c);
if (r) m_trail.push_back(r); if (r) m_trail.push_back(r);
m_table.insert(op_entry(op, a, b, r)); m_table.insert(op_entry(op, a, b, c, r));
} }
void seq_rewriter::op_cache::cleanup() { void seq_rewriter::op_cache::cleanup() {

View file

@ -117,20 +117,20 @@ class seq_rewriter {
class op_cache { class op_cache {
struct op_entry { struct op_entry {
decl_kind k; decl_kind k;
expr* a, *b, *r; expr* a, *b, *c, *r;
op_entry(decl_kind k, expr* a, expr* b, expr* r): k(k), a(a), b(b), r(r) {} op_entry(decl_kind k, expr* a, expr* b, expr* c, expr* r): k(k), a(a), b(b), c(c), r(r) {}
op_entry():k(0), a(nullptr), b(nullptr), r(nullptr) {} op_entry():k(0), a(nullptr), b(nullptr), c(nullptr), r(nullptr) {}
}; };
struct hash_entry { struct hash_entry {
unsigned operator()(op_entry const& e) const { unsigned operator()(op_entry const& e) const {
return mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0); return combine_hash(mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0), e.c ? e.c->get_id() : 0);
} }
}; };
struct eq_entry { struct eq_entry {
bool operator()(op_entry const& a, op_entry const& b) const { bool operator()(op_entry const& a, op_entry const& b) const {
return a.k == b.k && a.a == b.a && a.b == b.b; return a.k == b.k && a.a == b.a && a.b == b.b && a.c == b.c;
} }
}; };
@ -143,8 +143,8 @@ class seq_rewriter {
public: public:
op_cache(ast_manager& m); op_cache(ast_manager& m);
expr* find(decl_kind op, expr* a, expr* b); expr* find(decl_kind op, expr* a, expr* b, expr* c);
void insert(decl_kind op, expr* a, expr* b, expr* r); void insert(decl_kind op, expr* a, expr* b, expr* c, expr* r);
}; };
seq_util m_util; seq_util m_util;
@ -208,8 +208,24 @@ class seq_rewriter {
bool check_deriv_normal_form(expr* r, int level = 3); bool check_deriv_normal_form(expr* r, int level = 3);
#endif #endif
void mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result);
expr_ref mk_antimirov_deriv(expr* e, expr* r, expr* path);
expr_ref mk_in_antimirov_rec(expr* s, expr* d);
expr_ref mk_in_antimirov(expr* s, expr* d);
expr_ref mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path);
expr_ref mk_antimirov_deriv_concat(expr* d, expr* r);
expr_ref mk_antimirov_deriv_negate(expr* d);
expr_ref mk_antimirov_deriv_union(expr* d1, expr* d2);
expr_ref mk_regex_reverse(expr* r);
expr_ref mk_regex_concat(expr* r1, expr* r2);
expr_ref simplify_path(expr* path);
bool lt_char(expr* ch1, expr* ch2); bool lt_char(expr* ch1, expr* ch2);
bool eq_char(expr* ch1, expr* ch2); bool eq_char(expr* ch1, expr* ch2);
bool neq_char(expr* ch1, expr* ch2);
bool le_char(expr* ch1, expr* ch2); bool le_char(expr* ch1, expr* ch2);
bool pred_implies(expr* a, expr* b); bool pred_implies(expr* a, expr* b);
bool are_complements(expr* r1, expr* r2) const; bool are_complements(expr* r1, expr* r2) const;
@ -286,6 +302,8 @@ class seq_rewriter {
expr_ref zero() { return expr_ref(m_autil.mk_int(0), m()); } expr_ref zero() { return expr_ref(m_autil.mk_int(0), m()); }
expr_ref one() { return expr_ref(m_autil.mk_int(1), m()); } expr_ref one() { return expr_ref(m_autil.mk_int(1), m()); }
expr_ref minus_one() { return expr_ref(m_autil.mk_int(-1), m()); } expr_ref minus_one() { return expr_ref(m_autil.mk_int(-1), m()); }
expr_ref mk_sub(expr* a, rational const& n);
expr_ref mk_sub(expr* a, unsigned n) { return mk_sub(a, rational(n)); }
bool is_suffix(expr* s, expr* offset, expr* len); bool is_suffix(expr* s, expr* offset, expr* len);
bool is_prefix(expr* s, expr* offset, expr* len); bool is_prefix(expr* s, expr* offset, expr* len);
@ -379,9 +397,19 @@ public:
void add_seqs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_pair_vector& new_eqs); void add_seqs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_pair_vector& new_eqs);
// Expose derivative and nullability check /*
create the nullability check for r
*/
expr_ref is_nullable(expr* r); expr_ref is_nullable(expr* r);
/*
make the derivative of r wrt the given element ele
*/
expr_ref mk_derivative(expr* ele, expr* r); expr_ref mk_derivative(expr* ele, expr* r);
/*
make the derivative of r wrt the canonical variable v0 = (:var 0),
for example mk_derivative(a+) = (if (v0 = 'a') then a* else [])
*/
expr_ref mk_derivative(expr* r);
// heuristic elimination of element from condition that comes form a derivative. // heuristic elimination of element from condition that comes form a derivative.
// special case optimization for conjunctions of equalities, disequalities and ranges. // special case optimization for conjunctions of equalities, disequalities and ranges.

View file

@ -28,8 +28,8 @@ skolem::skolem(ast_manager& m, th_rewriter& rw):
m_tail = "seq.tail"; m_tail = "seq.tail";
m_seq_first = "seq.first"; m_seq_first = "seq.first";
m_seq_last = "seq.last"; m_seq_last = "seq.last";
m_indexof_left = "seq.idx.left"; m_indexof_left = "seq.idx.l";
m_indexof_right = "seq.idx.right"; m_indexof_right = "seq.idx.r";
m_aut_step = "aut.step"; m_aut_step = "aut.step";
m_pre = "seq.pre"; // (seq.pre s l): prefix of string s of length l m_pre = "seq.pre"; // (seq.pre s l): prefix of string s of length l
m_post = "seq.post"; // (seq.post s l): suffix of string s of length k, based on extract starting at index i of length l m_post = "seq.post"; // (seq.post s l): suffix of string s of length k, based on extract starting at index i of length l

View file

@ -77,8 +77,10 @@ namespace seq {
expr_ref mk_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_left, t, s, offset); } expr_ref mk_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_left, t, s, offset); }
expr_ref mk_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_right, t, s, offset); } expr_ref mk_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_right, t, s, offset); }
expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_left", t, s, offset); } expr_ref mk_contains_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.l", t, s, offset); }
expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_right", t, s, offset); } expr_ref mk_contains_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.r", t, s, offset); }
expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.l", t, s, offset); }
expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.r", t, s, offset); }
expr_ref mk_tail(expr* s, expr* i) { return mk(m_tail, s, i); } expr_ref mk_tail(expr* s, expr* i) { return mk(m_tail, s, i); }
expr_ref mk_post(expr* s, expr* i) { return mk(m_post, s, i); } expr_ref mk_post(expr* s, expr* i) { return mk(m_post, s, i); }

View file

@ -839,7 +839,7 @@ bool seq_util::str::is_nth_i(expr const* n, expr*& s, unsigned& idx) const {
return arith_util(m).is_unsigned(i, idx); return arith_util(m).is_unsigned(i, idx);
} }
app* seq_util::str::mk_nth_i(expr* s, unsigned i) const { app* seq_util::str::mk_nth_c(expr* s, unsigned i) const {
return mk_nth_i(s, arith_util(m).mk_int(i)); return mk_nth_i(s, arith_util(m).mk_int(i));
} }
@ -854,6 +854,48 @@ void seq_util::str::get_concat(expr* e, expr_ref_vector& es) const {
} }
} }
/*
Returns true if s is an expression of the form (l = |u|) |u|-k or (-k)+|u| or |u|+(-k).
Also returns true and assigns k=0 and l=s if s is |u|.
*/
bool seq_util::str::is_len_sub(expr const* s, expr*& l, expr*& u, rational& k) const {
expr* x;
rational v;
arith_util a(m);
if (is_length(s, l)) {
k = 0;
return true;
}
else if (a.is_sub(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonneg()) {
k = v;
return true;
}
else if (a.is_add(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) {
k = - v;
return true;
}
else if (a.is_add(s, x, l) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) {
k = - v;
return true;
}
else
return false;
}
bool seq_util::str::is_unit_string(expr const* s, expr_ref& c) const {
zstring z;
expr* ch = nullptr;
if (is_string(s, z) && z.length() == 1) {
c = mk_char(z[0]);
return true;
}
else if (is_unit(s, ch)) {
c = ch;
return true;
}
return false;
}
void seq_util::str::get_concat_units(expr* e, expr_ref_vector& es) const { void seq_util::str::get_concat_units(expr* e, expr_ref_vector& es) const {
expr* e1, *e2; expr* e1, *e2;
while (is_concat(e, e1, e2)) { while (is_concat(e, e1, e2)) {
@ -876,8 +918,6 @@ app* seq_util::str::mk_is_empty(expr* s) const {
return m.mk_eq(s, mk_empty(s->get_sort())); return m.mk_eq(s, mk_empty(s->get_sort()));
} }
unsigned seq_util::str::min_length(expr* s) const { unsigned seq_util::str::min_length(expr* s) const {
SASSERT(u.is_seq(s)); SASSERT(u.is_seq(s));
unsigned result = 0; unsigned result = 0;
@ -892,7 +932,10 @@ unsigned seq_util::str::min_length(expr* s) const {
return 0u; return 0u;
}; };
while (is_concat(s, s1, s2)) { while (is_concat(s, s1, s2)) {
result += get_length(s1); if (is_concat(s1))
result += min_length(s1);
else
result += get_length(s1);
s = s2; s = s2;
} }
result += get_length(s); result += get_length(s);
@ -920,7 +963,10 @@ unsigned seq_util::str::max_length(expr* s) const {
return UINT_MAX; return UINT_MAX;
}; };
while (is_concat(s, s1, s2)) { while (is_concat(s, s1, s2)) {
result = u.max_plus(get_length(s1), result); if (is_concat(s1))
result = u.max_plus(max_length(s1), result);
else
result = u.max_plus(get_length(s1), result);
s = s2; s = s2;
} }
result = u.max_plus(get_length(s), result); result = u.max_plus(get_length(s), result);
@ -1065,38 +1111,70 @@ app* seq_util::rex::mk_epsilon(sort* seq_sort) {
/* /*
Produces compact view of concrete concatenations such as (abcd). Produces compact view of concrete concatenations such as (abcd).
*/ */
std::ostream& seq_util::rex::pp::compact_helper_seq(std::ostream& out, expr* s) const { bool seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const {
SASSERT(re.u.is_seq(s));
zstring z; zstring z;
expr* x, * j, * k, * l, * i, * x_;
if (re.u.str.is_empty(s)) if (re.u.str.is_empty(s))
out << "()"; out << "()";
else if (re.u.str.is_unit(s))
seq_unit(out, s);
else if (re.u.str.is_concat(s)) { else if (re.u.str.is_concat(s)) {
expr_ref_vector es(re.m); expr_ref_vector es(re.m);
re.u.str.get_concat(s, es); re.u.str.get_concat(s, es);
for (expr* e : es) for (expr* e : es)
compact_helper_seq(out, e); print(out, e);
} }
else if (re.u.str.is_string(s, z)) { else if (re.u.str.is_string(s, z)) {
for (unsigned i = 0; i < z.length(); i++) for (unsigned i = 0; i < z.length(); i++)
out << (char)z[i]; out << (char)z[i];
} }
//using braces to indicate 'full' output else if (re.u.str.is_at(s, x, i))
//for example an uninterpreted constant X will be printed as {X} print(out, x) << "@", print(out, i);
//while a unit sequence "X" will be printed as X else if (re.u.str.is_extract(s, x, j, k)) {
//thus for example (concat "X" "Y" Z "W") where Z is uninterpreted is printed as XY{Z}W rational jv, iv;
else out << "{" << mk_pp(s, re.m) << "}"; print(out, x);
return out; if (arith_util(re.m).is_numeral(j, jv)) {
if (arith_util(re.m).is_numeral(k, iv)) {
// output X[j,k]
out << "[" << jv.get_int32() << "," << jv.get_int32() << "]";
}
else if (arith_util(re.m).is_sub(k, l, i) && re.u.str.is_length(l, x_) && x == x_ &&
arith_util(re.m).is_numeral(i, iv) && iv == jv) {
// case X[j,|X|-j] is denoted by X[j..]
out << "[" << jv.get_int32() << "..]";
}
else if (((arith_util(re.m).is_add(k, l, i) && re.u.str.is_length(l, x_)) ||
(arith_util(re.m).is_add(k, i, l) && re.u.str.is_length(l, x_))) && x == x_ &&
arith_util(re.m).is_numeral(i, iv) && iv.get_int32() + jv.get_int32() == 0) {
// case X[j,|X|-j] is denoted by X[j..]
out << "[" << jv.get_int32() << "..]";
}
else {
out << "[" << jv.get_int32() << ",";
print(out, k);
out << "]";
}
}
else {
out << "[";
print(out, j);
out << ",";
print(out, k);
out << "]";
}
}
else
return false;
return true;
} }
/* /*
Produces output such as [a-z] for a range. Produces output such as [a-z] for a range.
*/ */
std::ostream& seq_util::rex::pp::compact_helper_range(std::ostream& out, expr* s1, expr* s2) const { std::ostream& seq_util::rex::pp::print_range(std::ostream& out, expr* s1, expr* s2) const {
out << "["; out << "[";
seq_unit(out, s1) << "-"; print(out, s1);
seq_unit(out, s2) << "]"; out << "-";
print(out, s2);
out << "]";
return out; return out;
} }
@ -1111,9 +1189,10 @@ bool seq_util::rex::pp::can_skip_parenth(expr* r) const {
/* /*
Specialize output for a unit sequence converting to visible ASCII characters if possible. Specialize output for a unit sequence converting to visible ASCII characters if possible.
*/ */
std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const { bool seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const {
expr* e; expr* e, * i;
unsigned n = 0; unsigned n = 0;
if ((re.u.str.is_unit(s, e) && re.u.is_const_char(e, n)) || re.u.is_const_char(s, n)) { if ((re.u.str.is_unit(s, e) && re.u.is_const_char(e, n)) || re.u.is_const_char(s, n)) {
char c = (char)n; char c = (char)n;
if (c == '\n') if (c == '\n')
@ -1122,22 +1201,21 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
out << "\\r"; out << "\\r";
else if (c == '\f') else if (c == '\f')
out << "\\f"; out << "\\f";
else if (c == ' ') else if (32 <= n && n < 127 && n != '\"' && n != ' '
out << "\\s"; && n != '\\' && n != '\'' && n != '?' && n != '.' && n != '(' && n != ')' && n != '[' && n != ']'
else if (c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || c == '.' || c == '\\') && n != '{' && n != '}' && n != '&') {
out << "\\" << c;
else if (32 < n && n < 127) {
if (html_encode) { if (html_encode) {
if (c == '<') if (c == '<')
out << "&lt;"; out << "&lt;";
else if (c == '>') else if (c == '>')
out << "&gt;"; out << "&gt;";
else if (c == '&') //else if (c == '&')
out << "&amp;"; // out << "&amp;";
else if (c == '\"') //else if (c == '\"')
out << "&quot;"; // out << "&quot;";
else else
out << "\\x" << std::hex << n; //out << "\\x" << std::hex << n;
out << c;
} }
else else
out << c; out << c;
@ -1151,92 +1229,190 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
else else
out << "\\u" << std::hex << n; out << "\\u" << std::hex << n;
} }
else if (re.u.str.is_nth_i(s, e, i)) {
print(out, e) << "[";
print(out, i) << "]";
}
else if (re.u.str.is_length(s, e))
print(out << "|", e) << "|";
else else
out << "{" << mk_pp(s, re.m) << "}"; return false;
return out; return true;
} }
/* /*
Pretty prints the regex r into the out stream Pretty prints the regex r into the ostream out
*/ */
std::ostream& seq_util::rex::pp::display(std::ostream& out) const { std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const {
expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr; expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr;
unsigned lo = 0, hi = 0; unsigned lo = 0, hi = 0;
if (re.u.is_char(e)) arith_util a(re.m);
return seq_unit(out, e); rational v;
else if (re.u.is_seq(e)) if (!e)
return compact_helper_seq(out, e); out << "null";
else if (print_unit(out, e))
;
else if (print_seq(out, e))
;
else if (re.is_full_char(e)) else if (re.is_full_char(e))
return out << "."; out << ".";
else if (re.is_full_seq(e)) else if (re.is_full_seq(e))
return out << ".*"; out << ".*";
else if (re.is_to_re(e, s)) else if (re.is_to_re(e, s))
return compact_helper_seq(out, s); print(out, s);
else if (re.is_range(e, s, s2)) else if (re.is_range(e, s, s2))
return compact_helper_range(out, s, s2); print_range(out, s, s2);
else if (re.is_epsilon(e)) else if (re.is_epsilon(e))
return out << "()"; // &#x03B5; = epsilon
out << (html_encode ? "&#x03B5;" : "()");
else if (re.is_empty(e)) else if (re.is_empty(e))
return out << "[]"; // &#x2205; = emptyset
else if (re.is_concat(e, r1, r2)) out << (html_encode ? "&#x2205;" : "[]");
return out << pp(re, r1) << pp(re, r2); else if (re.is_concat(e, r1, r2)) {
else if (re.is_union(e, r1, r2)) print(out, r1);
return out << "(" << pp(re, r1) << "|" << pp(re, r2) << ")"; print(out, r2);
else if (re.is_intersection(e, r1, r2)) }
return out << "(" << pp(re, r1) << "&amp;" /*(html_encode ? ")&amp;(" : ")&(")*/ << pp(re, r2) << ")"; else if (re.is_antimorov_union(e, r1, r2) || re.is_union(e, r1, r2)) {
out << "(";
print(out, r1);
out << (html_encode ? "&#x22C3;" : "|");
print(out, r2);
out << ")";
}
else if (re.is_intersection(e, r1, r2)) {
out << "(";
print(out, r1);
out << (html_encode ? "&#x22C2;" : "&");
print(out, r2);
out << ")";
}
else if (re.is_complement(e, r1)) { else if (re.is_complement(e, r1)) {
out << "~";
if (can_skip_parenth(r1)) if (can_skip_parenth(r1))
return out << "~" << pp(re, r1); print(out, r1);
else else {
return out << "~(" << pp(re, r1) << ")"; out << "(";
print(out, r1);
out << ")";
}
} }
else if (re.is_plus(e, r1)) { else if (re.is_plus(e, r1)) {
if (can_skip_parenth(r1)) if (can_skip_parenth(r1)) {
return out << pp(re, r1) << "+"; print(out, r1);
else out << "+";
return out << "(" << pp(re, r1) << ")+"; }
else {
out << "(";
print(out, r1);
out << ")+";
}
} }
else if (re.is_star(e, r1)) { else if (re.is_star(e, r1)) {
if (can_skip_parenth(r1)) if (can_skip_parenth(r1)) {
return out << pp(re, r1) << "*"; print(out, r1);
else out << "*";
return out << "(" << pp(re, r1) << ")*"; }
else {
out << "(";
print(out, r1);
out << ")*";
}
} }
else if (re.is_loop(e, r1, lo)) { else if (re.is_loop(e, r1, lo)) {
if (can_skip_parenth(r1)) if (can_skip_parenth(r1))
return out << pp(re, r1) << "{" << lo << ",}"; print(out, r1) << "{" << lo << ",}";
else else {
return out << "(" << pp(re, r1) << "){" << lo << ",}"; out << "(";
print(out, r1);
out << "){" << lo << ",}";
}
} }
else if (re.is_loop(e, r1, lo, hi)) { else if (re.is_loop(e, r1, lo, hi)) {
if (can_skip_parenth(r1)) { if (can_skip_parenth(r1)) {
print(out, r1);
if (lo == hi) if (lo == hi)
return out << pp(re, r1) << "{" << lo << "}"; out << "{" << lo << "}";
else else
return out << pp(re, r1) << "{" << lo << "," << hi << "}"; out << "{" << lo << "," << hi << "}";
} }
else { else {
out << "(";
print(out, r1);
if (lo == hi) if (lo == hi)
return out << "(" << pp(re, r1) << "){" << lo << "}"; out << "){" << lo << "}";
else else
return out << "(" << pp(re, r1) << "){" << lo << "," << hi << "}"; out << "){" << lo << "," << hi << "}";
} }
} }
else if (re.is_diff(e, r1, r2)) else if (re.is_diff(e, r1, r2)) {
return out << "(" << pp(re, r1) << ")\\(" << pp(re, r2) << ")"; out << "(";
else if (re.m.is_ite(e, s, r1, r2)) print(out, r1);
return out << "if(" << mk_pp(s, re.m) << "," << pp(re, r1) << "," << pp(re, r2) << ")"; out << ")\\(";
print(out, r2);
out << ")";
}
else if (re.m.is_ite(e, s, r1, r2)) {
out << (html_encode ? "(&#x1D422;&#x1D41F; " : "(if ");
print(out, s);
out << (html_encode ? " &#x1D42D;&#x1D5F5;&#x1D41E;&#x1D427; " : " then ");
print(out, r1);
out << (html_encode ? " &#x1D41E;&#x1D425;&#x1D600;&#x1D41E; " : " else ");
print(out, r2);
out << ")";
}
else if (re.is_opt(e, r1)) { else if (re.is_opt(e, r1)) {
if (can_skip_parenth(r1)) if (can_skip_parenth(r1))
return out << pp(re, r1) << "?"; print(out, r1) << "?";
else else {
return out << "(" << pp(re, r1) << ")?"; out << "(";
print(out, r1);
out << ")?";
}
}
else if (re.is_reverse(e, r1)) {
out << "(reverse ";
print(out, r1);
out << ")";
}
else if (re.m.is_eq(e, r1, r2)) {
out << "(";
print(out, r1);
out << " = ";
print(out, r2);
out << ")";
}
else if (re.m.is_not(e, r1)) {
out << "!";
print(out, r1);
}
else if (a.is_add(e, s, s2) && a.is_numeral(s, v) && v < 0)
print(out, s2) << " - " << -v;
else if (a.is_add(e, s, s2) && a.is_numeral(s2, v) && v < 0)
print(out, s) << " - " << -v;
else if (a.is_add(e, s, s2))
print(out, s) << " + ", print(out, s2);
else if (a.is_sub(e, s, s2) && a.is_numeral(s2, v) && v > 0)
print(out, s) << " - " << v;
else if (a.is_le(e, s, s2))
print(out << "(", s) << " <= ", print(out, s2) << ")";
else if (re.m.is_value(e))
out << mk_pp(e, re.m);
else if (is_app(e) && to_app(e)->get_num_args() == 0)
out << mk_pp(e, re.m);
else if (is_app(e)) {
out << "(" << to_app(e)->get_decl()->get_name();
for (expr* arg : *to_app(e))
print(out << " ", arg);
out << ")";
} }
else if (re.is_reverse(e, r1))
return out << "reverse(" << pp(re, r1) << ")";
else else
// Else: derivative or is_of_pred // for all remaining cases use the default pretty printer
return out << "{" << mk_pp(e, re.m) << "}"; out << mk_pp(e, re.m);
return out;
}
std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
return print(out, ex);
} }
/* /*
@ -1244,7 +1420,16 @@ std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
*/ */
std::string seq_util::rex::to_str(expr* r) const { std::string seq_util::rex::to_str(expr* r) const {
std::ostringstream out; std::ostringstream out;
out << pp(u.re, r); pp(u.re, r, false).display(out);
return out.str();
}
/*
Pretty prints the regex r into the output string that is htmlencoded
*/
std::string seq_util::rex::to_strh(expr* r) const {
std::ostringstream out;
pp(u.re, r, true).display(out);
return out.str(); return out.str();
} }
@ -1290,7 +1475,7 @@ seq_util::rex::info seq_util::rex::get_info_rec(expr* e) const {
else else
result = mk_info_rec(to_app(e)); result = mk_info_rec(to_app(e));
m_infos.setx(e->get_id(), result, invalid_info); m_infos.setx(e->get_id(), result, invalid_info);
STRACE("re_info", tout << "compute_info(" << pp(u.re, e) << ")=" << result << std::endl;); STRACE("re_info", tout << "compute_info(" << pp(u.re, e, false) << ")=" << result << std::endl;);
return result; return result;
} }
@ -1518,7 +1703,13 @@ seq_util::rex::info seq_util::rex::info::orelse(seq_util::rex::info const& i) co
// unsigned ite_min_length = std::min(min_length, i.min_length); // unsigned ite_min_length = std::min(min_length, i.min_length);
// lbool ite_nullable = (nullable == i.nullable ? nullable : l_undef); // lbool ite_nullable = (nullable == i.nullable ? nullable : l_undef);
// TBD: whether ite is interpreted or not depends on whether the condition is interpreted and both branches are interpreted // TBD: whether ite is interpreted or not depends on whether the condition is interpreted and both branches are interpreted
return info(false, false, false, false, normalized && i.normalized, monadic && i.monadic, singleton && i.singleton, nullable, std::min(min_length, i.min_length), std::max(star_height, i.star_height)); return info(false, false, false, false,
normalized && i.normalized,
monadic && i.monadic,
singleton && i.singleton,
((nullable == l_true && i.nullable == l_true) ? l_true : ((nullable == l_false && i.nullable == l_false) ? l_false : l_undef)),
std::min(min_length, i.min_length),
std::max(star_height, i.star_height));
} }
else else
return i; return i;

View file

@ -286,7 +286,8 @@ public:
app* mk_at(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_AT, 2, es); } app* mk_at(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_AT, 2, es); }
app* mk_nth(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH, 2, es); } app* mk_nth(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH, 2, es); }
app* mk_nth_i(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_I, 2, es); } app* mk_nth_i(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_I, 2, es); }
app* mk_nth_i(expr* s, unsigned i) const; app* mk_nth_u(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_U, 2, es); }
app* mk_nth_c(expr* s, unsigned i) const;
app* mk_substr(expr* a, expr* b, expr* c) const { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); } app* mk_substr(expr* a, expr* b, expr* c) const { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); }
app* mk_contains(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONTAINS, 2, es); } app* mk_contains(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONTAINS, 2, es); }
@ -350,6 +351,13 @@ public:
bool is_from_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_FROM_CODE); } bool is_from_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_FROM_CODE); }
bool is_to_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_TO_CODE); } bool is_to_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_TO_CODE); }
bool is_len_sub(expr const* n, expr*& l, expr*& u, rational& k) const;
/*
tests if s is a single character string(c) or a unit (c)
*/
bool is_unit_string(expr const* s, expr_ref& c) const;
bool is_string_term(expr const * n) const { bool is_string_term(expr const * n) const {
return u.is_string(n->get_sort()); return u.is_string(n->get_sort());
} }
@ -530,7 +538,20 @@ public:
bool is_loop(expr const* n) const { return is_app_of(n, m_fid, OP_RE_LOOP); } bool is_loop(expr const* n) const { return is_app_of(n, m_fid, OP_RE_LOOP); }
bool is_empty(expr const* n) const { return is_app_of(n, m_fid, OP_RE_EMPTY_SET); } bool is_empty(expr const* n) const { return is_app_of(n, m_fid, OP_RE_EMPTY_SET); }
bool is_full_char(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_CHAR_SET); } bool is_full_char(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_CHAR_SET); }
bool is_full_seq(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET); } bool is_full_seq(expr const* n) const {
expr* s;
return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET) || (is_star(n, s) && is_full_char(s));
}
bool is_dot_plus(expr const* n) const {
expr* s, * t;
if (is_plus(n, s) && is_full_char(s))
return true;
if (is_concat(n, s, t)) {
if ((is_full_char(s) && is_full_seq(t)) || (is_full_char(t) && is_full_seq(s)))
return true;
}
return false;
}
bool is_of_pred(expr const* n) const { return is_app_of(n, m_fid, OP_RE_OF_PRED); } bool is_of_pred(expr const* n) const { return is_app_of(n, m_fid, OP_RE_OF_PRED); }
bool is_reverse(expr const* n) const { return is_app_of(n, m_fid, OP_RE_REVERSE); } bool is_reverse(expr const* n) const { return is_app_of(n, m_fid, OP_RE_REVERSE); }
bool is_derivative(expr const* n) const { return is_app_of(n, m_fid, OP_RE_DERIVATIVE); } bool is_derivative(expr const* n) const { return is_app_of(n, m_fid, OP_RE_DERIVATIVE); }
@ -559,18 +580,32 @@ public:
app* mk_epsilon(sort* seq_sort); app* mk_epsilon(sort* seq_sort);
info get_info(expr* r) const; info get_info(expr* r) const;
std::string to_str(expr* r) const; std::string to_str(expr* r) const;
std::string to_strh(expr* r) const;
expr_ref mk_ite_simplify(expr* c, expr* t, expr* e)
{
expr_ref result(m);
if (m.is_true(c) || t == e)
result = t;
else if (m.is_false(c))
result = e;
else
result = m.mk_ite(c, t, e);
return result;
}
class pp { class pp {
seq_util::rex& re; seq_util::rex& re;
expr* e; expr* ex;
bool html_encode; bool html_encode;
bool can_skip_parenth(expr* r) const; bool can_skip_parenth(expr* r) const;
std::ostream& seq_unit(std::ostream& out, expr* s) const; bool print_unit(std::ostream& out, expr* s) const;
std::ostream& compact_helper_seq(std::ostream& out, expr* s) const; bool print_seq(std::ostream& out, expr* s) const;
std::ostream& compact_helper_range(std::ostream& out, expr* s1, expr* s2) const; std::ostream& print_range(std::ostream& out, expr* s1, expr* s2) const;
std::ostream& print(std::ostream& out, expr* e) const;
public: public:
pp(seq_util::rex& r, expr* e, bool html = false) : re(r), e(e), html_encode(html) {} pp(seq_util::rex& re, expr* ex, bool html) : re(re), ex(ex), html_encode(html) {}
std::ostream& display(std::ostream&) const; std::ostream& display(std::ostream&) const;
}; };
}; };

View file

@ -87,6 +87,7 @@ public:
bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); } bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); }
sr_property get_property(func_decl* f) const; sr_property get_property(func_decl* f) const;
sr_property get_property(app* e) const { return get_property(e->get_decl()); } sr_property get_property(app* e) const { return get_property(e->get_decl()); }
func_decl* get_relation(func_decl* f) const { SASSERT(is_special_relation(f)); return to_func_decl(f->get_parameter(0).get_ast()); }
func_decl* mk_to_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_TO); } func_decl* mk_to_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_TO); }
func_decl* mk_po_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_PO); } func_decl* mk_po_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_PO); }

View file

@ -1828,9 +1828,8 @@ void cmd_context::add_declared_functions(model& mdl) {
mdl.register_decl(f, fi); mdl.register_decl(f, fi);
} }
} }
mdl.add_rec_funs();
} }
mdl.add_rec_funs();
} }
void cmd_context::display_sat_result(lbool r) { void cmd_context::display_sat_result(lbool r) {

View file

@ -270,6 +270,7 @@ public:
class check_value { class check_value {
public: public:
virtual bool operator()(Value const& v) = 0; virtual bool operator()(Value const& v) = 0;
virtual ~check_value() = default;
}; };
bool find_le(Key const* keys, check_value& check) { bool find_le(Key const* keys, check_value& check) {

View file

@ -23,6 +23,7 @@ Revision History:
namespace lp { namespace lp {
class column_namer { class column_namer {
public: public:
virtual ~column_namer() = default;
virtual std::string get_variable_name(unsigned j) const = 0; virtual std::string get_variable_name(unsigned j) const = 0;
template <typename T> template <typename T>
std::ostream & print_row(const row_strip<T> & row, std::ostream & out) const { std::ostream & print_row(const row_strip<T> & row, std::ostream & out) const {

View file

@ -120,6 +120,8 @@ struct factorization_factory {
m_vars(vars), m_monic(m) { m_vars(vars), m_monic(m) {
} }
virtual ~factorization_factory() = default;
bool_vector get_mask() const { bool_vector get_mask() const {
// we keep the last element always in the first factor to avoid // we keep the last element always in the first factor to avoid
// repeating a pair twice, that is why m_mask is shorter by one then m_vars // repeating a pair twice, that is why m_mask is shorter by one then m_vars

View file

@ -16,6 +16,9 @@ namespace lp {
lp_settings const& lar_solver::settings() const { return m_settings; } lp_settings const& lar_solver::settings() const { return m_settings; }
statistics& lar_solver::stats() { return m_settings.stats(); }
void lar_solver::updt_params(params_ref const& _p) { void lar_solver::updt_params(params_ref const& _p) {
smt_params_helper p(_p); smt_params_helper p(_p);
set_track_pivoted_rows(p.arith_bprop_on_pivoted_rows()); set_track_pivoted_rows(p.arith_bprop_on_pivoted_rows());
@ -23,17 +26,9 @@ namespace lp {
m_settings.updt_params(_p); m_settings.updt_params(_p);
} }
void clear() {
lp_assert(false); // not implemented
}
lar_solver::lar_solver() : lar_solver::lar_solver() :
m_status(lp_status::UNKNOWN),
m_crossed_bounds_column(-1), m_crossed_bounds_column(-1),
m_mpq_lar_core_solver(m_settings, *this), m_mpq_lar_core_solver(m_settings, *this),
m_int_solver(nullptr),
m_need_register_terms(false),
m_var_register(false), m_var_register(false),
m_term_register(true), m_term_register(true),
m_constraints(*this) {} m_constraints(*this) {}
@ -197,11 +192,11 @@ namespace lp {
void lar_solver::set_status(lp_status s) { m_status = s; } void lar_solver::set_status(lp_status s) { m_status = s; }
lp_status lar_solver::find_feasible_solution() { lp_status lar_solver::find_feasible_solution() {
m_settings.stats().m_make_feasible++; stats().m_make_feasible++;
if (A_r().column_count() > m_settings.stats().m_max_cols) if (A_r().column_count() > stats().m_max_cols)
m_settings.stats().m_max_cols = A_r().column_count(); stats().m_max_cols = A_r().column_count();
if (A_r().row_count() > m_settings.stats().m_max_rows) if (A_r().row_count() > stats().m_max_rows)
m_settings.stats().m_max_rows = A_r().row_count(); stats().m_max_rows = A_r().row_count();
if (strategy_is_undecided()) if (strategy_is_undecided())
decide_on_strategy_and_adjust_initial_state(); decide_on_strategy_and_adjust_initial_state();
@ -269,9 +264,8 @@ namespace lp {
m_crossed_bounds_column.pop(k); m_crossed_bounds_column.pop(k);
unsigned n = m_columns_to_ul_pairs.peek_size(k); unsigned n = m_columns_to_ul_pairs.peek_size(k);
m_var_register.shrink(n); m_var_register.shrink(n);
if (m_settings.use_tableau()) { if (m_settings.use_tableau())
pop_tableau(); pop_tableau();
}
lp_assert(A_r().column_count() == n); lp_assert(A_r().column_count() == n);
TRACE("lar_solver_details", TRACE("lar_solver_details",
for (unsigned j = 0; j < n; j++) { for (unsigned j = 0; j < n; j++) {
@ -285,6 +279,10 @@ namespace lp {
clean_popped_elements(n, m_columns_with_changed_bounds); clean_popped_elements(n, m_columns_with_changed_bounds);
clean_popped_elements(n, m_incorrect_columns); clean_popped_elements(n, m_incorrect_columns);
for (auto rid : m_row_bounds_to_replay)
insert_row_with_changed_bounds(rid);
m_row_bounds_to_replay.reset();
unsigned m = A_r().row_count(); unsigned m = A_r().row_count();
clean_popped_elements(m, m_rows_with_changed_bounds); clean_popped_elements(m, m_rows_with_changed_bounds);
clean_inf_set_of_r_solver_after_pop(); clean_inf_set_of_r_solver_after_pop();
@ -633,6 +631,9 @@ namespace lp {
left_side.push_back(std::make_pair(p.second, p.first)); left_side.push_back(std::make_pair(p.second, p.first));
} }
void lar_solver::insert_row_with_changed_bounds(unsigned rid) {
m_rows_with_changed_bounds.insert(rid);
}
void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) {
if (A_r().row_count() != m_column_buffer.data_size()) if (A_r().row_count() != m_column_buffer.data_size())
@ -643,14 +644,14 @@ namespace lp {
m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer);
for (unsigned i : m_column_buffer.m_index) for (unsigned i : m_column_buffer.m_index)
m_rows_with_changed_bounds.insert(i); insert_row_with_changed_bounds(i);
} }
void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) {
for (auto& rc : m_mpq_lar_core_solver.m_r_A.m_columns[j]) for (auto& rc : m_mpq_lar_core_solver.m_r_A.m_columns[j])
m_rows_with_changed_bounds.insert(rc.var()); insert_row_with_changed_bounds(rc.var());
} }
bool lar_solver::use_tableau() const { return m_settings.use_tableau(); } bool lar_solver::use_tableau() const { return m_settings.use_tableau(); }
@ -743,7 +744,7 @@ namespace lp {
void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) {
if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) {
m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]);
return; return;
} }
@ -793,7 +794,7 @@ namespace lp {
update_x_and_inf_costs_for_columns_with_changed_bounds(); update_x_and_inf_costs_for_columns_with_changed_bounds();
m_mpq_lar_core_solver.solve(); m_mpq_lar_core_solver.solve();
set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); set_status(m_mpq_lar_core_solver.m_r_solver.get_status());
lp_assert(((m_settings.stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold()); lp_assert(((stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold());
} }
@ -974,9 +975,7 @@ namespace lp {
bool lar_solver::the_left_sides_sum_to_zero(const vector<std::pair<mpq, unsigned>>& evidence) const { bool lar_solver::the_left_sides_sum_to_zero(const vector<std::pair<mpq, unsigned>>& evidence) const {
std::unordered_map<var_index, mpq> coeff_map; std::unordered_map<var_index, mpq> coeff_map;
for (auto& it : evidence) { for (auto const & [coeff, con_ind] : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
lp_assert(m_constraints.valid_index(con_ind)); lp_assert(m_constraints.valid_index(con_ind));
register_in_map(coeff_map, m_constraints[con_ind], coeff); register_in_map(coeff_map, m_constraints[con_ind], coeff);
} }
@ -1337,7 +1336,7 @@ namespace lp {
void lar_solver::mark_rows_for_bound_prop(lpvar j) { void lar_solver::mark_rows_for_bound_prop(lpvar j) {
auto& column = A_r().m_columns[j]; auto& column = A_r().m_columns[j];
for (auto const& r : column) for (auto const& r : column)
m_rows_with_changed_bounds.insert(r.var()); insert_row_with_changed_bounds(r.var());
} }
@ -1659,7 +1658,7 @@ namespace lp {
m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size()); m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size());
m_mpq_lar_core_solver.m_r_basis.push_back(j); m_mpq_lar_core_solver.m_r_basis.push_back(j);
if (m_settings.bound_propagation()) if (m_settings.bound_propagation())
m_rows_with_changed_bounds.insert(A_r().row_count() - 1); insert_row_with_changed_bounds(A_r().row_count() - 1);
} }
else { else {
m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast<int>(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1); m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast<int>(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1);
@ -1755,7 +1754,7 @@ namespace lp {
if (use_tableau() && !coeffs.empty()) { if (use_tableau() && !coeffs.empty()) {
add_row_from_term_no_constraint(m_terms.back(), ret); add_row_from_term_no_constraint(m_terms.back(), ret);
if (m_settings.bound_propagation()) if (m_settings.bound_propagation())
m_rows_with_changed_bounds.insert(A_r().row_count() - 1); insert_row_with_changed_bounds(A_r().row_count() - 1);
} }
lp_assert(m_var_register.size() == A_r().column_count()); lp_assert(m_var_register.size() == A_r().column_count());
if (m_need_register_terms) { if (m_need_register_terms) {

View file

@ -76,13 +76,13 @@ class lar_solver : public column_namer {
//////////////////// fields ////////////////////////// //////////////////// fields //////////////////////////
lp_settings m_settings; lp_settings m_settings;
lp_status m_status; lp_status m_status = lp_status::UNKNOWN;
stacked_value<simplex_strategy_enum> m_simplex_strategy; stacked_value<simplex_strategy_enum> m_simplex_strategy;
// such can be found at the initialization step: u < l // such can be found at the initialization step: u < l
stacked_value<int> m_crossed_bounds_column; stacked_value<int> m_crossed_bounds_column;
lar_core_solver m_mpq_lar_core_solver; lar_core_solver m_mpq_lar_core_solver;
int_solver * m_int_solver; int_solver * m_int_solver = nullptr;
bool m_need_register_terms; bool m_need_register_terms = false;
var_register m_var_register; var_register m_var_register;
var_register m_term_register; var_register m_term_register;
stacked_vector<ul_pair> m_columns_to_ul_pairs; stacked_vector<ul_pair> m_columns_to_ul_pairs;
@ -90,6 +90,8 @@ class lar_solver : public column_namer {
// the set of column indices j such that bounds have changed for j // the set of column indices j such that bounds have changed for j
u_set m_columns_with_changed_bounds; u_set m_columns_with_changed_bounds;
u_set m_rows_with_changed_bounds; u_set m_rows_with_changed_bounds;
unsigned_vector m_row_bounds_to_replay;
u_set m_basic_columns_with_changed_cost; u_set m_basic_columns_with_changed_cost;
// these are basic columns with the value changed, so the the corresponding row in the tableau // these are basic columns with the value changed, so the the corresponding row in the tableau
// does not sum to zero anymore // does not sum to zero anymore
@ -164,7 +166,6 @@ class lar_solver : public column_namer {
void adjust_initial_state_for_lu(); void adjust_initial_state_for_lu();
void adjust_initial_state_for_tableau_rows(); void adjust_initial_state_for_tableau_rows();
void fill_last_row_of_A_d(static_matrix<double, double> & A, const lar_term* ls); void fill_last_row_of_A_d(static_matrix<double, double> & A, const lar_term* ls);
void clear();
bool use_lu() const; bool use_lu() const;
bool sizes_are_correct() const; bool sizes_are_correct() const;
bool implied_bound_is_correctly_explained(implied_bound const & be, const vector<std::pair<mpq, unsigned>> & explanation) const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector<std::pair<mpq, unsigned>> & explanation) const;
@ -219,6 +220,7 @@ class lar_solver : public column_namer {
void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair<mpq> & delta); void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair<mpq> & delta);
void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j); void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j);
unsigned num_changed_bounds() const { return m_rows_with_changed_bounds.size(); } unsigned num_changed_bounds() const { return m_rows_with_changed_bounds.size(); }
void insert_row_with_changed_bounds(unsigned rid);
void detect_rows_with_changed_bounds_for_column(unsigned j); void detect_rows_with_changed_bounds_for_column(unsigned j);
void detect_rows_with_changed_bounds(); void detect_rows_with_changed_bounds();
void set_value_for_nbasic_column(unsigned j, const impq & new_val); void set_value_for_nbasic_column(unsigned j, const impq & new_val);
@ -368,20 +370,19 @@ public:
// these two loops should be run sequentially // these two loops should be run sequentially
// since the first loop might change column bounds // since the first loop might change column bounds
// and add fixed columns this way // and add fixed columns this way
if (settings().cheap_eqs()) { if (settings().propagate_eqs()) {
bp.clear_for_eq(); bp.clear_for_eq();
for (unsigned i : m_rows_with_changed_bounds) { for (unsigned i : m_rows_with_changed_bounds) {
calculate_cheap_eqs_for_row(i, bp); unsigned offset_eqs = stats().m_offset_eqs;
bp.cheap_eq_tree(i);
if (settings().get_cancel_flag()) if (settings().get_cancel_flag())
return; return;
if (stats().m_offset_eqs > offset_eqs)
m_row_bounds_to_replay.push_back(i);
} }
} }
m_rows_with_changed_bounds.clear(); m_rows_with_changed_bounds.clear();
} }
template <typename T>
void calculate_cheap_eqs_for_row(unsigned i, lp_bound_propagator<T> & bp) {
bp.cheap_eq_tree(i);
}
bool is_fixed(column_index const& j) const { return column_is_fixed(j); } bool is_fixed(column_index const& j) const { return column_is_fixed(j); }
inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); }
@ -515,6 +516,8 @@ public:
unsigned column_to_reported_index(unsigned j) const; unsigned column_to_reported_index(unsigned j) const;
lp_settings & settings(); lp_settings & settings();
lp_settings const & settings() const; lp_settings const & settings() const;
statistics& stats();
void updt_params(params_ref const& p); void updt_params(params_ref const& p);
column_type get_column_type(unsigned j) const { return m_mpq_lar_core_solver.m_column_types()[j]; } column_type get_column_type(unsigned j) const { return m_mpq_lar_core_solver.m_column_types()[j]; }
const impq & get_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_lower_bounds()[j]; } const impq & get_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_lower_bounds()[j]; }

View file

@ -155,7 +155,7 @@ public:
}; };
class const_iterator { class const_iterator {
u_map< mpq>::iterator m_it; u_map<mpq>::iterator m_it;
public: public:
ival operator*() const { return ival(m_it->m_key, m_it->m_value); } ival operator*() const { return ival(m_it->m_key, m_it->m_value); }
const_iterator operator++() { const_iterator i = *this; m_it++; return i; } const_iterator operator++() { const_iterator i = *this; m_it++; return i; }

View file

@ -72,14 +72,15 @@ class lp_bound_propagator {
static int other(int x, int y, int z) { SASSERT(x == z || y == z); return x == z ? y : x; } static int other(int x, int y, int z) { SASSERT(x == z || y == z); return x == z ? y : x; }
std::ostream& print_vert(std::ostream & out, const vertex* v) const { std::ostream& print_vert(std::ostream & out, const vertex* v) const {
out << "(c = " << v->column() << ", parent = {"; out << "(c = " << v->column() << ", parent = {";
if (v->parent()) { out << "(" << v->parent()->column() << ")";} if (v->parent())
else { out << "null"; } out << "(" << v->parent()->column() << ")";
else
out << "null";
out << "} , lvl = " << v->level(); out << "} , lvl = " << v->level();
if (m_pol.contains(v->column())) { if (m_pol.contains(v->column()))
out << (pol(v) == -1? " -":" +"); out << (pol(v) == -1? " -":" +");
} else { else
out << " not in m_pol"; out << " not in m_pol";
}
out << ')'; out << ')';
return out; return out;
} }
@ -87,13 +88,13 @@ class lp_bound_propagator {
hashtable<unsigned, u_hash, u_eq> m_visited_rows; hashtable<unsigned, u_hash, u_eq> m_visited_rows;
hashtable<unsigned, u_hash, u_eq> m_visited_columns; hashtable<unsigned, u_hash, u_eq> m_visited_columns;
u_map<vertex*> m_vertices; u_map<vertex*> m_vertices;
vertex* m_root; vertex* m_root = nullptr;
// At some point we can find a row with a single vertex non fixed vertex // At some point we can find a row with a single vertex non fixed vertex
// then we can fix the whole tree, // then we can fix the whole tree,
// by adjusting the vertices offsets, so they become absolute. // by adjusting the vertices offsets, so they become absolute.
// If the tree is fixed then in addition to checking with the m_vals_to_verts // If the tree is fixed then in addition to checking with the m_vals_to_verts
// we are going to check with the m_fixed_var_tables. // we are going to check with the m_fixed_var_tables.
const vertex* m_fixed_vertex; const vertex* m_fixed_vertex = nullptr;
explanation m_fixed_vertex_explanation; explanation m_fixed_vertex_explanation;
// a pair (o, j) belongs to m_vals_to_verts iff x[j] = x[m_root->column()] + o // a pair (o, j) belongs to m_vals_to_verts iff x[j] = x[m_root->column()] + o
map<mpq, const vertex*, obj_hash<mpq>, default_eq<mpq>> m_vals_to_verts; map<mpq, const vertex*, obj_hash<mpq>, default_eq<mpq>> m_vals_to_verts;
@ -111,19 +112,199 @@ class lp_bound_propagator {
T& m_imp; T& m_imp;
vector<implied_bound> m_ibounds; vector<implied_bound> m_ibounds;
map<mpq, unsigned, obj_hash<mpq>, default_eq<mpq>> m_val2fixed_row;
void try_add_equation_with_internal_fixed_tables(unsigned r1, vertex const* v) {
SASSERT(m_fixed_vertex);
if (v != m_root)
return;
unsigned v1 = v->column();
unsigned r2 = UINT_MAX;
if (!m_val2fixed_row.find(val(v1), r2) || r2 >= lp().row_count()) {
m_val2fixed_row.insert(val(v1), r1);
return;
}
unsigned v2, v3;
int polarity;
if (!is_tree_offset_row(r2, v2, v3, polarity) || !not_set(v3) ||
is_int(v1) != is_int(v2) || val(v1) != val(v2)) {
m_val2fixed_row.insert(val(v1), r1);
return;
}
explanation ex;
explain_fixed_in_row(r1, ex);
explain_fixed_in_row(r2, ex);
add_eq_on_columns(ex, v1, v2, true);
}
void try_add_equation_with_lp_fixed_tables(unsigned row_index, const vertex *v) {
SASSERT(m_fixed_vertex);
unsigned v_j = v->column();
unsigned j = null_lpvar;
if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j)) {
// try_add_equation_with_internal_fixed_tables(row_index, v);
return;
}
TRACE("cheap_eq",
tout << "v_j = "; lp().print_column_info(v_j, tout) << std::endl;
tout << "v = "; print_vert(tout, v) << std::endl;
tout << "found j " << j << std::endl; lp().print_column_info(j, tout)<< std::endl;
tout << "found j = " << j << std::endl;);
vector<edge> path = connect_in_tree(v, m_fixed_vertex);
explanation ex = get_explanation_from_path(path);
ex.add_expl(m_fixed_vertex_explanation);
explain_fixed_column(j, ex);
add_eq_on_columns(ex, j, v_j, true);
}
void try_add_equation_with_val_table(const vertex *v) {
SASSERT(m_fixed_vertex);
unsigned v_j = v->column();
const vertex *u = nullptr;
if (!m_vals_to_verts.find(val(v_j), u)) {
m_vals_to_verts.insert(val(v_j), v);
return;
}
unsigned j = u->column();
if (j == v_j || is_int(j) != is_int(v_j))
return;
TRACE("cheap_eq", tout << "found j=" << j << " for v=";
print_vert(tout, v) << "\n in m_vals_to_verts\n";);
vector<edge> path = connect_in_tree(u, v);
explanation ex = get_explanation_from_path(path);
ex.add_expl(m_fixed_vertex_explanation);
add_eq_on_columns(ex, j, v_j, true);
}
static bool not_set(unsigned j) { return j == UINT_MAX; }
static bool is_set(unsigned j) { return j != UINT_MAX; }
void create_root(unsigned row_index) {
SASSERT(!m_root && !m_fixed_vertex);
unsigned x, y;
int polarity;
TRACE("cheap_eq_det", print_row(tout, row_index););
if (!is_tree_offset_row(row_index, x, y, polarity)) {
TRACE("cheap_eq_det", tout << "not an offset row\n";);
return;
}
TRACE("cheap_eq", print_row(tout, row_index););
m_root = alloc_v(x);
set_polarity(m_root, 1); // keep m_root in the positive table
if (not_set(y)) {
set_fixed_vertex(m_root);
explain_fixed_in_row(row_index, m_fixed_vertex_explanation);
}
else {
vertex *v = add_child_with_check(row_index, y, m_root, polarity);
if (v)
explore_under(v);
}
explore_under(m_root);
}
void explore_under(vertex * v) {
check_for_eq_and_add_to_val_tables(v);
go_over_vertex_column(v);
}
// In case of only one non fixed column, and the function returns true,
// this column would be represened by x.
bool is_tree_offset_row(unsigned row_index, unsigned & x, unsigned & y, int & polarity) const {
x = y = UINT_MAX;
const row_cell<mpq>* x_cell = nullptr;
const row_cell<mpq>* y_cell = nullptr;
const auto & row = lp().get_row(row_index);
for (unsigned k = 0; k < row.size(); k++) {
const auto& c = row[k];
if (column_is_fixed(c.var()))
continue;
if (not_set(x)) {
if (c.coeff().is_one() || c.coeff().is_minus_one()) {
x = c.var();
x_cell = & c;
}
else
return false;
}
else if (not_set(y)) {
if (c.coeff().is_one() || c.coeff().is_minus_one()) {
y = c.var();
y_cell = & c;
}
else
return false;
}
else
return false;
}
if (is_set(x)) {
if (is_set(y))
polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos()? -1 : 1;
else
polarity = 1;
return true;
}
return false;
}
void go_over_vertex_column(vertex * v) {
lpvar j = v->column();
if (!check_insert(m_visited_columns, j))
return;
for (const auto & c : lp().get_column(j)) {
unsigned row_index = c.var();
if (!check_insert(m_visited_rows, row_index))
continue;
vertex* u = get_child_from_row(row_index, v);
if (u)
explore_under(u);
}
}
void reset_cheap_eq_eh() {
if (!m_root)
return;
delete_tree(m_root);
m_root = nullptr;
set_fixed_vertex(nullptr);
m_fixed_vertex_explanation.clear();
m_vals_to_verts.reset();
m_vals_to_verts_neg.reset();
m_pol.reset();
m_vertices.reset();
}
struct reset_cheap_eq {
lp_bound_propagator& p;
reset_cheap_eq(lp_bound_propagator& p):p(p) {}
~reset_cheap_eq() { p.reset_cheap_eq_eh(); }
};
public: public:
lp_bound_propagator(T& imp):
m_imp(imp) {}
const vector<implied_bound>& ibounds() const { return m_ibounds; } const vector<implied_bound>& ibounds() const { return m_ibounds; }
void init() { void init() {
m_improved_upper_bounds.clear(); m_improved_upper_bounds.clear();
m_improved_lower_bounds.clear(); m_improved_lower_bounds.clear();
m_ibounds.reset(); m_ibounds.reset();
} }
lp_bound_propagator(T& imp): m_root(nullptr),
m_fixed_vertex(nullptr),
m_imp(imp) {}
const lar_solver& lp() const { return m_imp.lp(); } const lar_solver& lp() const { return m_imp.lp(); }
lar_solver& lp() { return m_imp.lp(); } lar_solver& lp() { return m_imp.lp(); }
column_type get_column_type(unsigned j) const { column_type get_column_type(unsigned j) const {
return m_imp.lp().get_column_type(j); return m_imp.lp().get_column_type(j);
} }
@ -133,10 +314,9 @@ public:
} }
const mpq & get_lower_bound_rational(unsigned j) const { const mpq & get_lower_bound_rational(unsigned j) const {
return m_imp.lp().get_lower_bound(j).x; return m_imp.lp().get_lower_bound(j).x;
} }
const impq & get_upper_bound(unsigned j) const { const impq & get_upper_bound(unsigned j) const {
return m_imp.lp().get_upper_bound(j); return m_imp.lp().get_upper_bound(j);
} }
@ -167,19 +347,22 @@ public:
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout););
} }
} else { }
else {
m_improved_lower_bounds[j] = m_ibounds.size(); m_improved_lower_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout););
} }
} else { // the upper bound case }
else { // the upper bound case
if (try_get_value(m_improved_upper_bounds, j, k)) { if (try_get_value(m_improved_upper_bounds, j, k)) {
auto & found_bound = m_ibounds[k]; auto & found_bound = m_ibounds[k];
if (v < found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) { if (v < found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) {
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout););
} }
} else { }
else {
m_improved_upper_bounds[j] = m_ibounds.size(); m_improved_upper_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout););
@ -199,54 +382,12 @@ public:
return val(v->column()); return val(v->column());
} }
void try_add_equation_with_lp_fixed_tables(const vertex *v) {
SASSERT(m_fixed_vertex);
unsigned v_j = v->column();
unsigned j = null_lpvar;
if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j))
return;
TRACE("cheap_eq", tout << "v_j = "; lp().print_column_info(v_j, tout) << std::endl;);
TRACE("cheap_eq", tout << "v = "; print_vert(tout, v) << std::endl;);
TRACE("cheap_eq", tout << "found j " << j << std::endl;
lp().print_column_info(j, tout)<< std::endl;);
TRACE("cheap_eq", tout << "found j = " << j << std::endl;);
vector<edge> path = connect_in_tree(v, m_fixed_vertex);
explanation ex = get_explanation_from_path(path);
ex.add_expl(m_fixed_vertex_explanation);
explain_fixed_column(j, ex);
add_eq_on_columns(ex, j, v->column());
}
void try_add_equation_with_val_table(const vertex *v) {
SASSERT(m_fixed_vertex);
unsigned v_j = v->column();
const vertex *u = nullptr;
if (!m_vals_to_verts.find(val(v_j), u)) {
m_vals_to_verts.insert(val(v_j), v);
return;
}
unsigned j = u->column();
if (j == v_j || is_int(j) != is_int(v_j))
return;
TRACE("cheap_eq", tout << "found j=" << j << " for v=";
print_vert(tout, v) << "\n in m_vals_to_verts\n";);
vector<edge> path = connect_in_tree(u, v);
explanation ex = get_explanation_from_path(path);
ex.add_expl(m_fixed_vertex_explanation);
add_eq_on_columns(ex, j, v_j);
}
bool tree_contains_r(vertex* root, vertex *v) const { bool tree_contains_r(vertex* root, vertex *v) const {
if (*root == *v) if (*root == *v)
return true; return true;
for (auto e : root->edges()) { for (auto e : root->edges())
if (tree_contains_r(e.target(), v)) if (tree_contains_r(e.target(), v))
return true; return true;
}
return false; return false;
} }
@ -294,31 +435,6 @@ public:
return v; return v;
} }
static bool not_set(unsigned j) { return j == UINT_MAX; }
static bool is_set(unsigned j) { return j != UINT_MAX; }
void create_root(unsigned row_index) {
SASSERT(!m_root && !m_fixed_vertex);
unsigned x, y;
int polarity;
TRACE("cheap_eq_det", print_row(tout, row_index););
if (!is_tree_offset_row(row_index, x, y, polarity)) {
TRACE("cheap_eq_det", tout << "not an offset row\n";);
return;
}
TRACE("cheap_eq", print_row(tout, row_index););
m_root = alloc_v(x);
set_polarity(m_root, 1); // keep m_root in the positive table
if (not_set(y)) {
set_fixed_vertex(m_root);
explain_fixed_in_row(row_index, m_fixed_vertex_explanation);
} else {
vertex *v = add_child_with_check(row_index, y, m_root, polarity);
if (v)
explore_under(v);
}
explore_under(m_root);
}
unsigned column(unsigned row, unsigned index) { unsigned column(unsigned row, unsigned index) {
return lp().get_row(row)[index].var(); return lp().get_row(row)[index].var();
@ -327,7 +443,6 @@ public:
bool fixed_phase() const { return m_fixed_vertex; } bool fixed_phase() const { return m_fixed_vertex; }
// Returns the vertex to start exploration from, or nullptr. // Returns the vertex to start exploration from, or nullptr.
// It is assumed that parent->column() is present in the row // It is assumed that parent->column() is present in the row
vertex* get_child_from_row(unsigned row_index, vertex* parent) { vertex* get_child_from_row(unsigned row_index, vertex* parent) {
@ -379,10 +494,12 @@ public:
is_int(k->column()) == is_int(v->column()) && is_int(k->column()) == is_int(v->column()) &&
!is_equal(k->column(), v->column())) { !is_equal(k->column(), v->column())) {
report_eq(k, v); report_eq(k, v);
} else { }
else {
TRACE("cheap_eq", tout << "no report\n";); TRACE("cheap_eq", tout << "no report\n";);
} }
} else { }
else {
TRACE("cheap_eq", tout << "registered: " << val(v) << " -> { "; print_vert(tout, v) << "} \n";); TRACE("cheap_eq", tout << "registered: " << val(v) << " -> { "; print_vert(tout, v) << "} \n";);
table.insert(val(v), v); table.insert(val(v), v);
} }
@ -411,37 +528,31 @@ public:
std::ostream& print_path(const vector<edge>& path, std::ostream& out) const { std::ostream& print_path(const vector<edge>& path, std::ostream& out) const {
out << "path = \n"; out << "path = \n";
for (const edge& k : path) { for (const edge& k : path)
print_edge(k, out) << "\n"; print_edge(k, out) << "\n";
}
return out; return out;
} }
// we have v_i and v_j, indices of vertices at the same offsets // we have v_i and v_j, indices of vertices at the same offsets
void report_eq(const vertex* v_i, const vertex* v_j) { void report_eq(const vertex* v_i, const vertex* v_j) {
SASSERT(v_i != v_j); SASSERT(v_i != v_j);
SASSERT(lp().get_column_value(v_i->column()) == lp().get_column_value(v_j->column())); SASSERT(lp().get_column_value(v_i->column()) == lp().get_column_value(v_j->column()));
TRACE("cheap_eq", tout << v_i->column() << " = " << v_j->column() << "\nu = "; TRACE("cheap_eq", tout << v_i->column() << " = " << v_j->column() << "\nu = ";
print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) <<"\n"; print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) <<"\n");
);
vector<edge> path = connect_in_tree(v_i, v_j); vector<edge> path = connect_in_tree(v_i, v_j);
lp::explanation exp = get_explanation_from_path(path); lp::explanation exp = get_explanation_from_path(path);
add_eq_on_columns(exp, v_i->column(), v_j->column()); add_eq_on_columns(exp, v_i->column(), v_j->column(), false);
} }
std::ostream& print_expl(std::ostream & out, const explanation& exp) const { std::ostream& print_expl(std::ostream & out, const explanation& exp) const {
for (auto p : exp) { for (auto p : exp)
lp().constraints().display(out, [this](lpvar j) { return lp().get_variable_name(j);}, p.ci()); lp().constraints().display(out, [this](lpvar j) { return lp().get_variable_name(j);}, p.ci());
}
return out; return out;
} }
void add_eq_on_columns(const explanation& exp, lpvar j, lpvar k) { bool add_eq_on_columns(const explanation& exp, lpvar j, lpvar k, bool is_fixed) {
SASSERT(j != k); SASSERT(j != k);
unsigned je = lp().column_to_reported_index(j); unsigned je = lp().column_to_reported_index(j);
unsigned ke = lp().column_to_reported_index(k); unsigned ke = lp().column_to_reported_index(k);
@ -452,8 +563,10 @@ public:
tout << "theory_vars v" << lp().local_to_external(je) << " == v" << lp().local_to_external(ke) << "\n"; tout << "theory_vars v" << lp().local_to_external(je) << " == v" << lp().local_to_external(ke) << "\n";
); );
m_imp.add_eq(je, ke, exp); bool added = m_imp.add_eq(je, ke, exp, is_fixed);
lp().settings().stats().m_cheap_eqs++; if (added)
lp().stats().m_offset_eqs++;
return added;
} }
// column to theory_var // column to theory_var
@ -478,14 +591,10 @@ public:
} }
void explain_fixed_in_row(unsigned row, explanation& ex) const { void explain_fixed_in_row(unsigned row, explanation& ex) const {
TRACE("cheap_eq", TRACE("cheap_eq", tout << lp().get_row(row) << std::endl);
tout << lp().get_row(row) << std::endl; for (const auto & c : lp().get_row(row))
); if (lp().is_fixed(c.var()))
for (const auto & c : lp().get_row(row)) {
if (lp().is_fixed(c.var())) {
explain_fixed_column(c.var(), ex); explain_fixed_column(c.var(), ex);
}
}
} }
void explain_fixed_column(unsigned j, explanation & ex) const { void explain_fixed_column(unsigned j, explanation & ex) const {
@ -536,10 +645,9 @@ public:
if (visited_verts.find(v->column()) != visited_verts.end()) if (visited_verts.find(v->column()) != visited_verts.end())
return false; return false;
visited_verts.insert(v->column()); visited_verts.insert(v->column());
for (auto e : v->edges()) { for (auto e : v->edges())
if (!tree_is_correct(e.target(), visited_verts)) if (!tree_is_correct(e.target(), visited_verts))
return false; return false;
}
return true; return true;
} }
std::ostream& print_tree(std::ostream & out, vertex* v) const { std::ostream& print_tree(std::ostream & out, vertex* v) const {
@ -553,43 +661,37 @@ public:
return out; return out;
} }
void try_add_equation_with_fixed_tables(const vertex* v) { void try_add_equation_with_fixed_tables(unsigned row_index, const vertex* v) {
try_add_equation_with_lp_fixed_tables(v); try_add_equation_with_lp_fixed_tables(row_index, v);
try_add_equation_with_val_table(v); try_add_equation_with_val_table(v);
} }
void create_fixed_eqs(const vertex* v) { void handle_fixed_phase(unsigned row_index) {
try_add_equation_with_fixed_tables(v); if (!fixed_phase())
return;
const vertex* v = m_root;
try_add_equation_with_fixed_tables(row_index, v);
for (auto e: v->edges()) for (auto e: v->edges())
try_add_equation_with_fixed_tables(e.target()); try_add_equation_with_fixed_tables(row_index, e.target());
} }
void handle_fixed_phase() {
create_fixed_eqs(m_root);
}
void cheap_eq_tree(unsigned row_index) { void cheap_eq_tree(unsigned row_index) {
reset_cheap_eq _reset(*this);
TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";); TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";);
if (!check_insert(m_visited_rows, row_index)) if (!check_insert(m_visited_rows, row_index))
return; // already explored
create_root(row_index);
if (m_root == nullptr) {
return; return;
} create_root(row_index);
if (!m_root)
return;
TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";); TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";);
SASSERT(tree_is_correct()); SASSERT(tree_is_correct());
if (fixed_phase()) handle_fixed_phase(row_index);
handle_fixed_phase();
TRACE("cheap_eq", tout << "done for row_index " << row_index << "\n";); TRACE("cheap_eq",
TRACE("cheap_eq", tout << "tree size = " << verts_size();); tout << "done for row_index " << row_index << "\n";
delete_tree(m_root); tout << "tree size = " << verts_size(););
m_root = nullptr;
set_fixed_vertex(nullptr);
m_fixed_vertex_explanation.clear();
m_vals_to_verts.reset();
m_vals_to_verts_neg.reset();
m_pol.reset();
m_vertices.reset();
} }
std::ostream& print_row(std::ostream & out, unsigned row_index) const { std::ostream& print_row(std::ostream & out, unsigned row_index) const {
@ -645,69 +747,5 @@ public:
return true; return true;
} }
void go_over_vertex_column(vertex * v) {
lpvar j = v->column();
if (!check_insert(m_visited_columns, j))
return;
for (const auto & c : lp().get_column(j)) {
unsigned row_index = c.var();
if (!check_insert(m_visited_rows, row_index))
continue;
vertex *u = get_child_from_row(row_index, v);
if (u) {
// debug
// if (verts_size() > 3) {
// std::cout << "big tree\n";
// TRACE("cheap_eq", print_tree(tout, m_root););
// exit(1);
// } // end debug
explore_under(u);
}
}
}
void explore_under(vertex * v) {
check_for_eq_and_add_to_val_tables(v);
go_over_vertex_column(v);
}
// In case of only one non fixed column, and the function returns true,
// this column would be represened by x.
bool is_tree_offset_row( unsigned row_index,
unsigned & x, unsigned & y, int & polarity ) const {
x = y = UINT_MAX;
const row_cell<mpq>* x_cell = nullptr;
const row_cell<mpq>* y_cell = nullptr;
const auto & row = lp().get_row(row_index);
for (unsigned k = 0; k < row.size(); k++) {
const auto& c = row[k];
if (column_is_fixed(c.var()))
continue;
if (not_set(x)) {
if (c.coeff().is_one() || c.coeff().is_minus_one()) {
x = c.var();
x_cell = & c;
} else {
return false;
}
} else if (not_set(y)) {
if (c.coeff().is_one() || c.coeff().is_minus_one()) {
y = c.var();
y_cell = & c;
} else
return false;
} else
return false;
}
if (is_set(x)) {
if (is_set(y))
polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos()? -1 : 1;
else
polarity = 1;
return true;
}
return false;
}
}; };
} }

View file

@ -27,7 +27,7 @@ template bool lp::vectors_are_equal<lp::mpq>(vector<lp::mpq > const&, vector<lp:
void lp::lp_settings::updt_params(params_ref const& _p) { void lp::lp_settings::updt_params(params_ref const& _p) {
smt_params_helper p(_p); smt_params_helper p(_p);
m_enable_hnf = p.arith_enable_hnf(); m_enable_hnf = p.arith_enable_hnf();
m_cheap_eqs = p.arith_propagate_eqs(); m_propagate_eqs = p.arith_propagate_eqs();
print_statistics = p.arith_print_stats(); print_statistics = p.arith_print_stats();
m_print_external_var_name = p.arith_print_ext_var_names(); m_print_external_var_name = p.arith_print_ext_var_names();
report_frequency = p.arith_rep_freq(); report_frequency = p.arith_rep_freq();

View file

@ -99,6 +99,7 @@ template <typename X> bool is_epsilon_small(const X & v, const double& eps);
class lp_resource_limit { class lp_resource_limit {
public: public:
virtual ~lp_resource_limit() = default;
virtual bool get_cancel_flag() = 0; virtual bool get_cancel_flag() = 0;
}; };
@ -125,7 +126,7 @@ struct statistics {
unsigned m_cross_nested_forms; unsigned m_cross_nested_forms;
unsigned m_grobner_calls; unsigned m_grobner_calls;
unsigned m_grobner_conflicts; unsigned m_grobner_conflicts;
unsigned m_cheap_eqs; unsigned m_offset_eqs;
statistics() { reset(); } statistics() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); } void reset() { memset(this, 0, sizeof(*this)); }
void collect_statistics(::statistics& st) const { void collect_statistics(::statistics& st) const {
@ -146,7 +147,7 @@ struct statistics {
st.update("arith-horner-cross-nested-forms", m_cross_nested_forms); st.update("arith-horner-cross-nested-forms", m_cross_nested_forms);
st.update("arith-grobner-calls", m_grobner_calls); st.update("arith-grobner-calls", m_grobner_calls);
st.update("arith-grobner-conflicts", m_grobner_conflicts); st.update("arith-grobner-conflicts", m_grobner_conflicts);
st.update("arith-cheap-eqs", m_cheap_eqs); st.update("arith-offset-eqs", m_offset_eqs);
} }
}; };
@ -241,10 +242,10 @@ private:
unsigned m_nlsat_delay; unsigned m_nlsat_delay;
bool m_enable_hnf { true }; bool m_enable_hnf { true };
bool m_print_external_var_name { false }; bool m_print_external_var_name { false };
bool m_cheap_eqs { false }; bool m_propagate_eqs { false };
public: public:
bool print_external_var_name() const { return m_print_external_var_name; } bool print_external_var_name() const { return m_print_external_var_name; }
bool cheap_eqs() const { return m_cheap_eqs;} bool propagate_eqs() const { return m_propagate_eqs;}
unsigned hnf_cut_period() const { return m_hnf_cut_period; } unsigned hnf_cut_period() const { return m_hnf_cut_period; }
void set_hnf_cut_period(unsigned period) { m_hnf_cut_period = period; } void set_hnf_cut_period(unsigned period) { m_hnf_cut_period = period; }
unsigned random_next() { return m_rand(); } unsigned random_next() { return m_rand(); }

View file

@ -71,6 +71,7 @@ namespace polynomial {
template<typename ValManager, typename Value = typename ValManager::numeral> template<typename ValManager, typename Value = typename ValManager::numeral>
class var2value { class var2value {
public: public:
virtual ~var2value() = default;
virtual ValManager & m() const = 0; virtual ValManager & m() const = 0;
virtual bool contains(var x) const = 0; virtual bool contains(var x) const = 0;
virtual Value const & operator()(var x) const = 0; virtual Value const & operator()(var x) const = 0;
@ -100,6 +101,7 @@ namespace polynomial {
struct display_var_proc { struct display_var_proc {
virtual std::ostream& operator()(std::ostream & out, var x) const { return out << "x" << x; } virtual std::ostream& operator()(std::ostream & out, var x) const { return out << "x" << x; }
virtual ~display_var_proc() = default;
}; };
class polynomial; class polynomial;
@ -228,6 +230,7 @@ namespace polynomial {
del_eh * m_next; del_eh * m_next;
public: public:
del_eh():m_next(nullptr) {} del_eh():m_next(nullptr) {}
virtual ~del_eh() = default;
virtual void operator()(polynomial * p) = 0; virtual void operator()(polynomial * p) = 0;
}; };

View file

@ -175,6 +175,8 @@ namespace upolynomial {
m_current_size = 0; m_current_size = 0;
} }
virtual ~factorization_combination_iterator_base() = default;
/** /**
\brief Returns the factors we are enumerating through. \brief Returns the factors we are enumerating through.
*/ */

View file

@ -37,6 +37,7 @@ namespace realclosure {
class mk_interval { class mk_interval {
public: public:
virtual ~mk_interval() = default;
virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) = 0; virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) = 0;
}; };

View file

@ -15,6 +15,7 @@ Notes:
#include "math/simplex/bit_matrix.h" #include "math/simplex/bit_matrix.h"
#include "util/stopwatch.h" #include "util/stopwatch.h"
#include "util/trace.h"
#include <cstring> #include <cstring>

View file

@ -42,6 +42,7 @@ public:
}; };
struct display_var_proc { struct display_var_proc {
virtual ~display_var_proc() = default;
virtual void operator()(std::ostream & out, var x) const { out << "x" << x; } virtual void operator()(std::ostream & out, var x) const { out << "x" << x; }
}; };

View file

@ -38,7 +38,7 @@ array_factory::array_factory(ast_manager & m, model_core & md):
} }
/** /**
\brieft Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s. \brief Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s.
Store in fi the function interpretation for f. Store in fi the function interpretation for f.
*/ */
expr * array_factory::mk_array_interp(sort * s, func_interp * & fi) { expr * array_factory::mk_array_interp(sort * s, func_interp * & fi) {

View file

@ -280,6 +280,9 @@ void func_interp::compress() {
} }
// other compression, if else is a default branch. // other compression, if else is a default branch.
// or function encode identity. // or function encode identity.
#if 0
// breaks array interpretations
// #5604
if (m().is_false(m_else)) { if (m().is_false(m_else)) {
expr_ref new_else(get_interp(), m()); expr_ref new_else(get_interp(), m());
for (func_entry * curr : m_entries) { for (func_entry * curr : m_entries) {
@ -291,7 +294,9 @@ void func_interp::compress() {
m().dec_ref(m_else); m().dec_ref(m_else);
m_else = new_else; m_else = new_else;
} }
else if (!m_entries.empty() && is_identity()) { else
#endif
if (!m_entries.empty() && is_identity()) {
for (func_entry * curr : m_entries) { for (func_entry * curr : m_entries) {
curr->deallocate(m(), m_arity); curr->deallocate(m(), m_arity);
} }
@ -335,14 +340,11 @@ expr * func_interp::get_interp_core() const {
expr * r = m_else; expr * r = m_else;
ptr_buffer<expr> vars; ptr_buffer<expr> vars;
for (func_entry * curr : m_entries) { for (func_entry * curr : m_entries) {
if (m_else == curr->get_result()) { if (m_else == curr->get_result())
continue; continue;
} if (vars.empty())
if (vars.empty()) { for (unsigned i = 0; i < m_arity; i++)
for (unsigned i = 0; i < m_arity; i++) {
vars.push_back(m().mk_var(i, curr->get_arg(i)->get_sort())); vars.push_back(m().mk_var(i, curr->get_arg(i)->get_sort()));
}
}
ptr_buffer<expr> eqs; ptr_buffer<expr> eqs;
for (unsigned i = 0; i < m_arity; i++) { for (unsigned i = 0; i < m_arity; i++) {
eqs.push_back(m().mk_eq(vars[i], curr->get_arg(i))); eqs.push_back(m().mk_eq(vars[i], curr->get_arg(i)));

View file

@ -238,15 +238,13 @@ void model::compress(bool force_inline) {
top_sort ts(m); top_sort ts(m);
collect_deps(ts); collect_deps(ts);
ts.topological_sort(); ts.topological_sort();
for (func_decl * f : ts.top_sorted()) { for (func_decl * f : ts.top_sorted())
cleanup_interp(ts, f, force_inline); cleanup_interp(ts, f, force_inline);
}
func_decl_set removed; func_decl_set removed;
ts.m_occur_count.reset(); ts.m_occur_count.reset();
for (func_decl * f : ts.top_sorted()) { for (func_decl * f : ts.top_sorted())
collect_occs(ts, f); collect_occs(ts, f);
}
// remove auxiliary declarations that are not used. // remove auxiliary declarations that are not used.
for (func_decl * f : ts.top_sorted()) { for (func_decl * f : ts.top_sorted()) {
@ -256,7 +254,8 @@ void model::compress(bool force_inline) {
removed.insert(f); removed.insert(f);
} }
} }
if (removed.empty()) break; if (removed.empty())
break;
TRACE("model", tout << "remove\n"; for (func_decl* f : removed) tout << f->get_name() << "\n";); TRACE("model", tout << "remove\n"; for (func_decl* f : removed) tout << f->get_name() << "\n";);
remove_decls(m_decls, removed); remove_decls(m_decls, removed);
remove_decls(m_func_decls, removed); remove_decls(m_func_decls, removed);
@ -268,12 +267,14 @@ void model::compress(bool force_inline) {
void model::collect_deps(top_sort& ts) { void model::collect_deps(top_sort& ts) {
for (auto const& kv : m_finterp) { recfun::util u(m);
ts.insert(kv.m_key, collect_deps(ts, kv.m_value)); for (auto const& [f, v] : m_finterp)
} if (!u.has_def(f))
for (auto const& kv : m_interp) { ts.insert(f, collect_deps(ts, v));
ts.insert(kv.m_key, collect_deps(ts, kv.m_value.second));
} for (auto const& [f,v] : m_interp)
if (!u.has_def(f))
ts.insert(f, collect_deps(ts, v.second));
} }
struct model::deps_collector { struct model::deps_collector {
@ -334,6 +335,7 @@ model::func_decl_set* model::collect_deps(top_sort& ts, func_interp * fi) {
*/ */
void model::cleanup_interp(top_sort& ts, func_decl* f, bool force_inline) { void model::cleanup_interp(top_sort& ts, func_decl* f, bool force_inline) {
unsigned pid = ts.partition_id(f); unsigned pid = ts.partition_id(f);
expr * e1 = get_const_interp(f); expr * e1 = get_const_interp(f);
if (e1) { if (e1) {

View file

@ -162,7 +162,6 @@ struct evaluator_cfg : public default_rewriter_cfg {
result_pr = nullptr; result_pr = nullptr;
family_id fid = f->get_family_id(); family_id fid = f->get_family_id();
bool _is_uninterp = fid != null_family_id && m.get_plugin(fid)->is_considered_uninterpreted(f); bool _is_uninterp = fid != null_family_id && m.get_plugin(fid)->is_considered_uninterpreted(f);
func_decl* g = nullptr;
br_status st = BR_FAILED; br_status st = BR_FAILED;
#if 0 #if 0
struct pp { struct pp {

View file

@ -122,6 +122,7 @@ namespace datalog {
class register_engine_base { class register_engine_base {
public: public:
virtual ~register_engine_base() = default;
virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0; virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0;
virtual void set_context(context* ctx) = 0; virtual void set_context(context* ctx) = 0;
}; };

View file

@ -42,7 +42,7 @@ namespace datalog {
/** /**
\brief Number of rules longer than two that contain this pair. \brief Number of rules longer than two that contain this pair.
This number is being updated by \c add_rule and \remove rule. Even though between This number is being updated by \c add_rule and \c remove_rule. Even though between
adding a rule and removing it, the length of a rule can decrease without this pair adding a rule and removing it, the length of a rule can decrease without this pair
being notified about it, it will surely see the decrease from length 3 to 2 which being notified about it, it will surely see the decrease from length 3 to 2 which
the threshold for rule being counted in this counter. the threshold for rule being counted in this counter.

View file

@ -34,7 +34,7 @@ public:
typedef obj_map<func_decl, unsigned> partition_map; typedef obj_map<func_decl, unsigned> partition_map;
/** /**
\Brief Model Based Cartesian projection of lits \brief Model Based Cartesian projection of lits
*/ */
void operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl, void operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl,
vector<expr_ref_vector> &res); vector<expr_ref_vector> &res);

View file

@ -32,6 +32,7 @@ namespace nlsat {
class display_assumption_proc { class display_assumption_proc {
public: public:
virtual ~display_assumption_proc() = default;
virtual std::ostream& operator()(std::ostream& out, assumption a) const = 0; virtual std::ostream& operator()(std::ostream& out, assumption a) const = 0;
}; };

View file

@ -133,14 +133,16 @@ namespace opt {
model_ref mdl; model_ref mdl;
s().get_model(mdl); s().get_model(mdl);
if (mdl) { if (mdl) {
TRACE("opt", tout << *mdl << "\n";);
for (auto & soft : m_soft) { for (auto & soft : m_soft) {
if (!mdl->is_true(soft.s)) { if (!mdl->is_true(soft.s)) {
break; update_bounds();
return;
} }
soft.set_value(l_true); soft.set_value(l_true);
assert_value(soft); assert_value(soft);
} }
update_bounds(); update_assignment();
} }
} }
@ -151,9 +153,8 @@ namespace opt {
unsigned sz = m_soft.size(); unsigned sz = m_soft.size();
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
auto& soft = m_soft[i]; auto& soft = m_soft[i];
if (soft.value != l_undef) { if (soft.value != l_undef)
continue; continue;
}
expr_ref_vector asms(m); expr_ref_vector asms(m);
asms.push_back(soft.s); asms.push_back(soft.s);
lbool is_sat = s().check_sat(asms); lbool is_sat = s().check_sat(asms);

View file

@ -24,6 +24,7 @@ Notes:
#include "ast/pb_decl_plugin.h" #include "ast/pb_decl_plugin.h"
#include "ast/ast_smt_pp.h" #include "ast/ast_smt_pp.h"
#include "ast/ast_pp_util.h" #include "ast/ast_pp_util.h"
#include "ast/ast_ll_pp.h"
#include "ast/display_dimacs.h" #include "ast/display_dimacs.h"
#include "model/model_smt2_pp.h" #include "model/model_smt2_pp.h"
#include "tactic/goal.h" #include "tactic/goal.h"
@ -966,12 +967,12 @@ namespace opt {
tout << "Convert minimization " << orig_term << "\n"; tout << "Convert minimization " << orig_term << "\n";
tout << "to maxsat: " << term << "\n"; tout << "to maxsat: " << term << "\n";
for (unsigned i = 0; i < weights.size(); ++i) { for (unsigned i = 0; i < weights.size(); ++i) {
tout << mk_pp(terms[i].get(), m) << ": " << weights[i] << "\n"; tout << mk_pp(terms.get(i), m) << ": " << weights[i] << "\n";
} }
tout << "offset: " << offset << "\n"; tout << "offset: " << offset << "\n";
); );
std::ostringstream out; std::ostringstream out;
out << orig_term << ':' << index; out << mk_bounded_pp(orig_term, m, 2) << ':' << index;
id = symbol(out.str()); id = symbol(out.str());
return true; return true;
} }
@ -994,7 +995,7 @@ namespace opt {
} }
neg = true; neg = true;
std::ostringstream out; std::ostringstream out;
out << orig_term << ':' << index; out << mk_bounded_pp(orig_term, m) << ':' << index;
id = symbol(out.str()); id = symbol(out.str());
return true; return true;
} }
@ -1013,7 +1014,7 @@ namespace opt {
} }
neg = is_max; neg = is_max;
std::ostringstream out; std::ostringstream out;
out << orig_term << ':' << index; out << mk_bounded_pp(orig_term, m, 2) << ':' << index;
id = symbol(out.str()); id = symbol(out.str());
return true; return true;
} }
@ -1200,7 +1201,7 @@ namespace opt {
app* context::purify(generic_model_converter_ref& fm, expr* term) { app* context::purify(generic_model_converter_ref& fm, expr* term) {
std::ostringstream out; std::ostringstream out;
out << mk_pp(term, m); out << mk_bounded_pp(term, m, 3);
app* q = m.mk_fresh_const(out.str(), term->get_sort()); app* q = m.mk_fresh_const(out.str(), term->get_sort());
if (!fm) fm = alloc(generic_model_converter, m, "opt"); if (!fm) fm = alloc(generic_model_converter, m, "opt");
if (m_arith.is_int_real(term)) { if (m_arith.is_int_real(term)) {

View file

@ -45,6 +45,7 @@ namespace opt {
class maxsat_context { class maxsat_context {
public: public:
virtual ~maxsat_context() = default;
virtual generic_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification. virtual generic_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification.
virtual bool sat_enabled() const = 0; // is using th SAT solver core enabled? virtual bool sat_enabled() const = 0; // is using th SAT solver core enabled?
virtual solver& get_solver() = 0; // retrieve solver object (SAT or SMT solver) virtual solver& get_solver() = 0; // retrieve solver object (SAT or SMT solver)

View file

@ -26,6 +26,7 @@ namespace opt {
class pareto_callback { class pareto_callback {
public: public:
virtual ~pareto_callback() = default;
virtual unsigned num_objectives() = 0; virtual unsigned num_objectives() = 0;
virtual expr_ref mk_gt(unsigned i, model_ref& model) = 0; virtual expr_ref mk_gt(unsigned i, model_ref& model) = 0;
virtual expr_ref mk_ge(unsigned i, model_ref& model) = 0; virtual expr_ref mk_ge(unsigned i, model_ref& model) = 0;

View file

@ -989,6 +989,7 @@ namespace nlarith {
imp& m_imp; imp& m_imp;
public: public:
isubst(imp& i) : m_imp(i) {} isubst(imp& i) : m_imp(i) {}
virtual ~isubst() = default;
virtual void mk_lt(poly const& p, app_ref& r) = 0; virtual void mk_lt(poly const& p, app_ref& r) = 0;
virtual void mk_eq(poly const& p, app_ref& r) = 0; virtual void mk_eq(poly const& p, app_ref& r) = 0;
virtual void mk_le(poly const& p, app_ref& r) { virtual void mk_le(poly const& p, app_ref& r) {

View file

@ -96,8 +96,6 @@ namespace nlarith {
bool create_branches(app* x, unsigned nl, expr* const* lits, branch_conditions& bc); bool create_branches(app* x, unsigned nl, expr* const* lits, branch_conditions& bc);
/** /**
\brief Extract non-linear variables from ground formula. \brief Extract non-linear variables from ground formula.
\requires a ground formula.
*/ */
void extract_non_linear(expr* e, ptr_vector<app>& nl_vars); void extract_non_linear(expr* e, ptr_vector<app>& nl_vars);

View file

@ -34,6 +34,7 @@ namespace qe {
class i_nnf_atom { class i_nnf_atom {
public: public:
virtual ~i_nnf_atom() = default;
virtual void operator()(expr* e, bool pol, expr_ref& result) = 0; virtual void operator()(expr* e, bool pol, expr_ref& result) = 0;
}; };

View file

@ -40,6 +40,7 @@ namespace sat {
class literal_occs_fun { class literal_occs_fun {
public: public:
virtual double operator()(literal l) = 0; virtual double operator()(literal l) = 0;
virtual ~literal_occs_fun() = default;
}; };

View file

@ -1893,7 +1893,6 @@ namespace sat {
void solver::init_ext_assumptions() { void solver::init_ext_assumptions() {
if (m_ext && m_ext->tracking_assumptions()) { if (m_ext && m_ext->tracking_assumptions()) {
m_ext_assumption_set.reset(); m_ext_assumption_set.reset();
unsigned trail_size = m_trail.size();
if (!inconsistent()) if (!inconsistent())
m_ext->add_assumptions(m_ext_assumption_set); m_ext->add_assumptions(m_ext_assumption_set);
} }

View file

@ -301,26 +301,27 @@ namespace arith {
m_explanation.add_pair(j, v); m_explanation.add_pair(j, v);
} }
void solver::add_eq(lpvar u, lpvar v, lp::explanation const& e) { bool solver::add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed) {
if (s().inconsistent()) if (s().inconsistent())
return; return false;
theory_var uv = lp().local_to_external(u); // variables that are returned should have external representations theory_var uv = lp().local_to_external(u); // variables that are returned should have external representations
theory_var vv = lp().local_to_external(v); // so maybe better to have them already transformed to external form theory_var vv = lp().local_to_external(v); // so maybe better to have them already transformed to external form
if (is_equal(uv, vv)) if (is_equal(uv, vv))
return; return false;
enode* n1 = var2enode(uv); enode* n1 = var2enode(uv);
enode* n2 = var2enode(vv); enode* n2 = var2enode(vv);
expr* e1 = n1->get_expr(); expr* e1 = n1->get_expr();
expr* e2 = n2->get_expr(); expr* e2 = n2->get_expr();
if (m.is_ite(e1) || m.is_ite(e2)) if (!is_fixed && !a.is_numeral(e1) && !a.is_numeral(e2) && (m.is_ite(e1) || m.is_ite(e2)))
return; return false;
if (e1->get_sort() != e2->get_sort()) if (e1->get_sort() != e2->get_sort())
return; return false;
reset_evidence(); reset_evidence();
for (auto ev : e) for (auto ev : e)
set_evidence(ev.ci(), m_core, m_eqs); set_evidence(ev.ci(), m_core, m_eqs);
auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, n1, n2); auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, n1, n2);
ctx.propagate(n1, n2, jst->to_index()); ctx.propagate(n1, n2, jst->to_index());
return true;
} }
bool solver::bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const { bool solver::bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const {
@ -627,7 +628,7 @@ namespace arith {
anum const& an = nl_value(v, *m_a1); anum const& an = nl_value(v, *m_a1);
if (a.is_int(o) && !m_nla->am().is_int(an)) if (a.is_int(o) && !m_nla->am().is_int(an))
value = a.mk_numeral(rational::zero(), a.is_int(o)); value = a.mk_numeral(rational::zero(), a.is_int(o));
value = a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o)); //value = a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o));
} }
else if (v != euf::null_theory_var) { else if (v != euf::null_theory_var) {
rational r = get_value(v); rational r = get_value(v);

View file

@ -450,7 +450,7 @@ namespace arith {
lp::lar_solver& lp() { return *m_solver; } lp::lar_solver& lp() { return *m_solver; }
lp::lar_solver const& lp() const { return *m_solver; } lp::lar_solver const& lp() const { return *m_solver; }
bool is_equal(theory_var x, theory_var y) const; bool is_equal(theory_var x, theory_var y) const;
void add_eq(lpvar u, lpvar v, lp::explanation const& e); bool add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed);
void consume(rational const& v, lp::constraint_index j); void consume(rational const& v, lp::constraint_index j);
bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const; bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const;
}; };

View file

@ -622,7 +622,6 @@ namespace array {
// arrays used as indices in other arrays have to be treated as shared issue #3532, #3529 // arrays used as indices in other arrays have to be treated as shared issue #3532, #3529
if (ctx.is_shared(r) || is_shared_arg(r)) if (ctx.is_shared(r) || is_shared_arg(r))
roots.push_back(r->get_th_var(get_id())); roots.push_back(r->get_th_var(get_id()));
r->mark1(); r->mark1();
to_unmark.push_back(r); to_unmark.push_back(r);
} }

View file

@ -120,8 +120,8 @@ namespace array {
SASSERT(!n || !n->is_attached_to(get_id())); SASSERT(!n || !n->is_attached_to(get_id()));
if (!n) if (!n)
n = mk_enode(e, false); n = mk_enode(e, false);
SASSERT(!n->is_attached_to(get_id())); if (!n->is_attached_to(get_id()))
mk_var(n); mk_var(n);
for (auto* arg : euf::enode_args(n)) for (auto* arg : euf::enode_args(n))
ensure_var(arg); ensure_var(arg);
switch (a->get_decl_kind()) { switch (a->get_decl_kind()) {

View file

@ -119,7 +119,7 @@ namespace array {
bool solver::must_have_different_model_values(theory_var v1, theory_var v2) { bool solver::must_have_different_model_values(theory_var v1, theory_var v2) {
euf::enode* else1 = nullptr, * else2 = nullptr; euf::enode* else1 = nullptr, * else2 = nullptr;
euf::enode* n1 = var2enode(v1), *n2 = var2enode(v2); euf::enode* n1 = var2enode(v1);
expr* e1 = n1->get_expr(); expr* e1 = n1->get_expr();
if (!a.is_array(e1)) if (!a.is_array(e1))
return true; return true;

View file

@ -46,6 +46,7 @@ namespace euf {
value = r->get_expr(); value = r->get_expr();
else else
value = factory.get_fresh_value(srt); value = factory.get_fresh_value(srt);
(void)s;
TRACE("model", tout << s.bpp(r) << " := " << value << "\n";); TRACE("model", tout << s.bpp(r) << " := " << value << "\n";);
values.set(id, value); values.set(id, value);
expr_ref_vector* vals = nullptr; expr_ref_vector* vals = nullptr;

View file

@ -22,6 +22,32 @@ Author:
namespace euf { namespace euf {
void solver::add_auto_relevant(expr* e) {
if (!relevancy_enabled())
return;
for (; m_auto_relevant_scopes > 0; --m_auto_relevant_scopes)
m_auto_relevant_lim.push_back(m_auto_relevant.size());
// std::cout << "add-auto " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n";
m_auto_relevant.push_back(e);
}
void solver::pop_relevant(unsigned n) {
if (m_auto_relevant_scopes >= n) {
m_auto_relevant_scopes -= n;
return;
}
n -= m_auto_relevant_scopes;
m_auto_relevant_scopes = 0;
unsigned top = m_auto_relevant_lim.size() - n;
unsigned lim = m_auto_relevant_lim[top];
m_auto_relevant_lim.shrink(top);
m_auto_relevant.shrink(lim);
}
void solver::push_relevant() {
++m_auto_relevant_scopes;
}
bool solver::is_relevant(expr* e) const { bool solver::is_relevant(expr* e) const {
return m_relevant_expr_ids.get(e->get_id(), true); return m_relevant_expr_ids.get(e->get_id(), true);
} }
@ -31,11 +57,11 @@ namespace euf {
} }
void solver::ensure_dual_solver() { void solver::ensure_dual_solver() {
if (!m_dual_solver) { if (m_dual_solver)
m_dual_solver = alloc(sat::dual_solver, s().rlimit()); return;
for (unsigned i = s().num_user_scopes(); i-- > 0; ) m_dual_solver = alloc(sat::dual_solver, s().rlimit());
m_dual_solver->push(); for (unsigned i = s().num_user_scopes(); i-- > 0; )
} m_dual_solver->push();
} }
/** /**
@ -65,8 +91,6 @@ namespace euf {
bool solver::init_relevancy() { bool solver::init_relevancy() {
m_relevant_expr_ids.reset(); m_relevant_expr_ids.reset();
bool_vector visited;
ptr_vector<expr> todo;
if (!relevancy_enabled()) if (!relevancy_enabled())
return true; return true;
if (!m_dual_solver) if (!m_dual_solver)
@ -77,12 +101,21 @@ namespace euf {
for (enode* n : m_egraph.nodes()) for (enode* n : m_egraph.nodes())
max_id = std::max(max_id, n->get_expr_id()); max_id = std::max(max_id, n->get_expr_id());
m_relevant_expr_ids.resize(max_id + 1, false); m_relevant_expr_ids.resize(max_id + 1, false);
ptr_vector<expr>& todo = m_relevant_todo;
bool_vector& visited = m_relevant_visited;
auto const& core = m_dual_solver->core(); auto const& core = m_dual_solver->core();
todo.reset();
for (auto lit : core) { for (auto lit : core) {
expr* e = m_bool_var2expr.get(lit.var(), nullptr); expr* e = m_bool_var2expr.get(lit.var(), nullptr);
if (e) if (e)
todo.push_back(e); todo.push_back(e);
} }
#if 0
std::cout << "init-relevant\n";
for (expr* e : m_auto_relevant)
std::cout << "auto-relevant " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n";
#endif
todo.append(m_auto_relevant);
for (unsigned i = 0; i < todo.size(); ++i) { for (unsigned i = 0; i < todo.size(); ++i) {
expr* e = todo[i]; expr* e = todo[i];
if (visited.get(e->get_id(), false)) if (visited.get(e->get_id(), false))
@ -114,6 +147,9 @@ namespace euf {
todo.push_back(arg); todo.push_back(arg);
} }
for (auto * e : todo)
visited[e->get_id()] = false;
TRACE("euf", TRACE("euf",
for (enode* n : m_egraph.nodes()) for (enode* n : m_egraph.nodes())
if (is_relevant(n)) if (is_relevant(n))

View file

@ -183,6 +183,7 @@ namespace euf {
} }
void solver::propagate(literal lit, ext_justification_idx idx) { void solver::propagate(literal lit, ext_justification_idx idx) {
add_auto_relevant(bool_var2expr(lit.var()));
s().assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), idx)); s().assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), idx));
} }
@ -527,6 +528,7 @@ namespace euf {
m_egraph.push(); m_egraph.push();
if (m_dual_solver) if (m_dual_solver)
m_dual_solver->push(); m_dual_solver->push();
push_relevant();
} }
void solver::pop(unsigned n) { void solver::pop(unsigned n) {
@ -536,6 +538,7 @@ namespace euf {
e->pop(n); e->pop(n);
si.pop(n); si.pop(n);
m_egraph.pop(n); m_egraph.pop(n);
pop_relevant(n);
scope const & sc = m_scopes[m_scopes.size() - n]; scope const & sc = m_scopes[m_scopes.size() - n];
for (unsigned i = m_var_trail.size(); i-- > sc.m_var_lim; ) { for (unsigned i = m_var_trail.size(); i-- > sc.m_var_lim; ) {
bool_var v = m_var_trail[i]; bool_var v = m_var_trail[i];
@ -994,7 +997,7 @@ namespace euf {
::solver::push_eh_t& push_eh, ::solver::push_eh_t& push_eh,
::solver::pop_eh_t& pop_eh, ::solver::pop_eh_t& pop_eh,
::solver::fresh_eh_t& fresh_eh) { ::solver::fresh_eh_t& fresh_eh) {
m_user_propagator = alloc(user::solver, *this); m_user_propagator = alloc(user_solver::solver, *this);
m_user_propagator->add(ctx, push_eh, pop_eh, fresh_eh); m_user_propagator->add(ctx, push_eh, pop_eh, fresh_eh);
for (unsigned i = m_scopes.size(); i-- > 0; ) for (unsigned i = m_scopes.size(); i-- > 0; )
m_user_propagator->push(); m_user_propagator->push();

View file

@ -99,9 +99,8 @@ namespace euf {
sat::lookahead* m_lookahead = nullptr; sat::lookahead* m_lookahead = nullptr;
ast_manager* m_to_m; ast_manager* m_to_m;
sat::sat_internalizer* m_to_si; sat::sat_internalizer* m_to_si;
scoped_ptr<euf::ackerman> m_ackerman; scoped_ptr<euf::ackerman> m_ackerman;
scoped_ptr<sat::dual_solver> m_dual_solver; user_solver::solver* m_user_propagator = nullptr;
user::solver* m_user_propagator = nullptr;
th_solver* m_qsolver = nullptr; th_solver* m_qsolver = nullptr;
unsigned m_generation = 0; unsigned m_generation = 0;
mutable ptr_vector<expr> m_todo; mutable ptr_vector<expr> m_todo;
@ -182,6 +181,8 @@ namespace euf {
// relevancy // relevancy
bool_vector m_relevant_expr_ids; bool_vector m_relevant_expr_ids;
bool_vector m_relevant_visited;
ptr_vector<expr> m_relevant_todo;
void ensure_dual_solver(); void ensure_dual_solver();
bool init_relevancy(); bool init_relevancy();
@ -363,6 +364,11 @@ namespace euf {
// relevancy // relevancy
bool m_relevancy = true; bool m_relevancy = true;
scoped_ptr<sat::dual_solver> m_dual_solver;
ptr_vector<expr> m_auto_relevant;
unsigned_vector m_auto_relevant_lim;
unsigned m_auto_relevant_scopes = 0;
bool relevancy_enabled() const { return m_relevancy && get_config().m_relevancy_lvl > 0; } bool relevancy_enabled() const { return m_relevancy && get_config().m_relevancy_lvl > 0; }
void disable_relevancy(expr* e) { IF_VERBOSE(0, verbose_stream() << "disabling relevancy " << mk_pp(e, m) << "\n"); m_relevancy = false; } void disable_relevancy(expr* e) { IF_VERBOSE(0, verbose_stream() << "disabling relevancy " << mk_pp(e, m) << "\n"); m_relevancy = false; }
void add_root(unsigned n, sat::literal const* lits); void add_root(unsigned n, sat::literal const* lits);
@ -377,6 +383,9 @@ namespace euf {
void track_relevancy(sat::bool_var v); void track_relevancy(sat::bool_var v);
bool is_relevant(expr* e) const; bool is_relevant(expr* e) const;
bool is_relevant(enode* n) const; bool is_relevant(enode* n) const;
void add_auto_relevant(expr* e);
void pop_relevant(unsigned n);
void push_relevant();
// model construction // model construction

View file

@ -52,6 +52,9 @@ namespace pb {
constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k): constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k):
m_tag(t), m_lit(l), m_size(sz), m_obj_size(osz), m_id(id), m_k(k) { m_tag(t), m_lit(l), m_size(sz), m_obj_size(osz), m_id(id), m_k(k) {
} }
virtual ~constraint() = default;
sat::ext_constraint_idx cindex() const { return sat::constraint_base::mem2base(this); } sat::ext_constraint_idx cindex() const { return sat::constraint_base::mem2base(this); }
void deallocate(small_object_allocator& a) { a.deallocate(obj_size(), sat::constraint_base::mem2base_ptr(this)); } void deallocate(small_object_allocator& a) { a.deallocate(obj_size(), sat::constraint_base::mem2base_ptr(this)); }
unsigned id() const { return m_id; } unsigned id() const { return m_id; }

View file

@ -35,6 +35,7 @@ namespace pb {
class solver_interface { class solver_interface {
public: public:
virtual ~solver_interface() = default;
virtual lbool value(bool_var v) const = 0; virtual lbool value(bool_var v) const = 0;
virtual lbool value(literal lit) const = 0; virtual lbool value(literal lit) const = 0;
virtual bool is_false(literal lit) const = 0; virtual bool is_false(literal lit) const = 0;

View file

@ -32,8 +32,8 @@ namespace q {
<< mk_bounded_pp(rhs, m, 2); << mk_bounded_pp(rhs, m, 2);
} }
std::ostream& binding::display(euf::solver& ctx, unsigned num_nodes, std::ostream& out) const { std::ostream& binding::display(euf::solver& ctx, std::ostream& out) const {
for (unsigned i = 0; i < num_nodes; ++i) for (unsigned i = 0; i < size(); ++i)
out << ctx.bpp((*this)[i]) << " "; out << ctx.bpp((*this)[i]) << " ";
return out; return out;
} }
@ -46,7 +46,7 @@ namespace q {
if (!b) if (!b)
return out; return out;
do { do {
b->display(ctx, num_decls(), out) << "\n"; b->display(ctx, out) << "\n";
b = b->next(); b = b->next();
} }
while (b != m_bindings); while (b != m_bindings);

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