3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-20 07:36:38 +00:00

Merge branch 'master' into polysat

This commit is contained in:
Jakob Rath 2022-07-21 12:56:50 +02:00
commit e168d8a2eb
109 changed files with 4372 additions and 2743 deletions

View file

@ -10,7 +10,7 @@ defaults:
working-directory: src/api/js
env:
EM_VERSION: 3.1.0
EM_VERSION: 3.1.15
jobs:
publish:

View file

@ -10,7 +10,7 @@ defaults:
working-directory: src/api/js
env:
EM_VERSION: 3.1.0
EM_VERSION: 3.1.15
jobs:
check:

1
.gitignore vendored
View file

@ -46,6 +46,7 @@ bld_rel/*
bld_dbg_x64/*
bld_rel_x64/*
.vscode
*build*/**
# Auto generated files.
config.log
config.status

View file

@ -2,7 +2,7 @@
cmake_minimum_required(VERSION 3.4)
set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake")
project(Z3 VERSION 4.8.18.0 LANGUAGES CXX C)
project(Z3 VERSION 4.9.2.0 LANGUAGES CXX C)
################################################################################
# Project version
@ -160,7 +160,7 @@ list(APPEND Z3_COMPONENT_CXX_DEFINES $<$<CONFIG:RelWithDebInfo>:_EXTERNAL_RELEAS
################################################################################
# Find Python
################################################################################
find_package(PythonInterp REQUIRED)
find_package(PythonInterp 3 REQUIRED)
message(STATUS "PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}")
################################################################################
@ -230,7 +230,7 @@ else()
message(FATAL_ERROR "Platform \"${CMAKE_SYSTEM_NAME}\" not recognised")
endif()
list(APPEND Z3_COMPONENT_EXTRA_INCLUDE_DIRS
list(APPEND Z3_COMPONENT_EXTRA_INCLUDE_DIRS
"${PROJECT_BINARY_DIR}/src"
"${PROJECT_SOURCE_DIR}/src"
)
@ -293,8 +293,8 @@ if ((TARGET_ARCHITECTURE STREQUAL "x86_64") OR (TARGET_ARCHITECTURE STREQUAL "i6
set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel")
set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2")
# Intel's compiler requires linking with libiomp5
list(APPEND Z3_DEPENDENT_LIBS "iomp5")
# Intel's compiler requires linking with libiomp5
list(APPEND Z3_DEPENDENT_LIBS "iomp5")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(SSE_FLAGS "/arch:SSE2")
else()
@ -624,7 +624,7 @@ install(
################################################################################
# Examples
################################################################################
cmake_dependent_option(Z3_ENABLE_EXAMPLE_TARGETS
cmake_dependent_option(Z3_ENABLE_EXAMPLE_TARGETS
"Build Z3 api examples" ON
"CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF)
if (Z3_ENABLE_EXAMPLE_TARGETS)

View file

@ -1,6 +1,6 @@
RELEASE NOTES
Version 4.8.next
Version 4.next
================
- Planned features
- sat.euf
@ -10,6 +10,73 @@ Version 4.8.next
- native word level bit-vector solving.
- introduction of simple induction lemmas to handle a limited repertoire of induction proofs.
Version 4.10.0
==============
- Added API Z3_enable_concurrent_dec_ref to be set by interfaces that
use concurrent GC to manage reference counts. This feature is integrated
with the OCaml bindings and fixes a regression introduced when OCaml
transitioned to concurrent GC. Use of this feature for .Net and Java
bindings is not integrated for this release. They use external queues
that are unnecessarily complicated.
- Added pre-declared abstract datatype declarations to the context so
that Z3_eval_smtlib2_string works with List examples.
- Fixed Java linking for MacOS Arm64.
- Added missing callback handlers in tactics for user-propagator,
Thanks to Clemens Eisenhofer
- Tuning to Grobner arithmetic reasoning for smt.arith.solver=6
(currently the default in most cases). The check for consistency
modulo multiplication was updated in the following way:
- polynomial equalities are extracted from Simplex tableau rows using
a cone of influence algorithm. Rows where the basic variables were
unbounded were previously included. Following the legacy implementation
such rows are not included when building polynomial equations.
- equations are pre-solved if they are linear and can be split
into two groups one containing a single variable that has a
lower (upper) bound, the other with more than two variables
with upper (lower) bounds. This avoids losing bounds information
during completion.
- After (partial) completion, perform constant propagation for equalities
of the form x = 0
- After (partial) completion, perform factorization for factors of the
form x*y*p = 0 where x, are variables, p is linear.
Version 4.9.1
=============
- Bugfix release to ensure npm package works
Version 4.9.0
=============
- Native M1 (Mac ARM64) binaries and pypi distribution.
- thanks to Isabel Garcia Contreras and Arie Gurfinkel for testing and fixes
- API for incremental parsing of assertions.
A description of the feature is given by example here: https://github.com/Z3Prover/z3/commit/815518dc026e900392bf0d08ed859e5ec42d1e43
It also allows incrementality at the level of adding assertions to the
solver object.
- Fold/map for sequences:
https://microsoft.github.io/z3guide/docs/guide/Sequences#map-and-fold
At this point these functions are only exposed over the SMTLIB2 interface (and not programmatic API)
maxdiff/mindiff on arrays are more likely to be deprecated
- User Propagator:
- Add functions and callbacks for external control over branching thanks to Clemens Eisenhofer
- A functioning dotnet API for the User Propagator
https://github.com/Z3Prover/z3/blob/master/src/api/dotnet/UserPropagator.cs
- Java Script API
- higher level object wrappers are available thanks to Kevin Gibbons and Olaf Tomalka
- Totalizers and RC2
- The MaxSAT engine now allows to run RC2 with totalizer encoding.
Totalizers are on by default as preliminary tests suggest this solves already 10% more problems on
standard benchmarks. The option opt.rc2.totalizer (which by default is true) is used to control whether to use
totalizer encoding or built-in cardinality constraints.
The default engine is still maxres, so you have to set opt.maxsat_engine=rc2 to
enable the rc2 option at this point. The engines maxres-bin and rc2bin are experimental should not be used
(they are inferior to default options).
- Incremental constraints during optimization set option opt.incremental = true
- The interface `Z3_optimize_register_model_eh` allows to monitor incremental results during optimization.
It is now possible to also add constraints to the optimization context during search.
You have to set the option opt.incremental=true to be able to add constraints. The option
disables some pre-processing functionality that removes variables from the constraints.
Version 4.8.17
==============
- fix breaking bug in python interface for user propagator pop
@ -17,6 +84,29 @@ Version 4.8.17
- initial support for nested algebraic datatypes with sequences
- initiate map/fold operators on sequences - full integration for next releases
- initiate maxdiff/mindiff on arrays - full integration for next releases
Examples:
```
(declare-sort Expr)
(declare-sort Var)
(declare-datatypes ((Stmt 0))
(((Assignment (lval Var) (rval Expr))
(If (cond Expr) (th Stmt) (el Stmt))
(Seq (stmts (Seq Stmt))))))
(declare-const s Stmt)
(declare-const t Stmt)
(assert ((_ is Seq) t))
(assert ((_ is Seq) s))
(assert (= s (seq.nth (stmts t) 2)))
(assert (>= (seq.len (stmts s)) 5))
(check-sat)
(get-model)
(assert (= s (Seq (seq.unit s))))
(check-sat)
```
Version 4.8.16
==============
@ -964,7 +1054,7 @@ The following bugs are fixed in this release:
- Non-termination problem associated with option LOOKAHEAD=true.
It gets set for QF_UF in auto-configuration mode.
Thanks to Pierre-Christophe Bué.
Thanks to Pierre-Christophe Bué.
- Incorrect axioms created for injective functions.
Thanks to Sascha Boehme.
@ -986,7 +1076,7 @@ Version 2.6
===========
This release fixes a few bugs.
Thanks to Marko Kääramees for reporting a bug in the strong context simplifier and
Thanks to Marko Kääramees for reporting a bug in the strong context simplifier and
to Josh Berdine.
This release also introduces some new preprocessing features:
@ -1018,7 +1108,7 @@ This release introduces the following features:
<tt>Z3_update_param_value</tt> in the C API. This is particularly useful
for turning the strong context simplifier on and off.
It also fixes bugs reported by Enric Rodríguez Carbonell,
It also fixes bugs reported by Enric Rodríguez Carbonell,
Nuno Lopes, Josh Berdine, Ethan Jackson, Rob Quigley and
Lucas Cordeiro.
@ -1085,7 +1175,7 @@ Version 2.1
===========
This is a bug fix release.
Many thanks to Robert Brummayer, Carine Pascal, François Remy,
Many thanks to Robert Brummayer, Carine Pascal, François Remy,
Rajesh K Karmani, Roberto Lublinerman and numerous others for their
feedback and bug reports.

View file

@ -335,6 +335,7 @@ try:
for modulename in (
'z3',
'z3.z3',
'z3.z3consts',
'z3.z3core',
'z3.z3num',

View file

@ -8,6 +8,5 @@
This website hosts the automatically generated documentation for the Z3 APIs.
- \ref @C_API@
- \ref @CPP_API@ @DOTNET_API@ @JAVA_API@ @PYTHON_API@ @OCAML_API@ @JS_API@
- \ref @C_API@ @CPP_API@ @DOTNET_API@ @JAVA_API@ @PYTHON_API@ @OCAML_API@ @JS_API@
*/

View file

@ -929,8 +929,8 @@ void enum_sort_example() {
sort s = ctx.enumeration_sort("enumT", 3, enum_names, enum_consts, enum_testers);
// enum_consts[0] is a func_decl of arity 0.
// we convert it to an expression using the operator()
expr a = enum_consts[0]();
expr b = enum_consts[1]();
expr a = enum_consts[0u]();
expr b = enum_consts[1u]();
expr x = ctx.constant("x", s);
expr test = (x==a) && (x==b);
std::cout << "1: " << test << std::endl;
@ -956,6 +956,55 @@ void tuple_example() {
std::cout << pair2 << "\n";
}
void datatype_example() {
std::cout << "datatype example\n";
context ctx;
constructors cs(ctx);
symbol ilist = ctx.str_symbol("ilist");
symbol accs[2] = { ctx.str_symbol("hd"), ctx.str_symbol("tl") };
sort sorts[2] = { ctx.int_sort(), ctx.datatype_sort(ilist) };
cs.add(ctx.str_symbol("nil"), ctx.str_symbol("is-nil"), 0, nullptr, nullptr);
cs.add(ctx.str_symbol("cons"), ctx.str_symbol("is-cons"), 2, accs, sorts);
sort ls = ctx.datatype(ilist, cs);
std::cout << ls << "\n";
func_decl nil(ctx), is_nil(ctx);
func_decl_vector nil_acc(ctx);
cs.query(0, nil, is_nil, nil_acc);
func_decl cons(ctx), is_cons(ctx);
func_decl_vector cons_acc(ctx);
cs.query(1, cons, is_cons, cons_acc);
std::cout << nil << " " << is_nil << " " << nil_acc << "\n";
std::cout << cons << " " << is_cons << " " << cons_acc << "\n";
symbol tree = ctx.str_symbol("tree");
symbol tlist = ctx.str_symbol("tree_list");
symbol accs1[2] = { ctx.str_symbol("left"), ctx.str_symbol("right") };
symbol accs2[2] = { ctx.str_symbol("hd"), ctx.str_symbol("tail") };
sort sorts1[2] = { ctx.datatype_sort(tlist), ctx.datatype_sort(tlist) };
sort sorts2[2] = { ctx.int_sort(), ctx.datatype_sort(tree) };
constructors cs1(ctx), cs2(ctx);
cs1.add(ctx.str_symbol("tnil"), ctx.str_symbol("is-tnil"), 0, nullptr, nullptr);
cs1.add(ctx.str_symbol("tnode"), ctx.str_symbol("is-tnode"), 2, accs1, sorts1);
constructor_list cl1(cs1);
cs2.add(ctx.str_symbol("lnil"), ctx.str_symbol("is-lnil"), 0, nullptr, nullptr);
cs2.add(ctx.str_symbol("lcons"), ctx.str_symbol("is-lcons"), 2, accs2, sorts2);
constructor_list cl2(cs2);
symbol names[2] = { tree, tlist };
constructor_list* cl[2] = { &cl1, &cl2 };
sort_vector dsorts = ctx.datatypes(2, names, cl);
std::cout << dsorts << "\n";
cs1.query(0, nil, is_nil, nil_acc);
cs1.query(1, cons, is_cons, cons_acc);
std::cout << nil << " " << is_nil << " " << nil_acc << "\n";
std::cout << cons << " " << is_cons << " " << cons_acc << "\n";
cs2.query(0, nil, is_nil, nil_acc);
cs2.query(1, cons, is_cons, cons_acc);
std::cout << nil << " " << is_nil << " " << nil_acc << "\n";
std::cout << cons << " " << is_cons << " " << cons_acc << "\n";
}
void expr_vector_example() {
std::cout << "expr_vector example\n";
context c;
@ -1343,6 +1392,7 @@ int main() {
incremental_example3(); std::cout << "\n";
enum_sort_example(); std::cout << "\n";
tuple_example(); std::cout << "\n";
datatype_example(); std::cout << "\n";
expr_vector_example(); std::cout << "\n";
exists_expr_vector_example(); std::cout << "\n";
substitute_example(); std::cout << "\n";

27
package-lock.json generated Normal file
View file

@ -0,0 +1,27 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"async-mutex": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz",
"integrity": "sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==",
"requires": {
"tslib": "^2.3.1"
}
},
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"z3-solver": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/z3-solver/-/z3-solver-4.9.0.tgz",
"integrity": "sha512-clSV0uyHsfrO84pSbHxoqvmd5HgSG4CoSJG2f8U65hBVylbV6p/0svctQWee9W2fWo0IsxHYRjxz2Z85GT0LAA==",
"requires": {
"async-mutex": "^0.3.2"
}
}
}
}

49
scripts/jsdoctest.yml Normal file
View file

@ -0,0 +1,49 @@
variables:
Major: '4'
Minor: '9'
Patch: '2'
NightlyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId)-$(Build.DefinitionName)
stages:
- stage: Build
jobs:
- job: UbuntuDoc
displayName: "Ubuntu Doc build"
pool:
vmImage: "Ubuntu-latest"
steps:
# TODO setup emscripten with no-install, then run
- script: npm --prefix=src/api/js ci
- script: npm --prefix=src/api/js run build:ts
- 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
- script: sudo apt-get install doxygen
- script: sudo apt-get install graphviz
- script: |
set -e
cd build
eval `opam config env`
make -j3
make -j3 examples
make -j3 test-z3
cd ..
- script: |
set -e
eval `opam config env`
cd doc
python mk_api_doc.py --mld --z3py-package-path=../build/python/z3 --js
mkdir api/html/ml
ocamldoc -html -d api/html/ml -sort -hide Z3 -I $( ocamlfind query zarith ) -I ../build/api/ml ../build/api/ml/z3enums.mli ../build/api/ml/z3.mli
cd ..
- script: zip -r z3doc.zip doc/api
- script: cp z3doc.zip $(Build.ArtifactStagingDirectory)/.
- task: PublishPipelineArtifact@0
inputs:
artifactName: 'UbuntuDoc'
targetPath: $(Build.ArtifactStagingDirectory)

View file

@ -8,7 +8,7 @@
from mk_util import *
def init_version():
set_version(4, 8, 18, 0)
set_version(4, 9, 2, 0)
# Z3 Project definition
def init_project_def():

View file

@ -1831,7 +1831,7 @@ class JavaDLLComponent(Component):
out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(LIB_EXT)\n' %
os.path.join('api', 'java', 'Native'))
else:
out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(SO_EXT)\n' %
out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) $(SLINK_EXTRA_FLAGS) %s$(OBJ_EXT) libz3$(SO_EXT)\n' %
os.path.join('api', 'java', 'Native'))
out.write('%s.jar: libz3java$(SO_EXT) ' % self.package_name)
deps = ''

View file

@ -1,8 +1,8 @@
variables:
Major: '4'
Minor: '8'
Patch: '18'
Minor: '9'
Patch: '2'
NightlyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId)-$(Build.DefinitionName)
stages:

View file

@ -6,7 +6,7 @@
trigger: none
variables:
ReleaseVersion: '4.8.18'
ReleaseVersion: '4.9.2'
stages:
@ -44,25 +44,18 @@ stages:
targetPath: $(Build.ArtifactStagingDirectory)
- job: MacBuildArm64
displayName: "macOS Build"
displayName: "macOS ARM64 Build"
pool:
vmImage: "macOS-latest"
steps:
- task: PythonScript@0
displayName: Build
- script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 --os=osx-11.0
- script: git clone https://github.com/z3prover/z3test z3test
- script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/.
- task: PublishPipelineArtifact@1
inputs:
scriptSource: 'filepath'
scriptPath: scripts/mk_unix_dist.py
arguments: --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --nojava --arch=arm64 --os=osx-11.0
- task: CopyFiles@2
inputs:
sourceFolder: dist
contents: '*.zip'
targetFolder: $(Build.ArtifactStagingDirectory)
- task: PublishPipelineArtifact@0
inputs:
artifactName: 'macOSBuildArm64'
artifactName: 'MacArm64'
targetPath: $(Build.ArtifactStagingDirectory)
- job: UbuntuBuild
@ -130,22 +123,14 @@ stages:
targetPath: $(Build.ArtifactStagingDirectory)
- job: LinuxBuilds
strategy:
matrix:
manyLinux:
name: ManyLinux
image: "quay.io/pypa/manylinux2010_x86_64:latest"
python: "/opt/python/cp37-cp37m/bin/python"
muslLinux:
name: MuslLinux
image: "quay.io/pypa/musllinux_1_1_x86_64:latest"
python: "/opt/python/cp310-cp310/bin/python"
displayName: "$(name) build"
displayName: "ManyLinux build"
variables:
name: ManyLinux
image: "quay.io/pypa/manylinux2010_x86_64:latest"
python: "/opt/python/cp37-cp37m/bin/python"
pool:
vmImage: "ubuntu-latest"
container: $(image)
variables:
python: $(python)
container: "quay.io/pypa/manylinux2010_x86_64:latest"
steps:
- task: PythonScript@0
displayName: Build
@ -170,7 +155,7 @@ stages:
targetFolder: $(Build.ArtifactStagingDirectory)
- task: PublishPipelineArtifact@0
inputs:
artifactName: '$(name)Build'
artifactName: 'ManyLinuxBuild'
targetPath: $(Build.ArtifactStagingDirectory)
- template: build-win-signed.yml
@ -405,16 +390,16 @@ stages:
inputs:
artifact: 'macOSBuild'
path: $(Agent.TempDirectory)
- task: DownloadPipelineArtifact@2
displayName: 'Download macOS Arm64 Build'
inputs:
artifact: 'MacArm64'
path: $(Agent.TempDirectory)
- task: DownloadPipelineArtifact@2
displayName: 'Download ManyLinux Build'
inputs:
artifact: 'ManyLinuxBuild'
path: $(Agent.TempDirectory)
- task: DownloadPipelineArtifact@2
displayName: 'Download MuslLinux Build'
inputs:
artifact: 'MuslLinuxBuild'
path: $(Agent.TempDirectory)
- task: DownloadPipelineArtifact@2
displayName: 'Download Win32 Build'
inputs:
@ -425,19 +410,17 @@ stages:
inputs:
artifact: 'WindowsBuild-x64'
path: $(Agent.TempDirectory)
- script: cd $(Agent.TempDirectory); mkdir osx-bin; cd osx-bin; unzip ../*osx*.zip
- script: cd $(Agent.TempDirectory); mkdir osx-x64-bin; cd osx-x64-bin; unzip ../*x64-osx*.zip
- script: cd $(Agent.TempDirectory); mkdir osx-arm64-bin; cd osx-arm64-bin; unzip ../*arm64-osx*.zip
- script: cd $(Agent.TempDirectory); mkdir libc-bin; cd libc-bin; unzip ../*glibc*.zip
- script: cd $(Agent.TempDirectory); mkdir musl-bin; cd musl-bin; unzip ../*-linux.zip
- script: cd $(Agent.TempDirectory); mkdir win32-bin; cd win32-bin; unzip ../*x86-win*.zip
- script: cd $(Agent.TempDirectory); mkdir win64-bin; cd win64-bin; unzip ../*x64-win*.zip
- script: python3 -m pip install --user -U setuptools wheel
- script: cd src/api/python; python3 setup.py sdist
# take a look at this PREMIUM HACK I came up with to get around the fact that the azure variable syntax overloads the bash syntax for subshells
- script: cd src/api/python; echo $(Agent.TempDirectory)/osx-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel
- script: cd src/api/python; echo $(Agent.TempDirectory)/osx-x64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel
- script: cd src/api/python; echo $(Agent.TempDirectory)/osx-arm64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel
- script: cd src/api/python; echo $(Agent.TempDirectory)/libc-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel
- script: cd src/api/python; echo $(Agent.TempDirectory)/musl-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel
- script: cd src/api/python; echo $(Agent.TempDirectory)/win32-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel
- script: cd src/api/python; echo $(Agent.TempDirectory)/win64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel
- task: PublishPipelineArtifact@0
@ -473,7 +456,7 @@ stages:
- task: DownloadPipelineArtifact@2
displayName: 'Download macOSArm64 Build'
inputs:
artifact: 'macOSBuildArm64'
artifact: 'MacArm64'
path: $(Agent.TempDirectory)
- task: DownloadPipelineArtifact@2
displayName: 'Download Win32 Build'
@ -513,7 +496,7 @@ stages:
releaseNotes: '$(ReleaseVersion) release'
assets: '$(Agent.TempDirectory)/*.*'
isDraft: true
isPreRelease: false
isPreRelease: true
# Enable on release (after fixing Nuget key)
- job: NuGetPublish

View file

@ -91,7 +91,7 @@ Type2Dotnet = { VOID : 'void', VOID_PTR : 'IntPtr', INT : 'int', UINT : 'uint',
# Mapping to ML types
Type2ML = { VOID : 'unit', VOID_PTR : 'ptr', INT : 'int', UINT : 'int', INT64 : 'int', UINT64 : 'int', DOUBLE : 'float',
Type2ML = { VOID : 'unit', VOID_PTR : 'ptr', INT : 'int', UINT : 'int', INT64 : 'int64', UINT64 : 'int64', DOUBLE : 'float',
FLOAT : 'float', STRING : 'string', STRING_PTR : 'char**',
BOOL : 'bool', SYMBOL : 'z3_symbol', PRINT_MODE : 'int', ERROR_CODE : 'int', CHAR : 'char', CHAR_PTR : 'string', LBOOL : 'int' }
@ -254,8 +254,10 @@ def param2pystr(p):
def param2ml(p):
k = param_kind(p)
if k == OUT:
if param_type(p) == INT or param_type(p) == UINT or param_type(p) == BOOL or param_type(p) == INT64 or param_type(p) == UINT64:
if param_type(p) == INT or param_type(p) == UINT or param_type(p) == BOOL:
return "int"
elif param_type(p) == INT64 or param_type(p) == UINT64:
return "int64"
elif param_type(p) == STRING:
return "string"
else:
@ -689,6 +691,7 @@ def mk_java(java_src, java_dir, package_name):
java_native.write('}\n')
java_wrapper = open(java_wrapperf, 'w')
pkg_str = package_name.replace('.', '_')
java_wrapper.write("// Automatically generated file\n")
with open(java_src + "/NativeStatic.txt") as ins:
for line in ins:
java_wrapper.write(line)
@ -1251,9 +1254,9 @@ def ml_unwrap(t, ts, s):
elif t == UINT:
return '(' + ts + ') Unsigned_int_val(' + s + ')'
elif t == INT64:
return '(' + ts + ') Long_val(' + s + ')'
return '(' + ts + ') Int64_val(' + s + ')'
elif t == UINT64:
return '(' + ts + ') Unsigned_long_val(' + s + ')'
return '(' + ts + ') Int64_val(' + s + ')'
elif t == DOUBLE:
return '(' + ts + ') Double_val(' + s + ')'
elif ml_has_plus_type(ts):
@ -1270,7 +1273,7 @@ def ml_set_wrap(t, d, n):
elif t == INT or t == UINT or t == PRINT_MODE or t == ERROR_CODE or t == LBOOL:
return d + ' = Val_int(' + n + ');'
elif t == INT64 or t == UINT64:
return d + ' = Val_long(' + n + ');'
return d + ' = caml_copy_int64(' + n + ');'
elif t == DOUBLE:
return d + '= caml_copy_double(' + n + ');'
elif t == STRING:

View file

@ -46,11 +46,15 @@ public:
void operator()(model_ref & md) override {
TRACE("ackermannize", tout << (fixed_model? "fixed" : "nonfixed") << "\n";);
SASSERT(!fixed_model || md.get() == 0 || (!md->get_num_constants() && !md->get_num_functions()));
model_ref& old_model = fixed_model ? abstr_model : md;
SASSERT(old_model.get());
model * new_model = alloc(model, m);
convert(old_model.get(), new_model);
CTRACE("ackermannize", md, tout << *md << "\n");
CTRACE("ackermannize", fixed_model, tout << *abstr_model << "\n");
model* new_model = alloc(model, m);
if (abstr_model)
convert(abstr_model.get(), new_model);
if (md)
convert(md.get(), new_model);
md = new_model;
}

View file

@ -35,11 +35,12 @@ namespace api {
object::object(context& c): m_ref_count(0), m_context(c) { this->m_id = m_context.add_object(this); }
void object::inc_ref() { m_ref_count++; }
void object::inc_ref() { ++m_ref_count; }
void object::dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) m_context.del_object(this); }
void object::dec_ref() { SASSERT(m_ref_count > 0); if (--m_ref_count == 0) m_context.del_object(this); }
unsigned context::add_object(api::object* o) {
flush_objects();
unsigned id = m_allocated_objects.size();
if (!m_free_object_ids.empty()) {
id = m_free_object_ids.back();
@ -50,9 +51,52 @@ namespace api {
}
void context::del_object(api::object* o) {
m_free_object_ids.push_back(o->id());
m_allocated_objects.remove(o->id());
dealloc(o);
#ifndef SINGLE_THREAD
if (m_concurrent_dec_ref) {
lock_guard lock(m_mux);
m_objects_to_flush.push_back(o);
}
else
#endif
{
m_free_object_ids.push_back(o->id());
m_allocated_objects.remove(o->id());
dealloc(o);
}
}
void context::dec_ref(ast* a) {
#ifndef SINGLE_THREAD
if (m_concurrent_dec_ref) {
lock_guard lock(m_mux);
m_asts_to_flush.push_back(a);
}
else
#endif
m().dec_ref(a);
}
void context::flush_objects() {
#ifndef SINGLE_THREAD
if (!m_concurrent_dec_ref)
return;
{
lock_guard lock(m_mux);
if (m_asts_to_flush.empty() && m_objects_to_flush.empty())
return;
m_asts_to_flush2.swap(m_asts_to_flush);
m_objects_to_flush2.swap(m_objects_to_flush);
}
for (ast* a : m_asts_to_flush2)
m().dec_ref(a);
for (auto* o : m_objects_to_flush2) {
m_free_object_ids.push_back(o->id());
m_allocated_objects.remove(o->id());
dealloc(o);
}
m_objects_to_flush2.reset();
m_asts_to_flush2.reset();
#endif
}
static void default_error_handler(Z3_context ctx, Z3_error_code c) {
@ -106,6 +150,7 @@ namespace api {
context::~context() {
m_last_obj = nullptr;
flush_objects();
for (auto& kv : m_allocated_objects) {
api::object* val = kv.m_value;
DEBUG_CODE(warning_msg("Uncollected memory: %d: %s", kv.m_key, typeid(*val).name()););
@ -356,6 +401,13 @@ extern "C" {
Z3_CATCH;
}
void Z3_API Z3_enable_concurrent_dec_ref(Z3_context c) {
Z3_TRY;
LOG_Z3_enable_concurrent_dec_ref(c);
mk_c(c)->enable_concurrent_dec_ref();
Z3_CATCH;
}
void Z3_API Z3_toggle_warning_messages(bool enabled) {
LOG_Z3_toggle_warning_messages(enabled);
enable_warning_messages(enabled != 0);
@ -365,6 +417,7 @@ extern "C" {
Z3_TRY;
LOG_Z3_inc_ref(c, a);
RESET_ERROR_CODE();
mk_c(c)->flush_objects();
mk_c(c)->m().inc_ref(to_ast(a));
Z3_CATCH;
}
@ -379,7 +432,7 @@ extern "C" {
return;
}
if (a) {
mk_c(c)->m().dec_ref(to_ast(a));
mk_c(c)->dec_ref(to_ast(a));
}
Z3_CATCH;
}

View file

@ -75,6 +75,9 @@ namespace api {
struct add_plugins { add_plugins(ast_manager & m); };
ast_context_params m_params;
bool m_user_ref_count; //!< if true, the user is responsible for managing reference counters.
#ifndef SINGLE_THREAD
bool m_concurrent_dec_ref = false;
#endif
scoped_ptr<ast_manager> m_manager;
scoped_ptr<cmd_context> m_cmd;
add_plugins m_plugins;
@ -91,8 +94,12 @@ namespace api {
smt_params m_fparams;
// -------------------------------
ast_ref_vector m_ast_trail;
#ifndef SINGLE_THREAD
ptr_vector<ast> m_asts_to_flush, m_asts_to_flush2;
ptr_vector<api::object> m_objects_to_flush, m_objects_to_flush2;
#endif
ast_ref_vector m_ast_trail;
ref<api::object> m_last_obj; //!< reference to the last API object returned by the APIs
u_map<api::object*> m_allocated_objects; // !< table containing current set of allocated API objects
unsigned_vector m_free_object_ids; // !< free list of identifiers available for allocated objects.
@ -169,9 +176,18 @@ namespace api {
void set_error_code(Z3_error_code err, char const* opt_msg);
void set_error_code(Z3_error_code err, std::string &&opt_msg);
void set_error_handler(Z3_error_handler h) { m_error_handler = h; }
void enable_concurrent_dec_ref() {
#ifdef SINGLE_THREAD
set_error_code(Z3_EXCEPTION, "Can't use concurrent features with a single-thread build");
#else
m_concurrent_dec_ref = true;
#endif
}
unsigned add_object(api::object* o);
void del_object(api::object* o);
void dec_ref(ast* a);
void flush_objects();
Z3_ast_print_mode get_print_mode() const { return m_print_mode; }
void set_print_mode(Z3_ast_print_mode m) { m_print_mode = m; }

View file

@ -35,6 +35,136 @@ extern "C" {
// ---------------
// Support for SMTLIB2
struct Z3_parser_context_ref : public api::object {
scoped_ptr<cmd_context> ctx;
Z3_parser_context_ref(api::context& c): api::object(c) {
ast_manager& m = c.m();
ctx = alloc(cmd_context, false, &(m));
install_dl_cmds(*ctx.get());
install_opt_cmds(*ctx.get());
install_smt2_extra_cmds(*ctx.get());
ctx->register_plist();
ctx->set_ignore_check(true);
}
~Z3_parser_context_ref() override {}
};
inline Z3_parser_context_ref * to_parser_context(Z3_parser_context pc) { return reinterpret_cast<Z3_parser_context_ref*>(pc); }
inline Z3_parser_context of_parser_context(Z3_parser_context_ref* pc) { return reinterpret_cast<Z3_parser_context>(pc); }
Z3_parser_context Z3_API Z3_mk_parser_context(Z3_context c) {
Z3_TRY;
LOG_Z3_mk_parser_context(c);
RESET_ERROR_CODE();
Z3_parser_context_ref * pc = alloc(Z3_parser_context_ref, *mk_c(c));
mk_c(c)->save_object(pc);
Z3_parser_context r = of_parser_context(pc);
RETURN_Z3(r);
Z3_CATCH_RETURN(nullptr);
}
void Z3_API Z3_parser_context_inc_ref(Z3_context c, Z3_parser_context pc) {
Z3_TRY;
LOG_Z3_parser_context_inc_ref(c, pc);
RESET_ERROR_CODE();
to_parser_context(pc)->inc_ref();
Z3_CATCH;
}
void Z3_API Z3_parser_context_dec_ref(Z3_context c, Z3_parser_context pc) {
Z3_TRY;
LOG_Z3_parser_context_dec_ref(c, pc);
RESET_ERROR_CODE();
to_parser_context(pc)->dec_ref();
Z3_CATCH;
}
static void insert_datatype(ast_manager& m, scoped_ptr<cmd_context>& ctx, sort* srt) {
datatype_util dt(m);
if (!dt.is_datatype(srt))
return;
for (func_decl * c : *dt.get_datatype_constructors(srt)) {
ctx->insert(c->get_name(), c);
func_decl * r = dt.get_constructor_recognizer(c);
ctx->insert(r->get_name(), r);
for (func_decl * a : *dt.get_constructor_accessors(c)) {
ctx->insert(a->get_name(), a);
}
}
}
static void insert_sort(ast_manager& m, scoped_ptr<cmd_context>& ctx, symbol const& name, sort* srt) {
if (ctx->find_psort_decl(name))
return;
psort* ps = ctx->pm().mk_psort_cnst(srt);
ctx->insert(ctx->pm().mk_psort_user_decl(0, name, ps));
insert_datatype(m, ctx, srt);
}
void Z3_API Z3_parser_context_add_sort(Z3_context c, Z3_parser_context pc, Z3_sort s) {
Z3_TRY;
LOG_Z3_parser_context_add_sort(c, pc, s);
RESET_ERROR_CODE();
auto& ctx = to_parser_context(pc)->ctx;
sort* srt = to_sort(s);
symbol name = srt->get_name();
insert_sort(mk_c(c)->m(), ctx, name, srt);
Z3_CATCH;
}
void Z3_API Z3_parser_context_add_decl(Z3_context c, Z3_parser_context pc, Z3_func_decl f) {
Z3_TRY;
LOG_Z3_parser_context_add_decl(c, pc, f);
RESET_ERROR_CODE();
auto& ctx = *to_parser_context(pc)->ctx;
func_decl* fn = to_func_decl(f);
symbol name = fn->get_name();
ctx.insert(name, fn);
Z3_CATCH;
}
Z3_ast_vector Z3_parser_context_parse_stream(Z3_context c, scoped_ptr<cmd_context>& ctx, bool owned, std::istream& is) {
Z3_TRY;
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(v);
std::stringstream errstrm;
ctx->set_regular_stream(errstrm);
try {
if (!parse_smt2_commands(*ctx, is)) {
if (owned)
ctx = nullptr;
SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str());
return of_ast_vector(v);
}
}
catch (z3_exception& e) {
if (owned)
ctx = nullptr;
errstrm << e.msg();
SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str());
return of_ast_vector(v);
}
for (expr* e : ctx->tracked_assertions())
v->m_ast_vector.push_back(e);
ctx->reset_tracked_assertions();
return of_ast_vector(v);
Z3_CATCH_RETURN(nullptr);
}
Z3_ast_vector Z3_API Z3_parser_context_from_string(Z3_context c, Z3_parser_context pc, Z3_string str) {
Z3_TRY;
LOG_Z3_parser_context_from_string(c, pc, str);
std::string s(str);
std::istringstream is(s);
auto& ctx = to_parser_context(pc)->ctx;
Z3_ast_vector r = Z3_parser_context_parse_stream(c, ctx, false, is);
RETURN_Z3(r);
Z3_CATCH_RETURN(nullptr);
}
Z3_ast_vector parse_smtlib2_stream(bool exec, Z3_context c, std::istream& is,
unsigned num_sorts,
Z3_symbol const _sort_names[],
@ -48,70 +178,16 @@ extern "C" {
install_dl_cmds(*ctx.get());
install_opt_cmds(*ctx.get());
install_smt2_extra_cmds(*ctx.get());
ctx->register_plist();
ctx->set_ignore_check(true);
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), m);
vector<symbol> sort_names;
ptr_vector<sort> sorts;
for (unsigned i = 0; i < num_sorts; ++i) {
sorts.push_back(to_sort(_sorts[i]));
sort_names.push_back(to_symbol(_sort_names[i]));
}
mk_c(c)->save_object(v);
for (unsigned i = 0; i < num_decls; ++i) {
func_decl* d = to_func_decl(decls[i]);
ctx->insert(to_symbol(decl_names[i]), d);
sort_names.push_back(d->get_range()->get_name());
sorts.push_back(d->get_range());
for (sort* s : *d) {
sort_names.push_back(s->get_name());
sorts.push_back(s);
}
}
datatype_util dt(m);
for (unsigned i = 0; i < num_sorts; ++i) {
sort* srt = sorts[i];
symbol name = sort_names[i];
if (ctx->find_psort_decl(name)) {
continue;
}
psort* ps = ctx->pm().mk_psort_cnst(srt);
ctx->insert(ctx->pm().mk_psort_user_decl(0, name, ps));
if (!dt.is_datatype(srt)) {
continue;
}
for (unsigned i = 0; i < num_decls; ++i)
ctx->insert(to_symbol(decl_names[i]), to_func_decl(decls[i]));
for (func_decl * c : *dt.get_datatype_constructors(srt)) {
ctx->insert(c->get_name(), c);
func_decl * r = dt.get_constructor_recognizer(c);
ctx->insert(r->get_name(), r);
for (func_decl * a : *dt.get_constructor_accessors(c)) {
ctx->insert(a->get_name(), a);
}
}
}
std::stringstream errstrm;
ctx->set_regular_stream(errstrm);
try {
if (!parse_smt2_commands(*ctx.get(), is)) {
ctx = nullptr;
SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str());
return of_ast_vector(v);
}
}
catch (z3_exception& e) {
errstrm << e.msg();
ctx = nullptr;
SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str());
return of_ast_vector(v);
}
for (expr* e : ctx->tracked_assertions()) {
v->m_ast_vector.push_back(e);
}
return of_ast_vector(v);
for (unsigned i = 0; i < num_sorts; ++i)
insert_sort(m, ctx, to_symbol(_sort_names[i]), to_sort(_sorts[i]));
return Z3_parser_context_parse_stream(c, ctx, true, is);
Z3_CATCH_RETURN(nullptr);
}
@ -155,11 +231,13 @@ extern "C" {
Z3_TRY;
LOG_Z3_eval_smtlib2_string(c, str);
if (!mk_c(c)->cmd()) {
mk_c(c)->cmd() = alloc(cmd_context, false, &(mk_c(c)->m()));
install_dl_cmds(*mk_c(c)->cmd());
install_opt_cmds(*mk_c(c)->cmd());
install_smt2_extra_cmds(*mk_c(c)->cmd());
mk_c(c)->cmd()->set_solver_factory(mk_smt_strategic_solver_factory());
auto* ctx = alloc(cmd_context, false, &(mk_c(c)->m()));
mk_c(c)->cmd() = ctx;
install_dl_cmds(*ctx);
install_opt_cmds(*ctx);
install_smt2_extra_cmds(*ctx);
ctx->register_plist();
ctx->set_solver_factory(mk_smt_strategic_solver_factory());
}
scoped_ptr<cmd_context>& ctx = mk_c(c)->cmd();
std::string s(str);
@ -180,4 +258,6 @@ extern "C" {
RETURN_Z3(mk_c(c)->mk_external_string(ous.str()));
Z3_CATCH_RETURN(mk_c(c)->mk_external_string(ous.str()));
}
};

View file

@ -256,7 +256,10 @@ extern "C" {
}
void solver_from_stream(Z3_context c, Z3_solver s, std::istream& is) {
scoped_ptr<cmd_context> ctx = alloc(cmd_context, false, &(mk_c(c)->m()));
auto& solver = *to_solver(s);
if (!solver.m_cmd_context)
solver.m_cmd_context = alloc(cmd_context, false, &(mk_c(c)->m()));
auto& ctx = solver.m_cmd_context;
ctx->set_ignore_check(true);
std::stringstream errstrm;
ctx->set_regular_stream(errstrm);
@ -272,6 +275,7 @@ extern "C" {
init_solver(c, s);
for (expr* e : ctx->tracked_assertions())
to_solver(s)->assert_expr(e);
ctx->reset_tracked_assertions();
to_solver_ref(s)->set_model_converter(ctx->get_model_converter());
}
@ -387,9 +391,11 @@ extern "C" {
bool new_model = params.get_bool("model", true);
if (old_model != new_model)
to_solver_ref(s)->set_produce_models(new_model);
param_descrs r;
to_solver_ref(s)->collect_param_descrs(r);
context_params::collect_solver_param_descrs(r);
param_descrs& r = to_solver(s)->m_param_descrs;
if(r.size () == 0) {
to_solver_ref(s)->collect_param_descrs(r);
context_params::collect_solver_param_descrs(r);
}
params.validate(r);
to_solver_ref(s)->updt_params(params);
}

View file

@ -42,8 +42,10 @@ struct Z3_solver_ref : public api::object {
scoped_ptr<solver_factory> m_solver_factory;
ref<solver> m_solver;
params_ref m_params;
param_descrs m_param_descrs;
symbol m_logic;
scoped_ptr<solver2smt2_pp> m_pp;
scoped_ptr<cmd_context> m_cmd_context;
mutex m_mux;
event_handler* m_eh;

View file

@ -19,6 +19,7 @@ Revision History:
#include "util/params.h"
#include "util/lbool.h"
#include "util/mutex.h"
#include "ast/ast.h"
#define Z3_TRY try {
@ -34,7 +35,7 @@ namespace api {
// Generic wrapper for ref-count objects exposed by the API
class object {
unsigned m_ref_count;
atomic<unsigned> m_ref_count;
unsigned m_id;
context& m_context;
public:
@ -87,7 +88,6 @@ inline lbool to_lbool(Z3_lbool b) { return static_cast<lbool>(b); }
struct Z3_params_ref : public api::object {
params_ref m_params;
Z3_params_ref(api::context& c): api::object(c) {}
~Z3_params_ref() override {}
};
inline Z3_params_ref * to_params(Z3_params p) { return reinterpret_cast<Z3_params_ref *>(p); }
@ -97,7 +97,6 @@ inline params_ref& to_param_ref(Z3_params p) { return p == nullptr ? const_cast<
struct Z3_param_descrs_ref : public api::object {
param_descrs m_descrs;
Z3_param_descrs_ref(api::context& c): api::object(c) {}
~Z3_param_descrs_ref() override {}
};
inline Z3_param_descrs_ref * to_param_descrs(Z3_param_descrs p) { return reinterpret_cast<Z3_param_descrs_ref *>(p); }

View file

@ -57,6 +57,8 @@ namespace z3 {
class param_descrs;
class ast;
class sort;
class constructors;
class constructor_list;
class func_decl;
class expr;
class solver;
@ -313,6 +315,34 @@ namespace z3 {
*/
func_decl tuple_sort(char const * name, unsigned n, char const * const * names, sort const* sorts, func_decl_vector & projs);
/**
\brief Create a recursive datatype over a single sort.
\c name is the name of the recursive datatype
\c n - the numer of constructors of the datatype
\c cs - the \c n constructors used to define the datatype
References to the datatype can be created using \ref datatype_sort.
*/
sort datatype(symbol const& name, constructors const& cs);
/**
\brief Create a set of mutually recursive datatypes.
\c n - number of recursive datatypes
\c names - array of names of length n
\c cons - array of constructor lists of length n
*/
sort_vector datatypes(unsigned n, symbol const* names,
constructor_list *const* cons);
/**
\brief a reference to a recursively defined datatype.
Expect that it gets defined as a \ref datatype.
*/
sort datatype_sort(symbol const& name);
/**
\brief create an uninterpreted sort with the name given by the string or symbol.
*/
@ -2894,7 +2924,7 @@ namespace z3 {
if (n == 0)
return ctx().bool_val(true);
else if (n == 1)
return operator[](0);
return operator[](0u);
else {
array<Z3_ast> args(n);
for (unsigned i = 0; i < n; i++)
@ -3330,6 +3360,98 @@ namespace z3 {
return func_decl(*this, tuple);
}
class constructor_list {
context& ctx;
Z3_constructor_list clist;
public:
constructor_list(constructors const& cs);
~constructor_list() { Z3_del_constructor_list(ctx, clist); }
operator Z3_constructor_list() const { return clist; }
};
class constructors {
friend class constructor_list;
context& ctx;
std::vector<Z3_constructor> cons;
std::vector<unsigned> num_fields;
public:
constructors(context& ctx): ctx(ctx) {}
~constructors() {
for (auto con : cons)
Z3_del_constructor(ctx, con);
}
void add(symbol const& name, symbol const& rec, unsigned n, symbol const* names, sort const* fields) {
array<unsigned> sort_refs(n);
array<Z3_sort> sorts(n);
array<Z3_symbol> _names(n);
for (unsigned i = 0; i < n; ++i) sorts[i] = fields[i], _names[i] = names[i];
cons.push_back(Z3_mk_constructor(ctx, name, rec, n, _names.ptr(), sorts.ptr(), sort_refs.ptr()));
num_fields.push_back(n);
}
Z3_constructor operator[](unsigned i) const { return cons[i]; }
unsigned size() const { return (unsigned)cons.size(); }
void query(unsigned i, func_decl& constructor, func_decl& test, func_decl_vector& accs) {
Z3_func_decl _constructor;
Z3_func_decl _test;
array<Z3_func_decl> accessors(num_fields[i]);
accs.resize(0);
Z3_query_constructor(ctx,
cons[i],
num_fields[i],
&_constructor,
&_test,
accessors.ptr());
constructor = func_decl(ctx, _constructor);
test = func_decl(ctx, _test);
for (unsigned j = 0; j < num_fields[i]; ++j)
accs.push_back(func_decl(ctx, accessors[j]));
}
};
constructor_list::constructor_list(constructors const& cs): ctx(cs.ctx) {
array<Z3_constructor> cons(cs.size());
for (unsigned i = 0; i < cs.size(); ++i)
cons[i] = cs[i];
clist = Z3_mk_constructor_list(ctx, cs.size(), cons.ptr());
}
inline sort context::datatype(symbol const& name, constructors const& cs) {
array<Z3_constructor> _cs(cs.size());
for (unsigned i = 0; i < cs.size(); ++i) _cs[i] = cs[i];
Z3_sort s = Z3_mk_datatype(*this, name, cs.size(), _cs.ptr());
check_error();
return sort(*this, s);
}
inline sort_vector context::datatypes(
unsigned n, symbol const* names,
constructor_list *const* cons) {
sort_vector result(*this);
array<Z3_symbol> _names(n);
array<Z3_sort> _sorts(n);
array<Z3_constructor_list> _cons(n);
for (unsigned i = 0; i < n; ++i)
_names[i] = names[i], _cons[i] = *cons[i];
Z3_mk_datatypes(*this, n, _names.ptr(), _sorts.ptr(), _cons.ptr());
for (unsigned i = 0; i < n; ++i)
result.push_back(sort(*this, _sorts[i]));
return result;
}
inline sort context::datatype_sort(symbol const& name) {
Z3_sort s = Z3_mk_datatype_sort(*this, name);
check_error();
return sort(*this, s);
}
inline sort context::uninterpreted_sort(char const* name) {
Z3_symbol _name = Z3_mk_string_symbol(*this, name);
return to_sort(*this, Z3_mk_uninterpreted_sort(*this, _name));

View file

@ -4620,16 +4620,16 @@ namespace Microsoft.Z3
/// </summary>
/// <remarks>
/// Produces a term that represents the conversion of the floating-point term t into a
/// bit-vector term of size sz in 2's complement format (signed when signed==true). If necessary,
/// bit-vector term of size sz in 2's complement format (signed when sign==true). If necessary,
/// the result will be rounded according to rounding mode rm.
/// </remarks>
/// <param name="rm">RoundingMode term.</param>
/// <param name="t">FloatingPoint term</param>
/// <param name="sz">Size of the resulting bit-vector.</param>
/// <param name="signed">Indicates whether the result is a signed or unsigned bit-vector.</param>
public BitVecExpr MkFPToBV(FPRMExpr rm, FPExpr t, uint sz, bool signed)
/// <param name="sign">Indicates whether the result is a signed or unsigned bit-vector.</param>
public BitVecExpr MkFPToBV(FPRMExpr rm, FPExpr t, uint sz, bool sign)
{
if (signed)
if (sign)
return new BitVecExpr(this, Native.Z3_mk_fpa_to_sbv(this.nCtx, rm.NativeObject, t.NativeObject, sz));
else
return new BitVecExpr(this, Native.Z3_mk_fpa_to_ubv(this.nCtx, rm.NativeObject, t.NativeObject, sz));

View file

@ -168,6 +168,16 @@ namespace Microsoft.Z3
{
return (Expr)base.Translate(ctx);
}
/// <summary>
/// Create a duplicate of expression.
/// This feature is to allow extending the life-time of expressions that were passed down as arguments
/// by the user propagator callbacks. By default the life-time of arguments to callbacks is within the
/// callback only.
/// </summary>
public Expr Dup() {
return Expr.Create(Context, NativeObject);
}
/// <summary>
/// Returns a string representation of the expression.

View file

@ -100,6 +100,17 @@ namespace Microsoft.Z3
return Native.Z3_mk_mul(nCtx, (uint)(ts?.Length ?? 0), ts);
}
/// <summary>
/// Create an expression representing <c>t[0] - t[1] - ...</c>.
/// </summary>
public Z3_ast MkSub(params Z3_ast[] t)
{
Debug.Assert(t != null);
Debug.Assert(t.All(a => a != IntPtr.Zero));
var ts = t.ToArray();
return Native.Z3_mk_sub(nCtx, (uint)(ts?.Length ?? 0), ts);
}
/// <summary>
/// Create an expression representing <c>t1 / t2</c>.
/// </summary>

View file

@ -41,6 +41,9 @@ namespace Microsoft.Z3
{
/// <summary>
/// Delegate type for fixed callback
/// Note that the life-time of the term and value only applies within the scope of the callback.
/// That means the term and value cannot be stored in an array, dictionary or similar and accessed after the callback has returned.
/// Use the functionality Dup on expressions to create a duplicate copy that extends the lifetime.
/// </summary>
public delegate void FixedEh(Expr term, Expr value);
@ -64,8 +67,7 @@ namespace Microsoft.Z3
// access managed objects through a static array.
// thread safety is ignored for now.
static List<UserPropagator> propagators = new List<UserPropagator>();
int id;
GCHandle gch;
Solver solver;
Context ctx;
Z3_solver_callback callback = IntPtr.Zero;
@ -107,27 +109,27 @@ namespace Microsoft.Z3
static void _push(voidp ctx, Z3_solver_callback cb)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
prop.Callback(() => prop.Push(), cb);
}
static void _pop(voidp ctx, Z3_solver_callback cb, uint num_scopes)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
prop.Callback(() => prop.Pop(num_scopes), cb);
}
static voidp _fresh(voidp _ctx, Z3_context new_context)
{
var prop = propagators[_ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(_ctx).Target;
var ctx = new Context(new_context);
var prop1 = prop.Fresh(prop.ctx);
return new IntPtr(prop1.id);
return GCHandle.ToIntPtr(prop1.gch);
}
static void _fixed(voidp ctx, Z3_solver_callback cb, Z3_ast _term, Z3_ast _value)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
using var term = Expr.Create(prop.ctx, _term);
using var value = Expr.Create(prop.ctx, _value);
prop.Callback(() => prop.fixed_eh(term, value), cb);
@ -135,13 +137,13 @@ namespace Microsoft.Z3
static void _final(voidp ctx, Z3_solver_callback cb)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
prop.Callback(() => prop.final_eh(), cb);
}
static void _eq(voidp ctx, Z3_solver_callback cb, Z3_ast a, Z3_ast b)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
using var s = Expr.Create(prop.ctx, a);
using var t = Expr.Create(prop.ctx, b);
prop.Callback(() => prop.eq_eh(s, t), cb);
@ -149,7 +151,7 @@ namespace Microsoft.Z3
static void _diseq(voidp ctx, Z3_solver_callback cb, Z3_ast a, Z3_ast b)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
using var s = Expr.Create(prop.ctx, a);
using var t = Expr.Create(prop.ctx, b);
prop.Callback(() => prop.diseq_eh(s, t), cb);
@ -157,14 +159,14 @@ namespace Microsoft.Z3
static void _created(voidp ctx, Z3_solver_callback cb, Z3_ast a)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
using var t = Expr.Create(prop.ctx, a);
prop.Callback(() => prop.created_eh(t), cb);
}
static void _decide(voidp ctx, Z3_solver_callback cb, ref Z3_ast a, ref uint idx, ref int phase)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
var t = Expr.Create(prop.ctx, a);
var u = t;
prop.callback = cb;
@ -179,14 +181,13 @@ namespace Microsoft.Z3
/// </summary>
public UserPropagator(Solver s)
{
id = propagators.Count;
propagators.Add(this);
gch = GCHandle.Alloc(this);
solver = s;
ctx = solver.Context;
push_eh = _push;
pop_eh = _pop;
fresh_eh = _fresh;
Native.Z3_solver_propagate_init(ctx.nCtx, solver.NativeObject, new IntPtr(id), push_eh, pop_eh, fresh_eh);
Native.Z3_solver_propagate_init(ctx.nCtx, solver.NativeObject, GCHandle.ToIntPtr(gch), push_eh, pop_eh, fresh_eh);
}
/// <summary>
@ -194,8 +195,7 @@ namespace Microsoft.Z3
/// </summary>
public UserPropagator(Context _ctx)
{
id = propagators.Count;
propagators.Add(this);
gch = GCHandle.Alloc(this);
solver = null;
ctx = _ctx;
}
@ -205,7 +205,7 @@ namespace Microsoft.Z3
/// </summary>
~UserPropagator()
{
propagators[id] = null;
gch.Free();
if (solver == null)
ctx.Dispose();
}

View file

@ -1717,8 +1717,8 @@ public class Context implements AutoCloseable {
* {@code [domain -> range]}, and {@code i} must have the sort
* {@code domain}. The sort of the result is {@code range}.
*
* @see #mkArraySort(Sort[], Sort)
* @see #mkStore
* @see #mkArraySort(Sort[], R)
* @see #mkStore(Expr<ArraySort<D, R>> a, Expr<D> i, Expr<R> v)
**/
public <D extends Sort, R extends Sort> Expr<R> mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
{
@ -1739,8 +1739,8 @@ public class Context implements AutoCloseable {
* {@code [domains -> range]}, and {@code args} must have the sorts
* {@code domains}. The sort of the result is {@code range}.
*
* @see #mkArraySort(Sort[], Sort)
* @see #mkStore
* @see #mkArraySort(Sort[], R)
* @see #mkStore(Expr<ArraySort<D, R>> a, Expr<D> i, Expr<R> v)
**/
public <R extends Sort> Expr<R> mkSelect(Expr<ArraySort<Sort, R>> a, Expr<?>[] args)
{
@ -1763,8 +1763,8 @@ public class Context implements AutoCloseable {
* {@code select}) on all indices except for {@code i}, where it
* maps to {@code v} (and the {@code select} of {@code a}
* with respect to {@code i} may be a different value).
* @see #mkArraySort(Sort[], Sort)
* @see #mkSelect
* @see #mkArraySort(Sort[], R)
* @see #mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
**/
public <D extends Sort, R extends Sort> ArrayExpr<D, R> mkStore(Expr<ArraySort<D, R>> a, Expr<D> i, Expr<R> v)
@ -1788,8 +1788,8 @@ public class Context implements AutoCloseable {
* {@code select}) on all indices except for {@code args}, where it
* maps to {@code v} (and the {@code select} of {@code a}
* with respect to {@code args} may be a different value).
* @see #mkArraySort(Sort[], Sort)
* @see #mkSelect
* @see #mkArraySort(Sort[], R)
* @see #mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
**/
public <R extends Sort> ArrayExpr<Sort, R> mkStore(Expr<ArraySort<Sort, R>> a, Expr<?>[] args, Expr<R> v)
@ -1806,8 +1806,8 @@ public class Context implements AutoCloseable {
* Remarks: The resulting term is an array, such
* that a {@code select} on an arbitrary index produces the value
* {@code v}.
* @see #mkArraySort(Sort[], Sort)
* @see #mkSelect
* @see #mkArraySort(Sort[], R)
* @see #mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
*
**/
public <D extends Sort, R extends Sort> ArrayExpr<D, R> mkConstArray(D domain, Expr<R> v)
@ -1826,9 +1826,9 @@ public class Context implements AutoCloseable {
* {@code f} must have type {@code range_1 .. range_n -> range}.
* {@code v} must have sort range. The sort of the result is
* {@code [domain_i -> range]}.
* @see #mkArraySort(Sort[], Sort)
* @see #mkSelect
* @see #mkStore
* @see #mkArraySort(Sort[], R)
* @see #mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
* @see #mkStore(Expr<ArraySort<D, R>> a, Expr<D> i, Expr<R> v)
**/
@SafeVarargs
@ -2476,7 +2476,7 @@ public class Context implements AutoCloseable {
*
* @return A Term with value {@code num}/{@code den}
* and sort Real
* @see #mkNumeral(String,Sort)
* @see #mkNumeral(String v, R ty)
**/
public RatNum mkReal(int num, int den)
{
@ -2612,7 +2612,7 @@ public class Context implements AutoCloseable {
* 'names' of the bound variables, and {@code body} is the body
* of the quantifier. Quantifiers are associated with weights indicating the
* importance of using the quantifier during instantiation.
* Note that the bound variables are de-Bruijn indices created using {@link #mkBound}.
* Note that the bound variables are de-Bruijn indices created using {#mkBound}.
* Z3 applies the convention that the last element in {@code names} and
* {@code sorts} refers to the variable with index 0, the second to last element
* of {@code names} and {@code sorts} refers to the variable
@ -2707,7 +2707,7 @@ public class Context implements AutoCloseable {
* with the sorts of the bound variables, {@code names} is an array with the
* 'names' of the bound variables, and {@code body} is the body of the
* lambda.
* Note that the bound variables are de-Bruijn indices created using {@link #mkBound}
* Note that the bound variables are de-Bruijn indices created using {#mkBound}
* Z3 applies the convention that the last element in {@code names} and
* {@code sorts} refers to the variable with index 0, the second to last element
* of {@code names} and {@code sorts} refers to the variable

View file

@ -242,7 +242,7 @@ public class Model extends Z3Object {
* values. We say this finite set is the "universe" of the sort.
*
* @see #getNumSorts
* @see #getSortUniverse
* @see #getSortUniverse(R s)
*
* @throws Z3Exception
**/

View file

@ -1,4 +1,4 @@
// Automatically generated file
#include<jni.h>
#include<stdlib.h>
#include"z3.h"

View file

@ -161,6 +161,12 @@ public class Quantifier extends BoolExpr
/**
* Create a quantified expression.
*
* @param ctx Context to create the quantifier on.
* @param isForall Quantifier type.
* @param sorts Sorts of bound variables.
* @param names Names of bound variables
* @param body Body of quantifier
* @param weight Weight used to indicate priority for qunatifier instantiation
* @param patterns Nullable patterns
* @param noPatterns Nullable noPatterns
* @param quantifierID Nullable quantifierID

View file

@ -9,7 +9,7 @@ The readme for the bindings themselves is located in [`PUBLISHED_README.md`](./P
You'll need to have emscripten set up, along with all of its dependencies. The easiest way to do that is with [emsdk](https://github.com/emscripten-core/emsdk).
Then run `npm i` to install dependencies, `npm run build-ts` to build the TypeScript wrapper, and `npm run build-wasm` to build the wasm artifact.
Then run `npm i` to install dependencies, `npm run build:ts` to build the TypeScript wrapper, and `npm run build:wasm` to build the wasm artifact.
## Tests

View file

@ -27,7 +27,7 @@
"typescript": "^4.5.4"
},
"engines": {
"node": ">=16 <18"
"node": ">=16"
}
},
"node_modules/@ampproject/remapping": {

View file

@ -12,13 +12,13 @@
"homepage": "https://github.com/Z3Prover/z3/tree/master/src/api/js",
"repository": "github:Z3Prover/z3",
"engines": {
"node": ">=16 <18"
"node": ">=16"
},
"browser": "build/browser.js",
"main": "build/node.js",
"types": "build/node.d.ts",
"files": [
"build/*.{js,d.ts,wasm}"
"build/**/*.{js,d.ts,wasm}"
],
"scripts": {
"build:ts": "run-s -l build:ts:generate build:ts:tsc",

View file

@ -59,6 +59,7 @@ let mk_context (settings:(string * string) list) =
Z3native.del_config cfg;
Z3native.set_ast_print_mode res (Z3enums.int_of_ast_print_mode PRINT_SMTLIB2_COMPLIANT);
Z3native.set_internal_error_handler res;
Z3native.enable_concurrent_dec_ref res;
res
module Symbol =

View file

@ -49,6 +49,13 @@ type context
val mk_context : (string * string) list -> context
(** Interaction logging for Z3
Interaction logs are used to record calls into the API into a text file.
The text file can be replayed using z3. It has to be the same version of z3
to ensure that low level codes emitted in a log are compatible with the
version of z3 replaying the log. The file suffix ".log" is understood
by z3 as the format of the file being an interaction log. You can use the
optional comman-line parameter "-log" to force z3 to treat an input file
as an interaction log.
Note that this is a global, static log and if multiple Context
objects are created, it logs the interaction with all of them. *)
module Log :
@ -927,10 +934,10 @@ end
module FiniteDomain :
sig
(** Create a new finite domain sort. *)
val mk_sort : context -> Symbol.symbol -> int -> Sort.sort
val mk_sort : context -> Symbol.symbol -> int64 -> Sort.sort
(** Create a new finite domain sort. *)
val mk_sort_s : context -> string -> int -> Sort.sort
val mk_sort_s : context -> string -> int64 -> Sort.sort
(** Indicates whether the term is of an array sort. *)
val is_finite_domain : Expr.expr -> bool
@ -939,7 +946,7 @@ sig
val is_lt : Expr.expr -> bool
(** The size of the finite domain sort. *)
val get_size : Sort.sort -> int
val get_size : Sort.sort -> int64
end
@ -2078,7 +2085,7 @@ sig
val mk_numeral_i : context -> int -> Sort.sort -> Expr.expr
(** Create a numeral of FloatingPoint sort from a sign bit and two integers. *)
val mk_numeral_i_u : context -> bool -> int -> int -> Sort.sort -> Expr.expr
val mk_numeral_i_u : context -> bool -> int64 -> int64 -> Sort.sort -> Expr.expr
(** Create a numeral of FloatingPoint sort from a string *)
val mk_numeral_s : context -> string -> Sort.sort -> Expr.expr
@ -2303,7 +2310,7 @@ sig
val get_numeral_exponent_string : context -> Expr.expr -> bool -> string
(** Return the exponent value of a floating-point numeral as a signed integer *)
val get_numeral_exponent_int : context -> Expr.expr -> bool -> bool * int
val get_numeral_exponent_int : context -> Expr.expr -> bool -> bool * int64
(** Return the exponent of a floating-point numeral as a bit-vector expression.
Remark: NaN's do not have a bit-vector exponent, so they are invalid arguments. *)
@ -2320,7 +2327,7 @@ sig
Remark: This function extracts the significand bits, without the
hidden bit or normalization. Throws an exception if the
significand does not fit into an int. *)
val get_numeral_significand_uint : context -> Expr.expr -> bool * int
val get_numeral_significand_uint : context -> Expr.expr -> bool * int64
(** Indicates whether a floating-point numeral is a NaN. *)
val is_numeral_nan : context -> Expr.expr -> bool

View file

@ -21,6 +21,7 @@ and solver_callback = ptr
and goal = ptr
and tactic = ptr
and params = ptr
and parser_context = ptr
and probe = ptr
and stats = ptr
and ast_vector = ptr

View file

@ -418,6 +418,7 @@ MK_PLUS_OBJ_NO_REF(constructor_list, 16)
MK_PLUS_OBJ_NO_REF(rcf_num, 16)
MK_PLUS_OBJ(params, 64)
MK_PLUS_OBJ(param_descrs, 64)
MK_PLUS_OBJ(parser_context, 64)
MK_PLUS_OBJ(model, 64)
MK_PLUS_OBJ(func_interp, 32)
MK_PLUS_OBJ(func_entry, 32)

View file

@ -129,6 +129,15 @@ def _configure_z3():
for key, val in cmake_options.items():
if type(val) is bool:
cmake_options[key] = str(val).upper()
# Allow command-line arguments to add and override Z3_ options
for i in range(len(sys.argv) - 1):
key = sys.argv[i]
if key.starts_with("Z3_"):
val = sys.argv[i + 1].upper()
if val == "TRUE" or val == "FALSE":
cmake_options[key] = val
cmake_args = [ '-D' + key + '=' + value for key,value in cmake_options.items() ]
args = [ 'cmake', *cmake_args, SRC_DIR ]
if subprocess.call(args, env=build_env, cwd=BUILD_DIR) != 0:

View file

@ -5345,6 +5345,10 @@ class DatatypeRef(ExprRef):
"""Return the datatype sort of the datatype expression `self`."""
return DatatypeSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx)
def DatatypeSort(name, ctx = None):
"""Create a reference to a sort that was declared, or will be declared, as a recursive datatype"""
ctx = _get_ctx(ctx)
return DatatypeSortRef(Z3_mk_datatype_sort(ctx.ref(), to_symbol(name, ctx)), ctx)
def TupleSort(name, sorts, ctx=None):
"""Create a named tuple sort base on a set of underlying sorts
@ -9192,6 +9196,25 @@ def _dict2darray(decls, ctx):
i = i + 1
return sz, _names, _decls
class ParserContext:
def __init__(self, ctx= None):
self.ctx = _get_ctx(ctx)
self.pctx = Z3_mk_parser_context(self.ctx.ref())
Z3_parser_context_inc_ref(self.ctx.ref(), self.pctx)
def __del__(self):
if self.ctx.ref() is not None and self.pctx is not None and Z3_parser_context_dec_ref is not None:
Z3_parser_context_dec_ref(self.ctx.ref(), self.pctx)
self.pctx = None
def add_sort(self, sort):
Z3_parser_context_add_sort(self.ctx.ref(), self.pctx, sort.as_ast())
def add_decl(self, decl):
Z3_parser_context_add_decl(self.ctx.ref(), self.pctx, decl.as_ast())
def from_string(self, s):
return AstVector(Z3_parser_context_from_string(self.ctx.ref(), self.pctx, s), self.ctx)
def parse_smt2_string(s, sorts={}, decls={}, ctx=None):
"""Parse a string in SMT 2.0 format using the given sorts and decls.
@ -11334,11 +11357,20 @@ def user_prop_pop(ctx, cb, num_scopes):
prop.cb = cb
prop.pop(num_scopes)
def to_ContextObj(ptr,):
ctx = ContextObj(ptr)
super(ctypes.c_void_p, ctx).__init__(ptr)
return ctx
def user_prop_fresh(id, ctx):
def user_prop_fresh(ctx, new_ctx):
_prop_closures.set_threaded()
prop = _prop_closures.get(id)
new_prop = prop.fresh()
prop = _prop_closures.get(ctx)
nctx = Context()
new_ctx = to_ContextObj(new_ctx)
nctx.ctx = new_ctx
nctx.eh = Z3_set_error_handler(new_ctx, z3_error_handler)
new_prop = prop.fresh(nctx)
_prop_closures.set(new_prop.id, new_prop)
return ctypes.c_void_p(new_prop.id)
@ -11401,6 +11433,7 @@ class UserPropagateBase:
ensure_prop_closures()
self.solver = s
self._ctx = None
self.fresh_ctx = None
self.cb = None
self.id = _prop_closures.insert(self)
self.fixed = None
@ -11408,12 +11441,7 @@ class UserPropagateBase:
self.eq = None
self.diseq = None
if ctx:
# TBD fresh is broken: ctx is not of the right type when we reach here.
self._ctx = Context()
#Z3_del_context(self._ctx.ctx)
#self._ctx.ctx = ctx
#self._ctx.eh = Z3_set_error_handler(ctx, z3_error_handler)
#Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT)
self.fresh_ctx = ctx
if s:
Z3_solver_propagate_init(self.ctx_ref(),
s.solver,
@ -11427,8 +11455,8 @@ class UserPropagateBase:
self._ctx.ctx = None
def ctx(self):
if self._ctx:
return self._ctx
if self.fresh_ctx:
return self.fresh_ctx
else:
return self.solver.ctx
@ -11438,25 +11466,29 @@ class UserPropagateBase:
def add_fixed(self, fixed):
assert not self.fixed
assert not self._ctx
Z3_solver_propagate_fixed(self.ctx_ref(), self.solver.solver, _user_prop_fixed)
if self.solver:
Z3_solver_propagate_fixed(self.ctx_ref(), self.solver.solver, _user_prop_fixed)
self.fixed = fixed
def add_final(self, final):
assert not self.final
assert not self._ctx
Z3_solver_propagate_final(self.ctx_ref(), self.solver.solver, _user_prop_final)
if self.solver:
Z3_solver_propagate_final(self.ctx_ref(), self.solver.solver, _user_prop_final)
self.final = final
def add_eq(self, eq):
assert not self.eq
assert not self._ctx
Z3_solver_propagate_eq(self.ctx_ref(), self.solver.solver, _user_prop_eq)
if self.solver:
Z3_solver_propagate_eq(self.ctx_ref(), self.solver.solver, _user_prop_eq)
self.eq = eq
def add_diseq(self, diseq):
assert not self.diseq
assert not self._ctx
Z3_solver_propagate_diseq(self.ctx_ref(), self.solver.solver, _user_prop_diseq)
if self.solver:
Z3_solver_propagate_diseq(self.ctx_ref(), self.solver.solver, _user_prop_diseq)
self.diseq = diseq
def push(self):
@ -11465,7 +11497,7 @@ class UserPropagateBase:
def pop(self, num_scopes):
raise Z3Exception("pop needs to be overwritten")
def fresh(self):
def fresh(self, new_ctx):
raise Z3Exception("fresh needs to be overwritten")
def add(self, e):

View file

@ -216,6 +216,13 @@ class ParamDescrs(ctypes.c_void_p):
def from_param(obj):
return obj
class ParserContextObj(ctypes.c_void_p):
def __init__(self, pc):
self._as_parameter_ = pc
def from_param(obj):
return obj
class FuncInterpObj(ctypes.c_void_p):
def __init__(self, f):

View file

@ -20,6 +20,7 @@ DEFINE_TYPE(Z3_constructor);
DEFINE_TYPE(Z3_constructor_list);
DEFINE_TYPE(Z3_params);
DEFINE_TYPE(Z3_param_descrs);
DEFINE_TYPE(Z3_parser_context);
DEFINE_TYPE(Z3_goal);
DEFINE_TYPE(Z3_tactic);
DEFINE_TYPE(Z3_probe);
@ -58,6 +59,7 @@ DEFINE_TYPE(Z3_rcf_num);
- \c Z3_constructor_list: list of constructors for a (recursive) datatype.
- \c Z3_params: parameter set used to configure many components such as: simplifiers, tactics, solvers, etc.
- \c Z3_param_descrs: provides a collection of parameter names, their types, default values and documentation strings. Solvers, tactics, and other objects accept different collection of parameters.
- \c Z3_parser_context: context for incrementally parsing strings. Declarations can be added incrementally to the parser state.
- \c Z3_model: model for the constraints asserted into the logical context.
- \c Z3_func_interp: interpretation of a function in a model.
- \c Z3_func_entry: representation of the value of a \c Z3_func_interp at a particular point.
@ -1413,6 +1415,7 @@ typedef enum
def_Type('CONSTRUCTOR_LIST', 'Z3_constructor_list', 'ConstructorList')
def_Type('SOLVER', 'Z3_solver', 'SolverObj')
def_Type('SOLVER_CALLBACK', 'Z3_solver_callback', 'SolverCallbackObj')
def_Type('PARSER_CONTEXT', 'Z3_parser_context', 'ParserContextObj')
def_Type('GOAL', 'Z3_goal', 'GoalObj')
def_Type('TACTIC', 'Z3_tactic', 'TacticObj')
def_Type('PARAMS', 'Z3_params', 'Params')
@ -1702,6 +1705,16 @@ extern "C" {
void Z3_API Z3_interrupt(Z3_context c);
/**
\brief use concurrency control for dec-ref.
Reference counting decrements are allowed in separate threads from the context.
If this setting is not invoked, reference counting decrements are not going to be thread safe.
def_API('Z3_enable_concurrent_dec_ref', VOID, (_in(CONTEXT),))
*/
void Z3_API Z3_enable_concurrent_dec_ref(Z3_context c);
/**@}*/
/** @name Parameters */
@ -5827,6 +5840,55 @@ extern "C" {
Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str);
/**
\brief Create a parser context.
A parser context maintains state between calls to \c Z3_parser_context_parse_string
where the caller can pass in a set of SMTLIB2 commands.
It maintains all the declarations from previous calls together with
of sorts and function declarations (including 0-ary) that are added directly to the context.
def_API('Z3_mk_parser_context', PARSER_CONTEXT, (_in(CONTEXT),))
*/
Z3_parser_context Z3_API Z3_mk_parser_context(Z3_context c);
/**
\brief Increment the reference counter of the given \c Z3_parser_context object.
def_API('Z3_parser_context_inc_ref', VOID, (_in(CONTEXT), _in(PARSER_CONTEXT)))
*/
void Z3_API Z3_parser_context_inc_ref(Z3_context c, Z3_parser_context pc);
/**
\brief Decrement the reference counter of the given \c Z3_parser_context object.
def_API('Z3_parser_context_dec_ref', VOID, (_in(CONTEXT), _in(PARSER_CONTEXT)))
*/
void Z3_API Z3_parser_context_dec_ref(Z3_context c, Z3_parser_context pc);
/**
\brief Add a sort declaration.
def_API('Z3_parser_context_add_sort', VOID, (_in(CONTEXT), _in(PARSER_CONTEXT), _in(SORT)))
*/
void Z3_API Z3_parser_context_add_sort(Z3_context c, Z3_parser_context pc, Z3_sort s);
/**
\brief Add a function declaration.
def_API('Z3_parser_context_add_decl', VOID, (_in(CONTEXT), _in(PARSER_CONTEXT), _in(FUNC_DECL)))
*/
void Z3_API Z3_parser_context_add_decl(Z3_context c, Z3_parser_context pc, Z3_func_decl f);
/**
\brief Parse a string of SMTLIB2 commands. Return assertions.
def_API('Z3_parser_context_from_string', AST_VECTOR, (_in(CONTEXT), _in(PARSER_CONTEXT), _in(STRING)))
*/
Z3_ast_vector Z3_API Z3_parser_context_from_string(Z3_context c, Z3_parser_context pc, Z3_string s);
/**@}*/
/** @name Error Handling */
@ -6811,7 +6873,7 @@ extern "C" {
/**
\brief register a callback when a new expression with a registered function is used by the solver
The registered function appears at the top level and is created using \ref Z3_propagate_solver_declare.
The registered function appears at the top level and is created using \ref Z3_solver_propagate_declare.
def_API('Z3_solver_propagate_created', VOID, (_in(CONTEXT), _in(SOLVER), _fnptr(Z3_created_eh)))
*/
@ -6837,7 +6899,7 @@ extern "C" {
/**
Create uninterpreted function declaration for the user propagator.
When expressions using the function are created by the solver invoke a callback
to \ref \Z3_solver_progate_created with arguments
to \ref \Z3_solver_propagate_created with arguments
1. context and callback solve
2. declared_expr: expression using function that was used as the top-level symbol
3. declared_id: a unique identifier (unique within the current scope) to track the expression.

View file

@ -471,26 +471,31 @@ bool compare_nodes(ast const * n1, ast const * n2) {
return
to_var(n1)->get_idx() == to_var(n2)->get_idx() &&
to_var(n1)->get_sort() == to_var(n2)->get_sort();
case AST_QUANTIFIER:
case AST_QUANTIFIER: {
quantifier const* q1 = to_quantifier(n1);
quantifier const* q2 = to_quantifier(n2);
return
to_quantifier(n1)->get_kind() == to_quantifier(n2)->get_kind() &&
to_quantifier(n1)->get_num_decls() == to_quantifier(n2)->get_num_decls() &&
compare_arrays(to_quantifier(n1)->get_decl_sorts(),
to_quantifier(n2)->get_decl_sorts(),
to_quantifier(n1)->get_num_decls()) &&
compare_arrays(to_quantifier(n1)->get_decl_names(),
to_quantifier(n2)->get_decl_names(),
to_quantifier(n1)->get_num_decls()) &&
to_quantifier(n1)->get_expr() == to_quantifier(n2)->get_expr() &&
to_quantifier(n1)->get_weight() == to_quantifier(n2)->get_weight() &&
to_quantifier(n1)->get_num_patterns() == to_quantifier(n2)->get_num_patterns() &&
compare_arrays(to_quantifier(n1)->get_patterns(),
to_quantifier(n2)->get_patterns(),
to_quantifier(n1)->get_num_patterns()) &&
to_quantifier(n1)->get_num_no_patterns() == to_quantifier(n2)->get_num_no_patterns() &&
compare_arrays(to_quantifier(n1)->get_no_patterns(),
to_quantifier(n2)->get_no_patterns(),
to_quantifier(n1)->get_num_no_patterns());
q1->get_kind() == q2->get_kind() &&
q1->get_num_decls() == q2->get_num_decls() &&
compare_arrays(q1->get_decl_sorts(),
q2->get_decl_sorts(),
q1->get_num_decls()) &&
compare_arrays(q1->get_decl_names(),
q2->get_decl_names(),
q1->get_num_decls()) &&
q1->get_expr() == q2->get_expr() &&
q1->get_weight() == q2->get_weight() &&
q1->get_num_patterns() == q2->get_num_patterns() &&
((q1->get_qid().is_numerical() && q2->get_qid().is_numerical()) ||
(q1->get_qid() == q2->get_qid())) &&
compare_arrays(q1->get_patterns(),
q2->get_patterns(),
q1->get_num_patterns()) &&
q1->get_num_no_patterns() == q2->get_num_no_patterns() &&
compare_arrays(q1->get_no_patterns(),
q2->get_no_patterns(),
q1->get_num_no_patterns());
}
default:
UNREACHABLE();
}

View file

@ -67,6 +67,30 @@ namespace recfun {
m_decl = m.mk_func_decl(s, arity, domain, range, info);
}
def* def::copy(util& dst, ast_translation& tr) {
SASSERT(&dst.m() == &tr.to());
sort_ref_vector domain(tr.to());
sort_ref range(tr(m_range.get()), tr.to());
for (auto* s : m_domain)
domain.push_back(tr(s));
family_id fid = dst.get_family_id();
bool is_generated = m_decl->get_parameter(0).get_int() != 0;
def* r = alloc(def, tr.to(), fid, m_name, domain.size(), domain.data(), range, is_generated);
r->m_rhs = tr(m_rhs.get());
for (auto* v : m_vars)
r->m_vars.push_back(tr(v));
for (auto const& c1 : m_cases) {
r->m_cases.push_back(case_def(tr.to()));
auto& c2 = r->m_cases.back();
c2.m_pred = tr(c1.m_pred.get());
c2.m_guards = tr(c1.m_guards);
c2.m_rhs = tr(c1.m_rhs.get());
c2.m_def = r;
c2.m_immediate = c1.m_immediate;
}
return r;
}
bool def::contains_def(util& u, expr * e) {
struct def_find_p : public i_expr_pred {
util& u;
@ -415,6 +439,19 @@ namespace recfun {
return promise_def(&u(), d);
}
void plugin::inherit(decl_plugin* other, ast_translation& tr) {
for (auto [k, v] : static_cast<plugin*>(other)->m_defs) {
func_decl_ref f(tr(k), tr.to());
if (m_defs.contains(f))
continue;
def* d = v->copy(u(), tr);
m_defs.insert(f, d);
for (case_def & c : d->get_cases())
m_case_defs.insert(c.get_decl(), &c);
}
}
promise_def plugin::ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) {
def* d = u().decl_fun(name, n, params, range, is_generated);
erase_def(d->get_decl());

View file

@ -21,6 +21,7 @@ Revision History:
#include "ast/ast.h"
#include "ast/ast_pp.h"
#include "ast/ast_translation.h"
#include "util/obj_hashtable.h"
namespace recfun {
@ -62,6 +63,12 @@ namespace recfun {
def * m_def; //<! definition this is a part of
bool m_immediate; //<! does `rhs` contain no defined_fun/case_pred?
case_def(ast_manager& m):
m_pred(m),
m_guards(m),
m_rhs(m)
{}
case_def(ast_manager & m,
family_id fid,
def * d,
@ -132,6 +139,8 @@ namespace recfun {
bool is_fun_macro() const { return m_cases.size() == 1; }
bool is_fun_defined() const { return !is_fun_macro(); }
def* copy(util& dst, ast_translation& tr);
};
// definition to be complete (missing RHS)
@ -205,6 +214,8 @@ namespace recfun {
expr_ref redirect_ite(replace& subst, unsigned n, var * const* vars, expr * e);
void inherit(decl_plugin* other, ast_translation& tr) override;
};
}

View file

@ -37,21 +37,57 @@ br_status char_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * co
case OP_CHAR_CONST:
break;
case OP_CHAR_LE:
st = mk_char_le(args[0], args[1], result);
break;
case OP_CHAR_TO_INT:
st = mk_char_to_int(args[0], result);
break;
case OP_CHAR_TO_BV:
st = mk_char_to_bv(args[0], result);
break;
case OP_CHAR_FROM_BV:
st = mk_char_from_bv(args[0], result);
break;
case OP_CHAR_IS_DIGIT:
st = mk_char_is_digit(args[0], result);
break;
}
return st;
}
br_status char_rewriter::mk_char_is_digit(expr* a, expr_ref& result) {
unsigned n;
if (!m_char->is_const_char(a, n))
return BR_FAILED;
result = m.mk_bool_val('0' <= n && n <= '9');
return BR_DONE;
}
br_status char_rewriter::mk_char_to_bv(expr* a, expr_ref& result) {
return BR_FAILED;
}
br_status char_rewriter::mk_char_le(expr* a, expr* b, expr_ref& result) {
unsigned na = 0, nb = 0;
if (m_char->is_const_char(a, na)) {
if (na == 0) {
result = m.mk_true();
return BR_DONE;
}
}
if (m_char->is_const_char(b, nb)) {
if (na != 0) {
result = m.mk_bool_val(na <= nb);
return BR_DONE;
}
if (nb == m_char->max_char()) {
result = m.mk_true();
return BR_DONE;
}
}
return BR_FAILED;
}
br_status char_rewriter::mk_char_from_bv(expr* e, expr_ref& result) {
bv_util bv(m);
rational n;

View file

@ -35,6 +35,12 @@ class char_rewriter {
br_status mk_char_to_int(expr* e, expr_ref& result);
br_status mk_char_le(expr* a, expr* b, expr_ref& result);
br_status mk_char_is_digit(expr* a, expr_ref& result);
br_status mk_char_to_bv(expr* a, expr_ref& result);
public:
char_rewriter(ast_manager& m);

View file

@ -532,7 +532,12 @@ func_decl* seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p
case _OP_STRING_FROM_CHAR: {
if (!(num_parameters == 1 && parameters[0].is_int()))
m.raise_exception("character literal expects integer parameter");
zstring zs(parameters[0].get_int());
int i = parameters[0].get_int();
if (i < 0)
m.raise_exception("character literal expects a non-negative integer parameter");
if (i > (int)m_char_plugin->max_char())
m.raise_exception("character literal is out of bounds");
zstring zs(i);
parameter p(zs);
return m.mk_const_decl(m_stringc_sym, m_string,func_decl_info(m_family_id, OP_STRING_CONST, 1, &p));
}
@ -966,6 +971,22 @@ bool seq_util::str::is_len_sub(expr const* s, expr*& l, expr*& u, rational& k) c
return false;
}
bool seq_util::str::is_concat_of_units(expr* s) const {
ptr_vector<expr> todo;
todo.push_back(s);
while (!todo.empty()) {
expr* e = todo.back();
todo.pop_back();
if (is_empty(e) || is_unit(e))
continue;
if (is_concat(e))
todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
else
return false;
}
return true;
}
bool seq_util::str::is_unit_string(expr const* s, expr_ref& c) const {
zstring z;
expr* ch = nullptr;

View file

@ -375,6 +375,7 @@ public:
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;
bool is_concat_of_units(expr* n) const;
/*
tests if s is a single character string(c) or a unit (c)

View file

@ -219,9 +219,8 @@ ATOMIC_CMD(get_proof_graph_cmd, "get-proof-graph", "retrieve proof and print it
pr = ctx.get_check_sat_result()->get_proof();
if (pr == 0)
throw cmd_exception("proof is not available");
if (ctx.well_sorted_check_enabled() && !is_well_sorted(ctx.m(), pr)) {
if (ctx.well_sorted_check_enabled() && !is_well_sorted(ctx.m(), pr))
throw cmd_exception("proof is not well sorted");
}
context_params& params = ctx.params();
const std::string& file = params.m_dot_proof_file;
@ -235,11 +234,11 @@ static void print_core(cmd_context& ctx) {
ctx.regular_stream() << "(";
bool first = true;
for (expr* e : core) {
if (first)
first = false;
else
ctx.regular_stream() << " ";
ctx.regular_stream() << mk_ismt2_pp(e, ctx.m());
if (first)
first = false;
else
ctx.regular_stream() << " ";
ctx.regular_stream() << mk_ismt2_pp(e, ctx.m());
}
ctx.regular_stream() << ")" << std::endl;
}
@ -260,9 +259,8 @@ ATOMIC_CMD(get_unsat_assumptions_cmd, "get-unsat-assumptions", "retrieve subset
return;
if (!ctx.produce_unsat_assumptions())
throw cmd_exception("unsat assumptions construction is not enabled, use command (set-option :produce-unsat-assumptions true)");
if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat) {
if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat)
throw cmd_exception("unsat assumptions is not available");
}
print_core(ctx);
});
@ -410,6 +408,15 @@ class set_option_cmd : public set_get_option_cmd {
}
}
static void check_no_assertions(cmd_context & ctx, symbol const & opt_name) {
if (ctx.has_assertions()) {
std::string msg = "error setting '";
msg += opt_name.str();
msg += "', option value cannot be modified after assertions have been added";
throw cmd_exception(std::move(msg));
}
}
void set_param(cmd_context & ctx, char const * value) {
try {
gparams::set(m_option, value);
@ -437,11 +444,11 @@ class set_option_cmd : public set_get_option_cmd {
ctx.set_interactive_mode(to_bool(value));
}
else if (m_option == m_produce_proofs) {
check_not_initialized(ctx, m_produce_proofs);
check_no_assertions(ctx, m_produce_proofs);
ctx.set_produce_proofs(to_bool(value));
}
else if (m_option == m_produce_unsat_cores) {
check_not_initialized(ctx, m_produce_unsat_cores);
check_no_assertions(ctx, m_produce_unsat_cores);
ctx.set_produce_unsat_cores(to_bool(value));
}
else if (m_option == m_produce_unsat_assumptions) {

View file

@ -538,22 +538,9 @@ public:
cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l):
m_main_ctx(main_ctx),
m_logic(l),
m_interactive_mode(false),
m_global_decls(false),
m_print_success(m_params.m_smtlib2_compliant),
m_random_seed(0),
m_produce_unsat_cores(false),
m_produce_unsat_assumptions(false),
m_produce_assignments(false),
m_status(UNKNOWN),
m_numeral_as_real(false),
m_ignore_check(false),
m_exit_on_error(false),
m_manager(m),
m_own_manager(m == nullptr),
m_manager_initialized(false),
m_pmanager(nullptr),
m_sexpr_manager(nullptr),
m_regular("stdout", std::cout),
m_diagnostic("stderr", std::cerr) {
SASSERT(m != 0 || !has_manager());
@ -607,6 +594,7 @@ void cmd_context::global_params_updated() {
m_params.updt_params();
if (m_params.m_smtlib2_compliant)
m_print_success = true;
set_produce_proofs(m_params.m_proof);
if (m_solver) {
params_ref p;
if (!m_params.m_auto_config)
@ -626,13 +614,14 @@ void cmd_context::set_produce_models(bool f) {
void cmd_context::set_produce_unsat_cores(bool f) {
// can only be set before initialization
SASSERT(!has_manager());
SASSERT(!has_assertions());
m_params.m_unsat_core |= f;
}
void cmd_context::set_produce_proofs(bool f) {
// can only be set before initialization
SASSERT(!has_manager());
SASSERT(!has_assertions() || m_params.m_proof == f);
if (has_manager())
m().toggle_proof_mode(f ? PGM_ENABLED : PGM_DISABLED);
m_params.m_proof = f;
}
@ -835,15 +824,16 @@ bool cmd_context::set_logic(symbol const & s) {
TRACE("cmd_context", tout << s << "\n";);
if (has_logic())
throw cmd_exception("the logic has already been set");
if (has_manager() && m_main_ctx)
if (has_assertions() && m_main_ctx)
throw cmd_exception("logic must be set before initialization");
if (!smt_logics::supported_logic(s)) {
if (!smt_logics::supported_logic(s))
return false;
}
m_logic = s;
if (smt_logics::logic_has_reals_only(s)) {
if (m_solver)
mk_solver();
if (smt_logics::logic_has_reals_only(s))
m_numeral_as_real = true;
}
return true;
}
@ -1837,6 +1827,10 @@ void cmd_context::add_declared_functions(model& mdl) {
}
void cmd_context::display_sat_result(lbool r) {
if (has_manager() && m().has_trace_stream()) {
m().trace_stream().flush();
}
switch (r) {
case l_true:
regular_stream() << "sat" << std::endl;
@ -2203,22 +2197,25 @@ expr_ref_vector cmd_context::tracked_assertions() {
for (unsigned i = 0; i < assertions().size(); ++i) {
expr* an = assertion_names()[i];
expr* asr = assertions()[i];
if (an) {
if (an)
result.push_back(m().mk_implies(an, asr));
}
else {
else
result.push_back(asr);
}
}
}
else {
for (expr * e : assertions()) {
for (expr * e : assertions())
result.push_back(e);
}
}
return result;
}
void cmd_context::reset_tracked_assertions() {
m_assertion_names.reset();
for (expr* a : m_assertions)
m().dec_ref(a);
m_assertions.reset();
}
void cmd_context::display_assertions() {
if (!m_interactive_mode)
@ -2254,9 +2251,8 @@ format_ns::format * cmd_context::pp(sort * s) const {
}
cmd_context::pp_env & cmd_context::get_pp_env() const {
if (m_pp_env.get() == nullptr) {
if (m_pp_env.get() == nullptr)
const_cast<cmd_context*>(this)->m_pp_env = alloc(pp_env, *const_cast<cmd_context*>(this));
}
return *(m_pp_env.get());
}
@ -2314,9 +2310,8 @@ void cmd_context::display_smt2_benchmark(std::ostream & out, unsigned num, expr
out << "(set-logic " << logic << ")" << std::endl;
// collect uninterpreted function declarations
decl_collector decls(m());
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; i++)
decls.visit(assertions[i]);
}
// TODO: display uninterpreted sort decls, and datatype decls.
@ -2342,9 +2337,8 @@ void cmd_context::slow_progress_sample() {
svector<symbol> labels;
m_solver->get_labels(labels);
regular_stream() << "(labels";
for (symbol const& s : labels) {
for (symbol const& s : labels)
regular_stream() << " " << s;
}
regular_stream() << "))" << std::endl;
}

View file

@ -194,21 +194,21 @@ public:
protected:
ast_context_params m_params;
ast_context_params m_params;
bool m_main_ctx;
symbol m_logic;
bool m_interactive_mode;
bool m_global_decls;
bool m_interactive_mode = false;
bool m_global_decls = false;
bool m_print_success;
unsigned m_random_seed;
bool m_produce_unsat_cores;
bool m_produce_unsat_assumptions;
bool m_produce_assignments;
status m_status;
bool m_numeral_as_real;
bool m_ignore_check; // used by the API to disable check-sat() commands when parsing SMT 2.0 files.
bool m_exit_on_error;
bool m_allow_duplicate_declarations { false };
unsigned m_random_seed = 0;
bool m_produce_unsat_cores = false;
bool m_produce_unsat_assumptions = false;
bool m_produce_assignments = false;
status m_status = UNKNOWN;
bool m_numeral_as_real = false;
bool m_ignore_check = false; // used by the API to disable check-sat() commands when parsing SMT 2.0 files.
bool m_exit_on_error = false;
bool m_allow_duplicate_declarations = false;
static std::ostringstream g_error_stream;
@ -216,9 +216,9 @@ protected:
sref_vector<generic_model_converter> m_mcs;
ast_manager * m_manager;
bool m_own_manager;
bool m_manager_initialized;
pdecl_manager * m_pmanager;
sexpr_manager * m_sexpr_manager;
bool m_manager_initialized = false;
pdecl_manager * m_pmanager = nullptr;
sexpr_manager * m_sexpr_manager = nullptr;
check_logic m_check_logic;
stream_ref m_regular;
stream_ref m_diagnostic;
@ -362,6 +362,7 @@ public:
bool produce_unsat_cores() const;
bool well_sorted_check_enabled() const;
bool validate_model_enabled() const;
bool has_assertions() const { return !m_assertions.empty(); }
void set_produce_models(bool flag);
void set_produce_unsat_cores(bool flag);
void set_produce_proofs(bool flag);
@ -485,6 +486,7 @@ public:
ptr_vector<expr> const& assertions() const { return m_assertions; }
ptr_vector<expr> const& assertion_names() const { return m_assertion_names; }
expr_ref_vector tracked_assertions();
void reset_tracked_assertions();
/**
\brief Hack: consume assertions if there are no scopes.

View file

@ -377,8 +377,8 @@ public:
};
class get_interpolant_cmd : public cmd {
expr* m_a;
expr* m_b;
scoped_ptr<expr_ref> m_a;
scoped_ptr<expr_ref> m_b;
public:
get_interpolant_cmd():cmd("get-interpolant") {}
char const * get_usage() const override { return "<expr> <expr>"; }
@ -388,17 +388,24 @@ public:
return CPK_EXPR;
}
void set_next_arg(cmd_context& ctx, expr * arg) override {
if (m_a == nullptr)
m_a = arg;
ast_manager& m = ctx.m();
if (!m.is_bool(arg))
throw default_exception("argument to interpolation is not Boolean");
if (!m_a)
m_a = alloc(expr_ref, arg, m);
else
m_b = arg;
m_b = alloc(expr_ref, arg, m);
}
void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; }
void execute(cmd_context & ctx) override {
ast_manager& m = ctx.m();
qe::interpolator mbi(m);
if (!m_a || !m_b)
throw default_exception("interpolation requires two arguments");
if (!m.is_bool(*m_a) || !m.is_bool(*m_b))
throw default_exception("interpolation requires two Boolean arguments");
expr_ref itp(m);
mbi.pogo(ctx.get_solver_factory(), m_a, m_b, itp);
mbi.pogo(ctx.get_solver_factory(), *m_a, *m_b, itp);
ctx.regular_stream() << itp << "\n";
}
};

View file

@ -1741,6 +1741,87 @@ namespace dd {
return (*this) * rational::power_of_two(n);
}
/**
* \brief substitute variable v by r.
* This base line implementation is simplistic and does not use operator caching.
*/
pdd pdd::subst_pdd(unsigned v, pdd const& r) const {
if (is_val())
return *this;
if (m.m_var2level[var()] < m.m_var2level[v])
return *this;
pdd l = lo().subst_pdd(v, r);
pdd h = hi().subst_pdd(v, r);
if (var() == v)
return r*h + l;
else if (l == lo() && h == hi())
return *this;
else
return m.mk_var(var())*h + l;
}
std::pair<unsigned_vector, pdd> pdd::var_factors() const {
if (is_val())
return { unsigned_vector(), *this };
unsigned v = var();
if (lo().is_val()) {
if (!lo().is_zero())
return { unsigned_vector(), *this };
auto [vars, p] = hi().var_factors();
vars.push_back(v);
return {vars, p};
}
auto [lo_vars, q] = lo().var_factors();
if (lo_vars.empty())
return { unsigned_vector(), *this };
unsigned_vector lo_and_hi;
auto merge = [&](unsigned_vector& lo_vars, unsigned_vector& hi_vars) {
unsigned ir = 0, jr = 0;
for (unsigned i = 0, j = 0; i < lo_vars.size() || j < hi_vars.size(); ) {
if (i == lo_vars.size())
hi_vars[jr++] = hi_vars[j++];
else if (j == hi_vars.size())
lo_vars[ir++] = lo_vars[i++];
else if (lo_vars[i] == hi_vars[j]) {
lo_and_hi.push_back(lo_vars[i]);
++i;
++j;
}
else if (m.m_var2level[lo_vars[i]] > m.m_var2level[hi_vars[j]])
hi_vars[jr++] = hi_vars[j++];
else
lo_vars[ir++] = lo_vars[i++];
}
lo_vars.shrink(ir);
hi_vars.shrink(jr);
};
auto mul = [&](unsigned_vector const& vars, pdd p) {
for (auto v : vars)
p *= m.mk_var(v);
return p;
};
auto [hi_vars, p] = hi().var_factors();
if (lo_vars.back() == v) {
lo_vars.pop_back();
merge(lo_vars, hi_vars);
lo_and_hi.push_back(v);
return { lo_and_hi, mul(lo_vars, q) + mul(hi_vars, p) };
}
if (hi_vars.empty())
return { unsigned_vector(), *this };
merge(lo_vars, hi_vars);
hi_vars.push_back(v);
if (lo_and_hi.empty())
return { unsigned_vector(), *this };
else
return { lo_and_hi, mul(lo_vars, q) + mul(hi_vars, p) };
}
std::ostream& operator<<(std::ostream& out, pdd const& b) { return b.display(out); }
void pdd_iterator::next() {

View file

@ -447,11 +447,22 @@ namespace dd {
bool resolve(unsigned v, pdd const& other, pdd& result) { return m.resolve(v, *this, other, result); }
pdd reduce(unsigned v, pdd const& other) const { return m.reduce(v, *this, other); }
/**
* \brief factor out variables
*/
std::pair<unsigned_vector, pdd> var_factors() const;
pdd subst_val0(vector<std::pair<unsigned, rational>> const& s) const { return m.subst_val0(*this, s); }
pdd subst_val(pdd const& s) const { return m.subst_val(*this, s); }
pdd subst_val(vector<std::pair<unsigned, rational>> const& s) const { return m.subst_val0(*this, s); }
pdd subst_val(unsigned v, rational const& val) const { return m.subst_val(*this, v, val); }
pdd subst_add(unsigned var, rational const& val) { return m.subst_add(*this, var, val); }
/**
* \brief substitute variable v by r.
*/
pdd subst_pdd(unsigned v, pdd const& r) const;
std::ostream& display(std::ostream& out) const { return m.display(out, *this); }
bool operator==(pdd const& other) const { return root == other.root; }
bool operator!=(pdd const& other) const { return root != other.root; }

View file

@ -27,7 +27,33 @@ typedef dep_intervals::with_deps_t w_dep;
class pdd_interval {
dep_intervals& m_dep_intervals;
std::function<void (unsigned, bool, scoped_dep_interval&)> m_var2interval;
// retrieve intervals after distributing multiplication over addition.
template <w_dep wd>
void get_interval_distributed(pdd const& p, scoped_dep_interval& i, scoped_dep_interval& ret) {
bool deps = wd == w_dep::with_deps;
if (p.is_val()) {
if (deps)
m_dep_intervals.mul<dep_intervals::with_deps>(p.val(), i, ret);
else
m_dep_intervals.mul<dep_intervals::without_deps>(p.val(), i, ret);
return;
}
scoped_dep_interval hi(m()), lo(m()), t(m()), a(m());
get_interval_distributed<wd>(p.lo(), i, lo);
m_var2interval(p.var(), deps, a);
if (deps) {
m_dep_intervals.mul<dep_intervals::with_deps>(a, i, t);
get_interval_distributed<wd>(p.hi(), t, hi);
m_dep_intervals.add<dep_intervals::with_deps>(hi, lo, ret);
}
else {
m_dep_intervals.mul<dep_intervals::without_deps>(a, i, t);
get_interval_distributed<wd>(p.hi(), t, hi);
m_dep_intervals.add<dep_intervals::without_deps>(hi, lo, ret);
}
}
public:
pdd_interval(dep_intervals& d): m_dep_intervals(d) {}
@ -57,5 +83,11 @@ public:
}
}
template <w_dep wd>
void get_interval_distributed(pdd const& p, scoped_dep_interval& ret) {
scoped_dep_interval i(m());
m_dep_intervals.set_interval_for_scalar(i, rational::one());
get_interval_distributed<wd>(p, i, ret);
}
};
}

View file

@ -132,7 +132,7 @@ void grobner::display_vars(std::ostream & out, unsigned num_vars, expr * const *
}
}
void grobner::display_monomial(std::ostream & out, monomial const & m) const {
void grobner::display_monomial(std::ostream & out, monomial const & m, std::function<void(std::ostream&, expr*)>& display_var) const {
if (!m.m_coeff.is_one() || m.m_vars.empty()) {
out << m.m_coeff;
if (!m.m_vars.empty())
@ -165,7 +165,7 @@ void grobner::display_monomial(std::ostream & out, monomial const & m) const {
}
}
void grobner::display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const {
void grobner::display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials, std::function<void(std::ostream&, expr*)>& display_var) const {
bool first = true;
for (unsigned i = 0; i < num_monomials; i++) {
monomial const * m = monomials[i];
@ -173,26 +173,26 @@ void grobner::display_monomials(std::ostream & out, unsigned num_monomials, mono
first = false;
else
out << " + ";
display_monomial(out, *m);
display_monomial(out, *m, display_var);
}
}
void grobner::display_equation(std::ostream & out, equation const & eq) const {
display_monomials(out, eq.m_monomials.size(), eq.m_monomials.data());
void grobner::display_equation(std::ostream & out, equation const & eq, std::function<void(std::ostream&, expr*)>& display_var) const {
display_monomials(out, eq.m_monomials.size(), eq.m_monomials.data(), display_var);
out << " = 0\n";
}
void grobner::display_equations(std::ostream & out, equation_set const & v, char const * header) const {
if (!v.empty()) {
out << header << "\n";
for (equation const* eq : v)
display_equation(out, *eq);
}
void grobner::display_equations(std::ostream & out, equation_set const & v, char const * header, std::function<void(std::ostream&, expr*)>& display_var) const {
if (v.empty())
return;
out << header << "\n";
for (equation const* eq : v)
display_equation(out, *eq, display_var);
}
void grobner::display(std::ostream & out) const {
display_equations(out, m_processed, "processed:");
display_equations(out, m_to_process, "to process:");
void grobner::display(std::ostream & out, std::function<void(std::ostream&, expr*)>& display_var) const {
display_equations(out, m_processed, "processed:", display_var);
display_equations(out, m_to_process, "to process:", display_var);
}
void grobner::set_weight(expr * n, int weight) {
@ -528,7 +528,7 @@ bool grobner::is_subset(monomial const * m1, monomial const * m2, ptr_vector<exp
for (; i2 < sz2; i2++)
rest.push_back(m2->m_vars[i2]);
TRACE("grobner",
tout << "monomail: "; display_monomial(tout, *m1); tout << " is a subset of ";
tout << "monomial: "; display_monomial(tout, *m1); tout << " is a subset of ";
display_monomial(tout, *m2); tout << "\n";
tout << "rest: "; display_vars(tout, rest.size(), rest.data()); tout << "\n";);
return true;
@ -552,7 +552,7 @@ bool grobner::is_subset(monomial const * m1, monomial const * m2, ptr_vector<exp
}
}
// is not subset
TRACE("grobner", tout << "monomail: "; display_monomial(tout, *m1); tout << " is not a subset of ";
TRACE("grobner", tout << "monomial: "; display_monomial(tout, *m1); tout << " is not a subset of ";
display_monomial(tout, *m2); tout << "\n";);
return false;
}

View file

@ -120,9 +120,16 @@ protected:
void display_var(std::ostream & out, expr * var) const;
void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const;
void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials, std::function<void(std::ostream&, expr*)>& display_var) const;
void display_equations(std::ostream & out, equation_set const & v, char const * header) const;
void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const {
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { display_var(out, v); };
display_monomials(out, num_monomials, monomials, _fn);
}
void display_equations(std::ostream & out, equation_set const & v, char const * header, std::function<void(std::ostream&, expr*)>& display_var) const;
void del_equations(unsigned old_size);
@ -281,11 +288,26 @@ public:
void pop_scope(unsigned num_scopes);
void display_equation(std::ostream & out, equation const & eq) const;
void display_equation(std::ostream & out, equation const & eq) const {
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { display_var(out, v); };
display_equation(out, eq, _fn);
}
void display_monomial(std::ostream & out, monomial const & m) const;
void display_monomial(std::ostream & out, monomial const & m) const {
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { display_var(out, v); };
display_monomial(out, m, _fn);
}
void display_equation(std::ostream & out, equation const & eq, std::function<void(std::ostream&, expr*)>& display_var) const;
void display(std::ostream & out) const;
void display_monomial(std::ostream & out, monomial const & m, std::function<void(std::ostream&, expr*)>& display_var) const;
void display(std::ostream & out) const {
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { display_var(out, v); };
display(out, _fn);
}
void display(std::ostream & out, std::function<void(std::ostream&, expr*)>& display_var) const;
};

View file

@ -223,6 +223,8 @@ namespace dd {
for (unsigned i = 0; i < s.m_to_simplify.size(); ++i) {
equation* e = s.m_to_simplify[i];
pdd p = e->poly();
if (p.is_val())
continue;
if (!p.hi().is_val()) {
continue;
}

View file

@ -11,9 +11,9 @@
--*/
#include "util/uint_set.h"
#include "math/grobner/pdd_solver.h"
#include "math/grobner/pdd_simplifier.h"
#include "util/uint_set.h"
#include <math.h>
@ -169,7 +169,7 @@ namespace dd {
/*
Use the given equation to simplify equations in set
*/
void solver::simplify_using(equation_vector& set, equation const& eq) {
void solver::simplify_using(equation_vector& set, std::function<bool(equation&, bool&)>& simplifier) {
struct scoped_update {
equation_vector& set;
unsigned i, j, sz;
@ -191,7 +191,7 @@ namespace dd {
equation& target = *set[sr.i];
bool changed_leading_term = false;
bool simplified = true;
simplified = !done() && try_simplify_using(target, eq, changed_leading_term);
simplified = !done() && simplifier(target, changed_leading_term);
if (simplified && is_trivial(target)) {
retire(&target);
@ -200,7 +200,6 @@ namespace dd {
// pushed to solved
}
else if (simplified && changed_leading_term) {
SASSERT(target.state() == processed);
push_equation(to_simplify, target);
if (!m_var2level.empty()) {
m_levelp1 = std::max(m_var2level[target.poly().var()]+1, m_levelp1);
@ -210,6 +209,13 @@ namespace dd {
sr.nextj();
}
}
}
void solver::simplify_using(equation_vector& set, equation const& eq) {
std::function<bool(equation&, bool&)> simplifier = [&](equation& target, bool& changed_leading_term) {
return try_simplify_using(target, eq, changed_leading_term);
};
simplify_using(set, simplifier);
}
/*
@ -342,6 +348,7 @@ namespace dd {
for (equation* e : m_solved) dealloc(e);
for (equation* e : m_to_simplify) dealloc(e);
for (equation* e : m_processed) dealloc(e);
m_subst.reset();
m_solved.reset();
m_processed.reset();
m_to_simplify.reset();
@ -352,18 +359,54 @@ namespace dd {
}
void solver::add(pdd const& p, u_dependency * dep) {
if (p.is_zero()) return;
equation * eq = alloc(equation, p, dep);
if (check_conflict(*eq)) {
if (p.is_zero())
return;
equation * eq = alloc(equation, p, dep);
if (check_conflict(*eq))
return;
}
push_equation(to_simplify, eq);
if (!m_var2level.empty()) {
if (!m_var2level.empty())
m_levelp1 = std::max(m_var2level[p.var()]+1, m_levelp1);
}
update_stats_max_degree_and_size(*eq);
}
}
void solver::add_subst(unsigned v, pdd const& p, u_dependency* dep) {
m_subst.push_back({v, p, dep});
if (!m_var2level.empty())
m_levelp1 = std::max(m_var2level[v]+1, std::max(m_var2level[p.var()]+1, m_levelp1));
std::function<bool(equation&, bool&)> simplifier = [&](equation& dst, bool& changed_leading_term) {
auto r = dst.poly().subst_pdd(v, p);
if (r == dst.poly())
return false;
if (is_too_complex(r)) {
m_too_complex = true;
return false;
}
changed_leading_term = m.different_leading_term(r, dst.poly());
dst = r;
dst = m_dep_manager.mk_join(dst.dep(), dep);
update_stats_max_degree_and_size(dst);
return true;
};
if (!done())
simplify_using(m_processed, simplifier);
if (!done())
simplify_using(m_to_simplify, simplifier);
if (!done())
simplify_using(m_solved, simplifier);
}
void solver::simplify(pdd& p, u_dependency*& d) {
for (auto const& [v, q, d2] : m_subst) {
pdd r = p.subst_pdd(v, q);
if (r != p) {
p = r;
d = m_dep_manager.mk_join(d, d2);
}
}
}
bool solver::canceled() {
return m_limit.is_canceled();
@ -446,9 +489,24 @@ namespace dd {
}
std::ostream& solver::display(std::ostream& out) const {
out << "solved\n"; for (auto e : m_solved) display(out, *e);
out << "processed\n"; for (auto e : m_processed) display(out, *e);
out << "to_simplify\n"; for (auto e : m_to_simplify) display(out, *e);
if (!m_solved.empty()) {
out << "solved\n"; for (auto e : m_solved) display(out, *e);
}
if (!m_processed.empty()) {
out << "processed\n"; for (auto e : m_processed) display(out, *e);
}
if (!m_to_simplify.empty()) {
out << "to_simplify\n"; for (auto e : m_to_simplify) display(out, *e);
}
if (!m_subst.empty()) {
out << "subst\n";
for (auto const& [v, p, d] : m_subst) {
out << "v" << v << " := " << p;
if (m_print_dep)
m_print_dep(d, out);
out << "\n";
}
}
return display_statistics(out);
}

View file

@ -118,6 +118,7 @@ private:
equation_vector m_solved; // equations with solved variables, triangular
equation_vector m_processed;
equation_vector m_to_simplify;
vector<std::tuple<unsigned, pdd, u_dependency*>> m_subst;
mutable u_dependency_manager m_dep_manager;
equation_vector m_all_eqs;
equation* m_conflict;
@ -136,6 +137,9 @@ public:
void add(pdd const& p) { add(p, nullptr); }
void add(pdd const& p, u_dependency * dep);
void simplify(pdd& p, u_dependency*& dep);
void add_subst(unsigned v, pdd const& p, u_dependency* dep);
void simplify();
void saturate();
@ -160,6 +164,7 @@ private:
void simplify_using(equation& eq, equation_vector const& eqs);
void simplify_using(equation_vector& set, equation const& eq);
void simplify_using(equation & dst, equation const& src, bool& changed_leading_term);
void simplify_using(equation_vector& set, std::function<bool(equation&, bool&)>& simplifier);
bool try_simplify_using(equation& target, equation const& source, bool& changed_leading_term);
bool is_trivial(equation const& eq) const { return eq.poly().is_zero(); }

View file

@ -222,7 +222,6 @@ public:
template <enum with_deps_t wd>
void mul(const rational& r, const interval& a, interval& b) const {
if (r.is_zero()) return;
m_imanager.mul(r.to_mpq(), a, b);
if (wd == with_deps) {
auto lower_dep = a.m_lower_dep;

View file

@ -34,6 +34,7 @@ z3_add_component(lp
nla_basics_lemmas.cpp
nla_common.cpp
nla_core.cpp
nla_grobner.cpp
nla_intervals.cpp
nla_monotone_lemmas.cpp
nla_order_lemmas.cpp

View file

@ -40,7 +40,7 @@ bool horner::row_has_monomial_to_refine(const T& row) const {
template <typename T>
bool horner::row_is_interesting(const T& row) const {
TRACE("nla_solver_details", c().print_row(row, tout););
if (row.size() > c().m_nla_settings.horner_row_length_limit()) {
if (row.size() > c().m_nla_settings.horner_row_length_limit) {
TRACE("nla_solver_details", tout << "disregard\n";);
return false;
}
@ -98,7 +98,7 @@ bool horner::lemmas_on_row(const T& row) {
}
bool horner::horner_lemmas() {
if (!c().m_nla_settings.run_horner()) {
if (!c().m_nla_settings.run_horner) {
TRACE("nla_solver", tout << "not generating horner lemmas\n";);
return false;
}

View file

@ -275,9 +275,6 @@ class lar_solver : public column_namer {
return m_column_buffer;
}
bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const;
inline unsigned get_base_column_in_row(unsigned row_index) const {
return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index);
}
inline lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; }
void catch_up_in_updating_int_solver();
var_index to_column(unsigned ext_j) const;
@ -357,6 +354,10 @@ public:
}
void set_value_for_nbasic_column(unsigned j, const impq& new_val);
inline unsigned get_base_column_in_row(unsigned row_index) const {
return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index);
}
// lp_assert(implied_bound_is_correctly_explained(ib, explanation)); }
constraint_index mk_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side);
@ -630,6 +631,7 @@ public:
}
void round_to_integer_solution();
inline const row_strip<mpq> & get_row(unsigned i) const { return A_r().m_rows[i]; }
inline const row_strip<mpq> & basic2row(unsigned i) const { return A_r().m_rows[row_of_basic_column(i)]; }
inline const column_strip & get_column(unsigned i) const { return A_r().m_columns[i]; }
bool row_is_correct(unsigned i) const;
bool ax_is_correct() const;

View file

@ -71,11 +71,11 @@ void common::add_deps_of_fixed(lpvar j, u_dependency*& dep) {
// creates a nex expression for the coeff and var,
nex * common::nexvar(const rational & coeff, lpvar j, nex_creator& cn, u_dependency*& dep) {
SASSERT(!coeff.is_zero());
if (c().m_nla_settings.horner_subs_fixed() == 1 && c().var_is_fixed(j)) {
if (c().m_nla_settings.horner_subs_fixed == 1 && c().var_is_fixed(j)) {
add_deps_of_fixed(j, dep);
return cn.mk_scalar(coeff * c().m_lar_solver.column_lower_bound(j).x);
}
if (c().m_nla_settings.horner_subs_fixed() == 2 && c().var_is_fixed_to_zero(j)) {
if (c().m_nla_settings.horner_subs_fixed == 2 && c().var_is_fixed_to_zero(j)) {
add_deps_of_fixed(j, dep);
return cn.mk_scalar(rational(0));
}
@ -89,10 +89,10 @@ nex * common::nexvar(const rational & coeff, lpvar j, nex_creator& cn, u_depende
mf *= coeff;
u_dependency * initial_dep = dep;
for (lpvar k : m.vars()) {
if (c().m_nla_settings.horner_subs_fixed() && c().var_is_fixed(k)) {
if (c().m_nla_settings.horner_subs_fixed == 1 && c().var_is_fixed(k)) {
add_deps_of_fixed(k, dep);
mf *= c().m_lar_solver.column_lower_bound(k).x;
} else if (c().m_nla_settings.horner_subs_fixed() == 2 &&
} else if (c().m_nla_settings.horner_subs_fixed == 2 &&
c().var_is_fixed_to_zero(k)) {
dep = initial_dep;
add_deps_of_fixed(k, dep);

File diff suppressed because it is too large Load diff

View file

@ -18,15 +18,18 @@
#include "math/lp/nla_basics_lemmas.h"
#include "math/lp/nla_order_lemmas.h"
#include "math/lp/nla_monotone_lemmas.h"
#include "math/lp/nla_grobner.h"
#include "math/lp/emonics.h"
#include "math/lp/nla_settings.h"
#include "math/lp/nex.h"
#include "math/lp/horner.h"
#include "math/lp/monomial_bounds.h"
#include "math/lp/nla_intervals.h"
#include "math/grobner/pdd_solver.h"
#include "nlsat/nlsat_solver.h"
namespace nra {
class solver;
}
namespace nla {
@ -139,6 +142,20 @@ struct pp_factorization {
};
class core {
friend struct common;
friend class new_lemma;
friend class grobner;
friend class order;
friend struct basics;
friend struct tangents;
friend class monotone;
friend struct nla_settings;
friend class intervals;
friend class horner;
friend class solver;
friend class monomial_bounds;
friend class nra::solver;
struct stats {
unsigned m_nla_explanations;
unsigned m_nla_lemmas;
@ -148,16 +165,18 @@ class core {
memset(this, 0, sizeof(*this));
}
};
stats m_stats;
friend class new_lemma;
unsigned m_nlsat_delay { 50 };
unsigned m_nlsat_fails { 0 };
stats m_stats;
unsigned m_nlsat_delay = 50;
unsigned m_nlsat_fails = 0;
bool should_run_bounded_nlsat();
lbool bounded_nlsat();
public:
var_eqs<emonics> m_evars;
lp::lar_solver& m_lar_solver;
reslimit& m_reslim;
vector<lemma> * m_lemma_vec;
lp::u_set m_to_refine;
tangents m_tangents;
@ -166,24 +185,21 @@ public:
monotone m_monotone;
intervals m_intervals;
monomial_bounds m_monomial_bounds;
nla_settings m_nla_settings;
horner m_horner;
nla_settings m_nla_settings;
dd::pdd_manager m_pdd_manager;
dd::solver m_pdd_grobner;
private:
grobner m_grobner;
emonics m_emons;
svector<lpvar> m_add_buffer;
mutable lp::u_set m_active_var_set;
lp::u_set m_rows;
reslimit m_nra_lim;
public:
reslimit& m_reslim;
bool m_use_nra_model;
bool m_use_nra_model = false;
nra::solver m_nra;
private:
bool m_cautious_patching;
lpvar m_patched_var;
monic const* m_patched_monic;
bool m_cautious_patching = true;
lpvar m_patched_var = 0;
monic const* m_patched_monic = nullptr;
void check_weighted(unsigned sz, std::pair<unsigned, std::function<void(void)>>* checks);
@ -205,6 +221,8 @@ public:
m_active_var_set.resize(m_lar_solver.number_of_vars());
}
unsigned get_var_weight(lpvar) const;
reslimit& reslim() { return m_reslim; }
emonics& emons() { return m_emons; }
const emonics& emons() const { return m_emons; }
@ -243,12 +261,15 @@ public:
// returns true if the combination of the Horner's schema and Grobner Basis should be called
bool need_run_horner() const {
return m_nla_settings.run_horner() && lp_settings().stats().m_nla_calls % m_nla_settings.horner_frequency() == 0;
return m_nla_settings.run_horner && lp_settings().stats().m_nla_calls % m_nla_settings.horner_frequency == 0;
}
bool need_run_grobner() const {
return m_nla_settings.run_grobner() && lp_settings().stats().m_nla_calls % m_nla_settings.grobner_frequency() == 0;
return m_nla_settings.run_grobner && lp_settings().stats().m_nla_calls % m_nla_settings.grobner_frequency == 0;
}
void set_active_vars_weights(nex_creator&);
std::unordered_set<lpvar> get_vars_of_expr_with_opening_terms(const nex* e);
void incremental_linearization(bool);
@ -450,31 +471,19 @@ public:
lpvar map_to_root(lpvar) const;
std::ostream& print_terms(std::ostream&) const;
std::ostream& print_term(const lp::lar_term&, std::ostream&) const;
template <typename T>
std::ostream& print_row(const T & row , std::ostream& out) const {
std::ostream& print_row(const T& row, std::ostream& out) const {
vector<std::pair<rational, lpvar>> v;
for (auto p : row) {
v.push_back(std::make_pair(p.coeff(), p.var()));
}
return lp::print_linear_combination_customized(v, [this](lpvar j) { return var_str(j); },
out);
return lp::print_linear_combination_customized(v, [this](lpvar j) { return var_str(j); }, out);
}
void run_grobner();
void find_nl_cluster();
void prepare_rows_and_active_vars();
void add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar>& q);
std::unordered_set<lpvar> get_vars_of_expr_with_opening_terms(const nex* e);
void display_matrix_of_m_rows(std::ostream & out) const;
void set_active_vars_weights(nex_creator&);
unsigned get_var_weight(lpvar) const;
void add_row_to_grobner(const vector<lp::row_cell<rational>> & row);
bool check_pdd_eq(const dd::solver::equation*);
const rational& val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep);
dd::pdd pdd_expr(const rational& c, lpvar j, u_dependency*&);
void set_level2var_for_grobner();
void configure_grobner();
bool influences_nl_var(lpvar) const;
bool is_nl_var(lpvar) const;
bool is_used_in_monic(lpvar) const;
void patch_monomials();
void patch_monomials_on_to_refine();

545
src/math/lp/nla_grobner.cpp Normal file
View file

@ -0,0 +1,545 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
nla_grobner.cpp
Author:
Lev Nachmanson (levnach)
Nikolaj Bjorner (nbjorner)
--*/
#include "util/uint_set.h"
#include "math/lp/nla_core.h"
#include "math/lp/factorization_factory_imp.h"
#include "math/lp/nex.h"
#include "math/grobner/pdd_solver.h"
#include "math/dd/pdd_interval.h"
#include "math/dd/pdd_eval.h"
namespace nla {
grobner::grobner(core* c):
common(c),
m_pdd_manager(m_core.m_lar_solver.number_of_vars()),
m_solver(m_core.m_reslim, m_pdd_manager),
m_lar_solver(m_core.m_lar_solver)
{}
lp::lp_settings& grobner::lp_settings() {
return c().lp_settings();
}
void grobner::operator()() {
unsigned& quota = c().m_nla_settings.grobner_quota;
if (quota == 1)
return;
lp_settings().stats().m_grobner_calls++;
find_nl_cluster();
configure();
m_solver.saturate();
if (is_conflicting())
return;
if (propagate_bounds())
return;
if (propagate_eqs())
return;
if (propagate_factorization())
return;
if (quota > 1)
quota--;
IF_VERBOSE(2, verbose_stream() << "grobner miss, quota " << quota << "\n");
IF_VERBOSE(4, diagnose_pdd_miss(verbose_stream()));
#if 0
// diagnostics: did we miss something
vector<dd::pdd> eqs;
for (auto eq : m_solver.equations())
eqs.push_back(eq->poly());
c().m_nra.check(eqs);
#endif
}
bool grobner::is_conflicting() {
unsigned conflicts = 0;
for (auto eq : m_solver.equations())
if (is_conflicting(*eq) && ++conflicts >= m_solver.number_of_conflicts_to_report())
break;
if (conflicts > 0)
lp_settings().stats().m_grobner_conflicts++;
TRACE("grobner", m_solver.display(tout));
IF_VERBOSE(2, if (conflicts > 0) verbose_stream() << "grobner conflict\n");
return conflicts > 0;
}
bool grobner::propagate_bounds() {
unsigned changed = 0;
for (auto eq : m_solver.equations())
if (propagate_bounds(*eq) && ++changed >= m_solver.number_of_conflicts_to_report())
return true;
return changed > 0;
}
bool grobner::propagate_eqs() {
unsigned changed = 0;
for (auto eq : m_solver.equations())
if (propagate_fixed(*eq) && ++changed >= m_solver.number_of_conflicts_to_report())
return true;
return changed > 0;
}
bool grobner::propagate_factorization() {
unsigned changed = 0;
for (auto eq : m_solver.equations())
if (propagate_factorization(*eq) && ++changed >= m_solver.number_of_conflicts_to_report())
return true;
return changed > 0;
}
/**
\brief detect equalities
- k*x = 0, that is x = 0
- ax + b = 0
*/
typedef lp::lar_term term;
bool grobner::propagate_fixed(const dd::solver::equation& eq) {
dd::pdd const& p = eq.poly();
//IF_VERBOSE(0, verbose_stream() << p << "\n");
if (p.is_unary()) {
unsigned v = p.var();
if (c().var_is_fixed(v))
return false;
ineq new_eq(v, llc::EQ, rational::zero());
if (c().ineq_holds(new_eq))
return false;
new_lemma lemma(c(), "pdd-eq");
add_dependencies(lemma, eq);
lemma |= new_eq;
return true;
}
if (p.is_offset()) {
unsigned v = p.var();
if (c().var_is_fixed(v))
return false;
rational a = p.hi().val();
rational b = -p.lo().val();
rational d = lcm(denominator(a), denominator(b));
a *= d;
b *= d;
ineq new_eq(term(a, v), llc::EQ, b);
if (c().ineq_holds(new_eq))
return false;
new_lemma lemma(c(), "pdd-eq");
add_dependencies(lemma, eq);
lemma |= new_eq;
return true;
}
return false;
}
/**
\brief detect simple factors
x*q = 0 => x = 0 or q = 0
*/
bool grobner::propagate_factorization(const dd::solver::equation& eq) {
dd::pdd const& p = eq.poly();
auto [vars, q] = p.var_factors();
if (vars.empty() || !q.is_linear())
return false;
// IF_VERBOSE(0, verbose_stream() << "factored " << q << " : " << vars << "\n");
term t;
while (!q.is_val()) {
t.add_monomial(q.hi().val(), q.var());
q = q.lo();
}
vector<ineq> ineqs;
for (auto v : vars)
ineqs.push_back(ineq(v, llc::EQ, rational::zero()));
ineqs.push_back(ineq(t, llc::EQ, -q.val()));
for (auto const& i : ineqs)
if (c().ineq_holds(i))
return false;
new_lemma lemma(c(), "pdd-factored");
add_dependencies(lemma, eq);
for (auto const& i : ineqs)
lemma |= i;
//lemma.display(verbose_stream());
return true;
}
void grobner::add_dependencies(new_lemma& lemma, const dd::solver::equation& eq) {
lp::explanation ex;
u_dependency_manager dm;
vector<unsigned, false> lv;
dm.linearize(eq.dep(), lv);
for (unsigned ci : lv)
ex.push_back(ci);
lemma &= ex;
}
void grobner::configure() {
m_solver.reset();
try {
set_level2var();
TRACE("grobner",
tout << "base vars: ";
for (lpvar j : c().active_var_set())
if (m_lar_solver.is_base(j))
tout << "j" << j << " ";
tout << "\n");
for (lpvar j : c().active_var_set()) {
if (m_lar_solver.is_base(j))
add_row(m_lar_solver.basic2row(j));
if (c().is_monic_var(j) && c().var_is_fixed(j))
add_fixed_monic(j);
}
}
catch (...) {
IF_VERBOSE(2, verbose_stream() << "pdd throw\n");
return;
}
TRACE("grobner", m_solver.display(tout));
#if 0
IF_VERBOSE(2, m_pdd_grobner.display(verbose_stream()));
dd::pdd_eval eval(m_pdd_manager);
eval.var2val() = [&](unsigned j){ return val(j); };
for (auto* e : m_pdd_grobner.equations()) {
dd::pdd p = e->poly();
rational v = eval(p);
if (p.is_linear() && !eval(p).is_zero()) {
IF_VERBOSE(0, verbose_stream() << "violated linear constraint " << p << "\n");
}
}
#endif
struct dd::solver::config cfg;
cfg.m_max_steps = m_solver.equations().size();
cfg.m_max_simplified = c().m_nla_settings.grobner_max_simplified;
cfg.m_eqs_growth = c().m_nla_settings.grobner_eqs_growth;
cfg.m_expr_size_growth = c().m_nla_settings.grobner_expr_size_growth;
cfg.m_expr_degree_growth = c().m_nla_settings.grobner_expr_degree_growth;
cfg.m_number_of_conflicts_to_report = c().m_nla_settings.grobner_number_of_conflicts_to_report;
m_solver.set(cfg);
m_solver.adjust_cfg();
m_pdd_manager.set_max_num_nodes(10000); // or something proportional to the number of initial nodes.
}
std::ostream& grobner::diagnose_pdd_miss(std::ostream& out) {
// m_pdd_grobner.display(out);
dd::pdd_eval eval;
eval.var2val() = [&](unsigned j){ return val(j); };
for (auto* e : m_solver.equations()) {
dd::pdd p = e->poly();
rational v = eval(p);
if (!v.is_zero()) {
out << p << " := " << v << "\n";
}
}
for (unsigned j = 0; j < m_lar_solver.number_of_vars(); ++j) {
if (m_lar_solver.column_has_lower_bound(j) || m_lar_solver.column_has_upper_bound(j)) {
out << j << ": [";
if (m_lar_solver.column_has_lower_bound(j)) out << m_lar_solver.get_lower_bound(j);
out << "..";
if (m_lar_solver.column_has_upper_bound(j)) out << m_lar_solver.get_upper_bound(j);
out << "]\n";
}
}
return out;
}
bool grobner::is_conflicting(const dd::solver::equation& e) {
auto& di = c().m_intervals.get_dep_intervals();
dd::pdd_interval eval(di);
eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) {
if (deps) c().m_intervals.set_var_interval<dd::w_dep::with_deps>(j, a);
else c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
};
scoped_dep_interval i(di), i_wd(di);
eval.get_interval<dd::w_dep::without_deps>(e.poly(), i);
if (!di.separated_from_zero(i)) {
TRACE("grobner", m_solver.display(tout << "not separated from 0 ", e) << "\n";
eval.get_interval_distributed<dd::w_dep::without_deps>(e.poly(), i);
tout << "separated from 0: " << di.separated_from_zero(i) << "\n";
for (auto j : e.poly().free_vars()) {
scoped_dep_interval a(di);
c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
c().m_intervals.display(tout << "j" << j << " ", a); tout << " ";
}
tout << "\n");
return false;
}
eval.get_interval<dd::w_dep::with_deps>(e.poly(), i_wd);
std::function<void (const lp::explanation&)> f = [this](const lp::explanation& e) {
new_lemma lemma(m_core, "pdd");
lemma &= e;
};
if (di.check_interval_for_conflict_on_zero(i_wd, e.dep(), f)) {
TRACE("grobner", m_solver.display(tout << "conflict ", e) << "\n");
return true;
}
else {
TRACE("grobner", m_solver.display(tout << "no conflict ", e) << "\n");
return false;
}
}
bool grobner::propagate_bounds(const dd::solver::equation& e) {
return false;
// TODO
auto& di = c().m_intervals.get_dep_intervals();
dd::pdd_interval eval(di);
eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) {
if (deps) c().m_intervals.set_var_interval<dd::w_dep::with_deps>(j, a);
else c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
};
scoped_dep_interval i(di), i_wd(di);
eval.get_interval<dd::w_dep::without_deps>(e.poly(), i);
return false;
}
void grobner::add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar> & q) {
if (c().active_var_set_contains(j))
return;
c().insert_to_active_var_set(j);
if (c().is_monic_var(j)) {
const monic& m = c().emons()[j];
for (auto fcn : factorization_factory_imp(m, m_core))
for (const factor& fc: fcn)
q.push_back(var(fc));
}
if (c().var_is_fixed(j))
return;
const auto& matrix = m_lar_solver.A_r();
for (auto & s : matrix.m_columns[j]) {
unsigned row = s.var();
if (m_rows.contains(row))
continue;
m_rows.insert(row);
unsigned k = m_lar_solver.get_base_column_in_row(row);
if (m_lar_solver.column_is_free(k) && k != j)
continue;
CTRACE("grobner", matrix.m_rows[row].size() > c().m_nla_settings.grobner_row_length_limit,
tout << "ignore the row " << row << " with the size " << matrix.m_rows[row].size() << "\n";);
if (matrix.m_rows[row].size() > c().m_nla_settings.grobner_row_length_limit)
continue;
for (auto& rc : matrix.m_rows[row])
add_var_and_its_factors_to_q_and_collect_new_rows(rc.var(), q);
}
}
const rational& grobner::val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep) {
unsigned lc, uc;
m_lar_solver.get_bound_constraint_witnesses_for_column(j, lc, uc);
dep = c().m_intervals.mk_join(dep, c().m_intervals.mk_leaf(lc));
dep = c().m_intervals.mk_join(dep, c().m_intervals.mk_leaf(uc));
return m_lar_solver.column_lower_bound(j).x;
}
dd::pdd grobner::pdd_expr(const rational& coeff, lpvar j, u_dependency*& dep) {
dd::pdd r = m_pdd_manager.mk_val(coeff);
sbuffer<lpvar> vars;
vars.push_back(j);
u_dependency* zero_dep = dep;
while (!vars.empty()) {
j = vars.back();
vars.pop_back();
if (c().m_nla_settings.grobner_subs_fixed > 0 && c().var_is_fixed_to_zero(j)) {
r = m_pdd_manager.mk_val(val_of_fixed_var_with_deps(j, zero_dep));
dep = zero_dep;
return r;
}
if (c().m_nla_settings.grobner_subs_fixed == 1 && c().var_is_fixed(j))
r *= val_of_fixed_var_with_deps(j, dep);
else if (!c().is_monic_var(j))
r *= m_pdd_manager.mk_var(j);
else
for (lpvar k : c().emons()[j].vars())
vars.push_back(k);
}
return r;
}
/**
\brief convert p == 0 into a solved form v == r, such that
v has bounds [lo, oo) iff r has bounds [lo', oo)
v has bounds (oo,hi] iff r has bounds (oo,hi']
The solved form allows the Grobner solver identify more bounds conflicts.
A bad leading term can miss bounds conflicts.
For example for x + y + z == 0 where x, y : [0, oo) and z : (oo,0]
we prefer to solve z == -x - y instead of x == -z - y
because the solution -z - y has neither an upper, nor a lower bound.
*/
bool grobner::is_solved(dd::pdd const& p, unsigned& v, dd::pdd& r) {
if (!p.is_linear())
return false;
r = p;
unsigned num_lo = 0, num_hi = 0;
unsigned lo = 0, hi = 0;
rational lc, hc, c;
while (!r.is_val()) {
SASSERT(r.hi().is_val());
v = r.var();
rational val = r.hi().val();
switch (m_lar_solver.get_column_type(v)) {
case lp::column_type::lower_bound:
if (val > 0) num_lo++, lo = v, lc = val; else num_hi++, hi = v, hc = val;
break;
case lp::column_type::upper_bound:
if (val < 0) num_lo++, lo = v, lc = val; else num_hi++, hi = v, hc = val;
break;
case lp::column_type::fixed:
case lp::column_type::boxed:
break;
default:
return false;
}
if (num_lo > 1 && num_hi > 1)
return false;
r = r.lo();
}
if (num_lo == 1 && num_hi > 1) {
v = lo;
c = lc;
}
else if (num_hi == 1 && num_lo > 1) {
v = hi;
c = hc;
}
else
return false;
r = c*m_pdd_manager.mk_var(v) - p;
if (c != 1)
r = r * (1/c);
return true;
}
/**
\brief add an equality to grobner solver, convert it to solved form if available.
*/
void grobner::add_eq(dd::pdd& p, u_dependency* dep) {
unsigned v;
dd::pdd q(m_pdd_manager);
m_solver.simplify(p, dep);
if (is_solved(p, v, q))
m_solver.add_subst(v, q, dep);
else
m_solver.add(p, dep);
}
void grobner::add_fixed_monic(unsigned j) {
u_dependency* dep = nullptr;
dd::pdd r = m_pdd_manager.mk_val(rational(1));
for (lpvar k : c().emons()[j].vars())
r *= pdd_expr(rational::one(), k, dep);
r -= val_of_fixed_var_with_deps(j, dep);
add_eq(r, dep);
}
void grobner::add_row(const vector<lp::row_cell<rational>> & row) {
u_dependency *dep = nullptr;
rational val;
dd::pdd sum = m_pdd_manager.mk_val(rational(0));
for (const auto &p : row)
sum += pdd_expr(p.coeff(), p.var(), dep);
TRACE("grobner", c().print_row(row, tout) << " " << sum << "\n");
add_eq(sum, dep);
}
void grobner::find_nl_cluster() {
prepare_rows_and_active_vars();
svector<lpvar> q;
TRACE("grobner", for (lpvar j : c().m_to_refine) print_monic(c().emons()[j], tout) << "\n";);
for (lpvar j : c().m_to_refine)
q.push_back(j);
while (!q.empty()) {
lpvar j = q.back();
q.pop_back();
add_var_and_its_factors_to_q_and_collect_new_rows(j, q);
}
TRACE("grobner", tout << "vars in cluster: ";
for (lpvar j : c().active_var_set()) tout << "j" << j << " "; tout << "\n";
display_matrix_of_m_rows(tout);
);
}
void grobner::prepare_rows_and_active_vars() {
m_rows.clear();
m_rows.resize(m_lar_solver.row_count());
c().clear_and_resize_active_var_set();
}
void grobner::display_matrix_of_m_rows(std::ostream & out) const {
const auto& matrix = m_lar_solver.A_r();
out << m_rows.size() << " rows" << "\n";
out << "the matrix\n";
for (const auto & r : matrix.m_rows)
c().print_row(r, out) << std::endl;
}
void grobner::set_level2var() {
unsigned n = m_lar_solver.column_count();
unsigned_vector sorted_vars(n), weighted_vars(n);
for (unsigned j = 0; j < n; j++) {
sorted_vars[j] = j;
weighted_vars[j] = c().get_var_weight(j);
}
#if 1
// potential update to weights
for (unsigned j = 0; j < n; j++) {
if (c().is_monic_var(j) && c().m_to_refine.contains(j)) {
for (lpvar k : c().m_emons[j].vars()) {
weighted_vars[k] += 6;
}
}
}
#endif
std::sort(sorted_vars.begin(), sorted_vars.end(), [&](unsigned a, unsigned b) {
unsigned wa = weighted_vars[a];
unsigned wb = weighted_vars[b];
return wa < wb || (wa == wb && a < b); });
unsigned_vector l2v(n);
for (unsigned j = 0; j < n; j++)
l2v[j] = sorted_vars[j];
m_pdd_manager.reset(l2v);
TRACE("grobner",
for (auto v : sorted_vars)
tout << "j" << v << " w:" << weighted_vars[v] << " ";
tout << "\n");
}
}

64
src/math/lp/nla_grobner.h Normal file
View file

@ -0,0 +1,64 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
--*/
#pragma once
#include "math/lp/nla_common.h"
#include "math/lp/nla_intervals.h"
#include "math/lp/nex.h"
#include "math/lp/cross_nested.h"
#include "math/lp/u_set.h"
#include "math/grobner/pdd_solver.h"
namespace nla {
class core;
class grobner : common {
dd::pdd_manager m_pdd_manager;
dd::solver m_solver;
lp::lar_solver& m_lar_solver;
lp::u_set m_rows;
lp::lp_settings& lp_settings();
// solving
bool is_conflicting();
bool is_conflicting(const dd::solver::equation& eq);
bool propagate_bounds();
bool propagate_bounds(const dd::solver::equation& eq);
bool propagate_eqs();
bool propagate_fixed(const dd::solver::equation& eq);
bool propagate_factorization();
bool propagate_factorization(const dd::solver::equation& eq);
void add_dependencies(new_lemma& lemma, const dd::solver::equation& eq);
// setup
void configure();
void set_level2var();
void find_nl_cluster();
void prepare_rows_and_active_vars();
void add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar>& q);
void add_row(const vector<lp::row_cell<rational>>& row);
void add_fixed_monic(unsigned j);
bool is_solved(dd::pdd const& p, unsigned& v, dd::pdd& r);
void add_eq(dd::pdd& p, u_dependency* dep);
const rational& val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep);
dd::pdd pdd_expr(const rational& c, lpvar j, u_dependency*& dep);
void display_matrix_of_m_rows(std::ostream& out) const;
std::ostream& diagnose_pdd_miss(std::ostream& out);
public:
grobner(core *core);
void operator()();
};
}

View file

@ -19,7 +19,7 @@ typedef lp::lar_term term;
// a > b && c > 0 => ac > bc
void order::order_lemma() {
TRACE("nla_solver", );
if (!c().m_nla_settings.run_order()) {
if (!c().m_nla_settings.run_order) {
TRACE("nla_solver", tout << "not generating order lemmas\n";);
return;
}

View file

@ -9,94 +9,38 @@ Author:
#pragma once
namespace nla {
class nla_settings {
bool m_run_order;
bool m_run_tangents;
bool m_run_horner;
// how often to call the horner heuristic
unsigned m_horner_frequency;
unsigned m_horner_row_length_limit;
unsigned m_horner_subs_fixed;
// grobner fields
bool m_run_grobner;
unsigned m_grobner_row_length_limit;
unsigned m_grobner_subs_fixed;
unsigned m_grobner_eqs_growth;
unsigned m_grobner_tree_size_growth;
unsigned m_grobner_expr_size_growth;
unsigned m_grobner_expr_degree_growth;
unsigned m_grobner_max_simplified;
unsigned m_grobner_number_of_conflicts_to_report;
unsigned m_grobner_quota;
unsigned m_grobner_frequency;
bool m_run_nra;
// expensive patching
bool m_expensive_patching;
public:
nla_settings() : m_run_order(true),
m_run_tangents(true),
m_run_horner(true),
m_horner_frequency(4),
m_horner_row_length_limit(10),
m_horner_subs_fixed(2),
m_run_grobner(true),
m_grobner_row_length_limit(50),
m_grobner_subs_fixed(false),
m_grobner_quota(0),
m_grobner_frequency(4),
m_run_nra(false),
m_expensive_patching(false)
{}
unsigned grobner_eqs_growth() const { return m_grobner_eqs_growth;}
unsigned& grobner_eqs_growth() { return m_grobner_eqs_growth;}
bool run_order() const { return m_run_order; }
bool& run_order() { return m_run_order; }
struct nla_settings {
bool run_order = true;
bool run_tangents = true;
// horner fields
bool run_horner = true;
unsigned horner_frequency = 4;
unsigned horner_row_length_limit = 10;
unsigned horner_subs_fixed = 2;
bool run_tangents() const { return m_run_tangents; }
bool& run_tangents() { return m_run_tangents; }
// grobner fields
bool run_grobner = true;
unsigned grobner_row_length_limit = 50;
unsigned grobner_subs_fixed = 1;
unsigned grobner_eqs_growth = 10;
unsigned grobner_tree_size_growth = 2;
unsigned grobner_expr_size_growth = 2;
unsigned grobner_expr_degree_growth = 2;
unsigned grobner_max_simplified = 10000;
unsigned grobner_number_of_conflicts_to_report = 1;
unsigned grobner_quota = 0;
unsigned grobner_frequency = 4;
bool expensive_patching() const { return m_expensive_patching; }
bool& expensive_patching() { return m_expensive_patching; }
bool run_horner() const { return m_run_horner; }
bool& run_horner() { return m_run_horner; }
unsigned horner_frequency() const { return m_horner_frequency; }
unsigned& horner_frequency() { return m_horner_frequency; }
unsigned horner_row_length_limit() const { return m_horner_row_length_limit; }
unsigned& horner_row_length_limit() { return m_horner_row_length_limit; }
unsigned horner_subs_fixed() const { return m_horner_subs_fixed; }
unsigned& horner_subs_fixed() { return m_horner_subs_fixed; }
bool run_grobner() const { return m_run_grobner; }
bool& run_grobner() { return m_run_grobner; }
unsigned grobner_frequency() const { return m_grobner_frequency; }
unsigned& grobner_frequency() { return m_grobner_frequency; }
bool run_nra() const { return m_run_nra; }
bool& run_nra() { return m_run_nra; }
unsigned grobner_row_length_limit() const { return m_grobner_row_length_limit; }
unsigned& grobner_row_length_limit() { return m_grobner_row_length_limit; }
unsigned grobner_subs_fixed() const { return m_grobner_subs_fixed; }
unsigned& grobner_subs_fixed() { return m_grobner_subs_fixed; }
unsigned grobner_tree_size_growth() const { return m_grobner_tree_size_growth; }
unsigned & grobner_tree_size_growth() { return m_grobner_tree_size_growth; }
unsigned grobner_expr_size_growth() const { return m_grobner_expr_size_growth; }
unsigned & grobner_expr_size_growth() { return m_grobner_expr_size_growth; }
unsigned grobner_expr_degree_growth() const { return m_grobner_expr_degree_growth; }
unsigned & grobner_expr_degree_growth() { return m_grobner_expr_degree_growth; }
unsigned grobner_max_simplified() const { return m_grobner_max_simplified; }
unsigned & grobner_max_simplified() { return m_grobner_max_simplified; }
unsigned grobner_number_of_conflicts_to_report() const { return m_grobner_number_of_conflicts_to_report; }
unsigned & grobner_number_of_conflicts_to_report() { return m_grobner_number_of_conflicts_to_report; }
unsigned& grobner_quota() { return m_grobner_quota; }
// nra fields
bool run_nra = false;
};
// expensive patching
bool expensive_patching = false;
nla_settings() {}
};
}

View file

@ -186,7 +186,7 @@ tangents::tangents(core * c) : common(c) {}
void tangents::tangent_lemma() {
factorization bf(nullptr);
const monic* m = nullptr;
if (c().m_nla_settings.run_tangents() && c().find_bfc_to_refine(m, bf)) {
if (c().m_nla_settings.run_tangents && c().find_bfc_to_refine(m, bf)) {
lpvar j = m->var();
tangent_imp tangent(point(val(bf[0]), val(bf[1])), c().val(j), *m, bf, *this);
tangent();

View file

@ -65,12 +65,10 @@ struct solver::imp {
}
// add polynomial definitions.
for (auto const& m : m_nla_core.emons()) {
for (auto const& m : m_nla_core.emons())
add_monic_eq(m);
}
for (unsigned i : m_term_set) {
for (unsigned i : m_term_set)
add_term(i);
}
// TBD: add variable bounds?
lbool r = l_undef;
@ -176,7 +174,103 @@ struct solver::imp {
lp_assert(false); // unreachable
}
m_nlsat->mk_clause(1, &lit, a);
}
}
lbool check(vector<dd::pdd> const& eqs) {
m_zero = nullptr;
m_nlsat = alloc(nlsat::solver, m_limit, m_params, false);
m_zero = alloc(scoped_anum, am());
m_lp2nl.reset();
m_term_set.clear();
for (auto const& eq : eqs)
add_eq(eq);
for (auto const& [v, w] : m_lp2nl) {
auto& ls = m_nla_core.m_lar_solver;
if (ls.column_has_lower_bound(v))
add_lb(ls.get_lower_bound(v), w);
if (ls.column_has_upper_bound(v))
add_ub(ls.get_upper_bound(v), w);
}
lbool r = l_undef;
try {
r = m_nlsat->check();
}
catch (z3_exception&) {
if (m_limit.is_canceled()) {
r = l_undef;
}
else {
throw;
}
}
IF_VERBOSE(0, verbose_stream() << "check-nra " << r << "\n";
m_nlsat->display(verbose_stream());
for (auto const& [v, w] : m_lp2nl) {
auto& ls = m_nla_core.m_lar_solver;
if (ls.column_has_lower_bound(v))
verbose_stream() << w << " >= " << ls.get_lower_bound(v) << "\n";
if (ls.column_has_upper_bound(v))
verbose_stream() << w << " <= " << ls.get_upper_bound(v) << "\n";
});
return r;
}
void add_eq(dd::pdd const& eq) {
dd::pdd normeq = eq;
rational lc(1);
for (auto const& [c, m] : eq)
lc = lcm(denominator(c), lc);
if (lc != 1)
normeq *= lc;
polynomial::manager& pm = m_nlsat->pm();
polynomial::polynomial_ref p(pdd2polynomial(normeq), pm);
bool is_even[1] = { false };
polynomial::polynomial* ps[1] = { p };
nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even);
m_nlsat->mk_clause(1, &lit, nullptr);
}
void add_lb(lp::impq const& b, unsigned w) {
add_bound(b.x, w, b.y <= 0, b.y > 0 ? nlsat::atom::kind::GT : nlsat::atom::kind::LT);
}
void add_ub(lp::impq const& b, unsigned w) {
add_bound(b.x, w, b.y >= 0, b.y < 0 ? nlsat::atom::kind::LT : nlsat::atom::kind::GT);
}
// w - bound < 0
// w - bound > 0
void add_bound(lp::mpq const& bound, unsigned w, bool neg, nlsat::atom::kind k) {
polynomial::manager& pm = m_nlsat->pm();
polynomial::polynomial_ref p1(pm.mk_polynomial(w), pm);
polynomial::polynomial_ref p2(pm.mk_const(bound), pm);
polynomial::polynomial_ref p(pm.sub(p1, p2), pm);
polynomial::polynomial* ps[1] = { p };
bool is_even[1] = { false };
nlsat::literal lit = m_nlsat->mk_ineq_literal(k, 1, ps, is_even);
if (neg)
lit.neg();
m_nlsat->mk_clause(1, &lit, nullptr);
}
polynomial::polynomial* pdd2polynomial(dd::pdd const& p) {
polynomial::manager& pm = m_nlsat->pm();
if (p.is_val())
return pm.mk_const(p.val());
polynomial::polynomial_ref lo(pdd2polynomial(p.lo()), pm);
polynomial::polynomial_ref hi(pdd2polynomial(p.hi()), pm);
unsigned w, v = p.var();
if (!m_lp2nl.find(v, w)) {
w = m_nlsat->mk_var(false);
m_lp2nl.insert(v, w);
}
polynomial::polynomial_ref vp(pm.mk_polynomial(w, 1), pm);
return pm.add(lo, pm.mul(vp, hi));
}
bool is_int(lp::var_index v) {
return s.var_is_int(v);
@ -265,6 +359,10 @@ lbool solver::check() {
return m_imp->check();
}
lbool solver::check(vector<dd::pdd> const& eqs) {
return m_imp->check(eqs);
}
bool solver::need_check() {
return m_imp->need_check();
}

View file

@ -9,6 +9,7 @@
#include "util/rlimit.h"
#include "util/params.h"
#include "nlsat/nlsat_solver.h"
#include "math/dd/dd_pdd.h"
namespace lp {
class lar_solver;
@ -36,6 +37,11 @@ namespace nra {
*/
lbool check();
/**
\breif Check feasibility of equalities modulo bounds constraints on their variables.
*/
lbool check(vector<dd::pdd> const& eqs);
/*
\brief determine whether nra check is needed.
*/

View file

@ -137,9 +137,6 @@ namespace opt {
m_model_fixed(),
m_objective_refs(m),
m_core(m),
m_enable_sat(false),
m_is_clausal(false),
m_pp_neat(false),
m_unknown("unknown")
{
params_ref p;
@ -196,6 +193,8 @@ namespace opt {
void context::add_hard_constraint(expr* f) {
if (m_calling_on_model) {
if (!m_incremental)
throw default_exception("Set opt.incremental = true to allow adding constraints during search");
get_solver().assert_expr(f);
for (auto const& [k, v] : m_maxsmts)
v->reset_upper();
@ -838,19 +837,14 @@ namespace opt {
}
goal_ref g(alloc(goal, m, true, !asms.empty()));
for (expr* fml : fmls) {
for (expr* fml : fmls)
g->assert_expr(fml);
}
for (expr * a : asms) {
for (expr * a : asms)
g->assert_expr(a, a);
}
tactic_ref tac0 =
and_then(mk_simplify_tactic(m, m_params),
mk_propagate_values_tactic(m),
mk_solve_eqs_tactic(m),
// NB: cannot ackermannize because max/min objectives would disappear
// mk_ackermannize_bv_tactic(m, m_params),
// NB: mk_elim_uncstr_tactic(m) is not sound with soft constraints
m_incremental ? mk_skip_tactic() : mk_solve_eqs_tactic(m),
mk_simplify_tactic(m));
opt_params optp(m_params);
tactic_ref tac1, tac2, tac3, tac4;
@ -861,7 +855,7 @@ namespace opt {
m.linearize(core, deps);
has_dep |= !deps.empty();
}
if (optp.elim_01() && m_logic.is_null() && !has_dep) {
if (optp.elim_01() && m_logic.is_null() && !has_dep && !m_incremental) {
tac1 = mk_dt2bv_tactic(m);
tac2 = mk_lia2card_tactic(m);
tac3 = mk_eq2bv_tactic(m);
@ -1568,6 +1562,7 @@ namespace opt {
m_maxsat_engine = _p.maxsat_engine();
m_pp_neat = _p.pp_neat();
m_pp_wcnf = _p.pp_wcnf();
m_incremental = _p.incremental();
}
std::string context::to_string() {

View file

@ -194,11 +194,12 @@ namespace opt {
func_decl_ref_vector m_objective_refs;
expr_ref_vector m_core;
tactic_ref m_simplify;
bool m_enable_sat { true } ;
bool m_enable_sls { false };
bool m_is_clausal { false };
bool m_pp_neat { true };
bool m_pp_wcnf { false };
bool m_enable_sat = true;
bool m_enable_sls = false;
bool m_is_clausal = false;
bool m_pp_neat = false;
bool m_pp_wcnf = false;
bool m_incremental = false;
symbol m_maxsat_engine;
symbol m_logic;
svector<symbol> m_labels;

View file

@ -15,6 +15,7 @@ def_module_params('opt',
('enable_core_rotate', BOOL, False, 'enable core rotation to both sample cores and correction sets'),
('enable_sat', BOOL, True, 'enable the new SAT core for propositional constraints'),
('elim_01', BOOL, True, 'eliminate 01 variables'),
('incremental', BOOL, False, 'set incremental mode. It disables pre-processing and enables adding constraints in model event handler'),
('pp.neat', BOOL, True, 'use neat (as opposed to less readable, but faster) pretty printer when displaying context'),
('pb.compile_equality', BOOL, False, 'compile arithmetical equalities into pseudo-Boolean equality (instead of two inequalites)'),
('pp.wcnf', BOOL, False, 'print maxsat benchmark into wcnf format'),

View file

@ -536,6 +536,7 @@ namespace qe {
th_rewriter rewrite(m);
rewrite(a);
rewrite(b);
TRACE("interpolator", tout << a << " " << b << "\n");
solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null);
solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null);
solver_ref sNotA = sf(m, p, false /* no proofs */, true, true, symbol::null);

View file

@ -1787,6 +1787,7 @@ namespace sat {
clause& c = it.curr();
if (!c.is_learned() && !c.was_removed()) {
r.push_back(clause_wrapper(c));
SASSERT(r.back().contains(l));
SASSERT(r.back().size() == c.size());
}
}
@ -1808,9 +1809,13 @@ namespace sat {
Return false if the result is a tautology
*/
bool simplifier::resolve(clause_wrapper const & c1, clause_wrapper const & c2, literal l, literal_vector & r) {
CTRACE("resolve_bug", !c1.contains(l), tout << c1 << "\n" << c2 << "\nl: " << l << "\n";);
CTRACE("resolve_bug", !c1.contains(l) || !c2.contains(~l), tout << c1 << "\n" << c2 << "\nl: " << l << "\n";);
if (m_visited.size() <= 2*s.num_vars())
m_visited.resize(2*s.num_vars(), false);
if (c1.was_removed())
return false;
if (c2.was_removed())
return false;
SASSERT(c1.contains(l));
SASSERT(c2.contains(~l));
bool res = true;
@ -1973,7 +1978,14 @@ namespace sat {
}
}
}
TRACE("sat_simplifier", tout << "eliminate " << v << ", before: " << before_clauses << " after: " << after_clauses << "\n";);
TRACE("sat_simplifier", tout << "eliminate " << v << ", before: " << before_clauses << " after: " << after_clauses << "\n";
tout << "pos\n";
for (auto & c : m_pos_cls)
tout << c << "\n";
tout << "neg\n";
for (auto & c : m_neg_cls)
tout << c << "\n";
);
m_elim_counter -= num_pos * num_neg + before_lits;
m_elim_counter -= num_pos * num_neg + before_lits;
@ -1988,6 +2000,8 @@ namespace sat {
m_elim_counter -= num_pos * num_neg + before_lits;
for (auto & c1 : m_pos_cls) {
if (c1.was_removed())
continue;
for (auto & c2 : m_neg_cls) {
m_new_cls.reset();
if (!resolve(c1, c2, pos_l, m_new_cls))

View file

@ -69,23 +69,23 @@ namespace arith {
m_nla->push();
}
smt_params_helper prms(s().params());
m_nla->settings().run_order() = prms.arith_nl_order();
m_nla->settings().run_tangents() = prms.arith_nl_tangents();
m_nla->settings().run_horner() = prms.arith_nl_horner();
m_nla->settings().horner_subs_fixed() = prms.arith_nl_horner_subs_fixed();
m_nla->settings().horner_frequency() = prms.arith_nl_horner_frequency();
m_nla->settings().horner_row_length_limit() = prms.arith_nl_horner_row_length_limit();
m_nla->settings().run_grobner() = prms.arith_nl_grobner();
m_nla->settings().run_nra() = prms.arith_nl_nra();
m_nla->settings().grobner_subs_fixed() = prms.arith_nl_grobner_subs_fixed();
m_nla->settings().grobner_eqs_growth() = prms.arith_nl_grobner_eqs_growth();
m_nla->settings().grobner_expr_size_growth() = prms.arith_nl_grobner_expr_size_growth();
m_nla->settings().grobner_expr_degree_growth() = prms.arith_nl_grobner_expr_degree_growth();
m_nla->settings().grobner_max_simplified() = prms.arith_nl_grobner_max_simplified();
m_nla->settings().grobner_number_of_conflicts_to_report() = prms.arith_nl_grobner_cnfl_to_report();
m_nla->settings().grobner_quota() = prms.arith_nl_gr_q();
m_nla->settings().grobner_frequency() = prms.arith_nl_grobner_frequency();
m_nla->settings().expensive_patching() = false;
m_nla->settings().run_order = prms.arith_nl_order();
m_nla->settings().run_tangents = prms.arith_nl_tangents();
m_nla->settings().run_horner = prms.arith_nl_horner();
m_nla->settings().horner_subs_fixed = prms.arith_nl_horner_subs_fixed();
m_nla->settings().horner_frequency = prms.arith_nl_horner_frequency();
m_nla->settings().horner_row_length_limit = prms.arith_nl_horner_row_length_limit();
m_nla->settings().run_grobner = prms.arith_nl_grobner();
m_nla->settings().run_nra = prms.arith_nl_nra();
m_nla->settings().grobner_subs_fixed = prms.arith_nl_grobner_subs_fixed();
m_nla->settings().grobner_eqs_growth = prms.arith_nl_grobner_eqs_growth();
m_nla->settings().grobner_expr_size_growth = prms.arith_nl_grobner_expr_size_growth();
m_nla->settings().grobner_expr_degree_growth = prms.arith_nl_grobner_expr_degree_growth();
m_nla->settings().grobner_max_simplified = prms.arith_nl_grobner_max_simplified();
m_nla->settings().grobner_number_of_conflicts_to_report = prms.arith_nl_grobner_cnfl_to_report();
m_nla->settings().grobner_quota = prms.arith_nl_gr_q();
m_nla->settings().grobner_frequency = prms.arith_nl_grobner_frequency();
m_nla->settings().expensive_patching = false;
}
}

View file

@ -442,6 +442,7 @@ namespace bv {
SASSERT(bv.is_int2bv(n));
euf::enode* e = expr2enode(n);
mk_bits(e->get_th_var(get_id()));
get_var(e->get_arg(0));
assert_int2bv_axiom(n);
}

View file

@ -209,6 +209,22 @@ namespace bv {
if (is_bv(eq.v1())) {
m_find.merge(eq.v1(), eq.v2());
VERIFY(eq.is_eq());
return;
}
euf::enode* n1 = var2enode(eq.v1());
for (euf::enode* bv2int : euf::enode_class(n1)) {
if (!bv.is_bv2int(bv2int->get_expr()))
continue;
euf::enode* bv2int_arg = bv2int->get_arg(0);
for (euf::enode* p : euf::enode_parents(n1->get_root())) {
if (bv.is_int2bv(p->get_expr()) && p->get_sort() == bv2int_arg->get_sort() && p->get_root() != bv2int_arg->get_root()) {
euf::enode_pair_vector eqs;
eqs.push_back({ n1, p->get_arg(0) });
eqs.push_back({ n1, bv2int });
ctx.propagate(p, bv2int_arg, euf::th_explain::propagate(*this, eqs, p, bv2int_arg));
break;
}
}
}
}

View file

@ -506,7 +506,7 @@ namespace dt {
return m_nodes;
}
ptr_vector<euf::enode> const& solver::get_seq_args(enode* n) {
ptr_vector<euf::enode> const& solver::get_seq_args(enode* n, enode*& sibling) {
m_nodes.reset();
m_todo.reset();
auto add_todo = [&](enode* n) {
@ -515,9 +515,15 @@ namespace dt {
m_todo.push_back(n);
}
};
for (enode* sib : euf::enode_class(n))
add_todo(sib);
for (enode* sib : euf::enode_class(n)) {
if (m_sutil.str.is_concat_of_units(sib->get_expr())) {
add_todo(sib);
sibling = sib;
break;
}
}
for (unsigned i = 0; i < m_todo.size(); ++i) {
enode* n = m_todo[i];
@ -551,10 +557,10 @@ namespace dt {
// collect equalities on all children that may have been used.
bool found = false;
auto add = [&](enode* arg) {
if (arg->get_root() == child->get_root()) {
if (arg != child)
m_used_eqs.push_back(enode_pair(arg, child));
auto add = [&](enode* seq_arg) {
if (seq_arg->get_root() == child->get_root()) {
if (seq_arg != child)
m_used_eqs.push_back(enode_pair(seq_arg, child));
found = true;
}
};
@ -564,11 +570,14 @@ namespace dt {
if (m_autil.is_array(s) && dt.is_datatype(get_array_range(s)))
for (enode* aarg : get_array_args(arg))
add(aarg);
}
sort* se;
if (m_sutil.is_seq(child->get_sort(), se) && dt.is_datatype(se)) {
for (enode* aarg : get_seq_args(child))
add(aarg);
sort* se;
if (m_sutil.is_seq(arg->get_sort(), se) && dt.is_datatype(se)) {
enode* sibling = nullptr;
for (enode* seq_arg : get_seq_args(arg, sibling))
add(seq_arg);
if (sibling && sibling != arg)
m_used_eqs.push_back(enode_pair(arg, sibling));
}
}
VERIFY(found);
@ -636,12 +645,13 @@ namespace dt {
// explore `arg` (with parent)
expr* earg = arg->get_expr();
sort* s = earg->get_sort(), *se;
enode* sibling;
if (dt.is_datatype(s)) {
m_parent.insert(arg->get_root(), parent);
oc_push_stack(arg);
}
else if (m_sutil.is_seq(s, se) && dt.is_datatype(se)) {
for (enode* sarg : get_seq_args(arg))
for (enode* sarg : get_seq_args(arg, sibling))
if (process_arg(sarg))
return true;
}

View file

@ -112,7 +112,7 @@ namespace dt {
void oc_push_stack(enode * n);
ptr_vector<enode> m_nodes, m_todo;
ptr_vector<enode> const& get_array_args(enode* n);
ptr_vector<enode> const& get_seq_args(enode* n);
ptr_vector<enode> const& get_seq_args(enode* n, enode*& sibling);
void pop_core(unsigned n) override;

View file

@ -1795,9 +1795,9 @@ namespace pb {
}
if (c.lit() != sat::null_literal && value(c.lit()) != l_true) return true;
SASSERT(c.lit() == sat::null_literal || lvl(c.lit()) == 0 || (c.is_watched(*this, c.lit()) && c.is_watched(*this, ~c.lit())));
if (eval(c) == l_true) {
if (eval(c) == l_true)
return true;
}
literal_vector lits(c.literals());
for (literal l : lits) {
if (lvl(l) == 0) continue;
@ -1823,6 +1823,8 @@ namespace pb {
}
bool solver::validate_watch(pbc const& p, literal alit) const {
if (value(p.lit()) != l_true)
return true;
for (unsigned i = 0; i < p.size(); ++i) {
literal l = p[i].second;
if (l != alit && lvl(l) != 0 && p.is_watched(*this, l) != (i < p.num_watch())) {
@ -1833,9 +1835,8 @@ namespace pb {
}
}
unsigned slack = 0;
for (unsigned i = 0; i < p.num_watch(); ++i) {
slack += p[i].first;
}
for (unsigned i = 0; i < p.num_watch(); ++i)
slack += p[i].first;
if (slack != p.slack()) {
IF_VERBOSE(0, display(verbose_stream(), p, true););
UNREACHABLE();

View file

@ -270,6 +270,10 @@ namespace euf {
return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
}
th_explain* th_explain::propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma) {
return mk(th, 0, nullptr, eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
}
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y) {
return mk(th, 1, &lit, 0, nullptr, sat::null_literal, x, y);
}

View file

@ -241,6 +241,7 @@ namespace euf {
static th_explain* conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
static th_explain* conflict(th_euf_solver& th, euf::enode* x, euf::enode* y);
static th_explain* propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
static th_explain* propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, sat::proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma = nullptr);

View file

@ -72,7 +72,7 @@ def_module_params(module_name='smt',
('arith.nl.grobner_max_simplified', UINT, 10000, 'grobner\'s maximum number of simplifications'),
('arith.nl.grobner_cnfl_to_report', UINT, 1, 'grobner\'s maximum number of conflicts to report'),
('arith.nl.gr_q', UINT, 10, 'grobner\'s quota'),
('arith.nl.grobner_subs_fixed', UINT, 2, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'),
('arith.nl.grobner_subs_fixed', UINT, 1, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'),
('arith.nl.delay', UINT, 500, 'number of calls to final check before invoking bounded nlsat check'),
('arith.propagate_eqs', BOOL, True, 'propagate (cheap) equalities'),
('arith.propagation_mode', UINT, 1, '0 - no propagation, 1 - propagate existing literals, 2 - refine finite bounds'),

View file

@ -1429,7 +1429,10 @@ namespace smt {
inc_ref(l2);
m_watches[(~l1).index()].insert_literal(l2);
m_watches[(~l2).index()].insert_literal(l1);
if (get_assignment(l2) == l_false) {
if (get_assignment(l1) == l_false) {
assign(l2, b_justification(~l1));
}
else if (get_assignment(l2) == l_false) {
assign(l1, b_justification(~l2));
}
m_clause_proof.add(l1, l2, k, j);

View file

@ -258,7 +258,7 @@ namespace smt {
bindings.set(num_decls - i - 1, sk_value);
}
TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: " << bindings << "\n" << defs << "\n";);
TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: " << bindings << "\ndefs:\n" << defs << "\n";);
if (!defs.empty()) def = mk_and(defs);
max_generation = std::max(m_qm->get_generation(q), max_generation);
add_instance(q, bindings, max_generation, def.get());
@ -453,7 +453,7 @@ namespace smt {
TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";);
m_max_cexs += m_params.m_mbqi_max_cexs;
if (num_failures == 0 && (!m_context->validate_model())) {
if (num_failures == 0 && !m_context->validate_model()) {
num_failures = 1;
// this time force expanding recursive function definitions
// that are not forced true in the current model.

View file

@ -348,6 +348,7 @@ public:
m_eq_eh = nullptr;
m_diseq_eh = nullptr;
m_created_eh = nullptr;
m_decide_eh = nullptr;
}
void user_propagate_init(
@ -385,6 +386,10 @@ public:
void user_propagate_register_created(user_propagator::created_eh_t& created_eh) override {
m_created_eh = created_eh;
}
void user_propagate_register_decide(user_propagator::decide_eh_t& decide_eh) override {
m_decide_eh = decide_eh;
}
};
static tactic * mk_seq_smt_tactic(ast_manager& m, params_ref const & p) {

View file

@ -492,6 +492,7 @@ namespace smt {
void theory_arith<Ext>::mk_axiom(expr * ante, expr * conseq, bool simplify_conseq) {
th_rewriter & s = ctx.get_rewriter();
expr_ref s_ante(m), s_conseq(m);
expr_ref p_ante(ante, m), p_conseq(conseq, m); // pinned versions
expr* s_conseq_n, * s_ante_n;
bool negated;
@ -562,7 +563,7 @@ namespace smt {
if (!m_util.is_zero(divisor)) {
// if divisor is zero, then idiv and mod are uninterpreted functions.
expr_ref div(m), mod(m), zero(m), abs_divisor(m), one(m);
expr_ref eqz(m), eq(m), lower(m), upper(m);
expr_ref eqz(m), eq(m), lower(m), upper(m), qr(m);
div = m_util.mk_idiv(dividend, divisor);
mod = m_util.mk_mod(dividend, divisor);
zero = m_util.mk_int(0);
@ -570,7 +571,8 @@ namespace smt {
abs_divisor = m_util.mk_sub(m.mk_ite(m_util.mk_lt(divisor, zero), m_util.mk_sub(zero, divisor), divisor), one);
s(abs_divisor);
eqz = m.mk_eq(divisor, zero);
eq = m.mk_eq(m_util.mk_add(m_util.mk_mul(divisor, div), mod), dividend);
qr = m_util.mk_add(m_util.mk_mul(divisor, div), mod);
eq = m.mk_eq(qr, dividend);
lower = m_util.mk_ge(mod, zero);
upper = m_util.mk_le(mod, abs_divisor);
TRACE("div_axiom_bug",
@ -584,6 +586,8 @@ namespace smt {
mk_axiom(eqz, upper, !m_util.is_numeral(abs_divisor));
rational k;
m_arith_eq_adapter.mk_axioms(ensure_enode(qr), ensure_enode(mod));
if (m_util.is_zero(dividend)) {
mk_axiom(eqz, m.mk_eq(div, zero));
mk_axiom(eqz, m.mk_eq(mod, zero));
@ -591,7 +595,7 @@ namespace smt {
// (or (= y 0) (<= (* y (div x y)) x))
else if (!m_util.is_numeral(divisor)) {
expr_ref div_ge(m), div_le(m), ge(m), le(m);
expr_ref div_ge(m);
div_ge = m_util.mk_ge(m_util.mk_sub(dividend, m_util.mk_mul(divisor, div)), zero);
s(div_ge);
mk_axiom(eqz, div_ge, false);

View file

@ -80,14 +80,12 @@ void theory_arith<Ext>::mark_dependents(theory_var v, svector<theory_var> & vars
if (is_fixed(v))
return;
column & c = m_columns[v];
typename svector<col_entry>::iterator it = c.begin_entries();
typename svector<col_entry>::iterator end = c.end_entries();
for (; it != end; ++it) {
if (it->is_dead() || already_visited_rows.contains(it->m_row_id))
for (auto& ce : c) {
if (ce.is_dead() || already_visited_rows.contains(ce.m_row_id))
continue;
TRACE("non_linear_bug", tout << "visiting row: " << it->m_row_id << "\n";);
already_visited_rows.insert(it->m_row_id);
row & r = m_rows[it->m_row_id];
TRACE("non_linear_bug", tout << "visiting row: " << ce.m_row_id << "\n";);
already_visited_rows.insert(ce.m_row_id);
row & r = m_rows[ce.m_row_id];
theory_var s = r.get_base_var();
// ignore quasi base vars... actually they should not be used if the problem is non linear...
if (is_quasi_base(s))
@ -97,14 +95,10 @@ void theory_arith<Ext>::mark_dependents(theory_var v, svector<theory_var> & vars
// was eliminated by substitution.
if (s != null_theory_var && is_free(s) && s != v)
continue;
typename vector<row_entry>::const_iterator it2 = r.begin_entries();
typename vector<row_entry>::const_iterator end2 = r.end_entries();
for (; it2 != end2; ++it2) {
if (!it2->is_dead() && !is_fixed(it2->m_var))
mark_var(it2->m_var, vars, already_found);
if (!it2->is_dead() && is_fixed(it2->m_var)) {
TRACE("non_linear", tout << "skipped fixed\n";);
}
for (auto& re : r) {
if (!re.is_dead() && !is_fixed(re.m_var))
mark_var(re.m_var, vars, already_found);
CTRACE("non_linear", !re.is_dead() && is_fixed(re.m_var), tout << "skipped fixed\n");
}
}
}
@ -119,6 +113,7 @@ void theory_arith<Ext>::get_non_linear_cluster(svector<theory_var> & vars) {
return;
var_set already_found;
row_set already_visited_rows;
for (theory_var v : m_nl_monomials) {
expr * n = var2expr(v);
if (ctx.is_relevant(n))
@ -130,9 +125,9 @@ void theory_arith<Ext>::get_non_linear_cluster(svector<theory_var> & vars) {
TRACE("non_linear", tout << "marking dependents of: v" << v << "\n";);
mark_dependents(v, vars, already_found, already_visited_rows);
}
TRACE("non_linear", tout << "variables in non linear cluster:\n";
for (theory_var v : vars) tout << "v" << v << " ";
tout << "\n";);
TRACE("non_linear", tout << "variables in non linear cluster: ";
for (theory_var v : vars) tout << "v" << v << " "; tout << "\n";
for (theory_var v : m_nl_monomials) tout << "non-linear v" << v << " " << mk_pp(var2expr(v), m) << "\n";);
}
@ -1740,22 +1735,21 @@ grobner::monomial * theory_arith<Ext>::mk_gb_monomial(rational const & _coeff, e
*/
template<typename Ext>
void theory_arith<Ext>::add_row_to_gb(row const & r, grobner & gb) {
TRACE("non_linear", tout << "adding row to gb\n"; display_row(tout, r););
TRACE("grobner", tout << "adding row to gb\n"; display_row(tout, r););
ptr_buffer<grobner::monomial> monomials;
v_dependency * dep = nullptr;
m_tmp_var_set.reset();
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
rational coeff = it->m_coeff.to_rational();
expr * m = var2expr(it->m_var);
TRACE("non_linear", tout << "monomial: " << mk_pp(m, get_manager()) << "\n";);
grobner::monomial * new_m = mk_gb_monomial(coeff, m, gb, dep, m_tmp_var_set);
TRACE("non_linear", tout << "new monomial:\n"; if (new_m) gb.display_monomial(tout, *new_m); else tout << "null"; tout << "\n";);
if (new_m)
monomials.push_back(new_m);
}
for (auto& re : r) {
if (re.is_dead())
continue;
rational coeff = re.m_coeff.to_rational();
expr * m = var2expr(re.m_var);
grobner::monomial * new_m = mk_gb_monomial(coeff, m, gb, dep, m_tmp_var_set);
if (new_m)
monomials.push_back(new_m);
TRACE("grobner",
tout << "monomial: " << mk_pp(m, get_manager()) << "\n";
tout << "new monomial: "; if (new_m) gb.display_monomial(tout, *new_m); else tout << "null"; tout << "\n";);
}
gb.assert_eq_0(monomials.size(), monomials.data(), dep);
}
@ -2158,8 +2152,9 @@ bool theory_arith<Ext>::get_gb_eqs_and_look_for_conflict(ptr_vector<grobner::equ
eqs.reset();
gb.get_equations(eqs);
TRACE("grobner", tout << "after gb\n";
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { out << "v" << expr2var(v); };
for (grobner::equation* eq : eqs)
gb.display_equation(tout, *eq);
gb.display_equation(tout, *eq, _fn);
);
for (grobner::equation* eq : eqs) {
if (is_inconsistent(eq, gb) || is_inconsistent2(eq, gb)) {
@ -2259,7 +2254,9 @@ typename theory_arith<Ext>::gb_result theory_arith<Ext>::compute_grobner(svector
ptr_vector<grobner::equation> eqs;
do {
TRACE("non_linear_gb", tout << "before:\n"; gb.display(tout););
TRACE("grobner", tout << "before grobner:\n";
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { out << "v" << expr2var(v); };
gb.display(tout, _fn));
compute_basis(gb, warn);
update_statistics(gb);
TRACE("non_linear_gb", tout << "after:\n"; gb.display(tout););

View file

@ -101,7 +101,7 @@ namespace smt {
SASSERT(num_args >= 3);
sel_args.push_back(n);
for (unsigned i = 1; i < num_args - 1; ++i) {
sel_args.push_back(to_app(n->get_arg(i)));
sel_args.push_back(n->get_arg(i));
}
expr_ref sel(m);
sel = mk_select(sel_args.size(), sel_args.data());

View file

@ -594,10 +594,13 @@ namespace smt {
if (!ctx.add_fingerprint(this, m_default_lambda_fingerprint, 1, &arr))
return false;
m_stats.m_num_default_lambda_axiom++;
expr* def = mk_default(arr->get_expr());
expr* e = arr->get_expr();
expr* def = mk_default(e);
quantifier* lam = m.is_lambda_def(arr->get_decl());
expr_ref_vector args(m);
args.push_back(lam);
TRACE("array", tout << mk_pp(lam, m) << "\n" << mk_pp(e, m) << "\n");
expr_ref_vector args(m);
var_subst subst(m, false);
args.push_back(subst(lam, to_app(e)->get_num_args(), to_app(e)->get_args()));
for (unsigned i = 0; i < lam->get_num_decls(); ++i)
args.push_back(mk_epsilon(lam->get_decl_sort(i)).first);
expr_ref val(mk_select(args), m);

View file

@ -52,9 +52,8 @@ namespace smt {
bits.reset();
m_bits_expr.reset();
for (unsigned i = 0; i < bv_size; i++) {
for (unsigned i = 0; i < bv_size; i++)
m_bits_expr.push_back(mk_bit2bool(owner, i));
}
ctx.internalize(m_bits_expr.data(), bv_size, true);
for (unsigned i = 0; i < bv_size; i++) {
@ -601,9 +600,8 @@ namespace smt {
TRACE("bv", tout << mk_bounded_pp(n, m) << "\n";);
process_args(n);
mk_enode(n);
if (!ctx.relevancy()) {
if (!ctx.relevancy())
assert_bv2int_axiom(n);
}
}
@ -669,10 +667,12 @@ namespace smt {
mk_enode(n);
theory_var v = ctx.get_enode(n)->get_th_var(get_id());
mk_bits(v);
if (!ctx.relevancy()) {
enode* k = ctx.get_enode(n->get_arg(0));
if (!is_attached_to_var(k))
mk_var(k);
if (!ctx.relevancy())
assert_int2bv_axiom(n);
}
}
void theory_bv::assert_int2bv_axiom(app* n) {
@ -1497,6 +1497,26 @@ namespace smt {
unsigned sz = m_bits[v1].size();
bool changed = true;
TRACE("bv", tout << "bits size: " << sz << "\n";);
if (sz == 0) {
// int2bv(bv2int(x)) = x when int2bv(bv2int(x)) has same sort as x
enode* n1 = get_enode(r1);
for (enode* bv2int : *n1) {
if (!m_util.is_bv2int(bv2int->get_expr()))
continue;
enode* bv2int_arg = bv2int->get_arg(0);
for (enode* p : enode::parents(n1->get_root())) {
if (m_util.is_int2bv(p->get_expr()) && p->get_root() != bv2int_arg->get_root() && p->get_sort() == bv2int_arg->get_sort()) {
enode_pair_vector eqs;
eqs.push_back({n1, p->get_arg(0) });
eqs.push_back({n1, bv2int});
justification * js = ctx.mk_justification(
ext_theory_eq_propagation_justification(get_id(), ctx.get_region(), 0, nullptr, eqs.size(), eqs.data(), p, bv2int_arg));
ctx.assign_eq(p, bv2int_arg, eq_justification(js));
break;
}
}
}
}
do {
// This outerloop is necessary to avoid missing propagation steps.
// For example, let's assume that bits1 and bits2 contains the following

View file

@ -25,6 +25,7 @@ Revision History:
#include "smt/theory_datatype.h"
#include "smt/theory_array.h"
#include "smt/smt_model_generator.h"
#include <iostream>
namespace smt {
@ -519,9 +520,8 @@ namespace smt {
void theory_datatype::explain_is_child(enode* parent, enode* child) {
enode * parentc = oc_get_cstor(parent);
if (parent != parentc) {
if (parent != parentc)
m_used_eqs.push_back(enode_pair(parent, parentc));
}
// collect equalities on all children that may have been used.
bool found = false;
@ -546,14 +546,17 @@ namespace smt {
}
sort* se = nullptr;
if (m_sutil.is_seq(s, se) && m_util.is_datatype(se)) {
for (enode* aarg : get_seq_args(arg)) {
enode* sibling;
for (enode* aarg : get_seq_args(arg, sibling)) {
if (aarg->get_root() == child->get_root()) {
if (aarg != child) {
if (aarg != child)
m_used_eqs.push_back(enode_pair(aarg, child));
}
found = true;
}
}
if (sibling && sibling != arg)
m_used_eqs.push_back(enode_pair(arg, sibling));
}
}
VERIFY(found);
@ -582,9 +585,11 @@ namespace smt {
TRACE("datatype",
tout << "occurs_check\n";
for (enode_pair const& p : m_used_eqs) {
for (enode_pair const& p : m_used_eqs)
tout << enode_eq_pp(p, ctx);
});
for (auto const& [a,b] : m_used_eqs)
tout << mk_pp(a->get_expr(), m) << " = " << mk_pp(b->get_expr(), m) << "\n";
);
}
// start exploring subgraph below `app`
@ -596,9 +601,9 @@ namespace smt {
}
v = m_find.find(v);
var_data * d = m_var_data[v];
if (!d->m_constructor) {
if (!d->m_constructor)
return false;
}
enode * parent = d->m_constructor;
oc_mark_on_stack(parent);
auto process_arg = [&](enode* aarg) {
@ -616,9 +621,8 @@ namespace smt {
};
for (enode * arg : enode::args(parent)) {
if (oc_cycle_free(arg)) {
if (oc_cycle_free(arg))
continue;
}
if (oc_on_stack(arg)) {
// arg was explored before app, and is still on the stack: cycle
occurs_check_explain(parent, arg);
@ -632,9 +636,11 @@ namespace smt {
oc_push_stack(arg);
}
else if (m_sutil.is_seq(s, se) && m_util.is_datatype(se)) {
for (enode* sarg : get_seq_args(arg))
if (process_arg(sarg))
enode* sibling;
for (enode* sarg : get_seq_args(arg, sibling)) {
if (process_arg(sarg))
return true;
}
}
else if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) {
for (enode* aarg : get_array_args(arg))
@ -645,7 +651,7 @@ namespace smt {
return false;
}
ptr_vector<enode> const& theory_datatype::get_seq_args(enode* n) {
ptr_vector<enode> const& theory_datatype::get_seq_args(enode* n, enode*& sibling) {
m_args.reset();
m_todo.reset();
auto add_todo = [&](enode* n) {
@ -654,9 +660,14 @@ namespace smt {
m_todo.push_back(n);
}
};
for (enode* sib : *n)
add_todo(sib);
for (enode* sib : *n) {
if (m_sutil.str.is_concat_of_units(sib->get_expr())) {
add_todo(sib);
sibling = sib;
break;
}
}
for (unsigned i = 0; i < m_todo.size(); ++i) {
enode* n = m_todo[i];
@ -691,7 +702,7 @@ namespace smt {
a3 = cons(v3, a1)
*/
bool theory_datatype::occurs_check(enode * n) {
TRACE("datatype", tout << "occurs check: " << enode_pp(n, ctx) << "\n";);
TRACE("datatype_verbose", tout << "occurs check: " << enode_pp(n, ctx) << "\n";);
m_stats.m_occurs_check++;
bool res = false;
@ -706,7 +717,7 @@ namespace smt {
if (oc_cycle_free(app))
continue;
TRACE("datatype", tout << "occurs check loop: " << enode_pp(app, ctx) << (op==ENTER?" enter":" exit")<< "\n";);
TRACE("datatype_verbose", tout << "occurs check loop: " << enode_pp(app, ctx) << (op==ENTER?" enter":" exit")<< "\n";);
switch (op) {
case ENTER:
@ -830,15 +841,14 @@ namespace smt {
SASSERT(d->m_constructor);
func_decl * c_decl = d->m_constructor->get_decl();
datatype_value_proc * result = alloc(datatype_value_proc, c_decl);
for (enode* arg : enode::args(d->m_constructor)) {
for (enode* arg : enode::args(d->m_constructor))
result->add_dependency(arg);
}
TRACE("datatype",
tout << pp(n, m) << "\n";
tout << "depends on\n";
for (enode* arg : enode::args(d->m_constructor)) {
for (enode* arg : enode::args(d->m_constructor))
tout << " " << pp(arg, m) << "\n";
});
);
return result;
}
@ -965,12 +975,11 @@ namespace smt {
SASSERT(!lits.empty());
region & reg = ctx.get_region();
TRACE("datatype_conflict", tout << mk_ismt2_pp(recognizer->get_expr(), m) << "\n";
for (literal l : lits) {
for (literal l : lits)
ctx.display_detailed_literal(tout, l) << "\n";
}
for (auto const& p : eqs) {
for (auto const& p : eqs)
tout << enode_eq_pp(p, ctx);
});
);
ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), reg, lits.size(), lits.data(), eqs.size(), eqs.data())));
}
else if (num_unassigned == 1) {
@ -1052,9 +1061,8 @@ namespace smt {
ctx.mark_as_relevant(curr);
return;
}
else if (ctx.get_assignment(curr) != l_false) {
else if (ctx.get_assignment(curr) != l_false)
return;
}
}
if (r == nullptr)
return; // all recognizers are asserted to false... conflict will be detected...

View file

@ -94,7 +94,7 @@ namespace smt {
void oc_push_stack(enode * n);
ptr_vector<enode> m_args, m_todo;
ptr_vector<enode> const& get_array_args(enode* n);
ptr_vector<enode> const& get_seq_args(enode* n);
ptr_vector<enode> const& get_seq_args(enode* n, enode*& sibling);
// class for managing state of final_check
class final_check_st {

View file

@ -276,23 +276,23 @@ class theory_lra::imp {
m_nla->push();
}
smt_params_helper prms(ctx().get_params());
m_nla->settings().run_order() = prms.arith_nl_order();
m_nla->settings().run_tangents() = prms.arith_nl_tangents();
m_nla->settings().run_horner() = prms.arith_nl_horner();
m_nla->settings().horner_subs_fixed() = prms.arith_nl_horner_subs_fixed();
m_nla->settings().horner_frequency() = prms.arith_nl_horner_frequency();
m_nla->settings().horner_row_length_limit() = prms.arith_nl_horner_row_length_limit();
m_nla->settings().run_grobner() = prms.arith_nl_grobner();
m_nla->settings().run_nra() = prms.arith_nl_nra();
m_nla->settings().grobner_subs_fixed() = prms.arith_nl_grobner_subs_fixed();
m_nla->settings().grobner_eqs_growth() = prms.arith_nl_grobner_eqs_growth();
m_nla->settings().grobner_expr_size_growth() = prms.arith_nl_grobner_expr_size_growth();
m_nla->settings().grobner_expr_degree_growth() = prms.arith_nl_grobner_expr_degree_growth();
m_nla->settings().grobner_max_simplified() = prms.arith_nl_grobner_max_simplified();
m_nla->settings().grobner_number_of_conflicts_to_report() = prms.arith_nl_grobner_cnfl_to_report();
m_nla->settings().grobner_quota() = prms.arith_nl_gr_q();
m_nla->settings().grobner_frequency() = prms.arith_nl_grobner_frequency();
m_nla->settings().expensive_patching() = false;
m_nla->settings().run_order = prms.arith_nl_order();
m_nla->settings().run_tangents = prms.arith_nl_tangents();
m_nla->settings().run_horner = prms.arith_nl_horner();
m_nla->settings().horner_subs_fixed = prms.arith_nl_horner_subs_fixed();
m_nla->settings().horner_frequency = prms.arith_nl_horner_frequency();
m_nla->settings().horner_row_length_limit = prms.arith_nl_horner_row_length_limit();
m_nla->settings().run_grobner = prms.arith_nl_grobner();
m_nla->settings().run_nra = prms.arith_nl_nra();
m_nla->settings().grobner_subs_fixed = prms.arith_nl_grobner_subs_fixed();
m_nla->settings().grobner_eqs_growth = prms.arith_nl_grobner_eqs_growth();
m_nla->settings().grobner_expr_size_growth = prms.arith_nl_grobner_expr_size_growth();
m_nla->settings().grobner_expr_degree_growth = prms.arith_nl_grobner_expr_degree_growth();
m_nla->settings().grobner_max_simplified = prms.arith_nl_grobner_max_simplified();
m_nla->settings().grobner_number_of_conflicts_to_report = prms.arith_nl_grobner_cnfl_to_report();
m_nla->settings().grobner_quota = prms.arith_nl_gr_q();
m_nla->settings().grobner_frequency = prms.arith_nl_grobner_frequency();
m_nla->settings().expensive_patching = false;
}
}
@ -1224,9 +1224,9 @@ public:
return;
}
expr_ref mod_r(a.mk_add(a.mk_mul(q, div), mod), m);
ctx().get_rewriter()(mod_r);
expr_ref eq_r(th.mk_eq_atom(mod_r, p), m);
ctx().internalize(eq_r, false);
ctx().internalize(eq_r, false);
literal eq = ctx().get_literal(eq_r);
rational k(0);
@ -1256,6 +1256,39 @@ public:
}
else {
expr_ref abs_q(m.mk_ite(a.mk_ge(q, zero), q, a.mk_uminus(q)), m);
expr_ref mone(a.mk_int(-1), m);
expr_ref modmq(a.mk_sub(mod, abs_q), m);
ctx().get_rewriter()(modmq);
literal eqz = mk_literal(m.mk_eq(q, zero));
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
literal mod_lt_q = mk_literal(a.mk_le(modmq, mone));
// q = 0 or p = (p mod q) + q * (p div q)
// q = 0 or (p mod q) >= 0
// q = 0 or (p mod q) < abs(q)
mk_axiom(eqz, eq);
mk_axiom(eqz, mod_ge_0);
mk_axiom(eqz, mod_lt_q);
m_arith_eq_adapter.mk_axioms(th.ensure_enode(mod_r), th.ensure_enode(p));
if (a.is_zero(p)) {
mk_axiom(eqz, mk_literal(m.mk_eq(div, zero)));
mk_axiom(eqz, mk_literal(m.mk_eq(mod, zero)));
}
// (or (= y 0) (<= (* y (div x y)) x))
else if (!a.is_numeral(q)) {
expr_ref div_ge(m);
div_ge = a.mk_ge(a.mk_sub(p, a.mk_mul(q, div)), zero);
ctx().get_rewriter()(div_ge);
mk_axiom(eqz, mk_literal(div_ge));
TRACE("arith", tout << eqz << " " << div_ge << "\n");
}
#if 0
/*literal div_ge_0 = */ mk_literal(a.mk_ge(div, zero));
/*literal div_le_0 = */ mk_literal(a.mk_le(div, zero));
/*literal p_ge_0 = */ mk_literal(a.mk_ge(p, zero));
@ -1266,7 +1299,7 @@ public:
// q >= 0 or (p mod q) >= 0
// q <= 0 or (p mod q) >= 0
// q <= 0 or (p mod q) < q
// q >= 0 or (p mod q) < -q
// q >= 0 or (p mod q) < -q
literal q_ge_0 = mk_literal(a.mk_ge(q, zero));
literal q_le_0 = mk_literal(a.mk_le(q, zero));
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
@ -1277,11 +1310,11 @@ public:
mk_axiom(q_le_0, mod_ge_0);
mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero)));
mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero)));
#endif
#if 0
// seem expensive
mk_axiom(q_le_0, ~p_ge_0, div_ge_0);
mk_axiom(q_le_0, ~p_le_0, div_le_0);
mk_axiom(q_ge_0, ~p_ge_0, div_le_0);
@ -1293,19 +1326,21 @@ public:
mk_axiom(q_ge_0, p_le_0, ~div_ge_0);
#endif
#if 0
std::function<void(void)> log = [&,this]() {
th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(eq.var())));
th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(mod_ge_0.var())));
th.log_axiom_unit(m.mk_implies(a.mk_lt(q, zero), a.mk_lt(a.mk_sub(mod, q), zero)));
th.log_axiom_unit(m.mk_implies(a.mk_lt(q, zero), a.mk_lt(a.mk_add(mod, q), zero)));
};
if_trace_stream _ts(m, log);
#endif
#if 0
th.log_axiom_unit(m.mk_implies(m.mk_and(a.mk_gt(q, zero), c.bool_var2expr(p_ge_0.var())), c.bool_var2expr(div_ge_0.var())));
th.log_axiom_unit(m.mk_implies(m.mk_and(a.mk_gt(q, zero), c.bool_var2expr(p_le_0.var())), c.bool_var2expr(div_le_0.var())));
th.log_axiom_unit(m.mk_implies(m.mk_and(a.mk_lt(q, zero), c.bool_var2expr(p_ge_0.var())), c.bool_var2expr(div_le_0.var())));
th.log_axiom_unit(m.mk_implies(m.mk_and(a.mk_lt(q, zero), c.bool_var2expr(p_le_0.var())), c.bool_var2expr(div_ge_0.var())));
#endif
};
if_trace_stream _ts(m, log);
}
if (params().m_arith_enum_const_mod && k.is_pos() && k < rational(8)) {
unsigned _k = k.get_unsigned();

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