3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-11-03 13:07:53 +00:00
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2022-01-07 12:57:39 -08:00
commit ec1a04bbb5
145 changed files with 1703 additions and 1141 deletions

61
.github/workflows/docker-image.yml vendored Normal file
View file

@ -0,0 +1,61 @@
name: Publish Docker image
on:
schedule:
- cron: "0 1 * * 0" # every Sunday at 1 am
workflow_dispatch: # on button click
jobs:
push_to_registry:
name: Push Docker image to GitHub Docker registry
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Log in to GitHub Docker registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# -------
# BARE Z3
# -------
- name: Extract metadata (tags, labels) for Bare Z3 Docker Image
id: meta
uses: docker/metadata-action@v3
with:
images: ghcr.io/z3prover/z3
flavor: |
latest=auto
prefix=ubuntu-20.04-bare-z3-
tags: |
type=schedule,pattern={{date 'YYYYMMDD'}}
type=ref,event=tag
type=edge
type=sha,prefix=ubuntu-20.04-bare-z3-sha-
- name: Build and push Bare Z3 Docker Image
uses: docker/build-push-action@v2.7.0
with:
context: .
push: true
target: bare-z3
file: ./docker/ubuntu-20-04.Dockerfile
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# ------------------------------
# Repo description on GHCR
# ------------------------------
# - name: Update repo description
# uses: peter-evans/dockerhub-description@v2
# with:
# registry: ghcr.io
# repository: z3prover/z3
# username: ${{ secrets.DOCKER_USERNAME }}
# password: ${{ secrets.DOCKER_PASSWORD }}
# short-description: ${{ github.event.repository.description }}
# readme-filepath: ./docker/README.md

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.14.0 LANGUAGES CXX)
project(Z3 VERSION 4.8.15.0 LANGUAGES CXX)
################################################################################
# Project version

View file

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

View file

@ -288,6 +288,7 @@ jobs:
- job: "MacOSOCaml"
displayName: "MacOS build with OCaml"
condition: eq(0,1)
pool:
vmImage: "macOS-latest"
steps:
@ -310,9 +311,9 @@ jobs:
eval `opam config env`
make -j3
make -j3 _ex_ml_example_post_install
./ml_example_shared.byte
./ml_example_shared_custom.byte
./ml_example_shared
# ./ml_example_shared.byte
# ./ml_example_shared_custom.byte
# ./ml_example_shared
cd ..
# Skip as dead-slow in debug mode:
# - template: scripts/test-z3.yml

View file

@ -0,0 +1,45 @@
# -------------
# OS Base image
# -------------
# >> Includes system-wide dependencies
FROM ubuntu:20.04 as lib-base
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get -y --no-install-recommends install \
cmake \
make \
clang \
g++ \
curl \
default-jdk \
python3 \
python3-setuptools \
python-is-python3 \
sudo
# ----------------
# Z3 Builder Image
# ----------------
# >> Includes build files and compiles the basic z3 sources
FROM lib-base as builder
COPY ./ /z3-source/
WORKDIR /z3-source/
RUN python scripts/mk_make.py
WORKDIR /z3-source/build/
RUN make
RUN sudo make install
WORKDIR /z3-source/
# -------
# Bare z3
# -------
# >> Includes only stnadard z3 installations.
# >> Can be used as a standalone interface to z3.
FROM builder as bare-z3
ENTRYPOINT [ "z3" ]
# TODO: introduce Python-binding stage
# ...
# TODO(optional): introduce C/C++ -binding stage
# ...

View file

@ -65,7 +65,7 @@ def unpack(packages, symbols):
mk_dir(f"out/runtimes/{dst}/native")
replace(f"{tmp}/{package_dir}/bin/libz3.{ext}", f"out/runtimes/{dst}/native/libz3.{ext}")
if "x64-win" in f:
mk_dir("out/lib/netstandard1.4/")
mk_dir("out/lib/netstandard2.0/")
if symbols:
zip_ref.extract(f"{package_dir}/bin/libz3.pdb", f"{tmp}")
replace(f"{tmp}/{package_dir}/bin/libz3.pdb", f"out/runtimes/{dst}/native/libz3.pdb")
@ -74,7 +74,7 @@ def unpack(packages, symbols):
files += ["Microsoft.Z3.pdb", "Microsoft.Z3.xml"]
for b in files:
zip_ref.extract(f"{package_dir}/bin/{b}", f"{tmp}")
replace(f"{tmp}/{package_dir}/bin/{b}", f"out/lib/netstandard1.4/{b}")
replace(f"{tmp}/{package_dir}/bin/{b}", f"out/lib/netstandard2.0/{b}")
def mk_targets(source_root):
mk_dir("out/build")
@ -107,7 +107,7 @@ Linux Dependencies:
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<language>en</language>
<dependencies>
<group targetFramework=".NETStandard1.4" />
<group targetFramework=".netstandard2.0" />
</dependencies>
</metadata>
</package>""".format(version, repo, branch, commit)

View file

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

View file

@ -15,7 +15,7 @@ import shutil
from mk_exception import *
import mk_genfile_common
from fnmatch import fnmatch
import distutils.sysconfig
import sysconfig
import compileall
import subprocess
@ -48,7 +48,7 @@ CXX_COMPILERS=['g++', 'clang++']
C_COMPILERS=['gcc', 'clang']
JAVAC=None
JAR=None
PYTHON_PACKAGE_DIR=distutils.sysconfig.get_python_lib(prefix=getenv("PREFIX", None))
PYTHON_PACKAGE_DIR=sysconfig.get_path('purelib')
BUILD_DIR='build'
REV_BUILD_DIR='..'
SRC_DIR='src'
@ -92,7 +92,6 @@ DOTNET_CORE_ENABLED=False
DOTNET_KEY_FILE=getenv("Z3_DOTNET_KEY_FILE", None)
JAVA_ENABLED=False
ML_ENABLED=False
JS_ENABLED=False
PYTHON_INSTALL_ENABLED=False
STATIC_LIB=False
STATIC_BIN=False
@ -681,7 +680,7 @@ def display_help(exit_code):
# Parse configuration option for mk_make script
def parse_options():
global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM
global DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, JAVA_ENABLED, ML_ENABLED, JS_ENABLED, STATIC_LIB, STATIC_BIN, PREFIX, GMP, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH, GIT_DESCRIBE, PYTHON_INSTALL_ENABLED, PYTHON_ENABLED
global DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, JAVA_ENABLED, ML_ENABLED, STATIC_LIB, STATIC_BIN, PREFIX, GMP, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH, GIT_DESCRIBE, PYTHON_INSTALL_ENABLED, PYTHON_ENABLED
global LINUX_X64, SLOW_OPTIMIZE, LOG_SYNC, SINGLE_THREADED
global GUARD_CF, ALWAYS_DYNAMIC_BASE
try:
@ -749,8 +748,6 @@ def parse_options():
GIT_DESCRIBE = True
elif opt in ('', '--ml'):
ML_ENABLED = True
elif opt == "--js":
JS_ENABLED = True
elif opt in ('', '--log-sync'):
LOG_SYNC = True
elif opt == '--single-threaded':
@ -813,16 +810,6 @@ def set_build_dir(d):
BUILD_DIR = norm_path(d)
REV_BUILD_DIR = reverse_path(d)
def set_z3js_dir(p):
global SRC_DIR, Z3JS_SRC_DIR
p = norm_path(p)
full = os.path.join(SRC_DIR, p)
if not os.path.exists(full):
raise MKException("Python bindings directory '%s' does not exist" % full)
Z3JS_SRC_DIR = full
if VERBOSE:
print("Js bindings directory was detected.")
def set_z3py_dir(p):
global SRC_DIR, Z3PY_SRC_DIR
p = norm_path(p)
@ -858,9 +845,6 @@ def get_components():
def get_z3py_dir():
return Z3PY_SRC_DIR
# Return directory where the js bindings are located
def get_z3js_dir():
return Z3JS_SRC_DIR
# Return true if in verbose mode
def is_verbose():
@ -872,9 +856,6 @@ def is_java_enabled():
def is_ml_enabled():
return ML_ENABLED
def is_js_enabled():
return JS_ENABLED
def is_dotnet_core_enabled():
return DOTNET_CORE_ENABLED
@ -1611,7 +1592,7 @@ class PythonInstallComponent(Component):
os.path.join(self.pythonPkgDir,'z3'),
in_prefix=self.in_prefix_install)
if PYTHON_PACKAGE_DIR != distutils.sysconfig.get_python_lib():
if PYTHON_PACKAGE_DIR != sysconfig.get_path('purelib'):
out.write('\t@echo Z3Py was installed at \'%s\', make sure this directory is in your PYTHONPATH environment variable.' % PYTHON_PACKAGE_DIR)
def mk_uninstall(self, out):
@ -2692,7 +2673,7 @@ def mk_config():
print("Python pkg dir: %s" % PYTHON_PACKAGE_DIR)
if GPROF:
print('gprof: enabled')
print('Python version: %s' % distutils.sysconfig.get_python_version())
print('Python version: %s' % sysconfig.get_python_version())
if is_java_enabled():
print('JNI Bindings: %s' % JNI_HOME)
print('Java Compiler: %s' % JAVAC)
@ -3025,17 +3006,14 @@ def mk_bindings(api_files):
ml_output_dir = None
if is_ml_enabled():
ml_output_dir = get_component('ml').src_dir
if is_js_enabled():
set_z3js_dir("api/js")
js_output_dir = get_component('js').src_dir
# Get the update_api module to do the work for us
update_api.VERBOSE = is_verbose()
update_api.generate_files(api_files=new_api_files,
api_output_dir=get_component('api').src_dir,
z3py_output_dir=get_z3py_dir(),
dotnet_output_dir=dotnet_output_dir,
java_output_dir=java_output_dir,
java_package_name=java_package_name,
js_output_dir=get_z3js_dir(),
ml_output_dir=ml_output_dir,
ml_src_dir=ml_output_dir
)

View file

@ -171,6 +171,7 @@ stages:
inputs:
artifact: 'WindowsBuild-x64'
path: $(Agent.TempDirectory)\package
- task: DownloadPipelineArtifact@2
displayName: 'Download Win32 Build'
inputs:
artifact: 'WindowsBuild-x86'
@ -313,7 +314,7 @@ stages:
jobs:
- job: GitHubPublish
condition: eq(0,1)
condition: eq(1,1)
displayName: "Publish to GitHub"
pool:
vmImage: "windows-latest"

View file

@ -15,7 +15,7 @@ several API header files. It can also optionally
emit some of the files required for Z3's different
language bindings.
"""
import mk_util
import mk_exception
import argparse
import logging
@ -23,6 +23,10 @@ import re
import os
import sys
VERBOSE = True
def is_verbose():
return VERBOSE
##########################################################
# TODO: rewrite this file without using global variables.
# This file is a big HACK.
@ -78,42 +82,44 @@ Type2Dotnet = { VOID : 'void', VOID_PTR : 'IntPtr', INT : 'int', UINT : 'uint',
FLOAT : 'float', STRING : 'string', STRING_PTR : 'byte**', BOOL : 'byte', SYMBOL : 'IntPtr',
PRINT_MODE : 'uint', ERROR_CODE : 'uint', CHAR : 'char', CHAR_PTR : 'IntPtr' }
# Mapping to Java types
Type2Java = { VOID : 'void', VOID_PTR : 'long', INT : 'int', UINT : 'int', INT64 : 'long', UINT64 : 'long', DOUBLE : 'double',
FLOAT : 'float', STRING : 'String', STRING_PTR : 'StringPtr',
BOOL : 'boolean', SYMBOL : 'long', PRINT_MODE : 'int', ERROR_CODE : 'int', CHAR : 'char', CHAR_PTR : 'long' }
Type2JavaW = { VOID : 'void', VOID_PTR : 'jlong', INT : 'jint', UINT : 'jint', INT64 : 'jlong', UINT64 : 'jlong', DOUBLE : 'jdouble',
FLOAT : 'jfloat', STRING : 'jstring', STRING_PTR : 'jobject',
BOOL : 'jboolean', SYMBOL : 'jlong', PRINT_MODE : 'jint', ERROR_CODE : 'jint', CHAR : 'jchar', CHAR_PTR : 'jlong'}
# Mapping to ML types
Type2ML = { VOID : 'unit', VOID_PTR : 'VOIDP', INT : 'int', UINT : 'int', INT64 : 'int', UINT64 : 'int', 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' }
next_type_id = FIRST_OBJ_ID
class APITypes:
def __init__(self):
self.next_type_id = FIRST_OBJ_ID
def def_Type(var, c_type, py_type):
global next_type_id
exec('%s = %s' % (var, next_type_id), globals())
Type2Str[next_type_id] = c_type
Type2PyStr[next_type_id] = py_type
next_type_id = next_type_id + 1
def def_Types(api_files):
pat1 = re.compile(" *def_Type\(\'(.*)\',[^\']*\'(.*)\',[^\']*\'(.*)\'\)[ \t]*")
for api_file in api_files:
api = open(api_file, 'r')
for line in api:
m = pat1.match(line)
if m:
def_Type(m.group(1), m.group(2), m.group(3))
for k in Type2Str:
v = Type2Str[k]
if is_obj(k):
Type2Dotnet[k] = v
Type2ML[k] = v.lower()
def def_Type(self, var, c_type, py_type):
"""Process type definitions of the form def_Type(var, c_type, py_type)
The variable 'var' is set to a unique number and recorded globally using exec
It is used by 'def_APIs' to that uses the unique numbers to access the
corresponding C and Python types.
"""
id = self.next_type_id
exec('%s = %s' % (var, id), globals())
Type2Str[id] = c_type
Type2PyStr[id] = py_type
self.next_type_id += 1
def def_Types(self, api_files):
pat1 = re.compile(" *def_Type\(\'(.*)\',[^\']*\'(.*)\',[^\']*\'(.*)\'\)[ \t]*")
for api_file in api_files:
with open(api_file, 'r') as api:
for line in api:
m = pat1.match(line)
if m:
self.def_Type(m.group(1), m.group(2), m.group(3))
#
# Populate object type entries in dotnet and ML bindings.
#
for k in Type2Str:
v = Type2Str[k]
if is_obj(k):
Type2Dotnet[k] = v
Type2ML[k] = v.lower()
def type2str(ty):
global Type2Str
@ -127,20 +133,6 @@ def type2dotnet(ty):
global Type2Dotnet
return Type2Dotnet[ty]
def type2java(ty):
global Type2Java
if (ty >= FIRST_OBJ_ID):
return 'long'
else:
return Type2Java[ty]
def type2javaw(ty):
global Type2JavaW
if (ty >= FIRST_OBJ_ID):
return 'jlong'
else:
return Type2JavaW[ty]
def type2ml(ty):
global Type2ML
q = Type2ML[ty]
@ -214,42 +206,8 @@ def param2dotnet(p):
else:
return type2dotnet(param_type(p))
def param2java(p):
k = param_kind(p)
if k == OUT:
if param_type(p) == INT or param_type(p) == UINT:
return "IntPtr"
elif param_type(p) == INT64 or param_type(p) == UINT64 or param_type(p) == VOID_PTR or param_type(p) >= FIRST_OBJ_ID:
return "LongPtr"
elif param_type(p) == STRING:
return "StringPtr"
else:
print("ERROR: unreachable code")
assert(False)
exit(1)
elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY:
return "%s[]" % type2java(param_type(p))
elif k == OUT_MANAGED_ARRAY:
if param_type(p) == UINT:
return "UIntArrayPtr"
else:
return "ObjArrayPtr"
else:
return type2java(param_type(p))
def param2javaw(p):
k = param_kind(p)
if k == OUT:
return "jobject"
elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY:
if param_type(p) == INT or param_type(p) == UINT or param_type(p) == BOOL:
return "jintArray"
else:
return "jlongArray"
elif k == OUT_MANAGED_ARRAY:
return "jlong"
else:
return type2javaw(param_type(p))
# --------------
def param2pystr(p):
if param_kind(p) == IN_ARRAY or param_kind(p) == OUT_ARRAY or param_kind(p) == IN_ARRAY or param_kind(p) == INOUT_ARRAY or param_kind(p) == OUT:
@ -257,6 +215,9 @@ def param2pystr(p):
else:
return type2pystr(param_type(p))
# --------------
# ML
def param2ml(p):
k = param_kind(p)
if k == OUT:
@ -507,6 +468,68 @@ def mk_dotnet_wrappers(dotnet):
dotnet.write(" }\n\n")
dotnet.write("}\n\n")
# ----------------------
# Java
Type2Java = { VOID : 'void', VOID_PTR : 'long', INT : 'int', UINT : 'int', INT64 : 'long', UINT64 : 'long', DOUBLE : 'double',
FLOAT : 'float', STRING : 'String', STRING_PTR : 'StringPtr',
BOOL : 'boolean', SYMBOL : 'long', PRINT_MODE : 'int', ERROR_CODE : 'int', CHAR : 'char', CHAR_PTR : 'long' }
Type2JavaW = { VOID : 'void', VOID_PTR : 'jlong', INT : 'jint', UINT : 'jint', INT64 : 'jlong', UINT64 : 'jlong', DOUBLE : 'jdouble',
FLOAT : 'jfloat', STRING : 'jstring', STRING_PTR : 'jobject',
BOOL : 'jboolean', SYMBOL : 'jlong', PRINT_MODE : 'jint', ERROR_CODE : 'jint', CHAR : 'jchar', CHAR_PTR : 'jlong'}
def type2java(ty):
global Type2Java
if (ty >= FIRST_OBJ_ID):
return 'long'
else:
return Type2Java[ty]
def type2javaw(ty):
global Type2JavaW
if (ty >= FIRST_OBJ_ID):
return 'jlong'
else:
return Type2JavaW[ty]
def param2java(p):
k = param_kind(p)
if k == OUT:
if param_type(p) == INT or param_type(p) == UINT:
return "IntPtr"
elif param_type(p) == INT64 or param_type(p) == UINT64 or param_type(p) == VOID_PTR or param_type(p) >= FIRST_OBJ_ID:
return "LongPtr"
elif param_type(p) == STRING:
return "StringPtr"
else:
print("ERROR: unreachable code")
assert(False)
exit(1)
elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY:
return "%s[]" % type2java(param_type(p))
elif k == OUT_MANAGED_ARRAY:
if param_type(p) == UINT:
return "UIntArrayPtr"
else:
return "ObjArrayPtr"
else:
return type2java(param_type(p))
def param2javaw(p):
k = param_kind(p)
if k == OUT:
return "jobject"
elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY:
if param_type(p) == INT or param_type(p) == UINT or param_type(p) == BOOL:
return "jintArray"
else:
return "jlongArray"
elif k == OUT_MANAGED_ARRAY:
return "jlong"
else:
return type2javaw(param_type(p))
def java_method_name(name):
result = ''
name = name[3:] # Remove Z3_
@ -776,62 +799,9 @@ def mk_java(java_dir, package_name):
java_wrapper.write('#ifdef __cplusplus\n')
java_wrapper.write('}\n')
java_wrapper.write('#endif\n')
if mk_util.is_verbose():
if is_verbose():
print("Generated '%s'" % java_nativef)
Type2Napi = { VOID : '', VOID_PTR : '', INT : 'number', UINT : 'number', INT64 : 'number', UINT64 : 'number', DOUBLE : 'number',
FLOAT : 'number', STRING : 'string', STRING_PTR : 'array',
BOOL : 'number', SYMBOL : 'external', PRINT_MODE : 'number', ERROR_CODE : 'number', CHAR : 'number' }
def type2napi(t):
try:
return Type2Napi[t]
except:
return "external"
Type2NapiBuilder = { VOID : '', VOID_PTR : '', INT : 'int32', UINT : 'uint32', INT64 : 'int64', UINT64 : 'uint64', DOUBLE : 'double',
FLOAT : 'float', STRING : 'string', STRING_PTR : 'array',
BOOL : 'bool', SYMBOL : 'external', PRINT_MODE : 'int32', ERROR_CODE : 'int32', CHAR : 'char' }
def type2napibuilder(t):
try:
return Type2NapiBuilder[t]
except:
return "external"
def mk_js(js_output_dir):
with open(os.path.join(js_output_dir, "z3.json"), 'w') as ous:
ous.write("{\n")
ous.write(" \"api\": [\n")
for name, result, params in _dotnet_decls:
ous.write(" {\n")
ous.write(" \"name\": \"%s\",\n" % name)
ous.write(" \"c_type\": \"%s\",\n" % Type2Str[result])
ous.write(" \"napi_type\": \"%s\",\n" % type2napi(result))
ous.write(" \"arg_list\": [")
first = True
for p in params:
if first:
first = False
ous.write("\n {\n")
else:
ous.write(",\n {\n")
t = param_type(p)
k = t
ous.write(" \"name\": \"%s\",\n" % "") # TBD
ous.write(" \"c_type\": \"%s\",\n" % type2str(t))
ous.write(" \"napi_type\": \"%s\",\n" % type2napi(t))
ous.write(" \"napi_builder\": \"%s\"\n" % type2napibuilder(t))
ous.write( " }")
ous.write("],\n")
ous.write(" \"napi_builder\": \"%s\"\n" % type2napibuilder(result))
ous.write(" },\n")
ous.write(" ]\n")
ous.write("}\n")
def mk_log_header(file, name, params):
file.write("void log_%s(" % name)
i = 0
@ -842,6 +812,10 @@ def mk_log_header(file, name, params):
i = i + 1
file.write(")")
# ---------------------------------
# Logging
def log_param(p):
kind = param_kind(p)
ty = param_type(p)
@ -1364,7 +1338,7 @@ def mk_ml(ml_src_dir, ml_output_dir):
ml_native.write('(**/**)\n')
ml_native.close()
if mk_util.is_verbose():
if is_verbose():
print ('Generated "%s"' % ml_nativef)
mk_z3native_stubs_c(ml_src_dir, ml_output_dir)
@ -1689,7 +1663,7 @@ def mk_z3native_stubs_c(ml_src_dir, ml_output_dir): # C interface
ml_wrapper.write('}\n')
ml_wrapper.write('#endif\n')
if mk_util.is_verbose():
if is_verbose():
print ('Generated "%s"' % ml_wrapperf)
# Collect API(...) commands from
@ -1889,7 +1863,6 @@ def generate_files(api_files,
dotnet_output_dir=None,
java_output_dir=None,
java_package_name=None,
js_output_dir=None,
ml_output_dir=None,
ml_src_dir=None):
"""
@ -1933,6 +1906,7 @@ def generate_files(api_files,
import tempfile
return tempfile.TemporaryFile(mode=mode)
apiTypes = APITypes()
with mk_file_or_temp(api_output_dir, 'api_log_macros.h') as log_h:
with mk_file_or_temp(api_output_dir, 'api_log_macros.cpp') as log_c:
with mk_file_or_temp(api_output_dir, 'api_commands.cpp') as exe_c:
@ -1944,13 +1918,13 @@ def generate_files(api_files,
write_core_py_preamble(core_py)
# FIXME: these functions are awful
def_Types(api_files)
apiTypes.def_Types(api_files)
def_APIs(api_files)
mk_bindings(exe_c)
mk_py_wrappers()
write_core_py_post(core_py)
if mk_util.is_verbose():
if is_verbose():
print("Generated '{}'".format(log_h.name))
print("Generated '{}'".format(log_c.name))
print("Generated '{}'".format(exe_c.name))
@ -1960,7 +1934,7 @@ def generate_files(api_files,
with open(os.path.join(dotnet_output_dir, 'Native.cs'), 'w') as dotnet_file:
mk_dotnet(dotnet_file)
mk_dotnet_wrappers(dotnet_file)
if mk_util.is_verbose():
if is_verbose():
print("Generated '{}'".format(dotnet_file.name))
if java_output_dir:
@ -1970,8 +1944,6 @@ def generate_files(api_files,
assert not ml_src_dir is None
mk_ml(ml_src_dir, ml_output_dir)
if js_output_dir:
mk_js(js_output_dir)
def main(args):
logging.basicConfig(level=logging.INFO)
@ -2006,10 +1978,6 @@ def main(args):
dest="ml_output_dir",
default=None,
help="Directory to emit OCaml files. If not specified no files are emitted.")
parser.add_argument("--js_output_dir",
dest="js_output_dir",
default=None,
help="Directory to emit js bindings. If not specified no files are emitted.")
pargs = parser.parse_args(args)
if pargs.java_output_dir:
@ -2033,7 +2001,6 @@ def main(args):
dotnet_output_dir=pargs.dotnet_output_dir,
java_output_dir=pargs.java_output_dir,
java_package_name=pargs.java_package_name,
js_output_dir=pargs.js_output_dir,
ml_output_dir=pargs.ml_output_dir,
ml_src_dir=pargs.ml_src_dir)
return 0

View file

@ -47,7 +47,7 @@ extern "C" {
env_params::updt_params();
}
Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value) {
Z3_bool Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value) {
memory::initialize(UINT_MAX);
LOG_Z3_global_param_get(param_id, param_value);
*param_value = nullptr;

View file

@ -585,7 +585,7 @@ extern "C" {
to_fixedpoint_ref(d)->collect_param_descrs(descrs);
to_params(p)->m_params.validate(descrs);
to_fixedpoint_ref(d)->updt_params(to_param_ref(p));
to_fixedpoint(d)->m_params = to_param_ref(p);
to_fixedpoint(d)->m_params.append(to_param_ref(p));
Z3_CATCH;
}

View file

@ -701,6 +701,9 @@ namespace z3 {
sort array_range() const { assert(is_array()); Z3_sort s = Z3_get_array_sort_range(ctx(), *this); check_error(); return sort(ctx(), s); }
friend std::ostream & operator<<(std::ostream & out, sort const & s) { return out << Z3_sort_to_string(s.ctx(), Z3_sort(s.m_ast)); }
func_decl_vector constructors();
func_decl_vector recognizers();
};
/**
@ -741,6 +744,9 @@ namespace z3 {
expr operator()(expr const & a1, expr const & a2, expr const & a3) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const;
func_decl_vector accessors();
};
/**
@ -3863,6 +3869,42 @@ namespace z3 {
return expr_vector(*this, r);
}
inline func_decl_vector sort::constructors() {
assert(is_datatype());
func_decl_vector cs(ctx());
unsigned n = Z3_get_datatype_sort_num_constructors(ctx(), *this);
for (unsigned i = 0; i < n; ++i)
cs.push_back(func_decl(ctx(), Z3_get_datatype_sort_constructor(ctx(), *this, i)));
return cs;
}
inline func_decl_vector sort::recognizers() {
assert(is_datatype());
func_decl_vector rs(ctx());
unsigned n = Z3_get_datatype_sort_num_constructors(ctx(), *this);
for (unsigned i = 0; i < n; ++i)
rs.push_back(func_decl(ctx(), Z3_get_datatype_sort_recognizer(ctx(), *this, i)));
return rs;
}
inline func_decl_vector func_decl::accessors() {
sort s = range();
assert(s.is_datatype());
unsigned n = Z3_get_datatype_sort_num_constructors(ctx(), s);
unsigned idx = 0;
for (; idx < n; ++idx) {
func_decl f(ctx(), Z3_get_datatype_sort_constructor(ctx(), s, idx));
if (id() == f.id())
break;
}
assert(idx < n);
n = arity();
func_decl_vector as(ctx());
for (unsigned i = 0; i < n; ++i)
as.push_back(func_decl(ctx(), Z3_get_datatype_sort_constructor_accessor(ctx(), s, idx, i)));
return as;
}
inline expr expr::substitute(expr_vector const& src, expr_vector const& dst) {
assert(src.size() == dst.size());

View file

@ -18,8 +18,6 @@ add_custom_command(OUTPUT "${Z3_DOTNET_NATIVE_FILE}"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"${PROJECT_SOURCE_DIR}/scripts/update_api.py"
${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES}
# FIXME: When update_api.py no longer uses ``mk_util`` drop this dependency
"${PROJECT_SOURCE_DIR}/scripts/mk_util.py"
COMMENT "Generating ${Z3_DOTNET_NATIVE_FILE}"
${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG}
)

View file

@ -203,7 +203,7 @@ end = struct
let equal = (=)
(* The standard comparison uses the custom operations of the C layer *)
let compare = Pervasives.compare
let compare = Stdlib.compare
let translate (x:ast) (to_ctx:context) =
if gc x = to_ctx then

View file

@ -99,7 +99,7 @@ if (Z3_INSTALL_PYTHON_BINDINGS)
message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR not set. Trying to guess")
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" "-c"
"import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())"
"import sysconfig; print(sysconfig.get_path('purelib'))"
RESULT_VARIABLE exit_code
OUTPUT_VARIABLE CMAKE_INSTALL_PYTHON_PKG_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE

View file

@ -8789,6 +8789,10 @@ def Product(*args):
_args, sz = _to_ast_array(args)
return ArithRef(Z3_mk_mul(ctx.ref(), sz, _args), ctx)
def Abs(arg):
"""Create the absolute value of an arithmetic expression"""
return If(arg > 0, arg, -arg)
def AtMost(*args):
"""Create an at-most Pseudo-Boolean k constraint.

View file

@ -1516,7 +1516,7 @@ extern "C" {
def_API('Z3_global_param_get', BOOL, (_in(STRING), _out(STRING)))
*/
Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value);
Z3_bool Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value);
/**@}*/
@ -4325,7 +4325,7 @@ extern "C" {
def_API('Z3_get_finite_domain_sort_size', BOOL, (_in(CONTEXT), _in(SORT), _out(UINT64)))
*/
Z3_bool_opt Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, uint64_t* r);
Z3_bool Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, uint64_t* r);
/**
\brief Return the domain of the given array sort.
@ -5264,7 +5264,7 @@ extern "C" {
def_API('Z3_model_eval', BOOL, (_in(CONTEXT), _in(MODEL), _in(AST), _in(BOOL), _out(AST)))
*/
Z3_bool_opt Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, bool model_completion, Z3_ast * v);
Z3_bool Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, bool model_completion, Z3_ast * v);
/**
\brief Return the interpretation (i.e., assignment) of constant \c a in the model \c m.

View file

@ -4,9 +4,6 @@ Copyright (c) 2015 Microsoft Corporation
--*/
#ifndef Z3_bool_opt
#define Z3_bool_opt Z3_bool
#endif
#ifndef Z3_API
# ifdef __GNUC__

View file

@ -273,6 +273,17 @@ public:
bool is_rem0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_REM0); }
bool is_mod0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_MOD0); }
bool is_power0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER0); }
bool is_power(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER); }
bool is_add(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_ADD); }
bool is_mul(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_MUL); }
bool is_sub(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_SUB); }
bool is_uminus(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_UMINUS); }
bool is_div(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_DIV); }
bool is_rem(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_REM); }
bool is_mod(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_MOD); }
bool is_to_real(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_TO_REAL); }
bool is_to_int(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_TO_INT); }
bool is_is_int(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_IS_INT); }
bool is_add(expr const * n) const { return is_app_of(n, arith_family_id, OP_ADD); }
bool is_sub(expr const * n) const { return is_app_of(n, arith_family_id, OP_SUB); }

View file

@ -150,6 +150,10 @@ public:
bool is_const(expr* n) const { return is_app_of(n, m_fid, OP_CONST_ARRAY); }
bool is_ext(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_EXT); }
bool is_map(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_MAP); }
bool is_union(expr* n) const { return is_app_of(n, m_fid, OP_SET_UNION); }
bool is_intersect(expr* n) const { return is_app_of(n, m_fid, OP_SET_INTERSECT); }
bool is_difference(expr* n) const { return is_app_of(n, m_fid, OP_SET_DIFFERENCE); }
bool is_complement(expr* n) const { return is_app_of(n, m_fid, OP_SET_COMPLEMENT); }
bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); }
bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); }
bool is_set_has_size(expr* e) const { return is_app_of(e, m_fid, OP_SET_HAS_SIZE); }
@ -158,11 +162,14 @@ public:
bool is_store(func_decl* f) const { return is_decl_of(f, m_fid, OP_STORE); }
bool is_const(func_decl* f) const { return is_decl_of(f, m_fid, OP_CONST_ARRAY); }
bool is_map(func_decl* f) const { return is_decl_of(f, m_fid, OP_ARRAY_MAP); }
bool is_union(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_UNION); }
bool is_intersect(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_INTERSECT); }
bool is_as_array(func_decl* f) const { return is_decl_of(f, m_fid, OP_AS_ARRAY); }
bool is_set_has_size(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_HAS_SIZE); }
bool is_set_card(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_CARD); }
bool is_default(func_decl* f) const { return is_decl_of(f, m_fid, OP_ARRAY_DEFAULT); }
bool is_default(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_DEFAULT); }
bool is_subset(expr const* n) const { return is_app_of(n, m_fid, OP_SET_SUBSET); }
bool is_as_array(func_decl* f, func_decl*& g) const { return is_decl_of(f, m_fid, OP_AS_ARRAY) && (g = get_as_array_func_decl(f), true); }
func_decl * get_as_array_func_decl(expr * n) const;
func_decl * get_as_array_func_decl(func_decl* f) const;
@ -172,6 +179,8 @@ public:
bool is_const(expr* e, expr*& v) const;
bool is_store_ext(expr* e, expr_ref& a, expr_ref_vector& args, expr_ref& value);
MATCH_BINARY(is_subset);
};
class array_util : public array_recognizers {

View file

@ -264,8 +264,12 @@ void flatten_and(expr_ref_vector& result) {
expr_fast_mark1 seen;
for (unsigned i = 0; i < result.size(); ++i) {
expr* e = result.get(i);
if (seen.is_marked(e))
if (seen.is_marked(e)) {
result[i] = result.back();
result.pop_back();
--i;
continue;
}
seen.mark(e);
pin.push_back(e);
if (m.is_and(e)) {
@ -330,8 +334,12 @@ void flatten_or(expr_ref_vector& result) {
expr_fast_mark1 seen;
for (unsigned i = 0; i < result.size(); ++i) {
expr* e = result.get(i);
if (seen.is_marked(e))
if (seen.is_marked(e)) {
result[i] = result.back();
result.pop_back();
--i;
continue;
}
seen.mark(e);
pin.push_back(e);
if (m.is_or(e)) {

View file

@ -25,6 +25,8 @@ namespace euf {
enode* egraph::mk_enode(expr* f, unsigned generation, unsigned num_args, enode * const* args) {
enode* n = enode::mk(m_region, f, generation, num_args, args);
if (m_default_relevant)
n->set_relevant(true);
m_nodes.push_back(n);
m_exprs.push_back(f);
if (is_app(f) && num_args > 0) {
@ -187,10 +189,10 @@ namespace euf {
add_th_diseq(id, v1, v2, n->get_expr());
return;
}
for (auto p : euf::enode_th_vars(r1)) {
for (auto const& p : euf::enode_th_vars(r1)) {
if (!th_propagates_diseqs(p.get_id()))
continue;
for (auto q : euf::enode_th_vars(r2))
for (auto const& q : euf::enode_th_vars(r2))
if (p.get_id() == q.get_id())
add_th_diseq(p.get_id(), p.get_var(), q.get_var(), n->get_expr());
}
@ -269,6 +271,13 @@ namespace euf {
}
}
void egraph::set_relevant(enode* n) {
if (n->is_relevant())
return;
n->set_relevant(true);
m_updates.push_back(update_record(n, update_record::set_relevant()));
}
void egraph::toggle_merge_enabled(enode* n, bool backtracking) {
bool enable_merge = !n->merge_enabled();
n->set_merge_enabled(enable_merge);
@ -380,6 +389,10 @@ namespace euf {
case update_record::tag_t::is_lbl_set:
p.r1->m_lbls.set(p.m_lbls);
break;
case update_record::tag_t::is_set_relevant:
SASSERT(p.r1->is_relevant());
p.r1->set_relevant(false);
break;
case update_record::tag_t::is_update_children:
for (unsigned i = 0; i < p.r1->num_args(); ++i) {
SASSERT(p.r1->m_args[i]->get_root()->m_parents.back() == p.r1);
@ -446,8 +459,8 @@ namespace euf {
r2->inc_class_size(r1->class_size());
merge_th_eq(r1, r2);
reinsert_parents(r1, r2);
if (m_on_merge)
m_on_merge(r2, r1);
for (auto& cb : m_on_merge)
cb(r2, r1);
}
void egraph::remove_parents(enode* r1, enode* r2) {
@ -493,7 +506,7 @@ namespace euf {
void egraph::merge_th_eq(enode* n, enode* root) {
SASSERT(n != root);
for (auto iv : enode_th_vars(n)) {
for (auto const& iv : enode_th_vars(n)) {
theory_id id = iv.get_id();
theory_var v = root->get_th_var(id);
if (v == null_theory_var) {
@ -754,6 +767,8 @@ namespace euf {
}
std::ostream& egraph::display(std::ostream& out, unsigned max_args, enode* n) const {
if (!n->is_relevant())
out << "n";
out << "#" << n->get_expr_id() << " := ";
expr* f = n->get_expr();
if (is_app(f))
@ -770,11 +785,18 @@ namespace euf {
out << " " << p->get_expr_id();
out << "] ";
}
if (n->value() != l_undef)
out << "[b" << n->bool_var() << " := " << (n->value() == l_true ? "T":"F") << (n->merge_tf()?"":" no merge") << "] ";
auto value_of = [&]() {
switch (n->value()) {
case l_true: return "T";
case l_false: return "F";
default: return "?";
}
};
if (n->bool_var() != sat::null_bool_var)
out << "[b" << n->bool_var() << " := " << value_of() << (n->merge_tf() ? "" : " no merge") << "] ";
if (n->has_th_vars()) {
out << "[t";
for (auto v : enode_th_vars(n))
for (auto const& v : enode_th_vars(n))
out << " " << v.get_id() << ":" << v.get_var();
out << "] ";
}

View file

@ -32,6 +32,7 @@ Notes:
#include "ast/euf/euf_enode.h"
#include "ast/euf/euf_etable.h"
#include "ast/ast_ll_pp.h"
#include <vector>
namespace euf {
@ -105,10 +106,11 @@ namespace euf {
struct lbl_hash {};
struct lbl_set {};
struct update_children {};
struct set_relevant {};
enum class tag_t { is_set_parent, is_add_node, is_toggle_merge, is_update_children,
is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq,
is_lbl_hash, is_new_th_eq_qhead, is_new_lits_qhead,
is_inconsistent, is_value_assignment, is_lbl_set };
is_inconsistent, is_value_assignment, is_lbl_set, is_set_relevant };
tag_t tag;
enode* r1;
enode* n1;
@ -151,6 +153,8 @@ namespace euf {
tag(tag_t::is_lbl_set), r1(n), n1(nullptr), m_lbls(n->m_lbls.get()) {}
update_record(enode* n, update_children) :
tag(tag_t::is_update_children), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
update_record(enode* n, set_relevant) :
tag(tag_t::is_set_relevant), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
};
ast_manager& m;
svector<to_merge> m_to_merge;
@ -181,7 +185,8 @@ namespace euf {
enode_vector m_todo;
stats m_stats;
bool m_uses_congruence = false;
std::function<void(enode*,enode*)> m_on_merge;
bool m_default_relevant = true;
std::vector<std::function<void(enode*,enode*)>> m_on_merge;
std::function<void(enode*)> m_on_make;
std::function<void(expr*,expr*,expr*)> m_used_eq;
std::function<void(app*,app*)> m_used_cc;
@ -292,8 +297,10 @@ namespace euf {
void set_merge_enabled(enode* n, bool enable_merge);
void set_value(enode* n, lbool value);
void set_bool_var(enode* n, unsigned v) { n->set_bool_var(v); }
void set_relevant(enode* n);
void set_default_relevant(bool b) { m_default_relevant = b; }
void set_on_merge(std::function<void(enode* root,enode* other)>& on_merge) { m_on_merge = on_merge; }
void set_on_merge(std::function<void(enode* root,enode* other)>& on_merge) { m_on_merge.push_back(on_merge); }
void set_on_make(std::function<void(enode* n)>& on_make) { m_on_make = on_make; }
void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; }
void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; }

View file

@ -49,6 +49,7 @@ namespace euf {
bool m_interpreted = false;
bool m_merge_enabled = true;
bool m_is_equality = false; // Does the expression represent an equality
bool m_is_relevant = false;
lbool m_value = l_undef; // Assignment by SAT solver for Boolean node
sat::bool_var m_bool_var = sat::null_bool_var; // SAT solver variable associated with Boolean node
unsigned m_class_size = 1; // Size of the equivalence class if the enode is the root.
@ -145,6 +146,8 @@ namespace euf {
unsigned num_parents() const { return m_parents.size(); }
bool interpreted() const { return m_interpreted; }
bool is_equality() const { return m_is_equality; }
bool is_relevant() const { return m_is_relevant; }
void set_relevant(bool b) { m_is_relevant = b; }
lbool value() const { return m_value; }
bool value_conflict() const { return value() != l_undef && get_root()->value() != l_undef && value() != get_root()->value(); }
sat::bool_var bool_var() const { return m_bool_var; }

View file

@ -722,12 +722,12 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
return BR_DONE;
}
if (m().is_not(rhs))
if (m().is_not(rhs))
std::swap(lhs, rhs);
if (m().is_not(lhs, lhs)) {
result = m().mk_not(m().mk_eq(lhs, rhs));
return BR_REWRITE2;
if (m().is_not(lhs, lhs)) {
result = m().mk_not(m().mk_eq(lhs, rhs));
return BR_REWRITE2;
}
if (unfolded) {

View file

@ -21,8 +21,11 @@ Revision History:
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/recfun_decl_plugin.h"
#include "ast/rewriter/recfun_replace.h"
#include "ast/rewriter/seq_axioms.h"
namespace seq {
axioms::axioms(th_rewriter& r):
@ -1052,6 +1055,79 @@ namespace seq {
NOT_IMPLEMENTED_YET();
}
// A basic strategy for supporting replace_all and other
// similar functions is to use recursive relations.
// Then the features for recursive functions that allow expanding definitions
// using iterative deepening can be re-used.
//
// create recursive relation 'ra' with properties:
// ra(i, j, s, p, t, r) <- len(s) = i && len(r) = j
// ra(i, j, s, p, t, r) <- len(s) > i = 0 && p = "" && r = t + s
// ra(i, j, s, p, t, r) <- len(s) > i && p != "" && s = extract(s, 0, i) + p + extract(s, i + len(p), len(s)) && r = extract(r, 0, i) + t + extract(r, i + len(p), len(r)) && ra(i + len(p), j + len(t), s, p, t, r)
// ra(i, s, p, t, r) <- ~prefix(p, extract(s, i, len(s)) && at(s,i) = at(r,j) && ra(i + 1, j + 1, s, p, t, r)
// which amounts to:
//
//
// Then assert
// ra(s, p, t, replace_all(s, p, t))
//
void axioms::replace_all_axiom(expr* r) {
expr* s = nullptr, *p = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_all(r, s, p, t));
recfun::util rec(m);
recfun::decl::plugin& plugin = rec.get_plugin();
recfun_replace replace(m);
sort* srt = s->get_sort();
sort* domain[4] = { srt, srt, srt, srt };
auto d = plugin.ensure_def(symbol("ra"), 4, domain, m.mk_bool_sort(), true);
func_decl* ra = d.get_def()->get_decl();
sort* isrt = a.mk_int();
var_ref vi(m.mk_var(5, isrt), m);
var_ref vj(m.mk_var(4, isrt), m);
var_ref vs(m.mk_var(3, srt), m);
var_ref vp(m.mk_var(2, srt), m);
var_ref vt(m.mk_var(1, srt), m);
var_ref vr(m.mk_var(0, srt), m);
var* vars[6] = { vi, vj, vs, vp, vt, vr };
expr_ref len_s(seq.str.mk_length(vs), m);
expr_ref len_r(seq.str.mk_length(vr), m);
expr_ref test1(m.mk_eq(len_s, vi), m);
expr_ref branch1(m.mk_eq(len_r, vj), m);
expr_ref test2(m.mk_and(a.mk_gt(len_s, vi), m.mk_eq(vi, a.mk_int(0)), seq.str.mk_is_empty(vp)), m);
expr_ref branch2(m.mk_eq(vr, seq.str.mk_concat(vt, vs)), m);
NOT_IMPLEMENTED_YET();
#if 0
expr_ref test3(, m);
expr_ref s1(m_sk.mk_prefix_inv(vp, vs), m);
expr_ref r1(m_sk.mk_prefix_inv(vp, vr), m);
expr* args1[4] = { s1, vp, vt, r1 };
expr_ref branch3(m.mk_and(m.mk_eq(seq.str.mk_concat(vp, s1), vs),
m.mk_eq(seq.str.mk_concat(vr, r1), vr),
m.mk_app(ra, 4, args1)
), m);
expr_ref s0(m), r0(m);
m_sk.decompose(vs, s0, s1);
m_sk.decompose(vr, r0, r1);
expr* args2[4] = { s1, vp, vt, r1 };
expr_ref branch4(m.mk_and(m.mk_eq(vs, seq.str.mk_concat(s0, s1)),
m.mk_eq(vr, seq.str.mk_concat(s0, r1)),
m.mk_app(ra, 4, args2)), m);
// s = [s0] + s' && r = [s0] + r' && ra(s', p, t, r')
expr_ref body(m.mk_ite(test1, branch1, m.mk_ite(test2, branch2, m.mk_ite(test3, branch3, branch4))), m);
plugin.set_definition(replace, d, true, 4, vars, body);
expr* args3[4] = { s, p, t, r };
expr_ref lit(m.mk_app(ra, 4, args3), m);
add_clause(lit);
#endif
}
void axioms::replace_re_all_axiom(expr* e) {
expr* s = nullptr, *p = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_re_all(e, s, p, t));
NOT_IMPLEMENTED_YET();
}
/**

View file

@ -107,6 +107,8 @@ namespace seq {
void length_axiom(expr* n);
void unroll_not_contains(expr* e);
void replace_re_axiom(expr* e);
void replace_all_axiom(expr* e);
void replace_re_all_axiom(expr* e);
expr_ref length_limit(expr* s, unsigned k);
expr_ref is_digit(expr* ch);

View file

@ -3112,7 +3112,7 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref
else
// D(e,r1)r2|(ite (r1nullable) (D(e,r2)) [])
// observe that (mk_ite_simplify(true, D(e,r2), []) = D(e,r2)
result = mk_regex_union_normalize(c1, re().mk_ite_simplify(r1nullable, mk_antimirov_deriv(e, r2, path), nothing()));
result = mk_antimirov_deriv_union(c1, re().mk_ite_simplify(r1nullable, mk_antimirov_deriv(e, r2, path), nothing()));
}
else if (m().is_ite(r, c, r1, r2)) {
c1 = simplify_path(e, m().mk_and(c, path));
@ -3171,7 +3171,7 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref
result = re().mk_ite_simplify(range, epsilon(), nothing());
}
else if (re().is_union(r, r1, r2))
result = mk_regex_union_normalize(mk_antimirov_deriv(e, r1, path), mk_antimirov_deriv(e, r2, path));
result = mk_antimirov_deriv_union(mk_antimirov_deriv(e, r1, path), mk_antimirov_deriv(e, r2, path));
else if (re().is_intersection(r, r1, r2))
result = mk_antimirov_deriv_intersection(e,
mk_antimirov_deriv(e, r1, path),
@ -3237,11 +3237,11 @@ expr_ref seq_rewriter::mk_antimirov_deriv_intersection(expr* e, expr* d1, expr*
result = mk_antimirov_deriv_restrict(e, d2, path);
else if (re().is_union(d1, a, b))
// distribute intersection over the union in d1
result = mk_regex_union_normalize(mk_antimirov_deriv_intersection(e, a, d2, path),
result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(e, a, d2, path),
mk_antimirov_deriv_intersection(e, b, d2, path));
else if (re().is_union(d2, a, b))
// distribute intersection over the union in d2
result = mk_regex_union_normalize(mk_antimirov_deriv_intersection(e, d1, a, path),
result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(e, d1, a, path),
mk_antimirov_deriv_intersection(e, d1, b, path));
else
result = mk_regex_inter_normalize(d1, d2);
@ -3256,7 +3256,7 @@ expr_ref seq_rewriter::mk_antimirov_deriv_concat(expr* d, expr* r) {
if (m().is_ite(d, c, t, e))
result = m().mk_ite(c, mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
else if (re().is_union(d, t, e))
result = mk_regex_union_normalize(mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
result = mk_antimirov_deriv_union(mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
else
result = mk_re_append(d, r);
return result;
@ -3284,7 +3284,7 @@ expr_ref seq_rewriter::mk_antimirov_deriv_negate(expr* elem, expr* d) {
else if (re().is_union(d, t, e))
result = mk_antimirov_deriv_intersection(elem, mk_antimirov_deriv_negate(elem, t), mk_antimirov_deriv_negate(elem, e), m().mk_true());
else if (re().is_intersection(d, t, e))
result = mk_regex_union_normalize(mk_antimirov_deriv_negate(elem, t), mk_antimirov_deriv_negate(elem, e));
result = mk_antimirov_deriv_union(mk_antimirov_deriv_negate(elem, t), mk_antimirov_deriv_negate(elem, e));
else if (re().is_complement(d, t))
result = t;
else
@ -3292,6 +3292,20 @@ expr_ref seq_rewriter::mk_antimirov_deriv_negate(expr* elem, expr* d) {
return result;
}
expr_ref seq_rewriter::mk_antimirov_deriv_union(expr* d1, expr* d2) {
sort* seq_sort = nullptr, * ele_sort = nullptr;
VERIFY(m_util.is_re(d1, seq_sort));
VERIFY(m_util.is_seq(seq_sort, ele_sort));
expr_ref result(m());
expr* c1, * t1, * e1, * c2, * t2, * e2;
if (m().is_ite(d1, c1, t1, e1) && m().is_ite(d2, c2, t2, e2) && c1 == c2)
// eliminate duplicate branching on exactly the same condition
result = m().mk_ite(c1, mk_antimirov_deriv_union(t1, t2), mk_antimirov_deriv_union(e1, e2));
else
result = mk_regex_union_normalize(d1, d2);
return result;
}
// restrict the guards of all conditionals id d and simplify the resulting derivative
// restrict(if(c, a, b), cond) = if(c, restrict(a, cond & c), restrict(b, cond & ~c))
// restrict(a U b, cond) = restrict(a, cond) U restrict(b, cond)
@ -3320,7 +3334,7 @@ expr_ref seq_rewriter::mk_antimirov_deriv_restrict(expr* e, expr* d, expr* cond)
else if (re().is_union(d, a, b)) {
expr_ref a1(mk_antimirov_deriv_restrict(e, a, cond), m());
expr_ref b1(mk_antimirov_deriv_restrict(e, b, cond), m());
result = mk_regex_union_normalize(a1, b1);
result = mk_antimirov_deriv_union(a1, b1);
}
return result;
}
@ -3331,7 +3345,7 @@ expr_ref seq_rewriter::mk_regex_union_normalize(expr* r1, expr* r2) {
SASSERT(m_util.is_re(r2));
expr_ref result(m());
std::function<bool(expr*, expr*&, expr*&)> test = [&](expr* t, expr*& a, expr*& b) { return re().is_union(t, a, b); };
std::function<expr* (expr*, expr*)> compose = [&](expr* r1, expr* r2) { return re().mk_union(r1, r2); };
std::function<expr* (expr*, expr*)> compose = [&](expr* r1, expr* r2) { return (is_subset(r1, r2) ? r2 : (is_subset(r2, r1) ? r1 : re().mk_union(r1, r2))); };
if (r1 == r2 || re().is_empty(r2) || re().is_full_seq(r1))
result = r1;
else if (re().is_empty(r1) || re().is_full_seq(r2))
@ -3353,7 +3367,7 @@ expr_ref seq_rewriter::mk_regex_inter_normalize(expr* r1, expr* r2) {
if (re().is_epsilon(r2))
std::swap(r1, r2);
std::function<bool(expr*, expr*&, expr*&)> test = [&](expr* t, expr*& a, expr*& b) { return re().is_intersection(t, a, b); };
std::function<expr* (expr*, expr*)> compose = [&](expr* r1, expr* r2) { return re().mk_inter(r1, r2); };
std::function<expr* (expr*, expr*)> compose = [&](expr* r1, expr* r2) { return (is_subset(r1, r2) ? r1 : (is_subset(r2, r1) ? r2 : re().mk_inter(r1, r2))); };
if (r1 == r2 || re().is_empty(r1) || re().is_full_seq(r2))
result = r1;
else if (re().is_empty(r2) || re().is_full_seq(r1))
@ -4586,6 +4600,7 @@ bool seq_rewriter::is_subset(expr* r1, expr* r2) const {
// return false;
expr* ra1 = nullptr, *ra2 = nullptr, *ra3 = nullptr;
expr* rb1 = nullptr, *rb2 = nullptr, *rb3 = nullptr;
unsigned la, ua, lb, ub;
if (re().is_complement(r1, ra1) &&
re().is_complement(r2, rb1)) {
return is_subset(rb1, ra1);
@ -4598,6 +4613,8 @@ bool seq_rewriter::is_subset(expr* r1, expr* r2) const {
return true;
if (re().is_full_seq(r2))
return true;
if (re().is_dot_plus(r2) && re().get_info(r1).nullable == l_false)
return true;
if (is_concat(r1, ra1, ra2, ra3) &&
is_concat(r2, rb1, rb2, rb3) && ra1 == rb1 && ra2 == rb2) {
r1 = ra3;
@ -4609,6 +4626,20 @@ bool seq_rewriter::is_subset(expr* r1, expr* r2) const {
r1 = ra2;
continue;
}
// r1=ra3{la,ua}ra2, r2=rb3{lb,ub}rb2, ra3=rb3, lb<=la, ua<=ub
if (re().is_concat(r1, ra1, ra2) && re().is_loop(ra1, ra3, la, ua) &&
re().is_concat(r2, rb1, rb2) && re().is_loop(rb1, rb3, lb, ub) &&
ra3 == rb3 && lb <= la && ua <= ub) {
r1 = ra2;
r2 = rb2;
continue;
}
// ra1=ra3{la,ua}, r2=rb3{lb,ub}, ra3=rb3, lb<=la, ua<=ub
if (re().is_loop(r1, ra3, la, ua) &&
re().is_loop(r2, rb3, lb, ub) &&
ra3 == rb3 && lb <= la && ua <= ub) {
return true;
}
return false;
}
}

View file

@ -217,6 +217,7 @@ class seq_rewriter {
expr_ref mk_antimirov_deriv_intersection(expr* elem, expr* d1, expr* d2, expr* path);
expr_ref mk_antimirov_deriv_concat(expr* d, expr* r);
expr_ref mk_antimirov_deriv_negate(expr* elem, expr* d);
expr_ref mk_antimirov_deriv_union(expr* d1, expr* d2);
expr_ref mk_antimirov_deriv_restrict(expr* elem, expr* d1, expr* cond);
expr_ref mk_regex_reverse(expr* r);
expr_ref mk_regex_concat(expr* r1, expr* r2);

View file

@ -627,7 +627,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
count_down_subterm_references(result, reference_map);
// Any term that was newly introduced by the rewrite step is only referenced within / reachable from the result term.
for (auto kv : reference_map) {
for (auto const& kv : reference_map) {
if (kv.m_value == 0) {
m().trace_stream() << "[attach-enode] #" << kv.m_key->get_id() << " 0\n";
}
@ -691,7 +691,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
return;
if (m_new_subst) {
m_rep.reset();
for (auto kv : m_subst->sub())
for (auto const& kv : m_subst->sub())
m_rep.insert(kv.m_key, kv.m_value);
m_new_subst = false;
}
@ -859,8 +859,8 @@ ast_manager & th_rewriter::m() const {
}
void th_rewriter::updt_params(params_ref const & p) {
m_params = p;
m_imp->cfg().updt_params(p);
m_params.append(p);
m_imp->cfg().updt_params(m_params);
}
void th_rewriter::get_param_descrs(param_descrs & r) {

View file

@ -301,10 +301,23 @@ UNARY_CMD(pp_cmd, "display", "<term>", "display the given term.", CPK_EXPR, expr
ctx.regular_stream() << std::endl;
});
UNARY_CMD(echo_cmd, "echo", "<string>", "display the given string", CPK_STRING, char const *,
bool smt2c = ctx.params().m_smtlib2_compliant;
ctx.regular_stream() << (smt2c ? "\"" : "") << arg << (smt2c ? "\"" : "") << std::endl;);
static std::string escape_string(char const* arg) {
std::string result;
while (*arg) {
auto ch = *arg++;
if (ch == '"')
result.push_back(ch);
result.push_back(ch);
}
return result;
}
UNARY_CMD(echo_cmd, "echo", "<string>", "display the given string", CPK_STRING, char const *,
bool smt2c = ctx.params().m_smtlib2_compliant;
if (smt2c)
ctx.regular_stream() << "\"" << escape_string(arg) << "\"" << std::endl;
else
ctx.regular_stream() << arg << std::endl;);
class set_get_option_cmd : public cmd {
protected:

View file

@ -2107,6 +2107,21 @@ void cmd_context::analyze_failure(expr_mark& seen, model_evaluator& ev, expr* a,
<< (expected_value?"true":"false") << "\n";);
IF_VERBOSE(11, display_detailed_analysis(verbose_stream(), ev, a));
if (m().is_iff(a)) {
ptr_vector<expr> todo;
todo.push_back(a);
for (unsigned i = 0; i < todo.size(); ++i) {
e = todo[i];
if (m().is_and(e) || m().is_or(e) || m().is_iff(e) || m().is_implies(e) || m().is_not(e))
for (expr* arg : *to_app(e))
todo.push_back(arg);
else
IF_VERBOSE(10, verbose_stream() << "#" << e->get_id() << " " << mk_bounded_pp(e, m()) << " " << (ev.is_true(e)?"true":"false") << "\n");
}
return;
}
}
void cmd_context::display_detailed_analysis(std::ostream& out, model_evaluator& ev, expr* e) {

View file

@ -199,12 +199,12 @@ void tst_params(cmd_context & ctx) {
params_ref p1;
params_ref p2;
p1.set_uint("val", 100);
p2 = p1;
p2.append(p1);
SASSERT(p2.get_uint("val", 0) == 100);
p2.set_uint("val", 200);
SASSERT(p2.get_uint("val", 0) == 200);
SASSERT(p1.get_uint("val", 0) == 100);
p2 = p1;
p2.append(p1);
SASSERT(p2.get_uint("val", 0) == 100);
SASSERT(p1.get_uint("val", 0) == 100);
ctx.regular_stream() << "worked" << std::endl;

View file

@ -227,8 +227,8 @@ public:
}
void updt_params(params_ref const & p) override {
m_params = p;
m_imp->updt_params(p);
m_params.append(p);
m_imp->updt_params(m_params);
}
void collect_param_descrs(param_descrs & r) override {

View file

@ -111,6 +111,7 @@ void model_core::unregister_decl(func_decl * d) {
m_interp[m_const_decls.back()].first = v.first;
m_const_decls.pop_back();
m_interp.remove(d);
m_decls.erase(d);
m.dec_ref(k);
m.dec_ref(v.second);
return;
@ -122,6 +123,7 @@ void model_core::unregister_decl(func_decl * d) {
auto v = ef->get_data().m_value;
m_finterp.remove(d);
m_func_decls.erase(d);
m_decls.erase(d);
m.dec_ref(k);
dealloc(v);
}

View file

@ -395,8 +395,8 @@ public:
char const* name() const override { return "horn"; }
void updt_params(params_ref const & p) override {
m_params = p;
m_imp->updt_params(p);
m_params.append(p);
m_imp->updt_params(m_params);
}

View file

@ -55,8 +55,8 @@ class nlsat_tactic : public tactic {
}
void updt_params(params_ref const & p) {
m_params = p;
m_solver.updt_params(p);
m_params.append(p);
m_solver.updt_params(m_params);
}
bool contains_unsupported(expr_ref_vector & b2a, expr_ref_vector & x2t) {
@ -226,7 +226,7 @@ public:
char const* name() const override { return "nlsat"; }
void updt_params(params_ref const & p) override {
m_params = p;
m_params.append(p);
}
void collect_param_descrs(param_descrs & r) override {

View file

@ -259,6 +259,12 @@ namespace opt {
if (!m_models[i])
m_models.set(i, m_last_model.get());
if (val > m_objective_values[i])
m_objective_values[i] = val;
if (!m_last_model)
return true;
//
// retrieve value of objective from current model and update
// current optimal.
@ -267,7 +273,7 @@ namespace opt {
rational r;
expr_ref value = (*m_last_model)(m_objective_terms.get(i));
if (arith_util(m).is_numeral(value, r) && r > m_objective_values[i])
m_objective_values[i] = inf_eps(r);
m_objective_values[i] = inf_eps(r);
};
update_objective();

View file

@ -49,14 +49,14 @@ namespace opt {
dst[i] = src[i];
m_models.set(i, m_s->get_model_idx(i));
m_s->get_labels(m_labels);
m_lower_fmls[i] = fmls[i].get();
m_lower_fmls[i] = fmls.get(i);
if (dst[i].is_pos() && !dst[i].is_finite()) { // review: likely done already.
m_lower_fmls[i] = m.mk_false();
fmls[i] = m.mk_false();
}
}
else if (src[i] < dst[i] && !m.is_true(m_lower_fmls[i].get())) {
fmls[i] = m_lower_fmls[i].get();
else if (src[i] < dst[i] && !m.is_true(m_lower_fmls.get(i))) {
fmls[i] = m_lower_fmls.get(i);
}
}
}
@ -202,6 +202,9 @@ namespace opt {
for (unsigned i = 0; i < obj_index; ++i)
commit_assignment(i);
// m_s->maximize_objective(obj_index, bound);
// m_s->assert_expr(bound);
unsigned steps = 0;
unsigned step_incs = 0;
rational delta_per_step(1);
@ -282,7 +285,7 @@ namespace opt {
bool optsmt::can_increment_delta(vector<inf_eps> const& lower, unsigned i) {
arith_util arith(m);
inf_eps max_delta;
if (m_lower[i] < m_upper[i] && arith.is_int(m_objs[i].get())) {
if (m_lower[i] < m_upper[i] && arith.is_int(m_objs.get(i))) {
inf_eps delta = m_lower[i] - lower[i];
if (m_lower[i].is_finite() && delta > max_delta) {
return true;
@ -435,7 +438,7 @@ namespace opt {
bool progress = false;
for (unsigned i = 0; i < m_lower.size() && m.inc(); ++i) {
if (m_lower[i] <= mid[i] && mid[i] <= m_upper[i] && m_lower[i] < m_upper[i]) {
th.enable_record_conflict(bounds[i].get());
th.enable_record_conflict(bounds.get(i));
lbool is_sat = m_s->check_sat(1, bounds.data() + i);
switch(is_sat) {
case l_true:
@ -484,10 +487,10 @@ namespace opt {
}
for (unsigned i = 0; i < m_objs.size(); ++i) {
smt::theory_var v = solver.add_objective(m_objs[i].get());
smt::theory_var v = solver.add_objective(m_objs.get(i));
if (v == smt::null_theory_var) {
std::ostringstream out;
out << "Objective function '" << mk_pp(m_objs[i].get(), m) << "' is not supported";
out << "Objective function '" << mk_pp(m_objs.get(i), m) << "' is not supported";
throw default_exception(out.str());
}
m_vars.push_back(v);
@ -547,7 +550,7 @@ namespace opt {
// force lower_bound(i) <= objective_value(i)
void optsmt::commit_assignment(unsigned i) {
inf_eps lo = m_lower[i];
TRACE("opt", tout << "set lower bound of " << mk_pp(m_objs[i].get(), m) << " to: " << lo << "\n";
TRACE("opt", tout << "set lower bound of " << mk_pp(m_objs.get(i), m) << " to: " << lo << "\n";
tout << get_lower(i) << ":" << get_upper(i) << "\n";);
// Only assert bounds for bounded objectives
if (lo.is_finite()) {

View file

@ -190,7 +190,8 @@ void context_params::get_solver_params(params_ref & p, bool & proofs_enabled, bo
proofs_enabled &= p.get_bool("proof", m_proof);
models_enabled &= p.get_bool("model", m_model);
unsat_core_enabled = m_unsat_core || p.get_bool("unsat_core", false);
p = merge_default_params(p);
if (!m_auto_config && !p.contains("auto_config"))
p.set_bool("auto_config", false);
}

View file

@ -249,8 +249,8 @@ namespace mbp {
bool operator()(model& model, app* v, app_ref_vector& vars, expr_ref_vector& lits) {
app_ref_vector vs(m);
vs.push_back(v);
project(model, vs, lits, false);
return vs.empty();
vector<def> defs;
return project(model, vs, lits, defs, false) && vs.empty();
}
typedef opt::model_based_opt::var var;
@ -265,12 +265,12 @@ namespace mbp {
return t;
}
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& fmls, bool compute_def) {
bool project(model& model, app_ref_vector& vars, expr_ref_vector& fmls, vector<def>& result, bool compute_def) {
bool has_arith = false;
for (expr* v : vars)
has_arith |= is_arith(v);
if (!has_arith)
return vector<def>();
return true;
model_evaluator eval(model);
TRACE("qe", tout << model;);
eval.set_model_completion(true);
@ -294,7 +294,7 @@ namespace mbp {
}
fmls.shrink(j);
TRACE("qe", tout << "formulas\n" << fmls << "\n";
for (auto [e, id] : tids)
for (auto const& [e, id] : tids)
tout << mk_pp(e, m) << " -> " << id << "\n";);
// fmls holds residue,
@ -312,7 +312,7 @@ namespace mbp {
rational r;
expr_ref val = eval(v);
if (!m.inc())
return vector<def>();
return false;
if (!a.is_numeral(val, r))
throw default_exception("evaluation did not produce a numeral");
TRACE("qe", tout << mk_pp(v, m) << " " << val << "\n";);
@ -326,15 +326,13 @@ namespace mbp {
if (is_arith(e) && !var_mark.is_marked(e))
mark_rec(fmls_mark, e);
}
if (m_check_purified) {
for (expr* fml : fmls)
mark_rec(fmls_mark, fml);
for (auto& kv : tids) {
expr* e = kv.m_key;
if (!var_mark.is_marked(e)) {
mark_rec(fmls_mark, e);
}
if (!var_mark.is_marked(e))
mark_rec(fmls_mark, e);
}
}
@ -364,16 +362,18 @@ namespace mbp {
for (auto const& d : defs) tout << "def: " << d << "\n";
tout << fmls << "\n";);
vector<def> result;
if (compute_def)
optdefs2mbpdef(defs, index2expr, real_vars, result);
if (m_apply_projection)
apply_projection(result, fmls);
if (m_apply_projection && !apply_projection(eval, result, fmls))
return false;
TRACE("qe",
for (auto [v, t] : result)
for (auto const& [v, t] : result)
tout << v << " := " << t << "\n";
for (auto* f : fmls)
tout << mk_pp(f, m) << " := " << eval(f) << "\n";
tout << "fmls:" << fmls << "\n";);
return result;
return true;
}
void optdefs2mbpdef(vector<opt::model_based_opt::def> const& defs, ptr_vector<expr> const& index2expr, unsigned_vector const& real_vars, vector<def>& result) {
@ -548,10 +548,11 @@ namespace mbp {
}
}
void apply_projection(vector<def>& defs, expr_ref_vector& fmls) {
bool apply_projection(model_evaluator& eval, vector<def> const& defs, expr_ref_vector& fmls) {
if (fmls.empty() || defs.empty())
return;
return true;
expr_safe_replace subst(m);
expr_ref_vector fmls_tmp(m);
expr_ref tmp(m);
for (unsigned i = defs.size(); i-- > 0; ) {
auto const& d = defs[i];
@ -561,8 +562,11 @@ namespace mbp {
unsigned j = 0;
for (expr* fml : fmls) {
subst(fml, tmp);
if (m.is_false(eval(tmp)))
return false;
fmls[j++] = tmp;
}
return true;
}
};
@ -579,12 +583,13 @@ namespace mbp {
return (*m_imp)(model, var, vars, lits);
}
void arith_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
m_imp->project(model, vars, lits, false);
bool arith_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
vector<def> defs;
return m_imp->project(model, vars, lits, defs, false);
}
vector<def> arith_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
return m_imp->project(model, vars, lits, true);
bool arith_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) {
return m_imp->project(model, vars, lits, defs, true);
}
void arith_project_plugin::set_check_purified(bool check_purified) {

View file

@ -29,8 +29,8 @@ namespace mbp {
bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override;
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override { return false; }
family_id get_family_id() override;
void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) override;
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override { UNREACHABLE(); }
opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt);

View file

@ -1624,8 +1624,8 @@ namespace mbp {
);
}
vector<def> array_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
return vector<def>();
bool array_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) {
return true;
}
void array_project_plugin::saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) {

View file

@ -35,7 +35,7 @@ namespace mbp {
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
void operator()(model& model, app_ref_vector& vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects);
family_id get_family_id() override;
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) override;
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override;
};

View file

@ -300,8 +300,8 @@ namespace mbp {
return m_imp->solve(model, vars, lits);
}
vector<def> datatype_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
return vector<def>();
bool datatype_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) {
return true;
}
void datatype_project_plugin::saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) {

View file

@ -34,7 +34,7 @@ namespace mbp {
bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override;
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
family_id get_family_id() override;
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) override;
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override;
};

View file

@ -67,7 +67,7 @@ namespace mbp {
virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; }
virtual family_id get_family_id() { return null_family_id; }
virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { };
virtual bool operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; };
/**
\brief project vars modulo model, return set of definitions for eliminated variables.
@ -76,7 +76,7 @@ namespace mbp {
- returns set of definitions
(TBD: in triangular form, the last definition can be substituted into definitions that come before)
*/
virtual vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return vector<def>(); }
virtual bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) { return true; }
/**
\brief model based saturation. Saturates theory axioms to equi-satisfiable literals over EUF,

View file

@ -2466,7 +2466,7 @@ public:
}
void updt_params(params_ref const & p) override {
m_params = p;
m_params.append(p);
// m_imp->updt_params(p);
}

View file

@ -266,7 +266,10 @@ namespace qe {
vector<mbp::def> uflia_mbi::arith_project(model_ref& mdl, app_ref_vector& avars, expr_ref_vector& lits) {
mbp::arith_project_plugin ap(m);
ap.set_check_purified(false);
return ap.project(*mdl.get(), avars, lits);
vector<mbp::def> defs;
bool ok = ap.project(*mdl.get(), avars, lits, defs);
CTRACE("qe", !ok, tout << "projection failure ignored!!!!\n");
return defs;
}
mbi_result uflia_mbi::operator()(expr_ref_vector& lits, model_ref& mdl) {

View file

@ -103,8 +103,8 @@ public:
char const* name() const override { return "qe"; }
void updt_params(params_ref const & p) override {
m_params = p;
m_imp->updt_params(p);
m_params.append(p);
m_imp->updt_params(m_params);
}

View file

@ -288,6 +288,7 @@ namespace sat {
m_free_vars.pop_back();
m_active_vars.push_back(v);
reset_var(v, ext, dvar);
SASSERT(v < m_justification.size());
return v;
}
m_active_vars.push_back(v);
@ -3008,7 +3009,7 @@ namespace sat {
svector<double> logits(vars.size(), 0.0);
double itau = m_config.m_reorder_itau;
double lse = 0;
double mid = m_rand.max_value()/2;
double mid = (double)(m_rand.max_value()/2);
double max = 0;
for (double& f : logits) {
f = itau * (m_rand() - mid)/mid;
@ -3508,7 +3509,6 @@ namespace sat {
unsigned old_num_vars = m_vars_lim.pop(num_scopes);
if (old_num_vars == m_active_vars.size())
return;
unsigned free_vars_head = m_free_vars.size();
unsigned sz = m_active_vars.size(), j = old_num_vars;
unsigned new_lvl = m_scopes.size() - num_scopes;
@ -3538,9 +3538,7 @@ namespace sat {
}
else {
set_eliminated(v, true);
if (!is_external(v) || true) {
m_free_vars.push_back(v);
}
m_vars_to_free.push_back(v);
}
}
m_active_vars.shrink(j);
@ -3550,8 +3548,7 @@ namespace sat {
IF_VERBOSE(0, verbose_stream() << "cleanup: " << lit << " " << w.is_binary_clause() << "\n");
}
};
for (unsigned i = m_free_vars.size(); i-- > free_vars_head; ) {
bool_var v = m_free_vars[i];
for (bool_var v : m_vars_to_free) {
cleanup_watch(literal(v, false));
cleanup_watch(literal(v, true));
@ -3560,7 +3557,7 @@ namespace sat {
tout << "clauses to reinit: " << (m_clauses_to_reinit.size() - old_sz) << "\n";
tout << "new level: " << new_lvl << "\n";
tout << "vars to reinit: " << m_vars_to_reinit << "\n";
tout << "free vars: " << bool_var_vector(m_free_vars.size() - free_vars_head, m_free_vars.data() + free_vars_head) << "\n";
tout << "free vars: " << bool_var_vector(m_vars_to_free) << "\n";
for (unsigned i = m_clauses_to_reinit.size(); i-- > old_sz; )
tout << "reinit: " << m_clauses_to_reinit[i] << "\n";
display(tout););
@ -3599,7 +3596,6 @@ namespace sat {
void solver::pop(unsigned num_scopes) {
if (num_scopes == 0)
return;
unsigned free_vars_head = m_free_vars.size();
if (m_ext) {
pop_vars(num_scopes);
m_ext->pop(num_scopes);
@ -3609,13 +3605,16 @@ namespace sat {
scope & s = m_scopes[new_lvl];
m_inconsistent = false; // TBD: use model seems to make this redundant: s.m_inconsistent;
unassign_vars(s.m_trail_lim, new_lvl);
for (unsigned i = m_free_vars.size(); i-- > free_vars_head; )
m_case_split_queue.del_var_eh(m_free_vars[i]);
for (bool_var v : m_vars_to_free)
m_case_split_queue.del_var_eh(v);
m_scope_lvl -= num_scopes;
reinit_clauses(s.m_clauses_to_reinit_lim);
m_scopes.shrink(new_lvl);
if (m_ext)
if (m_ext) {
m_ext->pop_reinit();
m_free_vars.append(m_vars_to_free);
m_vars_to_free.reset();
}
}
void solver::unassign_vars(unsigned old_sz, unsigned new_lvl) {
@ -3692,6 +3691,7 @@ namespace sat {
//
void solver::user_push() {
pop_to_base_level();
m_free_var_freeze.push_back(m_free_vars);
m_free_vars.reset(); // resetting free_vars forces new variables to be assigned above new_v

View file

@ -124,7 +124,7 @@ namespace sat {
clause_vector m_clauses;
clause_vector m_learned;
unsigned m_num_frozen;
unsigned_vector m_active_vars, m_free_vars, m_vars_to_reinit;
unsigned_vector m_active_vars, m_free_vars, m_vars_to_free, m_vars_to_reinit;
vector<watch_list> m_watches;
svector<lbool> m_assignment;
svector<justification> m_justification;
@ -266,6 +266,11 @@ namespace sat {
//
// -----------------------
void add_clause(unsigned num_lits, literal * lits, sat::status st) override { mk_clause(num_lits, lits, st); }
void add_clause(literal l1, literal l2, status st) {
literal lits[2] = { l1, l2 };
add_clause(2, lits, st);
}
void add_clause(literal lit, status st) { literal lits[1] = { lit }; add_clause(1, lits, st); }
bool_var add_var(bool ext) override { return mk_var(ext, true); }
bool_var mk_var(bool ext = false, bool dvar = true);
@ -444,9 +449,10 @@ namespace sat {
void flush_roots();
typedef std::pair<literal, literal> bin_clause;
struct bin_clause_hash { unsigned operator()(bin_clause const& b) const { return b.first.hash() + 2*b.second.hash(); } };
protected:
watch_list & get_wlist(literal l) { return m_watches[l.index()]; }
watch_list const & get_wlist(literal l) const { return m_watches[l.index()]; }
watch_list const& get_wlist(literal l) const { return m_watches[l.index()]; }
watch_list& get_wlist(literal l) { return m_watches[l.index()]; }
protected:
watch_list & get_wlist(unsigned l_idx) { return m_watches[l_idx]; }
bool is_marked(bool_var v) const { return m_mark[v]; }
void mark(bool_var v) { SASSERT(!is_marked(v)); m_mark[v] = true; }

View file

@ -35,10 +35,7 @@ namespace sat {
// add clauses
virtual void add_clause(unsigned n, literal* lits, status st) = 0;
void add_clause(literal l1, literal l2, status st) {
literal lits[2] = {l1, l2};
add_clause(2, lits, st);
}
void add_clause(literal l1, literal l2, literal l3, status st) {
literal lits[3] = {l1, l2, l3};
add_clause(3, lits, st);

View file

@ -38,7 +38,6 @@ z3_add_component(sat_smt
q_queue.cpp
q_solver.cpp
recfun_solver.cpp
sat_dual_solver.cpp
sat_th.cpp
user_solver.cpp
COMPONENT_DEPENDENCIES

View file

@ -545,11 +545,13 @@ namespace arith {
}
m_left_side.clear();
// reset the coefficients after they have been used.
for (unsigned i = 0; i < vars.size(); ++i) {
theory_var var = vars[i];
for (theory_var var : vars) {
rational const& r = m_columns[var];
if (!r.is_zero()) {
m_left_side.push_back(std::make_pair(r, register_theory_var_in_lar_solver(var)));
auto vi = register_theory_var_in_lar_solver(var);
if (lp::tv::is_term(vi))
vi = lp().map_term_index_to_column_index(vi);
m_left_side.push_back(std::make_pair(r, vi));
m_columns[var].reset();
}
}

View file

@ -953,6 +953,7 @@ namespace arith {
if (n1->get_root() == n2->get_root())
continue;
literal eq = eq_internalize(n1, n2);
ctx.mark_relevant(eq);
if (s().value(eq) != l_true)
return true;
}
@ -1446,21 +1447,19 @@ namespace arith {
TRACE("arith", tout << "canceled\n";);
return l_undef;
}
if (!m_nla) {
TRACE("arith", tout << "no nla\n";);
CTRACE("arith", !m_nla, tout << "no nla\n";);
if (!m_nla)
return l_true;
}
if (!m_nla->need_check())
return l_true;
m_a1 = nullptr; m_a2 = nullptr;
lbool r = m_nla->check(m_nla_lemma_vector);
switch (r) {
case l_false: {
case l_false:
for (const nla::lemma& l : m_nla_lemma_vector)
false_case_of_check_nla(l);
break;
}
case l_true:
if (assume_eqs())
return l_false;
@ -1477,11 +1476,27 @@ namespace arith {
}
bool solver::include_func_interp(func_decl* f) const {
return
a.is_div0(f) ||
a.is_idiv0(f) ||
a.is_power0(f) ||
a.is_rem0(f) ||
a.is_mod0(f);
SASSERT(f->get_family_id() == get_id());
switch (f->get_decl_kind()) {
case OP_ADD:
case OP_SUB:
case OP_UMINUS:
case OP_MUL:
case OP_LE:
case OP_LT:
case OP_GE:
case OP_GT:
case OP_MOD:
case OP_REM:
case OP_DIV:
case OP_IDIV:
case OP_POWER:
case OP_IS_INT:
case OP_TO_INT:
case OP_TO_REAL:
return false;
default:
return true;
}
}
}

View file

@ -445,6 +445,7 @@ namespace arith {
bool is_shared(theory_var v) const override;
lbool get_phase(bool_var v) override;
bool include_func_interp(func_decl* f) const override;
bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); }
// bounds and equality propagation callbacks
lp::lar_solver& lp() { return *m_solver; }

View file

@ -57,8 +57,6 @@ namespace array {
bool solver::assert_axiom(unsigned idx) {
axiom_record& r = m_axiom_trail[idx];
if (!is_relevant(r))
return false;
switch (r.m_kind) {
case axiom_record::kind_t::is_store:
return assert_store_axiom(to_app(r.n->get_expr()));
@ -86,35 +84,12 @@ namespace array {
return assert_default_const_axiom(to_app(child));
else if (a.is_store(child))
return assert_default_store_axiom(to_app(child));
else if (a.is_map(child))
else if (is_map_combinator(child))
return assert_default_map_axiom(to_app(child));
else
return false;
}
bool solver::is_relevant(axiom_record const& r) const {
return true;
#if 0
// relevancy propagation is currently incomplete on terms
expr* child = r.n->get_expr();
switch (r.m_kind) {
case axiom_record::kind_t::is_select: {
app* select = r.select->get_app();
for (unsigned i = 1; i < select->get_num_args(); ++i)
if (!ctx.is_relevant(select->get_arg(i)))
return false;
return ctx.is_relevant(child);
}
case axiom_record::kind_t::is_default:
return ctx.is_relevant(child);
default:
return true;
}
#endif
}
bool solver::assert_select(unsigned idx, axiom_record& r) {
expr* child = r.n->get_expr();
app* select = r.select->get_app();
@ -140,7 +115,7 @@ namespace array {
return assert_select_as_array_axiom(select, to_app(child));
else if (a.is_store(child))
return assert_select_store_axiom(select, to_app(child));
else if (a.is_map(child))
else if (is_map_combinator(child))
return assert_select_map_axiom(select, to_app(child));
else if (is_lambda(child))
return assert_select_lambda_axiom(select, child);
@ -215,10 +190,17 @@ namespace array {
return new_prop;
sat::literal sel_eq = sat::null_literal;
auto ensure_relevant = [&](sat::literal lit) {
if (ctx.is_relevant(lit))
return;
new_prop = true;
ctx.mark_relevant(lit);
};
auto init_sel_eq = [&]() {
if (sel_eq != sat::null_literal)
return true;
sel_eq = mk_literal(sel_eq_e);
ensure_relevant(sel_eq);
return s().value(sel_eq) != l_true;
};
@ -235,6 +217,7 @@ namespace array {
break;
}
sat::literal idx_eq = eq_internalize(idx1, idx2);
ensure_relevant(idx_eq);
if (s().value(idx_eq) == l_true)
continue;
if (s().value(idx_eq) == l_undef)
@ -253,8 +236,7 @@ namespace array {
* Assert
* select(const(v), i) = v
*/
bool solver::assert_select_const_axiom(app* select, app* cnst) {
bool solver::assert_select_const_axiom(app* select, app* cnst) {
++m_stats.m_num_select_const_axiom;
expr* val = nullptr;
VERIFY(a.is_const(cnst, val));
@ -291,16 +273,19 @@ namespace array {
return add_clause(lit1, ~lit2);
}
bool solver::is_map_combinator(expr* map) const {
return a.is_map(map) || a.is_union(map) || a.is_intersect(map) || a.is_difference(map) || a.is_complement(map);
}
/**
* Assert axiom:
* select(map[f](a, ... d), i) = f(select(a,i),...,select(d,i))
*/
bool solver::assert_select_map_axiom(app* select, app* map) {
++m_stats.m_num_select_map_axiom;
SASSERT(a.is_map(map));
SASSERT(a.is_select(select));
SASSERT(is_map_combinator(map));
SASSERT(map->get_num_args() > 0);
func_decl* f = a.get_map_func_decl(map);
unsigned num_args = select->get_num_args();
ptr_buffer<expr> args1, args2;
vector<ptr_vector<expr> > args2l;
@ -321,7 +306,8 @@ namespace array {
expr_ref sel1(m), sel2(m);
sel1 = a.mk_select(args1);
sel2 = m.mk_app(f, args2);
sel2 = apply_map(map, args2.size(), args2.data());
rewrite(sel2);
euf::enode* n1 = e_internalize(sel1);
euf::enode* n2 = e_internalize(sel2);
@ -348,21 +334,44 @@ namespace array {
return ctx.propagate(n1, n2, array_axiom());
}
expr_ref solver::apply_map(app* map, unsigned n, expr* const* args) {
expr_ref result(m);
if (a.is_map(map))
result = m.mk_app(a.get_map_func_decl(map), n, args);
else if (a.is_union(map))
result = m.mk_or(n, args);
else if (a.is_intersect(map))
result = m.mk_and(n, args);
else if (a.is_difference(map)) {
SASSERT(n > 0);
result = args[0];
for (unsigned i = 1; i < n; ++i)
result = m.mk_and(result, m.mk_not(args[i]));
}
else if (a.is_complement(map)) {
SASSERT(n == 1);
result = m.mk_not(args[0]);
}
else {
UNREACHABLE();
}
rewrite(result);
return result;
}
/**
* Assert:
* default(map[f](a,..,d)) = f(default(a),..,default(d))
*/
bool solver::assert_default_map_axiom(app* map) {
++m_stats.m_num_default_map_axiom;
SASSERT(a.is_map(map));
func_decl* f = a.get_map_func_decl(map);
SASSERT(map->get_num_args() == f->get_arity());
SASSERT(is_map_combinator(map));
expr_ref_vector args2(m);
for (expr* arg : *map)
args2.push_back(a.mk_default(arg));
expr_ref def1(a.mk_default(map), m);
expr_ref def2(m.mk_app(f, args2), m);
rewrite(def2);
expr_ref def2 = apply_map(map, args2.size(), args2.data());
return ctx.propagate(e_internalize(def1), e_internalize(def2), array_axiom());
}
@ -534,11 +543,14 @@ namespace array {
unsigned num_vars = get_num_vars();
bool change = false;
for (unsigned v = 0; v < num_vars; v++) {
propagate_parent_select_axioms(v);
auto& d = get_var_data(v);
if (!d.m_prop_upward)
continue;
euf::enode* n = var2enode(v);
if (!ctx.is_relevant(n))
continue;
for (euf::enode* lambda : d.m_parent_lambdas)
propagate_select_axioms(d, lambda);
if (add_as_array_eqs(n))
change = true;
bool has_default = false;
@ -552,8 +564,8 @@ namespace array {
m_delay_qhead = 0;
for (; m_delay_qhead < sz; ++m_delay_qhead)
if (m_axiom_trail[m_delay_qhead].is_delayed() && assert_axiom(m_delay_qhead))
change = true;
if (m_axiom_trail[m_delay_qhead].is_delayed() && assert_axiom(m_delay_qhead))
change = true;
flet<bool> _enable_delay(m_enable_delay, false);
if (unit_propagate())
change = true;
@ -567,6 +579,8 @@ namespace array {
return false;
for (unsigned i = 0; i < ctx.get_egraph().enodes_of(f).size(); ++i) {
euf::enode* p = ctx.get_egraph().enodes_of(f)[i];
if (!ctx.is_relevant(p))
continue;
expr_ref_vector select(m);
select.push_back(n->get_expr());
for (expr* arg : *to_app(p->get_expr()))
@ -574,7 +588,8 @@ namespace array {
expr_ref _e(a.mk_select(select.size(), select.data()), m);
euf::enode* e = e_internalize(_e);
if (e->get_root() != p->get_root()) {
add_unit(eq_internalize(_e, p->get_expr()));
sat::literal eq = eq_internalize(_e, p->get_expr());
add_unit(eq);
change = true;
}
}
@ -594,13 +609,12 @@ namespace array {
expr* e2 = var2expr(v2);
if (e1->get_sort() != e2->get_sort())
continue;
if (must_have_different_model_values(v1, v2)) {
continue;
}
if (ctx.get_egraph().are_diseq(var2enode(v1), var2enode(v2))) {
continue;
}
if (must_have_different_model_values(v1, v2))
continue;
if (ctx.get_egraph().are_diseq(var2enode(v1), var2enode(v2)))
continue;
sat::literal lit = eq_internalize(e1, e2);
ctx.mark_relevant(lit);
if (s().value(lit) == l_undef)
prop = true;
}
@ -612,10 +626,11 @@ namespace array {
ptr_buffer<euf::enode> to_unmark;
unsigned num_vars = get_num_vars();
for (unsigned i = 0; i < num_vars; i++) {
euf::enode * n = var2enode(i);
euf::enode * n = var2enode(i);
if (!is_array(n))
continue;
continue;
if (!ctx.is_relevant(n))
continue;
euf::enode * r = n->get_root();
if (r->is_marked1())
continue;

View file

@ -49,7 +49,7 @@ namespace array {
if (v == euf::null_theory_var) {
mk_var(n);
if (is_lambda(n->get_expr()))
internalize_lambda(n);
internalize_lambda_eh(n);
}
}
@ -57,45 +57,6 @@ namespace array {
ensure_var(n);
}
void solver::internalize_store(euf::enode* n) {
add_parent_lambda(n->get_arg(0)->get_th_var(get_id()), n);
push_axiom(store_axiom(n));
add_lambda(n->get_th_var(get_id()), n);
SASSERT(!get_var_data(n->get_th_var(get_id())).m_prop_upward);
}
void solver::internalize_map(euf::enode* n) {
for (auto* arg : euf::enode_args(n)) {
add_parent_lambda(arg->get_th_var(get_id()), n);
set_prop_upward(arg);
}
push_axiom(default_axiom(n));
add_lambda(n->get_th_var(get_id()), n);
SASSERT(!get_var_data(n->get_th_var(get_id())).m_prop_upward);
}
void solver::internalize_lambda(euf::enode* n) {
SASSERT(is_lambda(n->get_expr()) || a.is_const(n->get_expr()) || a.is_as_array(n->get_expr()));
theory_var v = n->get_th_var(get_id());
push_axiom(default_axiom(n));
add_lambda(v, n);
set_prop_upward(v);
}
void solver::internalize_select(euf::enode* n) {
add_parent_select(n->get_arg(0)->get_th_var(get_id()), n);
}
void solver::internalize_ext(euf::enode* n) {
SASSERT(is_array(n->get_arg(0)));
push_axiom(extensionality_axiom(n->get_arg(0), n->get_arg(1)));
}
void solver::internalize_default(euf::enode* n) {
add_parent_default(n->get_arg(0)->get_th_var(get_id()), n);
set_prop_upward(n);
}
bool solver::visited(expr* e) {
euf::enode* n = expr2enode(e);
return n && n->is_attached_to(get_id());
@ -124,40 +85,111 @@ namespace array {
mk_var(n);
for (auto* arg : euf::enode_args(n))
ensure_var(arg);
switch (a->get_decl_kind()) {
case OP_STORE:
internalize_store(n);
internalize_eh(n);
if (ctx.is_relevant(n))
relevant_eh(n);
return true;
}
void solver::internalize_lambda_eh(euf::enode* n) {
push_axiom(default_axiom(n));
auto& d = get_var_data(find(n));
ctx.push_vec(d.m_lambdas, n);
}
void solver::internalize_eh(euf::enode* n) {
switch (n->get_decl()->get_decl_kind()) {
case OP_STORE:
ctx.push_vec(get_var_data(find(n)).m_lambdas, n);
push_axiom(store_axiom(n));
break;
case OP_SELECT:
internalize_select(n);
case OP_SELECT:
break;
case OP_AS_ARRAY:
case OP_CONST_ARRAY:
internalize_lambda(n);
case OP_CONST_ARRAY:
internalize_lambda_eh(n);
break;
case OP_ARRAY_EXT:
internalize_ext(n);
case OP_ARRAY_EXT:
SASSERT(is_array(n->get_arg(0)));
push_axiom(extensionality_axiom(n->get_arg(0), n->get_arg(1)));
break;
case OP_ARRAY_DEFAULT:
internalize_default(n);
case OP_ARRAY_DEFAULT:
add_parent_default(find(n->get_arg(0)), n);
break;
case OP_ARRAY_MAP:
internalize_map(n);
case OP_ARRAY_MAP:
case OP_SET_UNION:
case OP_SET_INTERSECT:
case OP_SET_DIFFERENCE:
case OP_SET_COMPLEMENT:
for (auto* arg : euf::enode_args(n))
add_parent_lambda(find(arg), n);
internalize_lambda_eh(n);
break;
case OP_SET_UNION:
case OP_SET_INTERSECT:
case OP_SET_DIFFERENCE:
case OP_SET_COMPLEMENT:
case OP_SET_SUBSET:
case OP_SET_HAS_SIZE:
case OP_SET_CARD:
ctx.unhandled_function(a->get_decl());
case OP_SET_SUBSET: {
expr* x, *y;
VERIFY(a.is_subset(n->get_expr(), x, y));
expr_ref diff(a.mk_setminus(x, y), m);
expr_ref emp(a.mk_empty_set(x->get_sort()), m);
sat::literal eq = eq_internalize(diff, emp);
sat::literal sub = expr2literal(n->get_expr());
add_equiv(eq, sub);
break;
}
case OP_SET_HAS_SIZE:
case OP_SET_CARD:
ctx.unhandled_function(n->get_decl());
break;
default:
UNREACHABLE();
break;
break;
}
}
void solver::relevant_eh(euf::enode* n) {
if (is_lambda(n->get_expr())) {
set_prop_upward(find(n));
return;
}
if (!is_app(n->get_expr()))
return;
if (n->get_decl()->get_family_id() != a.get_family_id())
return;
switch (n->get_decl()->get_decl_kind()) {
case OP_STORE:
add_parent_lambda(find(n->get_arg(0)), n);
break;
case OP_SELECT:
add_parent_select(find(n->get_arg(0)), n);
break;
case OP_CONST_ARRAY:
case OP_AS_ARRAY:
set_prop_upward(find(n));
propagate_parent_default(find(n));
break;
case OP_ARRAY_EXT:
break;
case OP_ARRAY_DEFAULT:
set_prop_upward(find(n->get_arg(0)));
break;
case OP_ARRAY_MAP:
case OP_SET_UNION:
case OP_SET_INTERSECT:
case OP_SET_DIFFERENCE:
case OP_SET_COMPLEMENT:
for (auto* arg : euf::enode_args(n))
set_prop_upward_store(arg);
set_prop_upward(find(n));
break;
case OP_SET_SUBSET:
break;
case OP_SET_HAS_SIZE:
case OP_SET_CARD:
ctx.unhandled_function(n->get_decl());
break;
default:
UNREACHABLE();
break;
}
return true;
}
/**

View file

@ -186,17 +186,16 @@ namespace array {
if (should_set_prop_upward(d))
set_prop_upward(d);
ctx.push_vec(d.m_lambdas, lambda);
if (should_set_prop_upward(d)) {
set_prop_upward(lambda);
propagate_select_axioms(d, lambda);
}
propagate_select_axioms(d, lambda);
if (should_set_prop_upward(d))
set_prop_upward_store(lambda);
}
void solver::add_parent_lambda(theory_var v_child, euf::enode* lambda) {
SASSERT(can_beta_reduce(lambda));
auto& d = get_var_data(find(v_child));
ctx.push_vec(d.m_parent_lambdas, lambda);
if (should_set_prop_upward(d))
if (should_prop_upward(d))
propagate_select_axioms(d, lambda);
}
@ -231,6 +230,9 @@ namespace array {
for (euf::enode* lambda : d.m_lambdas)
propagate_select_axioms(d, lambda);
if (!should_prop_upward(d))
return;
for (euf::enode* lambda : d.m_parent_lambdas)
propagate_select_axioms(d, lambda);
}
@ -246,14 +248,14 @@ namespace array {
set_prop_upward(d);
}
void solver::set_prop_upward(euf::enode* n) {
void solver::set_prop_upward_store(euf::enode* n) {
if (a.is_store(n->get_expr()))
set_prop_upward(n->get_arg(0)->get_th_var(get_id()));
}
void solver::set_prop_upward(var_data& d) {
for (auto* p : d.m_lambdas)
set_prop_upward(p);
set_prop_upward_store(p);
}
/**
@ -273,6 +275,6 @@ namespace array {
}
bool solver::can_beta_reduce(expr* c) const {
return a.is_const(c) || a.is_as_array(c) || a.is_store(c) || is_lambda(c) || a.is_map(c);
return a.is_const(c) || a.is_as_array(c) || a.is_store(c) || is_lambda(c) || is_map_combinator(c);
}
}

View file

@ -66,6 +66,7 @@ namespace array {
array_union_find m_find;
theory_var find(theory_var v) { return m_find.find(v); }
theory_var find(euf::enode* n) { return find(n->get_th_var(get_id())); }
func_decl_ref_vector const& sort2diff(sort* s);
// internalize
@ -73,12 +74,8 @@ namespace array {
bool visited(expr* e) override;
bool post_visit(expr* e, bool sign, bool root) override;
void ensure_var(euf::enode* n);
void internalize_store(euf::enode* n);
void internalize_select(euf::enode* n);
void internalize_lambda(euf::enode* n);
void internalize_ext(euf::enode* n);
void internalize_default(euf::enode* n);
void internalize_map(euf::enode* n);
void internalize_eh(euf::enode* n);
void internalize_lambda_eh(euf::enode* n);
// axioms
struct axiom_record {
@ -157,7 +154,6 @@ namespace array {
bool assert_axiom(unsigned idx);
bool assert_select(unsigned idx, axiom_record & r);
bool assert_default(axiom_record & r);
bool is_relevant(axiom_record const& r) const;
void set_applied(unsigned idx) { m_axiom_trail[idx].set_applied(); }
bool is_applied(unsigned idx) const { return m_axiom_trail[idx].is_applied(); }
bool is_delayed(unsigned idx) const { return m_axiom_trail[idx].is_delayed(); }
@ -185,7 +181,9 @@ namespace array {
bool assert_congruent_axiom(expr* e1, expr* e2);
bool add_delayed_axioms();
bool add_as_array_eqs(euf::enode* n);
expr_ref apply_map(app* map, unsigned n, expr* const* args);
bool is_map_combinator(expr* e) const;
bool has_unitary_domain(app* array_term);
bool has_large_domain(expr* array_term);
std::pair<app*, func_decl*> mk_epsilon(sort* s);
@ -197,7 +195,7 @@ namespace array {
// solving
void add_parent_select(theory_var v_child, euf::enode* select);
void add_parent_default(theory_var v_child, euf::enode* def);
void add_lambda(theory_var v, euf::enode* lambda);
void add_lambda(theory_var v, euf::enode* lambda);
void add_parent_lambda(theory_var v_child, euf::enode* lambda);
void propagate_select_axioms(var_data const& d, euf::enode* a);
@ -206,7 +204,7 @@ namespace array {
void set_prop_upward(theory_var v);
void set_prop_upward(var_data& d);
void set_prop_upward(euf::enode* n);
void set_prop_upward_store(euf::enode* n);
unsigned get_lambda_equiv_size(var_data const& d) const;
bool should_set_prop_upward(var_data const& d) const;
bool should_prop_upward(var_data const& d) const;
@ -264,7 +262,9 @@ namespace array {
void apply_sort_cnstr(euf::enode* n, sort* s) override;
bool is_shared(theory_var v) const override;
bool enable_self_propagate() const override { return true; }
void relevant_eh(euf::enode* n) override;
bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_array(n->get_sort()); }
void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2);
void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {}
void unmerge_eh(theory_var v1, theory_var v2) {}

View file

@ -21,7 +21,10 @@ Author:
namespace bv {
bool solver::check_delay_internalized(expr* e) {
if (!ctx.is_relevant(e))
euf::enode* n = expr2enode(e);
if (!n)
return true;
if (!ctx.is_relevant(n))
return true;
if (get_internalize_mode(e) != internalize_mode::delay_i)
return true;
@ -247,7 +250,7 @@ namespace bv {
return;
expr_ref tmp = literal2expr(bits.back());
for (unsigned i = bits.size() - 1; i-- > 0; ) {
auto b = bits[i];
sat::literal b = bits[i];
tmp = m.mk_or(literal2expr(b), tmp);
xs.push_back(tmp);
}

View file

@ -166,6 +166,7 @@ namespace bv {
#define internalize_pun(F) pun = [&](unsigned sz, expr* const* xs, unsigned p, expr_ref_vector& bits) { m_bb.F(sz, xs, p, bits);}; internalize_par_unary(a, pun);
#define internalize_nfl(F) ebin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref& out) { m_bb.F(sz, xs, ys, out);}; internalize_novfl(a, ebin);
#define internalize_int(B, U) ibin = [&](expr* x, expr* y) { return B(x, y); }; iun = [&](expr* x) { return U(x); }; internalize_interp(a, ibin, iun);
#define if_unary(F) if (a->get_num_args() == 1) { internalize_un(F); break; }
switch (a->get_decl_kind()) {
case OP_BV_NUM: internalize_num(a); break;
@ -190,7 +191,7 @@ namespace bv {
case OP_BXOR: internalize_ac(mk_xor); break;
case OP_BNAND: internalize_bin(mk_nand); break;
case OP_BNOR: internalize_bin(mk_nor); break;
case OP_BXNOR: internalize_bin(mk_xnor); break;
case OP_BXNOR: if_unary(mk_not); internalize_bin(mk_xnor); break;
case OP_BCOMP: internalize_bin(mk_comp); break;
case OP_SIGN_EXT: internalize_pun(mk_sign_extend); break;
case OP_ZERO_EXT: internalize_pun(mk_zero_extend); break;
@ -426,7 +427,6 @@ namespace bv {
expr_ref sum(m_autil.mk_add(sz, args.data()), m);
sat::literal lit = eq_internalize(n, sum);
add_unit(lit);
ctx.add_root(lit);
}
void solver::internalize_int2bv(app* n) {
@ -456,7 +456,6 @@ namespace bv {
rhs = m_autil.mk_mod(e, m_autil.mk_int(mod));
sat::literal eq_lit = eq_internalize(lhs, rhs);
add_unit(eq_lit);
ctx.add_root(eq_lit);
expr_ref_vector n_bits(m);
get_bits(n_enode, n_bits);
@ -469,7 +468,6 @@ namespace bv {
lhs = n_bits.get(i);
eq_lit = eq_internalize(lhs, rhs);
add_unit(eq_lit);
ctx.add_root(eq_lit);
}
}
@ -535,7 +533,6 @@ namespace bv {
if (p.hi_div0()) {
eq_lit = eq_internalize(n, ibin(arg1, arg2));
add_unit(eq_lit);
ctx.add_root(eq_lit);
}
else {
unsigned sz = bv.get_bv_size(n);
@ -653,7 +650,6 @@ namespace bv {
mk_bits(get_th_var(e));
sat::literal eq_lit = eq_internalize(e, r);
add_unit(eq_lit);
ctx.add_root(eq_lit);
}
void solver::internalize_bit2bool(app* n) {

View file

@ -99,14 +99,38 @@ namespace euf {
m_tmp_inference->init(m_tmp_inference);
}
bool ackerman::enable_cc(app* a, app* b) {
if (!s.enable_ackerman_axioms(a))
return false;
if (!s.enable_ackerman_axioms(b))
return false;
for (expr* arg : *a)
if (!s.enable_ackerman_axioms(arg))
return false;
for (expr* arg : *b)
if (!s.enable_ackerman_axioms(arg))
return false;
return true;
}
bool ackerman::enable_eq(expr* a, expr* b, expr* c) {
return s.enable_ackerman_axioms(a) &&
s.enable_ackerman_axioms(b) &&
s.enable_ackerman_axioms(c);
}
void ackerman::cg_conflict_eh(expr * n1, expr * n2) {
if (!is_app(n1) || !is_app(n2))
return;
if (!s.enable_ackerman_axioms(n1))
return;
SASSERT(!s.m_drating);
app* a = to_app(n1);
app* b = to_app(n2);
if (a->get_decl() != b->get_decl() || a->get_num_args() != b->get_num_args())
return;
if (!enable_cc(a, b))
return;
TRACE("ack", tout << "conflict eh: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";);
insert(a, b);
gc();
@ -117,6 +141,8 @@ namespace euf {
return;
if (s.m_drating)
return;
if (!enable_eq(a, b, c))
return;
TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";);
insert(a, b, c);
gc();
@ -128,6 +154,8 @@ namespace euf {
TRACE("ack", tout << "used cc: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";);
SASSERT(a->get_decl() == b->get_decl());
SASSERT(a->get_num_args() == b->get_num_args());
if (!enable_cc(a, b))
return;
insert(a, b);
gc();
}
@ -173,13 +201,13 @@ namespace euf {
app* b = to_app(_b);
TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << "\n";);
sat::literal_vector lits;
unsigned sz = a->get_num_args();
unsigned sz = a->get_num_args();
for (unsigned i = 0; i < sz; ++i) {
expr_ref eq(m.mk_eq(a->get_arg(i), b->get_arg(i)), m);
expr_ref eq = s.mk_eq(a->get_arg(i), b->get_arg(i));
lits.push_back(~s.mk_literal(eq));
}
expr_ref eq(m.mk_eq(a, b), m);
expr_ref eq = s.mk_eq(a, b);
lits.push_back(s.mk_literal(eq));
s.s().mk_clause(lits, sat::status::th(true, m.get_basic_family_id()));
}
@ -187,9 +215,9 @@ namespace euf {
void ackerman::add_eq(expr* a, expr* b, expr* c) {
flet<bool> _is_redundant(s.m_is_redundant, true);
sat::literal lits[3];
expr_ref eq1(m.mk_eq(a, c), m);
expr_ref eq2(m.mk_eq(b, c), m);
expr_ref eq3(m.mk_eq(a, b), m);
expr_ref eq1(s.mk_eq(a, c), m);
expr_ref eq2(s.mk_eq(b, c), m);
expr_ref eq3(s.mk_eq(a, b), m);
TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";);
lits[0] = ~s.mk_literal(eq1);
lits[1] = ~s.mk_literal(eq2);

View file

@ -70,6 +70,9 @@ namespace euf {
void add_cc(expr* a, expr* b);
void add_eq(expr* a, expr* b, expr* c);
void gc();
bool enable_cc(app* a, app* b);
bool enable_eq(expr* a, expr* b, expr* c);
public:
ackerman(solver& s, ast_manager& m);

View file

@ -162,10 +162,8 @@ namespace euf {
sat::literal lit2 = literal(v, false);
s().mk_clause(~lit, lit2, sat::status::th(m_is_redundant, m.get_basic_family_id()));
s().mk_clause(lit, ~lit2, sat::status::th(m_is_redundant, m.get_basic_family_id()));
if (relevancy_enabled()) {
add_aux(~lit, lit2);
add_aux(lit, ~lit2);
}
add_aux(~lit, lit2);
add_aux(lit, ~lit2);
lit = lit2;
}
@ -185,8 +183,6 @@ namespace euf {
m_egraph.set_bool_var(n, v);
if (m.is_eq(e) || m.is_or(e) || m.is_and(e) || m.is_not(e))
m_egraph.set_merge_enabled(n, false);
if (!si.is_bool_op(e))
track_relevancy(lit.var());
if (s().value(lit) != l_undef)
m_egraph.set_value(n, s().value(lit));
return lit;
@ -224,9 +220,8 @@ namespace euf {
lits.push_back(lit);
}
}
add_root(lits);
s().mk_clause(lits, st);
if (relevancy_enabled())
add_root(lits);
}
else {
// g(f(x_i)) = x_i
@ -244,15 +239,13 @@ namespace euf {
expr_ref gapp(m.mk_app(g, fapp.get()), m);
expr_ref eq = mk_eq(gapp, arg);
sat::literal lit = mk_literal(eq);
s().add_clause(1, &lit, st);
s().add_clause(lit, st);
eqs.push_back(mk_eq(fapp, a));
}
pb_util pb(m);
expr_ref at_least2(pb.mk_at_least_k(eqs.size(), eqs.data(), 2), m);
sat::literal lit = si.internalize(at_least2, m_is_redundant);
s().mk_clause(1, &lit, st);
if (relevancy_enabled())
add_root(1, &lit);
s().add_clause(lit, st);
}
}
@ -268,9 +261,7 @@ namespace euf {
for (unsigned j = i + 1; j < sz; ++j) {
expr_ref eq = mk_eq(args[i]->get_expr(), args[j]->get_expr());
sat::literal lit = ~mk_literal(eq);
s().add_clause(1, &lit, st);
if (relevancy_enabled())
add_root(1, &lit);
s().add_clause(lit, st);
}
}
}
@ -287,9 +278,7 @@ namespace euf {
n->mark_interpreted();
expr_ref eq = mk_eq(fapp, fresh);
sat::literal lit = mk_literal(eq);
s().add_clause(1, &lit, st);
if (relevancy_enabled())
add_root(1, &lit);
s().add_clause(lit, st);
}
}
}
@ -302,16 +291,16 @@ namespace euf {
expr_ref eq_th = mk_eq(e, th);
sat::literal lit_th = mk_literal(eq_th);
if (th == el) {
s().add_clause(1, &lit_th, st);
s().add_clause(lit_th, st);
}
else {
sat::literal lit_c = mk_literal(c);
expr_ref eq_el = mk_eq(e, el);
sat::literal lit_el = mk_literal(eq_el);
literal lits1[2] = { ~lit_c, lit_th };
literal lits2[2] = { lit_c, lit_el };
s().add_clause(2, lits1, st);
s().add_clause(2, lits2, st);
add_root(~lit_c, lit_th);
add_root(lit_c, lit_el);
s().add_clause(~lit_c, lit_th, st);
s().add_clause(lit_c, lit_el, st);
}
}
else if (m.is_distinct(e)) {
@ -326,10 +315,10 @@ namespace euf {
expr_ref fml(m.mk_or(eqs), m);
sat::literal dist(si.to_bool_var(e), false);
sat::literal some_eq = si.internalize(fml, m_is_redundant);
sat::literal lits1[2] = { ~dist, ~some_eq };
sat::literal lits2[2] = { dist, some_eq };
s().add_clause(2, lits1, st);
s().add_clause(2, lits2, st);
add_root(~dist, ~some_eq);
add_root(dist, some_eq);
s().add_clause(~dist, ~some_eq, st);
s().add_clause(dist, some_eq, st);
}
else if (m.is_eq(e, th, el) && !m.is_iff(e)) {
sat::literal lit1 = expr2literal(e);
@ -338,10 +327,10 @@ namespace euf {
enode* n2 = m_egraph.find(e2);
if (n2) {
sat::literal lit2 = expr2literal(e2);
sat::literal lits1[2] = { ~lit1, lit2 };
sat::literal lits2[2] = { lit1, ~lit2 };
s().add_clause(2, lits1, st);
s().add_clause(2, lits2, st);
add_root(~lit1, lit2);
add_root(lit1, ~lit2);
s().add_clause(~lit1, lit2, st);
s().add_clause(lit1, ~lit2, st);
}
}
}
@ -357,7 +346,7 @@ namespace euf {
// contains a parent application.
family_id th_id = m.get_basic_family_id();
for (auto p : euf::enode_th_vars(n)) {
for (auto const& p : euf::enode_th_vars(n)) {
family_id id = p.get_id();
if (m.get_basic_family_id() != id) {
if (th_id != m.get_basic_family_id())
@ -402,7 +391,7 @@ namespace euf {
// Remark: The inconsistency is not going to be detected if they are
// not marked as shared.
for (auto p : euf::enode_th_vars(n))
for (auto const& p : euf::enode_th_vars(n))
if (fid2solver(p.get_id())->is_shared(p.get_var()))
return true;

View file

@ -35,7 +35,7 @@ namespace euf {
s(s), m(s.m), mdl(mdl), values(values), factory(m) {}
~user_sort() {
for (auto kv : sort2values)
for (auto const& kv : sort2values)
mdl->register_usort(kv.m_key, kv.m_value->size(), kv.m_value->data());
}
@ -161,10 +161,9 @@ namespace euf {
default:
break;
}
if (is_app(e) && to_app(e)->get_family_id() == m.get_basic_family_id())
continue;
sat::bool_var v = get_enode(e)->bool_var();
SASSERT(v != sat::null_bool_var);
if (v == sat::null_bool_var)
continue;
switch (s().value(v)) {
case l_true:
m_values.set(id, m.mk_true());
@ -200,6 +199,8 @@ namespace euf {
expr* e = n->get_expr();
if (!is_app(e))
continue;
if (!is_relevant(n))
continue;
app* a = to_app(e);
func_decl* f = a->get_decl();
if (!include_func_interp(f))
@ -224,7 +225,7 @@ namespace euf {
enode* earg = get_enode(arg);
expr* val = m_values.get(earg->get_root_id());
args.push_back(val);
CTRACE("euf", !val, tout << "no value for " << bpp(earg) << "\n";);
CTRACE("euf", !val, tout << "no value for " << bpp(earg) << "\n" << bpp(n) << "\n"; display(tout););
SASSERT(val);
}
SASSERT(args.size() == arity);
@ -259,7 +260,7 @@ namespace euf {
if (n->is_root() && m_values.get(n->get_expr_id()))
m_values2root.insert(m_values.get(n->get_expr_id()), n);
TRACE("model",
for (auto kv : m_values2root)
for (auto const& kv : m_values2root)
tout << mk_bounded_pp(kv.m_key, m) << "\n -> " << bpp(kv.m_value) << "\n";);
return m_values2root;
@ -271,6 +272,8 @@ namespace euf {
void solver::display_validation_failure(std::ostream& out, model& mdl, enode* n) {
out << "Failed to validate " << n->bool_var() << " " << bpp(n) << " " << mdl(n->get_expr()) << "\n";
s().display(out);
return;
euf::enode_vector nodes;
nodes.push_back(n);
for (unsigned i = 0; i < nodes.size(); ++i) {
@ -289,7 +292,6 @@ namespace euf {
for (euf::enode* r : nodes)
r->unmark1();
out << mdl << "\n";
s().display(out);
}
void solver::validate_model(model& mdl) {
@ -297,6 +299,8 @@ namespace euf {
ev.set_model_completion(true);
TRACE("model",
for (enode* n : m_egraph.nodes()) {
if (!is_relevant(n))
continue;
unsigned id = n->get_root_id();
expr* val = m_values.get(id, nullptr);
if (!val)
@ -322,6 +326,8 @@ namespace euf {
IF_VERBOSE(0, display_validation_failure(verbose_stream(), mdl, n););
CTRACE("euf", first, display_validation_failure(tout, mdl, n););
(void)first;
first = false;
return;
exit(1);
first = false;
}

View file

@ -3,157 +3,326 @@ Copyright (c) 2020 Microsoft Corporation
Module Name:
euf_relevancy.cpp
smt_relevant.cpp
Abstract:
Features for relevancy tracking.
A reduced (minimal) implicant is extracted by running a dual solver.
Then the literals in the implicant are used as a basis for marking
subterms relevant.
Relevancy propagation
Author:
Nikolaj Bjorner (nbjorner) 2020-09-22
Nikolaj Bjorner (nbjorner) 2021-12-27
--*/
#include "sat/sat_solver.h"
#include "sat/smt/euf_solver.h"
#include "sat/smt/euf_relevancy.h"
namespace euf {
void solver::add_auto_relevant(expr* e) {
if (!relevancy_enabled())
return;
for (; m_auto_relevant_scopes > 0; --m_auto_relevant_scopes)
m_auto_relevant_lim.push_back(m_auto_relevant.size());
// std::cout << "add-auto " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n";
m_auto_relevant.push_back(e);
}
void solver::pop_relevant(unsigned n) {
if (m_auto_relevant_scopes >= n) {
m_auto_relevant_scopes -= n;
void relevancy::pop(unsigned n) {
if (!m_enabled)
return;
if (n <= m_num_scopes) {
m_num_scopes -= n;
return;
}
n -= m_auto_relevant_scopes;
m_auto_relevant_scopes = 0;
unsigned top = m_auto_relevant_lim.size() - n;
unsigned lim = m_auto_relevant_lim[top];
m_auto_relevant_lim.shrink(top);
m_auto_relevant.shrink(lim);
else if (m_num_scopes > 0) {
n -= m_num_scopes;
m_num_scopes = 0;
}
SASSERT(n > 0);
unsigned sz = m_lim[m_lim.size() - n];
for (unsigned i = m_trail.size(); i-- > sz; ) {
auto const& [u, idx] = m_trail[i];
switch (u) {
case update::relevant_var:
m_relevant_var_ids[idx] = false;
break;
case update::add_queue:
m_queue.pop_back();
break;
case update::add_clause: {
sat::clause* c = m_clauses.back();
for (sat::literal lit : *c) {
SASSERT(m_occurs[lit.index()].back() == m_clauses.size() - 1);
m_occurs[lit.index()].pop_back();
}
m_clauses.pop_back();
m_roots.pop_back();
m_alloc.del_clause(c);
break;
}
case update::set_root:
m_roots[idx] = false;
break;
case update::set_qhead:
m_qhead = idx;
break;
default:
UNREACHABLE();
break;
}
}
m_trail.shrink(sz);
m_lim.shrink(m_lim.size() - n);
}
void solver::push_relevant() {
++m_auto_relevant_scopes;
}
bool solver::is_relevant(expr* e) const {
return m_relevant_expr_ids.get(e->get_id(), true);
}
bool solver::is_relevant(enode* n) const {
return m_relevant_expr_ids.get(n->get_expr_id(), true);
}
void solver::ensure_dual_solver() {
if (m_dual_solver)
void relevancy::add_root(unsigned n, sat::literal const* lits) {
if (!m_enabled)
return;
m_dual_solver = alloc(sat::dual_solver, s().rlimit());
for (unsigned i = s().num_user_scopes(); i-- > 0; )
m_dual_solver->push();
flush();
TRACE("relevancy", tout << "root " << sat::literal_vector(n, lits) << "\n");
sat::literal true_lit = sat::null_literal;
for (unsigned i = 0; i < n; ++i) {
if (ctx.s().value(lits[i]) == l_true) {
if (is_relevant(lits[i]))
return;
true_lit = lits[i];
}
}
if (true_lit != sat::null_literal) {
mark_relevant(true_lit);
return;
}
sat::clause* cl = m_alloc.mk_clause(n, lits, false);
unsigned sz = m_clauses.size();
m_clauses.push_back(cl);
m_roots.push_back(true);
m_trail.push_back(std::make_pair(update::add_clause, 0));
for (sat::literal lit : *cl) {
ctx.s().set_external(lit.var());
occurs(lit).push_back(sz);
}
}
void relevancy::add_def(unsigned n, sat::literal const* lits) {
if (!m_enabled)
return;
flush();
TRACE("relevancy", tout << "def " << sat::literal_vector(n, lits) << "\n");
for (unsigned i = 0; i < n; ++i) {
if (ctx.s().value(lits[i]) == l_false && is_relevant(lits[i])) {
add_root(n, lits);
return;
}
}
sat::clause* cl = m_alloc.mk_clause(n, lits, false);
unsigned sz = m_clauses.size();
m_clauses.push_back(cl);
m_roots.push_back(false);
m_trail.push_back(std::make_pair(update::add_clause, 0));
for (sat::literal lit : *cl) {
ctx.s().set_external(lit.var());
occurs(lit).push_back(sz);
}
}
void relevancy::add_to_propagation_queue(sat::literal lit) {
m_trail.push_back(std::make_pair(update::add_queue, lit.var()));
m_queue.push_back(std::make_pair(lit, nullptr));
}
void relevancy::set_relevant(sat::literal lit) {
euf::enode* n = ctx.bool_var2enode(lit.var());
if (n)
mark_relevant(n);
m_relevant_var_ids.setx(lit.var(), true, false);
m_trail.push_back(std::make_pair(update::relevant_var, lit.var()));
}
void relevancy::set_asserted(sat::literal lit) {
SASSERT(!is_relevant(lit));
SASSERT(ctx.s().value(lit) == l_true);
set_relevant(lit);
add_to_propagation_queue(lit);
ctx.asserted(lit);
}
/**
* Add a root clause. Root clauses must all be satisfied by the
* final assignment. If a clause is added above search level it
* is subject to removal on backtracking. These clauses are therefore
* not tracked.
*/
void solver::add_root(unsigned n, sat::literal const* lits) {
if (!relevancy_enabled())
* Boolean variable is set relevant because an E-node is relevant.
*
*/
void relevancy::relevant_eh(sat::bool_var v) {
if (is_relevant(v))
return;
ensure_dual_solver();
m_dual_solver->add_root(n, lits);
}
void solver::add_aux(unsigned n, sat::literal const* lits) {
if (!relevancy_enabled())
return;
ensure_dual_solver();
m_dual_solver->add_aux(n, lits);
}
void solver::track_relevancy(sat::bool_var v) {
ensure_dual_solver();
m_dual_solver->track_relevancy(v);
}
bool solver::init_relevancy() {
m_relevant_expr_ids.reset();
if (!relevancy_enabled())
return true;
if (!m_dual_solver)
return true;
if (!(*m_dual_solver)(s()))
return false;
unsigned max_id = 0;
for (enode* n : m_egraph.nodes())
max_id = std::max(max_id, n->get_expr_id());
m_relevant_expr_ids.resize(max_id + 1, false);
ptr_vector<expr>& todo = m_relevant_todo;
bool_vector& visited = m_relevant_visited;
auto const& core = m_dual_solver->core();
todo.reset();
for (auto lit : core) {
expr* e = m_bool_var2expr.get(lit.var(), nullptr);
if (e)
todo.push_back(e);
sat::literal lit(v);
switch (ctx.s().value(lit)) {
case l_undef:
set_relevant(lit);
break;
case l_true:
set_asserted(lit);
break;
case l_false:
set_asserted(~lit);
break;
}
#if 0
std::cout << "init-relevant\n";
for (expr* e : m_auto_relevant)
std::cout << "auto-relevant " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n";
#endif
todo.append(m_auto_relevant);
for (unsigned i = 0; i < todo.size(); ++i) {
expr* e = todo[i];
if (visited.get(e->get_id(), false))
}
void relevancy::asserted(sat::literal lit) {
TRACE("relevancy", tout << "asserted " << lit << " relevant " << is_relevant(lit) << "\n");
if (!m_enabled)
return;
flush();
if (is_relevant(lit)) {
add_to_propagation_queue(lit);
return;
}
if (ctx.s().lvl(lit) <= ctx.s().search_lvl()) {
set_relevant(lit);
add_to_propagation_queue(lit);
return;
}
for (auto idx : occurs(lit)) {
if (!m_roots[idx])
continue;
visited.setx(e->get_id(), true, false);
if (!si.is_bool_op(e))
m_relevant_expr_ids.setx(e->get_id(), true, false);
if (!is_app(e))
for (sat::literal lit2 : *m_clauses[idx])
if (lit2 != lit && ctx.s().value(lit2) == l_true && is_relevant(lit2))
goto next;
set_relevant(lit);
add_to_propagation_queue(lit);
return;
next:
;
}
}
void relevancy::propagate() {
if (!m_enabled)
return;
flush();
if (m_qhead == m_queue.size())
return;
m_trail.push_back(std::make_pair(update::set_qhead, m_qhead));
while (m_qhead < m_queue.size() && !ctx.s().inconsistent() && ctx.get_manager().inc()) {
auto const& [lit, n] = m_queue[m_qhead++];
SASSERT(n || lit != sat::null_literal);
SASSERT(!n || lit == sat::null_literal);
if (n)
propagate_relevant(n);
else
propagate_relevant(lit);
}
}
void relevancy::merge(euf::enode* root, euf::enode* other) {
TRACE("relevancy", tout << "merge #" << ctx.bpp(root) << " " << is_relevant(root) << " #" << ctx.bpp(other) << " " << is_relevant(other) << "\n");
if (is_relevant(root))
mark_relevant(other);
else if (is_relevant(other))
mark_relevant(root);
}
void relevancy::mark_relevant(euf::enode* n) {
if (!m_enabled)
return;
flush();
if (is_relevant(n))
return;
TRACE("relevancy", tout << "mark #" << ctx.bpp(n) << "\n");
m_trail.push_back(std::make_pair(update::add_queue, 0));
m_queue.push_back(std::make_pair(sat::null_literal, n));
}
void relevancy::mark_relevant(sat::literal lit) {
TRACE("relevancy", tout << "mark " << lit << " " << is_relevant(lit) << " " << ctx.s().value(lit) << " lim: " << m_lim.size() << "\n");
if (!m_enabled)
return;
flush();
if (is_relevant(lit))
return;
set_relevant(lit);
switch (ctx.s().value(lit)) {
case l_true:
break;
case l_false:
lit.neg();
break;
default:
return;
}
add_to_propagation_queue(lit);
}
void relevancy::propagate_relevant(sat::literal lit) {
SASSERT(m_num_scopes == 0);
TRACE("relevancy", tout << "propagate " << lit << " lim: " << m_lim.size() << "\n");
SASSERT(ctx.s().value(lit) == l_true);
SASSERT(is_relevant(lit));
euf::enode* n = ctx.bool_var2enode(lit.var());
if (n && !ctx.get_si().is_bool_op(n->get_expr()))
return;
for (auto idx : occurs(~lit)) {
if (m_roots[idx])
continue;
expr* c = nullptr, *th = nullptr, *el = nullptr;
if (m.is_ite(e, c, th, el) && get_enode(c)) {
sat::literal lit = expr2literal(c);
todo.push_back(c);
switch (s().value(lit)) {
case l_true:
todo.push_back(th);
break;
case l_false:
todo.push_back(el);
break;
default:
todo.push_back(th);
todo.push_back(el);
break;
sat::clause* cl = m_clauses[idx];
sat::literal true_lit = sat::null_literal;
for (sat::literal lit2 : *cl) {
if (ctx.s().value(lit2) == l_true) {
if (is_relevant(lit2))
goto next;
true_lit = lit2;
}
continue;
}
for (expr* arg : *to_app(e))
todo.push_back(arg);
if (true_lit != sat::null_literal)
set_asserted(true_lit);
else {
m_trail.push_back(std::make_pair(update::set_root, idx));
m_roots[idx] = true;
}
next:
TRACE("relevancy", tout << "propagate " << lit << " " << true_lit << " " << m_roots[idx] << "\n");
;
}
for (auto * e : todo)
visited[e->get_id()] = false;
TRACE("euf",
for (enode* n : m_egraph.nodes())
if (is_relevant(n))
tout << "relevant " << n->get_expr_id() << " [r" << n->get_root_id() << "]: " << mk_bounded_pp(n->get_expr(), m) << "\n";);
return true;
}
void relevancy::propagate_relevant(euf::enode* n) {
m_todo.push_back(n);
while (!m_todo.empty()) {
n = m_todo.back();
m_todo.pop_back();
TRACE("relevancy", tout << "propagate #" << ctx.bpp(n) << " lim: " << m_lim.size() << "\n");
if (n->is_relevant())
continue;
m_stack.push_back(n);
while (!m_stack.empty()) {
n = m_stack.back();
unsigned sz = m_stack.size();
bool is_bool_op = ctx.get_si().is_bool_op(n->get_expr());
if (!is_bool_op)
for (euf::enode* arg : euf::enode_args(n))
if (!arg->is_relevant())
m_stack.push_back(arg);
if (sz != m_stack.size())
continue;
if (!n->is_relevant()) {
ctx.get_egraph().set_relevant(n);
ctx.relevant_eh(n);
sat::bool_var v = n->bool_var();
if (v != sat::null_bool_var)
relevant_eh(v);
for (euf::enode* sib : euf::enode_class(n))
if (!sib->is_relevant())
m_todo.push_back(sib);
}
if (!ctx.get_manager().inc()) {
m_todo.reset();
m_stack.reset();
return;
}
m_stack.pop_back();
}
}
}
void relevancy::set_enabled(bool e) {
m_enabled = e;
ctx.get_egraph().set_default_relevant(!e);
}
}

173
src/sat/smt/euf_relevancy.h Normal file
View file

@ -0,0 +1,173 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
smt_relevant.h
Abstract:
Relevancy propagation
Author:
Nikolaj Bjorner (nbjorner) 2021-12-27
Clauses are split into two parts:
- Roots
- Defs
The goal is to establish a labeling of literals as "relevant" such that
- the set of relevant literals satisfies Roots
- there is a set of blocked literals that can be used to satisfy the clauses in Defs
independent of their real assignment.
The idea is that the Defs clauses are obtained from Tseitin transformation so they can be
grouped by the blocked literal that was used to introduce them.
For example, when clausifying (and a b) we have the clauses
(=> (and a b) a)
(=> (and a b) b)
(or (not a) (not b) (and a b))
then the literal for "(and a b)" is blocked.
And recursively for clauses introduced for a, b if they use a Boolean connectives
at top level.
The state transitions are:
- A literal lit is assigned:
lit appears positively in a Root clause R and no other literal in R are relevant.
->
lit is set relevant
lit is justified at search level
->
lit is set relevant
- An equality n1 = n2 is assigned:
n1 is relevant
->
n2 is marked as relevant
- A lit is set relevant:
->
all clauses D in Defs where lit appears negatively are added to Roots
- When a clause R is added to Roots:
R contains a positive literal lit that is relevant
->
skip adding R to Roots
- When a clause R is added to Roots:
R contains a positive literal lit, no positive literal in R are relevant
->
lit is set relevant
- When a clause D is added to Defs:
D contains a negative literal that is relevant
->
Add D to Roots
- When an expression is set relevant:
All non-relevant children above Boolean connectives are set relevant
If nodes are treated as Boolean connectives because they are clausified
to (=> cond (= n then)) and (=> (not cond) (= n else))
Replay:
- literals that are replayed in clauses that are marked relevant are
marked relevant again.
- expressions corresponding to auxiliary clauses are added as auxiliary clauses.
- TBD: Are root clauses added under a scope discarded?
The SAT solver re-initializes clauses on its own, should we just use this mechanism?
Can a literal that is not in a root be set relevant?
- yes, if we propagate over expressions
Do we need full watch lists instead of 2-watch lists?
- probably, but unclear. The dual SAT solver only uses 2-watch lists, but uses a large clause for tracking
roots.
State machine for literals: relevant(lit), assigned(lit)
relevant(lit) transitions false -> true
if assigned(lit): add to propagation queue
if not assigned(lit): no-op (or mark enodes as relevant)
assigned(lit) transitions false -> true
if relevant(lit): add to propagation queue
if not relevant(lit): set relevant if member of root, add to propagation queue
--*/
#pragma once
#include "sat/sat_solver.h"
#include "sat/smt/sat_th.h"
namespace euf {
class solver;
class relevancy {
euf::solver& ctx;
enum class update { relevant_var, add_queue, add_clause, set_root, set_qhead };
bool m_enabled = false;
svector<std::pair<update, unsigned>> m_trail;
unsigned_vector m_lim;
unsigned m_num_scopes = 0;
bool_vector m_relevant_var_ids; // identifiers of relevant Boolean variables
sat::clause_allocator m_alloc;
sat::clause_vector m_clauses; // clauses
bool_vector m_roots; // indicate if clause is a root
vector<unsigned_vector> m_occurs; // where do literals occur
unsigned m_qhead = 0; // queue head for relevancy
svector<std::pair<sat::literal, euf::enode*>> m_queue; // propagation queue for relevancy
euf::enode_vector m_stack, m_todo;
void push_core() { m_lim.push_back(m_trail.size()); }
void flush() { for (; m_num_scopes > 0; --m_num_scopes) push_core(); }
unsigned_vector& occurs(sat::literal lit) { m_occurs.reserve(lit.index() + 1); return m_occurs[lit.index()]; }
void propagate_relevant(sat::literal lit);
void add_to_propagation_queue(sat::literal lit);
void set_relevant(sat::literal lit);
void set_asserted(sat::literal lit);
void relevant_eh(sat::bool_var v);
void propagate_relevant(euf::enode* n);
public:
relevancy(euf::solver& ctx): ctx(ctx) {}
void push() { if (m_enabled) ++m_num_scopes; }
void pop(unsigned n);
void add_root(unsigned n, sat::literal const* lits);
void add_def(unsigned n, sat::literal const* lits);
void asserted(sat::literal lit);
void propagate();
bool can_propagate() const { return m_qhead < m_queue.size(); }
void mark_relevant(euf::enode* n);
void mark_relevant(sat::literal lit);
void merge(euf::enode* n1, euf::enode* n2);
bool is_relevant(sat::bool_var v) const { return !m_enabled || m_relevant_var_ids.get(v, false); }
bool is_relevant(sat::literal lit) const { return is_relevant(lit.var()); }
bool is_relevant(euf::enode* n) const { return !m_enabled || n->is_relevant(); }
bool enabled() const { return m_enabled; }
void set_enabled(bool e);
};
}

View file

@ -41,6 +41,7 @@ namespace euf {
extension(symbol("euf"), m.mk_family_id("euf")),
m(m),
si(si),
m_relevancy(*this),
m_egraph(m),
m_trail(),
m_rewriter(m),
@ -51,12 +52,21 @@ namespace euf {
m_values(m)
{
updt_params(p);
m_relevancy.set_enabled(get_config().m_relevancy_lvl > 2);
std::function<void(std::ostream&, void*)> disp =
[&](std::ostream& out, void* j) {
display_justification_ptr(out, reinterpret_cast<size_t*>(j));
};
m_egraph.set_display_justification(disp);
if (m_relevancy.enabled()) {
std::function<void(euf::enode* root, euf::enode* other)> on_merge =
[&](enode* root, enode* other) {
m_relevancy.merge(root, other);
};
m_egraph.set_on_merge(on_merge);
}
}
void solver::updt_params(params_ref const& p) {
@ -183,7 +193,7 @@ namespace euf {
}
void solver::propagate(literal lit, ext_justification_idx idx) {
add_auto_relevant(bool_var2expr(lit.var()));
mark_relevant(lit);
s().assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), idx));
}
@ -286,6 +296,11 @@ namespace euf {
}
void solver::asserted(literal l) {
m_relevancy.asserted(l);
if (!m_relevancy.is_relevant(l))
return;
expr* e = m_bool_var2expr.get(l.var(), nullptr);
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << " := " << mk_bounded_pp(e, m) << "\n";);
if (!e)
@ -329,6 +344,8 @@ namespace euf {
bool solver::unit_propagate() {
bool propagated = false;
while (!s().inconsistent()) {
if (m_relevancy.enabled())
m_relevancy.propagate();
if (m_egraph.inconsistent()) {
unsigned lvl = s().scope_lvl();
s().set_conflict(sat::justification::mk_ext_justification(lvl, conflict_constraint().to_index()));
@ -345,9 +362,13 @@ namespace euf {
if (m_solvers[i]->unit_propagate())
propagated1 = true;
if (!propagated1)
break;
propagated = true;
if (propagated1) {
propagated = true;
continue;
}
if (m_relevancy.enabled() && m_relevancy.can_propagate())
continue;
break;
}
DEBUG_CODE(if (!propagated && !s().inconsistent()) check_missing_eq_propagation(););
return propagated;
@ -422,12 +443,10 @@ namespace euf {
void solver::propagate_th_eqs() {
for (; m_egraph.has_th_eq() && !s().inconsistent() && !m_egraph.inconsistent(); m_egraph.next_th_eq()) {
th_eq eq = m_egraph.get_th_eq();
if (eq.is_eq()) {
if (!is_self_propagated(eq))
m_id2solver[eq.id()]->new_eq_eh(eq);
}
else
if (!eq.is_eq())
m_id2solver[eq.id()]->new_diseq_eh(eq);
else if (!is_self_propagated(eq))
m_id2solver[eq.id()]->new_eq_eh(eq);
}
}
@ -458,9 +477,6 @@ namespace euf {
if (unit_propagate())
return sat::check_result::CR_CONTINUE;
if (!init_relevancy())
give_up = true;
unsigned num_nodes = m_egraph.num_nodes();
auto apply_solver = [&](th_solver* e) {
@ -525,9 +541,7 @@ namespace euf {
for (auto* e : m_solvers)
e->push();
m_egraph.push();
if (m_dual_solver)
m_dual_solver->push();
push_relevant();
m_relevancy.push();
}
void solver::pop(unsigned n) {
@ -537,7 +551,7 @@ namespace euf {
e->pop(n);
si.pop(n);
m_egraph.pop(n);
pop_relevant(n);
m_relevancy.pop(n);
scope const & sc = m_scopes[m_scopes.size() - n];
for (unsigned i = m_var_trail.size(); i-- > sc.m_var_lim; ) {
bool_var v = m_var_trail[i];
@ -546,8 +560,6 @@ namespace euf {
}
m_var_trail.shrink(sc.m_var_lim);
m_scopes.shrink(m_scopes.size() - n);
if (m_dual_solver)
m_dual_solver->pop(n);
SASSERT(m_egraph.num_scopes() == m_scopes.size());
TRACE("euf_verbose", display(tout << "pop to: " << m_scopes.size() << "\n"););
}
@ -720,6 +732,35 @@ namespace euf {
}
}
bool solver::is_relevant(bool_var v) const {
if (m_relevancy.enabled())
return m_relevancy.is_relevant(v);
auto* e = bool_var2enode(v);
return !e || is_relevant(e);
}
void solver::relevant_eh(euf::enode* n) {
if (m_qsolver)
m_qsolver->relevant_eh(n);
for (auto const& thv : enode_th_vars(n)) {
auto* th = m_id2solver.get(thv.get_id(), nullptr);
if (th && th != m_qsolver)
th->relevant_eh(n);
}
}
bool solver::enable_ackerman_axioms(expr* e) const {
euf::enode* n = get_enode(e);
if (!n)
return false;
for (auto const& thv : enode_th_vars(n)) {
auto* th = m_id2solver.get(thv.get_id(), nullptr);
if (th && !th->enable_ackerman_axioms(n))
return false;
}
return true;
}
void solver::pre_simplify() {
for (auto* e : m_solvers)
e->pre_simplify();
@ -765,6 +806,8 @@ namespace euf {
}
bool solver::set_root(literal l, literal r) {
if (m_relevancy.enabled())
return false;
expr* e = bool_var2expr(l.var());
if (!e)
return true;
@ -794,7 +837,7 @@ namespace euf {
out << "bool-vars\n";
for (unsigned v : m_var_trail) {
expr* e = m_bool_var2expr[v];
out << v << ": " << e->get_id() << " " << m_solver->value(v) << " " << mk_bounded_pp(e, m, 1) << "\n";
out << v << (is_relevant(v)?"":"n") << ": " << e->get_id() << " " << m_solver->value(v) << " " << mk_bounded_pp(e, m, 1) << "\n";
}
for (auto* e : m_solvers)
e->display(out);

View file

@ -25,11 +25,12 @@ Author:
#include "sat/sat_extension.h"
#include "sat/smt/atom2bool_var.h"
#include "sat/smt/sat_th.h"
#include "sat/smt/sat_dual_solver.h"
#include "sat/smt/euf_ackerman.h"
#include "sat/smt/user_solver.h"
#include "sat/smt/euf_relevancy.h"
#include "smt/params/smt_params.h"
namespace euf {
typedef sat::literal literal;
typedef sat::ext_constraint_idx ext_constraint_idx;
@ -91,6 +92,7 @@ namespace euf {
std::function<::solver*(void)> m_mk_solver;
ast_manager& m;
sat::sat_internalizer& si;
relevancy m_relevancy;
smt_params m_config;
euf::egraph m_egraph;
trail_stack m_trail;
@ -181,17 +183,17 @@ namespace euf {
void init_drat();
// relevancy
bool_vector m_relevant_expr_ids;
bool_vector m_relevant_visited;
ptr_vector<expr> m_relevant_todo;
void ensure_dual_solver();
bool init_relevancy();
//bool_vector m_relevant_expr_ids;
//bool_vector m_relevant_visited;
//ptr_vector<expr> m_relevant_todo;
//void init_relevant_expr_ids();
//void push_relevant(sat::bool_var v);
bool is_propagated(sat::literal lit);
// invariant
void check_eqc_bool_assignment() const;
void check_missing_bool_enode_propagation() const;
void check_missing_eq_propagation() const;
// diagnosis
std::ostream& display_justification_ptr(std::ostream& out, size_t* j) const;
@ -250,6 +252,10 @@ namespace euf {
sat::sat_internalizer& get_si() { return si; }
ast_manager& get_manager() { return m; }
enode* get_enode(expr* e) const { return m_egraph.find(e); }
enode* bool_var2enode(sat::bool_var b) const {
expr* e = m_bool_var2expr.get(b, nullptr);
return e ? get_enode(e) : nullptr;
}
sat::literal expr2literal(expr* e) const { return enode2literal(get_enode(e)); }
sat::literal enode2literal(enode* n) const { return sat::literal(n->bool_var(), false); }
lbool value(enode* n) const { return s().value(enode2literal(n)); }
@ -362,32 +368,28 @@ namespace euf {
th_rewriter& get_rewriter() { return m_rewriter; }
void rewrite(expr_ref& e) { m_rewriter(e); }
bool is_shared(euf::enode* n) const;
bool enable_ackerman_axioms(expr* n) const;
// relevancy
bool m_relevancy = true;
scoped_ptr<sat::dual_solver> m_dual_solver;
ptr_vector<expr> m_auto_relevant;
unsigned_vector m_auto_relevant_lim;
unsigned m_auto_relevant_scopes = 0;
bool relevancy_enabled() const { return m_relevancy && get_config().m_relevancy_lvl > 0; }
void disable_relevancy(expr* e) { IF_VERBOSE(0, verbose_stream() << "disabling relevancy " << mk_pp(e, m) << "\n"); m_relevancy = false; }
void add_root(unsigned n, sat::literal const* lits);
bool relevancy_enabled() const { return m_relevancy.enabled(); }
void disable_relevancy(expr* e) { IF_VERBOSE(0, verbose_stream() << "disabling relevancy " << mk_pp(e, m) << "\n"); m_relevancy.set_enabled(false); }
void add_root(unsigned n, sat::literal const* lits) { m_relevancy.add_root(n, lits); }
void add_root(sat::literal_vector const& lits) { add_root(lits.size(), lits.data()); }
void add_root(sat::literal lit) { add_root(1, &lit); }
void add_root(sat::literal a, sat::literal b) { sat::literal lits[2] = {a, b}; add_root(2, lits); }
void add_root(sat::literal lit1, sat::literal lit2) { sat::literal lits[2] = { lit1, lit2, }; add_root(2, lits); }
void add_aux(sat::literal_vector const& lits) { add_aux(lits.size(), lits.data()); }
void add_aux(unsigned n, sat::literal const* lits);
void add_aux(unsigned n, sat::literal const* lits) { m_relevancy.add_def(n, lits); }
void add_aux(sat::literal a) { sat::literal lits[1] = { a }; add_aux(1, lits); }
void add_aux(sat::literal a, sat::literal b) { sat::literal lits[2] = {a, b}; add_aux(2, lits); }
void add_aux(sat::literal a, sat::literal b, sat::literal c) { sat::literal lits[3] = { a, b, c }; add_aux(3, lits); }
void track_relevancy(sat::bool_var v);
bool is_relevant(expr* e) const;
bool is_relevant(enode* n) const;
void add_auto_relevant(expr* e);
void pop_relevant(unsigned n);
void push_relevant();
void mark_relevant(sat::literal lit) { m_relevancy.mark_relevant(lit); }
bool is_relevant(enode* n) const { return m_relevancy.is_relevant(n); }
bool is_relevant(bool_var v) const;
bool is_relevant(sat::literal lit) const { return is_relevant(lit.var()); }
void relevant_eh(euf::enode* n);
relevancy& get_relevancy() { return m_relevancy; }
// model construction
void update_model(model_ref& mdl);

View file

@ -269,7 +269,7 @@ namespace fpa {
expr* xe = e_x->get_expr();
expr* ye = e_y->get_expr();
if (m_fpa_util.is_bvwrap(xe) || m_fpa_util.is_bvwrap(ye))
if (fu.is_bvwrap(xe) || fu.is_bvwrap(ye))
return;
expr_ref xc = convert(xe);

View file

@ -2006,7 +2006,7 @@ namespace pb {
s().pop_to_base_level();
if (s().inconsistent())
return;
unsigned trail_sz, count = 0;
unsigned trail_sz = 0, count = 0;
do {
trail_sz = s().init_trail_size();
m_simplify_change = false;

View file

@ -69,10 +69,16 @@ namespace q {
m_mam->add_node(n, false);
};
ctx.get_egraph().set_on_merge(_on_merge);
ctx.get_egraph().set_on_make(_on_make);
if (!ctx.relevancy_enabled())
ctx.get_egraph().set_on_make(_on_make);
m_mam = mam::mk(ctx, *this);
}
void ematch::relevant_eh(euf::enode* n) {
if (ctx.relevancy_enabled())
m_mam->add_node(n, false);
}
void ematch::ensure_ground_enodes(expr* e) {
mam::ground_subterms(e, m_ground);
for (expr* g : m_ground)
@ -352,7 +358,7 @@ namespace q {
if (m_prop_queue.empty())
return false;
for (unsigned i = 0; i < m_prop_queue.size(); ++i) {
auto [is_conflict, idx, j_idx] = m_prop_queue[i];
auto const& [is_conflict, idx, j_idx] = m_prop_queue[i];
propagate(is_conflict, idx, j_idx);
}
m_prop_queue.reset();
@ -581,7 +587,7 @@ namespace q {
return m_inst_queue.propagate() || propagated;
ctx.push(value_trail<unsigned>(m_qhead));
ptr_buffer<binding> to_remove;
for (; m_qhead < m_clause_queue.size(); ++m_qhead) {
for (; m_qhead < m_clause_queue.size() && m.inc(); ++m_qhead) {
unsigned idx = m_clause_queue[m_qhead];
clause& c = *m_clauses[idx];
binding* b = c.m_bindings;

View file

@ -135,9 +135,10 @@ namespace q {
bool unit_propagate();
void add(quantifier* q);
void relevant_eh(euf::enode* n);
void collect_statistics(statistics& st) const;
void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector& r, bool probing);

View file

@ -424,15 +424,19 @@ namespace q {
friend class compiler;
friend class code_tree_manager;
void display_seq(std::ostream & out, instruction * head, unsigned indent) const {
for (unsigned i = 0; i < indent; i++) {
void spaces(std::ostream& out, unsigned indent) const {
for (unsigned i = 0; i < indent; i++)
out << " ";
}
}
void display_seq(std::ostream & out, instruction * head, unsigned indent) const {
spaces(out, indent);
instruction * curr = head;
out << *curr;
curr = curr->m_next;
while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) {
out << "\n";
spaces(out, indent);
out << *curr;
curr = curr->m_next;
}
@ -598,7 +602,7 @@ namespace q {
ast_manager & m = m_egraph->get_manager();
out << "patterns:\n";
for (auto [q, a] : m_patterns)
out << mk_pp(q, m) << " " << mk_pp(a, m) << "\n";
out << q->get_id() << ": " << mk_pp(q, m) << " " << mk_pp(a, m) << "\n";
}
#endif
out << "function: " << m_root_lbl->get_name();
@ -2878,8 +2882,10 @@ namespace q {
if (tree->expected_num_args() == p->get_num_args())
m_compiler.insert(tree, qa, mp, first_idx, false);
}
DEBUG_CODE(m_trees[lbl_id]->get_patterns().push_back(std::make_pair(qa, mp));
ctx.push(push_back_trail<std::pair<quantifier*,app*>, false>(m_trees[lbl_id]->get_patterns())););
DEBUG_CODE(if (first_idx == 0) {
m_trees[lbl_id]->get_patterns().push_back(std::make_pair(qa, mp));
ctx.push(push_back_trail<std::pair<quantifier*, app*>, false>(m_trees[lbl_id]->get_patterns()));
});
TRACE("trigger_bug", tout << "after add_pattern, first_idx: " << first_idx << "\n"; m_trees[lbl_id]->display(tout););
}

View file

@ -51,7 +51,7 @@ namespace q {
m_instantiations.reset();
for (sat::literal lit : m_qs.m_universal) {
quantifier* q = to_quantifier(ctx.bool_var2expr(lit.var()));
if (!ctx.is_relevant(q))
if (!ctx.is_relevant(lit.var()))
continue;
init_model();
switch (check_forall(q)) {
@ -67,11 +67,10 @@ namespace q {
}
}
m_max_cex += ctx.get_config().m_mbqi_max_cexs;
for (auto [qlit, fml, generation] : m_instantiations) {
for (auto const& [qlit, fml, generation] : m_instantiations) {
euf::solver::scoped_generation sg(ctx, generation + 1);
sat::literal lit = ctx.mk_literal(fml);
m_qs.add_clause(~qlit, ~lit);
ctx.add_root(~qlit, ~lit);
}
m_instantiations.reset();
return result;
@ -130,7 +129,7 @@ namespace q {
r = n;
}
else if (n->generation() == gen) {
if ((++count) % m_qs.random() == 0)
if ((m_qs.random() % ++count) == 0)
r = n;
}
if (count > m_max_choose_candidates)
@ -309,8 +308,10 @@ namespace q {
proj.extract_literals(*m_model, vars, fmls);
fmls_extracted = true;
}
if (p)
(*p)(*m_model, vars, fmls);
if (!p)
continue;
if (!(*p)(*m_model, vars, fmls))
return expr_ref(nullptr, m);
}
for (app* v : vars) {
expr_ref term(m);

View file

@ -66,7 +66,7 @@ namespace q {
ptr_vector<quantifier> univ;
for (sat::literal lit : m_qs.universal()) {
quantifier* q = to_quantifier(ctx.bool_var2expr(lit.var()));
if (ctx.is_relevant(q))
if (ctx.is_relevant(lit.var()))
univ.push_back(q);
}
if (univ.empty())
@ -256,7 +256,7 @@ namespace q {
tout << "invert-app " << mk_pp(t, m) << " = " << mk_pp(value, m) << "\n";
if (v2r.find(value, r))
tout << "inverse " << mk_pp(r->get_expr(), m) << "\n";
ctx.display(tout);
/*ctx.display(tout); */
);
if (v2r.find(value, r))
return r->get_expr();

View file

@ -149,12 +149,7 @@ namespace q {
if (false && em.propagate(true, f.nodes(), gen, *f.c, new_propagation))
return;
#if 0
std::cout << mk_pp(q, m) << "\n";
std::cout << num_bindings << "\n";
for (unsigned i = 0; i < num_bindings; ++i)
std::cout << mk_pp(f[i]->get_expr(), m) << " " << mk_pp(f[i]->get_sort(), m) << "\n";
#endif
auto* ebindings = m_subst(q, num_bindings);
for (unsigned i = 0; i < num_bindings; ++i)
ebindings[i] = f[i]->get_expr();
@ -168,7 +163,15 @@ namespace q {
m_stats.m_num_instances++;
// f.display(ctx, std::cout << mk_pp(f.q(), m) << "\n" << instance << "\n") << "\n";
#if 0
std::cout << "instantiate\n";
for (unsigned i = 0; i < num_bindings; ++i)
std::cout << ctx.bpp(f[i]) << " ";
std::cout << "\n";
std::cout << mk_pp(q, m) << "\n";
#endif
// f.display(ctx, std::cout << mk_pp(f.q(), m) << "\n" << instance << "\n") << "\n";
euf::solver::scoped_generation _sg(ctx, gen);

View file

@ -44,19 +44,16 @@ namespace q {
if (l.sign() == is_forall(e)) {
sat::literal lit = skolemize(q);
add_clause(~l, lit);
ctx.add_root(~l, lit);
}
else if (expand(q)) {
for (expr* e : m_expanded) {
sat::literal lit = ctx.internalize(e, l.sign(), false, false);
add_clause(~l, lit);
ctx.add_root(~l, lit);
}
}
else if (is_ground(q->get_expr())) {
auto lit = ctx.internalize(q->get_expr(), l.sign(), false, false);
add_clause(~l, lit);
ctx.add_root(~l, lit);
}
else {
ctx.push_vec(m_universal, l);

View file

@ -83,6 +83,7 @@ namespace q {
void init_search() override;
void finalize_model(model& mdl) override;
bool is_shared(euf::theory_var v) const override { return true; }
void relevant_eh(euf::enode* n) override { m_ematch.relevant_eh(n); }
ast_manager& get_manager() { return m; }
sat::literal_vector const& universal() const { return m_universal; }

View file

@ -68,8 +68,8 @@ namespace recfun {
TRACEFN("case expansion " << e);
SASSERT(e.m_def->is_fun_macro());
auto & vars = e.m_def->get_vars();
auto lhs = e.m_lhs;
auto rhs = apply_args(vars, e.m_args, e.m_def->get_rhs());
app_ref lhs = e.m_lhs;
expr_ref rhs = apply_args(vars, e.m_args, e.m_def->get_rhs());
unsigned generation = std::max(ctx.get_max_generation(lhs), ctx.get_max_generation(rhs));
euf::solver::scoped_generation _sgen(ctx, generation + 1);
auto eq = eq_internalize(lhs, rhs);

View file

@ -1,185 +0,0 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
sat_dual_solver.cpp
Abstract:
Solver for obtaining implicant.
Author:
Nikolaj Bjorner (nbjorner) 2020-08-25
--*/
#include "sat/smt/sat_dual_solver.h"
namespace sat {
dual_solver::dual_solver(reslimit& l):
m_solver(m_params, l)
{
SASSERT(!m_solver.get_config().m_drat);
m_solver.set_incremental(true);
}
void dual_solver::flush() {
while (m_num_scopes > 0) {
m_solver.user_push();
m_roots.push_scope();
m_tracked_vars.push_scope();
m_units.push_scope();
m_vars.push_scope();
--m_num_scopes;
}
}
void dual_solver::push() {
++m_num_scopes;
}
void dual_solver::pop(unsigned num_scopes) {
if (m_num_scopes >= num_scopes) {
m_num_scopes -= num_scopes;
return;
}
num_scopes -= m_num_scopes;
m_num_scopes = 0;
m_solver.user_pop(num_scopes);
unsigned old_sz = m_tracked_vars.old_size(num_scopes);
for (unsigned i = m_tracked_vars.size(); i-- > old_sz; )
m_is_tracked[m_tracked_vars[i]] = false;
old_sz = m_vars.old_size(num_scopes);
for (unsigned i = m_vars.size(); i-- > old_sz; ) {
unsigned v = m_vars[i];
unsigned w = m_ext2var[v];
m_ext2var[v] = null_bool_var;
m_var2ext[w] = null_bool_var;
}
m_vars.pop_scope(num_scopes);
m_units.pop_scope(num_scopes);
m_roots.pop_scope(num_scopes);
m_tracked_vars.pop_scope(num_scopes);
}
bool_var dual_solver::ext2var(bool_var v) {
bool_var w = m_ext2var.get(v, null_bool_var);
if (null_bool_var == w) {
w = m_solver.mk_var();
m_solver.set_external(w);
m_ext2var.setx(v, w, null_bool_var);
m_var2ext.setx(w, v, null_bool_var);
m_vars.push_back(v);
}
return w;
}
void dual_solver::track_relevancy(bool_var w) {
flush();
bool_var v = ext2var(w);
if (!m_is_tracked.get(v, false)) {
m_is_tracked.setx(v, true, false);
m_tracked_vars.push_back(v);
}
}
literal dual_solver::ext2lit(literal lit) {
return literal(ext2var(lit.var()), lit.sign());
}
literal dual_solver::lit2ext(literal lit) {
return literal(m_var2ext[lit.var()], lit.sign());
}
void dual_solver::add_root(unsigned sz, literal const* clause) {
flush();
if (false && sz == 1) {
TRACE("dual", tout << "unit: " << clause[0] << "\n";);
m_units.push_back(clause[0]);
return;
}
literal root;
if (sz == 1)
root = ext2lit(clause[0]);
else {
root = literal(m_solver.mk_var(), false);
for (unsigned i = 0; i < sz; ++i)
m_solver.mk_clause(root, ~ext2lit(clause[i]), status::input());
}
m_solver.set_external(root.var());
m_roots.push_back(~root);
TRACE("dual", tout << "root: " << ~root << " => " << literal_vector(sz, clause) << "\n";);
}
void dual_solver::add_aux(unsigned sz, literal const* clause) {
flush();
m_lits.reset();
for (unsigned i = 0; i < sz; ++i)
m_lits.push_back(ext2lit(clause[i]));
TRACE("dual", tout << "aux: " << literal_vector(sz, clause) << " -> " << m_lits << "\n";);
m_solver.mk_clause(sz, m_lits.data(), status::input());
}
void dual_solver::add_assumptions(solver const& s) {
flush();
m_lits.reset();
for (bool_var v : m_tracked_vars)
m_lits.push_back(literal(v, l_false == s.value(m_var2ext[v])));
for (auto lit : m_units) {
bool_var w = m_ext2var.get(lit.var(), null_bool_var);
if (w != null_bool_var)
m_lits.push_back(ext2lit(lit));
}
}
bool dual_solver::operator()(solver const& s) {
m_core.reset();
m_core.append(m_units);
if (m_roots.empty())
return true;
m_solver.user_push();
m_solver.add_clause(m_roots.size(), m_roots.data(), status::input());
add_assumptions(s);
lbool is_sat = m_solver.check(m_lits.size(), m_lits.data());
if (is_sat == l_false)
for (literal lit : m_solver.get_core())
m_core.push_back(lit2ext(lit));
if (is_sat == l_true) {
TRACE("dual", display(s, tout); s.display(tout););
IF_VERBOSE(0, verbose_stream() << "unexpected SAT\n");
UNREACHABLE();
return false;
}
TRACE("dual", m_solver.display(tout << "is-sat: " << is_sat << " core: " << m_core << "\n"););
m_solver.user_pop(1);
return is_sat == l_false;
}
std::ostream& dual_solver::display(solver const& s, std::ostream& out) const {
for (unsigned v = 0; v < m_solver.num_vars(); ++v) {
bool_var w = m_var2ext.get(v, null_bool_var);
if (w == null_bool_var)
continue;
lbool v1 = m_solver.value(v);
lbool v2 = s.value(w);
if (v1 == v2 || v2 == l_undef)
continue;
out << "ext: " << w << " " << v2 << "\n";
out << "int: " << v << " " << v1 << "\n";
}
literal_vector lits;
for (bool_var v : m_tracked_vars)
lits.push_back(literal(m_var2ext[v], l_false == s.value(m_var2ext[v])));
out << "tracked: " << lits << "\n";
lits.reset();
for (literal r : m_roots)
if (m_solver.value(r) == l_true)
lits.push_back(r);
out << "roots: " << lits << "\n";
m_solver.display(out);
return out;
}
}

View file

@ -1,83 +0,0 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
sat_dual_solver.h
Abstract:
Solver for obtaining implicant.
Based on an idea by Armin Biere to use dual propagation
for representation of negated goal.
Author:
Nikolaj Bjorner (nbjorner) 2020-08-25
--*/
#pragma once
#include "util/lim_vector.h"
#include "sat/sat_solver.h"
namespace sat {
class dual_solver {
class dual_params : public no_drat_params {
public:
dual_params() {
set_bool("core.minimize", false);
}
};
dual_params m_params;
solver m_solver;
lim_svector<literal> m_units, m_roots;
lim_svector<bool_var> m_tracked_vars;
literal_vector m_lits, m_core;
bool_var_vector m_is_tracked;
unsigned_vector m_ext2var;
unsigned_vector m_var2ext;
lim_svector<unsigned> m_vars;
unsigned m_num_scopes = 0;
void add_literal(literal lit);
bool_var ext2var(bool_var v);
bool_var var2ext(bool_var v);
literal ext2lit(literal lit);
literal lit2ext(literal lit);
void add_assumptions(solver const& s);
std::ostream& display(solver const& s, std::ostream& out) const;
void flush();
public:
dual_solver(reslimit& l);
void push();
void pop(unsigned num_scopes);
/*
* track model relevancy for variable
*/
void track_relevancy(bool_var v);
/*
* Add a root clause from the input problem.
* At least one literal has to be satisfied in every root.
*/
void add_root(unsigned sz, literal const* clause);
/*
* Add auxiliary clauses that originate from compiling definitions.
*/
void add_aux(unsigned sz, literal const* clause);
/*
* Extract a minimized subset of relevant literals from a model for s.
*/
bool operator()(solver const& s);
literal_vector const& core() const { return m_core; }
};
}

View file

@ -132,6 +132,7 @@ namespace euf {
bool th_euf_solver::add_unit(sat::literal lit) {
bool was_true = is_true(lit);
ctx.s().add_clause(1, &lit, mk_status());
ctx.add_root(lit);
return !was_true;
}
@ -143,32 +144,27 @@ namespace euf {
return is_new;
}
bool th_euf_solver::add_clause(sat::literal a, sat::literal b) {
bool was_true = is_true(a, b);
bool th_euf_solver::add_clause(sat::literal a, sat::literal b) {
sat::literal lits[2] = { a, b };
ctx.s().add_clause(2, lits, mk_status());
return !was_true;
return add_clause(2, lits);
}
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) {
bool was_true = is_true(a, b, c);
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) {
sat::literal lits[3] = { a, b, c };
ctx.s().add_clause(3, lits, mk_status());
return !was_true;
return add_clause(3, lits);
}
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d) {
bool was_true = is_true(a, b, c, d);
sat::literal lits[4] = { a, b, c, d };
ctx.s().add_clause(4, lits, mk_status());
return !was_true;
return add_clause(4, lits);
}
bool th_euf_solver::add_clause(sat::literal_vector const& lits) {
bool th_euf_solver::add_clause(unsigned n, sat::literal* lits) {
bool was_true = false;
for (auto lit : lits)
was_true |= is_true(lit);
s().add_clause(lits.size(), lits.data(), mk_status());
for (unsigned i = 0; i < n; ++i)
was_true |= is_true(lits[i]);
ctx.add_root(n, lits);
s().add_clause(n, lits, mk_status());
return !was_true;
}

View file

@ -111,6 +111,10 @@ namespace euf {
virtual void new_diseq_eh(euf::th_eq const& eq) {}
virtual bool enable_ackerman_axioms(euf::enode* n) const { return true; }
virtual void relevant_eh(euf::enode* n) {}
/**
\brief Parametric theories (e.g. Arrays) should implement this method.
*/
@ -139,7 +143,8 @@ namespace euf {
bool add_clause(sat::literal a, sat::literal b);
bool add_clause(sat::literal a, sat::literal b, sat::literal c);
bool add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d);
bool add_clause(sat::literal_vector const& lits);
bool add_clause(sat::literal_vector const& lits) { return add_clause(lits.size(), lits.data()); }
bool add_clause(unsigned n, sat::literal* lits);
void add_equiv(sat::literal a, sat::literal b);
void add_equiv_and(sat::literal a, sat::literal_vector const& bs);

View file

@ -125,20 +125,6 @@ struct goal2sat::imp : public sat::sat_internalizer {
bool top_level_relevant() {
return m_top_level && relevancy_enabled();
}
void add_dual_def(unsigned n, sat::literal const* lits) {
if (relevancy_enabled())
ensure_euf()->add_aux(n, lits);
}
void add_dual_root(unsigned n, sat::literal const* lits) {
if (relevancy_enabled())
ensure_euf()->add_root(n, lits);
}
void add_dual_root(sat::literal lit) {
add_dual_root(1, &lit);
}
void mk_clause(sat::literal l) {
mk_clause(1, &l);
@ -156,7 +142,8 @@ struct goal2sat::imp : public sat::sat_internalizer {
void mk_clause(unsigned n, sat::literal * lits) {
TRACE("goal2sat", tout << "mk_clause: "; for (unsigned i = 0; i < n; i++) tout << lits[i] << " "; tout << "\n";);
add_dual_def(n, lits);
if (relevancy_enabled())
ensure_euf()->add_aux(n, lits);
m_solver.add_clause(n, lits, mk_status());
}
@ -176,7 +163,8 @@ struct goal2sat::imp : public sat::sat_internalizer {
void mk_root_clause(unsigned n, sat::literal * lits) {
TRACE("goal2sat", tout << "mk_root_clause: "; for (unsigned i = 0; i < n; i++) tout << lits[i] << " "; tout << "\n";);
add_dual_root(n, lits);
if (relevancy_enabled())
ensure_euf()->add_root(n, lits);
m_solver.add_clause(n, lits, m_is_redundant ? mk_status() : sat::status::input());
}
@ -186,8 +174,6 @@ struct goal2sat::imp : public sat::sat_internalizer {
return v;
v = m_solver.add_var(is_ext);
log_def(v, n);
if (top_level_relevant() && !is_bool_op(n))
ensure_euf()->track_relevancy(v);
return v;
}
@ -216,14 +202,11 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (!m_expr2var_replay || !m_expr2var_replay->find(t, v))
v = add_var(true, t);
m_map.insert(t, v);
if (relevancy_enabled() && (m.is_true(t) || m.is_false(t))) {
add_dual_root(sat::literal(v, m.is_false(t)));
ensure_euf()->track_relevancy(v);
}
return v;
}
sat::bool_var add_bool_var(expr* t) override {
force_push();
sat::bool_var v = m_map.to_bool_var(t);
if (v == sat::null_bool_var)
v = mk_bool_var(t);
@ -238,11 +221,11 @@ struct goal2sat::imp : public sat::sat_internalizer {
for (; m_num_scopes > 0; --m_num_scopes) {
m_map.push();
m_cache_lim.push_back(m_cache_trail.size());
}
}
}
void push() override {
++m_num_scopes;
++m_num_scopes;
}
void pop(unsigned n) override {
@ -263,7 +246,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
}
}
m_cache_trail.shrink(k);
m_cache_lim.shrink(m_cache_lim.size() - n);
m_cache_lim.shrink(m_cache_lim.size() - n);
}
// remove non-external literals from cache.
@ -677,8 +660,6 @@ struct goal2sat::imp : public sat::sat_internalizer {
}
if (lit == sat::null_literal)
return;
if (top_level_relevant())
euf->track_relevancy(lit.var());
if (root)
mk_root_clause(lit);
else

View file

@ -201,8 +201,8 @@ public:
char const* name() const override { return "sat"; }
void updt_params(params_ref const & p) override {
m_params = p;
if (m_imp) m_imp->updt_params(p);
m_params.append(p);
if (m_imp) m_imp->updt_params(m_params);
}
void collect_param_descrs(param_descrs & r) override {

View file

@ -226,6 +226,7 @@ namespace smt {
ebindings[i] = bindings[i]->get_expr();
expr_ref instance = m_subst();
TRACE("qi_queue", tout << "new instance:\n" << mk_pp(instance, m) << "\n";);
TRACE("qi_queue_instance", tout << "new instance:\n" << mk_pp(instance, m) << "\n";);
expr_ref s_instance(m);
@ -244,6 +245,15 @@ namespace smt {
return;
}
#if 0
std::cout << "instantiate\n";
enode_vector _bindings(num_bindings, bindings);
for (auto * b : _bindings)
std::cout << enode_pp(b, m_context) << " ";
std::cout << "\n";
std::cout << mk_pp(q, m) << "\n";
#endif
TRACE("qi_queue", tout << "simplified instance:\n" << s_instance << "\n";);
stat->inc_num_instances();
if (stat->get_num_instances() % m_params.m_qi_profile_freq == 0) {

View file

@ -73,6 +73,7 @@ namespace smt {
void add_indexof_axiom(expr* n) { m_ax.indexof_axiom(n); }
void add_last_indexof_axiom(expr* n) { m_ax.last_indexof_axiom(n); }
void add_replace_axiom(expr* n) { m_ax.replace_axiom(n); }
void add_replace_all_axiom(expr* n) { m_ax.replace_all_axiom(n); }
void add_at_axiom(expr* n) { m_ax.at_axiom(n); }
void add_nth_axiom(expr* n) { m_ax.nth_axiom(n); }
void add_itos_axiom(expr* n) { m_ax.itos_axiom(n); }

View file

@ -158,7 +158,6 @@ namespace smt {
void mark_as_interpreted() {
SASSERT(!m_interpreted);
SASSERT(m_owner->get_num_args() == 0);
SASSERT(m_class_size == 1);
m_interpreted = true;
}

View file

@ -974,7 +974,7 @@ namespace smt {
}
enode * e = enode::mk(m, m_region, m_app2enode, n, generation, suppress_args, merge_tf, m_scope_lvl, cgc_enabled, true);
TRACE("mk_enode_detail", tout << "e.get_num_args() = " << e->get_num_args() << "\n";);
if (n->get_num_args() == 0 && m.is_unique_value(n))
if (m.is_unique_value(n))
e->mark_as_interpreted();
TRACE("mk_var_bug", tout << "mk_enode: " << id << "\n";);
TRACE("generation", tout << "mk_enode: " << id << " " << generation << "\n";);

View file

@ -307,13 +307,13 @@ namespace smt {
/**
\brief Return a reference to smt::context.
This is a temporary hack to support user theories.
TODO: remove this hack.
We need to revamp user theories too.
This breaks abstractions.
It is currently used by the opt-solver
to access optimization services from arithmetic solvers
and to ensure that the solver has registered PB theory solver.
This method breaks the abstraction barrier.
\warning We should not use this method
\warning This method should not be used in new code.
*/
context & get_context();
};

View file

@ -134,11 +134,11 @@ namespace smt {
obj_map<expr, relevancy_ehs *> m_relevant_ehs;
obj_map<expr, relevancy_ehs *> m_watches[2];
struct eh_trail {
enum kind { POS_WATCH, NEG_WATCH, HANDLER };
enum class kind { POS_WATCH, NEG_WATCH, HANDLER };
kind m_kind;
expr * m_node;
eh_trail(expr * n):m_kind(HANDLER), m_node(n) {}
eh_trail(expr * n, bool val):m_kind(val ? POS_WATCH : NEG_WATCH), m_node(n) {}
eh_trail(expr * n):m_kind(kind::HANDLER), m_node(n) {}
eh_trail(expr * n, bool val):m_kind(val ? kind::POS_WATCH : kind::NEG_WATCH), m_node(n) {}
kind get_kind() const { return m_kind; }
expr * get_node() const { return m_node; }
};
@ -292,9 +292,9 @@ namespace smt {
expr * n = t.get_node();
relevancy_ehs * ehs;
switch (t.get_kind()) {
case eh_trail::POS_WATCH: ehs = get_watches(n, true); SASSERT(ehs); set_watches(n, true, ehs->tail()); break;
case eh_trail::NEG_WATCH: ehs = get_watches(n, false); SASSERT(ehs); set_watches(n, false, ehs->tail()); break;
case eh_trail::HANDLER: ehs = get_handlers(n); SASSERT(ehs); set_handlers(n, ehs->tail()); break;
case eh_trail::kind::POS_WATCH: ehs = get_watches(n, true); SASSERT(ehs); set_watches(n, true, ehs->tail()); break;
case eh_trail::kind::NEG_WATCH: ehs = get_watches(n, false); SASSERT(ehs); set_watches(n, false, ehs->tail()); break;
case eh_trail::kind::HANDLER: ehs = get_handlers(n); SASSERT(ehs); set_handlers(n, ehs->tail()); break;
default: UNREACHABLE(); break;
}
m.dec_ref(n);
@ -378,9 +378,7 @@ namespace smt {
break;
case l_true: {
expr * true_arg = nullptr;
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = n->get_arg(i);
for (expr* arg : *n) {
if (m_context.find_assignment(arg) == l_true) {
if (is_relevant_core(arg))
return;
@ -402,9 +400,7 @@ namespace smt {
switch (val) {
case l_false: {
expr * false_arg = nullptr;
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = n->get_arg(i);
for (expr* arg : *n) {
if (m_context.find_assignment(arg) == l_false) {
if (is_relevant_core(arg))
return;

View file

@ -125,7 +125,7 @@ namespace {
smt_params m_smt_params_save;
void push_params() override {
m_params_save = params_ref();
m_params_save.reset();
m_params_save.copy(solver::get_params());
m_smt_params_save = m_smt_params;
}

View file

@ -127,7 +127,8 @@ public:
scoped_init_ctx(smt_tactic & o, ast_manager & m):m_owner(o) {
m_params = o.fparams();
m_params_ref = o.params();
m_params_ref.reset();
m_params_ref.append(o.params());
smt::kernel * new_ctx = alloc(smt::kernel, m, m_params, m_params_ref);
TRACE("smt_tactic", tout << "logic: " << o.m_logic << "\n";);
new_ctx->set_logic(o.m_logic);

View file

@ -50,7 +50,7 @@ struct unit_subsumption_tactic : public tactic {
}
void updt_params(params_ref const& p) override {
m_params = p;
m_params.append(p);
// m_context.updt_params(p); does not exist.
}

View file

@ -2646,6 +2646,9 @@ void theory_seq::deque_axiom(expr* n) {
else if (m_util.str.is_replace(n)) {
m_ax.add_replace_axiom(n);
}
else if (m_util.str.is_replace_all(n)) {
m_ax.add_replace_all_axiom(n);
}
else if (m_util.str.is_extract(n)) {
m_ax.add_extract_axiom(n);
}
@ -3190,6 +3193,7 @@ void theory_seq::relevant_eh(app* n) {
m_util.str.is_to_code(n) ||
m_util.str.is_unit(n) ||
m_util.str.is_length(n) ||
/* m_util.str.is_replace_all(n) || uncomment to enable axiomatization */
m_util.str.is_le(n)) {
enque_axiom(n);
}

View file

@ -20,7 +20,10 @@ Notes:
void check_sat_result::set_reason_unknown(event_handler& eh) {
switch (eh.caller_id()) {
case UNSET_EH_CALLER: break;
case UNSET_EH_CALLER:
if (reason_unknown() == "")
set_reason_unknown("unclassifed exception");
break;
case CTRL_C_EH_CALLER:
set_reason_unknown("interrupted from keyboard");
break;

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