diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml new file mode 100644 index 000000000..74eb04d15 --- /dev/null +++ b/.github/workflows/Windows.yml @@ -0,0 +1,64 @@ +name: Windows + +on: + push: + branches: [ master ] + +jobs: + build: + strategy: + matrix: + arch : [x86,x64,amd64_arm64] + include: + - arch : x86 + - arch : amd64_arm64 + - arch : x64 + cmd1 : 'julia -e "using Pkg; Pkg.add(PackageSpec(name=\"libcxxwrap_julia_jll\"))"' + cmd2 : 'julia -e "using libcxxwrap_julia_jll; print(dirname(libcxxwrap_julia_jll.libcxxwrap_julia_path))" > tmp.env' + cmd3 : 'set /P JlCxxDir=> $GITHUB_OUTPUT - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: coverage-${{steps.date.outputs.date}} path: ${{github.workspace}}/coverage.html retention-days: 4 - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: coverage-details-${{steps.date.outputs.date}} path: ${{env.COV_DETAILS_PATH}} diff --git a/.github/workflows/wasm-release.yml b/.github/workflows/wasm-release.yml index f437fcc5f..ed5dacec8 100644 --- a/.github/workflows/wasm-release.yml +++ b/.github/workflows/wasm-release.yml @@ -36,7 +36,7 @@ jobs: cp ../../../LICENSE.txt . - name: Setup emscripten - uses: mymindstorm/setup-emsdk@v13 + uses: mymindstorm/setup-emsdk@v14 with: no-install: true version: ${{env.EM_VERSION}} diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 320f36817..8e157f5a4 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -29,7 +29,7 @@ jobs: node-version: "lts/*" - name: Setup emscripten - uses: mymindstorm/setup-emsdk@v13 + uses: mymindstorm/setup-emsdk@v14 with: no-install: true version: ${{env.EM_VERSION}} diff --git a/CMakeLists.txt b/CMakeLists.txt index 60531ca94..39f89ae74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") -project(Z3 VERSION 4.12.5.0 LANGUAGES CXX) +project(Z3 VERSION 4.12.6.0 LANGUAGES CXX) ################################################################################ # Project version diff --git a/README.md b/README.md index 230b3eeeb..dfbaea599 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ See the [release notes](RELEASE_NOTES.md) for notes on various stable releases o ## Build status -| 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) | +| Azure Pipelines | Code Coverage | Open Bugs | Android Build | WASM Build | Windows 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) | [![Windows](https://github.com/Z3Prover/z3/actions/workflows/Windows.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/Windows.yml) Docker image. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a10af72a6..9b041c07e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,14 +10,25 @@ Version 4.next - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. +Version 4.12.6 +============== +- remove expensive rewrite that coalesces adjacent stores +- improved Java use of reference queues thanks to Thomas Haas #7131 +- fixes to conditional import of python library thanks to Cal Jacobson #7116 +- include universe for constants that get removed during pre-processing #7121 +- code improvements, Bruce Mitchener #7119 +- fix nested callback handling for user propagators +- include ARM64 binaries in distribution +- added Julia API, Thanks to Yisu Remy Yang #7108 + Version 4.12.5 ============== - Fixes to pypi setup and build for MacOS distributions - A new theory solver "int-blast" enabled by using: - sat.smt=true smt.bv.solver=2 - It solves a few bit-vector problems not handled by bit-blasting, especially if the bit-widths are large. - - It is based on encoding bit-vector constraints to non-linear integer arithemtic. - + - It is based on encoding bit-vector constraints to non-linear integer arithmetic. +- Optimizations to the arithmetic solver. Description: https://github.com/Z3Prover/doc/tree/master/arithmetic Version 4.12.4 ============== diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 753c9ae9c..d9d2ab2b2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -183,63 +183,6 @@ jobs: - template: scripts/test-regressions.yml -- job: "WindowsLatest" - displayName: "Windows" - pool: - vmImage: "windows-latest" - strategy: - matrix: - x86: - arch: 'x86' - setupCmd1: '' - setupCmd2: '' - setupCmd3: '' - bindings: '$(cmakePy)' - runTests: 'False' - x64: - arch: 'x64' - setupCmd1: 'julia -e "using Pkg; Pkg.add(PackageSpec(name=\"libcxxwrap_julia_jll\"))"' - setupCmd2: 'julia -e "using libcxxwrap_julia_jll; print(dirname(libcxxwrap_julia_jll.libcxxwrap_julia_path))" > tmp.env' - setupCmd3: 'set /P JlCxxDir= fruit = ctx.mkEnumSort(name, ctx.mkSymbol("apple"), - ctx.mkSymbol("banana"), ctx.mkSymbol("orange")); + EnumSort fruit = ctx.mkEnumSort(name, ctx.mkSymbol("apple2"), + ctx.mkSymbol("banana2"), ctx.mkSymbol("orange2")); System.out.println((fruit.getConsts()[0])); System.out.println((fruit.getConsts()[1])); diff --git a/examples/ml/ml_example.ml b/examples/ml/ml_example.ml index e2c7734ff..318c805e8 100644 --- a/examples/ml/ml_example.ml +++ b/examples/ml/ml_example.ml @@ -308,6 +308,7 @@ let fpa_example ( ctx : context ) = ( let solver = (mk_solver ctx None) in (Solver.add solver [ c5 ]) ; + Printf.printf "Memory in use before `check`: %Lu bytes\n" (Statistics.get_estimated_alloc_size()); if (check solver []) != SATISFIABLE then raise (TestFailedException "") else diff --git a/examples/python/bincover.py b/examples/python/bincover.py new file mode 100644 index 000000000..d8a81c25a --- /dev/null +++ b/examples/python/bincover.py @@ -0,0 +1,382 @@ +from z3 import * +import math + +# Rudimentary bin cover solver using the UserPropagator feature. +# It supports the most basic propagation for bin covering. +# - each bin has a propositional variable set to true if the bin is covered +# - each item has a bit-vector recording the assigned bin +# It searches for a locally optimal solution. + +class Bin: + """ + Each bin carries values: + - min_bound - the lower bound required to be added to bin + - weight - the sum of weight of items currently added to bin + - slack - the difference between the maximal possible assignment and the assignments to other bin2bound. + - var - is propagated to true/false if the bin gets filled/cannot be filled. + """ + def __init__(self, min_bound, index): + assert min_bound > 0 + assert index >= 0 + self.index = index + self.min_bound = min_bound + self.weight = 0 + self.slack = 0 + self.added = [] + self.var = Bool(f"bin-{index}") + + def set_slack(self, slack): + self.slack = slack + + def set_fill(self, fill): + self.weight = fill + + def __repr__(self): + return f"{self.var}:bound-{self.min_bound}" + + +class Item: + def __init__(self, weight, index): + self.weight = weight + self.index = index + self.var = None + + def set_var(self, num_bits): + self.var = BitVec(f"binof-{self.index}", num_bits) + + def __repr__(self): + return f"binof-{self.index}:weight-{self.weight}" + +class BranchAndBound: + """Branch and Bound solver. + It keeps track of a current best score and a slack that tracks bins that are set unfilled. + It blocks branches that are worse than the current best score. + In Final check it blocks the current assignment. + """ + def __init__(self, user_propagator): + self.up = user_propagator + + def init(self, soft_literals): + self.value = 0 + self.best = 0 + self.slack = 0 + self.id2weight = {} + self.assigned_to_false = [] + for p, weight in soft_literals: + self.slack += weight + self.id2weight[p.get_id()] = weight + + def fixed(self, p, value): + weight = self.id2weight[p.get_id()] + if is_true(value): + old_value = self.value + self.up.trail += [lambda : self._undo_value(old_value)] + self.value += weight + elif self.best > self.slack - weight: + self.assigned_to_false += [ p ] + self.up.conflict(self.assigned_to_false) + self.assigned_to_false.pop(-1) + else: + old_slack = self.slack + self.up.trail += [lambda : self._undo_slack(old_slack)] + self.slack -= weight + self.assigned_to_false += [p] + + def final(self): + if self.value > self.best: + self.best = self.value + print("Number of bins filled", self.value) + for bin in self.up.bins: + print(bin.var, bin.added) + self.up.conflict(self.assigned_to_false) + + def _undo_value(self, old_value): + self.value = old_value + + def _undo_slack(self, old_slack): + self.slack = old_slack + self.assigned_to_false.pop(-1) + +class BinCoverSolver(UserPropagateBase): + """Represent a bin-covering problem by associating each bin with a variable + For each item i associate a bit-vector + - bin-of-i that carries the bin identifier where an item is assigned. + + """ + + def __init__(self, s=None, ctx=None): + UserPropagateBase.__init__(self, s, ctx) + self.bins = [] + self.items = [] + self.item2index = {} + self.trail = [] # Undo stack + self.lim = [] + self.solver = s + self.initialized = False + self.add_fixed(lambda x, v : self._fixed(x, v)) + self.branch_and_bound = None + + + # Initialize bit-vector variables for items. + # Register the bit-vector variables with the user propagator to get callbacks + # Ensure the bit-vector variables are assigned to a valid bin. + # Initialize the slack of each bin. + def init(self): + print(self.bins, len(self.bins)) + print(self.items) + assert not self.initialized + self.initialized = True + powerof2, num_bits = self._num_bits() + for item in self.items: + item.set_var(num_bits) + self.item2index[item.var.get_id()] = item.index + self.add(item.var) + if not powerof2: + bound = BitVecVal(len(self.bins), num_bits) + ineq = ULT(item.var, bound) + self.solver.add(ineq) + total_weight = sum(item.weight for item in self.items) + for bin in self.bins: + bin.slack = total_weight + + # + # Register optional branch and bound weighted solver. + # If it is registered, it + def init_branch_and_bound(self): + soft = [(bin.var, 1) for bin in self.bins] + self.branch_and_bound = BranchAndBound(self) + self.branch_and_bound.init(soft) + for bin in self.bins: + self.add(bin.var) + self.add_final(lambda : self.branch_and_bound.final()) + + def add_bin(self, min_bound): + assert not self.initialized + index = len(self.bins) + bin = Bin(min_bound, index) + self.bins += [bin] + return bin + + def add_item(self, weight): + assert not self.initialized + assert weight > 0 + index = len(self.items) + item = Item(weight, index) + self.items += [item] + return item + + def num_items(self): + return len(self.items) + + def num_bins(self): + return len(self.bins) + + def _num_bits(self): + log = math.log2(self.num_bins()) + if log.is_integer(): + return True, int(log) + else: + return False, int(log) + 1 + + def _set_slack(self, bin, slack_value): + bin.slack = slack_value + + def _set_fill(self, bin, fill_value): + bin.weight = fill_value + bin.added.pop() + + def _itemvar2item(self, v): + index = self.item2index[v.get_id()] + if index >= len(self.items): + return None + return self.items[index] + + def _value2bin(self, value): + assert isinstance(value, BitVecNumRef) + bin_index = value.as_long() + if bin_index >= len(self.bins): + return NOne + return self.bins[bin_index] + + def _add_item2bin(self, item, bin): + # print("add", item, "to", bin) + old_weight = bin.weight + bin.weight += item.weight + bin.added += [item] + self.trail += [lambda : self._set_fill(bin, old_weight)] + if old_weight < bin.min_bound and old_weight + item.weight >= bin.min_bound: + self._propagate_filled(bin) + + # This item can never go into bin + def _exclude_item2bin(self, item, bin): + # print("exclude", item, "from", bin) + # Check if bin has already been blocked + if bin.slack < bin.min_bound: + return + if bin.weight >= bin.min_bound: + return + old_slack = bin.slack + new_slack = old_slack - item.weight + bin.slack = new_slack + self.trail += [lambda : self._set_slack(bin, old_slack)] + # If the new slack does not permit the bin to be filled, propagate + if new_slack < bin.min_bound: + self._propagate_slack(bin) + + + # Callback from Z3 when an item gets fixed. + def _fixed(self, _item, value): + if self.branch_and_bound and is_bool(value): + self.branch_and_bound.fixed(_item, value) + return + item = self._itemvar2item(_item) + if item is None: + print("no item for ", _item) + return + bin = self._value2bin(value) + if bin is None: + print("no bin for ", value) + return + self._add_item2bin(item, bin) + for idx in range(len(self.bins)): + if idx == bin.index: + continue + other_bin = self.bins[idx] + self._exclude_item2bin(item, other_bin) + + def _propagate_filled(self, bin): + """Propagate that bin_index is filled justified by the set of + items that have been added + """ + justification = [i.var for i in bin.added] + self.propagate(bin.var, justification) + + def _propagate_slack(self, bin): + """Propagate that bin_index cannot be filled""" + justification = [] + for other_bin in self.bins: + if other_bin.index == bin.index: + continue + justification += other_bin.added + justification = [item.var for item in justification] + self.propagate(Not(bin.var), justification) + + def push(self): + self.lim += [len(self.trail)] + + def pop(self, n): + head = self.lim[len(self.lim) - n] + while len(self.trail) > head: + self.trail[-1]() + self.trail.pop(-1) + self.lim = self.lim[0:len(self.lim)-n] + +# Find a first maximally satisfying subset +class MaximalSatisfyingSubset: + def __init__(self, s): + self.s = s + self.model = None + + def tt(self, f): + return is_true(self.model.eval(f)) + + def get_mss(self, ps): + s = self.s + if sat != s.check(): + return [] + self.model = s.model() + mss = { q for q in ps if self.tt(q) } + return self._get_mss(mss, ps) + + def _get_mss(self, mss, ps): + ps = set(ps) - mss + backbones = set([]) + s = self.s + while len(ps) > 0: + p = ps.pop() + if sat == s.check(mss | backbones | { p }): + self.model = s.model() + mss = mss | { p } | { q for q in ps if self.tt(q) } + ps = ps - mss + else: + backbones = backbones | { Not(p) } + return mss + + +class OptimizeBinCoverSolver: + def __init__(self): + self.solver = Solver() + self.bin_solver = BinCoverSolver(self.solver) + self.mss_solver = MaximalSatisfyingSubset(self.solver) + + # + # Facilities to set up solver + # First add items and bins. + # Keep references to the returned objects. + # Then call init + # Then add any other custom constraints to the "solver" object. + # + def init(self): + self.bin_solver.init() + + def add_item(self, weight): + return self.bin_solver.add_item(weight) + + def add_bin(self, min_bound): + return self.bin_solver.add_bin(min_bound) + + def optimize(self): + self.init() + mss = self.mss_solver.get_mss([bin.var for bin in self.bin_solver.bins]) + print(self.mss_solver.model) + print("filled bins", mss) + print("bin contents") + for bin in self.bin_solver.bins: + print(bin, bin.added) + + +def example1(): + s = OptimizeBinCoverSolver() + i1 = s.add_item(2) + i2 = s.add_item(4) + i3 = s.add_item(5) + i4 = s.add_item(2) + b1 = s.add_bin(3) + b2 = s.add_bin(6) + b3 = s.add_bin(1) + s.optimize() + +#example1() + + +class BranchAndBoundCoverSolver: + def __init__(self): + self.solver = Solver() + self.bin_solver = BinCoverSolver(self.solver) + + def init(self): + self.bin_solver.init() + self.bin_solver.init_branch_and_bound() + + def add_item(self, weight): + return self.bin_solver.add_item(weight) + + def add_bin(self, min_bound): + return self.bin_solver.add_bin(min_bound) + + def optimize(self): + self.init() + self.solver.check() + +def example2(): + s = BranchAndBoundCoverSolver() + i1 = s.add_item(2) + i2 = s.add_item(4) + i3 = s.add_item(5) + i4 = s.add_item(2) + b1 = s.add_bin(3) + b2 = s.add_bin(6) + b3 = s.add_bin(1) + s.optimize() + +example2() diff --git a/examples/python/proofreplay.py b/examples/python/proofreplay.py index c8c9ff47e..5c82f43a9 100644 --- a/examples/python/proofreplay.py +++ b/examples/python/proofreplay.py @@ -86,7 +86,7 @@ if __name__ == "__main__": # The pair -inst 2 indicates that two quantifier instantiations were not self-validated # They were instead validated using a call to SMT solving. A log for an smt invocation # is exemplified in the next line. - # Note that the pair +inst 6 indicates that 6 quantifier instantations were validated + # Note that the pair +inst 6 indicates that 6 quantifier instantiations were validated # using a syntactic (cheap) check. Some quantifier instantiations based on quantifier elimination # are not simple substitutions and therefore a simple syntactic check does not suffice. set_param("solver.proof.check", True) diff --git a/scripts/build-win-signed-cmake.yml b/scripts/build-win-signed-cmake.yml new file mode 100644 index 000000000..620379f0c --- /dev/null +++ b/scripts/build-win-signed-cmake.yml @@ -0,0 +1,99 @@ +parameters: + ReleaseVersion: '' + BuildArchitecture: '' + VCArchitecture: '' + +jobs: +- job: WindowsBuild${{parameters.BuildArchitecture}} + displayName: "Windows build (${{parameters.BuildArchitecture}})" + pool: + vmImage: "windows-latest" + steps: + - powershell: write-host $(System.TeamProjectId) + displayName: 'System.TeamProjectId' + - powershell: write-host $(System.DefinitionId) + displayName: 'System.DefinitionId' + - powershell: write-host $(Build.BuildId) + displayName: 'Build.BuildId' + - task: CmdLine@2 + displayName: Build + inputs: + script: + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{parameters.VCArchitecture}} & + python scripts\mk_win_dist_cmake.py + --${{parameters.BuildArchitecture}}-only + --assembly-version=${{parameters.ReleaseVersion}} + --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - task: CopyFiles@2 + displayName: 'Collect Symbols' + inputs: + sourceFolder: build-dist/${{parameters.BuildArchitecture}}/ + contents: '*.pdb' + targetFolder: '$(Build.ArtifactStagingDirectory)/symbols' + # Publish symbol archive to match nuget package + # Index your source code and publish symbols to a file share or Azure Artifacts symbol server + - task: PublishSymbols@2 + inputs: + symbolsFolder: '$(Build.ArtifactStagingDirectory)/symbols' + searchPattern: '**/*.pdb' + indexSources: false # Github sources not supported + publishSymbols: true + symbolServerType: TeamServices + detailedLog: true + - task: EsrpCodeSigning@2 + displayName: Sign + inputs: + ConnectedServiceName: 'z3-esrp-signing-2' + FolderPath: 'build-dist/${{parameters.BuildArchitecture}}/dist/z3-${{parameters.ReleaseVersion}}-${{parameters.BuildArchitecture}}-win/bin' + Pattern: 'Microsoft.Z3.dll,libz3.dll,libz3java.dll,z3.exe' + signConfigType: 'inlineSignParams' + inlineOperation: | + [ + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolSign", + "parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "Microsoft" + }, + { + "parameterName": "OpusInfo", + "parameterValue": "http://www.microsoft.com" + }, + { + "parameterName": "PageHash", + "parameterValue": "/NPH" + }, + { + "parameterName": "FileDigest", + "parameterValue": "/fd sha256" + }, + { + "parameterName": "TimeStamp", + "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + } + ], + "toolName": "signtool.exe", + "toolVersion": "6.2.9304.0" + } + ] + SessionTimeout: '60' + MaxConcurrency: '50' + MaxRetryAttempts: '5' + - task: DeleteFiles@1 + displayName: Cleanup + inputs: + SourceFolder: 'build-dist/${{parameters.BuildArchitecture}}/dist/z3-${{parameters.ReleaseVersion}}-${{parameters.BuildArchitecture}}-win/bin' + Contents: 'CodeSignSummary*' + - task: ArchiveFiles@2 + displayName: Zip + inputs: + rootFolderOrFile: 'build-dist/${{parameters.BuildArchitecture}}/dist/z3-${{parameters.ReleaseVersion}}-${{parameters.BuildArchitecture}}-win' + includeRootFolder: true + archiveType: 'zip' + archiveFile: '$(Build.ArtifactStagingDirectory)/z3-${{parameters.ReleaseVersion}}-${{parameters.BuildArchitecture}}-win.zip' + - task: PublishPipelineArtifact@1 + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)/z3-${{parameters.ReleaseVersion}}-${{parameters.BuildArchitecture}}-win.zip' + artifactName: 'WindowsBuild-${{parameters.BuildArchitecture}}' \ No newline at end of file diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index e6958ddad..9de592c08 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -21,9 +21,10 @@ def mk_dir(d): if not os.path.exists(d): os.makedirs(d) -os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), - 'ubuntu-18' : ('so', 'linux-x64'), - 'ubuntu-20' : ('so', 'linux-x64'), +os_info = { 'x64-ubuntu-latest' : ('so', 'linux-x64'), + 'x64-ubuntu-18' : ('so', 'linux-x64'), + 'x64-ubuntu-20' : ('so', 'linux-x64'), + 'x64-ubuntu-22' : ('so', 'linux-x64'), 'x64-glibc-2.35' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), @@ -78,8 +79,15 @@ def unpack(packages, symbols, arch): if 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/netstandard2.0/{b}") + file = f"{package_dir}/bin/{b}" + if os.path.exists(file): + zip_ref.extract(file, f"{tmp}") + replace(f"{tmp}/{package_dir}/bin/{b}", f"out/lib/netstandard2.0/{b}") + file = os.path.join(file,"netstandard2.0") + if os.path.exists(file): + zip_ref.extract(file, f"{tmp}") + replace(f"{tmp}/{package_dir}/bin/netstandard2.0/{b}", f"out/lib/netstandard2.0/{b}") + def mk_targets(source_root): mk_dir("out/build") @@ -88,11 +96,7 @@ def mk_targets(source_root): def mk_icon(source_root): mk_dir("out/content") shutil.copy(f"{source_root}/resources/icon.jpg", "out/content/icon.jpg") -<<<<<<< HEAD # shutil.copy(f"{source_root}/src/api/dotnet/README.md", "out/content/README.md") -======= - shutil.copy(f"{source_root}/src/api/dotnet/README.md", "out/content/README.md") ->>>>>>> bdc40b1f5f83cca22dc1d6c5808e935a3b50176c @@ -113,7 +117,6 @@ Linux Dependencies: © Microsoft Corporation. All rights reserved. smt constraint solver theorem prover content/icon.jpg - content/README.md https://github.com/Z3Prover/z3 MIT @@ -123,10 +126,6 @@ Linux Dependencies: - - - - """.format(version, repo, branch, commit, arch) print(contents) sym = "sym." if symbols else "" diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 99051fe93..8a6b1b943 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 12, 5, 0) # express a default build version or pick up ci build version + set_version(4, 12, 6, 0) # express a default build version or pick up ci build version # Z3 Project definition def init_project_def(): @@ -40,6 +40,7 @@ def init_project_def(): add_lib('model', ['macros']) add_lib('converters', ['model'], 'ast/converters') add_lib('simplifiers', ['euf', 'normal_forms', 'bit_blaster', 'converters', 'substitution'], 'ast/simplifiers') + add_lib('ast_sls', ['ast','normal_forms','converters'], 'ast/sls') add_lib('tactic', ['simplifiers']) add_lib('mbp', ['model', 'simplex'], 'qe/mbp') add_lib('qe_lite', ['tactic', 'mbp'], 'qe/lite') @@ -65,7 +66,7 @@ def init_project_def(): add_lib('bv_tactics', ['tactic', 'bit_blaster', 'core_tactics'], 'tactic/bv') add_lib('fuzzing', ['ast'], 'test/fuzzing') add_lib('smt_tactic', ['smt'], 'smt/tactic') - add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') + add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics', 'ast_sls'], 'tactic/sls') add_lib('qe', ['smt', 'mbp', 'qe_lite', 'nlsat', 'tactic', 'nlsat_tactic'], 'qe') add_lib('sat_solver', ['solver', 'core_tactics', 'aig_tactic', 'bv_tactics', 'arith_tactics', 'sat_tactic'], 'sat/sat_solver') add_lib('fd_solver', ['core_tactics', 'arith_tactics', 'sat_solver', 'smt'], 'tactic/fd_solver') diff --git a/scripts/mk_unix_dist_cmake.py b/scripts/mk_unix_dist_cmake.py new file mode 100644 index 000000000..3a38ad65b --- /dev/null +++ b/scripts/mk_unix_dist_cmake.py @@ -0,0 +1,268 @@ +############################################ +# Copyright (c) 2013 Microsoft Corporation +# +# Scripts for automatically generating +# Linux/OSX/BSD distribution zip files. +# +# Author: Leonardo de Moura (leonardo) +############################################ + +import os +import subprocess +import zipfile +import re +import getopt +import sys +import shutil +from mk_exception import * +from fnmatch import fnmatch + +def getenv(name, default): + try: + return os.environ[name].strip(' "\'') + except: + return default + +BUILD_DIR = 'build-dist' +DIST_DIR = 'dist' +VERBOSE = True +FORCE_MK = False +ASSEMBLY_VERSION = None +DOTNET_CORE_ENABLED = True +DOTNET_KEY_FILE = None +JAVA_ENABLED = True +JULIA_ENABLED = False +GIT_HASH = False +PYTHON_ENABLED = True +ARM64 = False +MAKEJOBS = getenv("MAKEJOBS", "24") + +def set_verbose(flag): + global VERBOSE + VERBOSE = flag + +def is_verbose(): + return VERBOSE + +def mk_dir(d): + if not os.path.exists(d): + if is_verbose(): + print("Make directory", d) + os.makedirs(d) + +def get_z3_name(): + version = "4" + if ASSEMBLY_VERSION: + version = ASSEMBLY_VERSION + print("Assembly version:", version) + if GIT_HASH: + return 'z3-%s.%s' % (version, get_git_hash()) + else: + return 'z3-%s' % (version) + +def get_build_dir(): + return BUILD_DIR + +def get_build_dist(): + return os.path.join(get_build_dir(), DIST_DIR) + +def get_build_dist_path(): + return os.path.join(get_build_dist(), get_z3_name()) + +def set_build_dir(path): + global BUILD_DIR + BUILD_DIR = os.path.expanduser(os.path.normpath(path)) + mk_dir(BUILD_DIR) + +def display_help(): + print("mk_unix_dist_cmake.py: Z3 Unix distribution generator\n") + print("This script generates the zip files containing executables, shared objects, header files for Unix.") + print("It must be executed from the Z3 root directory.") + print("\nOptions:") + print(" -h, --help display this message.") + print(" -s, --silent do not print verbose messages.") + print(" -b , --build= subdirectory where Z3 will be built (default: build-dist).") + print(" -f, --force force script to regenerate Makefiles.") + print(" --version= release version.") + print(" --assembly-version assembly version for dll") + print(" --nodotnet do not include .NET bindings in the binary distribution files.") + print(" --dotnet-key= strongname sign the .NET assembly with the private key in .") + print(" --nojava do not include Java bindings in the binary distribution files.") + print(" --nopython do not include Python bindings in the binary distribution files.") + print(" --julia build Julia bindings.") + print(" --githash include git hash in the Zip file.") + print(" --arm64 build for ARM64 architecture.") + exit(0) + +# Parse configuration option for mk_make script +def parse_options(): + global FORCE_MK, JAVA_ENABLED, JULIA_ENABLED, GIT_HASH, DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, ASSEMBLY_VERSION, PYTHON_ENABLED, ARM64 + path = BUILD_DIR + options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:hsf', ['build=', + 'help', + 'silent', + 'force', + 'nojava', + 'nodotnet', + 'dotnet-key=', + 'assembly-version=', + 'githash', + 'nopython', + 'julia', + 'arm64' + ]) + for opt, arg in options: + if opt in ('-b', '--build'): + if arg == 'src': + raise MKException('The src directory should not be used to host the Makefile') + path = arg + elif opt in ('-s', '--silent'): + set_verbose(False) + elif opt in ('-h', '--help'): + display_help() + elif opt in ('-f', '--force'): + FORCE_MK = True + elif opt == '--nodotnet': + DOTNET_CORE_ENABLED = False + elif opt == '--assembly-version': + ASSEMBLY_VERSION = arg + elif opt == '--nopython': + PYTHON_ENABLED = False + elif opt == '--dotnet-key': + DOTNET_KEY_FILE = arg + elif opt == '--nojava': + JAVA_ENABLED = False + elif opt == '--julia': + JULIA_ENABLED = True + elif opt == '--githash': + GIT_HASH = True + elif opt == '--arm64': + ARM64 = True + else: + raise MKException("Invalid command line option '%s'" % opt) + set_build_dir(path) + +def check_output(cmd): + out = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] + if out != None: + enc = sys.getdefaultencoding() + if enc != None: return out.decode(enc).rstrip('\r\n') + else: return out.rstrip('\r\n') + else: + return "" + +def get_git_hash(): + try: + branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) + r = check_output(['git', 'show-ref', '--abbrev=12', 'refs/heads/%s' % branch]) + except: + raise MKException("Failed to retrieve git hash") + ls = r.split(' ') + if len(ls) != 2: + raise MKException("Unexpected git output " + r) + return ls[0] + +# Create a build directory using CMake +def mk_build_dir(): + build_path = get_build_dir() + if not os.path.exists(build_path) or FORCE_MK: + mk_dir(build_path) + cmds = [] + cmds.append(f"cd {build_path}") + cmd = [] + cmd.append("cmake -S .") + if DOTNET_CORE_ENABLED: + cmd.append(' -DZ3_BUILD_DOTNET_BINDINGS=ON') + if JAVA_ENABLED: + cmd.append(' -DZ3_BUILD_JAVA_BINDINGS=ON') + cmd.append(' -DZ3_INSTALL_JAVA_BINDINGS=ON') + cmd.append(' -DZ3_JAVA_JAR_INSTALLDIR=java') + cmd.append(' -DZ3_JAVA_JNI_LIB_INSTALLDIR=bin/java') + if PYTHON_ENABLED: + cmd.append(' -DZ3_BUILD_PYTHON_BINDINGS=ON') + cmd.append(' -DZ3_INSTALL_PYTHON_BINDINGS=ON') + cmd.append(' -DCMAKE_INSTALL_PYTHON_PKG_DIR=bin/python') + if JULIA_ENABLED: + cmd.append(' -DZ3_BUILD_JULIA_BINDINGS=ON') + cmd.append(' -DZ3_INSTALL_JULIA_BINDINGS=ON') + if GIT_HASH: + git_hash = get_git_hash() + cmd.append(' -DGIT_HASH=' + git_hash) + cmd.append(' -DZ3_USE_LIB_GMP=OFF') + cmd.append(' -DZ3_BUILD_LIBZ3_SHARED=ON') + cmd.append(' -DCMAKE_BUILD_TYPE=RelWithDebInfo') + cmd.append(' -DCMAKE_INSTALL_PREFIX=' + get_build_dist_path()) + cmd.append(' -G "Ninja"') + cmd.append(' ..\n') + cmds.append("".join(cmd)) + print("CMAKE commands:", cmds) + sys.stdout.flush() + if exec_cmds(cmds) != 0: + raise MKException("failed to run commands") + +def exec_cmds(cmds): + cmd_file = 'z3_tmp.sh' + f = open(cmd_file, 'w') + for cmd in cmds: + f.write(cmd) + f.write('\n') + f.close() + res = 0 + try: + res = subprocess.call(['sh', cmd_file]) + except: + res = 1 + try: + os.remove(cmd_file) + except: + pass + return res + +def build_z3(): + if is_verbose(): + print("build z3") + build_dir = get_build_dir() + cmds = [] + cmds.append('cd %s' % build_dir) + cmds.append('ninja install') + if exec_cmds(cmds) != 0: + raise MKException("Failed to make z3") + +def mk_zip(): + build_dist = get_build_dist_path() + dist_name = get_z3_name() + old = os.getcwd() + try: + if is_verbose(): + print("dist path", build_dist) + mk_dir(build_dist) + zfname = '%s.zip' % dist_name + zipout = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED) + os.chdir(get_build_dist()) + for root, dirs, files in os.walk("."): + for f in files: + if is_verbose(): + print("adding ", os.path.join(root, f)) + zipout.write(os.path.join(root, f)) + if is_verbose(): + print("Generated '%s'" % zfname) + except: + pass + os.chdir(old) + +def cp_license(): + if is_verbose(): + print("copy licence") + path = get_build_dist_path() + mk_dir(path) + shutil.copy("LICENSE.txt", path) + +# Entry point +def main(): + parse_options() + mk_build_dir() + build_z3() + cp_license() + mk_zip() + +main() diff --git a/scripts/mk_win_dist_cmake.py b/scripts/mk_win_dist_cmake.py new file mode 100644 index 000000000..f3c83cfb6 --- /dev/null +++ b/scripts/mk_win_dist_cmake.py @@ -0,0 +1,423 @@ +############################################ +# Copyright (c) 2012 Microsoft Corporation +# +# Scripts for automatically generating +# Window distribution zip files. +# +# Author: Leonardo de Moura (leonardo) +############################################ + +import os +import subprocess +import zipfile +import re +import getopt +import sys +import shutil +from mk_exception import * +from fnmatch import fnmatch + +def getenv(name, default): + try: + return os.environ[name].strip(' "\'') + except: + return default + +BUILD_DIR = 'build-dist' +DIST_DIR = 'dist' +BUILD_X64_DIR = os.path.join(BUILD_DIR, 'x64') +BUILD_X86_DIR = os.path.join(BUILD_DIR, 'x86') +BUILD_ARM64_DIR = os.path.join(BUILD_DIR, 'arm64') +VERBOSE = True +FORCE_MK = False +ASSEMBLY_VERSION = None +DOTNET_CORE_ENABLED = True +DOTNET_KEY_FILE = None +JAVA_ENABLED = True +ZIP_BUILD_OUTPUTS = False +GIT_HASH = False +PYTHON_ENABLED = True +X86ONLY = False +X64ONLY = False +ARM64ONLY = False +ARCHITECTURES = [] + +def set_verbose(flag): + global VERBOSE + VERBOSE = flag + +def is_verbose(): + return VERBOSE + +def mk_dir(d): + if not os.path.exists(d): + if is_verbose(): + print("Make directory", d) + os.makedirs(d) + +def get_z3_name(arch): + version = "4" + if ASSEMBLY_VERSION: + version = ASSEMBLY_VERSION + print("Assembly version:", version) + if GIT_HASH: + return 'z3-%s.%s-%s-win' % (version, get_git_hash(), arch) + else: + return 'z3-%s-%s-win' % (version, arch) + +def get_build_dir(arch): + return ARCHITECTURES[arch] + +def get_build_dist(arch): + return os.path.join(get_build_dir(arch), DIST_DIR) + +def get_build_dist_path(arch): + return os.path.join(get_build_dir(arch), DIST_DIR, get_z3_name(arch)) + +def get_bin_dist_path(arch): + return os.path.join(get_build_dist_path(arch), "bin") + +def get_lib_dist_path(arch): + return os.path.join(get_build_dist_path(arch), "lib") + +def get_java_dist_path(arch): + return os.path.join(get_build_dist_path(arch), "java") + +def get_dist_path(arch): + return os.path.join(DIST_DIR, arch) + +def set_build_dir(path): + global BUILD_DIR, BUILD_X86_DIR, BUILD_X64_DIR, BUILD_ARM64_DIR, ARCHITECTURES + BUILD_DIR = os.path.expanduser(os.path.normpath(path)) + BUILD_X86_DIR = os.path.join(path, 'x86') + BUILD_X64_DIR = os.path.join(path, 'x64') + BUILD_ARM64_DIR = os.path.join(path, 'arm64') # Set ARM64 build directory + ARCHITECTURES = {'x64': BUILD_X64_DIR, 'x86':BUILD_X86_DIR, 'arm64':BUILD_ARM64_DIR} + +def display_help(): + print("mk_win_dist.py: Z3 Windows distribution generator\n") + print("This script generates the zip files containing executables, dlls, header files for Windows.") + print("It must be executed from the Z3 root directory.") + print("\nOptions:") + print(" -h, --help display this message.") + print(" -s, --silent do not print verbose messages.") + print(" -b , --build= subdirectory where x86 and x64 Z3 versions will be built (default: build-dist).") + print(" -f, --force force script to regenerate Makefiles.") + print(" --version= release version.") + print(" --assembly-version assembly version for dll") + print(" --nodotnet do not include .NET bindings in the binary distribution files.") + print(" --dotnet-key= strongname sign the .NET assembly with the private key in .") + print(" --nojava do not include Java bindings in the binary distribution files.") + print(" --nopython do not include Python bindings in the binary distribution files.") + print(" --zip package build outputs in zip file.") + print(" --githash include git hash in the Zip file.") + print(" --x86-only x86 dist only.") + print(" --x64-only x64 dist only.") + print(" --arm64-only arm64 dist only.") + exit(0) + +# Parse configuration option for mk_make script +def parse_options(): + global FORCE_MK, JAVA_ENABLED, ZIP_BUILD_OUTPUTS, GIT_HASH, DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, ASSEMBLY_VERSION, PYTHON_ENABLED, X86ONLY, X64ONLY, ARM64ONLY + path = BUILD_DIR + options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:hsf', ['build=', + 'help', + 'silent', + 'force', + 'nojava', + 'nodotnet', + 'dotnet-key=', + 'assembly-version=', + 'zip', + 'githash', + 'nopython', + 'x86-only', + 'x64-only', + 'arm64-only' + ]) + for opt, arg in options: + if opt in ('-b', '--build'): + if arg == 'src': + raise MKException('The src directory should not be used to host the Makefile') + path = arg + elif opt in ('-s', '--silent'): + set_verbose(False) + elif opt in ('-h', '--help'): + display_help() + elif opt in ('-f', '--force'): + FORCE_MK = True + elif opt == '--nodotnet': + DOTNET_CORE_ENABLED = False + elif opt == '--assembly-version': + ASSEMBLY_VERSION = arg + elif opt == '--nopython': + PYTHON_ENABLED = False + elif opt == '--dotnet-key': + DOTNET_KEY_FILE = arg + elif opt == '--nojava': + JAVA_ENABLED = False + elif opt == '--zip': + ZIP_BUILD_OUTPUTS = True + elif opt == '--githash': + GIT_HASH = True + elif opt == '--x86-only' and not X64ONLY: + X86ONLY = True + elif opt == '--arm64-only' and not X86ONLY and not X64ONLY: + ARM64ONLY = True + elif opt == '--x64-only' and not X86ONLY: + X64ONLY = True + else: + raise MKException("Invalid command line option '%s'" % opt) + set_build_dir(path) + +# Check whether build directory already exists or not +def check_build_dir(path): + return os.path.exists(path) and os.path.exists(os.path.join(path, 'Makefile')) + +def check_output(cmd): + out = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] + if out != None: + enc = sys.getdefaultencoding() + if enc != None: return out.decode(enc).rstrip('\r\n') + else: return out.rstrip('\r\n') + else: + return "" + +def get_git_hash(): + try: + branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) + r = check_output(['git', 'show-ref', '--abbrev=12', 'refs/heads/%s' % branch]) + except: + raise MKException("Failed to retrieve git hash") + ls = r.split(' ') + if len(ls) != 2: + raise MKException("Unexpected git output " + r) + return ls[0] + + + +# Create a build directory using mk_make.py +def mk_build_dir(arch): + build_path = get_build_dir(arch) + if not check_build_dir(build_path) or FORCE_MK: + mk_dir(build_path) + vsarch = arch + if arch == "arm64": + vsarch = "amd64_arm64" + + cmds = [] + cmds.append(f"cd {build_path}") + cmds.append('call "%VCINSTALLDIR%Auxiliary\\build\\vcvarsall.bat" ' + vsarch) + cmd = [] + cmd.append("cmake -S .") + if DOTNET_CORE_ENABLED: + cmd.append(' -DZ3_BUILD_DOTNET_BINDINGS=ON') +# cmd.append(' -DZ3_INSTALL_DOTNET_BINDINGS=ON') + if JAVA_ENABLED: + cmd.append(' -DZ3_BUILD_JAVA_BINDINGS=ON') + cmd.append(' -DZ3_INSTALL_JAVA_BINDINGS=ON') + cmd.append(' -DZ3_JAVA_JAR_INSTALLDIR=java') + cmd.append(' -DZ3_JAVA_JNI_LIB_INSTALLDIR=bin/java') + if PYTHON_ENABLED: + cmd.append(' -DZ3_BUILD_PYTHON_BINDINGS=ON') + cmd.append(' -DZ3_INSTALL_PYTHON_BINDINGS=ON') + cmd.append(' -DCMAKE_INSTALL_PYTHON_PKG_DIR=bin/python') + + if GIT_HASH: + git_hash = get_git_hash() + cmd.append(' -DGIT_HASH=' + git_hash) + cmd.append(' -DZ3_USE_LIB_GMP=OFF') + cmd.append(' -DZ3_BUILD_LIBZ3_SHARED=ON') + cmd.append(' -DCMAKE_BUILD_TYPE=RelWithDebInfo') + cmd.append(' -DCMAKE_INSTALL_PREFIX=' + os.path.join(DIST_DIR, get_z3_name(arch))) + cmd.append(' -G "Ninja"') + cmd.append(' ../..\n') + cmds.append("".join(cmd)) + print("CMAKE commands:", cmds) + sys.stdout.flush() + if exec_cmds(cmds) != 0: + raise MKException("failed to run commands") + + + +# Check if on Visual Studio command prompt +def check_vc_cmd_prompt(): + try: + DEVNULL = open(os.devnull, 'wb') + subprocess.call(['cl'], stdout=DEVNULL, stderr=DEVNULL) + except: + raise MKException("You must execute the mk_win_dist.py script on a Visual Studio Command Prompt") + +def exec_cmds(cmds): + cmd_file = 'z3_tmp.cmd' + f = open(cmd_file, 'w') + for cmd in cmds: + f.write(cmd) + f.write('\n') + f.close() + res = 0 + try: + res = subprocess.call(cmd_file, shell=True) + except: + res = 1 + try: + os.erase(cmd_file) + except: + pass + return res + + + +def build_z3(arch): + if is_verbose(): + print("build z3") + build_dir = get_build_dir(arch) + if arch == "arm64": + arch = "amd64_arm64" + cmds = [] + cmds.append('call "%VCINSTALLDIR%Auxiliary\\build\\vcvarsall.bat" ' + arch) + cmds.append('cd %s' % build_dir) + cmds.append('ninja install') + if exec_cmds(cmds) != 0: + raise MKException("Failed to make z3") + + + +def mk_zip(arch): + if not ZIP_BUILD_OUTPUTS: + return + build_dist = get_build_dist_path(arch) + dist_name = get_z3_name(arch) + dist_path = get_dist_path(arch) + build_dir = get_build_dir(arch) + old = os.getcwd() + try: + if is_verbose(): + print("dist path", dist_path) + mk_dir(dist_path) + zfname = os.path.join(dist_path, '%s.zip' % dist_name) + zipout = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED) + os.chdir(get_build_dist(arch)) + for root, dirs, files in os.walk("."): + for f in files: + if is_verbose(): + print("adding ", os.path.join(root, f)) + zipout.write(os.path.join(root, f)) + if is_verbose(): + print("Generated '%s'" % zfname) + except: + pass + os.chdir(old) + + + +VS_RUNTIME_PATS = [re.compile(r'vcomp.*\.dll'), + re.compile(r'msvcp.*\.dll'), + re.compile(r'msvcr.*\.dll'), + re.compile(r'vcrun.*\.dll')] + +# Copy Visual Studio Runtime libraries +def cp_vs_runtime(arch): + platform = arch + vcdir = os.environ['VCINSTALLDIR'] + path = '%sredist' % vcdir + vs_runtime_files = [] + print("Walking %s" % path) + # Everything changes with every release of VS + # Prior versions of VS had DLLs under "redist\x64" + # There are now several variants of redistributables + # The naming convention defies my understanding so + # we use a "check_root" filter to find some hopefully suitable + # redistributable. + def check_root(root): + return platform in root and ("CRT" in root or "MP" in root) and "onecore" not in root and "debug" not in root + for root, dirs, files in os.walk(path): + for filename in files: + if fnmatch(filename, '*.dll') and check_root(root): + print("Checking %s %s" % (root, filename)) + for pat in VS_RUNTIME_PATS: + if pat.match(filename): + fname = os.path.join(root, filename) + if not os.path.isdir(fname): + vs_runtime_files.append(fname) + if not vs_runtime_files: + raise MKException("Did not find any runtime files to include") + bin_dist_path = get_bin_dist_path(arch) + for f in vs_runtime_files: + shutil.copy(f, bin_dist_path) + if is_verbose(): + print("Copied '%s' to '%s'" % (f, bin_dist_path)) + +def cp_license(arch): + if is_verbose(): + print("copy licence") + path = get_build_dist_path(arch) + mk_dir(path) + shutil.copy("LICENSE.txt", path) + +def cp_dotnet(arch): + if not DOTNET_CORE_ENABLED: + return + if is_verbose(): + print("copy dotnet") + build_dir = get_build_dir(arch) + dist_dir = get_bin_dist_path(arch) + shutil.copytree(os.path.join(build_dir, "Microsoft.Z3"), + dist_dir, + dirs_exist_ok=True) + +def cp_into_bin(arch): + if is_verbose(): + print("copy lib") + lib_dir = get_lib_dist_path(arch) + bin_dir = get_bin_dist_path(arch) + shutil.copyfile(os.path.join(lib_dir, "libz3.lib"), + os.path.join(bin_dir, "libz3.lib")) + shutil.rmtree(lib_dir) + if JAVA_ENABLED: + java_dir = get_java_dist_path(arch) + shutil.copytree(java_dir, + bin_dir, + dirs_exist_ok=True) + shutil.rmtree(java_dir) + +def cp_pdb(arch): + if is_verbose(): + print("copy pdb") + build_dir = get_build_dir(arch) + bin_path = get_bin_dist_path(arch) + mk_dir(bin_path) + for f in os.listdir(build_dir): + if f.endswith("libz3.pdb"): + shutil.copy(os.path.join(build_dir, f), bin_path) + +def build_for_arch(arch): + mk_build_dir(arch) + build_z3(arch) + cp_license(arch) + cp_pdb(arch) + cp_dotnet(arch) + cp_vs_runtime(arch) + cp_into_bin(arch) + mk_zip(arch) + +# Entry point +def main(): + if os.name != 'nt': + raise MKException("This script is for Windows only") + + parse_options() + check_vc_cmd_prompt() + + if X86ONLY: + build_for_arch("x86") + elif X64ONLY: + build_for_arch("x64") + elif ARM64ONLY: + build_for_arch("arm64") + else: + for arch in ARCHITECTURES: + build_for_arch(arch) + +main() + diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 0f5337654..f6c3c66c7 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -1,28 +1,44 @@ variables: Major: '4' Minor: '12' - Patch: '5' + Patch: '6' + ReleaseVersion: $(Major).$(Minor).$(Patch) AssemblyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId) - NightlyVersion: $(AssemblyVersion)-$(Build.DefinitionName) + NightlyVersion: $(AssemblyVersion)-$(Build.buildId) stages: - stage: Build jobs: - - job: Mac - displayName: "Mac Build" + - job: MacBuild + displayName: "macOS Build" pool: vmImage: "macOS-11" steps: - - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk - - script: git clone https://github.com/z3prover/z3test z3test - - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. - - task: PublishPipelineArtifact@1 + - task: PythonScript@0 + displayName: Build inputs: - artifactName: 'Mac' + scriptSource: 'filepath' + scriptPath: scripts/mk_unix_dist.py + arguments: --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - script: git clone https://github.com/z3prover/z3test z3test + displayName: 'Clone z3test' + - task: PythonScript@0 + displayName: Test + inputs: + scriptSource: 'filepath' + scriptPath: z3test/scripts/test_benchmarks.py + arguments: build-dist/z3 z3test/regressions/smt2 + - task: CopyFiles@2 + inputs: + sourceFolder: dist + contents: '*.zip' + targetFolder: $(Build.ArtifactStagingDirectory) + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'macOSBuild' targetPath: $(Build.ArtifactStagingDirectory) - - job: MacArm64 + - job: MacBuildArm64 displayName: "Mac ARM64 Build" pool: vmImage: "macOS-11" @@ -35,32 +51,62 @@ stages: artifactName: 'MacArm64' targetPath: $(Build.ArtifactStagingDirectory) - - job: Ubuntu20 - displayName: "Ubuntu20 build" - pool: - vmImage: "ubuntu-20.04" - steps: - - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk - - script: git clone https://github.com/z3prover/z3test z3test - - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. - - task: PublishPipelineArtifact@0 - inputs: - artifactName: 'Ubuntu-20.04' - targetPath: $(Build.ArtifactStagingDirectory) - - - job: Ubuntu + - job: UbuntuBuild displayName: "Ubuntu build" pool: vmImage: "ubuntu-latest" steps: - - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - task: PythonScript@0 + displayName: Build + inputs: + scriptSource: 'filepath' + scriptPath: scripts/mk_unix_dist.py + arguments: --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk - script: git clone https://github.com/z3prover/z3test z3test - - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. + displayName: 'Clone z3test' + - task: PythonScript@0 + displayName: Test + inputs: + scriptSource: 'filepath' + scriptPath: z3test/scripts/test_benchmarks.py + arguments: build-dist/z3 z3test/regressions/smt2 + - task: CopyFiles@2 + inputs: + sourceFolder: dist + contents: '*.zip' + targetFolder: $(Build.ArtifactStagingDirectory) - task: PublishPipelineArtifact@0 inputs: - artifactName: 'Ubuntu' + artifactName: 'UbuntuBuild' + targetPath: $(Build.ArtifactStagingDirectory) + + - job: UbuntuBuild20 + displayName: "Ubuntu build 20" + pool: + vmImage: "ubuntu-20.04" + steps: + - task: PythonScript@0 + displayName: Build + inputs: + scriptSource: 'filepath' + scriptPath: scripts/mk_unix_dist.py + arguments: --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - script: git clone https://github.com/z3prover/z3test z3test + displayName: 'Clone z3test' + - task: PythonScript@0 + displayName: Test + inputs: + scriptSource: 'filepath' + scriptPath: z3test/scripts/test_benchmarks.py + arguments: build-dist/z3 z3test/regressions/smt2 + - task: CopyFiles@2 + inputs: + sourceFolder: dist + contents: '*.zip' + targetFolder: $(Build.ArtifactStagingDirectory) + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'UbuntuBuild20' targetPath: $(Build.ArtifactStagingDirectory) - job: UbuntuArm64 @@ -68,12 +114,9 @@ stages: pool: vmImage: "ubuntu-latest" steps: - - script: sudo apt update - - script: sudo apt install gcc-arm-none-eabi -y - - script: sudo apt install gcc-arm-linux-gnueabihf -y - - script: sudo apt install gcc-aarch64-linux-gnu -y - - script: sudo apt install g++-aarch64-linux-gnu -y - - script: CXX=aarch64-linux-gnu-g++ CC=aarch64-linux-gnu-gcc python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 + - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 + - script: git clone https://github.com/z3prover/z3test z3test + - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. - task: PublishPipelineArtifact@0 inputs: @@ -115,165 +158,57 @@ stages: inputs: artifactName: 'UbuntuDoc' targetPath: $(Build.ArtifactStagingDirectory) - - - job: ManyLinuxBuild - variables: - python: "/opt/python/cp37-cp37m/bin/python" - name: ManyLinux + + - job: LinuxBuilds displayName: "ManyLinux build" + variables: + name: ManyLinux + python: "/opt/python/cp37-cp37m/bin/python" pool: vmImage: "ubuntu-latest" container: "quay.io/pypa/manylinux2014_x86_64:latest" steps: - - script: $(python) scripts/mk_unix_dist.py --nodotnet --nojava + - task: PythonScript@0 + displayName: Build + inputs: + scriptSource: 'filepath' + scriptPath: scripts/mk_unix_dist.py + arguments: --nodotnet --nojava + pythonInterpreter: $(python) - script: git clone https://github.com/z3prover/z3test z3test - - script: $(python) z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/ + displayName: 'Clone z3test' + - task: PythonScript@0 + displayName: Test + inputs: + scriptSource: 'filepath' + scriptPath: z3test/scripts/test_benchmarks.py + arguments: build-dist/z3 z3test/regressions/smt2 + pythonInterpreter: $(python) + - task: CopyFiles@2 + inputs: + sourceFolder: dist + contents: '*.zip' + targetFolder: $(Build.ArtifactStagingDirectory) - task: PublishPipelineArtifact@0 inputs: - artifactName: '$(name)Build' + artifactName: 'ManyLinuxBuild' targetPath: $(Build.ArtifactStagingDirectory) + + - template: build-win-signed.yml + parameters: + ReleaseVersion: $(ReleaseVersion) + BuildArchitecture: 'x64' -# - job: MuslLinuxBuild -# condition: eq(0,1) -# variables: -# python: "/opt/python/cp310-cp310/bin/python" -# name: MuslLinux -# displayName: "MuslLinux build" -# pool: -# vmImage: "ubuntu-latest" -# container: "quay.io/pypa/musllinux_1_1_x86_64:latest" -# steps: -# - script: $(python) scripts/mk_unix_dist.py --nodotnet --nojava -# - script: git clone https://github.com/z3prover/z3test z3test -# - script: $(python) z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 -# - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/ -# - task: PublishPipelineArtifact@0 -# inputs: -# artifactName: '$(name)Build' -# targetPath: $(Build.ArtifactStagingDirectory) - - - job: Windows32 - displayName: "Windows 32-bit build" - pool: - vmImage: "windows-latest" - steps: - - task: CmdLine@2 - inputs: - script: - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86 & - python scripts\mk_win_dist.py - --assembly-version=$(AssemblyVersion) - --x86-only - --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk - --zip - - task: CopyFiles@2 - inputs: - sourceFolder: dist - contents: '*.zip' - targetFolder: $(Build.ArtifactStagingDirectory) - - task: PublishPipelineArtifact@1 - inputs: - targetPath: $(Build.ArtifactStagingDirectory) - artifactName: 'Windows32' - - task: CopyFiles@2 - displayName: 'Collect Symbols' - inputs: - sourceFolder: dist - contents: '**/*.pdb' - targetFolder: '$(Build.ArtifactStagingDirectory)/symbols' - # Publish symbol archive to match nuget package - # Index your source code and publish symbols to a file share or Azure Artifacts symbol server - - task: PublishSymbols@2 - inputs: - symbolsFolder: '$(Build.ArtifactStagingDirectory)/symbols' - searchPattern: '**/*.pdb' - indexSources: false # Github not supported - publishSymbols: true - symbolServerType: TeamServices - detailedLog: true - - - job: Windows64 - displayName: "Windows 64-bit build" - pool: - vmImage: "windows-latest" - steps: - - task: CmdLine@2 - inputs: - script: - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 & - python scripts\mk_win_dist.py - --assembly-version=$(AssemblyVersion) - --x64-only - --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk - --zip - - task: CopyFiles@2 - inputs: - sourceFolder: dist - contents: '*.zip' - targetFolder: $(Build.ArtifactStagingDirectory) - - task: PublishPipelineArtifact@1 - inputs: - targetPath: $(Build.ArtifactStagingDirectory) - artifactName: 'Windows64' - - task: CopyFiles@2 - displayName: 'Collect Symbols' - inputs: - sourceFolder: dist - contents: '**/*.pdb' - targetFolder: '$(Build.ArtifactStagingDirectory)/symbols' - # Publish symbol archive to match nuget package - # Index your source code and publish symbols to a file share or Azure Artifacts symbol server - - task: PublishSymbols@2 - inputs: - symbolsFolder: '$(Build.ArtifactStagingDirectory)/symbols' - searchPattern: '**/*.pdb' - indexSources: false # Github not supported - publishSymbols: true - symbolServerType: TeamServices - detailedLog: true - - - job: "WindowsArm64" - displayName: "WindowsArm64" - pool: - vmImage: "windows-latest" - variables: - arch: "amd64_arm64" - bindings: "-DCMAKE_BUILD_TYPE=RelWithDebInfo" - steps: - - script: md build - - script: | - cd build - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" $(arch) - cmake $(bindings) -G "NMake Makefiles" ../ - nmake - cd .. - - task: CopyFiles@2 - inputs: - sourceFolder: build - contents: '*z3*.*' - targetFolder: $(Build.ArtifactStagingDirectory) - - task: PublishPipelineArtifact@1 - inputs: - targetPath: $(Build.ArtifactStagingDirectory) - artifactName: 'WindowsArm64' - - task: CopyFiles@2 - displayName: 'Collect Symbols' - inputs: - sourceFolder: build - contents: '**/*.pdb' - targetFolder: '$(Build.ArtifactStagingDirectory)/symbols' - # Publish symbol archive to match nuget package - # Index your source code and publish symbols to a file share or Azure Artifacts symbol server - - task: PublishSymbols@2 - inputs: - symbolsFolder: '$(Build.ArtifactStagingDirectory)/symbols' - searchPattern: '**/*.pdb' - indexSources: false # Github not supported - publishSymbols: true - symbolServerType: TeamServices - detailedLog: true + - template: build-win-signed.yml + parameters: + ReleaseVersion: $(ReleaseVersion) + BuildArchitecture: 'x86' + - template: build-win-signed-cmake.yml + parameters: + ReleaseVersion: $(ReleaseVersion) + BuildArchitecture: 'arm64' + VCArchitecture: 'amd64_arm64' - stage: Package jobs: @@ -291,27 +226,22 @@ stages: - task: DownloadPipelineArtifact@2 displayName: 'Download Win64 Build' inputs: - artifact: 'Windows64' - path: $(Agent.TempDirectory)\package - - task: DownloadPipelineArtifact@2 - displayName: 'Download Ubuntu Build' - inputs: - artifact: 'Ubuntu' + artifact: 'WindowsBuild-x64' path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download Ubuntu 20.04 Build' inputs: - artifact: 'Ubuntu-20.04' + artifact: 'UbuntuBuild20' path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download Ubuntu ARM64 Build' inputs: artifact: 'UbuntuArm64' - path: $(Agent.TempDirectory)\package + path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download macOS Build' inputs: - artifact: 'Mac' + artifact: 'macOsBuild' path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download macOS Arm64 Build' @@ -420,7 +350,7 @@ stages: - task: DownloadPipelineArtifact@2 displayName: 'Download Win32 Build' inputs: - artifact: 'Windows32' + artifact: 'WindowsBuild-x86' path: $(Agent.TempDirectory)\package - task: NuGetToolInstaller@0 inputs: @@ -519,24 +449,19 @@ stages: steps: - task: DownloadPipelineArtifact@2 inputs: - artifactName: 'Windows32' + artifactName: 'WindowsBuild-x86' targetPath: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 inputs: - artifactName: 'Windows64' + artifactName: 'WindowsBuild-x64' targetPath: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 inputs: artifactName: 'ManyLinuxBuild' targetPath: $(Agent.TempDirectory) -# - task: DownloadPipelineArtifact@2 -# displayName: 'Download MuslLinux Build' -# inputs: -# artifact: 'MuslLinuxBuild' -# path: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 inputs: - artifactName: 'Mac' + artifactName: 'macOsBuild' targetPath: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 inputs: @@ -574,22 +499,22 @@ stages: - task: DownloadPipelineArtifact@2 displayName: "Download windows32" inputs: - artifactName: 'Windows32' - targetPath: tmp - - task: DownloadPipelineArtifact@2 - displayName: "Download windowsArm64" - inputs: - artifactName: 'WindowsArm64' + artifactName: 'WindowsBuild-x86' targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download windows64" inputs: - artifactName: 'Windows64' + artifactName: 'WindowsBuild-x64' + targetPath: tmp + - task: DownloadPipelineArtifact@2 + displayName: "Download windowsARM64" + inputs: + artifactName: 'WindowsBuild-arm64' targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download Mac" inputs: - artifactName: 'Mac' + artifactName: 'macOsBuild' targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download MacArm64" @@ -601,15 +526,10 @@ stages: inputs: artifactName: 'UbuntuArm64' targetPath: tmp - - task: DownloadPipelineArtifact@2 - displayName: "Download Ubuntu" - inputs: - artifactName: 'Ubuntu' - targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download Ubuntu-20.04" inputs: - artifactName: 'Ubuntu-20.04' + artifactName: 'UbuntuBuild20' targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download Doc" diff --git a/scripts/release.yml b/scripts/release.yml index 76f8a8a57..7a2c4ef41 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -6,7 +6,7 @@ trigger: none variables: - ReleaseVersion: '4.12.5' + ReleaseVersion: '4.12.6' stages: @@ -209,6 +209,12 @@ stages: ReleaseVersion: $(ReleaseVersion) BuildArchitecture: 'x86' + - template: build-win-signed-cmake.yml + parameters: + ReleaseVersion: $(ReleaseVersion) + BuildArchitecture: 'arm64' + VCArchitecture: 'amd64_arm64' + # Creates Z3 packages in various formats - stage: Package @@ -525,16 +531,21 @@ stages: inputs: artifact: 'WindowsBuild-x86' path: $(Agent.TempDirectory) - - task: DownloadPipelineArtifact@2 - displayName: "Download Python" - inputs: - artifactName: 'PythonPackage' - path: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 displayName: 'Download Win64 Build' inputs: artifact: 'WindowsBuild-x64' path: $(Agent.TempDirectory) + - task: DownloadPipelineArtifact@2 + displayName: 'Download Arm64 Build' + inputs: + artifact: 'WindowsBuild-arm64' + path: $(Agent.TempDirectory) + - task: DownloadPipelineArtifact@2 + displayName: "Download Python" + inputs: + artifactName: 'PythonPackage' + path: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 displayName: 'Download NuGet64 Package' inputs: diff --git a/scripts/update_api.py b/scripts/update_api.py index 7d3d8899f..79f144142 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -641,8 +641,8 @@ def mk_java(java_src, java_dir, package_name): public static native void propagateRegisterEq(Object o, long ctx, long solver); public static native void propagateRegisterDecide(Object o, long ctx, long solver); public static native void propagateRegisterFinal(Object o, long ctx, long solver); - public static native void propagateConflict(Object o, long ctx, long solver, long javainfo, int num_fixed, long[] fixed, long num_eqs, long[] eq_lhs, long[] eq_rhs, long conseq); public static native void propagateAdd(Object o, long ctx, long solver, long javainfo, long e); + public static native boolean propagateConsequence(Object o, long ctx, long solver, long javainfo, int num_fixed, long[] fixed, long num_eqs, long[] eq_lhs, long[] eq_rhs, long conseq); public static native boolean propagateNextSplit(Object o, long ctx, long solver, long javainfo, long e, long idx, int phase); public static native void propagateDestroy(Object o, long ctx, long solver, long javainfo); @@ -698,6 +698,8 @@ def mk_java(java_src, java_dir, package_name): protected abstract void createdWrapper(long le); protected abstract void fixedWrapper(long lvar, long lvalue); + + protected abstract void decideWrapper(long lvar, int bit, boolean is_pos); } """) java_native.write('\n') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13fd58db8..969736430 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ add_subdirectory(ast/euf) add_subdirectory(ast/converters) add_subdirectory(ast/substitution) add_subdirectory(ast/simplifiers) +add_subdirectory(ast/sls) add_subdirectory(tactic) add_subdirectory(qe/mbp) add_subdirectory(qe/lite) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index a28b3f20b..424b361f3 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -146,8 +146,7 @@ extern "C" { ast_manager& m = mk_c(c)->m(); recfun::decl::plugin& p = mk_c(c)->recfun().get_plugin(); if (!p.has_def(d)) { - std::string msg = "function " + mk_pp(d, m) + " needs to be declared using rec_func_decl"; - SET_ERROR_CODE(Z3_INVALID_ARG, msg.c_str()); + SET_ERROR_CODE(Z3_INVALID_ARG, "function " + mk_pp(d, m) + " needs to be declared using rec_func_decl"); return; } expr_ref abs_body(m); @@ -168,8 +167,7 @@ extern "C" { return; } if (!pd.get_def()->get_cases().empty()) { - std::string msg = "function " + mk_pp(d, m) + " has already been given a definition"; - SET_ERROR_CODE(Z3_INVALID_ARG, msg.c_str()); + SET_ERROR_CODE(Z3_INVALID_ARG, "function " + mk_pp(d, m) + " has already been given a definition"); return; } @@ -377,9 +375,7 @@ extern "C" { RESET_ERROR_CODE(); symbol _s = to_symbol(s); if (_s.is_numerical()) { - std::ostringstream buffer; - buffer << _s.get_num(); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::to_string(_s.get_num())); } else { return mk_c(c)->mk_external_string(_s.str()); @@ -823,7 +819,7 @@ extern "C" { param_descrs descrs; th_rewriter::get_param_descrs(descrs); descrs.display(buffer); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); Z3_CATCH_RETURN(""); } @@ -1031,7 +1027,7 @@ extern "C" { default: UNREACHABLE(); } - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); Z3_CATCH_RETURN(nullptr); } @@ -1066,7 +1062,7 @@ extern "C" { pp.add_assumption(to_expr(assumptions[i])); } pp.display_smt2(buffer, to_expr(formula)); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); Z3_CATCH_RETURN(""); } diff --git a/src/api/api_ast_map.cpp b/src/api/api_ast_map.cpp index 5976d0e41..523ba1f59 100644 --- a/src/api/api_ast_map.cpp +++ b/src/api/api_ast_map.cpp @@ -160,8 +160,8 @@ extern "C" { for (; it != end; ++it) { buffer << "\n (" << mk_ismt2_pp(it->m_key, mng, 3) << "\n " << mk_ismt2_pp(it->m_value, mng, 3) << ")"; } - buffer << ")"; - return mk_c(c)->mk_external_string(buffer.str()); + buffer << ')'; + return mk_c(c)->mk_external_string(std::move(buffer).str()); Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/api_config_params.cpp b/src/api/api_config_params.cpp index 93d4e27e1..d463b1fb7 100644 --- a/src/api/api_config_params.cpp +++ b/src/api/api_config_params.cpp @@ -98,7 +98,10 @@ extern "C" { LOG_Z3_set_param_value(c, param_id, param_value); try { ast_context_params * p = reinterpret_cast(c); - p->set(param_id, param_value); + if (p->is_shell_only_parameter(param_id)) + warning_msg("parameter %s can only be set for the shell, not binary API", param_id); + else + p->set(param_id, param_value); } catch (z3_exception & ex) { // The error handler is only available for contexts @@ -111,7 +114,10 @@ extern "C" { Z3_TRY; LOG_Z3_update_param_value(c, param_id, param_value); RESET_ERROR_CODE(); - mk_c(c)->params().set(param_id, param_value); + if (mk_c(c)->params().is_shell_only_parameter(param_id)) + warning_msg("parameter %s can only be set for the shell, not binary API", param_id); + else + mk_c(c)->params().set(param_id, param_value); Z3_CATCH; } diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 66cd715c9..344224dd3 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -338,12 +338,12 @@ namespace api { std::ostringstream buffer; app * a = to_app(n); buffer << mk_pp(a->get_decl(), m()) << " applied to: "; - if (a->get_num_args() > 1) buffer << "\n"; + if (a->get_num_args() > 1) buffer << '\n'; for (unsigned i = 0; i < a->get_num_args(); ++i) { buffer << mk_bounded_pp(a->get_arg(i), m(), 3) << " of sort "; - buffer << mk_pp(a->get_arg(i)->get_sort(), m()) << "\n"; + buffer << mk_pp(a->get_arg(i)->get_sort(), m()) << '\n'; } - auto str = buffer.str(); + auto str = std::move(buffer).str(); warning_msg("%s", str.c_str()); break; } diff --git a/src/api/api_fpa.cpp b/src/api/api_fpa.cpp index aeb075e45..3c350ed18 100644 --- a/src/api/api_fpa.cpp +++ b/src/api/api_fpa.cpp @@ -1022,7 +1022,7 @@ extern "C" { if (mpfm.is_inf(val)) mpqm.set(q, 0); std::stringstream ss; mpqm.display_decimal(ss, q, sbits); - return mk_c(c)->mk_external_string(ss.str()); + return mk_c(c)->mk_external_string(std::move(ss).str()); Z3_CATCH_RETURN(""); } @@ -1100,7 +1100,7 @@ extern "C" { } std::stringstream ss; ss << exp; - return mk_c(c)->mk_external_string(ss.str()); + return mk_c(c)->mk_external_string(std::move(ss).str()); Z3_CATCH_RETURN(""); } diff --git a/src/api/api_goal.cpp b/src/api/api_goal.cpp index cfe0974df..cbb67f2a2 100644 --- a/src/api/api_goal.cpp +++ b/src/api/api_goal.cpp @@ -185,7 +185,7 @@ extern "C" { std::ostringstream buffer; to_goal_ref(g)->display(buffer); // Hack for removing the trailing '\n' - std::string result = buffer.str(); + std::string result = std::move(buffer).str(); SASSERT(result.size() > 0); result.resize(result.size()-1); return mk_c(c)->mk_external_string(std::move(result)); @@ -203,7 +203,7 @@ extern "C" { } to_goal_ref(g)->display_dimacs(buffer, include_names); // Hack for removing the trailing '\n' - std::string result = buffer.str(); + std::string result = std::move(buffer).str(); SASSERT(result.size() > 0); result.resize(result.size()-1); return mk_c(c)->mk_external_string(std::move(result)); diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index 3512b4b05..e449cb0ea 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -432,14 +432,14 @@ extern "C" { if (mk_c(c)->get_print_mode() == Z3_PRINT_SMTLIB2_COMPLIANT) { model_smt2_pp(buffer, mk_c(c)->m(), *(to_model_ref(m)), 0); // Hack for removing the trailing '\n' - result = buffer.str(); + result = std::move(buffer).str(); if (!result.empty()) result.resize(result.size()-1); } else { model_params p; model_v2_pp(buffer, *(to_model_ref(m)), p.partial()); - result = buffer.str(); + result = std::move(buffer).str(); } return mk_c(c)->mk_external_string(std::move(result)); Z3_CATCH_RETURN(nullptr); diff --git a/src/api/api_numeral.cpp b/src/api/api_numeral.cpp index ebac4de31..b90a84bb7 100644 --- a/src/api/api_numeral.cpp +++ b/src/api/api_numeral.cpp @@ -188,8 +188,8 @@ extern "C" { bool ok = Z3_get_numeral_rational(c, a, r); if (ok && r.is_int() && !r.is_neg()) { std::stringstream strm; - r.display_bin(strm, r.get_num_bits()); - return mk_c(c)->mk_external_string(strm.str()); + strm << r.as_bin(r.get_num_bits()); + return mk_c(c)->mk_external_string(std::move(strm).str()); } else { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); @@ -237,7 +237,7 @@ extern "C" { else if (mk_c(c)->fpautil().is_numeral(to_expr(a), tmp)) { std::ostringstream buffer; fu.fm().display_smt2(buffer, tmp, false); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); } else { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); @@ -288,21 +288,21 @@ extern "C" { if (u.is_numeral(e, r) && !r.is_int()) { std::ostringstream buffer; r.display_decimal(buffer, precision); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); } if (u.is_irrational_algebraic_numeral(e)) { algebraic_numbers::anum const & n = u.to_irrational_algebraic_numeral(e); algebraic_numbers::manager & am = u.am(); std::ostringstream buffer; am.display_decimal(buffer, n, precision); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); } else if (mk_c(c)->fpautil().is_rm_numeral(to_expr(a), rm)) return Z3_get_numeral_string(c, a); else if (mk_c(c)->fpautil().is_numeral(to_expr(a), ftmp)) { std::ostringstream buffer; fu.fm().display_decimal(buffer, ftmp, 12); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); } else if (Z3_get_numeral_rational(c, a, r)) { return mk_c(c)->mk_external_string(r.to_string()); diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 953167fe9..ac100ee30 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -170,8 +170,8 @@ extern "C" { if (g_is_threaded || g_thread_id != std::this_thread::get_id()) { g_is_threaded = true; std::ostringstream strm; - strm << smt2log << "-" << std::this_thread::get_id(); - smt2log = symbol(strm.str()); + strm << smt2log << '-' << std::this_thread::get_id(); + smt2log = symbol(std::move(strm).str()); } to_solver(s)->m_pp = alloc(solver2smt2_pp, mk_c(c)->m(), smt2log.str()); } @@ -208,7 +208,7 @@ extern "C" { if (!smt_logics::supported_logic(to_symbol(logic))) { std::ostringstream strm; strm << "logic '" << to_symbol(logic) << "' is not recognized"; - SET_ERROR_CODE(Z3_INVALID_ARG, strm.str()); + SET_ERROR_CODE(Z3_INVALID_ARG, std::move(strm).str()); RETURN_Z3(nullptr); } else { @@ -306,7 +306,7 @@ extern "C" { if (!parse_smt2_commands(*ctx.get(), is)) { ctx = nullptr; - SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str()); + SET_ERROR_CODE(Z3_PARSER_ERROR, std::move(errstrm).str()); return; } @@ -333,7 +333,7 @@ extern "C" { std::stringstream err; sat::solver solver(to_solver_ref(s)->get_params(), m.limit()); if (!parse_dimacs(is, err, solver)) { - SET_ERROR_CODE(Z3_PARSER_ERROR, err.str()); + SET_ERROR_CODE(Z3_PARSER_ERROR, std::move(err).str()); return; } sat2goal s2g; @@ -400,7 +400,7 @@ extern "C" { if (!initialized) to_solver(s)->m_solver = nullptr; descrs.display(buffer); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); Z3_CATCH_RETURN(""); } @@ -799,7 +799,7 @@ extern "C" { init_solver(c, s); std::ostringstream buffer; to_solver_ref(s)->display(buffer); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); Z3_CATCH_RETURN(""); } @@ -810,7 +810,7 @@ extern "C" { init_solver(c, s); std::ostringstream buffer; to_solver_ref(s)->display_dimacs(buffer, include_names); - return mk_c(c)->mk_external_string(buffer.str()); + return mk_c(c)->mk_external_string(std::move(buffer).str()); Z3_CATCH_RETURN(""); } diff --git a/src/api/java/AST.java b/src/api/java/AST.java index c28c0cfcb..99cdde948 100644 --- a/src/api/java/AST.java +++ b/src/api/java/AST.java @@ -19,6 +19,8 @@ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; +import java.lang.ref.ReferenceQueue; + /** * The abstract syntax tree (AST) class. **/ @@ -196,7 +198,7 @@ public class AST extends Z3Object implements Comparable @Override void addToReferenceQueue() { - getContext().getASTDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, ASTRef::new); } static AST create(Context ctx, long obj) @@ -217,4 +219,16 @@ public class AST extends Z3Object implements Comparable throw new Z3Exception("Unknown AST kind"); } } + + private static class ASTRef extends Z3ReferenceQueue.Reference { + + private ASTRef(AST referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.decRef(ctx.nCtx(), z3Obj); + } + } } diff --git a/src/api/java/ASTDecRefQueue.java b/src/api/java/ASTDecRefQueue.java deleted file mode 100644 index b0a6fa217..000000000 --- a/src/api/java/ASTDecRefQueue.java +++ /dev/null @@ -1,31 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - ASTDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class ASTDecRefQueue extends IDecRefQueue -{ - public ASTDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.decRef(ctx.nCtx(), obj); - } -}; diff --git a/src/api/java/ASTMap.java b/src/api/java/ASTMap.java index 916811cec..23a16a828 100644 --- a/src/api/java/ASTMap.java +++ b/src/api/java/ASTMap.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * Map from AST to AST **/ @@ -123,6 +125,18 @@ class ASTMap extends Z3Object { @Override void addToReferenceQueue() { - getContext().getASTMapDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, ASTMapRef::new); + } + + private static class ASTMapRef extends Z3ReferenceQueue.Reference { + + private ASTMapRef(ASTMap referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.astMapDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/ASTVector.java b/src/api/java/ASTVector.java index a6b436a99..8a5603fcb 100644 --- a/src/api/java/ASTVector.java +++ b/src/api/java/ASTVector.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * Vectors of ASTs. **/ @@ -101,16 +103,6 @@ public class ASTVector extends Z3Object { super(ctx, Native.mkAstVector(ctx.nCtx())); } - @Override - void incRef() { - Native.astVectorIncRef(getContext().nCtx(), getNativeObject()); - } - - @Override - void addToReferenceQueue() { - getContext().getASTVectorDRQ().storeReference(getContext(), this); - } - /** * Translates the AST vector into an AST[] * */ @@ -241,4 +233,26 @@ public class ASTVector extends Z3Object { res[i] = (RealExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } + + @Override + void incRef() { + Native.astVectorIncRef(getContext().nCtx(), getNativeObject()); + } + + @Override + void addToReferenceQueue() { + getContext().getReferenceQueue().storeReference(this, ASTVectorRef::new); + } + + private static class ASTVectorRef extends Z3ReferenceQueue.Reference { + + private ASTVectorRef(ASTVector referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.astVectorDecRef(ctx.nCtx(), z3Obj); + } + } } \ No newline at end of file diff --git a/src/api/java/ApplyResult.java b/src/api/java/ApplyResult.java index 6cfedd404..b0e035c4f 100644 --- a/src/api/java/ApplyResult.java +++ b/src/api/java/ApplyResult.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * ApplyResult objects represent the result of an application of a tactic to a * goal. It contains the subgoals that were produced. @@ -66,6 +68,18 @@ public class ApplyResult extends Z3Object { @Override void addToReferenceQueue() { - getContext().getApplyResultDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, ApplyResultRef::new); + } + + private static class ApplyResultRef extends Z3ReferenceQueue.Reference { + + private ApplyResultRef(ApplyResult referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.applyResultDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/ApplyResultDecRefQueue.java b/src/api/java/ApplyResultDecRefQueue.java deleted file mode 100644 index e1a660781..000000000 --- a/src/api/java/ApplyResultDecRefQueue.java +++ /dev/null @@ -1,31 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - ApplyResultDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class ApplyResultDecRefQueue extends IDecRefQueue -{ - public ApplyResultDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.applyResultDecRef(ctx.nCtx(), obj); - } -}; diff --git a/src/api/java/AstMapDecRefQueue.java b/src/api/java/AstMapDecRefQueue.java deleted file mode 100644 index 6c96970b7..000000000 --- a/src/api/java/AstMapDecRefQueue.java +++ /dev/null @@ -1,30 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - AstMapDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class ASTMapDecRefQueue extends IDecRefQueue { - public ASTMapDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.astMapDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/AstVectorDecRefQueue.java b/src/api/java/AstVectorDecRefQueue.java deleted file mode 100644 index e7ce3e33e..000000000 --- a/src/api/java/AstVectorDecRefQueue.java +++ /dev/null @@ -1,30 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - AstVectorDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class ASTVectorDecRefQueue extends IDecRefQueue { - public ASTVectorDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.astVectorDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index 9501de3eb..c5221014f 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -91,17 +91,13 @@ add_custom_command(OUTPUT ${Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH} set(Z3_JAVA_JAR_SOURCE_FILES AlgebraicNum.java - ApplyResultDecRefQueue.java ApplyResult.java ArithExpr.java ArithSort.java ArrayExpr.java ArraySort.java - ASTDecRefQueue.java AST.java - AstMapDecRefQueue.java ASTMap.java - AstVectorDecRefQueue.java ASTVector.java BitVecExpr.java BitVecNum.java @@ -109,9 +105,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES BoolExpr.java BoolSort.java CharSort.java - ConstructorDecRefQueue.java Constructor.java - ConstructorListDecRefQueue.java ConstructorList.java Context.java DatatypeExpr.java @@ -121,7 +115,6 @@ set(Z3_JAVA_JAR_SOURCE_FILES FiniteDomainExpr.java FiniteDomainNum.java FiniteDomainSort.java - FixedpointDecRefQueue.java Fixedpoint.java FPExpr.java FPNum.java @@ -130,13 +123,9 @@ set(Z3_JAVA_JAR_SOURCE_FILES FPRMSort.java FPSort.java FuncDecl.java - FuncInterpDecRefQueue.java - FuncInterpEntryDecRefQueue.java FuncInterp.java Global.java - GoalDecRefQueue.java Goal.java - IDecRefQueue.java IntExpr.java IntNum.java IntSort.java @@ -144,16 +133,11 @@ set(Z3_JAVA_JAR_SOURCE_FILES Lambda.java ListSort.java Log.java - ModelDecRefQueue.java Model.java - OptimizeDecRefQueue.java Optimize.java - ParamDescrsDecRefQueue.java ParamDescrs.java - ParamsDecRefQueue.java Params.java Pattern.java - ProbeDecRefQueue.java Probe.java Quantifier.java RatNum.java @@ -166,16 +150,12 @@ set(Z3_JAVA_JAR_SOURCE_FILES SeqSort.java SetSort.java Simplifier.java - SimplifierDecRefQueue.java - SolverDecRefQueue.java Solver.java Sort.java - StatisticsDecRefQueue.java Statistics.java Status.java StringSymbol.java Symbol.java - TacticDecRefQueue.java Tactic.java TupleSort.java UninterpretedSort.java @@ -183,6 +163,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES Version.java Z3Exception.java Z3Object.java + Z3ReferenceQueue.java ) set(Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH "") foreach (java_src_file ${Z3_JAVA_JAR_SOURCE_FILES}) @@ -204,11 +185,13 @@ add_custom_target(build_z3_java_bindings # Rule to build ``com.microsoft.z3.jar`` # TODO: Should we set ``CMAKE_JNI_TARGET`` to ``TRUE``? +# REMARK: removed VERSION to fix issue with using this to create installations. + add_jar(z3JavaJar SOURCES ${Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH} OUTPUT_NAME ${Z3_JAVA_PACKAGE_NAME} OUTPUT_DIR "${PROJECT_BINARY_DIR}" - VERSION "${Z3_VERSION}" +# VERSION "${Z3_VERSION}" ) ############################################################################### @@ -219,21 +202,22 @@ if (Z3_INSTALL_JAVA_BINDINGS) # Provide cache variables for the install locations that the user can change. # This defaults to ``/usr/local/java`` which seems to be the location for ``.jar`` # files on Linux distributions - set(Z3_JAVA_JAR_INSTALLDIR - "${CMAKE_INSTALL_DATAROOTDIR}/java" - CACHE - PATH - "Directory to install Z3 Java jar file relative to install prefix" - ) - # FIXME: I don't think this the right installation location - set(Z3_JAVA_JNI_LIB_INSTALLDIR - "${CMAKE_INSTALL_LIBDIR}" - CACHE - PATH - "Directory to install Z3 Java JNI bridge library relative to install prefix" - ) + if (NOT Z3_JAVA_JAR_INSTALLDIR) + set(Z3_JAVA_JAR_INSTALLDIR + "${CMAKE_INSTALL_DATAROOTDIR}/java" + CACHE + PATH + "Directory to install Z3 Java jar file relative to install prefix" + ) + endif() + if (NOT Z3_JAVA_JNI_LIB_INSTALLDIR) + set(Z3_JAVA_JNI_LIB_INSTALLDIR + "${CMAKE_INSTALL_LIBDIR}" + CACHE + PATH + "Directory to install Z3 Java JNI bridge library relative to install prefix" + ) + endif() install(TARGETS z3java DESTINATION "${Z3_JAVA_JNI_LIB_INSTALLDIR}") - # Note: Don't use ``DESTINATION`` here as the version of ``UseJava.cmake`` shipped - # with CMake 2.8.12.2 handles that incorrectly. install_jar(z3JavaJar "${Z3_JAVA_JAR_INSTALLDIR}") endif() diff --git a/src/api/java/Constructor.java b/src/api/java/Constructor.java index 59565f565..8e1766cb0 100644 --- a/src/api/java/Constructor.java +++ b/src/api/java/Constructor.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * Constructors are used for datatype sorts. **/ @@ -91,7 +93,7 @@ public class Constructor extends Z3Object { @Override void addToReferenceQueue() { - getContext().getConstructorDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, ConstructorRef::new); } static Constructor of(Context ctx, Symbol name, Symbol recognizer, @@ -114,4 +116,16 @@ public class Constructor extends Z3Object { return new Constructor<>(ctx, n, nativeObj); } + + private static class ConstructorRef extends Z3ReferenceQueue.Reference> { + + private ConstructorRef(Constructor referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.delConstructor(ctx.nCtx(), z3Obj); + } + } } diff --git a/src/api/java/ConstructorDecRefQueue.java b/src/api/java/ConstructorDecRefQueue.java deleted file mode 100644 index a76b26bb7..000000000 --- a/src/api/java/ConstructorDecRefQueue.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.microsoft.z3; - -public class ConstructorDecRefQueue extends IDecRefQueue> { - public ConstructorDecRefQueue() { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.delConstructor(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/ConstructorList.java b/src/api/java/ConstructorList.java index d015c51c0..577c802f0 100644 --- a/src/api/java/ConstructorList.java +++ b/src/api/java/ConstructorList.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * Lists of constructors **/ @@ -34,7 +36,7 @@ public class ConstructorList extends Z3Object { @Override void addToReferenceQueue() { - getContext().getConstructorListDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, ConstructorListRef::new); } ConstructorList(Context ctx, Constructor[] constructors) @@ -43,4 +45,16 @@ public class ConstructorList extends Z3Object { constructors.length, Constructor.arrayToNative(constructors))); } + + private static class ConstructorListRef extends Z3ReferenceQueue.Reference> { + + private ConstructorListRef(ConstructorList referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.delConstructorList(ctx.nCtx(), z3Obj); + } + } } diff --git a/src/api/java/ConstructorListDecRefQueue.java b/src/api/java/ConstructorListDecRefQueue.java deleted file mode 100644 index 2f5dfcb35..000000000 --- a/src/api/java/ConstructorListDecRefQueue.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.microsoft.z3; - -public class ConstructorListDecRefQueue extends IDecRefQueue> { - public ConstructorListDecRefQueue() { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.delConstructorList(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/Context.java b/src/api/java/Context.java index ad690b7e9..f3efa632a 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -4319,119 +4319,9 @@ public class Context implements AutoCloseable { checkContextMatch(a); } - private ASTDecRefQueue m_AST_DRQ = new ASTDecRefQueue(); - private ASTMapDecRefQueue m_ASTMap_DRQ = new ASTMapDecRefQueue(); - private ASTVectorDecRefQueue m_ASTVector_DRQ = new ASTVectorDecRefQueue(); - private ApplyResultDecRefQueue m_ApplyResult_DRQ = new ApplyResultDecRefQueue(); - private FuncInterpEntryDecRefQueue m_FuncEntry_DRQ = new FuncInterpEntryDecRefQueue(); - private FuncInterpDecRefQueue m_FuncInterp_DRQ = new FuncInterpDecRefQueue(); - private GoalDecRefQueue m_Goal_DRQ = new GoalDecRefQueue(); - private ModelDecRefQueue m_Model_DRQ = new ModelDecRefQueue(); - private ParamsDecRefQueue m_Params_DRQ = new ParamsDecRefQueue(); - private ParamDescrsDecRefQueue m_ParamDescrs_DRQ = new ParamDescrsDecRefQueue(); - private ProbeDecRefQueue m_Probe_DRQ = new ProbeDecRefQueue(); - private SolverDecRefQueue m_Solver_DRQ = new SolverDecRefQueue(); - private StatisticsDecRefQueue m_Statistics_DRQ = new StatisticsDecRefQueue(); - private TacticDecRefQueue m_Tactic_DRQ = new TacticDecRefQueue(); - private SimplifierDecRefQueue m_Simplifier_DRQ = new SimplifierDecRefQueue(); - private FixedpointDecRefQueue m_Fixedpoint_DRQ = new FixedpointDecRefQueue(); - private OptimizeDecRefQueue m_Optimize_DRQ = new OptimizeDecRefQueue(); - private ConstructorDecRefQueue m_Constructor_DRQ = new ConstructorDecRefQueue(); - private ConstructorListDecRefQueue m_ConstructorList_DRQ = - new ConstructorListDecRefQueue(); + private Z3ReferenceQueue m_RefQueue = new Z3ReferenceQueue(this); - public IDecRefQueue> getConstructorDRQ() { - return m_Constructor_DRQ; - } - - public IDecRefQueue> getConstructorListDRQ() { - return m_ConstructorList_DRQ; - } - - public IDecRefQueue getASTDRQ() - { - return m_AST_DRQ; - } - - public IDecRefQueue getASTMapDRQ() - { - return m_ASTMap_DRQ; - } - - public IDecRefQueue getASTVectorDRQ() - { - return m_ASTVector_DRQ; - } - - public IDecRefQueue getApplyResultDRQ() - { - return m_ApplyResult_DRQ; - } - - public IDecRefQueue> getFuncEntryDRQ() - { - return m_FuncEntry_DRQ; - } - - public IDecRefQueue> getFuncInterpDRQ() - { - return m_FuncInterp_DRQ; - } - - public IDecRefQueue getGoalDRQ() - { - return m_Goal_DRQ; - } - - public IDecRefQueue getModelDRQ() - { - return m_Model_DRQ; - } - - public IDecRefQueue getParamsDRQ() - { - return m_Params_DRQ; - } - - public IDecRefQueue getParamDescrsDRQ() - { - return m_ParamDescrs_DRQ; - } - - public IDecRefQueue getProbeDRQ() - { - return m_Probe_DRQ; - } - - public IDecRefQueue getSolverDRQ() - { - return m_Solver_DRQ; - } - - public IDecRefQueue getStatisticsDRQ() - { - return m_Statistics_DRQ; - } - - public IDecRefQueue getTacticDRQ() - { - return m_Tactic_DRQ; - } - - public IDecRefQueue getSimplifierDRQ() - { - return m_Simplifier_DRQ; - } - - public IDecRefQueue getFixedpointDRQ() - { - return m_Fixedpoint_DRQ; - } - - public IDecRefQueue getOptimizeDRQ() - { - return m_Optimize_DRQ; - } + Z3ReferenceQueue getReferenceQueue() { return m_RefQueue; } /** * Disposes of the context. @@ -4439,27 +4329,16 @@ public class Context implements AutoCloseable { @Override public void close() { - m_AST_DRQ.forceClear(this); - m_ASTMap_DRQ.forceClear(this); - m_ASTVector_DRQ.forceClear(this); - m_ApplyResult_DRQ.forceClear(this); - m_FuncEntry_DRQ.forceClear(this); - m_FuncInterp_DRQ.forceClear(this); - m_Goal_DRQ.forceClear(this); - m_Model_DRQ.forceClear(this); - m_Params_DRQ.forceClear(this); - m_Probe_DRQ.forceClear(this); - m_Solver_DRQ.forceClear(this); - m_Optimize_DRQ.forceClear(this); - m_Statistics_DRQ.forceClear(this); - m_Tactic_DRQ.forceClear(this); - m_Simplifier_DRQ.forceClear(this); - m_Fixedpoint_DRQ.forceClear(this); + if (m_ctx == 0) + return; + + m_RefQueue.forceClear(); m_boolSort = null; m_intSort = null; m_realSort = null; m_stringSort = null; + m_RefQueue = null; synchronized (creation_lock) { Native.delContext(m_ctx); diff --git a/src/api/java/Fixedpoint.java b/src/api/java/Fixedpoint.java index 96e1dd0cb..c35f15e96 100644 --- a/src/api/java/Fixedpoint.java +++ b/src/api/java/Fixedpoint.java @@ -19,6 +19,8 @@ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_lbool; +import java.lang.ref.ReferenceQueue; + /** * Object for managing fixedpoints **/ @@ -327,9 +329,18 @@ public class Fixedpoint extends Z3Object @Override void addToReferenceQueue() { - getContext().getFixedpointDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, FixedpointRef::new); } - @Override - void checkNativeObject(long obj) { } + private static class FixedpointRef extends Z3ReferenceQueue.Reference { + + private FixedpointRef(Fixedpoint referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.fixedpointDecRef(ctx.nCtx(), z3Obj); + } + } } diff --git a/src/api/java/FixedpointDecRefQueue.java b/src/api/java/FixedpointDecRefQueue.java deleted file mode 100644 index 69ed82092..000000000 --- a/src/api/java/FixedpointDecRefQueue.java +++ /dev/null @@ -1,31 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - FixedpointDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class FixedpointDecRefQueue extends IDecRefQueue { - public FixedpointDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) - { - Native.fixedpointDecRef(ctx.nCtx(), obj); - } -}; diff --git a/src/api/java/FuncInterp.java b/src/api/java/FuncInterp.java index 64f96534b..3d2affc4a 100644 --- a/src/api/java/FuncInterp.java +++ b/src/api/java/FuncInterp.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * A function interpretation is represented as a finite map and an 'else' value. * Each entry in the finite map represents the value of a function given a set @@ -93,7 +95,19 @@ public class FuncInterp extends Z3Object { @Override void addToReferenceQueue() { - getContext().getFuncEntryDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, FuncEntryRef::new); + } + + private static class FuncEntryRef extends Z3ReferenceQueue.Reference> { + + private FuncEntryRef(Entry referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.funcEntryDecRef(ctx.nCtx(), z3Obj); + } } } @@ -186,6 +200,18 @@ public class FuncInterp extends Z3Object { @Override void addToReferenceQueue() { - getContext().getFuncInterpDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, FuncInterpRef::new); + } + + private static class FuncInterpRef extends Z3ReferenceQueue.Reference> { + + private FuncInterpRef(FuncInterp referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.funcInterpDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/FuncInterpDecRefQueue.java b/src/api/java/FuncInterpDecRefQueue.java deleted file mode 100644 index 06a6f2af8..000000000 --- a/src/api/java/FuncInterpDecRefQueue.java +++ /dev/null @@ -1,31 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - FuncInterpDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class FuncInterpDecRefQueue extends IDecRefQueue> -{ - public FuncInterpDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.funcInterpDecRef(ctx.nCtx(), obj); - } -}; diff --git a/src/api/java/FuncInterpEntryDecRefQueue.java b/src/api/java/FuncInterpEntryDecRefQueue.java deleted file mode 100644 index 77bb78f5b..000000000 --- a/src/api/java/FuncInterpEntryDecRefQueue.java +++ /dev/null @@ -1,30 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - FuncInterpEntryDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class FuncInterpEntryDecRefQueue extends IDecRefQueue> { - public FuncInterpEntryDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.funcEntryDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/Goal.java b/src/api/java/Goal.java index 3326f81fe..e79ca7b74 100644 --- a/src/api/java/Goal.java +++ b/src/api/java/Goal.java @@ -19,6 +19,8 @@ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_goal_prec; +import java.lang.ref.ReferenceQueue; + /** * A goal (aka problem). A goal is essentially a set of formulas, that can be * solved and/or transformed using tactics and solvers. @@ -262,6 +264,18 @@ public class Goal extends Z3Object { @Override void addToReferenceQueue() { - getContext().getGoalDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, GoalRef::new); + } + + private static class GoalRef extends Z3ReferenceQueue.Reference { + + private GoalRef(Goal referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.goalDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/GoalDecRefQueue.java b/src/api/java/GoalDecRefQueue.java deleted file mode 100644 index 90bad1fb1..000000000 --- a/src/api/java/GoalDecRefQueue.java +++ /dev/null @@ -1,30 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - GoalDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class GoalDecRefQueue extends IDecRefQueue { - public GoalDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.goalDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/IDecRefQueue.java b/src/api/java/IDecRefQueue.java deleted file mode 100644 index b9ece3172..000000000 --- a/src/api/java/IDecRefQueue.java +++ /dev/null @@ -1,83 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - IDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -import java.lang.ref.PhantomReference; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.util.IdentityHashMap; -import java.util.Map; - -/** - * A queue to handle management of native memory. - * - *

Mechanics: once an object is created, a metadata is stored for it in - * {@code referenceMap}, and a {@link PhantomReference} is created with a - * reference to {@code referenceQueue}. - * Once the object becomes strongly unreachable, the phantom reference gets - * added by JVM to the {@code referenceQueue}. - * After each object creation, we iterate through the available objects in - * {@code referenceQueue} and decrement references for them. - * - * @param Type of object stored in queue. - */ -public abstract class IDecRefQueue { - private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); - private final Map, Long> referenceMap = - new IdentityHashMap<>(); - - protected IDecRefQueue() {} - - /** - * An implementation of this method should decrement the reference on a - * given native object. - * This function should always be called on the {@code ctx} thread. - * - * @param ctx Z3 context. - * @param obj Pointer to a Z3 object. - */ - protected abstract void decRef(Context ctx, long obj); - - public void storeReference(Context ctx, T obj) { - PhantomReference ref = new PhantomReference<>(obj, referenceQueue); - referenceMap.put(ref, obj.getNativeObject()); - clear(ctx); - } - - /** - * Clean all references currently in {@code referenceQueue}. - */ - protected void clear(Context ctx) - { - Reference ref; - while ((ref = referenceQueue.poll()) != null) { - long z3ast = referenceMap.remove(ref); - decRef(ctx, z3ast); - } - } - - /** - * Clean all references stored in {@code referenceMap}, - * regardless of whether they are in {@code referenceMap} or not. - */ - public void forceClear(Context ctx) { - for (long ref : referenceMap.values()) { - decRef(ctx, ref); - } - } -} diff --git a/src/api/java/Model.java b/src/api/java/Model.java index ffc4dd47f..cf9ab4b64 100644 --- a/src/api/java/Model.java +++ b/src/api/java/Model.java @@ -19,6 +19,8 @@ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_sort_kind; +import java.lang.ref.ReferenceQueue; + /** * A Model contains interpretations (assignments) of constants and functions. **/ @@ -296,6 +298,18 @@ public class Model extends Z3Object { @Override void addToReferenceQueue() { - getContext().getModelDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, ModelRef::new); + } + + private static class ModelRef extends Z3ReferenceQueue.Reference { + + private ModelRef(Model referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.modelDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/ModelDecRefQueue.java b/src/api/java/ModelDecRefQueue.java deleted file mode 100644 index f1b7c3fdd..000000000 --- a/src/api/java/ModelDecRefQueue.java +++ /dev/null @@ -1,30 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - ModelDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class ModelDecRefQueue extends IDecRefQueue { - public ModelDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.modelDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/NativeStatic.txt b/src/api/java/NativeStatic.txt index 9507130fd..21d6ba075 100644 --- a/src/api/java/NativeStatic.txt +++ b/src/api/java/NativeStatic.txt @@ -150,7 +150,7 @@ static void final_eh(void* _p, Z3_solver_callback cb) { static void decide_eh(void* _p, Z3_solver_callback cb, Z3_ast _val, unsigned bit, bool is_pos) { JavaInfo *info = static_cast(_p); ScopedCB scoped(info, cb); - info->jenv->CallVoidMethod(info->jobj, info->decide, (jlong)_val); + info->jenv->CallVoidMethod(info->jobj, info->decide, (jlong)_val, bit, is_pos); } DLL_VIS JNIEXPORT jlong JNICALL Java_com_microsoft_z3_Native_propagateInit(JNIEnv *jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { @@ -166,7 +166,7 @@ DLL_VIS JNIEXPORT jlong JNICALL Java_com_microsoft_z3_Native_propagateInit(JNIEn info->fixed = jenv->GetMethodID(jcls, "fixedWrapper", "(JJ)V"); info->eq = jenv->GetMethodID(jcls, "eqWrapper", "(JJ)V"); info->final = jenv->GetMethodID(jcls, "finWrapper", "()V"); - info->decide = jenv->GetMethodID(jcls, "decideWrapper", "(JII)V"); + info->decide = jenv->GetMethodID(jcls, "decideWrapper", "(JIZ)V"); if (!info->push || !info->pop || !info->fresh || !info->created || !info->fixed || !info->eq || !info->final || !info->decide) { assert(false); @@ -203,15 +203,16 @@ DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterDec Z3_solver_propagate_decide((Z3_context)ctx, (Z3_solver)solver, decide_eh); } -DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateConflict(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, long num_fixed, jlongArray fixed, long num_eqs, jlongArray eq_lhs, jlongArray eq_rhs, jlong conseq) { +DLL_VIS JNIEXPORT jboolean JNICALL Java_com_microsoft_z3_Native_propagateConsequence(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, long num_fixed, jlongArray fixed, long num_eqs, jlongArray eq_lhs, jlongArray eq_rhs, jlong conseq) { JavaInfo *info = (JavaInfo*)javainfo; GETLONGAELEMS(Z3_ast, fixed, _fixed); GETLONGAELEMS(Z3_ast, eq_lhs, _eq_lhs); GETLONGAELEMS(Z3_ast, eq_rhs, _eq_rhs); - Z3_solver_propagate_consequence((Z3_context)ctx, info->cb, num_fixed, _fixed, num_eqs, _eq_lhs, _eq_rhs, (Z3_ast)conseq); + bool retval = Z3_solver_propagate_consequence((Z3_context)ctx, info->cb, num_fixed, _fixed, num_eqs, _eq_lhs, _eq_rhs, (Z3_ast)conseq); RELEASELONGAELEMS(fixed, _fixed); RELEASELONGAELEMS(eq_lhs, _eq_lhs); RELEASELONGAELEMS(eq_rhs, _eq_rhs); + return (jboolean) retval; } DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateAdd(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, jlong e) { @@ -227,8 +228,8 @@ DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateAdd(JNIEnv } -DLL_VIS JNIEXPORT bool JNICALL Java_com_microsoft_z3_Native_propagateNextSplit(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, jlong e, long idx, int phase) { +DLL_VIS JNIEXPORT jboolean JNICALL Java_com_microsoft_z3_Native_propagateNextSplit(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, jlong e, long idx, int phase) { JavaInfo *info = (JavaInfo*)javainfo; Z3_solver_callback cb = info->cb; - return Z3_solver_next_split((Z3_context)ctx, cb, (Z3_ast)e, idx, Z3_lbool(phase)); + return (jboolean) Z3_solver_next_split((Z3_context)ctx, cb, (Z3_ast)e, idx, Z3_lbool(phase)); } diff --git a/src/api/java/Optimize.java b/src/api/java/Optimize.java index 83833e36b..9679a96cd 100644 --- a/src/api/java/Optimize.java +++ b/src/api/java/Optimize.java @@ -20,6 +20,8 @@ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_lbool; +import java.lang.ref.ReferenceQueue; + /** * Object for managing optimization context @@ -421,6 +423,18 @@ public class Optimize extends Z3Object { @Override void addToReferenceQueue() { - getContext().getOptimizeDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, OptimizeRef::new); + } + + private static class OptimizeRef extends Z3ReferenceQueue.Reference { + + private OptimizeRef(Optimize referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.optimizeDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/OptimizeDecRefQueue.java b/src/api/java/OptimizeDecRefQueue.java deleted file mode 100644 index 0acf20068..000000000 --- a/src/api/java/OptimizeDecRefQueue.java +++ /dev/null @@ -1,30 +0,0 @@ -/** -Copyright (c) 2012-2015 Microsoft Corporation - -Module Name: - - OptimizeDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class OptimizeDecRefQueue extends IDecRefQueue { - public OptimizeDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.optimizeDecRef(ctx.nCtx(), obj); - } -}; diff --git a/src/api/java/ParamDescrs.java b/src/api/java/ParamDescrs.java index fdaf29647..0695f8fe0 100644 --- a/src/api/java/ParamDescrs.java +++ b/src/api/java/ParamDescrs.java @@ -19,6 +19,8 @@ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_param_kind; +import java.lang.ref.ReferenceQueue; + /** * A ParamDescrs describes a set of parameters. **/ @@ -97,6 +99,18 @@ public class ParamDescrs extends Z3Object { @Override void addToReferenceQueue() { - getContext().getParamDescrsDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, ParamDescrsRef::new); + } + + private static class ParamDescrsRef extends Z3ReferenceQueue.Reference { + + private ParamDescrsRef(ParamDescrs referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.paramDescrsDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/ParamDescrsDecRefQueue.java b/src/api/java/ParamDescrsDecRefQueue.java deleted file mode 100644 index ee3257db9..000000000 --- a/src/api/java/ParamDescrsDecRefQueue.java +++ /dev/null @@ -1,31 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - ParamDescrsDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class ParamDescrsDecRefQueue extends IDecRefQueue { - public ParamDescrsDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) - { - Native.paramDescrsDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/Params.java b/src/api/java/Params.java index a76dd3cab..1edfa67ba 100644 --- a/src/api/java/Params.java +++ b/src/api/java/Params.java @@ -18,6 +18,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * A ParameterSet represents a configuration in the form of Symbol/value pairs. **/ @@ -130,6 +132,18 @@ public class Params extends Z3Object { @Override void addToReferenceQueue() { - getContext().getParamsDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, ParamsRef::new); + } + + private static class ParamsRef extends Z3ReferenceQueue.Reference { + + private ParamsRef(Params referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.paramsDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/ParamsDecRefQueue.java b/src/api/java/ParamsDecRefQueue.java deleted file mode 100644 index 349713f67..000000000 --- a/src/api/java/ParamsDecRefQueue.java +++ /dev/null @@ -1,30 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - ParamDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class ParamsDecRefQueue extends IDecRefQueue { - public ParamsDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.paramsDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/Probe.java b/src/api/java/Probe.java index a36f3b64b..cb4b13442 100644 --- a/src/api/java/Probe.java +++ b/src/api/java/Probe.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * Probes are used to inspect a goal (aka problem) and collect information that * may be used to decide which solver and/or preprocessing step will be used. @@ -56,6 +58,18 @@ public class Probe extends Z3Object { @Override void addToReferenceQueue() { - getContext().getProbeDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, ProbeRef::new); + } + + private static class ProbeRef extends Z3ReferenceQueue.Reference { + + private ProbeRef(Probe referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.probeDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/ProbeDecRefQueue.java b/src/api/java/ProbeDecRefQueue.java deleted file mode 100644 index b25446c0c..000000000 --- a/src/api/java/ProbeDecRefQueue.java +++ /dev/null @@ -1,32 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - ProbeDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class ProbeDecRefQueue extends IDecRefQueue -{ - public ProbeDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) - { - Native.probeDecRef(ctx.nCtx(), obj); - } -}; diff --git a/src/api/java/Simplifier.java b/src/api/java/Simplifier.java index b3fc89ccf..c89241a7d 100644 --- a/src/api/java/Simplifier.java +++ b/src/api/java/Simplifier.java @@ -18,6 +18,8 @@ Author: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + public class Simplifier extends Z3Object { /* * A string containing a description of parameters accepted by the simplifier. @@ -32,7 +34,7 @@ public class Simplifier extends Z3Object { * Retrieves parameter descriptions for Simplifiers. */ public ParamDescrs getParameterDescriptions() { - return new ParamDescrs(getContext(), Native.simplifierGetParamDescrs(getContext().nCtx(), getNativeObject())); + return new ParamDescrs(getContext(), Native.simplifierGetParamDescrs(getContext().nCtx(), getNativeObject())); } Simplifier(Context ctx, long obj) @@ -53,6 +55,18 @@ public class Simplifier extends Z3Object { @Override void addToReferenceQueue() { - getContext().getSimplifierDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, SimplifierRef::new); } -} \ No newline at end of file + + private static class SimplifierRef extends Z3ReferenceQueue.Reference { + + private SimplifierRef(Simplifier referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.simplifierDecRef(ctx.nCtx(), z3Obj); + } + } +} \ No newline at end of file diff --git a/src/api/java/SimplifierDecRefQueue.java b/src/api/java/SimplifierDecRefQueue.java deleted file mode 100644 index ba15dd5be..000000000 --- a/src/api/java/SimplifierDecRefQueue.java +++ /dev/null @@ -1,31 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - SimplifierDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class SimplifierDecRefQueue extends IDecRefQueue { - public SimplifierDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) - { - Native.simplifierDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/Solver.java b/src/api/java/Solver.java index ce795d758..b814a4db6 100644 --- a/src/api/java/Solver.java +++ b/src/api/java/Solver.java @@ -19,6 +19,8 @@ Notes: package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_lbool; + +import java.lang.ref.ReferenceQueue; import java.util.*; /** @@ -403,6 +405,18 @@ public class Solver extends Z3Object { @Override void addToReferenceQueue() { - getContext().getSolverDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, SolverRef::new); + } + + private static class SolverRef extends Z3ReferenceQueue.Reference { + + private SolverRef(Solver referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.solverDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/SolverDecRefQueue.java b/src/api/java/SolverDecRefQueue.java deleted file mode 100644 index efa15d939..000000000 --- a/src/api/java/SolverDecRefQueue.java +++ /dev/null @@ -1,27 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - SolverDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class SolverDecRefQueue extends IDecRefQueue { - public SolverDecRefQueue() { super(); } - - @Override - protected void decRef(Context ctx, long obj) { - Native.solverDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/Statistics.java b/src/api/java/Statistics.java index d509424ed..6d42e1af0 100644 --- a/src/api/java/Statistics.java +++ b/src/api/java/Statistics.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * Objects of this class track statistical information about solvers. **/ @@ -25,7 +27,7 @@ public class Statistics extends Z3Object { * Statistical data is organized into pairs of [Key, Entry], where every * Entry is either a {@code DoubleEntry} or a {@code UIntEntry} **/ - public class Entry + public static class Entry { /** * The key of the entry. @@ -191,11 +193,23 @@ public class Statistics extends Z3Object { @Override void incRef() { - getContext().getStatisticsDRQ().storeReference(getContext(), this); + Native.statsIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { - Native.statsIncRef(getContext().nCtx(), getNativeObject()); + getContext().getReferenceQueue().storeReference(this, StatisticsRef::new); + } + + private static class StatisticsRef extends Z3ReferenceQueue.Reference { + + private StatisticsRef(Statistics referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.statsDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/StatisticsDecRefQueue.java b/src/api/java/StatisticsDecRefQueue.java deleted file mode 100644 index ed698e4ca..000000000 --- a/src/api/java/StatisticsDecRefQueue.java +++ /dev/null @@ -1,30 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - StatisticsDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class StatisticsDecRefQueue extends IDecRefQueue { - public StatisticsDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) { - Native.statsDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/Tactic.java b/src/api/java/Tactic.java index 11d02ca73..d70d8a409 100644 --- a/src/api/java/Tactic.java +++ b/src/api/java/Tactic.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import java.lang.ref.ReferenceQueue; + /** * Tactics are the basic building block for creating custom solvers for specific * problem domains. The complete list of tactics may be obtained using @@ -98,6 +100,19 @@ public class Tactic extends Z3Object { @Override void addToReferenceQueue() { - getContext().getTacticDRQ().storeReference(getContext(), this); + //getContext().getTacticDRQ().storeReference(getContext(), this); + getContext().getReferenceQueue().storeReference(this, TacticRef::new); + } + + private static class TacticRef extends Z3ReferenceQueue.Reference { + + private TacticRef(Tactic referent, ReferenceQueue q) { + super(referent, q); + } + + @Override + void decRef(Context ctx, long z3Obj) { + Native.tacticDecRef(ctx.nCtx(), z3Obj); + } } } diff --git a/src/api/java/TacticDecRefQueue.java b/src/api/java/TacticDecRefQueue.java deleted file mode 100644 index 8f151f25c..000000000 --- a/src/api/java/TacticDecRefQueue.java +++ /dev/null @@ -1,31 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - TacticDecRefQueue.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -class TacticDecRefQueue extends IDecRefQueue { - public TacticDecRefQueue() - { - super(); - } - - @Override - protected void decRef(Context ctx, long obj) - { - Native.tacticDecRef(ctx.nCtx(), obj); - } -} diff --git a/src/api/java/UserPropagatorBase.java b/src/api/java/UserPropagatorBase.java index 407d3d0da..46a61400d 100644 --- a/src/api/java/UserPropagatorBase.java +++ b/src/api/java/UserPropagatorBase.java @@ -60,36 +60,47 @@ public abstract class UserPropagatorBase extends Native.UserPropagatorBase { fixed(var, value); } + @Override + protected final void decideWrapper(long lvar, int bit, boolean is_pos) { + Expr var = new Expr(ctx, lvar); + decide(var, bit, is_pos); + } + public abstract void push(); public abstract void pop(int number); public abstract UserPropagatorBase fresh(Context ctx); - public void created(Expr ast) {} + public void created(Expr ast) {} - public void fixed(Expr var, Expr value) {} + public void fixed(Expr var, Expr value) {} - public void eq(Expr x, Expr y) {} + public void eq(Expr x, Expr y) {} + + public void decide(Expr var, int bit, boolean is_pos) {} public void fin() {} - public final void add(Expr expr) { + public final void add(Expr expr) { Native.propagateAdd(this, ctx.nCtx(), solver.getNativeObject(), javainfo, expr.getNativeObject()); } - public final void conflict(Expr[] fixed) { - conflict(fixed, new Expr[0], new Expr[0]); + public final boolean conflict(Expr[] fixed) { + return conflict(fixed, new Expr[0], new Expr[0]); } - public final void conflict(Expr[] fixed, Expr[] lhs, Expr[] rhs) { - AST conseq = ctx.mkBool(false); - Native.propagateConflict( + public final boolean conflict(Expr[] fixed, Expr[] lhs, Expr[] rhs) { + return consequence(fixed, lhs, rhs, ctx.mkBool(false)); + } + + public final boolean consequence(Expr[] fixed, Expr[] lhs, Expr[] rhs, Expr conseq) { + return Native.propagateConsequence( this, ctx.nCtx(), solver.getNativeObject(), javainfo, fixed.length, AST.arrayToNative(fixed), lhs.length, AST.arrayToNative(lhs), AST.arrayToNative(rhs), conseq.getNativeObject()); } - public final boolean nextSplit(Expr e, long idx, Z3_lbool phase) { + public final boolean nextSplit(Expr e, long idx, Z3_lbool phase) { return Native.propagateNextSplit( this, ctx.nCtx(), solver.getNativeObject(), javainfo, e.getNativeObject(), idx, phase.toInt()); diff --git a/src/api/java/Z3ReferenceQueue.java b/src/api/java/Z3ReferenceQueue.java new file mode 100644 index 000000000..22435599f --- /dev/null +++ b/src/api/java/Z3ReferenceQueue.java @@ -0,0 +1,144 @@ +/** +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + IDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ + +package com.microsoft.z3; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; + +/** + * A queue to handle management of native memory. + * + *

Mechanics: When an object is created, a so-called {@link PhantomReference} + * is constructed that is associated with the created object and the reference queue {@code referenceQueue}. + * Once the object becomes strongly unreachable, the phantom reference gets + * added by JVM to the {@code referenceQueue}. + * After each object creation, we iterate through the available objects in + * {@code referenceQueue} and decrement references for them. + *

+ * In order for this to work, we need to (i) associate to each phantom reference the underlying + * native object (and its type) that it references and (ii) keep the phantom references themselves alive, so they are not + * garbage collected before the object they reference. + * We use a doubly-linked list of custom phantom references, subclasses of {@link Reference}, to achieve this. + * + */ +class Z3ReferenceQueue { + private final Context ctx; + private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private final Reference referenceList = emptyList(); + + Z3ReferenceQueue(Context ctx) { + this.ctx = ctx; + } + + /** + * Create and store a new phantom reference. + */ + void storeReference(T z3Object, ReferenceConstructor refConstructor) { + referenceList.insert(refConstructor.construct(z3Object, referenceQueue)); + clear(); + } + + /** + * Clean all references currently in {@code referenceQueue}. + */ + private void clear() { + Reference ref; + while ((ref = (Reference)referenceQueue.poll()) != null) { + ref.cleanup(ctx); + } + } + + /** + * Clean all references stored in {@code referenceList}, + * regardless of whether they are in {@code referenceQueue} or not. + */ + @SuppressWarnings("StatementWithEmptyBody") + public void forceClear() { + // Decrement all reference counters + Reference cur = referenceList.next; + while (cur.next != null) { + cur.decRef(ctx, cur.nativePtr); + cur = cur.next; + } + + // Bulk-delete the reference list's entries + referenceList.next = cur; + cur.prev = referenceList; + + // Empty the reference queue so that there are no living phantom references anymore. + // This makes sure that all stored phantom references can be GC'd now. + while (referenceQueue.poll() != null) {} + } + + private static Reference emptyList() { + Reference head = new DummyReference(); + Reference tail = new DummyReference(); + head.next = tail; + tail.prev = head; + return head; + } + + // ================================================================================================================ + + @FunctionalInterface + interface ReferenceConstructor { + Reference construct(T reference, ReferenceQueue queue); + } + + abstract static class Reference extends PhantomReference { + + private Reference prev; + private Reference next; + private final long nativePtr; + + Reference(T referent, ReferenceQueue q) { + super(referent, q); + this.nativePtr = referent != null ? referent.getNativeObject() : 0; + } + + private void cleanup(Context ctx) { + decRef(ctx, nativePtr); + assert (prev != null && next != null); + prev.next = next; + next.prev = prev; + } + + private void insert(Reference ref) { + assert next != null; + ref.prev = this; + ref.next = this.next; + ref.next.prev = ref; + next = ref; + } + + abstract void decRef(Context ctx, long z3Obj); + } + + private static class DummyReference extends Reference { + + public DummyReference() { + super(null, null); + } + + @Override + void decRef(Context ctx, long z3Obj) { + // Should never be called. + assert false; + } + } +} diff --git a/src/api/julia/README.md b/src/api/julia/README.md index ec4b1e109..12e846191 100644 --- a/src/api/julia/README.md +++ b/src/api/julia/README.md @@ -14,7 +14,10 @@ make ## Julia part -The Z3 binaries are provided to [Z3.jl](https://github.com/ahumenberger/Z3.jl) via [z3_jll.jl](https://github.com/JuliaBinaryWrappers/z3_jll.jl). That is, in order to release a new Z3 version one has to update the corresponding [build script](https://github.com/JuliaPackaging/Yggdrasil/tree/master/Z/z3) which triggers a new version of z3_jll.jl. +The Z3 binaries are provided to [Z3.jl](https://github.com/ahumenberger/Z3.jl) via [z3_jll.jl](https://github.com/JuliaBinaryWrappers/z3_jll.jl). +That is, in order to propagate any C++ changes to the Julia side, one has to: +1. Release a new version of Z3. +2. Update the corresponding [build script](https://github.com/JuliaPackaging/Yggdrasil/tree/master/Z/z3) to use the new Z3 release. ### Using the compiled version of Z3 diff --git a/src/api/julia/z3jl.cpp b/src/api/julia/z3jl.cpp index 5aef2f41d..336a73976 100644 --- a/src/api/julia/z3jl.cpp +++ b/src/api/julia/z3jl.cpp @@ -303,6 +303,8 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m) m.method("xnor", &xnor); m.method("min", &min); m.method("max", &max); + m.method("exists", static_cast(&exists)); + m.method("forall", static_cast(&forall)); m.method("abs", static_cast(&abs)); m.method("sqrt", static_cast(&sqrt)); m.method("fma", static_cast(&fma)); diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 5bd554313..166e474d9 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1832,6 +1832,9 @@ struct let get (x:statistics) (key:string) = try Some(List.find (fun c -> Entry.get_key c = key) (get_entries x)) with | Not_found -> None + + let get_estimated_alloc_size = + Z3native.get_estimated_alloc_size end diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index bb3e70875..5320fc38e 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3224,6 +3224,9 @@ sig (** The value of a particular statistical counter. *) val get : statistics -> string -> Entry.statistics_entry option + + (** The estimated allocated memory in bytes. *) + val get_estimated_alloc_size : unit -> int64 end (** Solvers *) diff --git a/src/api/python/setup.py b/src/api/python/setup.py index 95f717c75..c3f65f848 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -313,11 +313,12 @@ if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: osver = RELEASE_METADATA[3] if osver.count('.') > 1: osver = '.'.join(osver.split('.')[:2]) - osver = osver.replace('.','_') + if osver.startswith("11"): + osver = "11_0" if arch == 'x64': - plat_name ='macosx_%s_x86_64' % re.sub(r'\A(1[1-9])(_[\d]+)*\Z', r'\1_0', osver) + plat_name ='macosx_%s_x86_64' % osver.replace('.', '_') elif arch == 'arm64': - plat_name ='macosx_%s_arm64' % osver + plat_name ='macosx_%s_arm64' % osver.replace('.', '_') else: raise Exception(f"idk how os {distos} {osver} works. what goes here?") else: @@ -340,7 +341,7 @@ setup( license='MIT License', keywords=['z3', 'smt', 'sat', 'prover', 'theorem'], packages=['z3'], - install_requires = ['importlib-resources'], + install_requires = ["importlib-resources; python_version < '3.9'"], include_package_data=True, package_data={ 'z3': [os.path.join('lib', '*'), os.path.join('include', '*.h'), os.path.join('include', 'c++', '*.h')] diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 18d3abc9d..16db39afd 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -8984,7 +8984,7 @@ def substitute_funs(t, *m): m = m1 if z3_debug(): _z3_assert(is_expr(t), "Z3 expression expected") - _z3_assert(all([isinstance(p, tuple) and is_func_decl(p[0]) and is_expr(p[1]) for p in m]), "Z3 invalid substitution, funcion pairs expected.") + _z3_assert(all([isinstance(p, tuple) and is_func_decl(p[0]) and is_expr(p[1]) for p in m]), "Z3 invalid substitution, function pairs expected.") num = len(m) _from = (FuncDecl * num)() _to = (Ast * num)() @@ -9069,7 +9069,7 @@ def AtMost(*args): def AtLeast(*args): - """Create an at-most Pseudo-Boolean k constraint. + """Create an at-least Pseudo-Boolean k constraint. >>> a, b, c = Bools('a b c') >>> f = AtLeast(a, b, c, 2) @@ -10969,10 +10969,10 @@ def CharVal(ch, ctx=None): raise Z3Exception("character value should be an ordinal") return _to_expr_ref(Z3_mk_char(ctx.ref(), ch), ctx) -def CharFromBv(ch, ctx=None): - if not is_expr(ch): - raise Z3Expression("Bit-vector expression needed") - return _to_expr_ref(Z3_mk_char_from_bv(ch.ctx_ref(), ch.as_ast()), ch.ctx) +def CharFromBv(bv): + if not is_expr(bv): + raise Z3Exception("Bit-vector expression needed") + return _to_expr_ref(Z3_mk_char_from_bv(bv.ctx_ref(), bv.as_ast()), bv.ctx) def CharToBv(ch, ctx=None): ch = _coerce_char(ch, ctx) @@ -11570,47 +11570,54 @@ def user_prop_fresh(ctx, _new_ctx): def user_prop_fixed(ctx, cb, id, value): prop = _prop_closures.get(ctx) - prop.cb = cb + old_cb = prop.cb + prop.cb = cb id = _to_expr_ref(to_Ast(id), prop.ctx()) value = _to_expr_ref(to_Ast(value), prop.ctx()) prop.fixed(id, value) - prop.cb = None + prop.cb = old_cb def user_prop_created(ctx, cb, id): prop = _prop_closures.get(ctx) + old_cb = prop.cb prop.cb = cb id = _to_expr_ref(to_Ast(id), prop.ctx()) prop.created(id) - prop.cb = None + prop.cb = old_cb + def user_prop_final(ctx, cb): prop = _prop_closures.get(ctx) + old_cb = prop.cb prop.cb = cb prop.final() - prop.cb = None + prop.cb = old_cb def user_prop_eq(ctx, cb, x, y): prop = _prop_closures.get(ctx) + old_cb = prop.cb prop.cb = cb x = _to_expr_ref(to_Ast(x), prop.ctx()) y = _to_expr_ref(to_Ast(y), prop.ctx()) prop.eq(x, y) - prop.cb = None + prop.cb = old_cb def user_prop_diseq(ctx, cb, x, y): prop = _prop_closures.get(ctx) + old_cb = prop.cb prop.cb = cb x = _to_expr_ref(to_Ast(x), prop.ctx()) y = _to_expr_ref(to_Ast(y), prop.ctx()) prop.diseq(x, y) - prop.cb = None + prop.cb = old_cb def user_prop_decide(ctx, cb, t, idx, phase): prop = _prop_closures.get(ctx) + old_cb = prop.cb prop.cb = cb t = _to_expr_ref(to_Ast(t_ref), prop.ctx()) prop.decide(t, idx, phase) - prop.cb = None + prop.cb = old_cb _user_prop_push = Z3_push_eh(user_prop_push) @@ -11651,7 +11658,7 @@ class UserPropagateBase: # # Either solver is set or ctx is set. - # Propagators that are created throuh callbacks + # Propagators that are created through callbacks # to "fresh" inherit the context of that is supplied # as argument to the callback. # This context should not be deleted. It is owned by the solver. diff --git a/src/api/python/z3/z3printer.py b/src/api/python/z3/z3printer.py index 228f212d9..2da5f89da 100644 --- a/src/api/python/z3/z3printer.py +++ b/src/api/python/z3/z3printer.py @@ -99,6 +99,7 @@ _z3_op_to_str = { Z3_OP_ARRAY_EXT: "Ext", Z3_OP_PB_AT_MOST: "AtMost", + Z3_OP_PB_AT_LEAST: "AtLeast", Z3_OP_PB_LE: "PbLe", Z3_OP_PB_GE: "PbGe", Z3_OP_PB_EQ: "PbEq", @@ -252,11 +253,11 @@ def _is_html_left_assoc(k): def _is_add(k): - return k == Z3_OP_ADD or k == Z3_OP_BADD + return k == Z3_OP_ADD or k == Z3_OP_BADD or k == Z3_OP_FPA_ADD def _is_sub(k): - return k == Z3_OP_SUB or k == Z3_OP_BSUB + return k == Z3_OP_SUB or k == Z3_OP_BSUB or k == Z3_OP_FPA_SUB if sys.version_info.major < 3: @@ -890,9 +891,21 @@ class Formatter: if self.is_infix(k) and n >= 3: rm = a.arg(0) if z3.is_fprm_value(rm) and z3.get_default_rounding_mode(a.ctx).eq(rm): - arg1 = to_format(self.pp_expr(a.arg(1), d + 1, xs)) - arg2 = to_format(self.pp_expr(a.arg(2), d + 1, xs)) + p = self.get_precedence(k) r = [] + x = a.arg(1) + y = a.arg(2) + arg1 = to_format(self.pp_expr(x, d + 1, xs)) + arg2 = to_format(self.pp_expr(y, d + 1, xs)) + if z3.is_app(x): + child_k = x.decl().kind() + if child_k != k and self.is_infix(child_k) and self.get_precedence(child_k) > p: + arg1 = self.add_paren(arg1) + if z3.is_app(y): + child_k = y.decl().kind() + if child_k != k and self.is_infix(child_k) and self.get_precedence(child_k) > p: + arg2 = self.add_paren(arg2) + r.append(arg1) r.append(to_format(" ")) r.append(to_format(op)) @@ -1099,6 +1112,10 @@ class Formatter: k = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 0) return seq1(self.pp_name(a), [seq3([self.pp_expr(ch, d + 1, xs) for ch in a.children()]), to_format(k)]) + def pp_atleast(self, a, d, f, xs): + k = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 0) + return seq1(self.pp_name(a), [seq3([self.pp_expr(ch, d + 1, xs) for ch in a.children()]), to_format(k)]) + def pp_pbcmp(self, a, d, f, xs): chs = a.children() rchs = range(len(chs)) @@ -1151,6 +1168,8 @@ class Formatter: return self.pp_K(a, d, xs) elif k == Z3_OP_PB_AT_MOST: return self.pp_atmost(a, d, f, xs) + elif k == Z3_OP_PB_AT_LEAST: + return self.pp_atleast(a, d, f, xs) elif k == Z3_OP_PB_LE: return self.pp_pbcmp(a, d, f, xs) elif k == Z3_OP_PB_GE: diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 0468ab91d..cbf9803db 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1363,7 +1363,7 @@ typedef enum { - Z3_NO_PARSER: Parser output is not available, that is, user didn't invoke #Z3_parse_smtlib2_string or #Z3_parse_smtlib2_file. - Z3_INVALID_PATTERN: Invalid pattern was used to build a quantifier. - Z3_MEMOUT_FAIL: A memory allocation failure was encountered. - - Z3_FILE_ACCESS_ERRROR: A file could not be accessed. + - Z3_FILE_ACCESS_ERROR: A file could not be accessed. - Z3_INVALID_USAGE: API call is invalid in the current state. - Z3_INTERNAL_FATAL: An error internal to Z3 occurred. - Z3_DEC_REF_ERROR: Trying to decrement the reference counter of an AST that was deleted or the reference counter was not initialized with #Z3_inc_ref. @@ -7184,15 +7184,25 @@ extern "C" { void Z3_API Z3_solver_propagate_register_cb(Z3_context c, Z3_solver_callback cb, Z3_ast e); /** - \brief propagate a consequence based on fixed values. - This is a callback a client may invoke during the fixed_eh callback. - The callback adds a propagation consequence based on the fixed values of the - \c ids. + \brief propagate a consequence based on fixed values and equalities. + A client may invoke it during the \c propagate_fixed, \c propagate_eq, \c propagate_diseq, and \c propagate_final callbacks. + The callback adds a propagation consequence based on the fixed values passed \c ids and equalities \c eqs based on parameters \c lhs, \c rhs. + The solver might discard the propagation in case it is true in the current state. The function returns false in this case; otw. the function returns true. At least one propagation in the final callback has to return true in order to prevent the solver from finishing. + Assume the callback has the signature: \c propagate_consequence_eh(context, solver_cb, num_ids, ids, num_eqs, lhs, rhs, consequence). + \param c - context + \param solver_cb - solver callback + \param num_ids - number of fixed terms used as premise to propagation + \param ids - array of length \c num_ids containing terms that are fixed in the current scope + \param num_eqs - number of equalities used as premise to propagation + \param lhs - left side of equalities + \param rhs - right side of equalities + \param consequence - consequence to propagate. It is typically an atomic formula, but it can be an arbitrary formula. + def_API('Z3_solver_propagate_consequence', BOOL, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(UINT), _in_array(2, AST), _in(UINT), _in_array(4, AST), _in_array(4, AST), _in(AST))) */ diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index f09daaf75..67a605869 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -984,7 +984,8 @@ bool arith_util::is_extended_numeral(expr* term, rational& r) const { return true; } return false; - } while (false); + } + while (true); return false; } diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index f38894d06..bd9d954c7 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -49,7 +49,7 @@ sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete m_manager->raise_exception("invalid array sort definition, invalid number of parameters"); return nullptr; } - parameter params[2] = { parameters[0], parameter(m_manager->mk_bool_sort()) }; + parameter params[2] = { parameter(parameters[0]), parameter(m_manager->mk_bool_sort()) }; return mk_sort(ARRAY_SORT, 2, params); } SASSERT(k == ARRAY_SORT); diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 6ea293b57..e1e3efe99 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -48,21 +48,13 @@ parameter::~parameter() { } } -parameter& parameter::operator=(parameter const& other) { - if (this == &other) { - return *this; - } - - this->~parameter(); - m_val = other.m_val; - +parameter::parameter(parameter const& other) : m_val(other.m_val) { if (auto p = std::get_if(&m_val)) { m_val = alloc(rational, **p); } if (auto p = std::get_if(&m_val)) { m_val = alloc(zstring, **p); } - return *this; } void parameter::init_eh(ast_manager & m) { @@ -319,26 +311,6 @@ func_decl::func_decl(symbol const & name, unsigned arity, sort * const * domain, // // ----------------------------------- -static app_flags mk_const_flags() { - app_flags r; - r.m_depth = 1; - r.m_ground = true; - r.m_has_quantifiers = false; - r.m_has_labels = false; - return r; -} - -static app_flags mk_default_app_flags() { - app_flags r; - r.m_depth = 1; - r.m_ground = true; - r.m_has_quantifiers = false; - r.m_has_labels = false; - return r; -} - -app_flags app::g_constant_flags = mk_const_flags(); - app::app(func_decl * decl, unsigned num_args, expr * const * args): expr(AST_APP), m_decl(decl), @@ -1762,8 +1734,7 @@ ast * ast_manager::register_node_core(ast * n) { inc_ref(t->get_decl()); unsigned num_args = t->get_num_args(); if (num_args > 0) { - app_flags * f = t->flags(); - *f = mk_default_app_flags(); + app_flags * f = &t->m_flags; SASSERT(t->is_ground()); SASSERT(!t->has_quantifiers()); SASSERT(!t->has_labels()); @@ -1776,13 +1747,13 @@ ast * ast_manager::register_node_core(ast * n) { unsigned arg_depth = 0; switch (arg->get_kind()) { case AST_APP: { - app_flags * arg_flags = to_app(arg)->flags(); - arg_depth = arg_flags->m_depth; - if (arg_flags->m_has_quantifiers) + app *app = to_app(arg); + arg_depth = app->get_depth(); + if (app->has_quantifiers()) f->m_has_quantifiers = true; - if (arg_flags->m_has_labels) + if (app->has_labels()) f->m_has_labels = true; - if (!arg_flags->m_ground) + if (!app->is_ground()) f->m_ground = false; break; } diff --git a/src/ast/ast.h b/src/ast/ast.h index b4b3b1b2f..053390022 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -142,7 +142,7 @@ public: explicit parameter(const char *s): m_val(symbol(s)) {} explicit parameter(const std::string &s): m_val(symbol(s)) {} explicit parameter(unsigned ext_id, bool): m_val(ext_id) {} - parameter(parameter const& other) { *this = other; } + explicit parameter(parameter const& other); parameter(parameter && other) noexcept : m_val(std::move(other.m_val)) { other.m_val = 0; @@ -150,7 +150,10 @@ public: ~parameter(); - parameter& operator=(parameter const& other); + parameter& operator=(parameter && other) { + std::swap(other.m_val, m_val); + return *this; + } kind_t get_kind() const { return static_cast(m_val.index()); } bool is_int() const { return get_kind() == PARAM_INT; } @@ -704,6 +707,7 @@ struct app_flags { unsigned m_ground:1; // application does not have free variables or nested quantifiers. unsigned m_has_quantifiers:1; // application has nested quantifiers. unsigned m_has_labels:1; // application has nested labels. + app_flags() : m_depth(1), m_ground(1), m_has_quantifiers(0), m_has_labels(0) {} }; class app : public expr { @@ -711,19 +715,15 @@ class app : public expr { func_decl * m_decl; unsigned m_num_args; + app_flags m_flags; expr * m_args[0]; - static app_flags g_constant_flags; - - // remark: store term depth in the end of the app. the depth is only stored if the num_args > 0 static unsigned get_obj_size(unsigned num_args) { - return num_args == 0 ? sizeof(app) : sizeof(app) + num_args * sizeof(expr *) + sizeof(app_flags); + return sizeof(app) + num_args * sizeof(expr *); } friend class tmp_app; - app_flags * flags() const { return m_num_args == 0 ? &g_constant_flags : reinterpret_cast(const_cast(m_args + m_num_args)); } - app(func_decl * decl, unsigned num_args, expr * const * args); public: func_decl * get_decl() const { return m_decl; } @@ -744,10 +744,10 @@ public: expr * const * end() const { return m_args + m_num_args; } sort * _get_sort() const { return get_decl()->get_range(); } - unsigned get_depth() const { return flags()->m_depth; } - bool is_ground() const { return flags()->m_ground; } - bool has_quantifiers() const { return flags()->m_has_quantifiers; } - bool has_labels() const { return flags()->m_has_labels; } + unsigned get_depth() const { return m_flags.m_depth; } + bool is_ground() const { return m_flags.m_ground; } + bool has_quantifiers() const { return m_flags.m_has_quantifiers; } + bool has_labels() const { return m_flags.m_has_labels; } }; // ----------------------------------- @@ -1102,7 +1102,7 @@ public: // Event handlers for deleting/translating PARAM_EXTERNAL virtual void del(parameter const & p) {} - virtual parameter translate(parameter const & p, decl_plugin & target) { UNREACHABLE(); return p; } + virtual parameter translate(parameter const & p, decl_plugin & target) { UNREACHABLE(); return {}; } virtual bool is_considered_uninterpreted(func_decl * f) { return false; } }; diff --git a/src/ast/ast_lt.cpp b/src/ast/ast_lt.cpp index 3537dc71e..869c7bff8 100644 --- a/src/ast/ast_lt.cpp +++ b/src/ast/ast_lt.cpp @@ -68,8 +68,8 @@ bool lt(ast * n1, ast * n2) { num = to_sort(n1)->get_num_parameters(); SASSERT(num > 0); for (unsigned i = 0; i < num; i++) { - parameter p1 = to_sort(n1)->get_parameter(i); - parameter p2 = to_sort(n2)->get_parameter(i); + const parameter &p1 = to_sort(n1)->get_parameter(i); + const parameter &p2 = to_sort(n2)->get_parameter(i); check_parameter(p1, p2); } UNREACHABLE(); @@ -80,8 +80,8 @@ bool lt(ast * n1, ast * n2) { check_value(to_func_decl(n1)->get_num_parameters(), to_func_decl(n2)->get_num_parameters()); num = to_func_decl(n1)->get_num_parameters(); for (unsigned i = 0; i < num; i++) { - parameter p1 = to_func_decl(n1)->get_parameter(i); - parameter p2 = to_func_decl(n2)->get_parameter(i); + const parameter &p1 = to_func_decl(n1)->get_parameter(i); + const parameter &p2 = to_func_decl(n2)->get_parameter(i); check_parameter(p1, p2); } num = to_func_decl(n1)->get_arity(); diff --git a/src/ast/ast_pp.h b/src/ast/ast_pp.h index 7ccb8ec15..1f20ce300 100644 --- a/src/ast/ast_pp.h +++ b/src/ast/ast_pp.h @@ -58,13 +58,13 @@ inline std::ostream& operator<<(std::ostream & out, mk_pp_vec const & pp) { inline std::string operator+(char const* s, mk_pp const& pp) { std::ostringstream strm; strm << s << pp; - return strm.str(); + return std::move(strm).str(); } inline std::string operator+(std::string const& s, mk_pp const& pp) { std::ostringstream strm; strm << s << pp; - return strm.str(); + return std::move(strm).str(); } inline std::string& operator+=(std::string& s, mk_pp const& pp) { diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 30cfe4cdb..327e280cf 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -454,9 +454,8 @@ func_decl * bv_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const // This cannot be enforced now, since some Z3 modules try to generate these invalid numerals. // After SMT-COMP, I should find all offending modules. // For now, I will just simplify the numeral here. - rational v = parameters[0].get_rational(); - parameter p0(mod2k(v, bv_size)); - parameter ps[2] = { std::move(p0), parameters[1] }; + const rational &v = parameters[0].get_rational(); + parameter ps[2] = { parameter(mod2k(v, bv_size)), parameter(parameters[1]) }; sort * bv = get_bv_sort(bv_size); return m_manager->mk_const_decl(m_bv_sym, bv, func_decl_info(m_family_id, OP_BV_NUM, num_parameters, ps)); } @@ -913,13 +912,9 @@ app * bv_util::mk_numeral(rational const & val, unsigned bv_size) const { if (m_plugin->log_constant_meaning_prelude(r)) { if (bv_size % 4 == 0) { - m_manager.trace_stream() << "#x"; - val.display_hex(m_manager.trace_stream(), bv_size); - m_manager.trace_stream() << "\n"; + m_manager.trace_stream() << "#x" << val.as_hex(bv_size) << "\n"; } else { - m_manager.trace_stream() << "#b"; - val.display_bin(m_manager.trace_stream(), bv_size); - m_manager.trace_stream() << "\n"; + m_manager.trace_stream() << "#b" << val.as_bin(bv_size) << "\n"; } } diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index bdc32e97c..a06d125a5 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -400,6 +400,7 @@ class bv_expr_inverter : public iexpr_inverter { } bool process_concat(func_decl* f, unsigned num, expr* const* args, expr_ref& r) { +// return false; if (num == 0) return false; if (!uncnstr(num, args)) diff --git a/src/ast/converters/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp index 1e81f9131..c50d86cae 100644 --- a/src/ast/converters/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -43,6 +43,7 @@ void generic_model_converter::operator()(model_ref & md) { expr_ref val(m); unsigned arity; bool reset_ev = false; + obj_map> uninterpreted; for (unsigned i = m_entries.size(); i-- > 0; ) { entry const& e = m_entries[i]; switch (e.m_instruction) { @@ -63,6 +64,13 @@ void generic_model_converter::operator()(model_ref & md) { reset_ev = old_val != nullptr; md->register_decl(e.m_f, val); } + // corner case when uninterpreted constants are eliminated + sort* s = e.m_f->get_range(); + if (m.is_uninterp(s) && !md->has_uninterpreted_sort(s)) { + uninterpreted.insert_if_not_there(s, {}); + if (!uninterpreted[s].contains(val)) + uninterpreted[s].push_back(val); + } } else { func_interp * old_val = md->get_func_interp(e.m_f); @@ -84,6 +92,9 @@ void generic_model_converter::operator()(model_ref & md) { break; } } + for (auto const& [s, u] : uninterpreted) { + md->register_usort(s, u.size(), u.data()); + } TRACE("model_converter", tout << "after generic_model_converter\n"; model_v2_pp(tout, *md);); } diff --git a/src/ast/converters/generic_model_converter.h b/src/ast/converters/generic_model_converter.h index 8a1c62347..0bc6b21b4 100644 --- a/src/ast/converters/generic_model_converter.h +++ b/src/ast/converters/generic_model_converter.h @@ -68,6 +68,8 @@ public: void get_units(obj_map& units) override; vector const& entries() const { return m_entries; } + + void reset() { m_entries.reset(); } }; typedef ref generic_model_converter_ref; diff --git a/src/ast/euf/euf_ac_plugin.cpp b/src/ast/euf/euf_ac_plugin.cpp index b49975ca4..174ef363b 100644 --- a/src/ast/euf/euf_ac_plugin.cpp +++ b/src/ast/euf/euf_ac_plugin.cpp @@ -59,9 +59,9 @@ TODOs: - The shared terms hash table is not incremental. It could be made incremental by updating it on every merge similar to how the egraph handles it. - V2 using multiplicities instead of repeated values in monomials. -- Squash trail updates when equations or monomials are modified within the same epoque. - - by an epoque counter that can be updated by the egraph class whenever there is a push/pop. - - store the epoque as a tick on equations and possibly when updating monomials on equations. +- Squash trail updates when equations or monomials are modified within the same epoch. + - by an epoch counter that can be updated by the egraph class whenever there is a push/pop. + - store the epoch as a tick on equations and possibly when updating monomials on equations. --*/ @@ -80,7 +80,7 @@ namespace euf { } ac_plugin::ac_plugin(egraph& g, func_decl* f) : - plugin(g), m_fid(f->get_family_id()), m_decl(f), + plugin(g), m_fid(f->get_family_id()), m_decl(f), m_dep_manager(get_region()), m_hash(*this), m_eq(*this), m_monomial_table(m_hash, m_eq) { diff --git a/src/ast/euf/euf_arith_plugin.cpp b/src/ast/euf/euf_arith_plugin.cpp index 26f8e0bd9..268eff38d 100644 --- a/src/ast/euf/euf_arith_plugin.cpp +++ b/src/ast/euf/euf_arith_plugin.cpp @@ -7,7 +7,7 @@ Module Name: Abstract: - plugin structure for arithetic + plugin structure for arithmetic Author: diff --git a/src/ast/euf/euf_arith_plugin.h b/src/ast/euf/euf_arith_plugin.h index a31c80a70..4c2a88d36 100644 --- a/src/ast/euf/euf_arith_plugin.h +++ b/src/ast/euf/euf_arith_plugin.h @@ -7,7 +7,7 @@ Module Name: Abstract: - plugin structure for arithetic + plugin structure for arithmetic Author: Nikolaj Bjorner (nbjorner) 2023-11-11 diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp index cc2abf01c..a3008851b 100644 --- a/src/ast/fpa/bv2fpa_converter.cpp +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -319,7 +319,7 @@ func_interp * bv2fpa_converter::convert_func_interp(model_core * mc, func_decl * if (m_fpa_util.is_to_sbv(f) || m_fpa_util.is_to_ubv(f)) { auto k = m_fpa_util.is_to_sbv(f) ? OP_FPA_TO_SBV_I : OP_FPA_TO_UBV_I; - parameter param = f->get_parameter(0); + const parameter ¶m = f->get_parameter(0); func_decl_ref to_bv_i(m.mk_func_decl(fid, k, 1, ¶m, dom.size(), dom.data()), m); expr_ref else_value(m.mk_app(to_bv_i, dom.size(), dom.data()), m); result->set_else(else_value); diff --git a/src/ast/fpa_decl_plugin.cpp b/src/ast/fpa_decl_plugin.cpp index 78d300ca7..76e44278d 100644 --- a/src/ast/fpa_decl_plugin.cpp +++ b/src/ast/fpa_decl_plugin.cpp @@ -208,8 +208,7 @@ sort * fpa_decl_plugin::mk_float_sort(unsigned ebits, unsigned sbits) { if (ebits > 63) m_manager->raise_exception("maximum number of exponent bits is 63"); - parameter p1(ebits), p2(sbits); - parameter ps[2] = { p1, p2 }; + parameter ps[2] = { parameter(ebits), parameter(sbits) }; sort_size sz; sz = sort_size::mk_very_big(); // TODO: refine return m_manager->mk_sort(symbol("FloatingPoint"), sort_info(m_family_id, FLOATING_POINT_SORT, sz, 2, ps)); diff --git a/src/ast/polymorphism_util.cpp b/src/ast/polymorphism_util.cpp index 1c961d21f..3431dd034 100644 --- a/src/ast/polymorphism_util.cpp +++ b/src/ast/polymorphism_util.cpp @@ -40,7 +40,7 @@ namespace polymorphism { unsigned n = s->get_num_parameters(); vector ps; for (unsigned i = 0; i < n; ++i) { - auto p = s->get_parameter(i); + auto &p = s->get_parameter(i); if (p.is_ast() && is_sort(p.get_ast())) { sort_ref s = (*this)(to_sort(p.get_ast())); ps.push_back(parameter(s.get())); @@ -167,8 +167,8 @@ namespace polymorphism { if (s1->get_num_parameters() != s2->get_num_parameters()) return false; for (unsigned i = s1->get_num_parameters(); i-- > 0;) { - auto p1 = s1->get_parameter(i); - auto p2 = s2->get_parameter(i); + auto &p1 = s1->get_parameter(i); + auto &p2 = s2->get_parameter(i); if (p1.is_ast() && is_sort(p1.get_ast())) { if (!p2.is_ast()) return false; @@ -204,8 +204,8 @@ namespace polymorphism { if (s1->get_num_parameters() != s2->get_num_parameters()) return false; for (unsigned i = s1->get_num_parameters(); i-- > 0;) { - auto p1 = s1->get_parameter(i); - auto p2 = s2->get_parameter(i); + auto &p1 = s1->get_parameter(i); + auto &p2 = s2->get_parameter(i); if (p1.is_ast() && is_sort(p1.get_ast())) { if (!p2.is_ast()) return false; @@ -282,7 +282,7 @@ namespace polymorphism { } vector params; for (unsigned i = 0; i < s->get_num_parameters(); ++i) { - parameter p = s->get_parameter(i); + const parameter &p = s->get_parameter(i); if (p.is_ast() && is_sort(p.get_ast())) { sort_ref fs = fresh(to_sort(p.get_ast())); params.push_back(parameter(fs.get())); diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index ede1c7194..20338aa04 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -1235,7 +1235,7 @@ static rational symmod(rational const& a, rational const& b) { if (2*r > b) r -= b; return r; } - + br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(arg1->get_sort()); numeral v1, v2; @@ -1297,9 +1297,9 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul } expr* x, *y; - if (is_num2 && v2.is_pos() && m_util.is_mul(arg1, x, y) && m_util.is_numeral(x, v1, is_int) && divides(v1, v2)) { - result = m_util.mk_mul(x, m_util.mk_mod(y, m_util.mk_int(v2/v1))); - return BR_REWRITE2; + if (is_num2 && v2.is_pos() && m_util.is_mul(arg1, x, y) && m_util.is_numeral(x, v1, is_int) && v1 > 0 && divides(v1, v2)) { + result = m_util.mk_mul(m_util.mk_int(v1), m_util.mk_mod(y, m_util.mk_int(v2/v1))); + return BR_REWRITE1; } if (is_num2 && v2 == 2 && m_util.is_mul(arg1, x, y)) { @@ -1310,6 +1310,27 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul return BR_FAILED; } +bool arith_rewriter::get_range(expr* e, rational& lo, rational& hi) { + expr* x, *y; + rational r; + if (m_util.is_idiv(e, x, y) && m_util.is_numeral(y, r) && get_range(x, lo, hi) && 0 <= lo && r > 0) { + lo = div(lo, r); + hi = div(hi, r); + return true; + } + if (m_util.is_mod(e, x, y) && m_util.is_numeral(y, r) && r > 0) { + lo = 0; + hi = r - 1; + return true; + } + if (m_util.is_numeral(e, r)) { + lo = hi = r; + return true; + } + return false; +} + + br_status arith_rewriter::mk_rem_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(arg1->get_sort()); numeral v1, v2; @@ -1454,7 +1475,7 @@ br_status arith_rewriter::mk_lshr_core(unsigned sz, expr* arg1, expr* arg2, expr } if (is_num_x && is_num_y) { if (y >= sz) - result = m_util.mk_int(N-1); + result = m_util.mk_int(0); else { rational d = div(x, rational::power_of_two(y.get_unsigned())); result = m_util.mk_int(d); diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index d59486273..01fea0ac7 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -63,6 +63,7 @@ class arith_rewriter : public poly_rewriter { bool m_eq2ineq; unsigned m_max_degree; + bool get_range(expr* e, rational& lo, rational& hi); void get_coeffs_gcd(expr * t, numeral & g, bool & first, unsigned & num_consts); enum const_treatment { CT_FLOOR, CT_CEIL, CT_FALSE }; bool div_polynomial(expr * t, numeral const & g, const_treatment ct, expr_ref & result); diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index 228ba9914..bd67a940e 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -206,7 +206,9 @@ br_status array_rewriter::mk_store_core(unsigned num_args, expr * const * args, bool array_rewriter::squash_store(unsigned n, expr* const* args, expr_ref& result) { ptr_buffer parents, sargs; expr* a = args[0]; - while (m_util.is_store(a)) { + unsigned rounds = 0; + while (m_util.is_store(a) && rounds < 10) { + ++rounds; lbool r = compare_args(n - 2, args + 1, to_app(a)->get_args() + 1); switch (r) { case l_undef: diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp index 68e3825c9..bb8b067e8 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp @@ -615,6 +615,8 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); if (m_blast_quant) { if (m_bindings.empty()) return false; + if (!butil().is_bv(t)) + return false; unsigned shift = m_shifts.back(); if (t->get_idx() >= m_bindings.size()) { if (shift == 0) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 9afab7a29..2176ff129 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -545,16 +545,9 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ bool simp = false; bool modified = false; bool forward = true; - unsigned rounds = 0; expr* narg; while (true) { - rounds++; -#if 0 - if (rounds > 10) - verbose_stream() << "rounds: " << rounds << "\n"; -#endif - #define PROCESS_ARG() \ { \ @@ -699,6 +692,22 @@ app* bool_rewriter::mk_eq(expr* lhs, expr* rhs) { return m().mk_eq(lhs, rhs); } +bool bool_rewriter::try_ite_eq(expr* lhs, expr* rhs, expr_ref& r) { + expr* c, *t, *e; + if (!m().is_ite(lhs, c, t, e)) + return false; + if (m().are_equal(t, rhs) && m().are_distinct(e, rhs)) { + r = c; + return true; + } + if (m().are_equal(e, rhs) && m().are_distinct(t, rhs)) { + r = m().mk_not(c); + return true; + } + return false; +} + + br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (m().are_equal(lhs, rhs)) { result = m().mk_true(); @@ -713,6 +722,12 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { br_status r = BR_FAILED; + if (try_ite_eq(lhs, rhs, result)) + return BR_REWRITE1; + + if (try_ite_eq(rhs, lhs, result)) + return BR_REWRITE1; + if (m_ite_extra_rules) { if (m().is_ite(lhs) && m().is_value(rhs)) { r = try_ite_value(to_app(lhs), to_app(rhs), result); diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 7c840b647..421811ed4 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -71,6 +71,8 @@ class bool_rewriter { void mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result); + bool try_ite_eq(expr* lhs, expr* rhs, expr_ref& r); + expr * mk_or_app(unsigned num_args, expr * const * args); bool simp_nested_not_or(unsigned num_args, expr * const * args, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result); expr * simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified); diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index fc672584e..db87cd008 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -20,8 +20,9 @@ Notes: #include "ast/rewriter/bv_rewriter.h" #include "ast/rewriter/poly_rewriter_def.h" #include "ast/rewriter/bool_rewriter.h" -#include "ast/ast_smt2_pp.h" #include "ast/ast_lt.h" +#include "ast/ast_pp.h" + void bv_rewriter::updt_local_params(params_ref const & _p) { @@ -54,45 +55,58 @@ void bv_rewriter::get_param_descrs(param_descrs & r) { br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == get_fid()); + br_status st = BR_FAILED; switch(f->get_decl_kind()) { case OP_BIT0: SASSERT(num_args == 0); result = mk_zero(1); return BR_DONE; case OP_BIT1: SASSERT(num_args == 0); result = mk_one(1); return BR_DONE; case OP_ULEQ: SASSERT(num_args == 2); - return mk_ule(args[0], args[1], result); + st = mk_ule(args[0], args[1], result); + break; case OP_UGEQ: SASSERT(num_args == 2); - return mk_uge(args[0], args[1], result); + st = mk_uge(args[0], args[1], result); + break; case OP_ULT: SASSERT(num_args == 2); - return mk_ult(args[0], args[1], result); + st = mk_ult(args[0], args[1], result); + break; case OP_UGT: SASSERT(num_args == 2); - return mk_ult(args[1], args[0], result); + st = mk_ult(args[1], args[0], result); + break; case OP_SLEQ: SASSERT(num_args == 2); - return mk_sle(args[0], args[1], result); + st = mk_sle(args[0], args[1], result); + break; case OP_SGEQ: SASSERT(num_args == 2); - return mk_sge(args[0], args[1], result); + st = mk_sge(args[0], args[1], result); + break; case OP_SLT: SASSERT(num_args == 2); - return mk_slt(args[0], args[1], result); + st = mk_slt(args[0], args[1], result); + break; case OP_SGT: SASSERT(num_args == 2); - return mk_slt(args[1], args[0], result); + st = mk_slt(args[1], args[0], result); + break; case OP_BADD: SASSERT(num_args > 0); - return mk_bv_add(num_args, args, result); + st = mk_bv_add(num_args, args, result); + break; case OP_BMUL: SASSERT(num_args > 0); - return mk_bv_mul(num_args, args, result); + st = mk_bv_mul(num_args, args, result); + break; case OP_BSUB: SASSERT(num_args > 0); - return mk_sub(num_args, args, result); + st = mk_sub(num_args, args, result); + break; case OP_BNEG: SASSERT(num_args == 1); - return mk_uminus(args[0], result); + st = mk_uminus(args[0], result); + break; case OP_BNEG_OVFL: SASSERT(num_args == 1); return mk_bvneg_overflow(args[0], result); @@ -220,6 +234,13 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons default: return BR_FAILED; } + + CTRACE("bv", st != BR_FAILED, tout << mk_pp(f, m) << "\n"; + for (unsigned i = 0; i < num_args; ++i) + tout << " " << mk_bounded_pp(args[i], m) << "\n"; + tout << mk_bounded_pp(result, m, 3) << "\n"); + + return st; } br_status bv_rewriter::mk_ule(expr * a, expr * b, expr_ref & result) { @@ -541,7 +562,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref const br_status cst = rw_leq_concats(is_signed, a, b, result); if (cst != BR_FAILED) { TRACE("le_extra", tout << (is_signed ? "bv_sle\n" : "bv_ule\n") - << mk_ismt2_pp(a, m, 2) << "\n" << mk_ismt2_pp(b, m, 2) << "\n--->\n"<< mk_ismt2_pp(result, m, 2) << "\n";); + << mk_pp(a, m, 2) << "\n" << mk_pp(b, m, 2) << "\n--->\n"<< mk_pp(result, m, 2) << "\n";); return cst; } } @@ -550,7 +571,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref const br_status cst = rw_leq_overflow(is_signed, a, b, result); if (cst != BR_FAILED) { TRACE("le_extra", tout << (is_signed ? "bv_sle\n" : "bv_ule\n") - << mk_ismt2_pp(a, m, 2) << "\n" << mk_ismt2_pp(b, m, 2) << "\n--->\n"<< mk_ismt2_pp(result, m, 2) << "\n";); + << mk_pp(a, m, 2) << "\n" << mk_pp(b, m, 2) << "\n--->\n"<< mk_pp(result, m, 2) << "\n";); return cst; } } @@ -802,8 +823,8 @@ br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_ const unsigned ep_rm = propagate_extract(high, arg, ep_res); if (ep_rm != 0) { result = m_mk_extract(high, low, ep_res); - TRACE("extract_prop", tout << mk_ismt2_pp(arg, m) << "\n[" << high <<"," << low << "]\n" << ep_rm << "---->\n" - << mk_ismt2_pp(result.get(), m) << "\n";); + TRACE("extract_prop", tout << mk_pp(arg, m) << "\n[" << high <<"," << low << "]\n" << ep_rm << "---->\n" + << mk_pp(result.get(), m) << "\n";); return BR_REWRITE2; } } @@ -1132,7 +1153,7 @@ br_status bv_rewriter::mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, e m_util.mk_bv_udiv0(arg1), m_util.mk_bv_udiv_i(arg1, arg2)); - TRACE("bv_udiv", tout << mk_ismt2_pp(arg1, m) << "\n" << mk_ismt2_pp(arg2, m) << "\n---->\n" << mk_ismt2_pp(result, m) << "\n";); + TRACE("bv_udiv", tout << mk_pp(arg1, m) << "\n" << mk_pp(arg2, m) << "\n---->\n" << mk_pp(result, m) << "\n";); return BR_REWRITE2; } @@ -1792,8 +1813,8 @@ br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & re std::reverse(exs.begin(), exs.end()); result = m_util.mk_concat(exs.size(), exs.data()); TRACE("mask_bug", - tout << "(assert (distinct (bvor (_ bv" << old_v1 << " " << sz << ")\n" << mk_ismt2_pp(t, m) << ")\n"; - tout << mk_ismt2_pp(result, m) << "))\n";); + tout << "(assert (distinct (bvor (_ bv" << old_v1 << " " << sz << ")\n" << mk_pp(t, m) << ")\n"; + tout << mk_pp(result, m) << "))\n";); return BR_REWRITE2; } @@ -2463,7 +2484,7 @@ br_status bv_rewriter::mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & resu unsigned sz = get_bv_size(lhs); if (sz == 1) return BR_FAILED; - TRACE("blast_eq_value", tout << "sz: " << sz << "\n" << mk_ismt2_pp(lhs, m) << "\n";); + TRACE("blast_eq_value", tout << "sz: " << sz << "\n" << mk_pp(lhs, m) << "\n";); if (is_numeral(lhs)) std::swap(lhs, rhs); @@ -2573,7 +2594,6 @@ void bv_rewriter::mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & r result = m.mk_eq(t1, m_util.mk_bv_sub(c, t2)); } -#include "ast/ast_pp.h" bool bv_rewriter::isolate_term(expr* lhs, expr* rhs, expr_ref& result) { if (!m_util.is_numeral(lhs) || !is_add(rhs)) { @@ -2730,13 +2750,13 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { st = mk_mul_eq(lhs, rhs, result); if (st != BR_FAILED) { - TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m) << "\n=\n" << mk_ismt2_pp(rhs, m) << "\n----->\n" << mk_ismt2_pp(result,m) << "\n";); + TRACE("mk_mul_eq", tout << mk_pp(lhs, m) << "\n=\n" << mk_pp(rhs, m) << "\n----->\n" << mk_pp(result,m) << "\n";); return st; } st = mk_mul_eq(rhs, lhs, result); if (st != BR_FAILED) { - TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m) << "\n=\n" << mk_ismt2_pp(rhs, m) << "\n----->\n" << mk_ismt2_pp(result,m) << "\n";); + TRACE("mk_mul_eq", tout << mk_pp(lhs, m) << "\n=\n" << mk_pp(rhs, m) << "\n----->\n" << mk_pp(result,m) << "\n";); return st; } @@ -2851,8 +2871,8 @@ bool bv_rewriter::is_eq_bit(expr * t, expr * & x, unsigned & val) { br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) { - TRACE("bv_ite", tout << "mk_ite_core:\n" << mk_ismt2_pp(c, m) << "?\n" - << mk_ismt2_pp(t, m) << "\n:" << mk_ismt2_pp(e, m) << "\n";); + TRACE("bv_ite", tout << "mk_ite_core:\n" << mk_pp(c, m) << "?\n" + << mk_pp(t, m) << "\n:" << mk_pp(e, m) << "\n";); if (m.are_equal(t, e)) { result = e; return BR_REWRITE1; diff --git a/src/ast/rewriter/pb_rewriter.cpp b/src/ast/rewriter/pb_rewriter.cpp index 2f06710ff..4f1fe3fd1 100644 --- a/src/ast/rewriter/pb_rewriter.cpp +++ b/src/ast/rewriter/pb_rewriter.cpp @@ -157,9 +157,7 @@ expr_ref pb_rewriter::mk_validate_rewrite(app_ref& e1, app_ref& e2) { continue; } - std::ostringstream strm; - strm << 'x' << i; - name = symbol(strm.str()); + name = symbol('x' + std::to_string(i)); trail.push_back(m.mk_const(name, a.mk_int())); expr* x = trail.back(); m.is_not(e,e); @@ -188,9 +186,7 @@ void pb_rewriter::validate_rewrite(func_decl* f, unsigned sz, expr*const* args, } void pb_rewriter::dump_pb_rewrite(expr* fml) { - std::ostringstream strm; - strm << "pb_rewrite_" << (s_lemma++) << ".smt2"; - std::ofstream out(strm.str()); + std::ofstream out("pb_rewrite_" + std::to_string(s_lemma++) + ".smt2"); ast_smt_pp pp(m()); pp.display_smt2(out, fml); out.close(); diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 12ab1cbb1..aa5813224 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -3478,7 +3478,7 @@ expr_ref seq_rewriter::mk_antimirov_deriv_union(expr* d1, expr* d2) { // // restrict(d, false) = [] // -// it is already assumed that the restriction takes place witin a branch +// it is already assumed that the restriction takes place within a branch // so the condition is not added explicitly but propagated down in order to eliminate // infeasible cases expr_ref seq_rewriter::mk_antimirov_deriv_restrict(expr* e, expr* d, expr* cond) { @@ -3717,7 +3717,7 @@ expr_ref seq_rewriter::mk_regex_concat(expr* r, expr* s) { result = re().mk_plus(re().mk_full_char(ele_sort)); else if (re().is_concat(r, r1, r2)) // create the resulting concatenation in right-associative form except for the following case - // TODO: maintain the followig invariant for A ++ B{m,n} + C + // TODO: maintain the following invariant for A ++ B{m,n} + C // concat(concat(A, B{m,n}), C) (if A != () and C != ()) // concat(B{m,n}, C) (if A == () and C != ()) // where A, B, C are regexes @@ -3725,11 +3725,11 @@ expr_ref seq_rewriter::mk_regex_concat(expr* r, expr* s) { // In other words, do not make A ++ B{m,n} into right-assoc form, but keep B{m,n} at the top // This will help to identify this situation in the merge routine: // concat(concat(A, B{0,m}), C) | concat(concat(A, B{0,n}), C) - // simplies to + // simplifies to // concat(concat(A, B{0,max(m,n)}), C) // analogously: // concat(concat(A, B{0,m}), C) & concat(concat(A, B{0,n}), C) - // simplies to + // simplifies to // concat(concat(A, B{0,min(m,n)}), C) result = mk_regex_concat(r1, mk_regex_concat(r2, s)); else { @@ -3850,12 +3850,12 @@ bool seq_rewriter::pred_implies(expr* a, expr* b) { Utility function to decide if two BDDs (nested if-then-else terms) have exactly the same structure and conditions. */ -bool seq_rewriter::ite_bdds_compatabile(expr* a, expr* b) { +bool seq_rewriter::ite_bdds_compatible(expr* a, expr* b) { expr* ca = nullptr, *a1 = nullptr, *a2 = nullptr; expr* cb = nullptr, *b1 = nullptr, *b2 = nullptr; if (m().is_ite(a, ca, a1, a2) && m().is_ite(b, cb, b1, b2)) { - return (ca == cb) && ite_bdds_compatabile(a1, b1) - && ite_bdds_compatabile(a2, b2); + return (ca == cb) && ite_bdds_compatible(a1, b1) + && ite_bdds_compatible(a2, b2); } else if (m().is_ite(a) || m().is_ite(b)) { return false; @@ -3915,7 +3915,7 @@ expr_ref seq_rewriter::mk_der_op_rec(decl_kind k, expr* a, expr* b) { // sophisticated: in an antimirov union of n terms, we really // want to check if any pair of them is compatible. else if (m().is_ite(a) && m().is_ite(b) && - !ite_bdds_compatabile(a, b)) { + !ite_bdds_compatible(a, b)) { k = _OP_RE_ANTIMIROV_UNION; } #endif @@ -4269,7 +4269,7 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) { } else if (re().is_reverse(r, r1)) { if (re().is_to_re(r1, r2)) { - // First try to exctract hd and tl such that r = hd ++ tl and |tl|=1 + // First try to extract hd and tl such that r = hd ++ tl and |tl|=1 expr_ref hd(m()), tl(m()); if (get_head_tail_reversed(r2, hd, tl)) { // Use mk_der_cond to normalize diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 92a6a17fa..af4756576 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -201,7 +201,7 @@ class seq_rewriter { expr_ref mk_der_compl(expr* a); expr_ref mk_der_cond(expr* cond, expr* ele, sort* seq_sort); expr_ref mk_der_antimirov_union(expr* r1, expr* r2); - bool ite_bdds_compatabile(expr* a, expr* b); + bool ite_bdds_compatible(expr* a, expr* b); /* if r has the form deriv(en..deriv(e1,to_re(s))..) returns 's = [e1..en]' else returns '() in r'*/ expr_ref is_nullable_symbolic_regex(expr* r, sort* seq_sort); #ifdef Z3DEBUG diff --git a/src/ast/rewriter/seq_skolem.h b/src/ast/rewriter/seq_skolem.h index 4b828abf6..4e327f0fa 100644 --- a/src/ast/rewriter/seq_skolem.h +++ b/src/ast/rewriter/seq_skolem.h @@ -8,7 +8,7 @@ Module Name: Abstract: Skolem function support for sequences. - Skolem functions are auxiliary funcions useful for axiomatizing sequence + Skolem functions are auxiliary functions useful for axiomatizing sequence operations. Author: diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 231858897..818800d99 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -66,7 +66,6 @@ bool elim_unconstrained::is_var_lt(int v1, int v2) const { } void elim_unconstrained::eliminate() { - while (!m_heap.empty()) { expr_ref r(m); int v = m_heap.erase_min(); @@ -86,7 +85,12 @@ void elim_unconstrained::eliminate() { n.m_refcount = 0; continue; } + if (m_heap.contains(root(e))) { + IF_VERBOSE(11, verbose_stream() << "already in heap " << mk_bounded_pp(e, m) << "\n"); + continue; + } app* t = to_app(e); + TRACE("elim_unconstrained", tout << "eliminating " << mk_pp(t, m) << "\n";); unsigned sz = m_args.size(); for (expr* arg : *to_app(t)) m_args.push_back(reconstruct_term(get_node(arg))); @@ -99,14 +103,17 @@ void elim_unconstrained::eliminate() { proof * pr = m.mk_apply_def(s, r, pr1); m_trail.push_back(pr); } + expr_ref rr(m.mk_app(t->get_decl(), t->get_num_args(), m_args.data() + sz), m); n.m_refcount = 0; m_args.shrink(sz); if (!inverted) { IF_VERBOSE(11, verbose_stream() << "not inverted " << mk_bounded_pp(e, m) << "\n"); continue; } + + IF_VERBOSE(11, verbose_stream() << "replace " << mk_pp(t, m) << " / " << rr << " -> " << r << "\n"); - TRACE("elim_unconstrained", tout << mk_pp(t, m) << " -> " << r << "\n"); + TRACE("elim_unconstrained", tout << mk_pp(t, m) << " / " << rr << " -> " << r << "\n"); SASSERT(r->get_sort() == t->get_sort()); m_stats.m_num_eliminated++; m_trail.push_back(r); @@ -119,7 +126,8 @@ void elim_unconstrained::eliminate() { get_node(e).m_term = r; get_node(e).m_proof = pr; get_node(e).m_refcount++; - IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(e, m) << "\n"); + get_node(e).m_dirty = false; + IF_VERBOSE(11, verbose_stream() << "set " << &get_node(e) << " " << root(e) << " " << mk_bounded_pp(e, m) << " := " << mk_bounded_pp(r, m) << "\n"); SASSERT(!m_heap.contains(root(e))); if (is_uninterp_const(r)) m_heap.insert(root(e)); @@ -263,12 +271,18 @@ void elim_unconstrained::gc(expr* t) { while (!todo.empty()) { t = todo.back(); todo.pop_back(); + node& n = get_node(t); if (n.m_refcount == 0) continue; + if (n.m_term && !is_node(n.m_term)) + continue; + dec_ref(t); if (n.m_refcount != 0) continue; + if (n.m_term) + t = n.m_term; if (is_app(t)) { for (expr* arg : *to_app(t)) todo.push_back(arg); @@ -283,13 +297,22 @@ expr_ref elim_unconstrained::reconstruct_term(node& n0) { expr* t = n0.m_term; if (!n0.m_dirty) return expr_ref(t, m); + if (!is_node(t)) + return expr_ref(t, m); ptr_vector todo; todo.push_back(t); while (!todo.empty()) { t = todo.back(); + if (!is_node(t)) { + UNREACHABLE(); + } node& n = get_node(t); unsigned sz0 = todo.size(); - if (is_app(t)) { + if (is_app(t)) { + if (n.m_term != t) { + todo.pop_back(); + continue; + } for (expr* arg : *to_app(t)) if (get_node(arg).m_dirty || !get_node(arg).m_term) todo.push_back(arg); @@ -300,7 +323,6 @@ expr_ref elim_unconstrained::reconstruct_term(node& n0) { for (expr* arg : *to_app(t)) m_args.push_back(get_node(arg).m_term); n.m_term = m.mk_app(to_app(t)->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz); - m_args.shrink(sz); } else if (is_quantifier(t)) { @@ -418,6 +440,6 @@ void elim_unconstrained::reduce() { vector old_fmls; assert_normalized(old_fmls); update_model_trail(*mc, old_fmls); + mc->reset(); } - } diff --git a/src/ast/simplifiers/model_reconstruction_trail.cpp b/src/ast/simplifiers/model_reconstruction_trail.cpp index 95f73fd7a..47ebea525 100644 --- a/src/ast/simplifiers/model_reconstruction_trail.cpp +++ b/src/ast/simplifiers/model_reconstruction_trail.cpp @@ -182,11 +182,11 @@ std::ostream& model_reconstruction_trail::display(std::ostream& out) const { out << "hide " << t->m_decl->get_name() << "\n"; else if (t->is_def()) { for (auto const& [f, def, dep] : t->m_defs) - out << f->get_name() << " <- " << mk_pp(def, m) << "\n"; + out << "def: " << f->get_name() << " <- " << mk_pp(def, m) << "\n"; } else { for (auto const& [v, def] : t->m_subst->sub()) - out << mk_pp(v, m) << " <- " << mk_pp(def, m) << "\n"; + out << "sub: " << mk_pp(v, m) << " -> " << mk_pp(def, m) << "\n"; } for (auto const& d : t->m_removed) out << "rm: " << d << "\n"; diff --git a/src/ast/sls/CMakeLists.txt b/src/ast/sls/CMakeLists.txt new file mode 100644 index 000000000..17b803cf3 --- /dev/null +++ b/src/ast/sls/CMakeLists.txt @@ -0,0 +1,9 @@ +z3_add_component(ast_sls + SOURCES + bvsls_opt_engine.cpp + sls_engine.cpp + COMPONENT_DEPENDENCIES + ast + converters + normal_forms +) diff --git a/src/tactic/sls/bvsls_opt_engine.cpp b/src/ast/sls/bvsls_opt_engine.cpp similarity index 98% rename from src/tactic/sls/bvsls_opt_engine.cpp rename to src/ast/sls/bvsls_opt_engine.cpp index 37454ca72..7dc71cd8c 100644 --- a/src/tactic/sls/bvsls_opt_engine.cpp +++ b/src/ast/sls/bvsls_opt_engine.cpp @@ -17,7 +17,7 @@ Notes: --*/ #include "ast/normal_forms/nnf.h" -#include "tactic/sls/bvsls_opt_engine.h" +#include "ast/sls/bvsls_opt_engine.h" bvsls_opt_engine::bvsls_opt_engine(ast_manager & m, params_ref const & p) : sls_engine(m, p), @@ -68,7 +68,8 @@ bvsls_opt_engine::optimization_result bvsls_opt_engine::optimize( if (is_sat != l_true) { do { - checkpoint(); + if (!m_manager.inc()) + return res; IF_VERBOSE(1, verbose_stream() << "Satisfying... restarts left:" << (m_max_restarts - m_stats.m_restarts) << std::endl;); is_sat = search(); @@ -136,7 +137,8 @@ expr_ref bvsls_opt_engine::maximize() while (m_mpz_manager.lt(score, max_score) && check_restart(m_stats.m_moves)) { - checkpoint(); + if (!m_manager.inc()) + goto bailout; m_stats.m_moves++; m_mpz_manager.set(old_score, score); new_const = (unsigned)-1; diff --git a/src/tactic/sls/bvsls_opt_engine.h b/src/ast/sls/bvsls_opt_engine.h similarity index 98% rename from src/tactic/sls/bvsls_opt_engine.h rename to src/ast/sls/bvsls_opt_engine.h index c6b3857af..435fa3af4 100644 --- a/src/tactic/sls/bvsls_opt_engine.h +++ b/src/ast/sls/bvsls_opt_engine.h @@ -18,7 +18,7 @@ Notes: --*/ #pragma once -#include "tactic/sls/sls_engine.h" +#include "ast/sls/sls_engine.h" class bvsls_opt_engine : public sls_engine { sls_tracker & m_hard_tracker; diff --git a/src/tactic/sls/sls_engine.cpp b/src/ast/sls/sls_engine.cpp similarity index 93% rename from src/tactic/sls/sls_engine.cpp rename to src/ast/sls/sls_engine.cpp index 58676edfb..8bf70f3dd 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/ast/sls/sls_engine.cpp @@ -23,11 +23,10 @@ Notes: #include "ast/ast_pp.h" #include "ast/rewriter/var_subst.h" #include "model/model_pp.h" -#include "tactic/tactic.h" #include "util/luby.h" -#include "tactic/sls/sls_params.hpp" -#include "tactic/sls/sls_engine.h" +#include "params/sls_params.hpp" +#include "ast/sls/sls_engine.h" sls_engine::sls_engine(ast_manager & m, params_ref const & p) : @@ -52,7 +51,6 @@ sls_engine::~sls_engine() { void sls_engine::updt_params(params_ref const & _p) { sls_params p(_p); - m_produce_models = _p.get_bool("model", false); m_max_restarts = p.max_restarts(); m_tracker.set_random_seed(p.random_seed()); m_walksat = p.walksat(); @@ -92,14 +90,12 @@ void sls_engine::collect_statistics(statistics& st) const { st.update("sls moves/sec", m_stats.m_moves / seconds); } -void sls_engine::checkpoint() { - tactic::checkpoint(m_manager); -} bool sls_engine::full_eval(model & mdl) { model::scoped_model_completion _scm(mdl, true); for (expr* a : m_assertions) { - checkpoint(); + if (!m_manager.inc()) + return false; if (!mdl.is_true(a)) { TRACE("sls", tout << "Evaluation: false\n";); return false; @@ -423,7 +419,8 @@ lbool sls_engine::search() { unsigned sz = m_assertions.size(); while (check_restart(m_stats.m_moves)) { - checkpoint(); + if (!m_manager.inc()) + return l_undef; m_stats.m_moves++; // Andreas: Every base restart interval ... @@ -523,38 +520,6 @@ bailout: return res; } -void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { - if (g->inconsistent()) { - mc = nullptr; - return; - } - - m_produce_models = g->models_enabled(); - - for (unsigned i = 0; i < g->size(); i++) - assert_expr(g->form(i)); - - lbool res = operator()(); - - if (res == l_true) { - report_tactic_progress("Number of flips:", m_stats.m_moves); - for (unsigned i = 0; i < g->size(); i++) - if (!m_mpz_manager.is_one(m_tracker.get_value(g->form(i)))) - { - verbose_stream() << "Terminated before all assertions were SAT!" << std::endl; - NOT_IMPLEMENTED_YET(); - } - - if (m_produce_models) { - model_ref mdl = m_tracker.get_model(); - mc = model2model_converter(mdl.get()); - TRACE("sls_model", mc->display(tout);); - } - g->reset(); - } - else - mc = nullptr; -} lbool sls_engine::operator()() { m_tracker.initialize(m_assertions); @@ -565,9 +530,10 @@ lbool sls_engine::operator()() { lbool res = l_undef; do { - checkpoint(); + if (!m_manager.inc()) + return l_undef; - report_tactic_progress("Searching... restarts left:", m_max_restarts - m_stats.m_restarts); + // report_tactic_progress("Searching... restarts left:", m_max_restarts - m_stats.m_restarts); res = search(); if (res == l_undef) diff --git a/src/tactic/sls/sls_engine.h b/src/ast/sls/sls_engine.h similarity index 91% rename from src/tactic/sls/sls_engine.h rename to src/ast/sls/sls_engine.h index 5f290c626..32338b8ae 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/ast/sls/sls_engine.h @@ -21,10 +21,9 @@ Notes: #include "util/stopwatch.h" #include "util/lbool.h" #include "ast/converters/model_converter.h" -#include "tactic/goal.h" -#include "tactic/sls/sls_tracker.h" -#include "tactic/sls/sls_evaluator.h" +#include "ast/sls/sls_tracker.h" +#include "ast/sls/sls_evaluator.h" #include "util/statistics.h" class sls_engine { @@ -62,7 +61,6 @@ protected: unsynch_mpz_manager m_mpz_manager; powers m_powers; mpz m_zero, m_one, m_two; - bool m_produce_models; bv_util m_bv_util; sls_tracker m_tracker; sls_evaluator m_evaluator; @@ -96,7 +94,7 @@ public: void assert_expr(expr * e) { m_assertions.push_back(e); } - // stats const & get_stats(void) { return m_stats; } + stats const & get_stats(void) { return m_stats; } void collect_statistics(statistics & st) const; void reset_statistics() { m_stats.reset(); } @@ -111,10 +109,14 @@ public: lbool search(); lbool operator()(); - void operator()(goal_ref const & g, model_converter_ref & mc); + + mpz & get_value(expr * n) { return m_tracker.get_value(n); } + + model_ref get_model() { return m_tracker.get_model(); } + + unsynch_mpz_manager& get_mpz_manager() { return m_mpz_manager; } protected: - void checkpoint(); bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, double & best_score, unsigned & best_const, mpz & best_value); @@ -135,5 +137,7 @@ protected: //double get_restart_armin(unsigned cnt_restarts); unsigned check_restart(unsigned curr_value); + + }; diff --git a/src/tactic/sls/sls_evaluator.h b/src/ast/sls/sls_evaluator.h similarity index 99% rename from src/tactic/sls/sls_evaluator.h rename to src/ast/sls/sls_evaluator.h index d386ece15..2ee03c928 100644 --- a/src/tactic/sls/sls_evaluator.h +++ b/src/ast/sls/sls_evaluator.h @@ -21,8 +21,8 @@ Notes: #include "model/model_evaluator.h" -#include "tactic/sls/sls_powers.h" -#include "tactic/sls/sls_tracker.h" +#include "ast/sls/sls_powers.h" +#include "ast/sls/sls_tracker.h" class sls_evaluator { ast_manager & m_manager; diff --git a/src/tactic/sls/sls_powers.h b/src/ast/sls/sls_powers.h similarity index 100% rename from src/tactic/sls/sls_powers.h rename to src/ast/sls/sls_powers.h diff --git a/src/tactic/sls/sls_tracker.h b/src/ast/sls/sls_tracker.h similarity index 99% rename from src/tactic/sls/sls_tracker.h rename to src/ast/sls/sls_tracker.h index 951153a5c..67723828f 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/ast/sls/sls_tracker.h @@ -25,8 +25,8 @@ Notes: #include "ast/bv_decl_plugin.h" #include "model/model.h" -#include "tactic/sls/sls_params.hpp" -#include "tactic/sls/sls_powers.h" +#include "params/sls_params.hpp" +#include "ast/sls/sls_powers.h" class sls_tracker { ast_manager & m_manager; diff --git a/src/ast/well_sorted.cpp b/src/ast/well_sorted.cpp index fa8e2768b..84cdf28ad 100644 --- a/src/ast/well_sorted.cpp +++ b/src/ast/well_sorted.cpp @@ -70,8 +70,7 @@ struct well_sorted_proc { strm << "Expected sort: " << mk_pp(expected_sort, m_manager) << '\n'; strm << "Actual sort: " << mk_pp(actual_sort, m_manager) << '\n'; strm << "Function sort: " << mk_pp(decl, m_manager) << '.'; - auto str = strm.str(); - warning_msg("%s", str.c_str()); + warning_msg("%s", std::move(strm).str().c_str()); m_error = true; return; } diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index b4d587bec..20e45a218 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -291,7 +291,6 @@ private: int bound_sign = (is_lower_bound ? 1 : -1); int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; - SASSERT(!tv::is_term(bound_j)); u_dependency* ret = nullptr; for (auto const& r : lar->get_row(row_index)) { unsigned j = r.var(); diff --git a/src/math/lp/ul_pair.h b/src/math/lp/column.h similarity index 77% rename from src/math/lp/ul_pair.h rename to src/math/lp/column.h index 354070281..1b2d1f2e5 100644 --- a/src/math/lp/ul_pair.h +++ b/src/math/lp/column.h @@ -37,17 +37,18 @@ inline std::ostream& operator<<(std::ostream& out, lconstraint_kind k) { return out << "??"; } -inline bool compare(const std::pair & a, const std::pair & b) { +inline bool compare(const std::pair & a, const std::pair & b) { return a.second < b.second; } - -class ul_pair { +class lar_term; // forward definition +class column { u_dependency* m_lower_bound_witness = nullptr; u_dependency* m_upper_bound_witness = nullptr; bool m_associated_with_row = false; + lar_term* m_term = nullptr; public: - // TODO - seems more straight-forward to just expose ul_pair as a struct with direct access to attributes. - + lar_term* term() const { return m_term; } + u_dependency*& lower_bound_witness() { return m_lower_bound_witness; } u_dependency* lower_bound_witness() const { return m_lower_bound_witness; } u_dependency*& upper_bound_witness() { return m_upper_bound_witness; } @@ -56,20 +57,21 @@ public: // equality is used by stackedvector operations. // this appears to be a low level reason - bool operator!=(const ul_pair & p) const { + bool operator!=(const column & p) const { return !(*this == p); } - bool operator==(const ul_pair & p) const { + bool operator==(const column & p) const { return m_lower_bound_witness == p.m_lower_bound_witness && m_upper_bound_witness == p.m_upper_bound_witness && m_associated_with_row == p.m_associated_with_row; } - // empty constructor - ul_pair() {} + column() = delete; + column(bool) = delete; - ul_pair(bool associated_with_row) : - m_associated_with_row(associated_with_row) {} + + column(bool associated_with_row, lar_term* term) : + m_associated_with_row(associated_with_row), m_term(term) {} bool associated_with_row() const { return m_associated_with_row; } }; diff --git a/src/math/lp/emonics.cpp b/src/math/lp/emonics.cpp index 5d0e664f2..e6e52e57b 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -536,7 +536,7 @@ bool emonics::invariant() const { do { auto const& m = m_monics[c->m_index]; bool found = false; - for (lp::var_index w : m.rvars()) { + for (lp::lpvar w : m.rvars()) { auto w1 = m_ve.find(w); found |= v1.var() == w1.var(); } diff --git a/src/math/lp/factorization.cpp b/src/math/lp/factorization.cpp index 229fca61f..e1dcff626 100644 --- a/src/math/lp/factorization.cpp +++ b/src/math/lp/factorization.cpp @@ -1,3 +1,12 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Author: +Lev Nachmanson (levnach) +Nikolaj Bjorner (nbjorner) + +--*/ + #include "util/vector.h" #include "math/lp/factorization.h" namespace nla { @@ -7,11 +16,10 @@ void const_iterator_mon::init_vars_by_the_mask(unsigned_vector & k_vars, unsigne SASSERT(m_mask.size() + 1 == m_ff->m_vars.size()); k_vars.push_back(m_ff->m_vars.back()); for (unsigned j = 0; j < m_mask.size(); j++) { - if (m_mask[j]) { - k_vars.push_back(m_ff->m_vars[j]); - } else { - j_vars.push_back(m_ff->m_vars[j]); - } + if (m_mask[j]) + k_vars.push_back(m_ff->m_vars[j]); + else + j_vars.push_back(m_ff->m_vars[j]); } } // todo : do we need the sign? @@ -29,9 +37,9 @@ bool const_iterator_mon::get_factors(factor& k, factor& j, rational& sign) const m_full_factorization_returned = true; return false; } - if (k_vars.size() == 1) { - k.set(k_vars[0], factor_type::VAR); - } else { + if (k_vars.size() == 1) + k.set(k_vars[0], factor_type::VAR); + else { unsigned i; if (!m_ff->find_canonical_monic_of_vars(k_vars, i)) { ++m_num_failures; @@ -41,9 +49,9 @@ bool const_iterator_mon::get_factors(factor& k, factor& j, rational& sign) const } m_num_failures = 0; - if (j_vars.size() == 1) { - j.set(j_vars[0], factor_type::VAR); - } else { + if (j_vars.size() == 1) + j.set(j_vars[0], factor_type::VAR); + else { unsigned i; if (!m_ff->find_canonical_monic_of_vars(j_vars, i)) { ++m_num_failures; diff --git a/src/math/lp/factorization.h b/src/math/lp/factorization.h index 04529d033..e1096a75f 100644 --- a/src/math/lp/factorization.h +++ b/src/math/lp/factorization.h @@ -2,20 +2,10 @@ /*++ Copyright (c) 2017 Microsoft Corporation - Module Name: - - - - Abstract: - - - Author: Lev Nachmanson (levnach) Nikolaj Bjorner (nbjorner) - Revision History: - --*/ #pragma once diff --git a/src/math/lp/general_matrix.h b/src/math/lp/general_matrix.h index a4f6693a2..fb1030e6b 100644 --- a/src/math/lp/general_matrix.h +++ b/src/math/lp/general_matrix.h @@ -109,7 +109,7 @@ public: auto & row = m_data[adjust_row(i)]; lp_assert(row_is_initialized_correctly(row)); for (lp::lar_term::ival p : c) { - unsigned j = adjust_column(column_fix(p.column().index())); + unsigned j = adjust_column(column_fix(p.j())); row[j] = sign * p.coeff(); } } diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 5c38a8c7f..7b4347af5 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -22,10 +22,10 @@ #include "math/lp/lar_solver.h" #include "math/lp/lp_utils.h" -#define SMALL_CUTS 1 namespace lp { - -class create_cut { + +enum class row_polarity { UNDEF, MIN, MAX, MIXED}; +struct create_cut { lar_term & m_t; // the term to return in the cut mpq & m_k; // the right side of the cut explanation* m_ex; // the conflict explanation @@ -36,10 +36,10 @@ class create_cut { mpq m_one_minus_f; mpq m_fj; mpq m_one_minus_fj; -#if SMALL_CUTS mpq m_abs_max, m_big_number; -#endif - struct found_big {}; + row_polarity m_polarity; + bool m_found_big; + u_dependency* m_dep; const impq & get_value(unsigned j) const { return lia.get_value(j); } bool is_int(unsigned j) const { return lia.column_is_int(j) || (lia.is_fixed(j) && @@ -69,8 +69,8 @@ class create_cut { // here we have the product of new_a*(xj - lb(j)), so new_a*lb(j) is added to m_k new_a = m_fj <= m_one_minus_f ? m_fj / m_one_minus_f : ((1 - m_fj) / m_f); lp_assert(new_a.is_pos()); - m_k.addmul(new_a, lower_bound(j).x); - push_explanation(column_lower_bound_constraint(j)); + m_k.addmul(new_a, lower_bound(j).x); + push_explanation(column_lower_bound_constraint(j)); } else { lp_assert(at_upper(j)); @@ -79,50 +79,58 @@ class create_cut { lp_assert(new_a.is_neg()); m_k.addmul(new_a, upper_bound(j).x); push_explanation(column_upper_bound_constraint(j)); - } + } m_t.add_monomial(new_a, j); TRACE("gomory_cut_detail", tout << "new_a = " << new_a << ", k = " << m_k << "\n";); -#if SMALL_CUTS - // if (numerator(new_a).is_big()) throw found_big(); if (numerator(new_a) > m_big_number) - throw found_big(); -#endif + m_found_big = true; + } + + void set_polarity(row_polarity p) { + if (m_polarity == row_polarity::MIXED) return; + if (m_polarity == row_polarity::UNDEF) m_polarity = p; + else if (m_polarity != p) m_polarity = row_polarity::MIXED; } void real_case_in_gomory_cut(const mpq & a, unsigned j) { TRACE("gomory_cut_detail_real", tout << "j = " << j << ", a = " << a << ", m_k = " << m_k << "\n";); mpq new_a; if (at_lower(j)) { - if (a.is_pos()) + if (a.is_pos()) { // the delta is a (x - f) is positive it has to grow and fight m_one_minus_f new_a = a / m_one_minus_f; - else + set_polarity(row_polarity::MIN); // reverse the polarity since a = -p.coeff() + } + else { // the delta is negative and it works again m_f new_a = - a / m_f; + set_polarity(row_polarity::MAX); + } m_k.addmul(new_a, lower_bound(j).x); // is it a faster operation than - // k += lower_bound(j).x * new_a; + // k += lower_bound(j).x * new_a; push_explanation(column_lower_bound_constraint(j)); } else { lp_assert(at_upper(j)); - if (a.is_pos()) + if (a.is_pos()) { // the delta is works again m_f - new_a = - a / m_f; - else + new_a = - a / m_f; + set_polarity(row_polarity::MAX); + } + else { // the delta is positive works again m_one_minus_f - new_a = a / m_one_minus_f; - m_k.addmul(new_a, upper_bound(j).x); // k += upper_bound(j).x * new_a; + new_a = a / m_one_minus_f; + set_polarity(row_polarity::MIN); + } + m_k.addmul(new_a, upper_bound(j).x); // k += upper_bound(j).x * new_a; push_explanation(column_upper_bound_constraint(j)); } m_t.add_monomial(new_a, j); TRACE("gomory_cut_detail_real", tout << "add " << new_a << "*v" << j << ", k: " << m_k << "\n"; tout << "m_t = "; lia.lra.print_term(m_t, tout) << "\nk: " << m_k << "\n";); -#if SMALL_CUTS - // if (numerator(new_a).is_big()) throw found_big(); if (numerator(new_a) > m_big_number) - throw found_big(); -#endif + m_found_big = true; } lia_move report_conflict_from_gomory_cut() { @@ -148,7 +156,7 @@ class create_cut { template void dump_coeff(std::ostream & out, const T& c) const { - dump_coeff_val(out << "(* ", c.coeff()) << " " << var_name(c.column().index()) << ")"; + dump_coeff_val(out << "(* ", c.coeff()) << " " << var_name(c.j()) << ")"; } std::ostream& dump_row_coefficients(std::ostream & out) const { @@ -175,15 +183,15 @@ class create_cut { for (const auto & p : m_row) dump_declaration(out, p.var()); for (lar_term::ival p : m_t) { - auto t = lia.lra.column2tv(p.column()); - if (t.is_term()) - dump_declaration(out, t.id()); + if (lia.lra.column_has_term(p.j())) + dump_declaration(out, p.j()); } } void dump_lower_bound_expl(std::ostream & out, unsigned j) const { out << "(assert (>= " << var_name(j) << " " << lower_bound(j).x << "))\n"; } + void dump_upper_bound_expl(std::ostream & out, unsigned j) const { out << "(assert (<= " << var_name(j) << " " << upper_bound(j).x << "))\n"; } @@ -238,17 +246,40 @@ public: lia_move cut() { TRACE("gomory_cut", dump(tout);); - - // gomory will be t >= k and the current solution has a property t < k + // If m_polarity is MAX, then + // the row constraints the base variable to be at the maximum, + // MIN - at the minimum, + // MIXED : the row does not constraint the base variable to be at an extremum + // UNDEF is the initial state + m_polarity = row_polarity::UNDEF; + // gomory cut will be m_t >= m_k and the current solution has a property m_t < m_k m_k = 1; m_t.clear(); - mpq m_f = fractional_part(get_value(m_inf_col)); + m_ex->clear(); + m_found_big = false; TRACE("gomory_cut_detail", tout << "m_f: " << m_f << ", "; tout << "1 - m_f: " << 1 - m_f << ", get_value(m_inf_col).x - m_f = " << get_value(m_inf_col).x - m_f << "\n";); lp_assert(m_f.is_pos() && (get_value(m_inf_col).x - m_f).is_int()); - - bool some_int_columns = false; -#if SMALL_CUTS + auto set_polarity_for_int = [&](const mpq & a, lpvar j) { + if (a.is_pos()) { + if (at_lower(j)) + set_polarity(row_polarity::MAX); + else if (at_upper(j)) + set_polarity(row_polarity::MIN); + else + set_polarity(row_polarity::MIXED); + } + else { + if (at_lower(j)) + set_polarity(row_polarity::MIN); + else if (at_upper(j)) + set_polarity(row_polarity::MAX); + else + set_polarity(row_polarity::MIXED); + } + }; + + m_abs_max = 0; for (const auto & p : m_row) { mpq t = abs(ceil(p.coeff())); @@ -256,143 +287,52 @@ public: m_abs_max = t; } m_big_number = m_abs_max.expt(2); -#endif - mpq one_min_m_f = 1 - m_f; + for (const auto & p : m_row) { unsigned j = p.var(); - if (j == m_inf_col) { - lp_assert(p.coeff() == one_of_type()); + if (j == m_inf_col) continue; + // use -p.coeff() to make the format compatible with the format used in: Integrating Simplex with DPLL(T) + + if (lia.is_fixed(j)) { + push_explanation(column_lower_bound_constraint(j)); + push_explanation(column_upper_bound_constraint(j)); continue; } - - // use -p.coeff() to make the format compatible with the format used in: Integrating Simplex with DPLL(T) - try { - if (lia.is_fixed(j)) { - push_explanation(column_lower_bound_constraint(j)); - push_explanation(column_upper_bound_constraint(j)); - continue; - } - if (is_real(j)) - real_case_in_gomory_cut(- p.coeff(), j); - else if (!p.coeff().is_int()) { - some_int_columns = true; + if (is_real(j)) + real_case_in_gomory_cut(- p.coeff(), j); + else { + if (!p.coeff().is_int()) { m_fj = fractional_part(-p.coeff()); m_one_minus_fj = 1 - m_fj; int_case_in_gomory_cut(j); } + if (m_polarity != row_polarity::MIXED) + set_polarity_for_int(p.coeff(), j); + } - catch (found_big) { - m_ex->clear(); - m_t.clear(); - m_k = 1; + + if (m_found_big) { return lia_move::undef; } } - if (m_t.is_empty()) + + if (m_t.is_empty()) { return report_conflict_from_gomory_cut(); - if (some_int_columns) - simplify_inequality(); - lp_assert(lia.current_solution_is_inf_on_cut()); // checks that indices are columns + } TRACE("gomory_cut", print_linear_combination_of_column_indices_only(m_t.coeffs_as_vector(), tout << "gomory cut: "); tout << " >= " << m_k << std::endl;); + + m_dep = nullptr; + for (auto c : *m_ex) + m_dep = lia.lra.join_deps(lia.lra.dep_manager().mk_leaf(c.ci()), m_dep); + TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout); lia.lra.display(tout)); + SASSERT(lia.current_solution_is_inf_on_cut()); + lia.settings().stats().m_gomory_cuts++; return lia_move::cut; } - // TODO: use this also for HNF cuts? - mpq m_lcm_den = { mpq(1) }; - - void simplify_inequality() { - - auto divd = [](mpq& r, mpq const& d) { - r /= d; - if (!r.is_int()) - r = ceil(r); - }; - SASSERT(!lia.m_upper); - lp_assert(!m_t.is_empty()); - // k = 1 + sum of m_t at bounds - lar_term t = lia.lra.unfold_nested_subterms(m_t); - auto pol = t.coeffs_as_vector(); - m_t.clear(); - if (pol.size() == 1 && is_int(pol[0].second)) { - TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); - auto const& [a, v] = pol[0]; - lp_assert(is_int(v)); - if (a.is_pos()) { // we have av >= k - divd(m_k, a); - m_t.add_monomial(mpq(1), v); - } - else { - // av >= k - // a/-a*v >= k / - a - // -v >= k / - a - // -v >= ceil(k / -a) - divd(m_k, -a); - m_t.add_monomial(-mpq(1), v); - } - } - else { - m_lcm_den = denominator(m_k); - for (auto const& [c, v] : pol) - m_lcm_den = lcm(m_lcm_den, denominator(c)); - lp_assert(m_lcm_den.is_pos()); - bool int_row = all_of(pol, [&](auto const& kv) { return is_int(kv.second); }); - TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;); - - if (!m_lcm_den.is_one()) { - // normalize coefficients of integer parameters to be integers. - for (auto & [c,v]: pol) { - c *= m_lcm_den; - SASSERT(!is_int(v) || c.is_int()); - } - m_k *= m_lcm_den; - } - // ax + by >= k - // b > 0, c1 <= y <= c2 - // ax + b*c2 >= ax + by >= k - // => - // ax >= k - by >= k - b*c1 - // b < 0 - // ax + b*c1 >= ax + by >= k - // - unsigned j = 0, i = 0; - for (auto & [c, v] : pol) { - if (lia.is_fixed(v)) { - push_explanation(column_lower_bound_constraint(v)); - push_explanation(column_upper_bound_constraint(v)); - m_k -= c * lower_bound(v).x; - } - else - pol[j++] = pol[i]; - ++i; - } - pol.shrink(j); - - // gcd reduction is loss-less: - mpq g(1); - for (const auto & [c, v] : pol) - g = gcd(g, c); - if (!int_row) - g = gcd(g, m_k); - - if (g != 1) { - for (auto & [c, v] : pol) - c /= g; - divd(m_k, g); - } - - for (const auto & [c, v]: pol) - m_t.add_monomial(c, v); - VERIFY(m_t.size() > 0); - } - - TRACE("gomory_cut_detail", tout << "k = " << m_k << std::endl;); - lp_assert(m_k.is_int()); - } - - create_cut(lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip& row, int_solver& lia) : m_t(t), m_k(k), @@ -403,24 +343,218 @@ public: m_f(fractional_part(get_value(basic_inf_int_j).x)), m_one_minus_f(1 - m_f) {} -}; + }; -lia_move gomory::cut(lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip& row) { - create_cut cc(t, k, ex, basic_inf_int_j, row, lia); - return cc.cut(); -} + bool gomory::is_gomory_cut_target(lpvar k) { + SASSERT(lia.is_base(k)); + const row_strip& row = lra.get_row(lia.row_of_basic_column(k)); + // Consider monomial c*x from the row, where x is non-basic. + // Then, for each such monomial, one of following conditions + // has to hold for the row to be eligible for Gomory cut: + // 1) c is integral and x integral varible with an integral value + // 2) the value of x is at a bound and has no infinitesimals. + + + unsigned j; + for (const auto & p : row) { + j = p.var(); + if (k == j) continue; + + if (p.coeff().is_int() && lia.column_is_int(j) && lia.get_value(j).is_int()) continue; + + if ( !lia.at_bound(j) || lia.get_value(j).y != 0) { + TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; + lia.display_column(tout, j); + tout << "infinitesimal: " << !(lia.get_value(j).y ==0) << "\n";); + return false; + } + } + return true; + + // Condition 1) above can be relaxed even more, allowing any value for x, but it will change the calculation for m_f. + } + + // return the minimal distance from the variable value to an integer + mpq get_gomory_score(const int_solver& lia, lpvar j) { + const mpq& val = lia.get_value(j).x; + auto l = val - floor(val); + if (l <= mpq(1, 2)) + return l; + return mpq(1) - l; + } + + unsigned_vector gomory::gomory_select_int_infeasible_vars(unsigned num_cuts) { + std::list sorted_vars; + std::unordered_map score; + for (lpvar j : lra.r_basis()) { + if (!lia.column_is_int_inf(j) || !is_gomory_cut_target(j)) + continue; + SASSERT(!lia.is_fixed(j)); + sorted_vars.push_back(j); + score[j] = get_gomory_score(lia, j); + } + // prefer the variables with the values close to integers + sorted_vars.sort([&](lpvar j, lpvar k) { + auto diff = score[j] - score[k]; + if (diff.is_neg()) + return true; + if (diff.is_pos()) + return false; + return lra.usage_in_terms(j) > lra.usage_in_terms(k); + }); + unsigned_vector ret; + unsigned n = static_cast(sorted_vars.size()); + + while (num_cuts-- && n > 0) { + unsigned k = lia.random() % n; + + double k_ratio = k / (double) n; + k_ratio *= k_ratio*k_ratio; // square k_ratio to make it smaller + k = static_cast(std::floor(k_ratio * n)); + // these operations move k to the beginning of the indices range + SASSERT(0 <= k && k < n); + auto it = sorted_vars.begin(); + while(k--) it++; + + ret.push_back(*it); + sorted_vars.erase(it); + n--; + } + return ret; + } -lia_move gomory::get_cut(lpvar j) { - unsigned r = lia.row_of_basic_column(j); - const row_strip& row = lra.get_row(r); - SASSERT(lra.row_is_correct(r)); - SASSERT(lia.is_gomory_cut_target(j)); - lia.m_upper = false; - lia.m_cut_vars.push_back(j); - return cut(lia.m_t, lia.m_k, lia.m_ex, j, row); -} - - -gomory::gomory(int_solver& lia): lia(lia), lra(lia.lra) { } - + row_polarity test_row_polarity(const int_solver& lia, const row_strip& row, lpvar basic_j) { + row_polarity ret = row_polarity::UNDEF; + for (const auto& p : row) { + lpvar j = p.var(); + if (j == basic_j) + continue; + if (lia.is_fixed(j)) + continue; + + row_polarity rp; + if (p.coeff().is_pos()) { + if (lia.at_lower(j)) + rp = row_polarity::MAX; + else if (lia.at_upper(j)) + rp = row_polarity::MIN; + else + rp = row_polarity::MIXED; + } + else { + if (lia.at_lower(j)) + rp = row_polarity::MIN; + else if (lia.at_upper(j)) + rp = row_polarity::MAX; + else + rp = row_polarity::MIXED; + + } + if (ret == row_polarity::UNDEF) + ret = rp; + if (ret != rp) + return row_polarity::MIXED; + } + return ret; + } + + u_dependency* gomory::add_deps(u_dependency* dep, const row_strip& row, lpvar basic_var) { + u_dependency* ret = dep; + for (const auto& p : row) { + lpvar j = p.var(); + if (j == basic_var) + continue; + if (lia.is_fixed(j)) + continue; + if (lia.is_real(j)) continue; + if (!p.coeff().is_int()) continue; + // the explanation for all above have been already added + if (lia.at_lower(j)) + ret = lia.lra.dep_manager().mk_join(lia.column_lower_bound_constraint(j), ret); + else { + SASSERT(lia.at_upper(j)); + ret = lia.lra.dep_manager().mk_join(lia.column_upper_bound_constraint(j), ret); + } + } + return ret; + } + + lia_move gomory::get_gomory_cuts(unsigned num_cuts) { + struct cut_result {lar_term t; mpq k; u_dependency *dep;}; + vector big_cuts; + unsigned_vector columns_for_cuts = gomory_select_int_infeasible_vars(num_cuts); + bool has_small_cut = false; + + // define inline helper functions + auto is_small_cut = [&](lar_term const& t) { + return all_of(t, [&](auto ci) { return ci.coeff().is_small(); }); + }; + auto add_cut = [&](const lar_term& t, const mpq& k, u_dependency * dep) { + lp::lpvar j = lra.add_term(t.coeffs_as_vector(), UINT_MAX); + lra.update_column_type_and_bound(j, lp::lconstraint_kind::GE, k, dep); + }; + auto _check_feasible = [&](void) { + lra.find_feasible_solution(); + if (!lra.is_feasible() && !lia.settings().get_cancel_flag()) { + lra.get_infeasibility_explanation(*lia.m_ex); + return false; + } + return true; + }; + +// start creating cuts + for (unsigned j : columns_for_cuts) { + SASSERT(is_gomory_cut_target(j)); + unsigned row_index = lia.row_of_basic_column(j); + const row_strip& row = lra.get_row(row_index); + create_cut cc(lia.m_t, lia.m_k, lia.m_ex, j, row, lia); + auto r = cc.cut(); + if (r != lia_move::cut) { + if (r == lia_move::conflict) + return lia_move::conflict; + continue; + } + SASSERT(test_row_polarity(lia, row, j) == cc.m_polarity); + if (cc.m_polarity == row_polarity::MAX) + lra.update_column_type_and_bound(j, lp::lconstraint_kind::LE, floor(lra.get_column_value(j).x), add_deps(cc.m_dep, row, j)); + else if (cc.m_polarity == row_polarity::MIN) + lra.update_column_type_and_bound(j, lp::lconstraint_kind::GE, ceil(lra.get_column_value(j).x), add_deps(cc.m_dep, row, j)); + + if (!is_small_cut(lia.m_t)) { + big_cuts.push_back({cc.m_t, cc.m_k, cc.m_dep}); + continue; + } + has_small_cut = true; + add_cut(cc.m_t, cc.m_k, cc.m_dep); + if (lia.settings().get_cancel_flag()) + return lia_move::undef; + } + + if (big_cuts.size()) { + lra.push(); + for (auto const& cut : big_cuts) + add_cut(cut.t, cut.k, cut.dep); + bool feas = _check_feasible(); + lra.pop(1); + + if (!feas) + for (auto const& cut : big_cuts) + add_cut(cut.t, cut.k, cut.dep); + } + + if (!_check_feasible()) + return lia_move::conflict; + + if (!lia.has_inf_int()) + return lia_move::sat; + + if (has_small_cut || big_cuts.size()) + return lia_move::continue_with_check; + + lra.move_non_basic_columns_to_bounds(); + return lia_move::undef; + } + + + gomory::gomory(int_solver& lia): lia(lia), lra(lia.lra) { } } diff --git a/src/math/lp/gomory.h b/src/math/lp/gomory.h index cdb21a0c3..0fdf4f8fe 100644 --- a/src/math/lp/gomory.h +++ b/src/math/lp/gomory.h @@ -27,9 +27,11 @@ namespace lp { class gomory { class int_solver& lia; class lar_solver& lra; - lia_move cut(lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip& row); + unsigned_vector gomory_select_int_infeasible_vars(unsigned num_cuts); + bool is_gomory_cut_target(lpvar j); + u_dependency* add_deps(u_dependency*, const row_strip&, lpvar); public: + lia_move get_gomory_cuts(unsigned num_cuts); gomory(int_solver& lia); - lia_move get_cut(lpvar j); }; } diff --git a/src/math/lp/hnf_cutter.cpp b/src/math/lp/hnf_cutter.cpp index 75095ca51..b120b7aac 100644 --- a/src/math/lp/hnf_cutter.cpp +++ b/src/math/lp/hnf_cutter.cpp @@ -20,7 +20,7 @@ namespace lp { lra(lia.lra), m_settings(lia.settings()), m_abs_max(zero_of_type()), - m_var_register(false) {} + m_var_register() {} bool hnf_cutter::is_full() const { return @@ -50,7 +50,7 @@ namespace lp { m_constraints_for_explanation.push_back(ci); for (lar_term::ival p : *t) { - m_var_register.add_var(p.column().index(), true); // hnf only deals with integral variables for now + m_var_register.add_var(p.j(), true); // hnf only deals with integral variables for now mpq t = abs(ceil(p.coeff())); if (t > m_abs_max) m_abs_max = t; @@ -227,12 +227,12 @@ branch y_i >= ceil(y0_i) is impossible. svector hnf_cutter::vars() const { return m_var_register.vars(); } - void hnf_cutter::try_add_term_to_A_for_hnf(tv const &i) { + void hnf_cutter::try_add_term_to_A_for_hnf(lpvar j) { mpq rs; - const lar_term& t = lra.get_term(i); + const lar_term& t = lra.get_term(j); u_dependency* dep; bool upper_bound; - if (!is_full() && lra.get_equality_and_right_side_for_term_on_current_x(i, rs, dep, upper_bound)) { + if (!is_full() && lra.get_equality_and_right_side_for_term_on_current_x(j, rs, dep, upper_bound)) { add_term(&t, rs, dep, upper_bound); } } @@ -243,8 +243,8 @@ branch y_i >= ceil(y0_i) is impossible. bool hnf_cutter::init_terms_for_hnf_cut() { clear(); - for (unsigned i = 0; i < lra.terms().size() && !is_full(); i++) - try_add_term_to_A_for_hnf(tv::term(i)); + for (const lar_term* t: lra.terms()) + try_add_term_to_A_for_hnf(t->j()); return hnf_has_var_with_non_integral_value(); } diff --git a/src/math/lp/hnf_cutter.h b/src/math/lp/hnf_cutter.h index 74fb52327..07ddc7b12 100644 --- a/src/math/lp/hnf_cutter.h +++ b/src/math/lp/hnf_cutter.h @@ -50,7 +50,7 @@ public: private: bool init_terms_for_hnf_cut(); bool hnf_has_var_with_non_integral_value() const; - void try_add_term_to_A_for_hnf(tv const& i); + void try_add_term_to_A_for_hnf(lpvar); unsigned terms_count() const { return m_terms.size(); } const mpq & abs_max() const { return m_abs_max; } diff --git a/src/math/lp/int_branch.cpp b/src/math/lp/int_branch.cpp index 1a998e92a..10211c4fa 100644 --- a/src/math/lp/int_branch.cpp +++ b/src/math/lp/int_branch.cpp @@ -31,9 +31,10 @@ lia_move int_branch::operator()() { lia_move int_branch::create_branch_on_column(int j) { TRACE("check_main_int", tout << "branching" << std::endl;); - lp_assert(lia.m_t.is_empty()); + lia.m_t.clear(); + lp_assert(j != -1); - lia.m_t.add_monomial(mpq(1), lra.column_to_reported_index(j)); + lia.m_t.add_monomial(mpq(1), j); if (lia.is_free(j)) { lia.m_upper = lia.random() % 2; lia.m_k = mpq(0); diff --git a/src/math/lp/int_cube.cpp b/src/math/lp/int_cube.cpp index 9ef9aa341..c8488ca37 100644 --- a/src/math/lp/int_cube.cpp +++ b/src/math/lp/int_cube.cpp @@ -55,21 +55,21 @@ namespace lp { lia.settings().stats().m_cube_success++; return lia_move::sat; } - +// i is the column index having the term bool int_cube::tighten_term_for_cube(unsigned i) { - if (!lra.term_is_used_as_row(i)) + if (!lra.column_associated_with_row(i)) return true; - const lar_term* t = lra.terms()[i]; - impq delta = get_cube_delta_for_term(*t); - TRACE("cube", lra.print_term_as_indices(*t, tout); tout << ", delta = " << delta << "\n";); + const lar_term& t = lra.get_term(i); + impq delta = get_cube_delta_for_term(t); + TRACE("cube", lra.print_term_as_indices(t, tout); tout << ", delta = " << delta << "\n";); if (is_zero(delta)) return true; - return lra.tighten_term_bounds_by_delta(tv::term(i), delta); + return lra.tighten_term_bounds_by_delta(i, delta); } bool int_cube::tighten_terms_for_cube() { - for (unsigned i = 0; i < lra.terms().size(); i++) - if (!tighten_term_for_cube(i)) { + for (const lar_term* t: lra.terms()) + if (!tighten_term_for_cube(t->j())) { TRACE("cube", tout << "cannot tighten";); return false; } @@ -86,7 +86,7 @@ namespace lp { bool seen_minus = false; bool seen_plus = false; for(lar_term::ival p : t) { - if (!lia.column_is_int(p.column())) + if (!lia.column_is_int(p.j())) goto usual_delta; const mpq & c = p.coeff(); if (c == one_of_type()) { @@ -104,7 +104,7 @@ namespace lp { usual_delta: mpq delta = zero_of_type(); for (lar_term::ival p : t) - if (lia.column_is_int(p.column())) + if (lia.column_is_int(p.j())) delta += abs(p.coeff()); delta *= mpq(1, 2); diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 90f5ff651..f547ba274 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -198,16 +198,10 @@ namespace lp { if (r == lia_move::undef) lra.move_non_basic_columns_to_bounds(); if (r == lia_move::undef && should_hnf_cut()) r = hnf_cut(); - std::function gomory_fn = [&](lpvar j) { return gomory(*this).get_cut(j); }; - if (r == lia_move::undef && should_gomory_cut()) r = local_cut(2, gomory_fn); + if (r == lia_move::undef && should_gomory_cut()) r = gomory(*this).get_gomory_cuts(2); if (r == lia_move::undef) r = int_branch(*this)(); - - m_cut_vars.reset(); - if (settings().get_cancel_flag()) - return lia_move::undef; - if (r == lia_move::undef) - r = int_branch(*this)(); + if (settings().get_cancel_flag()) r = lia_move::undef; return r; } @@ -234,7 +228,7 @@ namespace lp { bool int_solver::cut_indices_are_columns() const { for (lar_term::ival p : m_t) { - if (p.column().index() >= lra.A_r().column_count()) + if (p.j() >= lra.A_r().column_count()) return false; } return true; @@ -248,6 +242,7 @@ namespace lp { CTRACE("current_solution_is_inf_on_cut", v * sign <= impq(m_k) * sign, tout << "m_upper = " << m_upper << std::endl; tout << "v = " << v << ", k = " << m_k << std::endl; + tout << "term:";lra.print_term(m_t, tout) << "\n"; ); return v * sign > impq(m_k) * sign; } @@ -276,7 +271,7 @@ namespace lp { return lra.settings(); } - bool int_solver::column_is_int(column_index const& j) const { + bool int_solver::column_is_int(lpvar j) const { return lra.column_is_int(j); } @@ -301,7 +296,7 @@ namespace lp { } bool int_solver::is_term(unsigned j) const { - return lra.column_corresponds_to_term(j); + return lra.column_has_term(j); } unsigned int_solver::column_count() const { @@ -675,11 +670,11 @@ namespace lp { if (abs(value.x) < small_value || (has_upper(j) && small_value > upper_bound(j).x - value.x) || (has_lower(j) && small_value > value.x - lower_bound(j).x)) { - TRACE("gomory_cut", tout << "small j" << j << "\n"); + TRACE("int_solver", tout << "small j" << j << "\n"); add_column(true, r_small_value, n_small_value, j); continue; } - TRACE("gomory_cut", tout << "any j" << j << "\n"); + TRACE("int_solver", tout << "any j" << j << "\n"); add_column(usage >= prev_usage, r_any_value, n_any_value, j); if (usage > prev_usage) prev_usage = usage; @@ -697,7 +692,6 @@ namespace lp { } void int_solver::simplify(std::function& is_root) { - return; #if 0 @@ -834,165 +828,7 @@ namespace lp { #endif } - // return the minimal distance from the column value to an integer - mpq get_gomory_score(const int_solver& lia, lpvar j) { - const mpq& val = lia.get_value(j).x; - auto l = val - floor(val); - if (l <= mpq(1, 2)) - return l; - return mpq(1) - l; - } - - unsigned_vector int_solver::gomory_select_int_infeasible_vars(unsigned num_cuts) { - SASSERT(m_cut_vars.size() == 0&& num_cuts >= 0); - - std::list sorted_vars; - std::unordered_map score; - for (lpvar j : lra.r_basis()) { - if (!column_is_int_inf(j) || !is_gomory_cut_target(j)) - continue; - SASSERT(!is_fixed(j)); - sorted_vars.push_back(j); - score[j] = get_gomory_score(*this, j); - } - // prefer the columns with the values close to integers - sorted_vars.sort([&](lpvar j, lpvar k) { - auto diff = score[j] - score[k]; - if (diff.is_neg()) - return true; - if (diff.is_pos()) - return false; - return lra.usage_in_terms(j) > lra.usage_in_terms(k); - }); - unsigned_vector ret; - unsigned n = static_cast(sorted_vars.size()); - - while (num_cuts-- && n > 0) { - unsigned k = random() % n; - - double k_ratio = k / (double) n; - k_ratio *= k_ratio*k_ratio; // square k_ratio to make it smaller - k = static_cast(std::floor(k_ratio * n)); - // these operations move k to the beginning of the indices range - SASSERT(0 <= k && k < n); - auto it = sorted_vars.begin(); - while(k--) it++; - - ret.push_back(*it); - sorted_vars.erase(it); - n--; - } - return ret; - } - lia_move int_solver::local_cut(unsigned num_cuts, std::function& cut_fn) { - - struct ex { explanation m_ex; lar_term m_term; mpq m_k; bool m_is_upper; }; - unsigned_vector columns_for_cuts = gomory_select_int_infeasible_vars(num_cuts); - - vector cuts; - - for (unsigned j : columns_for_cuts) { - m_ex->clear(); - m_t.clear(); - m_k.reset(); - auto r = cut_fn(j); - if (r != lia_move::cut) - continue; - cuts.push_back({ *m_ex, m_t, m_k, is_upper() }); - if (settings().get_cancel_flag()) - return lia_move::undef; - } - m_cut_vars.reset(); - - auto is_small_cut = [&](ex const& cut) { - return all_of(cut.m_term, [&](auto ci) { return ci.coeff().is_small(); }); - }; - - auto add_cut = [&](ex const& cut) { - u_dependency* dep = nullptr; - for (auto c : cut.m_ex) - dep = lra.join_deps(lra.dep_manager().mk_leaf(c.ci()), dep); - lp::lpvar term_index = lra.add_term(cut.m_term.coeffs_as_vector(), UINT_MAX); - term_index = lra.map_term_index_to_column_index(term_index); - lra.update_column_type_and_bound(term_index, - cut.m_is_upper ? lp::lconstraint_kind::LE : lp::lconstraint_kind::GE, - cut.m_k, dep); - }; - - auto _check_feasible = [&](void) { - lra.find_feasible_solution(); - if (!lra.is_feasible() && !settings().get_cancel_flag()) { - lra.get_infeasibility_explanation(*m_ex); - return false; - } - return true; - }; - - bool has_small = false, has_large = false; - - for (auto const& cut : cuts) { - if (!is_small_cut(cut)) { - has_large = true; - continue; - } - has_small = true; - add_cut(cut); - } - - if (has_large) { - lra.push(); - - for (auto const& cut : cuts) - if (!is_small_cut(cut)) - add_cut(cut); - - bool feas = _check_feasible(); - lra.pop(1); - - if (!feas) { - for (auto const& cut : cuts) - if (!is_small_cut(cut)) - add_cut(cut); - } - } - - if (settings().get_cancel_flag()) - return lia_move::undef; - - if (!_check_feasible()) - return lia_move::conflict; - - - m_ex->clear(); - m_t.clear(); - m_k.reset(); - if (!has_inf_int()) - return lia_move::sat; - - if (has_small || has_large) - return lia_move::continue_with_check; - - lra.move_non_basic_columns_to_bounds(); - return lia_move::undef; - } - - bool int_solver::is_gomory_cut_target(lpvar k) { - SASSERT(is_base(k)); - // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed). - const row_strip& row = lra.get_row(row_of_basic_column(k)); - unsigned j; - for (const auto & p : row) { - j = p.var(); - if ( k != j && (!at_bound(j) || !is_zero(get_value(j).y))) { - TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; - display_column(tout, j); - tout << "infinitesimal: " << !is_zero(get_value(j).y) << "\n";); - return false; - } - } - return true; - } - - + + } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index feeb96986..524f8fb28 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -33,7 +33,7 @@ class lar_solver; class lar_core_solver; class int_solver { - friend class create_cut; + friend struct create_cut; friend class gomory; friend class int_cube; friend class int_branch; @@ -83,7 +83,7 @@ public: bool is_real(unsigned j) const; const impq & lower_bound(unsigned j) const; const impq & upper_bound(unsigned j) const; - bool column_is_int(column_index const& j) const; + bool column_is_int(lpvar j) const; const impq & get_value(unsigned j) const; bool at_lower(unsigned j) const; bool at_upper(unsigned j) const; @@ -94,7 +94,6 @@ private: // lia_move patch_nbasic_columns(); bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m); bool is_boxed(unsigned j) const; - bool is_fixed(unsigned j) const; bool is_free(unsigned j) const; bool value_is_int(unsigned j) const; bool is_feasible() const; @@ -111,9 +110,9 @@ private: bool has_upper(unsigned j) const; unsigned row_of_basic_column(unsigned j) const; bool cut_indices_are_columns() const; - lia_move local_cut(unsigned num_cuts, std::function& cut_fn); public: + bool is_fixed(unsigned j) const; std::ostream& display_column(std::ostream & out, unsigned j) const; u_dependency* column_upper_bound_constraint(unsigned j) const; u_dependency* column_lower_bound_constraint(unsigned j) const; @@ -129,11 +128,9 @@ private: public: bool is_term(unsigned j) const; unsigned column_count() const; - bool all_columns_are_bounded() const; lia_move hnf_cut(); int select_int_infeasible_var(); - unsigned_vector gomory_select_int_infeasible_vars(unsigned num_cuts); - bool is_gomory_cut_target(lpvar); + }; } diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index f0c937324..b9069625f 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -16,7 +16,7 @@ Author: #include "util/region.h" #include "util/stacked_value.h" #include "math/lp/lp_utils.h" -#include "math/lp/ul_pair.h" +#include "math/lp/column.h" #include "math/lp/lar_term.h" #include "math/lp/column_namer.h" namespace lp { @@ -46,7 +46,7 @@ class lar_base_constraint { public: - virtual vector> coeffs() const = 0; + virtual vector> coeffs() const = 0; lar_base_constraint(unsigned j, lconstraint_kind kind, u_dependency* dep, const mpq& right_side) : m_kind(kind), m_right_side(right_side), m_active(false), m_j(j), m_dep(dep) {} virtual ~lar_base_constraint() = default; @@ -69,8 +69,8 @@ public: lar_var_constraint(unsigned j, lconstraint_kind kind, u_dependency* dep, const mpq& right_side) : lar_base_constraint(j, kind, dep, right_side) {} - vector> coeffs() const override { - vector> ret; + vector> coeffs() const override { + vector> ret; ret.push_back(std::make_pair(one_of_type(), column())); return ret; } @@ -84,7 +84,7 @@ public: lar_term_constraint(unsigned j, const lar_term* t, lconstraint_kind kind, u_dependency* dep, const mpq& right_side) : lar_base_constraint(j, kind, dep, right_side), m_term(t) {} - vector> coeffs() const override { return m_term->coeffs_as_vector(); } + vector> coeffs() const override { return m_term->coeffs_as_vector(); } unsigned size() const override { return m_term->size();} }; @@ -168,7 +168,7 @@ public: m_region.pop_scope(k); } - constraint_index add_var_constraint(var_index j, lconstraint_kind k, mpq const& rhs) { + constraint_index add_var_constraint(lpvar j, lconstraint_kind k, mpq const& rhs) { return add(new (m_region) lar_var_constraint(j, k, mk_dep(), rhs)); } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index a55edfe35..ef61c2209 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -23,8 +23,7 @@ namespace lp { lar_solver::lar_solver() : m_mpq_lar_core_solver(m_settings, *this), - m_var_register(false), - m_term_register(true), + m_var_register(), m_constraints(m_dependencies, *this) {} // start or ends tracking the rows that were changed by solve() @@ -52,9 +51,9 @@ namespace lp { std::ostream& lar_solver::print_implied_bound(const implied_bound& be, std::ostream& out) const { out << "implied bound\n"; unsigned v = be.m_j; - if (tv::is_term(v)) { - out << "it is a term number " << tv::unmask_term(be.m_j) << std::endl; - print_term(*m_terms[tv::unmask_term(v)], out); + if (column_has_term(v)) { + out << "term for column " << v << std::endl; + print_term(*m_columns[v].term(), out); } else { out << get_variable_name(v); @@ -95,7 +94,7 @@ namespace lp { if (strict) kind = static_cast((static_cast(kind) / 2)); - if (!tv::is_term(be.m_j)) { + if (!column_has_term(be.m_j)) { if (coeff_map.size() != 1) return false; auto it = coeff_map.find(be.m_j); @@ -109,13 +108,13 @@ namespace lp { else { lar_term const& t = get_term(be.m_j); auto first_coeff = t.begin(); - unsigned j = (*first_coeff).column(); + unsigned j = (*first_coeff).j(); auto it = coeff_map.find(j); if (it == coeff_map.end()) return false; mpq ratio = it->second / (*first_coeff).coeff(); for (auto p : t) { - it = coeff_map.find(p.column()); + it = coeff_map.find(p.j()); if (it == coeff_map.end()) return false; if (p.coeff() * ratio != it->second) @@ -139,43 +138,6 @@ namespace lp { return false; } - void lar_solver::substitute_basis_var_in_terms_for_row(unsigned i) { - // todo : create a map from term basic vars to the rows where they are used - unsigned basis_j = m_mpq_lar_core_solver.m_r_solver.m_basis[i]; - for (unsigned k = 0; k < m_terms.size(); k++) { - if (term_is_used_as_row(k)) - continue; - if (!m_terms[k]->contains(basis_j)) - continue; - m_terms[k]->subst_in_row(basis_j, m_mpq_lar_core_solver.m_r_solver.m_pivot_row); - } - } - - // Returns the column index without changes, - // but in the case the column was created as - // the slack variable to a term return the term index. - // It is the same index that was returned by add_var(), or - // by add_term() - unsigned lar_solver::column_to_reported_index(unsigned j) const { - if (tv::is_term(j)) - return j; - unsigned ext_var_or_term = m_var_register.local_to_external(j); - if (tv::is_term(ext_var_or_term)) - j = ext_var_or_term; - return j; - } - - unsigned lar_solver::map_term_index_to_column_index(unsigned j) const { - SASSERT(tv::is_term(j)); - return m_var_register.external_to_local(j); - } - - // here i is just the term index - bool lar_solver::term_is_used_as_row(unsigned i) const { - SASSERT(i < m_terms.size()); - return m_var_register.external_is_used(tv::mask_term(i)); - } - lp_status lar_solver::get_status() const { return m_status; } void lar_solver::set_status(lp_status s) { @@ -189,8 +151,6 @@ namespace lp { stats().m_max_cols = A_r().column_count(); if (A_r().row_count() > stats().m_max_rows) stats().m_max_rows = A_r().row_count(); - if (strategy_is_undecided()) - decide_on_strategy_and_adjust_initial_state(); flet f(settings().simplex_strategy(), simplex_strategy_enum::tableau_rows); m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; auto ret = solve(); @@ -257,7 +217,7 @@ namespace lp { m_crossed_bounds_column = null_lpvar; m_crossed_bounds_deps = nullptr; m_trail.pop_scope(k); - unsigned n = m_columns_to_ul_pairs.size(); + unsigned n = m_columns.size(); m_var_register.shrink(n); lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); @@ -276,9 +236,7 @@ namespace lp { unsigned m = A_r().row_count(); clean_popped_elements(m, m_touched_rows); clean_inf_heap_of_r_solver_after_pop(); - lp_assert( - m_settings.simplex_strategy() == simplex_strategy_enum::undecided || - m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + SASSERT(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_constraints.pop(k); m_simplex_strategy.pop(k); @@ -296,9 +254,6 @@ namespace lp { bool lar_solver::maximize_term_on_tableau(const lar_term& term, impq& term_max) { flet f(m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only, false); - if (settings().simplex_strategy() == simplex_strategy_enum::undecided) - decide_on_strategy_and_adjust_initial_state(); - m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::FEASIBLE); m_mpq_lar_core_solver.solve(); lp_status st = m_mpq_lar_core_solver.m_r_solver.get_status(); @@ -325,7 +280,7 @@ namespace lp { TRACE("lar_solver_improve_bounds", tout << "d[" << j << "] = " << d_j << "\n"; this->m_mpq_lar_core_solver.m_r_solver.print_column_info(j, tout);); - const ul_pair& ul = m_columns_to_ul_pairs[j]; + const column& ul = m_columns[j]; u_dependency * bound_dep; if (d_j.is_pos()) bound_dep = ul.upper_bound_witness(); @@ -411,7 +366,7 @@ namespace lp { auto& d = rslv.m_d; auto& costs = rslv.m_costs; for (lar_term::ival p : term) { - unsigned j = p.column(); + unsigned j = p.j(); costs[j] = zero_of_type(); int i = rslv.m_basis_heading[j]; if (i < 0) @@ -433,7 +388,7 @@ namespace lp { move_non_basic_columns_to_bounds(); rslv.m_costs.resize(A_r().column_count(), zero_of_type()); for (lar_term::ival p : term) { - unsigned j = p.column(); + unsigned j = p.j(); rslv.m_costs[j] = p.coeff(); if (rslv.m_basis_heading[j] < 0) rslv.m_d[j] += p.coeff(); @@ -540,28 +495,28 @@ namespace lp { lp_assert(is_base(j)); unsigned i = row_of_basic_column(j); for (const auto & c : A_r().m_rows[i]) - if (j != c.var() && !is_fixed(c.var())) + if (j != c.var() && !column_is_fixed(c.var())) return m_mpq_lar_core_solver.m_r_solver.remove_from_basis_core(c.var(), j); return false; } - lar_term lar_solver::get_term_to_maximize(unsigned j_or_term) const { - if (tv::is_term(j_or_term)) { - return get_term(j_or_term); + lar_term lar_solver::get_term_to_maximize(unsigned j) const { + if (column_has_term(j)) { + return * m_columns[j].term(); } - if (j_or_term < m_mpq_lar_core_solver.m_r_x.size()) { + if (j < m_mpq_lar_core_solver.m_r_x.size()) { lar_term r; - r.add_monomial(one_of_type(), j_or_term); + r.add_monomial(one_of_type(), j); return r; } return lar_term(); // return an empty term } - lp_status lar_solver::maximize_term(unsigned j_or_term, + lp_status lar_solver::maximize_term(unsigned j, impq& term_max) { TRACE("lar_solver", print_values(tout);); SASSERT(m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis()); - lar_term term = get_term_to_maximize(j_or_term); + lar_term term = get_term_to_maximize(j); if (term.is_empty()) return lp_status::UNBOUNDED; impq prev_value = term.apply(m_mpq_lar_core_solver.m_r_x); auto backup = m_mpq_lar_core_solver.m_r_x; @@ -607,13 +562,6 @@ namespace lp { return lp_status::FEASIBLE; } - - - const lar_term& lar_solver::get_term(unsigned j) const { - lp_assert(tv::is_term(j)); - return *m_terms[tv::unmask_term(j)]; - } - void lar_solver::pop_core_solver_params() { pop_core_solver_params(1); } @@ -624,17 +572,17 @@ namespace lp { - void lar_solver::set_upper_bound_witness(var_index j, u_dependency* dep) { - m_trail.push(vector_value_trail(m_columns_to_ul_pairs, j)); - m_columns_to_ul_pairs[j].upper_bound_witness() = dep; + void lar_solver::set_upper_bound_witness(lpvar j, u_dependency* dep) { + m_trail.push(vector_value_trail(m_columns, j)); + m_columns[j].upper_bound_witness() = dep; } - void lar_solver::set_lower_bound_witness(var_index j, u_dependency* dep) { - m_trail.push(vector_value_trail(m_columns_to_ul_pairs, j)); - m_columns_to_ul_pairs[j].lower_bound_witness() = dep; + void lar_solver::set_lower_bound_witness(lpvar j, u_dependency* dep) { + m_trail.push(vector_value_trail(m_columns, j)); + m_columns[j].lower_bound_witness() = dep; } - void lar_solver::register_monoid_in_map(std::unordered_map& coeffs, const mpq& a, unsigned j) { + void lar_solver::register_monoid_in_map(std::unordered_map& coeffs, const mpq& a, unsigned j) { auto it = coeffs.find(j); if (it == coeffs.end()) coeffs[j] = a; @@ -643,19 +591,19 @@ namespace lp { } - void lar_solver::substitute_terms_in_linear_expression(const vector>& left_side_with_terms, - vector>& left_side) const { - std::unordered_map coeffs; + void lar_solver::substitute_terms_in_linear_expression(const vector>& left_side_with_terms, + vector>& left_side) const { + std::unordered_map coeffs; for (auto& t : left_side_with_terms) { unsigned j = t.second; - if (!tv::is_term(j)) { + if (!column_has_term(j)) { register_monoid_in_map(coeffs, t.first, j); } else { - const lar_term& term = *m_terms[tv::unmask_term(t.second)]; + const lar_term& term = *m_columns[t.second].term(); for (auto p : term) - register_monoid_in_map(coeffs, t.first * p.coeff(), p.column()); + register_monoid_in_map(coeffs, t.first * p.coeff(), p.j()); } } @@ -675,16 +623,16 @@ namespace lp { unsigned num = A_r().column_count(); unsigned_vector to_remove; for (unsigned j : m_fixed_base_var_set) { - if (j >= num || !is_base(j) || !is_fixed(j)) { + if (j >= num || !is_base(j) || !column_is_fixed(j)) { to_remove.push_back(j); continue; } - lp_assert(is_base(j) && is_fixed(j)); + lp_assert(is_base(j) && column_is_fixed(j)); auto const& r = basic2row(j); for (auto const& c : r) { unsigned j_entering = c.var(); - if (!is_fixed(j_entering)) { + if (!column_is_fixed(j_entering)) { pivot(j_entering, j); to_remove.push_back(j); lp_assert(is_base(j_entering)); @@ -839,15 +787,12 @@ namespace lp { return r; } - bool lar_solver::var_is_registered(var_index vj) const { - if (tv::is_term(vj)) { - return tv::unmask_term(vj) < m_terms.size(); - } - return vj < A_r().column_count(); + bool lar_solver::var_is_registered(lpvar vj) const { + return vj < A_r().column_count(); } - bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { + bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { for (auto it : left_side) { if (!var_is_registered(it.second)) return false; @@ -858,7 +803,7 @@ namespace lp { bool lar_solver::all_constraints_hold() const { if (m_settings.get_cancel_flag()) return true; - std::unordered_map var_map; + std::unordered_map var_map; get_model_do_not_care_about_diff_vars(var_map); for (auto const& c : m_constraints.active()) { @@ -874,7 +819,7 @@ namespace lp { return true; } - bool lar_solver::constraint_holds(const lar_base_constraint& constr, std::unordered_map& var_map) const { + bool lar_solver::constraint_holds(const lar_base_constraint& constr, std::unordered_map& var_map) const { mpq left_side_val = get_left_side_val(constr, var_map); switch (constr.kind()) { case LE: return left_side_val <= constr.rhs(); @@ -889,7 +834,7 @@ namespace lp { } - void lar_solver::register_in_map(std::unordered_map& coeffs, const lar_base_constraint& cn, const mpq& a) { + void lar_solver::register_in_map(std::unordered_map& coeffs, const lar_base_constraint& cn, const mpq& a) { for (auto& it : cn.coeffs()) { unsigned j = it.second; auto p = coeffs.find(j); @@ -904,7 +849,7 @@ namespace lp { } bool lar_solver::the_left_sides_sum_to_zero(const vector>& evidence) const { - std::unordered_map coeff_map; + std::unordered_map coeff_map; for (auto const & [coeff, con_ind] : evidence) { lp_assert(m_constraints.valid_index(con_ind)); register_in_map(coeff_map, m_constraints[con_ind], coeff); @@ -966,13 +911,13 @@ namespace lp { return ret; } - bool lar_solver::has_lower_bound(var_index var, u_dependency*& ci, mpq& value, bool& is_strict) const { + bool lar_solver::has_lower_bound(lpvar var, u_dependency*& ci, mpq& value, bool& is_strict) const { - if (var >= m_columns_to_ul_pairs.size()) { + if (var >= m_columns.size()) { // TBD: bounds on terms could also be used, caller may have to track these. return false; } - const ul_pair& ul = m_columns_to_ul_pairs[var]; + const column& ul = m_columns[var]; ci = ul.lower_bound_witness(); if (ci != nullptr) { auto& p = m_mpq_lar_core_solver.m_r_lower_bounds()[var]; @@ -985,13 +930,13 @@ namespace lp { } } - bool lar_solver::has_upper_bound(var_index var, u_dependency*& ci, mpq& value, bool& is_strict) const { + bool lar_solver::has_upper_bound(lpvar var, u_dependency*& ci, mpq& value, bool& is_strict) const { - if (var >= m_columns_to_ul_pairs.size()) { + if (var >= m_columns.size()) { // TBD: bounds on terms could also be used, caller may have to track these. return false; } - const ul_pair& ul = m_columns_to_ul_pairs[var]; + const column& ul = m_columns[var]; ci = ul.upper_bound_witness(); if (ci != nullptr) { auto& p = m_mpq_lar_core_solver.m_r_upper_bounds()[var]; @@ -1004,12 +949,12 @@ namespace lp { } } - bool lar_solver::has_value(var_index var, mpq& value) const { - if (tv::is_term(var)) { + bool lar_solver::has_value(lpvar var, mpq& value) const { + if (column_has_term(var)) { lar_term const& t = get_term(var); value = 0; for (lar_term::ival cv : t) { - impq const& r = get_column_value(cv.column()); + impq const& r = get_column_value(cv.j()); if (!numeric_traits::is_zero(r.y)) return false; value += r.x * cv.coeff(); } @@ -1049,7 +994,7 @@ namespace lp { unsigned j = it.second; int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign; - const ul_pair& ul = m_columns_to_ul_pairs[j]; + const column& ul = m_columns[j]; u_dependency* bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); svector deps; @@ -1062,7 +1007,7 @@ namespace lp { } // (x, y) != (x', y') => (x + delta*y) != (x' + delta*y') - void lar_solver::get_model(std::unordered_map& variable_values) const { + void lar_solver::get_model(std::unordered_map& variable_values) const { variable_values.clear(); if (!init_model()) return; @@ -1070,7 +1015,7 @@ namespace lp { unsigned n = m_mpq_lar_core_solver.m_r_x.size(); for (unsigned j = 0; j < n; j++) - variable_values[j] = get_value(column_index(j)); + variable_values[j] = get_value(j); TRACE("lar_solver_model", tout << "delta = " << m_delta << "\nmodel:\n"; for (auto p : variable_values) tout << this->get_variable_name(p.first) << " = " << p.second << "\n";); @@ -1108,7 +1053,7 @@ namespace lp { return true; } - void lar_solver::get_model_do_not_care_about_diff_vars(std::unordered_map& variable_values) const { + void lar_solver::get_model_do_not_care_about_diff_vars(std::unordered_map& variable_values) const { mpq delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(mpq(1)); for (unsigned i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++) { const impq& rp = m_mpq_lar_core_solver.m_r_x[i]; @@ -1116,32 +1061,13 @@ namespace lp { } } - mpq lar_solver::get_value(column_index const& j) const { + mpq lar_solver::get_value(lpvar j) const { SASSERT(get_status() == lp_status::OPTIMAL || get_status() == lp_status::FEASIBLE); VERIFY(m_columns_with_changed_bounds.empty()); numeric_pair const& rp = get_column_value(j); return from_model_in_impq_to_mpq(rp); } - mpq lar_solver::get_tv_value(tv const& t) const { - if (t.is_var()) - return get_value(t.column()); -#if 0 - unsigned term_j = 0; - if (m_var_register.term_is_used(t.id(), term_j)) - return get_value(column_index(term_j)); -#endif - mpq r(0); - for (lar_term::ival p : get_term(t)) - r += p.coeff() * get_value(p.column()); - return r; - } - //fetches the cached value of the term or the variable by the given index - const impq& lar_solver::get_tv_ivalue(tv const& t) const { - unsigned j = t.is_var()? (unsigned)t.column(): this->map_term_index_to_column_index(t.index()); - return this->get_column_value(j); - } - void lar_solver::get_rid_of_inf_eps() { bool y_is_zero = true; for (unsigned j = 0; j < number_of_vars(); j++) { @@ -1162,13 +1088,13 @@ namespace lp { } } - void lar_solver::set_variable_name(var_index vi, std::string name) { + void lar_solver::set_variable_name(lpvar vi, std::string name) { m_var_register.set_name(vi, name); } - std::string lar_solver::get_variable_name(var_index j) const { - if (tv::is_term(j)) - return std::string("_t") + T_to_string(tv::unmask_term(j)); + std::string lar_solver::get_variable_name(lpvar j) const { + if (column_has_term(j)) + return std::string("_t") + T_to_string(j); if (j >= m_var_register.size()) return std::string("_s") + T_to_string(j); @@ -1180,7 +1106,7 @@ namespace lp { return std::string("j") + T_to_string(m_var_register.local_to_external(j)); } else { - std::string s = column_corresponds_to_term(j) ? "t" : "j"; + std::string s = column_has_term(j) ? "t" : "j"; return s + T_to_string(j); } } @@ -1225,7 +1151,7 @@ namespace lp { out << " - "; else if (val != numeric_traits::one()) out << T_to_string(val); - out << this->get_variable_name(p.column()); + out << this->get_variable_name(p.j()); } return out; } @@ -1235,10 +1161,10 @@ namespace lp { return out; } - mpq lar_solver::get_left_side_val(const lar_base_constraint& cns, const std::unordered_map& var_map) const { + mpq lar_solver::get_left_side_val(const lar_base_constraint& cns, const std::unordered_map& var_map) const { mpq ret = cns.get_free_coeff_of_left_side(); for (auto& it : cns.coeffs()) { - var_index j = it.second; + lpvar j = it.second; auto vi = var_map.find(j); lp_assert(vi != var_map.end()); ret += it.first * vi->second; @@ -1247,13 +1173,13 @@ namespace lp { } - void lar_solver::fill_var_set_for_random_update(unsigned sz, var_index const* vars, vector& column_list) { + void lar_solver::fill_var_set_for_random_update(unsigned sz, lpvar const* vars, vector& column_list) { TRACE("lar_solver_rand", tout << "sz = " << sz << "\n";); for (unsigned i = 0; i < sz; i++) { - var_index var = vars[i]; - if (tv::is_term(var)) { - if (term_is_used_as_row(tv::unmask_term(var))) { - column_list.push_back(map_term_index_to_column_index(var)); + lpvar var = vars[i]; + if (column_has_term(var)) { + if (m_columns[var].associated_with_row()) { + column_list.push_back(var); } } else { @@ -1262,7 +1188,7 @@ namespace lp { } } - void lar_solver::random_update(unsigned sz, var_index const* vars) { + void lar_solver::random_update(unsigned sz, lpvar const* vars) { vector column_list; fill_var_set_for_random_update(sz, vars, column_list); random_updater ru(*this, column_list); @@ -1280,7 +1206,7 @@ namespace lp { } bool lar_solver::column_represents_row_in_tableau(unsigned j) { - return m_columns_to_ul_pairs[j].associated_with_row(); + return m_columns[j].associated_with_row(); } void lar_solver::make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j) { @@ -1430,7 +1356,7 @@ namespace lp { bool lar_solver::term_is_int(const lar_term* t) const { for (auto const p : *t) - if (!(column_is_int(p.column()) && p.coeff().is_int())) + if (!(column_is_int(p.j()) && p.coeff().is_int())) return false; return true; } @@ -1442,14 +1368,9 @@ namespace lp { return true; } - bool lar_solver::var_is_int(var_index v) const { - if (tv::is_term(v)) { - lar_term const& t = get_term(v); - return term_is_int(&t); - } - else { - return column_is_int(v); - } + bool lar_solver::var_is_int(lpvar v) const { + SASSERT(!column_has_term(v) || term_is_int(&get_term(v)) == column_is_int(v)); + return column_is_int(v); } bool lar_solver::column_is_int(unsigned j) const { @@ -1466,34 +1387,26 @@ namespace lp { // below is the initialization functionality of lar_solver - bool lar_solver::strategy_is_undecided() const { - return m_settings.simplex_strategy() == simplex_strategy_enum::undecided; - } - - var_index lar_solver::add_named_var(unsigned ext_j, bool is_int, const std::string& name) { - var_index j = add_var(ext_j, is_int); + lpvar lar_solver::add_named_var(unsigned ext_j, bool is_int, const std::string& name) { + lpvar j = add_var(ext_j, is_int); m_var_register.set_name(j, name); return j; } - unsigned lar_solver::external_to_column_index(unsigned ext_j) const { - unsigned j = external_to_local(ext_j); - if (j == null_lpvar) - return j; - - if (tv::is_term(j)) - return map_term_index_to_column_index(j); - - return j; - } - struct lar_solver::undo_add_column : public trail { lar_solver& s; undo_add_column(lar_solver& s) : s(s) {} void undo() override { + auto& col = s.m_columns.back(); + if (col.term() != nullptr) { + if (s.m_need_register_terms) + s.deregister_normalized_term(*col.term()); + delete col.term(); + s.m_terms.pop_back(); + } s.remove_last_column_from_tableau(); - s.m_columns_to_ul_pairs.pop_back(); - unsigned j = s.m_columns_to_ul_pairs.size(); + s.m_columns.pop_back(); + unsigned j = s.m_columns.size(); if (s.m_columns_with_changed_bounds.contains(j)) s.m_columns_with_changed_bounds.remove(j); if (s.m_incorrect_columns.contains(j)) @@ -1501,29 +1414,14 @@ namespace lp { } }; - struct lar_solver::undo_add_term : public trail { - lar_solver& s; - undo_add_term(lar_solver& s):s(s) {} - void undo() override { - auto* t = s.m_terms.back(); - if (s.m_need_register_terms) - s.deregister_normalized_term(*t); - delete t; - s.m_terms.pop_back(); - s.m_term_register.shrink(s.m_terms.size()); - } - }; - - var_index lar_solver::add_var(unsigned ext_j, bool is_int) { + lpvar lar_solver::add_var(unsigned ext_j, bool is_int) { TRACE("add_var", tout << "adding var " << ext_j << (is_int ? " int" : " nonint") << std::endl;); - var_index local_j; - SASSERT(!m_term_register.external_is_used(ext_j)); - lp_assert(!tv::is_term(ext_j)); + lpvar local_j; if (m_var_register.external_is_used(ext_j, local_j)) return local_j; - lp_assert(m_columns_to_ul_pairs.size() == A_r().column_count()); + lp_assert(m_columns.size() == A_r().column_count()); local_j = A_r().column_count(); - m_columns_to_ul_pairs.push_back(ul_pair(false)); // not associated with a row + m_columns.push_back(column(false, nullptr)); // false - not associated with a row, nullptr for term m_trail.push(undo_add_column(*this)); while (m_usage_in_terms.size() <= ext_j) m_usage_in_terms.push_back(0); @@ -1536,17 +1434,17 @@ namespace lp { return m_var_register.has_int_var(); } - void lar_solver::register_new_ext_var_index(unsigned ext_v, bool is_int) { + void lar_solver::register_new_external_var(unsigned ext_v, bool is_int) { lp_assert(!m_var_register.external_is_used(ext_v)); m_var_register.add_var(ext_v, is_int); } bool lar_solver::external_is_used(unsigned v) const { - return m_var_register.external_is_used(v) || m_term_register.external_is_used(v); + return m_var_register.external_is_used(v); } void lar_solver::add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int) { - register_new_ext_var_index(ext_j, is_int); + register_new_external_var(ext_j, is_int); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); add_new_var_to_core_fields_for_mpq(false); // false for not adding a row } @@ -1577,14 +1475,8 @@ namespace lp { } } - - var_index lar_solver::add_term_undecided(const vector>& coeffs) { - push_term(new lar_term(coeffs)); - return tv::mask_term(m_terms.size() - 1); - } - #if Z3DEBUG_CHECK_UNIQUE_TERMS - bool lar_solver::term_coeffs_are_ok(const vector>& coeffs) { + bool lar_solver::term_coeffs_are_ok(const vector>& coeffs) { for (const auto& p : coeffs) if (column_is_real(p.second)) @@ -1609,50 +1501,40 @@ namespace lp { } #endif - void lar_solver::push_term(lar_term* t) { - m_terms.push_back(t); - m_trail.push(undo_add_term(*this)); - } - + // terms - bool lar_solver::all_vars_are_registered(const vector>& coeffs) { + bool lar_solver::all_vars_are_registered(const vector>& coeffs) { return all_of(coeffs, [&](const auto& p) { return p.second < m_var_register.size(); }); } void lar_solver::subst_known_terms(lar_term* t) { std::set seen_terms; for (auto p : *t) { - auto j = p.column(); - if (this->column_corresponds_to_term(j)) + auto j = p.j(); + if (this->column_has_term(j)) seen_terms.insert(j); } while (!seen_terms.empty()) { unsigned j = *seen_terms.begin(); seen_terms.erase(j); - auto tj = this->m_var_register.local_to_external(j); - auto& ot = this->get_term(tj); + const lar_term& ot = this->get_term(j); for (auto p : ot) - if (this->column_corresponds_to_term(p.column())) - seen_terms.insert(p.column()); + if (this->column_has_term(p.j())) + seen_terms.insert(p.j()); t->subst_by_term(ot, j); } } - // do not register in m_var_register this term if ext_i == UINT_MAX - var_index lar_solver::add_term(const vector>& coeffs, unsigned ext_i) { - TRACE("lar_solver_terms", print_linear_combination_of_column_indices_only(coeffs, tout) << ", ext_i =" << ext_i << "\n";); + // if UINT_MAX == null_lpvar then the term does not correspond and external variable + lpvar lar_solver::add_term(const vector>& coeffs, unsigned ext_i) { + TRACE("lar_solver_terms", print_linear_combination_of_column_indices_only(coeffs, tout) << ", ext_i =" << ext_i << "\n";); SASSERT(!m_var_register.external_is_used(ext_i)); - m_term_register.add_var(ext_i, term_is_int(coeffs)); - lp_assert(all_vars_are_registered(coeffs)); - if (strategy_is_undecided()) - return add_term_undecided(coeffs); + SASSERT(all_vars_are_registered(coeffs)); lar_term* t = new lar_term(coeffs); subst_known_terms(t); - push_term(t); - SASSERT(m_terms.size() == m_term_register.size()); - unsigned adjusted_term_index = m_terms.size() - 1; - var_index ret = tv::mask_term(adjusted_term_index); - if (!coeffs.empty()) - add_row_from_term_no_constraint(m_terms.back(), ret); + SASSERT(t->is_empty() == false); + m_terms.push_back(t); + lpvar ret = A_r().column_count(); + add_row_from_term_no_constraint(t, ext_i); lp_assert(m_var_register.size() == A_r().column_count()); if (m_need_register_terms) @@ -1660,26 +1542,15 @@ namespace lp { return ret; } - /** - * \brief ensure there is a column index corresponding to vi - * If vi is already a column, just return vi - * If vi is for a term, then create a row that uses the term. - */ - var_index lar_solver::ensure_column(var_index vi) { - if (lp::tv::is_term(vi)) - return to_column(vi); - else - return vi; - } - - - void lar_solver::add_row_from_term_no_constraint(const lar_term* term, unsigned term_ext_index) { + void lar_solver::add_row_from_term_no_constraint(lar_term* term, unsigned ext_index) { TRACE("dump_terms", print_term(*term, tout) << std::endl;); - register_new_ext_var_index(term_ext_index, term_is_int(term)); + register_new_external_var(ext_index, term_is_int(term)); // j will be a new variable unsigned j = A_r().column_count(); - ul_pair ul(true); // to mark this column as associated_with_row - m_columns_to_ul_pairs.push_back(ul); + SASSERT(ext_index == null_lpvar || external_to_local(ext_index) == j); + column ul(true, term); // true - to mark this column as associated_with_row + term->j() = j; // point from the term to the column + m_columns.push_back(ul); m_trail.push(undo_add_column(*this)); add_basic_var_to_core_fields(); @@ -1690,7 +1561,7 @@ namespace lp { m_mpq_lar_core_solver.m_r_solver.update_x(j, get_basic_var_value_from_row(A_r().row_count() - 1)); for (lar_term::ival c : *term) { - unsigned j = c.column(); + unsigned j = c.j(); while (m_usage_in_terms.size() <= j) m_usage_in_terms.push_back(0); m_usage_in_terms[j] = m_usage_in_terms[j] + 1; @@ -1706,13 +1577,13 @@ namespace lp { return !column_is_int(j) || right_side.is_int(); } - constraint_index lar_solver::add_var_bound_check_on_equal(var_index j, lconstraint_kind kind, const mpq& right_side, var_index& equal_var) { + constraint_index lar_solver::add_var_bound_check_on_equal(lpvar j, lconstraint_kind kind, const mpq& right_side, lpvar& equal_var) { constraint_index ci = mk_var_bound(j, kind, right_side); activate_check_on_equal(ci, equal_var); return ci; } - constraint_index lar_solver::add_var_bound(var_index j, lconstraint_kind kind, const mpq& right_side) { + constraint_index lar_solver::add_var_bound(lpvar j, lconstraint_kind kind, const mpq& right_side) { constraint_index ci = mk_var_bound(j, kind, right_side); activate(ci); return ci; @@ -1762,7 +1633,7 @@ namespace lp { // SASSERT(column_is_fixed(k)); if (j != k && column_is_fixed(k)) { SASSERT(column_is_int(j) == column_is_int(k)); - equal_to_j = column_to_reported_index(k); + equal_to_j = k; TRACE("lar_solver", tout << "found equal column k = " << k << ", external = " << equal_to_j << "\n";); } @@ -1802,10 +1673,10 @@ namespace lp { } - constraint_index lar_solver::mk_var_bound(var_index j, lconstraint_kind kind, const mpq& right_side) { + constraint_index lar_solver::mk_var_bound(lpvar j, lconstraint_kind kind, const mpq& right_side) { TRACE("lar_solver", tout << "j = " << get_variable_name(j) << " " << lconstraint_kind_string(kind) << " " << right_side << std::endl;); constraint_index ci; - if (!tv::is_term(j)) { // j is a var + if (!column_has_term(j)) { mpq rs = adjust_bound_for_int(j, kind, right_side); lp_assert(bound_is_integer_for_integer_column(j, rs)); ci = m_constraints.add_var_constraint(j, kind, rs); @@ -1817,9 +1688,7 @@ namespace lp { return ci; } - bool lar_solver::compare_values(var_index j, lconstraint_kind k, const mpq& rhs) { - if (tv::is_term(j)) - j = to_column(j); + bool lar_solver::compare_values(lpvar j, lconstraint_kind k, const mpq& rhs) { return compare_values(get_column_value(j), k, rhs); } @@ -1903,7 +1772,7 @@ namespace lp { void lar_solver::add_constraint_to_validate(lar_solver& ls, constraint_index ci) { auto const& c = m_constraints[ci]; TRACE("lar_solver_validate", tout << "adding constr with column = "<< c.column() << "\n"; m_constraints.display(tout, c); tout << std::endl;); - vector> coeffs; + vector> coeffs; for (auto p : c.coeffs()) { lpvar jext = p.second; lpvar j = ls.external_to_local(jext); @@ -1916,7 +1785,7 @@ namespace lp { lpvar column_ext = c.column(); unsigned j = ls.external_to_local(column_ext); - var_index tv; + lpvar tv; if (j == UINT_MAX) { tv = ls.add_term(coeffs, column_ext); } @@ -1938,10 +1807,11 @@ namespace lp { tout << std::endl; } }); + mpq rs = adjust_bound_for_int(j, kind, right_side); if (column_has_upper_bound(j)) - update_column_type_and_bound_with_ub(j, kind, right_side, dep); + update_column_type_and_bound_with_ub(j, kind, rs, dep); else - update_column_type_and_bound_with_no_ub(j, kind, right_side, dep); + update_column_type_and_bound_with_no_ub(j, kind, rs, dep); if (is_base(j) && column_is_fixed(j)) m_fixed_base_var_set.insert(j); @@ -1964,37 +1834,10 @@ namespace lp { } } - constraint_index lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq& right_side) { - lp_assert(tv::is_term(j)); - unsigned adjusted_term_index = tv::unmask_term(j); - // lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); - unsigned term_j; - lar_term const* term = m_terms[adjusted_term_index]; - if (m_var_register.external_is_used(j, term_j)) { - mpq rs = adjust_bound_for_int(term_j, kind, right_side); - lp_assert(bound_is_integer_for_integer_column(term_j, rs)); - return m_constraints.add_term_constraint(term_j, term, kind, rs); - } - else { - return add_constraint_from_term_and_create_new_column_row(j, term, kind, right_side); - } - } - - constraint_index lar_solver::add_constraint_from_term_and_create_new_column_row( - unsigned term_j, const lar_term* term, lconstraint_kind kind, const mpq& right_side) { - add_row_from_term_no_constraint(term, term_j); - unsigned j = A_r().column_count() - 1; + constraint_index lar_solver::add_var_bound_on_constraint_for_term(lpvar j, lconstraint_kind kind, const mpq& right_side) { mpq rs = adjust_bound_for_int(j, kind, right_side); - lp_assert(bound_is_integer_for_integer_column(j, rs)); - return m_constraints.add_term_constraint(j, term, kind, rs); - } - - void lar_solver::decide_on_strategy_and_adjust_initial_state() { - lp_assert(strategy_is_undecided()); - - m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; - - adjust_initial_state(); + SASSERT(bound_is_integer_for_integer_column(j, rs)); + return m_constraints.add_term_constraint(j, m_columns[j].term(), kind, rs); } struct scoped_backup { @@ -2006,31 +1849,7 @@ namespace lp { m_s.restore_x(); } }; - - - void lar_solver::adjust_initial_state() { - switch (m_settings.simplex_strategy()) { - case simplex_strategy_enum::tableau_rows: - adjust_initial_state_for_tableau_rows(); - break; - case simplex_strategy_enum::tableau_costs: - UNREACHABLE(); // not implemented - case simplex_strategy_enum::undecided: - adjust_initial_state_for_tableau_rows(); - break; - } - } - - - void lar_solver::adjust_initial_state_for_tableau_rows() { - for (unsigned i = 0; i < m_terms.size(); i++) { - if (m_var_register.external_is_used(tv::mask_term(i))) - continue; - add_row_from_term_no_constraint(m_terms[i], tv::mask_term(i)); - } - } - - + void lar_solver::update_column_type_and_bound_with_ub(unsigned j, lp::lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { SASSERT(column_has_upper_bound(j)); if (column_has_lower_bound(j)) { @@ -2051,7 +1870,7 @@ namespace lp { } } - void lar_solver::update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { + void lar_solver::update_bound_with_ub_lb(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { lp_assert(column_has_lower_bound(j) && column_has_upper_bound(j)); lp_assert(m_mpq_lar_core_solver.m_column_types[j] == column_type::boxed || m_mpq_lar_core_solver.m_column_types[j] == column_type::fixed); @@ -2116,7 +1935,7 @@ namespace lp { } } - void lar_solver::update_bound_with_no_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { + void lar_solver::update_bound_with_no_ub_lb(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { lp_assert(column_has_lower_bound(j) && !column_has_upper_bound(j)); lp_assert(m_mpq_lar_core_solver.m_column_types[j] == column_type::lower_bound); @@ -2169,7 +1988,7 @@ namespace lp { } } - void lar_solver::update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { + void lar_solver::update_bound_with_ub_no_lb(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { lp_assert(!column_has_lower_bound(j) && column_has_upper_bound(j)); lp_assert(m_mpq_lar_core_solver.m_column_types[j] == column_type::upper_bound); mpq y_of_bound(0); @@ -2222,7 +2041,7 @@ namespace lp { } } - void lar_solver::update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { + void lar_solver::update_bound_with_no_ub_no_lb(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { lp_assert(!column_has_lower_bound(j) && !column_has_upper_bound(j)); mpq y_of_bound(0); @@ -2257,22 +2076,14 @@ namespace lp { UNREACHABLE(); } insert_to_columns_with_changed_bounds(j); - } - - bool lar_solver::column_corresponds_to_term(unsigned j) const { - return tv::is_term(m_var_register.local_to_external(j)); - } + } - var_index lar_solver::to_column(unsigned ext_j) const { + lpvar lar_solver::to_column(unsigned ext_j) const { return m_var_register.external_to_local(ext_j); } - bool lar_solver::tighten_term_bounds_by_delta(tv const& t, const impq& delta) { - SASSERT(t.is_term()); - unsigned tj = t.index(); - unsigned j; - if (!m_var_register.external_is_used(tj, j)) - return true; // the term is not a column so it has no bounds + bool lar_solver::tighten_term_bounds_by_delta(lpvar j, const impq& delta) { + SASSERT(column_has_term(j)); auto& slv = m_mpq_lar_core_solver.m_r_solver; TRACE("cube", tout << "delta = " << delta << std::endl; m_int_solver->display_column(tout, j); ); @@ -2285,15 +2096,15 @@ namespace lp { TRACE("cube", tout << "can tighten";); if (slv.column_has_upper_bound(j)) { if (!is_zero(delta.y) || !is_zero(slv.m_upper_bounds[j].y)) - add_var_bound(tj, lconstraint_kind::LT, slv.m_upper_bounds[j].x - delta.x); + add_var_bound(j, lconstraint_kind::LT, slv.m_upper_bounds[j].x - delta.x); else - add_var_bound(tj, lconstraint_kind::LE, slv.m_upper_bounds[j].x - delta.x); + add_var_bound(j, lconstraint_kind::LE, slv.m_upper_bounds[j].x - delta.x); } if (slv.column_has_lower_bound(j)) { if (!is_zero(delta.y) || !is_zero(slv.m_lower_bounds[j].y)) - add_var_bound(tj, lconstraint_kind::GT, slv.m_lower_bounds[j].x + delta.x); + add_var_bound(j, lconstraint_kind::GT, slv.m_lower_bounds[j].x + delta.x); else - add_var_bound(tj, lconstraint_kind::GE, slv.m_lower_bounds[j].x + delta.x); + add_var_bound(j, lconstraint_kind::GE, slv.m_lower_bounds[j].x + delta.x); } return true; } @@ -2302,7 +2113,7 @@ namespace lp { void lar_solver::round_to_integer_solution() { for (unsigned j = 0; j < column_count(); j++) { if (!column_is_int(j)) continue; - if (column_corresponds_to_term(j)) continue; + if (column_has_term(j)) continue; impq& v = m_mpq_lar_core_solver.m_r_x[j]; if (v.is_int()) continue; @@ -2327,20 +2138,20 @@ namespace lp { void lar_solver::fix_terms_with_rounded_columns() { - for (unsigned i = 0; i < m_terms.size(); i++) { - if (!term_is_used_as_row(i)) + for (const lar_term* t : m_terms) { + lpvar j = t->j(); + if (!m_columns[j].associated_with_row()) continue; bool need_to_fix = false; - const lar_term& t = *m_terms[i]; - for (lar_term::ival p : t) { - if (m_incorrect_columns.contains(p.column())) { + + for (lar_term::ival p : * t) { + if (m_incorrect_columns.contains(p.j())) { need_to_fix = true; break; } } if (need_to_fix) { - lpvar j = m_var_register.external_to_local(tv::mask_term(i)); - impq v = t.apply(m_mpq_lar_core_solver.m_r_x); + impq v = t->apply(m_mpq_lar_core_solver.m_r_x); m_mpq_lar_core_solver.m_r_solver.update_x(j, v); } } @@ -2352,7 +2163,7 @@ namespace lp { bool lar_solver::sum_first_coords(const lar_term& t, mpq& val) const { val = zero_of_type(); for (lar_term::ival c : t) { - const auto& x = m_mpq_lar_core_solver.m_r_x[c.column()]; + const auto& x = m_mpq_lar_core_solver.m_r_x[c.j()]; if (!is_zero(x.y)) return false; val += x.x * c.coeff(); @@ -2360,18 +2171,14 @@ namespace lp { return true; } - bool lar_solver::get_equality_and_right_side_for_term_on_current_x(tv const& t, mpq& rs, u_dependency*& ci, bool& upper_bound) const { - lp_assert(t.is_term()); - unsigned j; - bool is_int; - if (!m_var_register.external_is_used(t.index(), j, is_int)) - return false; // the term does not have a bound because it does not correspond to a column - if (!is_int) // todo - allow for the next version of hnf + bool lar_solver::get_equality_and_right_side_for_term_on_current_x(lpvar j, mpq& rs, u_dependency*& ci, bool& upper_bound) const { + lp_assert(column_has_term(j)); + if (!column_is_int(j)) // todo - allow for the next version of hnf return false; bool rs_is_calculated = false; mpq b; bool is_strict; - const lar_term& term = get_term(t); + const lar_term& term = get_term(j); if (has_upper_bound(j, ci, b, is_strict) && !is_strict) { lp_assert(b.is_int()); if (!sum_first_coords(term, rs)) @@ -2438,9 +2245,8 @@ namespace lp { void lar_solver::register_existing_terms() { if (!m_need_register_terms) { TRACE("nla_solver", tout << "registering " << m_terms.size() << " terms\n";); - for (unsigned k = 0; k < m_terms.size(); k++) { - lpvar j = m_var_register.external_to_local(tv::mask_term(k)); - register_normalized_term(*m_terms[k], j); + for (const lar_term* t : m_terms) { + register_normalized_term(*t, t->j()); } } m_need_register_terms = true; @@ -2460,44 +2266,19 @@ namespace lp { return false; } - lar_term lar_solver::unfold_nested_subterms(lar_term const& term) { - lar_term result; - vector> todo; - for (auto const & [j,c] : term.coeffs()) - todo.push_back({j, c}); - while (!todo.empty()) { - auto [j, c] = todo.back(); - todo.pop_back(); - auto tv = column2tv(j); - if (tv.is_term()) { - for (auto const& [j, c2] : get_term(tv).coeffs()) - todo.push_back({j, c*c2}); - } - else - result.add_monomial(c, j); - } - return result; - } - - std::pair lar_solver::add_equality(lpvar j, lpvar k) { - vector> coeffs; - if (tv::is_term(j)) - j = map_term_index_to_column_index(j); - - if (tv::is_term(k)) - k = map_term_index_to_column_index(k); - + vector> coeffs; + coeffs.push_back(std::make_pair(mpq(1), j)); coeffs.push_back(std::make_pair(mpq(-1), k)); - unsigned term_index = add_term(coeffs, UINT_MAX); // UINT_MAX is the external null var + unsigned ej = add_term(coeffs, UINT_MAX); // UINT_MAX is the external null var if (get_column_value(j) != get_column_value(k)) set_status(lp_status::UNKNOWN); return std::pair( - add_var_bound(term_index, lconstraint_kind::LE, mpq(0)), - add_var_bound(term_index, lconstraint_kind::GE, mpq(0))); + add_var_bound(ej, lconstraint_kind::LE, mpq(0)), + add_var_bound(ej, lconstraint_kind::GE, mpq(0))); } bool lar_solver::inside_bounds(lpvar j, const impq& val) const { @@ -2516,7 +2297,7 @@ namespace lp { SASSERT(m_crossed_bounds_deps == nullptr); set_status(lp_status::INFEASIBLE); m_crossed_bounds_column = j; - const auto& ul = this->m_columns_to_ul_pairs[j]; + const auto& ul = this->m_columns[j]; u_dependency* bdep = lower_bound? ul.lower_bound_witness() : ul.upper_bound_witness(); SASSERT(bdep != nullptr); m_crossed_bounds_deps = m_dependencies.mk_join(bdep, dep); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index a01a66a38..4d71d0181 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -59,7 +59,7 @@ class lar_solver : public column_namer { size_t seed = 0; int i = 0; for (const auto p : t) { - hash_combine(seed, (unsigned)p.column()); + hash_combine(seed, (unsigned)p.j()); hash_combine(seed, p.coeff()); if (i++ > 10) break; @@ -86,8 +86,7 @@ class lar_solver : public column_namer { int_solver* m_int_solver = nullptr; bool m_need_register_terms = false; var_register m_var_register; - var_register m_term_register; - svector m_columns_to_ul_pairs; + svector m_columns; constraint_set m_constraints; // the set of column indices j such that bounds have changed for j indexed_uint_set m_columns_with_changed_bounds; @@ -119,16 +118,13 @@ class lar_solver : public column_namer { ////////////////// nested structs ///////////////////////// struct undo_add_column; - struct undo_add_term; ////////////////// methods //////////////////////////////// static bool valid_index(unsigned j) { return static_cast(j) >= 0; } - const lar_term& get_term(unsigned j) const; bool row_has_a_big_num(unsigned i) const; // init region - bool strategy_is_undecided() const; - void register_new_ext_var_index(unsigned ext_v, bool is_int); + void register_new_external_var(unsigned ext_v, bool is_int); bool term_is_int(const lar_term* t) const; bool term_is_int(const vector>& coeffs) const; void add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int); @@ -136,11 +132,9 @@ class lar_solver : public column_namer { mpq adjust_bound_for_int(lpvar j, lconstraint_kind&, const mpq&); // terms - bool all_vars_are_registered(const vector>& coeffs); - var_index add_term_undecided(const vector>& coeffs); - bool term_coeffs_are_ok(const vector>& coeffs); - void push_term(lar_term* t); - void add_row_from_term_no_constraint(const lar_term* term, unsigned term_ext_index); + bool all_vars_are_registered(const vector>& coeffs); + bool term_coeffs_are_ok(const vector>& coeffs); + void add_row_from_term_no_constraint(lar_term* term, unsigned term_ext_index); void add_basic_var_to_core_fields(); bool compare_values(impq const& lhs, lconstraint_kind k, const mpq& rhs); @@ -169,27 +163,20 @@ class lar_solver : public column_namer { void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); private: void require_nbasis_sort() { m_mpq_lar_core_solver.m_r_solver.m_nbasis_sort_counter = 0; } - void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); - void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); - void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); - void update_bound_with_no_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); - void update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); - void update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_column_type_and_bound_with_ub(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_column_type_and_bound_with_no_ub(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_bound_with_ub_lb(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_bound_with_no_ub_lb(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_bound_with_ub_no_lb(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_bound_with_no_ub_no_lb(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void register_in_fixed_var_table(unsigned, unsigned&); void remove_non_fixed_from_fixed_var_table(); - constraint_index add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq& right_side); + constraint_index add_var_bound_on_constraint_for_term(lpvar j, lconstraint_kind kind, const mpq& right_side); void set_crossed_bounds_column_and_deps(unsigned j, bool lower_bound, u_dependency* dep); - constraint_index add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, - lconstraint_kind kind, const mpq& right_side); unsigned row_of_basic_column(unsigned) const; - void decide_on_strategy_and_adjust_initial_state(); - void adjust_initial_state(); - void adjust_initial_state_for_tableau_rows(); bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const& be, const vector>& explanation) const; - void substitute_basis_var_in_terms_for_row(unsigned i); - template unsigned calculate_implied_bounds_for_row(unsigned row_index, lp_bound_propagator& bp) { if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation || row_has_a_big_num(row_index)) @@ -215,10 +202,10 @@ class lar_solver : public column_namer { void pop_core_solver_params(); void pop_core_solver_params(unsigned k); - void set_upper_bound_witness(var_index j, u_dependency* ci); - void set_lower_bound_witness(var_index j, u_dependency* ci); - void substitute_terms_in_linear_expression(const vector>& left_side_with_terms, - vector>& left_side) const; + void set_upper_bound_witness(lpvar j, u_dependency* ci); + void set_lower_bound_witness(lpvar j, u_dependency* ci); + void substitute_terms_in_linear_expression(const vector>& left_side_with_terms, + vector>& left_side) const; bool use_tableau_costs() const; bool tableau_with_costs() const; @@ -232,11 +219,11 @@ class lar_solver : public column_namer { void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); numeric_pair get_basic_var_value_from_row(unsigned i); - bool all_constrained_variables_are_registered(const vector>& left_side); + bool all_constrained_variables_are_registered(const vector>& left_side); bool all_constraints_hold() const; - bool constraint_holds(const lar_base_constraint& constr, std::unordered_map& var_map) const; - static void register_in_map(std::unordered_map& coeffs, const lar_base_constraint& cn, const mpq& a); - static void register_monoid_in_map(std::unordered_map& coeffs, const mpq& a, unsigned j); + bool constraint_holds(const lar_base_constraint& constr, std::unordered_map& var_map) const; + static void register_in_map(std::unordered_map& coeffs, const lar_base_constraint& cn, const mpq& a); + static void register_monoid_in_map(std::unordered_map& coeffs, const mpq& a, unsigned j); bool the_left_sides_sum_to_zero(const vector>& evidence) const; bool explanation_is_correct(explanation&) const; bool inf_explanation_is_correct() const; @@ -245,8 +232,8 @@ class lar_solver : public column_namer { explanation& exp, const vector>& inf_row, int inf_sign) const; - mpq get_left_side_val(const lar_base_constraint& cns, const std::unordered_map& var_map) const; - void fill_var_set_for_random_update(unsigned sz, var_index const* vars, vector& column_list); + mpq get_left_side_val(const lar_base_constraint& cns, const std::unordered_map& var_map) const; + void fill_var_set_for_random_update(unsigned sz, lpvar const* vars, vector& column_list); bool column_represents_row_in_tableau(unsigned j); void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j); void remove_last_row_and_column_from_tableau(unsigned j); @@ -260,7 +247,7 @@ class lar_solver : public column_namer { bool bound_is_integer_for_integer_column(unsigned j, const mpq& right_side) const; inline lar_core_solver& get_core_solver() { return m_mpq_lar_core_solver; } - var_index to_column(unsigned ext_j) const; + lpvar to_column(unsigned ext_j) const; void fix_terms_with_rounded_columns(); bool remove_from_basis(unsigned); lar_term get_term_to_maximize(unsigned ext_j) const; @@ -300,8 +287,6 @@ public: template void remove_non_fixed_from_table(T&); - unsigned external_to_column_index(unsigned) const; - bool inside_bounds(lpvar, const impq&) const; inline void set_column_value(unsigned j, const impq& v) { @@ -312,7 +297,7 @@ public: set_column_value(j, v); } - var_index add_named_var(unsigned ext_j, bool is_integer, const std::string&); + lpvar add_named_var(unsigned ext_j, bool is_integer, const std::string&); lp_status maximize_term(unsigned j_or_term, impq& term_max); @@ -349,7 +334,7 @@ public: mpq const& a = r.coeff(); int a_sign = is_pos(a) ? 1 : -1; int sign = j_sign * a_sign; - const ul_pair& ul = m_columns_to_ul_pairs[j]; + const column& ul = m_columns[j]; auto* witness = sign > 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); lp_assert(witness); for (auto ci : flatten(witness)) @@ -368,10 +353,10 @@ public: #ifdef Z3DEBUG bool fixed_base_removed_correctly() const; #endif - constraint_index mk_var_bound(var_index j, lconstraint_kind kind, const mpq& right_side); - void activate_check_on_equal(constraint_index, var_index&); + constraint_index mk_var_bound(lpvar j, lconstraint_kind kind, const mpq& right_side); + void activate_check_on_equal(constraint_index, lpvar&); void activate(constraint_index); - void random_update(unsigned sz, var_index const* vars); + void random_update(unsigned sz, lpvar const* vars); void add_column_rows_to_touched_rows(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator& bp) { @@ -405,34 +390,24 @@ public: } } - bool is_fixed(column_index const& j) const { return column_is_fixed(j); } - inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } bool external_is_used(unsigned) const; void pop(unsigned k); unsigned num_scopes() const { return m_trail.get_num_scopes(); } - bool compare_values(var_index j, lconstraint_kind kind, const mpq& right_side); - var_index add_term(const vector>& coeffs, unsigned ext_i); + bool compare_values(lpvar j, lconstraint_kind kind, const mpq& right_side); + lpvar add_term(const vector>& coeffs, unsigned ext_i); void register_existing_terms(); - var_index ensure_column(var_index vi); - constraint_index add_var_bound(var_index, lconstraint_kind, const mpq&); - constraint_index add_var_bound_check_on_equal(var_index, lconstraint_kind, const mpq&, var_index&); + constraint_index add_var_bound(lpvar, lconstraint_kind, const mpq&); + constraint_index add_var_bound_check_on_equal(lpvar, lconstraint_kind, const mpq&, lpvar&); - var_index add_var(unsigned ext_j, bool is_integer); + lpvar add_var(unsigned ext_j, bool is_integer); void set_cut_strategy(unsigned cut_frequency); inline unsigned column_count() const { return A_r().column_count(); } - inline var_index local_to_external(var_index idx) const { - return tv::is_term(idx) ? m_term_register.local_to_external(idx) : m_var_register.local_to_external(idx); + inline lpvar local_to_external(lpvar idx) const { + return m_var_register.local_to_external(idx); } - bool column_corresponds_to_term(unsigned) const; - const lar_term& column_to_term(unsigned j) const { - SASSERT(column_corresponds_to_term(j)); - return get_term(column2tv(to_column_index(j))); - } - - lar_term unfold_nested_subterms(lar_term const& term); - + inline bool column_associated_with_row(lpvar j) const { return m_columns[j].associated_with_row(); } inline unsigned row_count() const { return A_r().row_count(); } - bool var_is_registered(var_index vj) const; + bool var_is_registered(lpvar vj) const; void clear_inf_heap() { m_mpq_lar_core_solver.m_r_solver.inf_heap().clear(); } @@ -519,33 +494,28 @@ public: u_dependency_manager& dep_manager() { return m_dependencies; } inline u_dependency* get_column_upper_bound_witness(unsigned j) const { - if (tv::is_term(j)) { - j = m_var_register.external_to_local(j); - } - return m_columns_to_ul_pairs[j].upper_bound_witness(); + return m_columns[j].upper_bound_witness(); } - inline const impq& get_upper_bound(column_index j) const { + inline const impq& get_upper_bound(lpvar j) const { return m_mpq_lar_core_solver.m_r_solver.m_upper_bounds[j]; } - inline const impq& get_lower_bound(column_index j) const { + inline const impq& get_lower_bound(lpvar j) const { return m_mpq_lar_core_solver.m_r_solver.m_lower_bounds[j]; } - inline mpq bound_span_x(column_index j) const { + inline mpq bound_span_x(lpvar j) const { return m_mpq_lar_core_solver.m_r_solver.m_upper_bounds[j].x - m_mpq_lar_core_solver.m_r_solver.m_lower_bounds[j].x; } - bool has_lower_bound(var_index var, u_dependency*& ci, mpq& value, bool& is_strict) const; - bool has_upper_bound(var_index var, u_dependency*& ci, mpq& value, bool& is_strict) const; - bool has_value(var_index var, mpq& value) const; + bool has_lower_bound(lpvar var, u_dependency*& ci, mpq& value, bool& is_strict) const; + bool has_upper_bound(lpvar var, u_dependency*& ci, mpq& value, bool& is_strict) const; + bool has_value(lpvar var, mpq& value) const; bool fetch_normalized_term_column(const lar_term& t, std::pair&) const; - unsigned map_term_index_to_column_index(unsigned j) const; bool column_is_fixed(unsigned j) const; bool column_is_free(unsigned j) const; bool column_is_feasible(unsigned j) const { return m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j);} - unsigned column_to_reported_index(unsigned j) const; lp_settings& settings(); lp_settings const& settings() const; statistics& stats(); @@ -553,8 +523,6 @@ public: void updt_params(params_ref const& p); column_type get_column_type(unsigned j) const { return m_mpq_lar_core_solver.m_column_types()[j]; } const vector& get_column_types() const { return m_mpq_lar_core_solver.m_column_types(); } - const impq& get_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_lower_bounds()[j]; } - const impq& get_upper_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_upper_bounds()[j]; } std::ostream& print_terms(std::ostream& out) const; std::ostream& print_term(lar_term const& term, std::ostream& out) const; static std::ostream& print_term_as_indices(lar_term const& term, std::ostream& out); @@ -562,17 +530,17 @@ public: std::ostream& print_implied_bound(const implied_bound& be, std::ostream& out) const; std::ostream& print_values(std::ostream& out) const; std::ostream& display(std::ostream& out) const; - + std::ostream& display_constraint(std::ostream& out, constraint_index ci) const { + return m_constraints.display(out, ci); + } bool init_model() const; mpq from_model_in_impq_to_mpq(const impq& v) const { return v.x + m_delta * v.y; } - mpq get_value(column_index const& j) const; - mpq get_tv_value(tv const& t) const; - const impq& get_tv_ivalue(tv const& t) const; - void get_model(std::unordered_map& variable_values) const; + mpq get_value(lpvar j) const; + void get_model(std::unordered_map& variable_values) const; void get_rid_of_inf_eps(); - void get_model_do_not_care_about_diff_vars(std::unordered_map& variable_values) const; - std::string get_variable_name(var_index vi) const override; - void set_variable_name(var_index vi, std::string); + void get_model_do_not_care_about_diff_vars(std::unordered_map& variable_values) const; + std::string get_variable_name(lpvar vi) const override; + void set_variable_name(lpvar vi, std::string); inline unsigned number_of_vars() const { return m_var_register.size(); } inline bool is_base(unsigned j) const { return m_mpq_lar_core_solver.m_r_heading[j] >= 0; } inline const impq& column_lower_bound(unsigned j) const { @@ -594,7 +562,7 @@ public: std::pair add_equality(lpvar j, lpvar k); u_dependency* get_bound_constraint_witnesses_for_column(unsigned j) { - const ul_pair& ul = m_columns_to_ul_pairs[j]; + const column& ul = m_columns[j]; return m_dependencies.mk_join(ul.lower_bound_witness(), ul.upper_bound_witness()); } template @@ -612,22 +580,15 @@ public: void pop(); inline u_dependency* get_column_lower_bound_witness(unsigned j) const { - if (tv::is_term(j)) { - j = m_var_register.external_to_local(j); - } - return m_columns_to_ul_pairs[j].lower_bound_witness(); + return m_columns[j].lower_bound_witness(); } - - inline tv column2tv(column_index const& c) const { - return tv::raw(column_to_reported_index(c)); - } - + inline bool column_has_term(lpvar j) const { return m_columns[j].term() != nullptr; } inline std::ostream& print_column_info(unsigned j, std::ostream& out) const { m_mpq_lar_core_solver.m_r_solver.print_column_info(j, out); - if (tv::is_term(j)) { + if (column_has_term(j)) { print_term_as_indices(get_term(j), out) << "\n"; - } else if (column_corresponds_to_term(j)) { + } else if (column_has_term(j)) { const lar_term& t = get_term(m_var_register.local_to_external(j)); print_term_as_indices(t, out) << "\n"; } @@ -650,13 +611,12 @@ public: return false; } inline const vector& terms() const { return m_terms; } - inline lar_term const& term(unsigned i) const { return *m_terms[i]; } + inline void set_int_solver(int_solver* int_slv) { m_int_solver = int_slv; } inline int_solver* get_int_solver() { return m_int_solver; } inline const int_solver* get_int_solver() const { return m_int_solver; } - inline const lar_term& get_term(tv const& t) const { - lp_assert(t.is_term()); - return *m_terms[t.id()]; + inline const lar_term& get_term(lpvar j) const { + return *m_columns[j].term(); } lp_status find_feasible_solution(); void move_non_basic_columns_to_bounds(); @@ -674,8 +634,8 @@ public: inline const column_strip& get_column(unsigned i) const { return A_r().m_columns[i]; } bool row_is_correct(unsigned i) const; bool ax_is_correct() const; - bool get_equality_and_right_side_for_term_on_current_x(tv const& t, mpq& rs, u_dependency*& ci, bool& upper_bound) const; - bool var_is_int(var_index v) const; + bool get_equality_and_right_side_for_term_on_current_x(lpvar j, mpq& rs, u_dependency*& ci, bool& upper_bound) const; + bool var_is_int(lpvar v) const; inline const vector& r_heading() const { return m_mpq_lar_core_solver.m_r_heading; } inline const vector& r_basis() const { return m_mpq_lar_core_solver.r_basis(); } inline const vector& r_nbasis() const { return m_mpq_lar_core_solver.r_nbasis(); } @@ -686,7 +646,7 @@ public: lp_status solve(); void fill_explanation_from_crossed_bounds_column(explanation& evidence) const; bool term_is_used_as_row(unsigned term) const; - bool tighten_term_bounds_by_delta(tv const& t, const impq&); + bool tighten_term_bounds_by_delta(lpvar j, const impq&); lar_solver(); void track_touched_rows(bool v); bool touched_rows_are_tracked() const; @@ -697,18 +657,16 @@ public: inline static_matrix& A_r() { return m_mpq_lar_core_solver.m_r_A; } inline const static_matrix& A_r() const { return m_mpq_lar_core_solver.m_r_A; } // columns - bool column_is_int(column_index const& j) const { return column_is_int((unsigned)j); } - const impq& get_column_value(column_index const& j) const { return m_mpq_lar_core_solver.m_r_x[j]; } - inline var_index external_to_local(unsigned j) const { - var_index local_j; - if (m_var_register.external_is_used(j, local_j) || - m_term_register.external_is_used(j, local_j)) { + const impq& get_column_value(lpvar j) const { return m_mpq_lar_core_solver.m_r_x[j]; } + inline lpvar external_to_local(unsigned j) const { + lpvar local_j; + if (m_var_register.external_is_used(j, local_j)) { return local_j; } else { return -1; } } - unsigned usage_in_terms(column_index j) const { + unsigned usage_in_terms(lpvar j) const { if (j >= m_usage_in_terms.size()) return 0; return m_usage_in_terms[j]; diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index 5ce1ff2fc..6547377d3 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -26,8 +26,12 @@ namespace lp { class lar_term { typedef unsigned lpvar; u_map m_coeffs; + // the column index related to the term + lpvar m_j = -1; public: - lar_term() {} + // the column index related to the term + lpvar j() const { return m_j; } + lpvar& j() { return m_j; } void add_monomial(const mpq& c, unsigned j) { if (c.is_zero()) return; @@ -62,10 +66,11 @@ public: mpq a = it->get_data().m_value; this->m_coeffs.erase(term_column); for (auto p : t) { - this->add_monomial(a * p.coeff(), p.column()); + this->add_monomial(a * p.coeff(), p.j()); } } - + // constructors + lar_term() {} lar_term(const vector>& coeffs) { for (auto const& p : coeffs) { add_monomial(p.first, p.second); @@ -138,17 +143,23 @@ public: } return ret; } + + lar_term& operator*=(mpq const& k) { + for (auto & t : m_coeffs) + t.m_value *= k; + return *this; + } void clear() { m_coeffs.reset(); } class ival { - unsigned m_var; + lpvar m_var; const mpq & m_coeff; public: - ival(unsigned var, const mpq & val) : m_var(var), m_coeff(val) { } - column_index column() const { return column_index(m_var); } + ival(lpvar var, const mpq & val) : m_var(var), m_coeff(val) { } + lpvar j() const { return m_var; } const mpq & coeff() const { return m_coeff; } }; @@ -167,13 +178,13 @@ public: lpvar min_var = -1; mpq c; for (ival p : *this) { - if (p.column() < min_var) { - min_var = p.column(); + if (p.j() < min_var) { + min_var = p.j(); } } lar_term r; for (ival p : *this) { - if (p.column() == min_var) { + if (p.j() == min_var) { return p.coeff().is_one(); } } diff --git a/src/math/lp/lp_api.h b/src/math/lp/lp_api.h index 021501ecd..325bc980b 100644 --- a/src/math/lp/lp_api.h +++ b/src/math/lp/lp_api.h @@ -31,7 +31,7 @@ namespace lp_api { class bound { Literal m_bv; theory_var m_var; - lp::lpvar m_vi; + lp::lpvar m_column_index; bool m_is_int; rational m_value; bound_kind m_bound_kind; @@ -41,7 +41,7 @@ namespace lp_api { bound(Literal bv, theory_var v, lp::lpvar vi, bool is_int, rational const& val, bound_kind k, lp::constraint_index ct, lp::constraint_index cf) : m_bv(bv), m_var(v), - m_vi(vi), + m_column_index(vi), m_is_int(is_int), m_value(val), m_bound_kind(k) { @@ -53,7 +53,7 @@ namespace lp_api { theory_var get_var() const { return m_var; } - lp::tv tv() const { return lp::tv::raw(m_vi); } + lp::lpvar column_index() const { return m_column_index; } Literal get_lit() const { return m_bv; } diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index c6fa5796d..daeac3b18 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -147,8 +147,6 @@ public: void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function explain_bound) { - j = lp().column_to_reported_index(j); - lconstraint_kind kind = is_low ? GE : LE; if (strict) kind = static_cast(kind / 2); @@ -222,14 +220,11 @@ public: return out; } - bool add_eq_on_columns(const explanation& exp, lpvar j, lpvar k, bool is_fixed) { - lp_assert(j != k && is_int(j) == is_int(k)); - lp_assert(ival(j) == ival(k)); + bool add_eq_on_columns(const explanation& exp, lpvar je, lpvar ke, bool is_fixed) { + lp_assert(je != ke && is_int(je) == is_int(ke)); + lp_assert(ival(je) == ival(ke)); - unsigned je = lp().column_to_reported_index(j); - unsigned ke = lp().column_to_reported_index(k); TRACE("eq", - tout << "reporting eq " << j << ", " << k << "\n"; tout << "reported idx " << je << ", " << ke << "\n"; print_expl(tout, exp); tout << "theory_vars v" << lp().local_to_external(je) << " == v" << lp().local_to_external(ke) << "\n";); @@ -246,12 +241,12 @@ public: // column to theory_var unsigned col_to_imp(unsigned j) const { - return lp().local_to_external(lp().column_to_reported_index(j)); + return lp().local_to_external(j); } // theory_var to column unsigned imp_to_col(unsigned j) const { - return lp().external_to_column_index(j); + return lp().external_to_local(j); } bool is_int(lpvar j) const { @@ -261,7 +256,7 @@ public: void explain_fixed_in_row(unsigned row, explanation& ex) { TRACE("eq", tout << lp().get_row(row) << std::endl); for (const auto& c : lp().get_row(row)) - if (lp().is_fixed(c.var())) + if (lp().column_is_fixed(c.var())) explain_fixed_column(c.var(), ex); } @@ -269,7 +264,7 @@ public: unsigned base = UINT_MAX; TRACE("eq", tout << lp().get_row(row) << std::endl); for (const auto& c : lp().get_row(row)) { - if (lp().is_fixed(c.var())) { + if (lp().column_is_fixed(c.var())) { explain_fixed_column(c.var(), ex); } else if (lp().is_base(c.var())) { @@ -288,7 +283,7 @@ public: #ifdef Z3DEBUG bool all_fixed_in_row(unsigned row) const { for (const auto& c : lp().get_row(row)) - if (!lp().is_fixed(c.var())) + if (!lp().column_is_fixed(c.var())) return false; return true; } diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index a0cb485b5..08fd36505 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -53,9 +53,8 @@ inline std::ostream& operator<<(std::ostream& out, column_type const& t) { } enum class simplex_strategy_enum { - undecided = 3, - tableau_rows = 0, - tableau_costs = 1 + tableau_rows, + tableau_costs }; std::string column_type_to_string(column_type t); diff --git a/src/math/lp/lp_types.h b/src/math/lp/lp_types.h index f770fe956..5883495fa 100644 --- a/src/math/lp/lp_types.h +++ b/src/math/lp/lp_types.h @@ -30,72 +30,12 @@ namespace nla { namespace lp { -typedef unsigned var_index; typedef unsigned constraint_index; typedef unsigned row_index; enum lconstraint_kind { LE = -2, LT = -1 , GE = 2, GT = 1, EQ = 0, NE = 3 }; typedef unsigned lpvar; const lpvar null_lpvar = UINT_MAX; const constraint_index null_ci = UINT_MAX; - - -class column_index { - unsigned m_index; - friend class lar_solver; - friend class lar_term; - friend nla::core; - - operator unsigned() const { return m_index; } - -public: - column_index(unsigned j): m_index(j) {} - unsigned index() const { return m_index; } - bool is_null() const { return m_index == null_lpvar; } -}; - - -// index that comes from term or variable. -class tv { - unsigned m_index; - static const unsigned EF = UINT_MAX >> 1; - tv(unsigned i): m_index(i) {} -public: - static tv term(unsigned i) { SASSERT(0 == (i & left_most_bit)); return tv(mask_term(i)); } - static tv var(unsigned i) { SASSERT(0 == (i & left_most_bit)); return tv(i); } - static tv raw(unsigned i) { return tv(i); } - - // retrieve the identifier associated with tv - unsigned id() const { return unmask_term(m_index); } - column_index column() const { SASSERT(is_var()); return column_index(id()); } - - // retrieve the raw index. - unsigned index() const { return m_index; } - - bool is_term() const { return 0 != (m_index & left_most_bit); } - bool is_var() const { return 0 == (m_index & left_most_bit); } - - // utilities useful where tv isn't already encapsulating id's. - static inline unsigned unmask_term(unsigned j) { return j & EF; } - static inline bool is_term(unsigned j) { return j & left_most_bit; } - static inline unsigned mask_term(unsigned j) { return j | left_most_bit; } - - // used by var_register. could we encapsulate even this? - static const unsigned left_most_bit = ~EF; - - std::string to_string() const { - std::ostringstream strm; - strm << (is_term() ? "t" : "j") << id(); - return strm.str(); - } - - bool is_null() const { return m_index == UINT_MAX; } - -}; - - } -inline std::ostream& operator<<(std::ostream& out, lp::tv const& t) { - return out << (t.is_term() ? "t":"j") << t.id() << "\n"; -} diff --git a/src/math/lp/lp_utils.h b/src/math/lp/lp_utils.h index 3c1383cb3..d6943bf50 100644 --- a/src/math/lp/lp_utils.h +++ b/src/math/lp/lp_utils.h @@ -116,12 +116,9 @@ template std::ostream& print_linear_combination_of_column_indices_only(const vector> & coeffs, std::ostream & out) { return print_linear_combination_customized( coeffs, - [](unsigned j) {std::stringstream ss; - if (tv::is_term(j)) { - ss << "t" << tv::unmask_term(j); - } else { - ss << "j" << j; - } + [](unsigned j) { + std::stringstream ss; + ss << "j" << j; return ss.str();}, out); } diff --git a/src/math/lp/monic.h b/src/math/lp/monic.h index 19137cd31..b51134166 100644 --- a/src/math/lp/monic.h +++ b/src/math/lp/monic.h @@ -21,15 +21,15 @@ namespace nla { class mon_eq { // fields - lp::var_index m_v; - svector m_vs; + lp::lpvar m_v; + svector m_vs; public: // constructors - mon_eq(lp::var_index v, unsigned sz, lp::var_index const* vs): + mon_eq(lp::lpvar v, unsigned sz, lp::lpvar const* vs): m_v(v), m_vs(sz, vs) { std::sort(m_vs.begin(), m_vs.end()); } - mon_eq(lp::var_index v, const svector &vs): + mon_eq(lp::lpvar v, const svector &vs): m_v(v), m_vs(vs) { std::sort(m_vs.begin(), m_vs.end()); } @@ -37,7 +37,7 @@ public: unsigned var() const { return m_v; } unsigned size() const { return m_vs.size(); } - const svector& vars() const { return m_vs; } + const svector& vars() const { return m_vs; } bool empty() const { return m_vs.empty(); } bool is_sorted() const { for (unsigned i = 0; i + 1 < size(); i++) @@ -49,7 +49,7 @@ public: return std::binary_search(m_vs.begin(), m_vs.end(), j); } protected: - svector& vars1() { return m_vs; } + svector& vars1() { return m_vs; } }; // support the congruence diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 9c99fa8c3..f2a1b0287 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -376,7 +376,7 @@ namespace nla { // propagate fixed equality auto exp = get_explanation(dep); - c().add_fixed_equality(c().lra.column_to_reported_index(m.var()), rational(0), exp); + c().add_fixed_equality(m.var(), rational(0), exp); } void monomial_bounds::propagate_fixed(monic const& m, rational const& k) { @@ -386,22 +386,21 @@ namespace nla { // propagate fixed equality auto exp = get_explanation(dep); - c().add_fixed_equality(c().lra.column_to_reported_index(m.var()), k, exp); + c().add_fixed_equality(m.var(), k, exp); } void monomial_bounds::propagate_nonfixed(monic const& m, rational const& k, lpvar w) { vector> coeffs; coeffs.push_back({-k, w}); coeffs.push_back({rational::one(), m.var()}); - lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); + lp::lpvar j = c().lra.add_term(coeffs, UINT_MAX); auto* dep = explain_fixed(m, k); - term_index = c().lra.map_term_index_to_column_index(term_index); TRACE("nla_solver", tout << "propagate nonfixed " << m << " = " << k << " " << w << "\n";); - c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, mpq(0), dep); + c().lra.update_column_type_and_bound(j, lp::lconstraint_kind::EQ, mpq(0), dep); if (k == 1) { lp::explanation exp = get_explanation(dep); - c().add_equality(c().lra.column_to_reported_index(m.var()), c().lra.column_to_reported_index(w), exp); + c().add_equality(m.var(), w, exp); } } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index f36fab52e..675bf5022 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -68,21 +68,10 @@ bool core::compare_holds(const rational& ls, llc cmp, const rational& rs) const rational core::value(const lp::lar_term& r) const { rational ret(0); for (lp::lar_term::ival t : r) - ret += t.coeff() * val(t.column()); + ret += t.coeff() * val(t.j()); return ret; } -lp::lar_term core::subs_terms_to_columns(const lp::lar_term& t) const { - lp::lar_term r; - for (lp::lar_term::ival p : t) { - lpvar j = p.column(); - if (lp::tv::is_term(j)) - j = lra.map_term_index_to_column_index(j); - r.add_monomial(p.coeff(), j); - } - return r; -} - bool core::ineq_holds(const ineq& n) const { return compare_holds(value(n.term()), n.cmp(), n.rs()); } @@ -139,10 +128,7 @@ bool core::canonize_sign(const factorization& f) const { void core::add_monic(lpvar v, unsigned sz, lpvar const* vs) { m_add_buffer.resize(sz); for (unsigned i = 0; i < sz; i++) { - lpvar j = vs[i]; - if (lp::tv::is_term(j)) - j = lra.map_term_index_to_column_index(j); - m_add_buffer[i] = j; + m_add_buffer[i] = vs[i]; } m_emons.add(v, m_add_buffer); m_monics_with_changed_bounds.insert(v); @@ -326,25 +312,25 @@ bool core::explain_coeff_lower_bound(const lp::lar_term::ival& p, rational& boun const rational& a = p.coeff(); SASSERT(!a.is_zero()); if (a.is_pos()) { - auto* dep = lra.get_column_lower_bound_witness(p.column()); + auto* dep = lra.get_column_lower_bound_witness(p.j()); if (!dep) return false; - bound = a * lra.get_lower_bound(p.column()).x; + bound = a * lra.get_lower_bound(p.j()).x; lra.push_explanation(dep, e); return true; } // a.is_neg() - auto* dep = lra.get_column_upper_bound_witness(p.column()); + auto* dep = lra.get_column_upper_bound_witness(p.j()); if (!dep) return false; - bound = a * lra.get_upper_bound(p.column()).x; + bound = a * lra.get_upper_bound(p.j()).x; lra.push_explanation(dep, e); return true; } bool core::explain_coeff_upper_bound(const lp::lar_term::ival& p, rational& bound, lp::explanation& e) const { const rational& a = p.coeff(); - lpvar j = p.column(); + lpvar j = p.j(); SASSERT(!a.is_zero()); if (a.is_neg()) { auto *dep = lra.get_column_lower_bound_witness(j); @@ -602,7 +588,7 @@ std::ostream & core::print_ineqs(const lemma& l, std::ostream & out) const { print_ineq(in, out); if (i + 1 < l.ineqs().size()) out << " or "; for (lp::lar_term::ival p: in.term()) - vars.insert(p.column()); + vars.insert(p.j()); } out << std::endl; for (lpvar j : vars) { @@ -701,13 +687,13 @@ unsigned core::random() { return lp_settings().random_next(); } void core::collect_equivs() { const lp::lar_solver& s = lra; - for (unsigned i = 0; i < s.terms().size(); i++) { - if (!s.term_is_used_as_row(i)) + for (const auto * t : s.terms()) { + if (!s.column_associated_with_row(t->j())) continue; - lpvar j = s.external_to_local(lp::tv::mask_term(i)); + lpvar j = t->j(); if (var_is_fixed_to_zero(j)) { - TRACE("nla_solver_mons", s.print_term_as_indices(*s.terms()[i], tout << "term = ") << "\n";); - add_equivalence_maybe(s.terms()[i], s.get_column_upper_bound_witness(j), s.get_column_lower_bound_witness(j)); + TRACE("nla_solver_mons", s.print_term_as_indices(*t, tout << "term = ") << "\n";); + add_equivalence_maybe(t, s.get_column_upper_bound_witness(j), s.get_column_lower_bound_witness(j)); } } m_emons.ensure_canonized(); @@ -732,9 +718,9 @@ bool core::is_octagon_term(const lp::lar_term& t, bool & sign, lpvar& i, lpvar & return false; } if (i == null_lpvar) - i = p.column(); + i = p.j(); else - j = p.column(); + j = p.j(); } SASSERT(j != null_lpvar); sign = (seen_minus && seen_plus)? false : true; @@ -871,7 +857,7 @@ std::unordered_set core::collect_vars(const lemma& l) const { for (const auto& i : l.ineqs()) { for (lp::lar_term::ival p : i.term()) { - insert_j(p.column()); + insert_j(p.j()); } } for (auto p : l.expl()) { @@ -1434,45 +1420,6 @@ void core::patch_monomials_on_to_refine() { void core::patch_monomials() { m_cautious_patching = true; patch_monomials_on_to_refine(); - if (m_to_refine.size() == 0 || !params().arith_nl_expensive_patching()) { - return; - } - NOT_IMPLEMENTED_YET(); - m_cautious_patching = false; - patch_monomials_on_to_refine(); - lra.push(); - save_tableau(); - constrain_nl_in_tableau(); - if (solve_tableau() && integrality_holds()) { - lra.pop(1); - } else { - lra.pop(); - restore_tableau(); - lra.clear_inf_heap(); - } - SASSERT(lra.ax_is_correct()); -} - -void core::constrain_nl_in_tableau() { - NOT_IMPLEMENTED_YET(); -} - -bool core::solve_tableau() { - NOT_IMPLEMENTED_YET(); - return false; -} - -void core::restore_tableau() { - NOT_IMPLEMENTED_YET(); -} - -void core::save_tableau() { - NOT_IMPLEMENTED_YET(); -} - -bool core::integrality_holds() { - NOT_IMPLEMENTED_YET(); - return false; } /** @@ -1668,17 +1615,9 @@ lbool core::test_check() { } std::ostream& core::print_terms(std::ostream& out) const { - for (unsigned i = 0; i < lra.terms().size(); i++) { - unsigned ext = lp::tv::mask_term(i); - if (!lra.var_is_registered(ext)) { - out << "term is not registered\n"; - continue; - } - - const lp::lar_term & t = *lra.terms()[i]; - out << "term:"; print_term(t, out) << std::endl; - lpvar j = lra.external_to_local(ext); - print_var(j, out); + for (const auto * t: lra.terms()) { + out << "term:"; print_term(*t, out) << std::endl; + print_var(t->j(), out); } return out; } @@ -1712,12 +1651,12 @@ std::unordered_set core::get_vars_of_expr_with_opening_terms(const nex *e } for (unsigned i = 0; i < added.size(); ++i) { lpvar j = added[i]; - if (ls.column_corresponds_to_term(j)) { - const auto& t = lra.get_term(lp::tv::raw(ls.local_to_external(j))); + if (ls.column_has_term(j)) { + const auto& t = lra.get_term(j); for (auto p : t) { - if (ret.find(p.column()) == ret.end()) { - added.push_back(p.column()); - ret.insert(p.column()); + if (ret.find(p.j()) == ret.end()) { + added.push_back(p.j()); + ret.insert(p.j()); } } } @@ -1768,8 +1707,6 @@ void core::set_active_vars_weights(nex_creator& nc) { } bool core::influences_nl_var(lpvar j) const { - if (lp::tv::is_term(j)) - j = lp::tv::unmask_term(j); if (is_nl_var(j)) return true; for (const auto & c : lra.A_r().m_columns[j]) { diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 29effd4e4..eb1f8b436 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -135,7 +135,6 @@ public: rational value(const lp::lar_term& r) const; - lp::lar_term subs_terms_to_columns(const lp::lar_term& t) const; bool ineq_holds(const ineq& n) const; bool lemma_holds(const lemma& l) const; bool is_monic_var(lpvar j) const { return m_emons.is_monic_var(j); } @@ -284,10 +283,10 @@ public: } const rational& get_upper_bound(unsigned j) const; const rational& get_lower_bound(unsigned j) const; - bool has_lower_bound(lp::var_index var, u_dependency*& ci, lp::mpq& value, bool& is_strict) const { + bool has_lower_bound(lp::lpvar var, u_dependency*& ci, lp::mpq& value, bool& is_strict) const { return lra.has_lower_bound(var, ci, value, is_strict); } - bool has_upper_bound(lp::var_index var, u_dependency*& ci, lp::mpq& value, bool& is_strict) const { + bool has_upper_bound(lp::lpvar var, u_dependency*& ci, lp::mpq& value, bool& is_strict) const { return lra.has_upper_bound(var, ci, value, is_strict); } @@ -424,18 +423,10 @@ public: vector const& literals() const { return m_literals; } vector const& equalities() const { return m_equalities; } vector const& fixed_equalities() const { return m_fixed_equalities; } - bool check_feasible() const { return m_check_feasible; } + bool should_check_feasible() const { return m_check_feasible; } void add_fixed_equality(lp::lpvar v, rational const& k, lp::explanation const& e) { m_fixed_equalities.push_back({v, k, e}); } void add_equality(lp::lpvar i, lp::lpvar j, lp::explanation const& e) { m_equalities.push_back({i, j, e}); } -private: - void restore_patched_values(); - void constrain_nl_in_tableau(); - bool solve_tableau(); - void restore_tableau(); - void save_tableau(); - bool integrality_holds(); - }; // end of core diff --git a/src/math/lp/nla_defs.h b/src/math/lp/nla_defs.h index e9807eaeb..b57603614 100644 --- a/src/math/lp/nla_defs.h +++ b/src/math/lp/nla_defs.h @@ -17,7 +17,7 @@ namespace nla { typedef lp::constraint_index lpci; typedef lp::lconstraint_kind llc; typedef lp::explanation expl_set; - typedef lp::var_index lpvar; + typedef unsigned lpvar; struct from_index_dummy{}; class signed_var { @@ -54,12 +54,12 @@ inline std::ostream& operator<<(std::ostream& out, signed_var const& sv) { retur * where m_vs = [v1, v2, .., vn] */ class monic_coeff { - svector m_vs; + svector m_vs; rational m_coeff; public: - monic_coeff(const svector& vs, rational const& coeff): m_vs(vs), m_coeff(coeff) {} + monic_coeff(const svector& vs, rational const& coeff): m_vs(vs), m_coeff(coeff) {} rational const& coeff() const { return m_coeff; } - const svector & vars() const { return m_vs; } + const svector & vars() const { return m_vs; } }; template bool has_zero(const T& product) { for (const rational & t : product) { diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index 7d52cd0ba..6c3bb178c 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -19,15 +19,9 @@ Description: namespace nla { void divisions::add_idivision(lpvar q, lpvar x, lpvar y) { - auto& lra = m_core.lra; + const auto& lra = m_core.lra; if (x == null_lpvar || y == null_lpvar || q == null_lpvar) return; - if (lp::tv::is_term(x)) - x = lra.map_term_index_to_column_index(x); - if (lp::tv::is_term(y)) - y = lra.map_term_index_to_column_index(y); - if (lp::tv::is_term(q)) - q = lra.map_term_index_to_column_index(q); m_idivisions.push_back({q, x, y}); m_core.trail().push(push_back_vector(m_idivisions)); } @@ -36,13 +30,6 @@ namespace nla { auto& lra = m_core.lra; if (x == null_lpvar || y == null_lpvar || q == null_lpvar) return; - if (lp::tv::is_term(x)) - x = lra.map_term_index_to_column_index(x); - if (lp::tv::is_term(y)) - y = lra.map_term_index_to_column_index(y); - if (lp::tv::is_term(q)) - q = lra.map_term_index_to_column_index(q); - m_rdivisions.push_back({ q, x, y }); m_core.trail().push(push_back_vector(m_rdivisions)); } @@ -50,7 +37,7 @@ namespace nla { void divisions::add_bounded_division(lpvar q, lpvar x, lpvar y) { if (x == null_lpvar || y == null_lpvar || q == null_lpvar) return; - if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(q)) + if (m_core.lra.column_has_term(x) || m_core.lra.column_has_term(y) || m_core.lra.column_has_term(q)) return; m_bounded_divisions.push_back({ q, x, y }); m_core.trail().push(push_back_vector(m_bounded_divisions)); diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index f9974b41c..4beb7eaff 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -406,9 +406,8 @@ namespace nla { coeffs.push_back({lc*coeff, m_mon2var.find(vars)->second}); } - lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); - term_index = c().lra.map_term_index_to_column_index(term_index); - c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, offset, e.dep()); + lp::lpvar j = c().lra.add_term(coeffs, UINT_MAX); + c().lra.update_column_type_and_bound(j, lp::lconstraint_kind::EQ, offset, e.dep()); c().m_check_feasible = true; return true; } diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index f389aad93..4e521bb01 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -80,12 +80,12 @@ namespace nla { lbool powers::check(lpvar r, lpvar x, lpvar y, vector& lemmas) { TRACE("nla", tout << r << " == " << x << "^" << y << "\n"); + core& c = m_core; if (x == null_lpvar || y == null_lpvar || r == null_lpvar) return l_undef; - if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(r)) + if (c.lra.column_has_term(x) || c.lra.column_has_term(y) || c.lra.column_has_term(r)) return l_undef; - core& c = m_core; if (c.use_nra_model()) return l_undef; diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 23d4016bd..f55eec6a6 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -83,7 +83,7 @@ namespace nla { nlsat::anum_manager& solver::am() { return m_core->m_nra.am(); } - nlsat::anum const& solver::am_value(lp::var_index v) const { + nlsat::anum const& solver::am_value(lp::lpvar v) const { SASSERT(use_nra_model()); return m_core->m_nra.value(v); } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 1fbafdf6b..53e62b4f0 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -46,13 +46,13 @@ namespace nla { bool use_nra_model() const; core& get_core(); nlsat::anum_manager& am(); - nlsat::anum const& am_value(lp::var_index v) const; + nlsat::anum const& am_value(lp::lpvar v) const; scoped_anum& tmp1(); scoped_anum& tmp2(); vector const& lemmas() const; vector const& literals() const; vector const& fixed_equalities() const; vector const& equalities() const; - bool check_feasible() const { return m_core->check_feasible(); } + bool should_check_feasible() const { return m_core->should_check_feasible(); } }; } diff --git a/src/math/lp/nla_types.h b/src/math/lp/nla_types.h index cb62c5a30..f0f796652 100644 --- a/src/math/lp/nla_types.h +++ b/src/math/lp/nla_types.h @@ -22,7 +22,7 @@ namespace nla { typedef lp::lconstraint_kind llc; typedef lp::constraint_index lpci; typedef lp::explanation expl_set; - typedef lp::var_index lpvar; + typedef lp::lpvar lpvar; const lpvar null_lpvar = UINT_MAX; diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index ccd95e6be..9c9db4e41 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -71,12 +71,11 @@ struct solver::imp { } } - for (unsigned i = lra.terms().size(); i-- > 0; ) { - auto const& t = lra.term(i); - for (auto const iv : t) { - auto v = iv.column().index(); + for (const auto *t : lra.terms() ) { + for (auto const iv : *t) { + auto v = iv.j(); var2occurs.reserve(v + 1); - var2occurs[v].terms.push_back(i); + var2occurs[v].terms.push_back(t->j()); } } @@ -99,17 +98,15 @@ struct solver::imp { todo.push_back(w); for (auto ti : var2occurs[v].terms) { - for (auto iv : lra.term(ti)) - todo.push_back(iv.column().index()); - auto vi = lp::tv::mask_term(ti); - todo.push_back(lra.map_term_index_to_column_index(vi)); + for (auto iv : lra.get_term(ti)) + todo.push_back(iv.j()); + todo.push_back(ti); } - if (lra.column_corresponds_to_term(v)) { + if (lra.column_has_term(v)) { m_term_set.insert(v); - lp::tv ti = lp::tv::raw(lra.column_to_reported_index(v)); - for (auto kv : lra.get_term(ti)) - todo.push_back(kv.column().index()); + for (auto kv : lra.get_term(v)) + todo.push_back(kv.j()); } if (m_nla_core.is_monic_var(v)) { @@ -158,6 +155,8 @@ struct solver::imp { for (unsigned i : m_term_set) add_term(i); + TRACE("nra", m_nlsat->display(tout)); + lbool r = l_undef; try { r = m_nlsat->check(); @@ -200,7 +199,7 @@ struct solver::imp { for (auto c : core) { unsigned idx = static_cast(static_cast(c) - this); ex.push_back(idx); - TRACE("arith", tout << "ex: " << idx << "\n";); + TRACE("nra", lra.display_constraint(tout << "ex: " << idx << ": ", idx) << "\n";); } nla::new_lemma lemma(m_nla_core, __FUNCTION__); lemma &= ex; @@ -515,16 +514,16 @@ struct solver::imp { - bool is_int(lp::var_index v) { + bool is_int(lp::lpvar v) { return lra.var_is_int(v); } - polynomial::var lp2nl(lp::var_index v) { + polynomial::var lp2nl(lp::lpvar v) { polynomial::var r; if (!m_lp2nl.find(v, r)) { r = m_nlsat->mk_var(is_int(v)); m_lp2nl.insert(v, r); - if (!m_term_set.contains(v) && lra.column_corresponds_to_term(v)) { + if (!m_term_set.contains(v) && lra.column_has_term(v)) { m_term_set.insert(v); } } @@ -532,14 +531,13 @@ struct solver::imp { } // void add_term(unsigned term_column) { - lp::tv ti = lp::tv::raw(lra.column_to_reported_index(term_column)); - const lp::lar_term& t = lra.get_term(ti); + const lp::lar_term& t = lra.get_term(term_column); // code that creates a polynomial equality between the linear coefficients and // variable representing the term. svector vars; rational den(1); for (lp::lar_term::ival kv : t) { - vars.push_back(lp2nl(kv.column().index())); + vars.push_back(lp2nl(kv.j())); den = lcm(den, denominator(kv.coeff())); } vars.push_back(lp2nl(term_column)); @@ -557,7 +555,7 @@ struct solver::imp { m_nlsat->mk_clause(1, &lit, nullptr); } - nlsat::anum const& value(lp::var_index v) { + nlsat::anum const& value(lp::lpvar v) { polynomial::var pv; if (m_lp2nl.find(v, pv)) return m_nlsat->value(pv); @@ -634,7 +632,7 @@ std::ostream& solver::display(std::ostream& out) const { return m_imp->display(out); } -nlsat::anum const& solver::value(lp::var_index v) { +nlsat::anum const& solver::value(lp::lpvar v) { return m_imp->value(v); } diff --git a/src/math/lp/nra_solver.h b/src/math/lp/nra_solver.h index 747b4cee3..90f022ba6 100644 --- a/src/math/lp/nra_solver.h +++ b/src/math/lp/nra_solver.h @@ -55,7 +55,7 @@ namespace nra { /* \brief Access model. */ - nlsat::anum const& value(lp::var_index v); + nlsat::anum const& value(lp::lpvar v); nlsat::anum_manager& am(); diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index 251274006..93780f7be 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -24,7 +24,6 @@ #include #ifdef lp_for_z3 #include "util/rational.h" -#include "util/sstream.h" #include "util/z3_exception.h" #else // include "util/numerics/mpq.h" diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index ffbd48021..9d6bb8599 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -346,14 +346,14 @@ public: // we use the form -it + 1 = 0 m_work_vector.set_value(one_of_type(), bj); for (auto p : row) { - m_work_vector.set_value(-p.coeff(), p.column().index()); + m_work_vector.set_value(-p.coeff(), p.j()); // but take care of the basis 1 later } // now iterate with pivoting fill_last_row_with_pivoting_loop_block(bj, basis_heading); for (auto p : row) { - fill_last_row_with_pivoting_loop_block(p.column().index(), basis_heading); + fill_last_row_with_pivoting_loop_block(p.j(), basis_heading); } unsigned last_row = row_count() - 1; diff --git a/src/math/lp/var_register.h b/src/math/lp/var_register.h index 3b326788b..bd7e6efe8 100644 --- a/src/math/lp/var_register.h +++ b/src/math/lp/var_register.h @@ -41,10 +41,7 @@ public: class var_register { vector m_local_to_external; std::unordered_map m_external_to_local; - unsigned m_locals_mask; - unsigned m_locals_mask_inverted; public: - var_register(bool mask_locals): m_locals_mask(mask_locals? tv::left_most_bit: 0), m_locals_mask_inverted(~m_locals_mask) {} void set_name(unsigned j, std::string name) { m_local_to_external[j].set_name(name); @@ -63,7 +60,7 @@ public: } m_local_to_external.push_back(ext_var_info(user_var, is_int)); - unsigned local = ( size() - 1 ) | m_locals_mask; + unsigned local = size() - 1; if (user_var != UINT_MAX) m_external_to_local[user_var] = local; @@ -79,7 +76,7 @@ public: // returns UINT_MAX if unsigned local_to_external(unsigned local_var) const { - unsigned k = local_var & m_locals_mask_inverted; + unsigned k = local_var; if (k >= m_local_to_external.size()) return UINT_MAX; return m_local_to_external[k].external_j(); @@ -119,7 +116,7 @@ public: local_j = UINT_MAX; return false; } - local_j = it->second & m_locals_mask_inverted; + local_j = it->second; is_int = m_local_to_external[local_j].is_integer(); return true; } @@ -129,7 +126,7 @@ public: } bool local_is_int(unsigned j) const { - return m_local_to_external[j & m_locals_mask_inverted].is_integer(); + return m_local_to_external[j].is_integer(); } void shrink(unsigned shrunk_size) { diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index d8e4c2852..731555613 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -180,7 +180,7 @@ namespace algebraic_numbers { return m_upmanager; } - void del(basic_cell * c) { + void del_basic(basic_cell * c) { qm().del(c->m_value); m_allocator.deallocate(sizeof(basic_cell), c); } @@ -204,13 +204,13 @@ namespace algebraic_numbers { } void del(numeral & a) { - if (a.m_cell == nullptr) + if (a.is_null()) return; if (a.is_basic()) - del(a.to_basic()); + del_basic(a.to_basic()); else del(a.to_algebraic()); - a.m_cell = nullptr; + a.clear(); } void reset(numeral & a) { @@ -218,7 +218,7 @@ namespace algebraic_numbers { } bool is_zero(numeral const & a) { - return a.m_cell == nullptr; + return a.is_null(); } bool is_pos(numeral const & a) { @@ -359,7 +359,7 @@ namespace algebraic_numbers { } void swap(numeral & a, numeral & b) noexcept { - std::swap(a.m_cell, b.m_cell); + a.swap(b); } basic_cell * mk_basic_cell(mpq & n) { @@ -432,13 +432,13 @@ namespace algebraic_numbers { } if (a.is_basic()) { if (is_zero(a)) - a.m_cell = mk_basic_cell(n); + a = mk_basic_cell(n); else qm().set(a.to_basic()->m_value, n); } else { del(a); - a.m_cell = mk_basic_cell(n); + a = mk_basic_cell(n); } } @@ -492,7 +492,7 @@ namespace algebraic_numbers { else { if (a.is_basic()) { del(a); - a.m_cell = TAG(void*, mk_algebraic_cell(sz, p, lower, upper, minimal), ROOT); + a = mk_algebraic_cell(sz, p, lower, upper, minimal); } else { SASSERT(sz > 2); @@ -526,7 +526,7 @@ namespace algebraic_numbers { del(a); void * mem = m_allocator.allocate(sizeof(algebraic_cell)); algebraic_cell * c = new (mem) algebraic_cell(); - a.m_cell = TAG(void *, c, ROOT); + a = c; copy(c, b.to_algebraic()); SASSERT(acell_inv(*c)); } @@ -797,8 +797,8 @@ namespace algebraic_numbers { // root was found scoped_mpq r(qm()); to_mpq(qm(), lower(c), r); - del(c); - a.m_cell = mk_basic_cell(r); + del(a); + a = mk_basic_cell(r); return false; } } @@ -818,8 +818,8 @@ namespace algebraic_numbers { // actual root was found scoped_mpq r(qm()); to_mpq(qm(), lower(c), r); - del(c); - a.m_cell = mk_basic_cell(r); + del(a); + a = mk_basic_cell(r); return false; } SASSERT(acell_inv(*c)); diff --git a/src/math/polynomial/algebraic_numbers.h b/src/math/polynomial/algebraic_numbers.h index 63b833b80..e2e95367c 100644 --- a/src/math/polynomial/algebraic_numbers.h +++ b/src/math/polynomial/algebraic_numbers.h @@ -360,19 +360,25 @@ namespace algebraic_numbers { struct basic_cell; struct algebraic_cell; - enum anum_kind { BASIC = 0, ROOT }; + + + class anum { + enum anum_kind { BASIC = 0, ROOT }; + void* m_cell; + public: + anum() :m_cell(nullptr) {} + anum(basic_cell* cell) :m_cell(TAG(void*, cell, BASIC)) { } + anum(algebraic_cell * cell):m_cell(TAG(void*, cell, ROOT)) { } - class anum { - friend struct manager::imp; - friend class manager; - void * m_cell; - anum(basic_cell * cell):m_cell(TAG(void*, cell, BASIC)) {} - anum(algebraic_cell * cell):m_cell(TAG(void*, cell, ROOT)) {} bool is_basic() const { return GET_TAG(m_cell) == BASIC; } basic_cell * to_basic() const { SASSERT(is_basic()); return UNTAG(basic_cell*, m_cell); } algebraic_cell * to_algebraic() const { SASSERT(!is_basic()); return UNTAG(algebraic_cell*, m_cell); } - public: - anum():m_cell(nullptr) {} + + bool is_null() const { return m_cell == nullptr; } + void clear() { m_cell = nullptr; } + void swap(anum & other) { std::swap(m_cell, other.m_cell); } + anum& operator=(basic_cell* cell) { SASSERT(is_null()); m_cell = TAG(void*, cell, BASIC); return *this; } + anum& operator=(algebraic_cell* cell) { SASSERT(is_null()); m_cell = TAG(void*, cell, ROOT); return *this; } }; }; @@ -428,6 +434,7 @@ AN_MK_BINARY(operator/, div) #undef AN_MK_BINARY #undef AN_MK_BINARY_CORE + inline scoped_anum root(scoped_anum const & a, unsigned k) { scoped_anum r(a.m()); a.m().root(a, k, r); diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index dde4a5f80..da6bc7b39 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -1285,10 +1285,7 @@ namespace polynomial { } }); monomial_table new_table; - monomial_table::iterator it = m_monomials.begin(); - monomial_table::iterator end = m_monomials.end(); - for (; it != end; ++it) { - monomial * m = *it; + for (monomial * m : m_monomials) { m->rename(sz, xs); SASSERT(!new_table.contains(m)); new_table.insert(m); @@ -1586,9 +1583,7 @@ namespace polynomial { m_i->display_smt2(out, proc); } else { - out << "(* "; m_i->display_smt2(out, proc); - out << ")"; } } else { @@ -3629,6 +3624,7 @@ namespace polynomial { unsigned counter = 0; while (true) { + (void)counter; SASSERT(degree(pp_u, x) >= degree(pp_v, x)); unsigned delta = degree(pp_u, x) - degree(pp_v, x); TRACE("polynomial_gcd_detail", @@ -4174,6 +4170,7 @@ namespace polynomial { unsigned counter = 0; for (;; counter++) { + (void) counter; while (true) { peek_fresh(interpolator.inputs(), p, val); // the selected value must satisfy lc_g(val) != 0 diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 9cef0472f..6f218934b 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -20,6 +20,7 @@ Revision History: #include "ast/ast_util.h" #include "ast/for_each_expr.h" #include "ast/recfun_decl_plugin.h" +#include "ast/polymorphism_util.h" #include "ast/rewriter/rewriter_types.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/arith_rewriter.h" @@ -171,18 +172,20 @@ struct evaluator_cfg : public default_rewriter_cfg { struct has_redex {}; struct has_redex_finder { - array_util& au; - has_redex_finder(array_util& au): au(au) {} + evaluator_cfg& ev; + has_redex_finder(evaluator_cfg& ev): ev(ev) {} void operator()(var* v) {} void operator()(quantifier* q) {} void operator()(app* a) { - if (au.is_as_array(a->get_decl())) + if (ev.m_ar.is_as_array(a->get_decl())) throw has_redex(); - if (au.get_manager().is_eq(a)) + if (ev.m_ar.get_manager().is_eq(a)) + throw has_redex(); + if (ev.m_fpau.is_fp(a)) throw has_redex(); } }; - has_redex_finder ha(m_ar); + has_redex_finder ha(*this); try { for_each_expr(ha, e); } @@ -371,7 +374,7 @@ struct evaluator_cfg : public default_rewriter_cfg { bool get_macro(func_decl * f, expr * & def, quantifier * & , proof * &) { func_interp * fi = m_model.get_func_interp(f); def = nullptr; - if (fi != nullptr) { + if (fi) { if (fi->is_partial()) { if (m_model_completion) { sort * s = f->get_range(); @@ -384,6 +387,24 @@ struct evaluator_cfg : public default_rewriter_cfg { def = fi->get_interp(); SASSERT(def != nullptr); } + else if (f->is_polymorphic() && (fi = m_model.get_func_interp(m.poly_root(f)))) { + if (fi->is_partial()) { + if (m_model_completion) { + sort * s = f->get_range(); + expr * val = m_model.get_some_value(s); + fi->set_else(val); + } + else + return false; + } + def = fi->get_interp(); + polymorphism::substitution subst(m); + polymorphism::util util(m); + util.unify(f, m.poly_root(f), subst); + def = subst(def); + SASSERT(def != nullptr); + + } else if (m_model_completion && (f->get_family_id() == null_family_id || m.get_plugin(f->get_family_id())->is_considered_uninterpreted(f))) { diff --git a/src/model/model_smt2_pp.cpp b/src/model/model_smt2_pp.cpp index b5ac3fbad..89ea98075 100644 --- a/src/model/model_smt2_pp.cpp +++ b/src/model/model_smt2_pp.cpp @@ -68,7 +68,7 @@ static void pp_uninterp_sorts(std::ostream & out, ast_printer_context & ctx, mod buffer << " "; } buffer << "\n-----------"; - std::string buffer_str = buffer.str(); + std::string buffer_str = std::move(buffer).str(); unsigned len = static_cast(buffer_str.length()); pp_indent(out, indent); out << ";; "; @@ -206,9 +206,7 @@ static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core co if (f_i->is_partial()) { body = mk_string(m, "#unspecified"); for (unsigned j = 0; j < f->get_arity(); j++) { - std::stringstream strm; - strm << "x!" << (j+1); - var_names.push_back(symbol(strm.str())); + var_names.push_back(symbol("x!" + std::to_string(j+1))); } } else { diff --git a/src/muz/rel/dl_mk_similarity_compressor.cpp b/src/muz/rel/dl_mk_similarity_compressor.cpp index ea1ef42b5..46415a0de 100644 --- a/src/muz/rel/dl_mk_similarity_compressor.cpp +++ b/src/muz/rel/dl_mk_similarity_compressor.cpp @@ -101,7 +101,7 @@ namespace datalog { /** \brief Return 0 if r1 and r2 could be similar. If the rough similarity - equaivelance class of r1 is greater than the one of r2, return 1; otherwise return -1. + equivalence class of r1 is greater than the one of r2, return 1; otherwise return -1. Two rules are in the same rough similarity class if they differ only in constant arguments of positive uninterpreted predicates. diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 800a63291..9fbd4e01c 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -209,10 +209,6 @@ namespace spacer { static proof_ref mk_th_lemma(ast_manager &m, ptr_buffer const &parents, unsigned num_params, parameter const *params) { - buffer v; - for (unsigned i = 1; i < num_params; ++i) - v.push_back(params[i]); - SASSERT(params[0].is_symbol()); family_id tid = m.mk_family_id(params[0].get_symbol()); SASSERT(tid != null_family_id); @@ -220,7 +216,7 @@ namespace spacer { proof_ref pf(m); pf = m.mk_th_lemma(tid, m.mk_false(), parents.size(), parents.data(), - v.size(), v.data()); + num_params - 1, params + 1); return pf; } diff --git a/src/nlsat/nlsat_evaluator.cpp b/src/nlsat/nlsat_evaluator.cpp index 97e0e3d72..ca6f9efae 100644 --- a/src/nlsat/nlsat_evaluator.cpp +++ b/src/nlsat/nlsat_evaluator.cpp @@ -286,23 +286,24 @@ namespace nlsat { } bool check_invariant() const { - DEBUG_CODE( - SASSERT(m_sections.size() == m_sorted_sections.size()); - for (unsigned i = 0; i < m_sorted_sections.size(); i++) { - SASSERT(m_sorted_sections[i] < m_sections.size()); - SASSERT(m_sections[m_sorted_sections[i]].m_pos == i); - } - unsigned total_num_sections = 0; - unsigned total_num_signs = 0; - for (unsigned i = 0; i < m_info.size(); i++) { - SASSERT(m_info[i].m_first_section <= m_poly_sections.size()); - SASSERT(m_info[i].m_num_roots == 0 || m_info[i].m_first_section < m_poly_sections.size()); - SASSERT(m_info[i].m_first_sign < m_poly_signs.size()); - total_num_sections += m_info[i].m_num_roots; - total_num_signs += m_info[i].m_num_roots + 1; - } - SASSERT(total_num_sections == m_poly_sections.size()); - SASSERT(total_num_signs == m_poly_signs.size());); +#ifdef Z3DEBUG + SASSERT(m_sections.size() == m_sorted_sections.size()); + for (unsigned i = 0; i < m_sorted_sections.size(); i++) { + SASSERT(m_sorted_sections[i] < m_sections.size()); + SASSERT(m_sections[m_sorted_sections[i]].m_pos == i); + } + unsigned total_num_sections = 0; + unsigned total_num_signs = 0; + for (unsigned i = 0; i < m_info.size(); i++) { + SASSERT(m_info[i].m_first_section <= m_poly_sections.size()); + SASSERT(m_info[i].m_num_roots == 0 || m_info[i].m_first_section < m_poly_sections.size()); + SASSERT(m_info[i].m_first_sign < m_poly_signs.size()); + total_num_sections += m_info[i].m_num_roots; + total_num_signs += m_info[i].m_num_roots + 1; + } + SASSERT(total_num_sections == m_poly_sections.size()); + SASSERT(total_num_signs == m_poly_signs.size()); +#endif return true; } @@ -491,7 +492,7 @@ namespace nlsat { interval_set_ref infeasible_intervals(ineq_atom * a, bool neg, clause const* cls) { sign_table & table = m_sign_table_tmp; table.reset(); - TRACE("nsat_evaluator", m_solver.display(tout, *a) << "\n";); + TRACE("nlsat_evaluator", m_solver.display(tout, *a) << "\n";); unsigned num_ps = a->size(); var x = a->max_var(); for (unsigned i = 0; i < num_ps; i++) { @@ -664,7 +665,7 @@ namespace nlsat { return result; } - interval_set_ref infeasible_intervals(atom * a, bool neg, clause const* cls) { + interval_set_ref infeasible_intervals(atom * a, bool neg, clause const* cls) { return a->is_ineq_atom() ? infeasible_intervals(to_ineq_atom(a), neg, cls) : infeasible_intervals(to_root_atom(a), neg, cls); } }; @@ -685,7 +686,7 @@ namespace nlsat { return m_imp->eval(a, neg); } - interval_set_ref evaluator::infeasible_intervals(atom * a, bool neg, clause const* cls) { + interval_set_ref evaluator::infeasible_intervals(atom * a, bool neg, clause const* cls) { return m_imp->infeasible_intervals(a, neg, cls); } diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 68a646f92..87fda76ef 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -38,7 +38,7 @@ namespace nlsat { polynomial_ref_vector m_ps; polynomial_ref_vector m_ps2; polynomial_ref_vector m_psc_tmp; - polynomial_ref_vector m_factors; + polynomial_ref_vector m_factors, m_factors_save; scoped_anum_vector m_roots_tmp; bool m_simplify_cores; bool m_full_dimensional; @@ -142,6 +142,7 @@ namespace nlsat { m_ps2(m_pm), m_psc_tmp(m_pm), m_factors(m_pm), + m_factors_save(m_pm), m_roots_tmp(m_am), m_todo(u), m_core1(s), @@ -259,22 +260,42 @@ namespace nlsat { */ ptr_vector m_zero_fs; bool_vector m_is_even; + struct restore_factors { + polynomial_ref_vector& m_factors, &m_factors_save; + unsigned num_saved = 0; + restore_factors(polynomial_ref_vector&f, polynomial_ref_vector& fs): + m_factors(f), m_factors_save(fs) + { + num_saved = m_factors_save.size(); + m_factors_save.append(m_factors); + } + + ~restore_factors() { + m_factors.reset(); + m_factors.append(m_factors_save.size() - num_saved, m_factors_save.data() + num_saved); + m_factors_save.shrink(num_saved); + } + + }; void add_zero_assumption(polynomial_ref & p) { // If p is of the form p1^n1 * ... * pk^nk, // then only the factors that are zero in the current interpretation needed to be considered. // I don't want to create a nested conjunction in the clause. // Then, I assert p_i1 * ... * p_im != 0 - factor(p, m_factors); - unsigned num_factors = m_factors.size(); - m_zero_fs.reset(); - m_is_even.reset(); - polynomial_ref f(m_pm); - for (unsigned i = 0; i < num_factors; i++) { - f = m_factors.get(i); - if (is_zero(sign(f))) { - m_zero_fs.push_back(m_factors.get(i)); - m_is_even.push_back(false); - } + { + restore_factors _restore(m_factors, m_factors_save); + factor(p, m_factors); + unsigned num_factors = m_factors.size(); + m_zero_fs.reset(); + m_is_even.reset(); + polynomial_ref f(m_pm); + for (unsigned i = 0; i < num_factors; i++) { + f = m_factors.get(i); + if (is_zero(sign(f))) { + m_zero_fs.push_back(m_factors.get(i)); + m_is_even.push_back(false); + } + } } SASSERT(!m_zero_fs.empty()); // one of the factors must be zero in the current interpretation, since p is zero in it. literal l = m_solver.mk_ineq_literal(atom::EQ, m_zero_fs.size(), m_zero_fs.data(), m_is_even.data()); @@ -582,8 +603,9 @@ namespace nlsat { if (is_const(p)) return; if (m_factor) { - TRACE("nlsat_explain", display(tout << "adding factors of\n", p); tout << "\n";); + restore_factors _restore(m_factors, m_factors_save); factor(p, m_factors); + TRACE("nlsat_explain", display(tout << "adding factors of\n", p); tout << "\n" << m_factors << "\n";); polynomial_ref f(m_pm); for (unsigned i = 0; i < m_factors.size(); i++) { f = m_factors.get(i); @@ -859,6 +881,7 @@ namespace nlsat { */ void mk_linear_root(atom::kind k, var y, unsigned i, poly * p, bool mk_neg) { + TRACE("nlsat_explain", display_var(tout, y); m_pm.display(tout << ": ", p, m_solver.display_proc()); tout << "\n"); polynomial_ref p_prime(m_pm); p_prime = p; bool lsign = false; @@ -1379,7 +1402,7 @@ namespace nlsat { var max_x = max_var(m_ps); TRACE("nlsat_explain", tout << "polynomials in the conflict:\n"; display(tout, m_ps); tout << "\n";); elim_vanishing(m_ps); - TRACE("nlsat_explain", tout << "elim vanishing\n"; display(tout, m_ps); tout << "\n";); + TRACE("nlsat_explain", tout << "elim vanishing x" << max_x << "\n"; display(tout, m_ps); tout << "\n";); project(m_ps, max_x); TRACE("nlsat_explain", tout << "after projection\n"; display(tout, m_ps); tout << "\n";); } @@ -1508,12 +1531,13 @@ namespace nlsat { m_solver.display(tout, num, ls); m_solver.display(tout);); - DEBUG_CODE( - for (unsigned i = 0; i < num; ++i) { - SASSERT(m_solver.value(ls[i]) == l_true); - atom* a = m_atoms[ls[i].var()]; - SASSERT(!a || m_evaluator.eval(a, ls[i].sign())); - }); +#ifdef Z3DEBUG + for (unsigned i = 0; i < num; ++i) { + SASSERT(m_solver.value(ls[i]) == l_true); + atom* a = m_atoms[ls[i].var()]; + SASSERT(!a || m_evaluator.eval(a, ls[i].sign())); + } +#endif split_literals(x, num, ls, lits); collect_polys(lits.size(), lits.data(), m_ps); var mx_var = max_var(m_ps); @@ -1548,13 +1572,13 @@ namespace nlsat { for (unsigned i = 0; i < result.size(); ++i) { result.set(i, ~result[i]); } - DEBUG_CODE( - TRACE("nlsat", m_solver.display(tout, result.size(), result.data()) << "\n"; ); - for (literal l : result) { - CTRACE("nlsat", l_true != m_solver.value(l), m_solver.display(tout, l) << " " << m_solver.value(l) << "\n";); - SASSERT(l_true == m_solver.value(l)); - }); - +#ifdef Z3DEBUG + TRACE("nlsat", m_solver.display(tout, result.size(), result.data()) << "\n"; ); + for (literal l : result) { + CTRACE("nlsat", l_true != m_solver.value(l), m_solver.display(tout, l) << " " << m_solver.value(l) << "\n";); + SASSERT(l_true == m_solver.value(l)); + } +#endif } void split_literals(var x, unsigned n, literal const* ls, svector& lits) { diff --git a/src/nlsat/nlsat_interval_set.cpp b/src/nlsat/nlsat_interval_set.cpp index 70b2bd02c..f928fd5b6 100644 --- a/src/nlsat/nlsat_interval_set.cpp +++ b/src/nlsat/nlsat_interval_set.cpp @@ -98,12 +98,13 @@ namespace nlsat { // Check if the intervals are valid, ordered, and are disjoint. bool check_interval_set(anum_manager & am, unsigned sz, interval const * ints) { - DEBUG_CODE( - for (unsigned i = 0; i < sz; i++) { - interval const & curr = ints[i]; - SASSERT(check_interval(am, curr)); - SASSERT(i >= sz - 1 || check_no_overlap(am, curr, ints[i+1])); - }); +#ifdef Z3DEBUG + for (unsigned i = 0; i < sz; i++) { + interval const & curr = ints[i]; + SASSERT(check_interval(am, curr)); + SASSERT(i >= sz - 1 || check_no_overlap(am, curr, ints[i+1])); + } +#endif return true; } @@ -663,9 +664,8 @@ namespace nlsat { continue; m_already_visited.setx(lidx, true, false); js.push_back(l); - if (s->m_intervals[i].m_clause) { + if (s->m_intervals[i].m_clause) clauses.push_back(const_cast(s->m_intervals[i].m_clause)); - } } for (unsigned i = 0; i < num; i++) { literal l = s->m_intervals[i].m_justification; @@ -695,12 +695,11 @@ namespace nlsat { scoped_mpq _w(m_am.qm()); m_am.qm().set(_w, num, den); m_am.set(w, _w); - return; } else { m_am.set(w, 0); - return; } + return; } unsigned n = 0; @@ -741,7 +740,7 @@ namespace nlsat { for (unsigned i = 1; i < num; i++) { if (s->m_intervals[i-1].m_upper_open && s->m_intervals[i].m_lower_open) { SASSERT(m_am.eq(s->m_intervals[i-1].m_upper, s->m_intervals[i].m_lower)); // otherwise we would have found it in the previous step - if (m_am.is_rational(s->m_intervals[i-1].m_upper)) { + if (m_am.is_rational(s->m_intervals[i-1].m_upper)) { m_am.set(w, s->m_intervals[i-1].m_upper); return; } diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 40bdd10d6..d99603dcd 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -114,10 +114,10 @@ namespace nlsat { unsigned_vector m_levels; // bool_var -> level svector m_justifications; vector m_bwatches; // bool_var (that are not attached to atoms) -> clauses where it is maximal - bool_vector m_dead; // mark dead boolean variables + bool_vector m_dead; // mark dead boolean variables id_gen m_bid_gen; - bool_vector m_is_int; // m_is_int[x] is true if variable is integer + bool_vector m_is_int; // m_is_int[x] is true if variable is integer vector m_watches; // var -> clauses where variable is maximal interval_set_vector m_infeasible; // var -> to a set of interval where the variable cannot be assigned to. atom_vector m_var2eq; // var -> to asserted equality @@ -486,7 +486,7 @@ namespace nlsat { SASSERT(x == num_vars()); m_is_int. push_back(is_int); m_watches. push_back(clause_vector()); - m_infeasible.push_back(0); + m_infeasible.push_back(nullptr); m_var2eq. push_back(nullptr); m_perm. push_back(x); m_inv_perm. push_back(x); @@ -810,19 +810,24 @@ namespace nlsat { void check_lemma(unsigned n, literal const* cls, bool is_valid, assumption_set a) { TRACE("nlsat", display(tout << "check lemma: ", n, cls) << "\n"; display(tout);); - IF_VERBOSE(0, display(verbose_stream() << "check lemma: ", n, cls) << "\n"); + IF_VERBOSE(2, display(verbose_stream() << "check lemma " << (is_valid?"valid: ":"consequence: "), n, cls) << "\n"); for (clause* c : m_learned) IF_VERBOSE(1, display(verbose_stream() << "lemma: ", *c) << "\n"); - - solver solver2(m_ctx); + scoped_suspend_rlimit _limit(m_rlimit); + ctx c(m_rlimit, m_ctx.m_params, m_ctx.m_incremental); + solver solver2(c); imp& checker = *(solver2.m_imp); checker.m_check_lemmas = false; checker.m_log_lemmas = false; checker.m_inline_vars = false; + auto pconvert = [&](poly* p) { + return convert(m_pm, p, checker.m_pm); + }; + // need to translate Boolean variables and literals scoped_bool_vars tr(checker); for (var x = 0; x < m_is_int.size(); ++x) { - checker.register_var(x, m_is_int[x]); + checker.register_var(x, is_int(x)); } bool_var bv = 0; tr.push_back(bv); @@ -834,10 +839,10 @@ namespace nlsat { else if (a->is_ineq_atom()) { ineq_atom& ia = *to_ineq_atom(a); unsigned sz = ia.size(); - ptr_vector ps; + polynomial_ref_vector ps(checker.m_pm); bool_vector is_even; for (unsigned i = 0; i < sz; ++i) { - ps.push_back(ia.p(i)); + ps.push_back(pconvert(ia.p(i))); is_even.push_back(ia.is_even(i)); } bv = checker.mk_ineq_atom(ia.get_kind(), sz, ps.data(), is_even.data()); @@ -847,7 +852,7 @@ namespace nlsat { if (r.x() >= max_var(r.p())) { // permutation may be reverted after check completes, // but then root atoms are not used in lemmas. - bv = checker.mk_root_atom(r.get_kind(), r.x(), r.i(), r.p()); + bv = checker.mk_root_atom(r.get_kind(), r.x(), r.i(), pconvert(r.p())); } } else { @@ -872,7 +877,6 @@ namespace nlsat { literal nlit(tr[lit.var()], !lit.sign()); checker.mk_clause(1, &nlit, nullptr); } - IF_VERBOSE(0, verbose_stream() << "check\n";); lbool r = checker.check(); if (r == l_true) { for (bool_var b : tr) { @@ -902,16 +906,30 @@ namespace nlsat { TRACE("nlsat", display(tout << "violdated tautology clause: ", *c) << "\n";); } } + throw default_exception("lemma did not check"); UNREACHABLE(); } } void log_lemma(std::ostream& out, clause const& cls) { - display_smt2(out); - out << "(assert (not "; - display_smt2(out, cls) << "))\n"; - display(out << "(echo \"#" << m_lemma_count << " ", cls) << "\")\n"; + log_lemma(out, cls.size(), cls.data(), false); + } + + void log_lemma(std::ostream& out, unsigned n, literal const* cls, bool is_valid) { + ++m_lemma_count; + out << "(set-logic NRA)\n"; + if (is_valid) { + display_smt2_bool_decls(out); + display_smt2_arith_decls(out); + } + else + display_smt2(out); + for (unsigned i = 0; i < n; ++i) + display_smt2(out << "(assert ", ~cls[i]) << ")\n"; + display(out << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"; out << "(check-sat)\n(reset)\n"; + + TRACE("nlsat", display(tout << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"); } clause * mk_clause_core(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { @@ -928,14 +946,13 @@ namespace nlsat { clause * mk_clause(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { SASSERT(num_lits > 0); clause * cls = mk_clause_core(num_lits, lits, learned, a); - ++m_lemma_count; TRACE("nlsat_sort", display(tout << "mk_clause:\n", *cls) << "\n";); std::sort(cls->begin(), cls->end(), lit_lt(*this)); - TRACE("nlsat_sort", display(tout << "#" << m_lemma_count << " after sort:\n", *cls) << "\n";); + TRACE("nlsat", display(tout << " after sort:\n", *cls) << "\n";); if (learned && m_log_lemmas) { log_lemma(verbose_stream(), *cls); } - if (learned && m_check_lemmas) { + if (learned && m_check_lemmas && false) { check_lemma(cls->size(), cls->data(), false, cls->assumptions()); } if (learned) @@ -993,7 +1010,8 @@ namespace nlsat { } void undo_set_updt(interval_set * old_set) { - if (m_xk == null_var) return; + if (m_xk == null_var) + return; var x = m_xk; if (x < m_infeasible.size()) { m_ism.dec_ref(m_infeasible[x]); @@ -1134,7 +1152,7 @@ namespace nlsat { \brief Assign literal using the given justification */ void assign(literal l, justification j) { - TRACE("nlsat", + TRACE("nlsat_assign", display(tout << "assigning literal: ", l); display(tout << " <- ", j);); @@ -1254,7 +1272,9 @@ namespace nlsat { m_ism.get_justifications(s, core, clauses); if (include_l) core.push_back(~l); - assign(l, mk_lazy_jst(m_allocator, core.size(), core.data(), clauses.size(), clauses.data())); + auto j = mk_lazy_jst(m_allocator, core.size(), core.data(), clauses.size(), clauses.data()); + TRACE("nlsat_resolve", display(tout, j); display_eval(tout << "evaluated:", j)); + assign(l, j); SASSERT(value(l) == l_true); } @@ -1364,7 +1384,9 @@ namespace nlsat { tmp = m_ism.mk_union(curr_set, xk_set); if (m_ism.is_full(tmp)) { TRACE("nlsat_inf_set", tout << "infeasible set + current set = R, skip literal\n"; - display(tout, cls) << "\n";); + display(tout, cls) << "\n"; + m_ism.display(tout, tmp); tout << "\n"; + ); R_propagate(~l, tmp, false); continue; } @@ -1454,7 +1476,7 @@ namespace nlsat { void select_witness() { scoped_anum w(m_am); SASSERT(!m_ism.is_full(m_infeasible[m_xk])); - m_ism.peek_in_complement(m_infeasible[m_xk], m_is_int[m_xk], w, m_randomize); + m_ism.peek_in_complement(m_infeasible[m_xk], is_int(m_xk), w, m_randomize); TRACE("nlsat", tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); @@ -1560,7 +1582,7 @@ namespace nlsat { vector> bounds; for (var x = 0; x < num_vars(); x++) { - if (m_is_int[x] && m_assignment.is_assigned(x) && !m_am.is_int(m_assignment.value(x))) { + if (is_int(x) && m_assignment.is_assigned(x) && !m_am.is_int(m_assignment.value(x))) { scoped_anum v(m_am), vlo(m_am); v = m_assignment.value(x); rational lo; @@ -1634,7 +1656,7 @@ namespace nlsat { restore_order(); } CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout);); - CTRACE("nlsat", r == l_false, display(tout);); + CTRACE("nlsat", r == l_false, display(tout << "unsat\n");); SASSERT(r != l_true || check_satisfied(m_clauses)); return r; } @@ -1805,7 +1827,7 @@ namespace nlsat { } void resolve_clause(bool_var b, clause const & c) { - TRACE("nlsat_resolve", tout << "resolving clause for b: " << b << "\n"; display(tout, c) << "\n";); + TRACE("nlsat_resolve", tout << "resolving clause "; if (b != null_bool_var) tout << "for b: " << b << "\n"; display(tout, c) << "\n";); resolve_clause(b, c.size(), c.data()); m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); } @@ -1837,11 +1859,17 @@ namespace nlsat { tout << "new valid clause:\n"; display(tout, m_lazy_clause.size(), m_lazy_clause.data()) << "\n";); + + if (m_log_lemmas) + log_lemma(verbose_stream(), m_lazy_clause.size(), m_lazy_clause.data(), true); + if (m_check_lemmas) { + check_lemma(m_lazy_clause.size(), m_lazy_clause.data(), true, nullptr); m_valids.push_back(mk_clause_core(m_lazy_clause.size(), m_lazy_clause.data(), false, nullptr)); } - DEBUG_CODE({ +#ifdef Z3DEBUG + { unsigned sz = m_lazy_clause.size(); for (unsigned i = 0; i < sz; i++) { literal l = m_lazy_clause[i]; @@ -1854,7 +1882,8 @@ namespace nlsat { SASSERT(l.sign() || m_bvalues[b] == l_true); } } - }); + } +#endif checkpoint(); resolve_clause(b, m_lazy_clause.size(), m_lazy_clause.data()); @@ -2076,9 +2105,12 @@ namespace nlsat { TRACE("nlsat", tout << "new lemma:\n"; display(tout, m_lemma.size(), m_lemma.data()); tout << "\n"; tout << "found_decision: " << found_decision << "\n";); - if (false && m_check_lemmas) { + if (m_check_lemmas) { check_lemma(m_lemma.size(), m_lemma.data(), false, m_lemma_assumptions.get()); } + + if (m_log_lemmas) + log_lemma(verbose_stream(), m_lemma.size(), m_lemma.data(), false); // There are two possibilities: // 1) m_lemma contains only literals from previous stages, and they @@ -2161,19 +2193,20 @@ namespace nlsat { // ----------------------- bool check_watches() const { - DEBUG_CODE( - for (var x = 0; x < num_vars(); x++) { +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { clause_vector const & cs = m_watches[x]; unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { SASSERT(max_var(*(cs[i])) == x); } - }); + } +#endif return true; } bool check_bwatches() const { - DEBUG_CODE( +#ifdef Z3DEBUG for (bool_var b = 0; b < m_bwatches.size(); b++) { clause_vector const & cs = m_bwatches[b]; unsigned sz = cs.size(); @@ -2182,7 +2215,8 @@ namespace nlsat { SASSERT(max_var(c) == null_var); SASSERT(max_bvar(c) == b); } - }); + } +#endif return true; } @@ -2371,13 +2405,9 @@ namespace nlsat { } bool can_reorder() const { - for (clause* c : m_learned) { - if (has_root_atom(*c)) return false; - } - for (clause* c : m_clauses) { - if (has_root_atom(*c)) return false; - } - return m_patch_var.empty(); + return m_patch_var.empty() + && all_of(m_learned, [&](clause* c) { return !has_root_atom(*c); }) + && all_of(m_clauses, [&](clause* c) { return !has_root_atom(*c); }); } /** @@ -2406,11 +2436,11 @@ namespace nlsat { // undo_until_size(0) undo_until_stage(null_var); m_cache.reset(); - DEBUG_CODE({ - for (var x = 0; x < num_vars(); x++) { - SASSERT(m_watches[x].empty()); - } - }); +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + SASSERT(m_watches[x].empty()); + } +#endif // update m_perm mapping for (unsigned ext_x = 0; ext_x < sz; ext_x++) { // p: internal -> new pos @@ -2426,12 +2456,12 @@ namespace nlsat { SASSERT(m_infeasible[x] == 0); } m_inv_perm.swap(new_inv_perm); - DEBUG_CODE({ - for (var x = 0; x < num_vars(); x++) { - SASSERT(x == m_inv_perm[m_perm[x]]); - SASSERT(m_watches[x].empty()); - } - }); +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + SASSERT(x == m_inv_perm[m_perm[x]]); + SASSERT(m_watches[x].empty()); + } +#endif m_pm.rename(sz, p); TRACE("nlsat_bool_assignment_bug", tout << "before reinit cache\n"; display_bool_assignment(tout);); reinit_cache(); @@ -2451,12 +2481,12 @@ namespace nlsat { var_vector p; p.append(m_perm); reorder(p.size(), p.data()); - DEBUG_CODE({ - for (var x = 0; x < num_vars(); x++) { - SASSERT(m_perm[x] == x); - SASSERT(m_inv_perm[x] == x); - } - }); +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + SASSERT(m_perm[x] == x); + SASSERT(m_inv_perm[x] == x); + } +#endif } /** @@ -2900,7 +2930,8 @@ namespace nlsat { var mx = max_var(p0); if (mx >= m_is_int.size()) return false; for (var x = 0; x <= mx; ++x) { - if (m_is_int[x]) continue; + if (is_int(x)) + continue; if (1 == m_pm.degree(p0, x)) { p = m_pm.coeff(p0, x, 1, q); if (!m_pm.is_const(p)) @@ -2944,10 +2975,10 @@ namespace nlsat { unsigned sz = m_atoms.size(); for (bool_var b = 0; b < sz; b++) { if (m_atoms[b] == nullptr && m_bvalues[b] != l_undef) { - out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << "\n"; + out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; } else if (m_atoms[b] != nullptr && m_bvalues[b] != l_undef) { - display(out << "b" << b << " ", *m_atoms[b]) << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << "\n"; + display(out << "b" << b << " ", *m_atoms[b]) << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; } } TRACE("nlsat_bool_assignment", @@ -3003,8 +3034,14 @@ namespace nlsat { } return out; } + + bool m_display_eval = false; + std::ostream& display_eval(std::ostream& out, justification j) { + flet _display(m_display_eval, true); + return display(out, j); + } - std::ostream& display(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { + std::ostream& display_ineq(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { unsigned sz = a.size(); for (unsigned i = 0; i < sz; i++) { if (use_star && i > 0) @@ -3012,7 +3049,7 @@ namespace nlsat { bool is_even = a.is_even(i); if (is_even || sz > 1) out << "("; - m_pm.display(out, a.p(i), proc, use_star); + display_polynomial(out, a.p(i), proc, use_star); if (is_even || sz > 1) out << ")"; if (is_even) @@ -3052,7 +3089,12 @@ namespace nlsat { return out; } - std::ostream& display_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { + std::ostream& display_polynomial_smt2(std::ostream & out, poly const* p, display_var_proc const & proc) const { + m_pm.display_smt2(out, p, proc); + return out; + } + + std::ostream& display_ineq_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { switch (a.get_kind()) { case atom::LT: out << "(< "; break; case atom::GT: out << "(> "; break; @@ -3066,13 +3108,13 @@ namespace nlsat { if (i > 0) out << " "; if (a.is_even(i)) { out << "(* "; - m_pm.display_smt2(out, a.p(i), proc); + display_polynomial_smt2(out, a.p(i), proc); out << " "; - m_pm.display_smt2(out, a.p(i), proc); + display_polynomial_smt2(out, a.p(i), proc); out << ")"; } else { - m_pm.display_smt2(out, a.p(i), proc); + display_polynomial_smt2(out, a.p(i), proc); } } if (sz > 1) @@ -3081,11 +3123,112 @@ namespace nlsat { return out; } - std::ostream& display_smt2(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { - return display(out, a, proc); + std::ostream& display_poly_root(std::ostream& out, char const* y, root_atom const& a, display_var_proc const& proc) const { + out << "(exists (("; proc(out,a.x()); out << " Real))\n"; + out << "(and (= " << y << " "; + proc(out, a.x()); + out << ") (= 0 "; + display_polynomial_smt2(out, a.p(), proc); + out << ")))\n"; + return out; + } + + std::ostream& display_binary_smt2(std::ostream& out, poly const* p1, char const* rel, poly const* p2, display_var_proc const& proc) const { + out << "(" << rel << " "; + display_polynomial_smt2(out, p1, proc); + out << " "; + display_polynomial_smt2(out, p2, proc); + out << ")"; + return out; } - std::ostream& display(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { + + std::ostream& display_linear_root_smt2(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { + polynomial_ref A(m_pm), B(m_pm), Z(m_pm), Ax(m_pm); + polynomial::scoped_numeral zero(m_qm); + m_pm.m().set(zero, 0); + A = m_pm.derivative(a.p(), a.x()); + B = m_pm.neg(m_pm.substitute(a.p(), a.x(), zero)); + Z = m_pm.mk_zero(); + + Ax = m_pm.mul(m_pm.mk_polynomial(a.x()), A); + + // x < root[1](ax + b) == (a > 0 => ax + b < 0) & (a < 0 => ax + b > 0) + // x < root[1](ax + b) == (a > 0 => ax < -b) & (a < 0 => ax > -b) + + char const* rel1 = "<", *rel2 = ">"; + switch (a.get_kind()) { + case atom::ROOT_LT: rel1 = "<"; rel2 = ">"; break; + case atom::ROOT_GT: rel1 = ">"; rel2 = "<"; break; + case atom::ROOT_LE: rel1 = "<="; rel2 = ">="; break; + case atom::ROOT_GE: rel1 = ">="; rel2 = "<="; break; + case atom::ROOT_EQ: rel1 = rel2 = "="; break; + default: UNREACHABLE(); break; + } + + out << "(and "; + out << "(=> "; display_binary_smt2(out, A, ">", Z, proc); display_binary_smt2(out, Ax, rel1, B, proc); out << ") "; + out << "(=> "; display_binary_smt2(out, A, "<", Z, proc); display_binary_smt2(out, Ax, rel2, B, proc); out << ") "; + out << ")"; + + return out; + } + + + std::ostream& display_root_smt2(std::ostream& out, root_atom const& a, display_var_proc const& proc) const { + if (a.i() == 1 && m_pm.degree(a.p(), a.x()) == 1) + return display_linear_root_smt2(out, a, proc); +#if 1 + out << "(exists ("; + for (unsigned j = 0; j < a.i(); ++j) { + std::string y = std::string("y") + std::to_string(j); + out << "(" << y << " Real) "; + } + out << ")\n"; + out << "(and\n"; + for (unsigned j = 0; j < a.i(); ++j) { + std::string y = std::string("y") + std::to_string(j); + display_poly_root(out, y.c_str(), a, proc); + } + for (unsigned j = 0; j + 1 < a.i(); ++j) { + std::string y1 = std::string("y") + std::to_string(j); + std::string y2 = std::string("y") + std::to_string(j+1); + out << "(< " << y1 << " " << y2 << ")\n"; + } + + std::string yn = "y" + std::to_string(a.i() - 1); + + // TODO we need (forall z : z < yn . p(z) => z = y1 or ... z = y_{n-1}) + // to say y1, .., yn are the first n distinct roots. + // + out << "(forall ((z Real)) (=> (and (< z " << yn << ") "; display_poly_root(out, "z", a, proc) << ") "; + if (a.i() == 1) { + out << "false))\n"; + } + else { + out << "(or "; + for (unsigned j = 0; j + 1 < a.i(); ++j) { + std::string y1 = std::string("y") + std::to_string(j); + out << "(= z " << y1 << ") "; + } + out << ")))\n"; + } + switch (a.get_kind()) { + case atom::ROOT_LT: out << "(< "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_GT: out << "(> "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_LE: out << "(<= "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_GE: out << "(>= "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_EQ: out << "(= "; proc(out, a.x()); out << " " << yn << ")"; NOT_IMPLEMENTED_YET(); break; + } + out << "))"; + return out; +#endif + + + return display_root(out, a, proc); + } + + std::ostream& display_root(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { proc(out, a.x()); switch (a.get_kind()) { case atom::ROOT_LT: out << " < "; break; @@ -3096,7 +3239,7 @@ namespace nlsat { default: UNREACHABLE(); break; } out << "root[" << a.i() << "]("; - m_pm.display(out, a.p(), proc); + display_polynomial(out, a.p(), proc); out << ")"; return out; } @@ -3124,21 +3267,16 @@ namespace nlsat { default: UNREACHABLE(); break; } out << "Root["; - m_pm.display(out, a.p(), mathematica_var_proc(a.x()), true); + display_polynomial(out, a.p(), mathematica_var_proc(a.x()), true); out << " &, " << a.i() << "]"; return out; } - - std::ostream& display_smt2(std::ostream & out, root_atom const & a) const { - NOT_IMPLEMENTED_YET(); - return out; - } std::ostream& display(std::ostream & out, atom const & a, display_var_proc const & proc) const { if (a.is_ineq_atom()) - return display(out, static_cast(a), proc); + return display_ineq(out, static_cast(a), proc); else - return display(out, static_cast(a), proc); + return display_root(out, static_cast(a), proc); } std::ostream& display(std::ostream & out, atom const & a) const { @@ -3154,9 +3292,9 @@ namespace nlsat { std::ostream& display_smt2(std::ostream & out, atom const & a, display_var_proc const & proc) const { if (a.is_ineq_atom()) - return display_smt2(out, static_cast(a), proc); + return display_ineq_smt2(out, static_cast(a), proc); else - return display_smt2(out, static_cast(a), proc); + return display_root_smt2(out, static_cast(a), proc); } std::ostream& display_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { @@ -3299,6 +3437,34 @@ namespace nlsat { return display(out, c, m_display_var); } + + std::ostream& display_polynomial(std::ostream& out, poly* p, display_var_proc const & proc, bool use_star = false) const { + if (m_display_eval) { + polynomial_ref q(m_pm); + q = p; + for (var x = 0; x < num_vars(); x++) + if (m_assignment.is_assigned(x)) { + auto& a = m_assignment.value(x); + if (!m_am.is_rational(a)) + continue; + mpq r; + m_am.to_rational(a, r); + q = m_pm.substitute(q, 1, &x, &r); + } + m_pm.display(out, q, proc, use_star); + } + else + m_pm.display(out, p, proc, use_star); + return out; + } + + // -- + + std::ostream& display_smt2(std::ostream & out, unsigned n, literal const* ls) const { + return display_smt2(out, n, ls, display_var_proc()); + } + + std::ostream& display_smt2(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { if (num == 0) { out << "false"; @@ -3455,7 +3621,7 @@ namespace nlsat { std::ostream& display_smt2_arith_decls(std::ostream & out) const { unsigned sz = m_is_int.size(); for (unsigned i = 0; i < sz; i++) { - if (m_is_int[i]) + if (is_int(i)) out << "(declare-fun x" << i << " () Int)\n"; else out << "(declare-fun x" << i << " () Real)\n"; diff --git a/src/nlsat/tactic/nlsat_tactic.cpp b/src/nlsat/tactic/nlsat_tactic.cpp index 3baf9da87..9426de78e 100644 --- a/src/nlsat/tactic/nlsat_tactic.cpp +++ b/src/nlsat/tactic/nlsat_tactic.cpp @@ -125,7 +125,9 @@ class nlsat_tactic : public tactic { continue; // don't care md->register_decl(to_app(a)->get_decl(), val == l_true ? m.mk_true() : m.mk_false()); } - DEBUG_CODE(eval_model(*md.get(), g);); +#ifdef Z3DEBUG + eval_model(*md.get(), g); +#endif // VERIFY(eval_model(*md.get(), g)); mc = model2model_converter(md.get()); return ok; diff --git a/src/opt/opt_lns.cpp b/src/opt/opt_lns.cpp index ba5a071d3..878c4a3ea 100644 --- a/src/opt/opt_lns.cpp +++ b/src/opt/opt_lns.cpp @@ -196,7 +196,7 @@ namespace opt { } unsigned lns::improve_linear(model_ref& mdl) { - scoped_bounding _scoped_bouding(*this); + scoped_bounding _scoped_bounding(*this); unsigned num_improved = 0; unsigned max_conflicts = m_max_conflicts; while (m.inc()) { diff --git a/src/params/CMakeLists.txt b/src/params/CMakeLists.txt index cdc21da97..763702caf 100644 --- a/src/params/CMakeLists.txt +++ b/src/params/CMakeLists.txt @@ -15,6 +15,7 @@ z3_add_component(params poly_rewriter_params.pyg rewriter_params.pyg seq_rewriter_params.pyg + sls_params.pyg solver_params.pyg tactic_params.pyg EXTRA_REGISTER_MODULE_HEADERS diff --git a/src/params/context_params.cpp b/src/params/context_params.cpp index fbdd90b8c..1d5d10b39 100644 --- a/src/params/context_params.cpp +++ b/src/params/context_params.cpp @@ -51,15 +51,18 @@ void context_params::set_uint(unsigned & opt, char const * param, char const * v } } -void context_params::set(char const * param, char const * value) { - std::string p = param; - unsigned n = static_cast(p.size()); - for (unsigned i = 0; i < n; i++) { +static void lower_case(std::string& p) { + for (size_t i = 0; i < p.size(); i++) { if (p[i] >= 'A' && p[i] <= 'Z') p[i] = p[i] - 'A' + 'a'; else if (p[i] == '-') p[i] = '_'; } +} + +void context_params::set(char const * param, char const * value) { + std::string p = param; + lower_case(p); if (p == "timeout") { set_uint(m_timeout, param, value); } @@ -195,5 +198,15 @@ void context_params::get_solver_params(params_ref & p, bool & proofs_enabled, bo p.set_bool("auto_config", false); } +bool context_params::is_shell_only_parameter(char const* _p) const { + std::string p(_p); + lower_case(p); + if (p == "dump_models" || p == "well_sorted_check" || + p == "model_validate" || p == "smtlib2_compliant" || + p == "stats") + return true; + + return false; +} diff --git a/src/params/context_params.h b/src/params/context_params.h index ad4cee31d..1f169a5bd 100644 --- a/src/params/context_params.h +++ b/src/params/context_params.h @@ -70,5 +70,10 @@ public: */ params_ref merge_default_params(params_ref const & p); + /** + \brief Is this a parameter that can only be set for the shell. + */ + bool is_shell_only_parameter(char const* p) const; + }; diff --git a/src/tactic/sls/sls_params.pyg b/src/params/sls_params.pyg similarity index 100% rename from src/tactic/sls/sls_params.pyg rename to src/params/sls_params.pyg diff --git a/src/qe/mbp/mbp_arith.cpp b/src/qe/mbp/mbp_arith.cpp index 91813c697..255c4f814 100644 --- a/src/qe/mbp/mbp_arith.cpp +++ b/src/qe/mbp/mbp_arith.cpp @@ -674,7 +674,7 @@ namespace mbp { id = mbo.add_var(r, a.is_int(v)); tids.insert(v, id); } - CTRACE("qe", kv.m_value.is_zero(), tout << mk_pp(v, m) << " has coefficeint 0\n";); + CTRACE("qe", kv.m_value.is_zero(), tout << mk_pp(v, m) << " has coefficient 0\n";); if (!kv.m_value.is_zero()) { coeffs.push_back(var(id, kv.m_value)); } diff --git a/src/qe/mbp/mbp_solve_plugin.cpp b/src/qe/mbp/mbp_solve_plugin.cpp index d46a09284..3820af59e 100644 --- a/src/qe/mbp/mbp_solve_plugin.cpp +++ b/src/qe/mbp/mbp_solve_plugin.cpp @@ -248,7 +248,7 @@ namespace mbp { return false; } - // returns `true` if a rewritting happened + // returns `true` if a rewriting happened bool try_int_mul_solve(expr *atom, bool is_pos, expr_ref &res) { if (!is_pos) diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index 12365b204..325f343b4 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -157,8 +157,8 @@ namespace qe { // // Partition variables into buckets. - // The var_paritions buckets covering disjoint subsets of - // the conjuncts. The remaining variables in vars are non-partioned. + // The var_partitions buckets covering disjoint subsets of + // the conjuncts. The remaining variables in vars are non-partitioned. // bool partition_vars( unsigned num_vars, diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 861c841a9..97f12238d 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -416,7 +416,7 @@ public: //don't use mbp_qel on some theories where model evaluation is //incomplete This is not a limitation of qel. Fix this either by //making mbp choices less dependent on the model evaluation methods - //or fix theory rewriters to make terms evalution complete + //or fix theory rewriters to make terms evaluation complete if (m_use_qel && !has_unsupported_th(fmls)) { bool dsub = m_dont_sub; m_dont_sub = !force_elim; diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 720614573..dc0613c59 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -1014,7 +1014,6 @@ namespace qe { case AST_APP: { app* a = to_app(e); expr_ref_vector args(m); - unsigned num_args = a->get_num_args(); bool all_visited = true; for (expr* arg : *a) { if (visited.find(arg, r)) { diff --git a/src/sat/sat_cutset.cpp b/src/sat/sat_cutset.cpp index aae9bf1ab..2d31bcf14 100644 --- a/src/sat/sat_cutset.cpp +++ b/src/sat/sat_cutset.cpp @@ -273,7 +273,7 @@ namespace sat { std::string cut::table2string(unsigned num_input, uint64_t table) { std::ostringstream strm; display_table(strm, num_input, table); - return strm.str(); + return std::move(strm).str(); } diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index d40d606d1..370bf1dea 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -64,7 +64,7 @@ def_module_params('sat', ('ddfw_search', BOOL, False, 'use ddfw local search instead of CDCL'), ('ddfw.init_clause_weight', UINT, 8, 'initial clause weight for DDFW local search'), ('ddfw.use_reward_pct', UINT, 15, 'percentage to pick highest reward variable when it has reward 0'), - ('ddfw.restart_base', UINT, 100000, 'number of flips used a starting point for hessitant restart backoff'), + ('ddfw.restart_base', UINT, 100000, 'number of flips used a starting point for hesitant restart backoff'), ('ddfw.reinit_base', UINT, 10000, 'increment basis for geometric backoff scheme of re-initialization of weights'), ('ddfw.threads', UINT, 0, 'number of ddfw threads to run in parallel with sat solver'), ('prob_search', BOOL, False, 'use probsat local search instead of CDCL'), @@ -105,7 +105,7 @@ def_module_params('sat', ('lookahead.preselect', BOOL, False, 'use pre-selection of subset of variables for branching'), ('lookahead_simplify', BOOL, False, 'use lookahead solver during simplification'), ('lookahead_scores', BOOL, False, 'extract lookahead scores. A utility that can only be used from the DIMACS front-end'), - ('lookahead.double', BOOL, True, 'enable doubld lookahead'), + ('lookahead.double', BOOL, True, 'enable double lookahead'), ('lookahead.use_learned', BOOL, False, 'use learned clauses when selecting lookahead literal'), ('lookahead_simplify.bca', BOOL, True, 'add learned binary clauses as part of lookahead simplification'), ('lookahead.global_autarky', BOOL, False, 'prefer to branch on variables that occur in clauses that are reduced'), diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index a1b88bed1..2d2962940 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -2280,7 +2280,7 @@ namespace sat { << std::setw(4) << m_stats.m_restart << mk_stat(*this) << " " << std::setw(6) << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n"; - std::string str(strm.str()); + std::string str = std::move(strm).str(); svector nums; for (size_t i = 0; i < str.size(); ++i) { while (i < str.size() && str[i] != ' ') ++i; diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index 47e3ee551..c408fbf96 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -15,6 +15,8 @@ Author: --*/ +#include "util/cancel_eh.h" +#include "util/scoped_timer.h" #include "ast/ast_util.h" #include "ast/scoped_proof.h" #include "sat/smt/euf_solver.h" @@ -43,8 +45,7 @@ namespace arith { } unsigned nv = get_num_vars(); for (unsigned v = 0; v < nv; ++v) { - auto t = get_tv(v); - auto vi = lp().external_to_column_index(v); + auto vi = lp().external_to_local(v); out << "v" << v << " "; if (is_bool(v)) { euf::enode* n = var2enode(v); @@ -55,10 +56,10 @@ namespace arith { } } else { - if (t.is_null()) + if (vi == lp::null_lpvar) out << "null"; else - out << (t.is_term() ? "t" : "j") << vi; + out << (lp().column_has_term(vi) ? "t" : "j") << vi; if (m_nla && m_nla->use_nra_model() && is_registered_var(v)) { scoped_anum an(m_nla->am()); m_nla->am().display(out << " = ", nl_value(v, an)); @@ -242,4 +243,21 @@ namespace arith { return m.mk_app(symbol(name), args.size(), args.data(), m.mk_proof_sort()); } + + bool solver::validate_conflict() { + scoped_ptr<::solver> vs = mk_smt2_solver(m, ctx.s().params(), symbol::null); + for (auto lit : m_core) + vs->assert_expr(ctx.literal2expr(lit)); + + for (auto [a, b] : m_eqs) + vs->assert_expr(m.mk_eq(a->get_expr(), b->get_expr())); + + cancel_eh eh(m.limit()); + scoped_timer timer(1000, &eh); + bool result = l_true != vs->check_sat(); + CTRACE("arith", !result, vs->display(tout)); + CTRACE("arith", !result, s().display(tout)); + SASSERT(result); + return result; + } } diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index ed49092fd..a389d13b8 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -472,7 +472,7 @@ namespace arith { bool _has_var = has_var(t); mk_enode(t); theory_var v = mk_evar(t); - + if (!_has_var) { svector vars; for (expr* n : *t) { @@ -507,11 +507,11 @@ namespace arith { } else { vi = lp().add_term(m_left_side, v); - SASSERT(lp::tv::is_term(vi)); + SASSERT(lp().column_has_term(vi)); TRACE("arith_verbose", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; - lp().print_term(lp().get_term(lp::tv::raw(vi)), tout) << "\n";); + lp().print_term(lp().get_term(vi), tout) << "\n";); } } return v; @@ -541,8 +541,6 @@ namespace arith { rational const& r = m_columns[var]; if (!r.is_zero()) { 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(); } @@ -625,9 +623,6 @@ namespace arith { return lp().external_to_local(v); } - lp::tv solver::get_tv(theory_var v) const { - return lp::tv::raw(get_lpvar(v)); - } /** \brief We must redefine this method, because theory of arithmetic contains diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 4fe153289..216829980 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -59,18 +59,10 @@ namespace arith { int64_t val = 0; lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival const& arg : term) { - auto t2 = s.lp().column2tv(arg.column()); - auto w = s.lp().local_to_external(t2.id()); + auto t2 = arg.j(); + auto w = s.lp().local_to_external(t2); val += to_numeral(arg.coeff()) * m_vars[w].m_best_value; } - if (v == 52) { - verbose_stream() << "update v" << v << " := " << val << "\n"; - for (lp::lar_term::ival const& arg : term) { - auto t2 = s.lp().column2tv(arg.column()); - auto w = s.lp().local_to_external(t2.id()); - verbose_stream() << "v" << w << " := " << m_vars[w].m_best_value << " * " << to_numeral(arg.coeff()) << "\n"; - } - } m_vars[v].m_best_value = val; } @@ -81,12 +73,12 @@ namespace arith { continue; int64_t new_value = m_vars[v].m_best_value; s.ensure_column(v); - lp::column_index vj = s.lp().to_column_index(v); - SASSERT(!vj.is_null()); - if (!s.lp().is_base(vj.index())) { + lp::lpvar vj = s.lp().external_to_local(v); + SASSERT(vj != lp::null_lpvar); + if (!s.lp().is_base(vj)) { rational new_value_(new_value, rational::i64()); lp::impq val(new_value_, rational::zero()); - s.lp().set_value_for_nbasic_column(vj.index(), val); + s.lp().set_value_for_nbasic_column(vj, val); } } @@ -460,18 +452,18 @@ namespace arith { return 0; } - void sls::add_args(sat::bool_var bv, ineq& ineq, lp::tv t, theory_var v, int64_t sign) { - if (t.is_term()) { + void sls::add_args(sat::bool_var bv, ineq& ineq, lp::lpvar t, theory_var v, int64_t sign) { + if (s.lp().column_has_term(t)) { lp::lar_term const& term = s.lp().get_term(t); m_terms.push_back({t,v}); for (lp::lar_term::ival arg : term) { - auto t2 = s.lp().column2tv(arg.column()); - auto w = s.lp().local_to_external(t2.id()); + auto t2 = arg.j(); + auto w = s.lp().local_to_external(t2); add_arg(bv, ineq, sign * to_numeral(arg.coeff()), w); } } else - add_arg(bv, ineq, sign, s.lp().local_to_external(t.id())); + add_arg(bv, ineq, sign, s.lp().local_to_external(t)); } void sls::init_bool_var(sat::bool_var bv) { @@ -480,7 +472,7 @@ namespace arith { api_bound* b = nullptr; s.m_bool_var2bound.find(bv, b); if (b) { - auto t = b->tv(); + auto t = b->column_index(); rational bound = b->get_value(); bool should_minus = false; sls::ineq_kind op; @@ -503,8 +495,8 @@ namespace arith { if (e && m.is_eq(e, l, r) && s.a.is_int_real(l)) { theory_var u = s.get_th_var(l); theory_var v = s.get_th_var(r); - lp::tv tu = s.get_tv(u); - lp::tv tv = s.get_tv(v); + lp::lpvar tu = s.get_column(u); + lp::lpvar tv = s.get_column(v); auto& ineq = new_ineq(sls::ineq_kind::EQ, 0); add_args(bv, ineq, tu, u, 1); add_args(bv, ineq, tv, v, -1); diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 09a56c84e..55d39b252 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -105,7 +105,7 @@ namespace arith { config m_config; scoped_ptr_vector m_bool_vars; vector m_vars; - svector> m_terms; + svector> m_terms; bool m_dscore_mode = false; @@ -140,7 +140,7 @@ namespace arith { void add_vars(); sls::ineq& new_ineq(ineq_kind op, int64_t const& bound); void add_arg(sat::bool_var bv, ineq& ineq, int64_t const& c, var_t v); - void add_args(sat::bool_var bv, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign); + void add_args(sat::bool_var bv, ineq& ineq, lp::lpvar j, euf::theory_var v, int64_t sign); void init_bool_var(sat::bool_var v); void init_bool_var_assignment(sat::bool_var v); diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 078515184..3086d75f4 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -370,7 +370,7 @@ namespace arith { void solver::refine_bound(theory_var v, const lp::implied_bound& be) { lpvar vi = be.m_j; - if (lp::tv::is_term(vi)) + if (lp().column_has_term(vi)) return; expr_ref w(var2expr(v), m); if (a.is_add(w) || a.is_numeral(w) || m.is_ite(w)) @@ -418,7 +418,7 @@ namespace arith { ++m_stats.m_assert_upper; inf_rational value = b.get_value(is_true); if (propagate_eqs() && value.is_rational()) - propagate_eqs(b.tv(), ci, k, b, value.get_rational()); + propagate_eqs(b.column_index(), ci, k, b, value.get_rational()); #if 0 if (propagation_mode() != BP_NONE) lp().add_column_rows_to_touched_rows(b.tv().id()); @@ -426,30 +426,29 @@ namespace arith { } - void solver::propagate_eqs(lp::tv t, lp::constraint_index ci1, lp::lconstraint_kind k, api_bound& b, rational const& value) { + void solver::propagate_eqs(lp::lpvar t, lp::constraint_index ci1, lp::lconstraint_kind k, api_bound& b, rational const& value) { u_dependency* dep; auto& dm = lp().dep_manager(); - if (k == lp::GE && set_lower_bound(t, ci1, value) && has_upper_bound(t.index(), dep, value)) { + if (k == lp::GE && set_lower_bound(t, ci1, value) && has_upper_bound(t, dep, value)) { fixed_var_eh(b.get_var(), dm.mk_join(dm.mk_leaf(ci1), dep), value); } - else if (k == lp::LE && set_upper_bound(t, ci1, value) && has_lower_bound(t.index(), dep, value)) { + else if (k == lp::LE && set_upper_bound(t, ci1, value) && has_lower_bound(t, dep, value)) { fixed_var_eh(b.get_var(), dm.mk_join(dm.mk_leaf(ci1), dep), value); } } - bool solver::set_bound(lp::tv tv, lp::constraint_index ci, rational const& v, bool is_lower) { - if (tv.is_term()) { - lpvar ti = tv.id(); + bool solver::set_bound(lp::lpvar tv, lp::constraint_index ci, rational const& v, bool is_lower) { + if (lp().column_has_term(tv)) { auto& vec = is_lower ? m_lower_terms : m_upper_terms; - if (vec.size() <= ti) { - vec.resize(ti + 1, constraint_bound(UINT_MAX, rational())); + if (vec.size() <= tv) { + vec.resize(tv + 1, constraint_bound(UINT_MAX, rational())); } - constraint_bound& b = vec[ti]; + constraint_bound& b = vec[tv]; if (b.first == UINT_MAX || (is_lower ? b.second < v : b.second > v)) { - TRACE("arith", tout << "tighter bound " << tv.to_string() << "\n";); - m_history.push_back(vec[ti]); - ctx.push(history_trail(vec, ti, m_history)); + TRACE("arith", tout << "tighter bound " << tv << "\n";); + m_history.push_back(vec[tv]); + ctx.push(history_trail(vec, tv, m_history)); b.first = ci; b.second = v; } @@ -461,10 +460,10 @@ namespace arith { rational b; u_dependency* dep = nullptr; if (is_lower) { - return lp().has_lower_bound(tv.id(), dep, b, is_strict) && !is_strict && b == v; + return lp().has_lower_bound(tv, dep, b, is_strict) && !is_strict && b == v; } else { - return lp().has_upper_bound(tv.id(), dep, b, is_strict) && !is_strict && b == v; + return lp().has_upper_bound(tv, dep, b, is_strict) && !is_strict && b == v; } } } @@ -772,7 +771,7 @@ namespace arith { bool solver::has_lower_bound(lpvar vi, u_dependency*& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } bool solver::has_bound(lpvar vi, u_dependency*& dep, rational const& bound, bool is_lower) { - if (lp::tv::is_term(vi)) { + if (lp().column_has_term(vi)) { theory_var v = lp().local_to_external(vi); rational val; TRACE("arith", tout << lp().get_variable_name(vi) << " " << v << "\n";); @@ -782,9 +781,8 @@ namespace arith { } auto& vec = is_lower ? m_lower_terms : m_upper_terms; - lpvar ti = lp::tv::unmask_term(vi); - if (vec.size() > ti) { - auto& [ci, coeff] = vec[ti]; + if (vec.size() > vi) { + auto& [ci, coeff] = vec[vi]; if (ci == UINT_MAX) return false; dep = lp().dep_manager().mk_leaf(ci); @@ -876,11 +874,16 @@ namespace arith { lp::impq solver::get_ivalue(theory_var v) const { SASSERT(is_registered_var(v)); - return m_solver->get_tv_ivalue(get_tv(v)); + return m_solver->get_column_value(get_column(v)); } + lp::lpvar solver::get_column(theory_var v) const { + SASSERT(is_registered_var(v)); + return m_solver->external_to_local(v); + } + rational solver::get_value(theory_var v) const { - return is_registered_var(v) ? m_solver->get_tv_value(get_tv(v)) : rational::zero(); + return is_registered_var(v) ? m_solver->get_value(get_column(v)) : rational::zero(); } void solver::random_update() { @@ -895,18 +898,18 @@ namespace arith { if (is_bool(v)) continue; ensure_column(v); - lp::column_index vj = lp().to_column_index(v); - SASSERT(!vj.is_null()); + lp::lpvar vj = lp().external_to_local(v); + SASSERT(vj != lp::null_lpvar); theory_var other = m_model_eqs.insert_if_not_there(v); if (is_equal(v, other)) continue; - if (!lp().is_fixed(vj)) - vars.push_back(vj.index()); + if (!lp().column_is_fixed(vj)) + vars.push_back(vj); else if (!m_tmp_var_set.contains(other)) { - lp::column_index other_j = lp().to_column_index(other); - if (!lp().is_fixed(other_j)) { + lp::lpvar other_j = lp().external_to_local(other); + if (!lp().column_is_fixed(other_j)) { m_tmp_var_set.insert(other); - vars.push_back(other_j.index()); + vars.push_back(other_j); } } } @@ -1068,14 +1071,14 @@ namespace arith { nlsat::anum const& solver::nl_value(theory_var v, scoped_anum& r) const { SASSERT(m_nla); SASSERT(m_nla->use_nra_model()); - auto t = get_tv(v); - if (!t.is_term()) { - m_nla->am().set(r, m_nla->am_value(t.id())); + auto t = get_column(v); + if (!lp().column_has_term(t)) { + m_nla->am().set(r, m_nla->am_value(t)); } else { m_todo_terms.push_back(std::make_pair(t, rational::one())); - TRACE("nl_value", tout << "v" << v << " " << t.to_string() << "\n";); - TRACE("nl_value", tout << "v" << v << " := w" << t.to_string() << "\n"; + TRACE("nl_value", tout << "v" << v << " " << t << "\n";); + TRACE("nl_value", tout << "v" << v << " := w" << t << "\n"; lp().print_term(lp().get_term(t), tout) << "\n";); m_nla->am().set(r, 0); @@ -1090,14 +1093,14 @@ namespace arith { m_nla->am().set(r1, c1.to_mpq()); m_nla->am().add(r, r1, r); for (lp::lar_term::ival arg : term) { - auto wi = lp().column2tv(arg.column()); + auto wi = arg.j(); c1 = arg.coeff() * wcoeff; - if (wi.is_term()) { + if (lp().column_has_term(wi)) { m_todo_terms.push_back(std::make_pair(wi, c1)); } else { m_nla->am().set(r1, c1.to_mpq()); - m_nla->am().mul(m_nla->am_value(wi.id()), r1, r1); + m_nla->am().mul(m_nla->am_value(wi), r1, r1); m_nla->am().add(r1, r, r); } } @@ -1251,6 +1254,9 @@ namespace arith { for (literal c : m_core) tout << c << ": " << literal2expr(c) << "\n"; for (auto p : m_eqs) tout << ctx.bpp(p.first) << " == " << ctx.bpp(p.second) << "\n";); + if (ctx.get_config().m_arith_validate) + VERIFY(validate_conflict()); + if (is_conflict) { DEBUG_CODE( for (literal c : m_core) VERIFY(s().value(c) == l_true); @@ -1390,17 +1396,17 @@ namespace arith { TRACE("arith", lp().print_term(term, tout) << "\n";); for (lp::lar_term::ival ti : term) { theory_var w; - auto tv = lp().column2tv(ti.column()); - if (tv.is_term()) { + auto tv = ti.j(); + if (lp().column_has_term(tv)) { lp::lar_term const& term1 = lp().get_term(tv); rational coeff2 = coeff * ti.coeff(); term2coeffs(term1, coeffs, coeff2); continue; } else { - w = lp().local_to_external(tv.id()); + w = lp().local_to_external(tv); SASSERT(w >= 0); - TRACE("arith", tout << (tv.id()) << ": " << w << "\n";); + TRACE("arith", tout << tv << ": " << w << "\n";); } rational c0(0); coeffs.find(w, c0); @@ -1506,7 +1512,7 @@ namespace arith { } void solver::add_lemmas() { - if (m_nla->check_feasible()) { + if (m_nla->should_check_feasible()) { auto is_sat = make_feasible(); if (l_false == is_sat) { get_infeasibility_explanation_and_set_conflict(); diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index cbf4206a9..755611474 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -38,7 +38,7 @@ namespace euf { namespace arith { typedef ptr_vector> lp_bounds; - typedef lp::var_index lpvar; + typedef lp::lpvar lpvar; typedef euf::theory_var theory_var; typedef euf::theory_id theory_id; typedef euf::enode enode; @@ -245,7 +245,7 @@ namespace arith { symbol m_farkas; std_vector m_implied_bounds; lp::lp_bound_propagator m_bp; - mutable vector> m_todo_terms; + mutable vector> m_todo_terms; // lemmas lp::explanation m_explanation; @@ -306,7 +306,7 @@ namespace arith { bool reflect(expr* n) const; lpvar get_lpvar(theory_var v) const; - lp::tv get_tv(theory_var v) const; + lp::lpvar get_column(theory_var v) const; // axioms void mk_div_axiom(expr* p, expr* q); @@ -348,7 +348,7 @@ namespace arith { iterator end, bool& found_compatible); - void propagate_eqs(lp::tv t, lp::constraint_index ci, lp::lconstraint_kind k, api_bound& b, rational const& value); + void propagate_eqs(lp::lpvar t, lp::constraint_index ci, lp::lconstraint_kind k, api_bound& b, rational const& value); void propagate_basic_bounds(unsigned qhead); void propagate_bounds_with_lp_solver(); void propagate_bound(literal lit, api_bound& b); @@ -362,9 +362,9 @@ namespace arith { api_bound* mk_var_bound(sat::literal lit, theory_var v, lp_api::bound_kind bk, rational const& bound); lp::lconstraint_kind bound2constraint_kind(bool is_int, lp_api::bound_kind bk, bool is_true); void fixed_var_eh(theory_var v1, u_dependency* dep, rational const& bound); - bool set_upper_bound(lp::tv t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, false); } - bool set_lower_bound(lp::tv t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, true); } - bool set_bound(lp::tv tv, lp::constraint_index ci, rational const& v, bool is_lower); + bool set_upper_bound(lp::lpvar t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, false); } + bool set_lower_bound(lp::lpvar t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, true); } + bool set_bound(lp::lpvar tv, lp::constraint_index ci, rational const& v, bool is_lower); typedef std::pair constraint_bound; vector m_lower_terms; @@ -483,6 +483,7 @@ namespace arith { arith_proof_hint const* explain_conflict(hint_type ty, sat::literal_vector const& core, euf::enode_pair_vector const& eqs); void explain_assumptions(lp::explanation const& e); + bool validate_conflict(); public: solver(euf::solver& ctx, theory_id id); diff --git a/src/sat/smt/array_solver.h b/src/sat/smt/array_solver.h index c63eedaca..8dc6e4e84 100644 --- a/src/sat/smt/array_solver.h +++ b/src/sat/smt/array_solver.h @@ -32,7 +32,7 @@ namespace array { typedef sat::literal literal; typedef sat::bool_var bool_var; typedef sat::literal_vector literal_vector; - typedef union_find array_union_find; + typedef union_find array_union_find; struct stats { diff --git a/src/sat/smt/bv_internalize.cpp b/src/sat/smt/bv_internalize.cpp index 99d2a34ae..f1e9e8374 100644 --- a/src/sat/smt/bv_internalize.cpp +++ b/src/sat/smt/bv_internalize.cpp @@ -191,8 +191,8 @@ namespace bv { case OP_BAND: internalize_ac(mk_and); break; case OP_BOR: internalize_ac(mk_or); break; 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_BNAND: if_unary(mk_not); internalize_bin(mk_nand); break; + case OP_BNOR: if_unary(mk_not); internalize_bin(mk_nor); 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; diff --git a/src/sat/smt/bv_solver.h b/src/sat/smt/bv_solver.h index 91e485a9f..df4e5c9c2 100644 --- a/src/sat/smt/bv_solver.h +++ b/src/sat/smt/bv_solver.h @@ -49,7 +49,7 @@ namespace bv { typedef std::pair value_sort_pair; typedef pair_hash, unsigned_hash> value_sort_pair_hash; typedef map > value2var; - typedef union_find bv_union_find; + typedef union_find bv_union_find; typedef std::pair var_pos; friend class ackerman; diff --git a/src/sat/smt/dt_solver.cpp b/src/sat/smt/dt_solver.cpp index 52c4ed953..0b3ca2a99 100644 --- a/src/sat/smt/dt_solver.cpp +++ b/src/sat/smt/dt_solver.cpp @@ -7,7 +7,7 @@ Module Name: Abstract: - Theory plugin for altegraic datatypes + Theory plugin for algebraic datatypes Author: diff --git a/src/sat/smt/dt_solver.h b/src/sat/smt/dt_solver.h index b2cbba63b..02f1300b8 100644 --- a/src/sat/smt/dt_solver.h +++ b/src/sat/smt/dt_solver.h @@ -7,7 +7,7 @@ Module Name: Abstract: - Theory plugin for altegraic datatypes + Theory plugin for algebraic datatypes Author: @@ -36,7 +36,7 @@ namespace dt { typedef sat::bool_var bool_var; typedef sat::literal literal; typedef sat::literal_vector literal_vector; - typedef union_find dt_union_find; + typedef union_find dt_union_find; struct var_data { ptr_vector m_recognizers; //!< recognizers of this equivalence class that are being watched. diff --git a/src/sat/smt/euf_model.cpp b/src/sat/smt/euf_model.cpp index 073d164be..2035e16b6 100644 --- a/src/sat/smt/euf_model.cpp +++ b/src/sat/smt/euf_model.cpp @@ -282,7 +282,7 @@ 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"; + out << "Failed to validate b" << n->bool_var() << " " << bpp(n) << " " << mdl(n->get_expr()) << "\n"; s().display(out); euf::enode_vector nodes; nodes.push_back(n); diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 834f2fd4b..0884b92bb 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -1113,7 +1113,7 @@ namespace euf { if (b != sat::null_bool_var) { r->m_bool_var2expr.setx(b, n->get_expr(), nullptr); SASSERT(r->m.is_bool(n->get_sort())); - IF_VERBOSE(11, verbose_stream() << "set bool_var " << b << " " << r->bpp(n) << " " << mk_bounded_pp(n->get_expr(), m) << "\n"); + IF_VERBOSE(20, verbose_stream() << "set bool_var " << b << " " << r->bpp(n) << " " << mk_bounded_pp(n->get_expr(), m) << "\n"); } } for (auto* s_orig : m_id2solver) { diff --git a/src/sat/smt/intblast_solver.cpp b/src/sat/smt/intblast_solver.cpp index 6877b328f..6d0b180d3 100644 --- a/src/sat/smt/intblast_solver.cpp +++ b/src/sat/smt/intblast_solver.cpp @@ -182,20 +182,21 @@ namespace intblast { translate_expr(e); } - lbool solver::check_axiom(sat::literal_vector const& lits) { + lbool solver::check_axiom(char const* name, sat::literal_vector const& lits) { sat::literal_vector core; for (auto lit : lits) core.push_back(~lit); - return check_core(core, {}); + return check_core(name, core, {}); } - lbool solver::check_propagation(sat::literal lit, sat::literal_vector const& lits, euf::enode_pair_vector const& eqs) { + + lbool solver::check_propagation(char const* name, sat::literal lit, sat::literal_vector const& lits, euf::enode_pair_vector const& eqs) { sat::literal_vector core; core.append(lits); core.push_back(~lit); - return check_core(core, eqs); + return check_core(name, core, eqs); } - lbool solver::check_core(sat::literal_vector const& lits, euf::enode_pair_vector const& eqs) { + lbool solver::check_core(char const* name, sat::literal_vector const& lits, euf::enode_pair_vector const& eqs) { m_core.reset(); m_vars.reset(); m_is_plugin = false; @@ -233,8 +234,42 @@ namespace intblast { es[i] = tmp; } +#if 0 + namespace fs = std::filesystem; + static unsigned num_check = 0; + fs::path filename = std::string("validation/int-") + std::to_string(++num_check) + ".smt2"; + fs::create_directories(filename.parent_path()); + IF_VERBOSE(1, verbose_stream() << "validation check written to file " << filename << "\n"); + std::ofstream file(filename); + std::string name_esc; + if (name) { + name_esc = name; + for (char& c : name_esc) + if (c == '|') + c = '!'; + } + else + name_esc = ""; + + file << "(set-logic ALL)\n"; + file << "(set-info :source |\n"; + file << " Name: " << name_esc << "\n"; + file << original_es << "\n|)\n"; + + m_solver->push(); + m_solver->assert_expr(es); + m_solver->display(file) << "(check-sat)\n"; + m_solver->pop(1); + + file.close(); + + // if (num_check == 68) + // std::abort(); + + r = l_false; +#else IF_VERBOSE(2, verbose_stream() << "check\n" << original_es << "\n"); - + IF_VERBOSE(2, { m_solver->push(); @@ -243,8 +278,8 @@ namespace intblast { m_solver->pop(1); }); - r = m_solver->check_sat(es); +#endif } m_solver->collect_statistics(m_stats); @@ -266,7 +301,6 @@ namespace intblast { return r; } - lbool solver::check_solver_state() { sat::literal_vector literals; diff --git a/src/sat/smt/intblast_solver.h b/src/sat/smt/intblast_solver.h index 0aceb8b2b..83ff52f11 100644 --- a/src/sat/smt/intblast_solver.h +++ b/src/sat/smt/intblast_solver.h @@ -105,9 +105,9 @@ namespace intblast { ~solver() override {} - lbool check_axiom(sat::literal_vector const& lits); - lbool check_core(sat::literal_vector const& lits, euf::enode_pair_vector const& eqs); - lbool check_propagation(sat::literal lit, sat::literal_vector const& lits, euf::enode_pair_vector const& eqs); + lbool check_axiom(char const* name, sat::literal_vector const& lits); + lbool check_core(char const* name, sat::literal_vector const& lits, euf::enode_pair_vector const& eqs); + lbool check_propagation(char const* name, sat::literal lit, sat::literal_vector const& lits, euf::enode_pair_vector const& eqs); lbool check_solver_state(); diff --git a/src/sat/smt/polysat/ule_constraint.cpp b/src/sat/smt/polysat/ule_constraint.cpp index c0c64ee2a..368dfc720 100644 --- a/src/sat/smt/polysat/ule_constraint.cpp +++ b/src/sat/smt/polysat/ule_constraint.cpp @@ -227,7 +227,6 @@ namespace polysat { lhs *= x; SASSERT(lhs.leading_coefficient().is_power_of_two()); } - TRACE("bv_verbose", tout << "simplified " << lhs << " <= " << rhs << "\n"); } // simplify_impl } @@ -262,6 +261,7 @@ namespace polysat { if (old_is_positive != is_positive || old_lhs != lhs || old_rhs != rhs) { ule_pp const old_ule(to_lbool(old_is_positive), old_lhs, old_rhs); ule_pp const new_ule(to_lbool(is_positive), lhs, rhs); + TRACE("bv", tout << "original: " << old_ule << "\nsimplified: " << new_ule << "\n"); // always-false and always-true constraints should be rewritten to 0 != 0 and 0 == 0, respectively. if (is_always_false(old_is_positive, old_lhs, old_rhs)) { SASSERT(!is_positive); diff --git a/src/sat/smt/polysat/viable.cpp b/src/sat/smt/polysat/viable.cpp index c4c4c9f10..aba3d1322 100644 --- a/src/sat/smt/polysat/viable.cpp +++ b/src/sat/smt/polysat/viable.cpp @@ -648,11 +648,17 @@ namespace polysat { verbose_stream() << "\n\n\n\n\n\nNon-viable assignment for v" << m_var << " size " << c.size(m_var) << "\n"; display_one(verbose_stream() << "entry: ", e) << "\n"; verbose_stream() << "value " << value << "\n"; + m_fixed_bits.display(verbose_stream() << "fixed: ") << "\n"; fixed_slice_extra_vector fixed; offset_slice_extra_vector subslices; c.s.get_fixed_sub_slices(m_var, fixed, subslices); // TODO: move into m_fixed bits? + // this case occurs if e-graph merges e.g. nodes "x - 2" and "3"; + // polysat will see assignment "x = 5" but no fixed bits + if (subslices.empty()) + return null_dependency; + unsigned max_level = 0; for (auto const& slice : subslices) max_level = std::max(max_level, slice.level); @@ -681,6 +687,9 @@ namespace polysat { unsigned w_level = slice.level; // level where value of w was fixed if (w == m_var) return null_dependency; + if (w == e->var) + return null_dependency; + // verbose_stream() << "v" << m_var << " size " << c.size(m_var) << ", v" << w << " size " << c.size(w) << " offset " << offset << " level " << w_level << "\n"; // Let: diff --git a/src/sat/smt/polysat_model.cpp b/src/sat/smt/polysat_model.cpp index 815f6df51..8f68ede0a 100644 --- a/src/sat/smt/polysat_model.cpp +++ b/src/sat/smt/polysat_model.cpp @@ -97,24 +97,24 @@ namespace polysat { return out; } - void solver::validate_propagate(sat::literal lit, sat::literal_vector const& core, euf::enode_pair_vector const& eqs) { + void solver::validate_propagate(char const* name, sat::literal lit, sat::literal_vector const& core, euf::enode_pair_vector const& eqs) { if (!ctx.get_config().m_core_validate) return; - auto r = m_intblast.check_propagation(lit, core, eqs); + auto r = m_intblast.check_propagation(name, lit, core, eqs); VERIFY (r != l_true); } - void solver::validate_conflict(sat::literal_vector const& core, euf::enode_pair_vector const& eqs) { + void solver::validate_conflict(char const* name, sat::literal_vector const& core, euf::enode_pair_vector const& eqs) { if (!ctx.get_config().m_core_validate) return; - auto r = m_intblast.check_core(core, eqs); + auto r = m_intblast.check_core(name, core, eqs); VERIFY (r != l_true); } - void solver::validate_axiom(sat::literal_vector const& clause) { + void solver::validate_axiom(char const* name, sat::literal_vector const& clause) { if (!ctx.get_config().m_core_validate) return; - auto r = m_intblast.check_axiom(clause); + auto r = m_intblast.check_axiom(name, clause); VERIFY (r != l_true); } diff --git a/src/sat/smt/polysat_solver.cpp b/src/sat/smt/polysat_solver.cpp index 7ae6c99ef..5a20737d0 100644 --- a/src/sat/smt/polysat_solver.cpp +++ b/src/sat/smt/polysat_solver.cpp @@ -130,7 +130,7 @@ namespace polysat { TRACE("bv", tout << "conflict: " << lits << " "; for (auto [a, b] : eqs) tout << ctx.bpp(a) << " == " << ctx.bpp(b) << " "; tout << "\n"; s().display(tout)); - validate_conflict(lits, eqs); + validate_conflict(hint_info, lits, eqs); ctx.set_conflict(ex); } @@ -264,7 +264,7 @@ namespace polysat { verbose_stream() << "contradictory propagation " << sc << " <- " << deps << "\n"; } auto ex = euf::th_explain::propagate(*this, core, eqs, lit, hint); - validate_propagate(lit, core, eqs); + validate_propagate(hint_info, lit, core, eqs); ctx.propagate(lit, ex); return dependency(lit.var()); } @@ -313,7 +313,7 @@ namespace polysat { if (ctx.use_drat() && hint_info) hint = mk_proof_hint(hint_info, core, eqs); auto ex = euf::th_explain::conflict(*this, core, eqs, hint); - validate_conflict(core, eqs); + validate_conflict(hint_info, core, eqs); ctx.set_conflict(ex); } } @@ -328,7 +328,7 @@ namespace polysat { core.pop_back(); } auto ex = euf::th_explain::propagate(*this, core, eqs, lit, hint); - validate_propagate(lit, core, eqs); + validate_propagate(hint_info, lit, core, eqs); ctx.propagate(lit, ex); } else if (sign) { @@ -341,7 +341,7 @@ namespace polysat { if (ctx.use_drat() && hint_info) hint = mk_proof_hint(hint_info, core, eqs); auto ex = euf::th_explain::conflict(*this, core, eqs, hint); - validate_conflict(core, eqs); + validate_conflict(hint_info, core, eqs); ctx.set_conflict(ex); } } @@ -385,7 +385,7 @@ namespace polysat { } TRACE("bv", display_clause(name, tout, lits)); IF_VERBOSE(1, display_clause(name, verbose_stream(), lits)); - validate_axiom(lits); + validate_axiom(name, lits); s().add_clause(lits.size(), lits.data(), sat::status::th(is_redundant, get_id(), hint)); return true; } @@ -393,7 +393,7 @@ namespace polysat { void solver::add_axiom(char const* name, sat::literal const* begin, sat::literal const* end, bool is_redundant) { ++m_stats.m_num_axioms; sat::literal_vector lits; - validate_axiom(sat::literal_vector(static_cast(end - begin), begin)); + validate_axiom(name, sat::literal_vector(static_cast(end - begin), begin)); for (auto it = begin; it != end; ++it) { auto lit = *it; if (s().value(lit) == l_true && s().lvl(lit) == 0) diff --git a/src/sat/smt/polysat_solver.h b/src/sat/smt/polysat_solver.h index 3eba74a82..1bfeb4f18 100644 --- a/src/sat/smt/polysat_solver.h +++ b/src/sat/smt/polysat_solver.h @@ -239,9 +239,9 @@ namespace polysat { void add_axiom(char const* name, sat::literal const* begin, sat::literal const* end, bool is_redundant); void equiv_axiom(char const* name, sat::literal a, sat::literal b); - void validate_propagate(sat::literal lit, sat::literal_vector const& core, euf::enode_pair_vector const& eqs); - void validate_conflict(sat::literal_vector const& core, euf::enode_pair_vector const& eqs); - void validate_axiom(sat::literal_vector const& clause); + void validate_propagate(char const* name, sat::literal lit, sat::literal_vector const& core, euf::enode_pair_vector const& eqs); + void validate_conflict(char const* name, sat::literal_vector const& core, euf::enode_pair_vector const& eqs); + void validate_axiom(char const* name, sat::literal_vector const& clause); std::ostream& display_clause(char const * name, std::ostream& out, sat::literal_vector const& lits) const; diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index aa79bab5b..76d234d8d 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -390,7 +390,7 @@ namespace q { m_qs.log_instantiation(lits, &j); euf::th_proof_hint* ph = nullptr; if (ctx.use_drat()) - ph = q_proof_hint::mk(ctx, j.m_generation, lits, j.m_clause.num_decls(), j.m_binding); + ph = q_proof_hint::mk(ctx, m_ematch, j.m_generation, lits, j.m_clause.num_decls(), j.m_binding); m_qs.add_clause(lits, ph); } diff --git a/src/sat/smt/q_ematch.h b/src/sat/smt/q_ematch.h index cbeb34679..f7de55fb8 100644 --- a/src/sat/smt/q_ematch.h +++ b/src/sat/smt/q_ematch.h @@ -90,6 +90,7 @@ namespace q { unsigned_vector m_clause_queue; euf::enode_pair_vector m_evidence; bool m_enable_propagate = true; + symbol m_ematch = symbol("ematch"); euf::enode* const* copy_nodes(clause& c, euf::enode* const* _binding); binding* tmp_binding(clause& c, app* pat, euf::enode* const* _binding); diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 539c4f943..07d4880c9 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -71,7 +71,7 @@ namespace q { for (auto const& [qlit, fml, inst, generation] : m_instantiations) { euf::solver::scoped_generation sg(ctx, generation + 1); sat::literal lit = ~ctx.mk_literal(fml); - auto* ph = ctx.use_drat()? q_proof_hint::mk(ctx, generation, ~qlit, lit, inst.size(), inst.data()) : nullptr; + auto* ph = ctx.use_drat()? q_proof_hint::mk(ctx, m_mbqi, generation, ~qlit, lit, inst.size(), inst.data()) : nullptr; m_qs.add_clause(~qlit, lit, ph); m_qs.log_instantiation(~qlit, lit); } diff --git a/src/sat/smt/q_mbi.h b/src/sat/smt/q_mbi.h index 96e3ba56f..71a15be74 100644 --- a/src/sat/smt/q_mbi.h +++ b/src/sat/smt/q_mbi.h @@ -72,6 +72,7 @@ namespace q { unsigned m_max_choose_candidates = 10; unsigned m_generation_bound = UINT_MAX; unsigned m_generation_max = UINT_MAX; + symbol m_mbqi = symbol("mbqi"); typedef std::tuple instantiation_t; vector m_instantiations; vector m_defs; diff --git a/src/sat/smt/q_solver.cpp b/src/sat/smt/q_solver.cpp index fff11898c..ed6cb643b 100644 --- a/src/sat/smt/q_solver.cpp +++ b/src/sat/smt/q_solver.cpp @@ -120,7 +120,6 @@ namespace q { } sat::literal solver::instantiate(quantifier* _q, bool negate, std::function& mk_var) { - sat::literal sk; expr_ref tmp(m); quantifier_ref q(_q, m); expr_ref_vector vars(m); @@ -364,10 +363,10 @@ namespace q { } } - q_proof_hint* q_proof_hint::mk(euf::solver& s, unsigned generation, sat::literal_vector const& lits, unsigned n, euf::enode* const* bindings) { + q_proof_hint* q_proof_hint::mk(euf::solver& s, symbol const& method, unsigned generation, sat::literal_vector const& lits, unsigned n, euf::enode* const* bindings) { SASSERT(n > 0); auto* mem = s.get_region().allocate(q_proof_hint::get_obj_size(n, lits.size())); - q_proof_hint* ph = new (mem) q_proof_hint(generation, n, lits.size()); + q_proof_hint* ph = new (mem) q_proof_hint(method, generation, n, lits.size()); for (unsigned i = 0; i < n; ++i) ph->m_bindings[i] = bindings[i]->get_expr(); for (unsigned i = 0; i < lits.size(); ++i) @@ -375,10 +374,10 @@ namespace q { return ph; } - q_proof_hint* q_proof_hint::mk(euf::solver& s, unsigned generation, sat::literal l1, sat::literal l2, unsigned n, expr* const* bindings) { + q_proof_hint* q_proof_hint::mk(euf::solver& s, symbol const& method, unsigned generation, sat::literal l1, sat::literal l2, unsigned n, expr* const* bindings) { SASSERT(n > 0); auto* mem = s.get_region().allocate(q_proof_hint::get_obj_size(n, 2)); - q_proof_hint* ph = new (mem) q_proof_hint(generation, n, 2); + q_proof_hint* ph = new (mem) q_proof_hint(method, generation, n, 2); for (unsigned i = 0; i < n; ++i) ph->m_bindings[i] = bindings[i]; ph->m_literals[0] = l1; @@ -402,6 +401,7 @@ namespace q { args.push_back(s.literal2expr(~m_literals[i])); args.push_back(binding); args.push_back(m.mk_app(symbol("gen"), 1, gens, range)); + args.push_back(m.mk_const(m_method, range)); return m.mk_app(symbol("inst"), args.size(), args.data(), range); } diff --git a/src/sat/smt/q_solver.h b/src/sat/smt/q_solver.h index d0581f852..a7220e68b 100644 --- a/src/sat/smt/q_solver.h +++ b/src/sat/smt/q_solver.h @@ -30,21 +30,23 @@ namespace euf { namespace q { struct q_proof_hint : public euf::th_proof_hint { + symbol m_method; unsigned m_generation; unsigned m_num_bindings; unsigned m_num_literals; sat::literal* m_literals; expr* m_bindings[0]; - q_proof_hint(unsigned g, unsigned b, unsigned l) { + q_proof_hint(symbol const& method, unsigned g, unsigned b, unsigned l) { + m_method = method; m_generation = g; m_num_bindings = b; m_num_literals = l; m_literals = reinterpret_cast(m_bindings + m_num_bindings); } static size_t get_obj_size(unsigned num_bindings, unsigned num_lits) { return sizeof(q_proof_hint) + num_bindings*sizeof(expr*) + num_lits*sizeof(sat::literal); } - static q_proof_hint* mk(euf::solver& s, unsigned generation, sat::literal_vector const& lits, unsigned n, euf::enode* const* bindings); - static q_proof_hint* mk(euf::solver& s, unsigned generation, sat::literal l1, sat::literal l2, unsigned n, expr* const* bindings); + static q_proof_hint* mk(euf::solver& s, symbol const& method, unsigned generation, sat::literal_vector const& lits, unsigned n, euf::enode* const* bindings); + static q_proof_hint* mk(euf::solver& s, symbol const& method, unsigned generation, sat::literal l1, sat::literal l2, unsigned n, expr* const* bindings); expr* get_hint(euf::solver& s) const override; }; diff --git a/src/sat/smt/user_solver.cpp b/src/sat/smt/user_solver.cpp index 01c1786ec..0204573dc 100644 --- a/src/sat/smt/user_solver.cpp +++ b/src/sat/smt/user_solver.cpp @@ -178,8 +178,10 @@ namespace user_solver { void solver::propagate_consequence(prop_info const& prop) { sat::literal lit = ctx.internalize(prop.m_conseq, false, false); if (s().value(lit) != l_true) { - s().assign(lit, mk_justification(m_qhead)); + auto j = mk_justification(m_qhead); + s().assign(lit, j); ++m_stats.m_num_propagations; + persist_clause(lit, j); } } @@ -188,9 +190,17 @@ namespace user_solver { } bool solver::unit_propagate() { - if (m_qhead == m_prop.size()) + if (m_qhead == m_prop.size() && m_replay_qhead == m_clauses_to_replay.size()) return false; force_push(); + + bool replayed = false; + if (m_replay_qhead < m_clauses_to_replay.size()) { + replayed = true; + ctx.push(value_trail(m_replay_qhead)); + for (; m_replay_qhead < m_clauses_to_replay.size(); ++m_replay_qhead) + replay_clause(m_clauses_to_replay.get(m_replay_qhead)); + } ctx.push(value_trail(m_qhead)); unsigned np = m_stats.m_num_propagations; for (; m_qhead < m_prop.size() && !s().inconsistent(); ++m_qhead) { @@ -200,7 +210,37 @@ namespace user_solver { else propagate_new_fixed(prop); } - return np < m_stats.m_num_propagations; + return np < m_stats.m_num_propagations || replayed; + } + + void solver::replay_clause(expr_ref_vector const& clause) { + sat::literal_vector lits; + for (expr* e : clause) + lits.push_back(ctx.mk_literal(e)); + add_clause(lits); + } + + void solver::persist_clause(sat::literal lit, sat::justification const& sj) { + if (!ctx.get_config().m_up_persist_clauses) + return; + + expr_ref_vector clause(m); + auto idx = sj.get_ext_justification_idx(); + auto& j = justification::from_index(idx); + auto const& prop = m_prop[j.m_propagation_index]; + sat::literal_vector r; + for (unsigned id : prop.m_ids) + r.append(m_id2justification[id]); + for (auto lit : r) + clause.push_back(ctx.literal2expr(~lit)); + for (auto const& [a,b] : prop.m_eqs) + clause.push_back(m.mk_not(m.mk_eq(a, b))); + clause.push_back(ctx.literal2expr(lit)); + + m_clauses_to_replay.push_back(clause); + if (m_replay_qhead + 1 < m_clauses_to_replay.size()) + std::swap(m_clauses_to_replay[m_replay_qhead], m_clauses_to_replay[m_clauses_to_replay.size()-1]); + ++m_replay_qhead; } void solver::collect_statistics(::statistics& st) const { diff --git a/src/sat/smt/user_solver.h b/src/sat/smt/user_solver.h index cd94441ea..373b046b8 100644 --- a/src/sat/smt/user_solver.h +++ b/src/sat/smt/user_solver.h @@ -77,6 +77,8 @@ namespace user_solver { stats m_stats; sat::bool_var m_next_split_var = sat::null_bool_var; lbool m_next_split_phase = l_undef; + vector m_clauses_to_replay; + unsigned m_replay_qhead = 0; struct justification { unsigned m_propagation_index { 0 }; @@ -105,6 +107,9 @@ namespace user_solver { sat::bool_var enode_to_bool(euf::enode* n, unsigned idx); + void replay_clause(expr_ref_vector const& clause); + void persist_clause(sat::literal lit, sat::justification const& j); + public: solver(euf::solver& ctx); diff --git a/src/smt/fingerprints.h b/src/smt/fingerprints.h index b1308e9b0..b1904a4ee 100644 --- a/src/smt/fingerprints.h +++ b/src/smt/fingerprints.h @@ -25,11 +25,11 @@ namespace smt { class fingerprint { protected: - void* m_data{ nullptr }; - unsigned m_data_hash{ 0 }; - expr* m_def{ nullptr }; - unsigned m_num_args{ 0 }; - enode** m_args{ nullptr }; + void* m_data = nullptr; + unsigned m_data_hash = 0; + expr* m_def = nullptr; + unsigned m_num_args = 0; + enode** m_args = nullptr; friend class fingerprint_set; fingerprint() {} diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index 3c63e2fff..ef617f724 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -51,6 +51,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_core_validate = p.core_validate(); m_logic = _p.get_sym("logic", m_logic); m_string_solver = p.string_solver(); + m_up_persist_clauses = p.up_persist_clauses(); validate_string_solver(m_string_solver); if (_p.get_bool("arith.greatest_error_pivot", false)) m_arith_pivot_strategy = arith_pivot_strategy::ARITH_PIVOT_GREATEST_ERROR; @@ -145,6 +146,7 @@ void smt_params::display(std::ostream & out) const { DISPLAY_PARAM(m_agility_factor); DISPLAY_PARAM(m_restart_agility_threshold); + DISPLAY_PARAM(m_up_persist_clauses); DISPLAY_PARAM(m_lemma_gc_strategy); DISPLAY_PARAM(m_lemma_gc_half); DISPLAY_PARAM(m_recent_lemmas_size); diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index 07b6b6095..c678b7536 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -170,6 +170,14 @@ struct smt_params : public preprocessor_params, unsigned m_old_clause_relevancy = 6; //!< Max. number of unassigned literals to be considered relevant. double m_inv_clause_decay = 1; //!< clause activity decay + // ----------------------------------- + // + // User propagator configuration + // + // ----------------------------------- + + bool m_up_persist_clauses = false; + // ----------------------------------- // // SMT-LIB (debug) pretty printer diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index f2e08aaf5..708fa87d8 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -95,12 +95,14 @@ def_module_params(module_name='smt', ('arith.rep_freq', UINT, 0, 'the report frequency, in how many iterations print the cost and other info'), ('arith.min', BOOL, False, 'minimize cost'), ('arith.print_stats', BOOL, False, 'print statistic'), + ('arith.validate', BOOL, False, 'validate lemmas generated by arithmetic solver'), ('arith.simplex_strategy', UINT, 0, 'simplex strategy for the solver'), ('arith.enable_hnf', BOOL, True, 'enable hnf (Hermite Normal Form) cuts'), ('arith.bprop_on_pivoted_rows', BOOL, True, 'propagate bounds on rows changed by the pivot operation'), ('arith.print_ext_var_names', BOOL, False, 'print external variable names'), ('pb.conflict_frequency', UINT, 1000, 'conflict frequency for Pseudo-Boolean theory'), ('pb.learn_complements', BOOL, True, 'learn complement literals for Pseudo-Boolean theory'), + ('up.persist_clauses', BOOL, True, 'replay propagated clauses below the levels they are asserted'), ('array.weak', BOOL, False, 'weak array theory'), ('array.extensional', BOOL, True, 'extensional array theory'), ('clause_proof', BOOL, False, 'record a clausal proof'), diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 9bc830dd1..fef7508f4 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -36,6 +36,7 @@ void theory_arith_params::updt_params(params_ref const & _p) { m_arith_bound_prop = static_cast(p.arith_propagation_mode()); m_arith_eager_eq_axioms = p.arith_eager_eq_axioms(); m_arith_auto_config_simplex = p.arith_auto_config_simplex(); + m_arith_validate = p.arith_validate(); m_nl_arith_propagate_linear_monomials = p.arith_nl_propagate_linear_monomials(); m_nl_arith_optimize_bounds = p.arith_nl_optimize_bounds(); m_nl_arith_cross_nested = p.arith_nl_cross_nested(); @@ -95,4 +96,5 @@ void theory_arith_params::display(std::ostream & out) const { DISPLAY_PARAM(m_nl_arith_propagate_linear_monomials); DISPLAY_PARAM(m_nl_arith_optimize_bounds); DISPLAY_PARAM(m_nl_arith_cross_nested); + DISPLAY_PARAM(m_arith_validate); } diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index f19544157..0a6b9edca 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -82,6 +82,7 @@ struct theory_arith_params { bool m_arith_adaptive_gcd = false; unsigned m_arith_propagation_threshold = UINT_MAX; + bool m_arith_validate = false; arith_pivot_strategy m_arith_pivot_strategy = arith_pivot_strategy::ARITH_PIVOT_SMALLEST; // used in diff-logic diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 9a9b92393..62c7c8ddd 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -4265,9 +4265,11 @@ namespace smt { SASSERT(num_lits == 1); expr * unit = bool_var2expr(lits[0].var()); bool unit_sign = lits[0].sign(); + while (m.is_not(unit, unit)) + unit_sign = !unit_sign; m_units_to_reassert.push_back(unit); m_units_to_reassert_sign.push_back(unit_sign); - TRACE("reassert_units", tout << "asserting #" << unit->get_id() << " " << unit_sign << " @ " << m_scope_lvl << "\n";); + TRACE("reassert_units", tout << "asserting " << mk_pp(unit, m) << " #" << unit->get_id() << " " << unit_sign << " @ " << m_scope_lvl << "\n";); } m_conflict_resolution->release_lemma_atoms(); diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 3925a4e21..57875a8bd 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -132,7 +132,7 @@ namespace smt { void context::display_literal_info(std::ostream & out, literal l) const { smt::display_compact(out, l, m_bool_var2expr.data()); - display_literal_smt2(out, l); + display_literal_smt2(out << " " << l << ": ", l); out << "relevant: " << is_relevant(bool_var2expr(l.var())) << ", val: " << get_assignment(l) << "\n"; } @@ -510,7 +510,7 @@ namespace smt { #else strm << "lemma_" << (++m_lemma_id) << ".smt2"; #endif - return strm.str(); + return std::move(strm).str(); } @@ -722,7 +722,7 @@ namespace smt { << std::setw(4) << m_stats.m_num_del_clauses << " " << std::setw(7) << mem_stat() << ")\n"; - std::string str(strm.str()); + std::string str = std::move(strm).str(); svector offsets; for (size_t i = 0; i < str.size(); ++i) { while (i < str.size() && str[i] != ' ') ++i; diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index b6d1e2f2b..2b18d9a3f 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1830,7 +1830,7 @@ namespace smt { // Case) there is a variable old_v in the var-list of n. // // Remark: This variable was moved to the var-list of n due to a add_eq. - SASSERT(th->get_enode(old_v) != n); // this varialbe is not owned by n + SASSERT(th->get_enode(old_v) != n); // this variable is not owned by n SASSERT(n->get_root()->get_th_var(th_id) != null_theory_var); // the root has also a variable in its var-list. n->replace_th_var(v, th_id); push_trail(replace_th_var_trail( n, th_id, old_v)); diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 4746040ce..75c8785a7 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -99,7 +99,7 @@ namespace smt { theory_var result = null_theory_var; numeral range; numeral new_range; - numeral small_range_thresold(1024); + numeral small_range_threshold(1024); unsigned n = 0; for (row const& row : m_rows) { theory_var v = row.get_base_var(); @@ -117,7 +117,7 @@ namespace smt { numeral const & u = upper_bound(v).get_rational(); new_range = u; new_range -= l; - if (new_range > small_range_thresold) { + if (new_range > small_range_threshold) { // } else if (result == null_theory_var || new_range < range) { @@ -503,7 +503,7 @@ namespace smt { theory_var x_i = r.get_base_var(); SASSERT(is_int(x_i)); - // The following assertion is wrong. It may be violated in mixed-real-interger problems. + // The following assertion is wrong. It may be violated in mixed-real-integer problems. // The check is_gomory_cut_target will discard rows where any variable contains infinitesimals. // SASSERT(m_value[x_i].is_rational()); // infinitesimals are not used for integer variables SASSERT(!m_value[x_i].is_int()); // the base variable is not assigned to an integer value. diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 8669a514c..f0a96ddd1 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -47,7 +47,7 @@ #include "util/scoped_timer.h" #include "util/distribution.h" -typedef lp::var_index lpvar; +typedef lp::lpvar lpvar; namespace smt { @@ -790,10 +790,6 @@ class theory_lra::imp { return v == null_theory_var ? lp::null_lpvar : lp().external_to_local(v); } - lp::tv get_tv(theory_var v) const { - return lp::tv::raw(get_lpvar(v)); - } - theory_var internalize_linearized_def(app* term, scoped_internalize_state& st) { theory_var v = mk_var(term); TRACE("arith_internalize", tout << "v" << v << " " << bpp(term) << "\n";); @@ -812,11 +808,11 @@ class theory_lra::imp { } else { vi = lp().add_term(m_left_side, v); - SASSERT(lp::tv::is_term(vi)); + SASSERT(lp().column_has_term(vi)); TRACE("arith_verbose", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; - lp().print_term(lp().get_term(lp::tv::raw(vi)), tout) << "\n";); + lp().print_term(lp().get_term(vi), tout) << "\n";); } } @@ -1432,15 +1428,15 @@ public: register_theory_var_in_lar_solver(v); } - mutable vector> m_todo_terms; - + mutable vector> m_todo_terms; + lp::impq get_ivalue(theory_var v) const { SASSERT(is_registered_var(v)); - return lp().get_tv_ivalue(get_tv(v)); + return lp().get_column_value(get_lpvar(v)); } rational get_value(theory_var v) const { - return is_registered_var(v) ? lp().get_tv_value(get_tv(v)) : rational::zero(); + return is_registered_var(v) ? lp().get_value(get_lpvar(v)) : rational::zero(); } bool m_model_is_initialized{ false }; @@ -1467,8 +1463,8 @@ public: continue; } ensure_column(v); - lp::column_index vj = lp().to_column_index(v); - SASSERT(!vj.is_null()); + lp::lpvar vj = lp().external_to_local(v); + SASSERT(vj != lp::null_lpvar); theory_var other = m_model_eqs.insert_if_not_there(v); if (other == v) { continue; @@ -1476,14 +1472,14 @@ public: enode * n2 = get_enode(other); if (n1->get_root() == n2->get_root()) continue; - if (!lp().is_fixed(vj)) { - vars.push_back(vj.index()); + if (!lp().column_is_fixed(vj)) { + vars.push_back(vj); } else if (!m_tmp_var_set.contains(other) ) { - lp::column_index other_j = lp().to_column_index(other); - if (!lp().is_fixed(other_j)) { + lp::lpvar other_j = lp().external_to_local(other); + if (!lp().column_is_fixed(other_j)) { m_tmp_var_set.insert(other); - vars.push_back(other_j.index()); + vars.push_back(other_j); } } } @@ -1794,12 +1790,12 @@ public: expr_ref t(m); expr_ref_vector ts(m); for (lp::lar_term::ival p : term) { - auto ti = lp().column2tv(p.column()); - if (ti.is_term()) { + auto ti = p.j(); + if (lp().column_has_term(ti)) { ts.push_back(multerm(p.coeff(), term2expr(lp().get_term(ti)))); } else { - ts.push_back(multerm(p.coeff(), var2expr(ti.id()))); + ts.push_back(multerm(p.coeff(), var2expr(ti))); } } if (ts.size() == 1) { @@ -1836,13 +1832,13 @@ public: lp().print_term(term, out << "bound: "); out << (upper?" <= ":" >= ") << k << "\n"; for (lp::lar_term::ival p : term) { - auto ti = lp().column2tv(p.column()); + auto ti = p.j(); out << p.coeff() << " * "; - if (ti.is_term()) { + if (lp().column_has_term(ti)) { lp().print_term(lp().get_term(ti), out) << "\n"; } else { - out << "v" << lp().local_to_external(ti.id()) << "\n"; + out << "v" << lp().local_to_external(ti) << "\n"; } } for (auto ev : ex) { @@ -2151,7 +2147,7 @@ public: } void add_lemmas() { - if (m_nla->check_feasible()) { + if (m_nla->should_check_feasible()) { auto is_sat = make_feasible(); if (l_false == is_sat) { get_infeasibility_explanation_and_set_conflict(); @@ -2324,7 +2320,7 @@ public: void refine_bound(theory_var v, const lp::implied_bound& be) { lpvar vi = be.m_j; - if (lp::tv::is_term(vi)) + if (lp().column_has_term(vi)) return; expr_ref w(get_enode(v)->get_expr(), m); if (a.is_add(w) || a.is_numeral(w) || m.is_ite(w)) @@ -2389,7 +2385,9 @@ public: literal_vector m_core2; - void assign(literal lit, literal_vector const& core, svector const& eqs, vector const& params) { + void assign(literal lit, literal_vector const& core, svector const& eqs, vector const& ps) { + if (params().m_arith_validate) + VERIFY(validate_assign(lit, core, eqs)); if (core.size() < small_lemma_size() && eqs.empty()) { m_core2.reset(); for (auto const& c : core) { @@ -2399,7 +2397,7 @@ public: justification * js = nullptr; if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx(), m_core2.size(), m_core2.data(), - params.size(), params.data()); + ps.size(), ps.data()); } ctx().mk_clause(m_core2.size(), m_core2.data(), js, CLS_TH_LEMMA, nullptr); } @@ -2408,7 +2406,7 @@ public: lit, ctx().mk_justification( ext_theory_propagation_justification( get_id(), ctx(), core.size(), core.data(), - eqs.size(), eqs.data(), lit, params.size(), params.data()))); + eqs.size(), eqs.data(), lit, ps.size(), ps.data()))); } } @@ -2745,27 +2743,27 @@ public: ++m_stats.m_bounds_propagations; } - svector m_todo_vars; + svector m_todo_vars; void add_use_lists(api_bound* b) { theory_var v = b->get_var(); lpvar vi = register_theory_var_in_lar_solver(v); - if (!lp::tv::is_term(vi)) { + if (!lp().column_has_term(vi)) { return; } - m_todo_vars.push_back(lp::tv::raw(vi)); + m_todo_vars.push_back(vi); while (!m_todo_vars.empty()) { auto ti = m_todo_vars.back(); - SASSERT(ti.is_term()); + SASSERT(lp().column_has_term(ti)); m_todo_vars.pop_back(); lp::lar_term const& term = lp().get_term(ti); for (auto p : term) { - lp::tv wi = lp().column2tv(p.column()); - if (wi.is_term()) { + lp::lpvar wi = p.j(); + if (lp().column_has_term(wi)) { m_todo_vars.push_back(wi); } else { - unsigned w = lp().local_to_external(wi.id()); + unsigned w = lp().local_to_external(wi); m_use_list.reserve(w + 1, ptr_vector()); m_use_list[w].push_back(b); } @@ -2776,22 +2774,22 @@ public: void del_use_lists(api_bound* b) { theory_var v = b->get_var(); lpvar vi = get_lpvar(v); - if (!lp::tv::is_term(vi)) { + if (!lp().column_has_term(vi)) { return; } - m_todo_vars.push_back(lp::tv::raw(vi)); + m_todo_vars.push_back(vi); while (!m_todo_vars.empty()) { auto ti = m_todo_vars.back(); - SASSERT(ti.is_term()); + SASSERT(lp().column_has_term(ti)); m_todo_vars.pop_back(); lp::lar_term const& term = lp().get_term(ti); for (auto coeff : term) { - auto wi = lp().column2tv(coeff.column()); - if (wi.is_term()) { + auto wi = coeff.j(); + if (lp().column_has_term(wi)) { m_todo_vars.push_back(wi); } else { - unsigned w = lp().local_to_external(wi.id()); + unsigned w = lp().local_to_external(wi); SASSERT(m_use_list[w].back() == b); m_use_list[w].pop_back(); } @@ -2870,20 +2868,20 @@ public: reset_evidence(); r.reset(); theory_var v = b.get_var(); - auto ti = get_tv(v); - SASSERT(ti.is_term()); + lp::lpvar ti = get_lpvar(v); + SASSERT(lp().column_has_term(ti)); lp::lar_term const& term = lp().get_term(ti); for (auto const mono : term) { - auto wi = lp().column2tv(mono.column()); + auto wi = mono.j(); u_dependency* ci = nullptr; rational value; bool is_strict; - if (wi.is_term()) { + if (lp().column_has_term(wi)) { return false; } if (mono.coeff().is_neg() == is_lub) { // -3*x ... <= lub based on lower bound for x. - if (!lp().has_lower_bound(wi.id(), ci, value, is_strict)) { + if (!lp().has_lower_bound(wi, ci, value, is_strict)) { return false; } if (is_strict) { @@ -2891,7 +2889,7 @@ public: } } else { - if (!lp().has_upper_bound(wi.id(), ci, value, is_strict)) { + if (!lp().has_upper_bound(wi, ci, value, is_strict)) { return false; } if (is_strict) { @@ -2931,7 +2929,7 @@ public: } inf_rational value = b.get_value(is_true); if (propagate_eqs() && value.is_rational()) - propagate_eqs(b.tv(), ci, k, b, value.get_rational()); + propagate_eqs(b.column_index(), ci, k, b, value.get_rational()); return true; #if 0 if (should_propagate()) @@ -2976,13 +2974,13 @@ public: vector m_lower_terms; vector m_upper_terms; - void propagate_eqs(lp::tv t, lp::constraint_index ci1, lp::lconstraint_kind k, api_bound& b, rational const& value) { + void propagate_eqs(lp::lpvar t, lp::constraint_index ci1, lp::lconstraint_kind k, api_bound& b, rational const& value) { u_dependency* ci2 = nullptr; auto pair = [&]() { return lp().dep_manager().mk_join(lp().dep_manager().mk_leaf(ci1), ci2); }; - if (k == lp::GE && set_lower_bound(t, ci1, value) && has_upper_bound(t.index(), ci2, value)) { + if (k == lp::GE && set_lower_bound(t, ci1, value) && has_upper_bound(t, ci2, value)) { fixed_var_eh(b.get_var(), t, pair(), value); } - else if (k == lp::LE && set_upper_bound(t, ci1, value) && has_lower_bound(t.index(), ci2, value)) { + else if (k == lp::LE && set_upper_bound(t, ci1, value) && has_lower_bound(t, ci2, value)) { fixed_var_eh(b.get_var(), t, pair(), value); } } @@ -2996,24 +2994,23 @@ public: bool proofs_enabled() const { return m.proofs_enabled(); } - bool set_upper_bound(lp::tv t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, false); } + bool set_upper_bound(lp::lpvar t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, false); } - bool set_lower_bound(lp::tv t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, true); } + bool set_lower_bound(lp::lpvar t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, true); } vector m_history; - bool set_bound(lp::tv tv, lp::constraint_index ci, rational const& v, bool is_lower) { - if (tv.is_term()) { - lpvar ti = tv.id(); + bool set_bound(lp::lpvar tv, lp::constraint_index ci, rational const& v, bool is_lower) { + if (lp().column_has_term(tv)) { auto& vec = is_lower ? m_lower_terms : m_upper_terms; - if (vec.size() <= ti) { - vec.resize(ti + 1, constraint_bound(UINT_MAX, rational())); + if (vec.size() <= tv) { + vec.resize(tv + 1, constraint_bound(UINT_MAX, rational())); } - constraint_bound& b = vec[ti]; + constraint_bound& b = vec[tv]; if (b.first == UINT_MAX || (is_lower? b.second < v : b.second > v)) { - TRACE("arith", tout << "tighter bound " << tv.to_string() << "\n";); - m_history.push_back(vec[ti]); - ctx().push_trail(history_trail(vec, ti, m_history)); + TRACE("arith", tout << "tighter bound " << tv << "\n";); + m_history.push_back(vec[tv]); + ctx().push_trail(history_trail(vec, tv, m_history)); b.first = ci; b.second = v; } @@ -3025,10 +3022,10 @@ public: rational b; u_dependency* dep = nullptr; if (is_lower) { - return lp().has_lower_bound(tv.id(), dep, b, is_strict) && !is_strict && b == v; + return lp().has_lower_bound(tv, dep, b, is_strict) && !is_strict && b == v; } else { - return lp().has_upper_bound(tv.id(), dep, b, is_strict) && !is_strict && b == v; + return lp().has_upper_bound(tv, dep, b, is_strict) && !is_strict && b == v; } } } @@ -3050,7 +3047,7 @@ public: bool has_lower_bound(lpvar vi, u_dependency*& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } bool has_bound(lpvar vi, u_dependency*& dep, rational const& bound, bool is_lower) { - if (lp::tv::is_term(vi)) { + if (lp().column_has_term(vi)) { theory_var v = lp().local_to_external(vi); rational val; TRACE("arith", tout << lp().get_variable_name(vi) << " " << v << "\n";); @@ -3060,9 +3057,8 @@ public: } auto& vec = is_lower ? m_lower_terms : m_upper_terms; - lpvar ti = lp::tv::unmask_term(vi); - if (vec.size() > ti) { - auto const& [ci, coeff] = vec[ti]; + if (vec.size() > vi) { + auto const& [ci, coeff] = vec[vi]; if (ci == UINT_MAX) return false; dep = lp().dep_manager().mk_leaf(ci); @@ -3138,11 +3134,12 @@ public: std::function fn = [&]() { return m.mk_eq(x->get_expr(), y->get_expr()); }; scoped_trace_stream _sts(th, fn); - // VERIFY(validate_eq(x, y)); + if (params().m_arith_validate) + VERIFY(validate_eq(x, y)); ctx().assign_eq(x, y, eq_justification(js)); } - void fixed_var_eh(theory_var v, lp::tv t, u_dependency* dep, rational const& bound) { + void fixed_var_eh(theory_var v, lp::lpvar t, u_dependency* dep, rational const& bound) { theory_var w = null_theory_var; enode* x = get_enode(v); if (m_value2var.find(bound, w)) @@ -3252,8 +3249,9 @@ public: for (auto ev : m_explanation) set_evidence(ev.ci(), m_core, m_eqs); - - // VERIFY(validate_conflict(m_core, m_eqs)); + + if (params().m_arith_validate) + VERIFY(validate_conflict(m_core, m_eqs)); if (is_conflict) { ctx().set_conflict( ctx().mk_justification( @@ -3310,14 +3308,14 @@ public: nlsat::anum const& nl_value(theory_var v, scoped_anum& r) const { SASSERT(use_nra_model()); - auto t = get_tv(v); - if (!t.is_term()) - m_nla->am().set(r, m_nla->am_value(t.id())); + auto t = get_lpvar(v); + if (!lp().column_has_term(t)) + m_nla->am().set(r, m_nla->am_value(t)); else { m_todo_terms.push_back({t, rational::one()}); - TRACE("nl_value", tout << "v" << v << " " << t.to_string() << "\n";); - TRACE("nl_value", tout << "v" << v << " := w" << t.to_string() << "\n"; + TRACE("nl_value", tout << "v" << v << " " << t << "\n";); + TRACE("nl_value", tout << "v" << v << " := w" << t << "\n"; lp().print_term(lp().get_term(t), tout) << "\n";); m_nla->am().set(r, 0); @@ -3332,14 +3330,14 @@ public: m_nla->am().set(r1, c1.to_mpq()); m_nla->am().add(r, r1, r); for (lp::lar_term::ival arg : term) { - auto wi = lp().column2tv(arg.column()); + auto wi = arg.j(); c1 = arg.coeff() * wcoeff; - if (wi.is_term()) { + if (lp().column_has_term(wi)) { m_todo_terms.push_back({wi, c1}); } else { m_nla->am().set(r1, c1.to_mpq()); - m_nla->am().mul(m_nla->am_value(wi.id()), r1, r1); + m_nla->am().mul(m_nla->am_value(wi), r1, r1); m_nla->am().add(r1, r, r); } } @@ -3495,7 +3493,8 @@ public: flet _svalid(s_validating, true); context nctx(m, ctx().get_fparams(), ctx().get_params()); add_background(nctx); - nctx.assert_expr(m.mk_not(m.mk_eq(x->get_expr(), y->get_expr()))); + expr_ref neq(m.mk_not(m.mk_eq(x->get_expr(), y->get_expr())), m); + nctx.assert_expr(neq); cancel_eh eh(m.limit()); scoped_timer timer(1000, &eh); lbool r = nctx.check(); @@ -3614,17 +3613,17 @@ public: TRACE("arith", lp().print_term(term, tout) << "\n";); for (lp::lar_term::ival ti : term) { theory_var w; - auto tv = lp().column2tv(ti.column()); - if (tv.is_term()) { + auto tv = ti.j(); + if (lp().column_has_term(tv)) { lp::lar_term const& term1 = lp().get_term(tv); rational coeff2 = coeff * ti.coeff(); term2coeffs(term1, coeffs, coeff2); continue; } else { - w = lp().local_to_external(tv.id()); + w = lp().local_to_external(tv); SASSERT(w >= 0); - TRACE("arith", tout << (tv.id()) << ": " << w << "\n";); + TRACE("arith", tout << tv << ": " << w << "\n";); } rational c0(0); coeffs.find(w, c0); @@ -3681,9 +3680,9 @@ public: } app_ref mk_obj(theory_var v) { - auto t = get_tv(v); + auto t = get_lpvar(v); bool is_int = a.is_int(get_enode(v)->get_expr()); - if (t.is_term()) { + if (lp().column_has_term(t)) { return mk_term(lp().get_term(t), is_int); } else { @@ -3739,11 +3738,10 @@ public: } unsigned nv = th.get_num_vars(); for (unsigned v = 0; v < nv; ++v) { - auto t = get_tv(v); - auto vi = lp().external_to_column_index(v); + auto vi = get_lpvar(v); if (!ctx().is_relevant(get_enode(v))) out << "irr: "; out << "v" << v << " "; - if (t.is_null()) out << "null"; else out << (t.is_term() ? "t":"j") << vi; + if (vi == lp::null_lpvar) out << "null"; else out << (lp().column_has_term(vi) ? "t":"j") << vi; if (use_nra_model() && is_registered_var(v)) m_nla->am().display(out << " = ", nl_value(v, m_nla->tmp1())); else if (can_get_value(v)) out << " = " << get_value(v); if (is_int(v)) out << ", int"; diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 718d5c65a..25c8e7195 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -1807,7 +1807,7 @@ namespace smt { bool theory_pb::resolve_conflict(card& c, literal_vector const& confl) { - TRACE("pb", display(tout, c, true); ); + TRACE("pb", display(tout << "resolve conflict\n", c, true); ); bool_var v; m_conflict_lvl = 0; @@ -1839,8 +1839,19 @@ namespace smt { literal conseq = ~confl[2]; int bound = 1; + auto clear_marks = [&]() { + while (m_num_marks > 0 && idx > 0) { + v = lits[idx].var(); + if (ctx.is_marked(v)) { + ctx.unset_mark(v); + } + --idx; + } + }; + while (m_num_marks > 0) { + TRACE("pb", tout << "conseq: " << conseq << "\n"); v = conseq.var(); int offset = get_abs_coeff(v); @@ -1850,13 +1861,7 @@ namespace smt { } SASSERT(validate_lemma()); if (offset > 1000) { - while (m_num_marks > 0 && idx > 0) { - v = lits[idx].var(); - if (ctx.is_marked(v)) { - ctx.unset_mark(v); - } - --idx; - } + clear_marks(); return false; } @@ -1884,8 +1889,11 @@ namespace smt { clause& cls = *js.get_clause(); justification* cjs = cls.get_justification(); unsigned num_lits = cls.get_num_literals(); - if (cjs && typeid(smt::unit_resolution_justification) == typeid(*cjs)) - ; + CTRACE("pb", cjs, tout << (typeid(smt::unit_resolution_justification) == typeid(*cjs)) << "\n"); + if (cjs && typeid(smt::unit_resolution_justification) == typeid(*cjs)) { + clear_marks(); + return false; + } else if (cjs && !is_proof_justification(*cjs)) { TRACE("pb", tout << "not processing justification over: " << conseq << " " << typeid(*cjs).name() << "\n";); break; @@ -1954,7 +1962,8 @@ namespace smt { while (true) { conseq = lits[idx]; v = conseq.var(); - if (ctx.is_marked(v)) break; + if (ctx.is_marked(v)) + break; SASSERT(idx > 0); --idx; } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 02c0c456b..0d16120b1 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -1244,7 +1244,7 @@ bool theory_seq::get_length(expr* e, expr_ref& len, literal_vector& lits) { /** * solve for fold/map (recursive function that depends on a sequence) - * Assumption: the Seq argument of fold/map expands into a concatentation of units + * Assumption: the Seq argument of fold/map expands into a concatenation of units * The assumption is enforced by tracking the length of the seq argument. * This is ensured in relevant_eh. * Under the assumption, evern occurrence of fold/map gets simplified by expanding @@ -1270,7 +1270,6 @@ bool theory_seq::solve_nc(unsigned idx) { expr_ref c(m); expr* a = nullptr, *b = nullptr; VERIFY(m_util.str.is_contains(n.contains(), a, b)); - literal pre, cnt, ctail, emp; lbool is_gt = ctx.get_assignment(len_gt); TRACE("seq", ctx.display_literal_smt2(tout << len_gt << " := " << is_gt << "\n", len_gt) << "\n";); diff --git a/src/smt/theory_special_relations.cpp b/src/smt/theory_special_relations.cpp index 6203df103..30eac685d 100644 --- a/src/smt/theory_special_relations.cpp +++ b/src/smt/theory_special_relations.cpp @@ -893,11 +893,17 @@ namespace smt { func_decl* memf, *nextf, *connectedf; - std::string member, next, connected_sym; - unsigned index = r.decl()->get_parameter(0).get_int(); - member = "member" + std::to_string(index); - next = "next" + std::to_string(index); - connected_sym = "connected" + std::to_string(index); + std::string member, next, connected_sym, id; + auto const& pa = r.decl()->get_parameter(0); + if (pa.is_int()) + id = std::to_string(pa.get_int()); + else if (pa.is_ast() && is_func_decl(pa.get_ast())) + id = to_func_decl(pa.get_ast())->get_name().str(); + else + throw default_exception("expected an integer or function declaration"); + member = "member" + id; + next = "next" + id; + connected_sym = "connected" + id; { sort* dom[2] = { s, listS }; recfun::promise_def mem = p.ensure_def(symbol(member), 2, dom, m.mk_bool_sort(), true); diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index 84a6799fb..93faf9e4e 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -293,7 +293,7 @@ void theory_user_propagator::pop_scope_eh(unsigned num_scopes) { } bool theory_user_propagator::can_propagate() { - return m_qhead < m_prop.size() || m_to_add_qhead < m_to_add.size(); + return m_qhead < m_prop.size() || m_to_add_qhead < m_to_add.size() || m_replay_qhead < m_clauses_to_replay.size(); } void theory_user_propagator::propagate_consequence(prop_info const& prop) { @@ -321,12 +321,10 @@ void theory_user_propagator::propagate_consequence(prop_info const& prop) { ctx.set_conflict(js); } else { -#if 1 for (auto& lit : m_lits) lit.neg(); for (auto const& [a,b] : m_eqs) m_lits.push_back(~mk_eq(a->get_expr(), b->get_expr(), false)); -#endif literal lit; if (has_quantifiers(prop.m_conseq)) { @@ -340,19 +338,20 @@ void theory_user_propagator::propagate_consequence(prop_info const& prop) { lit = mk_literal(prop.m_conseq); ctx.mark_as_relevant(lit); -#if 0 - justification* js = - ctx.mk_justification( - ext_theory_propagation_justification( - get_id(), ctx, m_lits.size(), m_lits.data(), m_eqs.size(), m_eqs.data(), lit)); - - ctx.assign(lit, js); -#endif - -#if 1 m_lits.push_back(lit); - ctx.mk_th_lemma(get_id(), m_lits); -#endif + if (ctx.get_fparams().m_up_persist_clauses) { + ctx.mk_th_axiom(get_id(), m_lits); + expr_ref_vector clause(m); + for (auto lit : m_lits) + clause.push_back(ctx.literal2expr(lit)); + m_clauses_to_replay.push_back(clause); + if (m_replay_qhead + 1 < m_clauses_to_replay.size()) + std::swap(m_clauses_to_replay[m_replay_qhead], m_clauses_to_replay[m_clauses_to_replay.size()-1]); + ++m_replay_qhead; + } + else { + ctx.mk_th_lemma(get_id(), m_lits); + } TRACE("user_propagate", ctx.display(tout);); } } @@ -363,12 +362,20 @@ void theory_user_propagator::propagate_new_fixed(prop_info const& prop) { void theory_user_propagator::propagate() { - if (m_qhead == m_prop.size() && m_to_add_qhead == m_to_add.size()) + if (m_qhead == m_prop.size() && m_to_add_qhead == m_to_add.size() && m_replay_qhead == m_clauses_to_replay.size()) return; TRACE("user_propagate", tout << "propagating queue head: " << m_qhead << " prop queue: " << m_prop.size() << "\n"); force_push(); - unsigned qhead = m_to_add_qhead; + unsigned qhead = m_replay_qhead; + if (qhead < m_clauses_to_replay.size()) { + for (; qhead < m_clauses_to_replay.size() && !ctx.inconsistent(); ++qhead) + replay_clause(m_clauses_to_replay.get(qhead)); + ctx.push_trail(value_trail(m_replay_qhead)); + m_replay_qhead = qhead; + } + + qhead = m_to_add_qhead; if (qhead < m_to_add.size()) { for (; qhead < m_to_add.size(); ++qhead) add_expr(m_to_add.get(qhead), true); @@ -391,6 +398,13 @@ void theory_user_propagator::propagate() { } +void theory_user_propagator::replay_clause(expr_ref_vector const& clause) { + m_lits.reset(); + for (expr* e : clause) + m_lits.push_back(mk_literal(e)); + ctx.mk_th_axiom(get_id(), m_lits); +} + bool theory_user_propagator::internalize_atom(app* atom, bool gate_ctx) { return internalize_term(atom); } diff --git a/src/smt/theory_user_propagator.h b/src/smt/theory_user_propagator.h index 4b365ef7a..5dbae59ed 100644 --- a/src/smt/theory_user_propagator.h +++ b/src/smt/theory_user_propagator.h @@ -86,6 +86,8 @@ namespace smt { expr* m_next_split_var = nullptr; unsigned m_next_split_idx = 0; lbool m_next_split_phase = l_undef; + vector m_clauses_to_replay; + unsigned m_replay_qhead = 0; expr* var2expr(theory_var v) { return m_var2expr.get(v); } theory_var expr2var(expr* e) { check_defined(e); return m_expr2var[e->get_id()]; } @@ -101,6 +103,8 @@ namespace smt { bool_var enode_to_bool(enode* n, unsigned bit); + void replay_clause(expr_ref_vector const& clause); + public: theory_user_propagator(context& ctx); diff --git a/src/smt/theory_utvpi.cpp b/src/smt/theory_utvpi.cpp index da2883938..2072474b0 100644 --- a/src/smt/theory_utvpi.cpp +++ b/src/smt/theory_utvpi.cpp @@ -11,7 +11,7 @@ Author: Revision History: - The implementaton is derived from theory_diff_logic. + The implementation is derived from theory_diff_logic. --*/ #include "smt/theory_utvpi.h" diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index cf2a93242..a213c679f 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -80,8 +80,10 @@ class simplifier_solver : public solver { void flatten_suffix() override { expr_mark seen; unsigned j = qhead(); + expr_ref_vector pinned(s.m); for (unsigned i = qhead(); i < qtail(); ++i) { expr* f = s.m_fmls[i].fml(), *g = nullptr; + pinned.push_back(f); if (seen.is_marked(f)) continue; seen.mark(f, true); diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index 411b8aa6e..dce1fe459 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -905,10 +905,10 @@ public: void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); - r.insert("common_patterns", CPK_BOOL, "minimize the number of auxiliary variables during CNF encoding by identifing commonly used patterns", "true"); + r.insert("common_patterns", CPK_BOOL, "minimize the number of auxiliary variables during CNF encoding by identifying commonly used patterns", "true"); r.insert("distributivity", CPK_BOOL, "minimize the number of auxiliary variables during CNF encoding by applying distributivity over unshared subformulas", "true"); r.insert("distributivity_blowup", CPK_UINT, "maximum overhead for applying distributivity during CNF encoding", "32"); - r.insert("ite_chaing", CPK_BOOL, "minimize the number of auxiliary variables during CNF encoding by identifing if-then-else chains", "true"); + r.insert("ite_chaing", CPK_BOOL, "minimize the number of auxiliary variables during CNF encoding by identifying if-then-else chains", "true"); r.insert("ite_extra", CPK_BOOL, "add redundant clauses (that improve unit propagation) when encoding if-then-else formulas", "true"); } diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index b5ea4d6c1..4d695e300 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -64,7 +64,7 @@ public: } /** - * size(), [](), update() and inconsisent() implement the abstract interface of dependent_expr_state + * size(), [](), update() and inconsistent() implement the abstract interface of dependent_expr_state */ unsigned qtail() const override { return m_goal->size(); } diff --git a/src/tactic/fd_solver/smtfd_solver.cpp b/src/tactic/fd_solver/smtfd_solver.cpp index 01370812f..4d0912fdc 100644 --- a/src/tactic/fd_solver/smtfd_solver.cpp +++ b/src/tactic/fd_solver/smtfd_solver.cpp @@ -433,7 +433,7 @@ namespace smtfd { void populate_model(model_ref& mdl, expr_ref_vector const& terms); /** - * \brief check consistency properties that can only be achived using a global analysis of terms + * \brief check consistency properties that can only be achieved using a global analysis of terms */ void global_check(expr_ref_vector const& core); diff --git a/src/tactic/goal_proof_converter.h b/src/tactic/goal_proof_converter.h index a17ff0ea1..cfe0d9709 100644 --- a/src/tactic/goal_proof_converter.h +++ b/src/tactic/goal_proof_converter.h @@ -37,7 +37,7 @@ public: } proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { - // ignore the proofs from the arguments, instead obtain the proofs fromt he subgoals. + // ignore the proofs from the arguments, instead obtain the proofs from the subgoals. SASSERT(num_source == 0); proof_converter_ref_buffer pc_buffer; for (goal_ref g : m_goals) { diff --git a/src/tactic/sls/CMakeLists.txt b/src/tactic/sls/CMakeLists.txt index 436b1742f..83599b827 100644 --- a/src/tactic/sls/CMakeLists.txt +++ b/src/tactic/sls/CMakeLists.txt @@ -1,15 +1,12 @@ z3_add_component(sls_tactic SOURCES - bvsls_opt_engine.cpp - sls_engine.cpp sls_tactic.cpp COMPONENT_DEPENDENCIES bv_tactics core_tactics normal_forms tactic - PYG_FILES - sls_params.pyg + ast_sls TACTIC_HEADERS sls_tactic.h ) diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index e631c23e9..6daadc83b 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -27,8 +27,8 @@ Notes: #include "tactic/core/nnf_tactic.h" #include "util/stopwatch.h" #include "tactic/sls/sls_tactic.h" -#include "tactic/sls/sls_params.hpp" -#include "tactic/sls/sls_engine.h" +#include "params/sls_params.hpp" +#include "ast/sls/sls_engine.h" class sls_tactic : public tactic { ast_manager & m; @@ -60,6 +60,38 @@ public: void collect_param_descrs(param_descrs & r) override { sls_params::collect_param_descrs(r); } + + void run(goal_ref const& g, model_converter_ref& mc) { + if (g->inconsistent()) { + mc = nullptr; + return; + } + + for (unsigned i = 0; i < g->size(); i++) + m_engine->assert_expr(g->form(i)); + + lbool res = m_engine->operator()(); + auto const& stats = m_engine->get_stats(); + if (res == l_true) { + report_tactic_progress("Number of flips:", stats.m_moves); + + for (unsigned i = 0; i < g->size(); i++) + if (!m_engine->get_mpz_manager().is_one(m_engine->get_value(g->form(i)))) { + verbose_stream() << "Terminated before all assertions were SAT!" << std::endl; + NOT_IMPLEMENTED_YET(); + } + + if (g->models_enabled()) { + model_ref mdl = m_engine->get_model(); + mc = model2model_converter(mdl.get()); + TRACE("sls_model", mc->display(tout);); + } + g->reset(); + } + else + mc = nullptr; + + } void operator()(goal_ref const & g, goal_ref_buffer & result) override { @@ -69,7 +101,7 @@ public: tactic_report report("sls", *g); model_converter_ref mc; - m_engine->operator()(g, mc); + run(g, mc); g->add(mc.get()); g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/ufbv/quasi_macros_tactic.h b/src/tactic/ufbv/quasi_macros_tactic.h index faa939954..ec2a45527 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.h +++ b/src/tactic/ufbv/quasi_macros_tactic.h @@ -18,7 +18,7 @@ Tactic Documentation ## Tactic quasi-macro-finder ### Short Description -dentifies and applies quasi-macros. +Identifies and applies quasi-macros. ### Long Description diff --git a/src/test/fuzzing/expr_rand.cpp b/src/test/fuzzing/expr_rand.cpp index 65d5f70c9..c0ffcc767 100644 --- a/src/test/fuzzing/expr_rand.cpp +++ b/src/test/fuzzing/expr_rand.cpp @@ -241,8 +241,7 @@ void expr_rand::initialize_bv(unsigned num_vars) { void expr_rand::initialize_array(unsigned num_vars, sort* dom, sort* rng) { family_id afid = m_manager.mk_family_id("array"); - parameter p1(dom), p2(rng); - parameter ps[2] = { p1, p2 }; + parameter ps[2] = { parameter(dom), parameter(rng) }; sort* a = m_manager.mk_sort(afid, ARRAY_SORT, 2, ps); sort* ss[3] = { a, dom, rng }; diff --git a/src/test/lp/gomory_test.h b/src/test/lp/gomory_test.h index c64c01036..9ac675d1a 100644 --- a/src/test/lp/gomory_test.h +++ b/src/test/lp/gomory_test.h @@ -189,7 +189,7 @@ struct gomory_test { void print_term(lar_term & t, std::ostream & out) { vector> row; for (auto p : t) - row.push_back(std::make_pair(p.coeff(), p.column().index())); + row.push_back(std::make_pair(p.coeff(), p.j())); print_row(out, row); } diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 088a7dac0..84077b98e 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -786,25 +786,25 @@ void test_term() { lar_solver solver; unsigned _x = 0; unsigned _y = 1; - var_index x = solver.add_named_var(_x, true, "x"); - var_index y = solver.add_named_var(_y, true, "y"); + lpvar x = solver.add_named_var(_x, true, "x"); + lpvar y = solver.add_named_var(_y, true, "y"); enable_trace("lar_solver"); enable_trace("cube"); - vector> pairs; - pairs.push_back(std::pair(mpq(2), x)); - pairs.push_back(std::pair(mpq(1), y)); + vector> pairs; + pairs.push_back(std::pair(mpq(2), x)); + pairs.push_back(std::pair(mpq(1), y)); int ti = 0; unsigned x_plus_y = solver.add_term(pairs, ti++); solver.add_var_bound(x_plus_y, lconstraint_kind::GE, mpq(5, 3)); solver.add_var_bound(x_plus_y, lconstraint_kind::LE, mpq(14, 3)); pairs.pop_back(); - pairs.push_back(std::pair(mpq(-1), y)); + pairs.push_back(std::pair(mpq(-1), y)); unsigned x_minus_y = solver.add_term(pairs, ti++); solver.add_var_bound(x_minus_y, lconstraint_kind::GE, mpq(5, 3)); solver.add_var_bound(x_minus_y, lconstraint_kind::LE, mpq(14, 3)); auto status = solver.solve(); std::cout << lp_status_to_string(status) << std::endl; - std::unordered_map model; + std::unordered_map model; if (status != lp_status::OPTIMAL) { std::cout << "non optimal" << std::endl; return; @@ -834,24 +834,24 @@ void test_term() { void test_evidence_for_total_inf_simple(argument_parser &args_parser) { lar_solver solver; - var_index x = solver.add_var(0, false); - var_index y = solver.add_var(1, false); + lpvar x = solver.add_var(0, false); + lpvar y = solver.add_var(1, false); solver.add_var_bound(x, LE, mpq(-1)); solver.add_var_bound(y, GE, mpq(0)); - vector> ls; + vector> ls; - ls.push_back(std::pair(mpq(1), x)); - ls.push_back(std::pair(mpq(1), y)); + ls.push_back(std::pair(mpq(1), x)); + ls.push_back(std::pair(mpq(1), y)); unsigned j = solver.add_term(ls, 1); solver.add_var_bound(j, GE, mpq(1)); ls.pop_back(); - ls.push_back(std::pair(-mpq(1), y)); + ls.push_back(std::pair(-mpq(1), y)); j = solver.add_term(ls, 2); solver.add_var_bound(j, GE, mpq(0)); auto status = solver.solve(); std::cout << lp_status_to_string(status) << std::endl; - std::unordered_map model; + std::unordered_map model; lp_assert(solver.get_status() == lp_status::INFEASIBLE); } void test_bound_propagation_one_small_sample1() { @@ -873,20 +873,20 @@ void test_bound_propagation_one_small_sample1() { unsigned a = ls.add_var(0, false); unsigned b = ls.add_var(1, false); unsigned c = ls.add_var(2, false); - vector> coeffs; - coeffs.push_back(std::pair(mpq(1), a)); - coeffs.push_back(std::pair(mpq(-1), c)); + vector> coeffs; + coeffs.push_back(std::pair(mpq(1), a)); + coeffs.push_back(std::pair(mpq(-1), c)); ls.add_term(coeffs, -1); coeffs.pop_back(); - coeffs.push_back(std::pair(mpq(-1), b)); + coeffs.push_back(std::pair(mpq(-1), b)); ls.add_term(coeffs, -1); coeffs.clear(); - coeffs.push_back(std::pair(mpq(1), a)); - coeffs.push_back(std::pair(mpq(-1), b)); + coeffs.push_back(std::pair(mpq(1), a)); + coeffs.push_back(std::pair(mpq(-1), b)); // ls.add_constraint(coeffs, LE, zero_of_type()); // coeffs.clear(); - // coeffs.push_back(std::pair(mpq(1), b)); - // coeffs.push_back(std::pair(mpq(-1), c)); + // coeffs.push_back(std::pair(mpq(1), b)); + // coeffs.push_back(std::pair(mpq(-1), c)); // ls.add_constraint(coeffs, LE, zero_of_type()); // vector ev; // ls.add_var_bound(a, LE, mpq(1)); @@ -942,9 +942,9 @@ void test_bound_propagation_one_row() { lar_solver ls; unsigned x0 = ls.add_var(0, false); unsigned x1 = ls.add_var(1, false); - vector> c; - c.push_back(std::pair(mpq(1), x0)); - c.push_back(std::pair(mpq(-1), x1)); + vector> c; + c.push_back(std::pair(mpq(1), x0)); + c.push_back(std::pair(mpq(-1), x1)); // todo : restore test // ls.add_constraint(c, EQ, one_of_type()); // vector ev; @@ -957,9 +957,9 @@ void test_bound_propagation_one_row_with_bounded_vars() { lar_solver ls; unsigned x0 = ls.add_var(0, false); unsigned x1 = ls.add_var(1, false); - vector> c; - c.push_back(std::pair(mpq(1), x0)); - c.push_back(std::pair(mpq(-1), x1)); + vector> c; + c.push_back(std::pair(mpq(1), x0)); + c.push_back(std::pair(mpq(-1), x1)); // todo: restore test // ls.add_constraint(c, EQ, one_of_type()); // vector ev; @@ -974,9 +974,9 @@ void test_bound_propagation_one_row_mixed() { lar_solver ls; unsigned x0 = ls.add_var(0, false); unsigned x1 = ls.add_var(1, false); - vector> c; - c.push_back(std::pair(mpq(1), x0)); - c.push_back(std::pair(mpq(-1), x1)); + vector> c; + c.push_back(std::pair(mpq(1), x0)); + c.push_back(std::pair(mpq(-1), x1)); // todo: restore test // ls.add_constraint(c, EQ, one_of_type()); // vector ev; @@ -991,16 +991,16 @@ void test_bound_propagation_two_rows() { unsigned x = ls.add_var(0, false); unsigned y = ls.add_var(1, false); unsigned z = ls.add_var(2, false); - vector> c; - c.push_back(std::pair(mpq(1), x)); - c.push_back(std::pair(mpq(2), y)); - c.push_back(std::pair(mpq(3), z)); + vector> c; + c.push_back(std::pair(mpq(1), x)); + c.push_back(std::pair(mpq(2), y)); + c.push_back(std::pair(mpq(3), z)); // todo: restore test // ls.add_constraint(c, GE, one_of_type()); // c.clear(); - // c.push_back(std::pair(mpq(3), x)); - // c.push_back(std::pair(mpq(2), y)); - // c.push_back(std::pair(mpq(y), z)); + // c.push_back(std::pair(mpq(3), x)); + // c.push_back(std::pair(mpq(2), y)); + // c.push_back(std::pair(mpq(y), z)); // ls.add_constraint(c, GE, one_of_type()); // ls.add_var_bound(x, LE, mpq(2)); // vector ev; @@ -1016,10 +1016,10 @@ void test_total_case_u() { unsigned x = ls.add_var(0, false); unsigned y = ls.add_var(1, false); unsigned z = ls.add_var(2, false); - vector> c; - c.push_back(std::pair(mpq(1), x)); - c.push_back(std::pair(mpq(2), y)); - c.push_back(std::pair(mpq(3), z)); + vector> c; + c.push_back(std::pair(mpq(1), x)); + c.push_back(std::pair(mpq(2), y)); + c.push_back(std::pair(mpq(3), z)); // todo: restore test // ls.add_constraint(c, LE, one_of_type()); // ls.add_var_bound(x, GE, zero_of_type()); @@ -1044,10 +1044,10 @@ void test_total_case_l() { unsigned x = ls.add_var(0, false); unsigned y = ls.add_var(1, false); unsigned z = ls.add_var(2, false); - vector> c; - c.push_back(std::pair(mpq(1), x)); - c.push_back(std::pair(mpq(2), y)); - c.push_back(std::pair(mpq(3), z)); + vector> c; + c.push_back(std::pair(mpq(1), x)); + c.push_back(std::pair(mpq(2), y)); + c.push_back(std::pair(mpq(3), z)); // todo: restore test // ls.add_constraint(c, GE, one_of_type()); // ls.add_var_bound(x, LE, one_of_type()); @@ -1611,15 +1611,15 @@ void test_maximize_term() { int_solver i_solver(solver); // have to create it too unsigned _x = 0; unsigned _y = 1; - var_index x = solver.add_var(_x, false); - var_index y = solver.add_var(_y, true); - vector> term_ls; - term_ls.push_back(std::pair(mpq(1), x)); - term_ls.push_back(std::pair(mpq(-1), y)); + lpvar x = solver.add_var(_x, false); + lpvar y = solver.add_var(_y, true); + vector> term_ls; + term_ls.push_back(std::pair(mpq(1), x)); + term_ls.push_back(std::pair(mpq(-1), y)); unsigned term_x_min_y = solver.add_term(term_ls, -1); term_ls.clear(); - term_ls.push_back(std::pair(mpq(2), x)); - term_ls.push_back(std::pair(mpq(2), y)); + term_ls.push_back(std::pair(mpq(2), x)); + term_ls.push_back(std::pair(mpq(2), y)); unsigned term_2x_pl_2y = solver.add_term(term_ls, -1); solver.add_var_bound(term_x_min_y, LE, zero_of_type()); @@ -1627,7 +1627,7 @@ void test_maximize_term() { solver.find_feasible_solution(); lp_assert(solver.get_status() == lp_status::OPTIMAL); std::cout << solver.constraints(); - std::unordered_map model; + std::unordered_map model; solver.get_model(model); for (auto p : model) { std::cout << "v[" << p.first << "] = " << p.second << std::endl; @@ -1918,4 +1918,4 @@ void test_patching() { test_patching_alpha(rational(x1, x2), rational(a1, a2)); } -} \ No newline at end of file +} diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 7843d5714..0e638399f 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -28,7 +28,7 @@ Revision History: #include #include #include -#include "math/lp/ul_pair.h" +#include "math/lp/column.h" #include "math/lp/lar_constraints.h" #include #include @@ -343,7 +343,7 @@ namespace lp { solver->add_constraint(&c); } - void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { + void create_equality_constraint_for_var(column* col, bound * b, lar_solver *solver) { lar_constraint c(EQ, b->m_fixed_value); var_index i = solver->add_var(col->m_name); c.add_variable_to_constraint(i, numeric_traits::one()); @@ -366,7 +366,7 @@ namespace lp { create_upper_constraint_for_var(col, b, solver); } if (b->m_value_is_fixed) { - create_equality_contraint_for_var(col, b, solver); + create_equality_constraint_for_var(col, b, solver); } } } @@ -383,7 +383,7 @@ namespace lp { } void add_constraint_to_solver(lar_solver * solver, formula_constraint & fc, unsigned i) { - vector> ls; + vector> ls; for (auto & it : fc.m_coeffs) { ls.push_back(std::make_pair(it.first, solver->add_var(register_name(it.second), false))); } diff --git a/src/test/mpz.cpp b/src/test/mpz.cpp index 694f13e96..2c76b3a66 100644 --- a/src/test/mpz.cpp +++ b/src/test/mpz.cpp @@ -322,7 +322,7 @@ void tst_scoped() { #define NUM_PRIMES 168 unsigned g_primes[NUM_PRIMES] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997 }; -// Return a big number by multipling powers of the first NUM_PRIMES. +// Return a big number by multiplying powers of the first NUM_PRIMES. // - ratio: rand() % ratio == 0 is used to decide whether a specific prime will be included or not. // - max_pw: if condition above is satisfied, then we use (rand() % max_pw) + 1 as the power. void mk_big_num(unsynch_mpz_manager & m, unsigned ratio, unsigned max_pw, mpz & r) { diff --git a/src/test/pdd.cpp b/src/test/pdd.cpp index 0c9b0f85c..740ae4f2b 100644 --- a/src/test/pdd.cpp +++ b/src/test/pdd.cpp @@ -153,12 +153,49 @@ public: pdd b = m.mk_var(1); pdd c = m.mk_var(2); pdd d = m.mk_var(3); - pdd p = (a + b)*(c + 3*d) + 2; - std::cout << p << "\n"; - for (auto const& m : p) { - std::cout << m << "\n"; - } + + auto const check = [](unsigned const expected_num_monomials, pdd const& p) { + unsigned count = 0; + std::cout << p << "\n"; + for (auto const& m : p) { + std::cout << " " << m << "\n"; + ++count; + } + VERIFY_EQ(expected_num_monomials, count); + }; + + check(9, (a + b + 2)*(c + 3*d + 5) + 2); + check(5, (a + b)*(c + 3*d) + 2); + check(1, a); + check(2, a + 5); + check(1, m.mk_val(5)); + check(0, m.mk_val(0)); } + + static void linear_iterator() { + std::cout << "test linear iterator\n"; + pdd_manager m(4); + pdd a = m.mk_var(0); + pdd b = m.mk_var(1); + pdd c = m.mk_var(2); + pdd d = m.mk_var(3); + pdd p = (a + b + 2)*(c + 3*d + 5) + 2; + std::cout << p << "\n"; + for (auto const& m : p.linear_monomials()) + std::cout << " " << m << "\n"; + std::cout << a << "\n"; + for (auto const& m : a.linear_monomials()) + std::cout << " " << m << "\n"; + pdd one = m.mk_val(5); + std::cout << one << "\n"; + for (auto const& m : one.linear_monomials()) + std::cout << " " << m << "\n"; + pdd zero = m.mk_val(0); + std::cout << zero << "\n"; + for (auto const& m : zero.linear_monomials()) + std::cout << " " << m << "\n"; + } + static void order() { std::cout << "order\n"; pdd_manager m(4); @@ -693,6 +730,7 @@ void tst_pdd() { dd::test::canonize(); dd::test::reset(); dd::test::iterator(); + dd::test::linear_iterator(); dd::test::order(); dd::test::order_lm(); dd::test::mod4_operations(); diff --git a/src/test/rational.cpp b/src/test/rational.cpp index 711958de5..8fe565c62 100644 --- a/src/test/rational.cpp +++ b/src/test/rational.cpp @@ -466,6 +466,28 @@ static void tst12() { std::cout << i << ": " << r.get_bit(i) << "\n"; } +static void tst13() { + std::cout << "test13\n"; + rational const step = rational(1) / rational(3); + for (rational r; r < 5000; r += step) { + { + unsigned k = r.prev_power_of_two(); + if (r >= 1) { + VERIFY(rational::power_of_two(k) <= r); + VERIFY(r < rational::power_of_two(k + 1)); + } + else { + VERIFY_EQ(k, 0); + } + } + { + unsigned k = r.next_power_of_two(); + VERIFY(r <= rational::power_of_two(k)); + VERIFY(k == 0 || rational::power_of_two(k - 1) < r); + } + } +} + void tst_rational() { TRACE("rational", tout << "starting rational test...\n";); @@ -492,4 +514,5 @@ void tst_rational() { tst10(true); tst10(false); tst12(); + tst13(); } diff --git a/src/util/chashtable.h b/src/util/chashtable.h index 450d2bfa4..b15d6017f 100644 --- a/src/util/chashtable.h +++ b/src/util/chashtable.h @@ -161,8 +161,12 @@ protected: unsigned curr_cellar = (m_capacity - m_slots); unsigned new_slots = m_slots * 2; unsigned new_cellar = curr_cellar * 2; + if (new_slots < m_slots || new_cellar < curr_cellar) + throw default_exception("table overflow"); while (true) { unsigned new_capacity = new_slots + new_cellar; + if (new_capacity < new_slots) + throw default_exception("table overflow"); cell * new_table = alloc_table(new_capacity); cell * next_cell = copy_table(m_table, m_slots, m_capacity, new_table, new_slots, new_capacity, @@ -179,6 +183,8 @@ protected: return; } dealloc_vect(new_table, new_capacity); + if (2*new_cellar < new_cellar) + throw default_exception("table overflow"); new_cellar *= 2; } } diff --git a/src/util/dlist.h b/src/util/dlist.h index 07aefa97e..4c0e51e58 100644 --- a/src/util/dlist.h +++ b/src/util/dlist.h @@ -188,14 +188,14 @@ class dll_iterator { dll_iterator(T const* elem, bool first): m_elem(elem), m_first(first) { } public: - static dll_iterator mk_begin(T const* elem) { - // Setting first==(bool)elem makes this also work for elem==nullptr; + static dll_iterator mk_begin(T const* list) { + // Setting first==(bool)list makes this also work for list==nullptr; // but we can't implement top-level begin/end for pointers because it clashes with the definition for arrays. - return {elem, (bool)elem}; + return {list, (bool)list}; } - static dll_iterator mk_end(T const* elem) { - return {elem, false}; + static dll_iterator mk_end(T const* list) { + return {list, false}; } using value_type = T; @@ -232,18 +232,17 @@ public: dll_iterator end() const { return dll_iterator::mk_end(m_list); } }; - template < typename T , typename U = std::enable_if_t, T>> // should only match if T actually inherits from dll_base > -dll_iterator begin(T const& elem) { - return dll_iterator::mk_begin(&elem); +dll_iterator begin(T const& list) { + return dll_iterator::mk_begin(&list); } template < typename T , typename U = std::enable_if_t, T>> // should only match if T actually inherits from dll_base > -dll_iterator end(T const& elem) +dll_iterator end(T const& list) { - return dll_iterator::mk_end(&elem); + return dll_iterator::mk_end(&list); } diff --git a/src/util/mpn.cpp b/src/util/mpn.cpp index bc9017726..e84c15d5e 100644 --- a/src/util/mpn.cpp +++ b/src/util/mpn.cpp @@ -21,24 +21,20 @@ Revision History: #include "util/buffer.h" #include "util/mpn.h" -#define max(a,b) (((a) > (b)) ? (a) : (b)) - typedef uint64_t mpn_double_digit; static_assert(sizeof(mpn_double_digit) == 2 * sizeof(mpn_digit), "size alignment"); -const mpn_digit mpn_manager::zero = 0; - int mpn_manager::compare(mpn_digit const * a, unsigned lnga, mpn_digit const * b, unsigned lngb) const { int res = 0; trace(a, lnga); - unsigned j = max(lnga, lngb); + unsigned j = std::max(lnga, lngb); for (; j-- > 0 && res == 0;) { - mpn_digit const & u_j = (j < lnga) ? a[j] : zero; - mpn_digit const & v_j = (j < lngb) ? b[j] : zero; - if (u_j > v_j) + mpn_digit u_j = (j < lnga) ? a[j] : 0; + mpn_digit v_j = (j < lngb) ? b[j] : 0; + if (u_j > v_j) res = 1; else if (u_j < v_j) res = -1; @@ -56,14 +52,14 @@ bool mpn_manager::add(mpn_digit const * a, unsigned lnga, unsigned * plngc) const { trace(a, lnga, b, lngb, "+"); // Essentially Knuth's Algorithm A - unsigned len = max(lnga, lngb); + unsigned len = std::max(lnga, lngb); SASSERT(lngc_alloc == len+1 && len > 0); mpn_digit k = 0; mpn_digit r; bool c1, c2; for (unsigned j = 0; j < len; j++) { - mpn_digit const & u_j = (j < lnga) ? a[j] : zero; - mpn_digit const & v_j = (j < lngb) ? b[j] : zero; + mpn_digit u_j = (j < lnga) ? a[j] : 0; + mpn_digit v_j = (j < lngb) ? b[j] : 0; r = u_j + v_j; c1 = r < u_j; c[j] = r + k; c2 = c[j] < r; k = c1 | c2; @@ -81,13 +77,13 @@ bool mpn_manager::sub(mpn_digit const * a, unsigned lnga, mpn_digit * c, mpn_digit * pborrow) const { trace(a, lnga, b, lngb, "-"); // Essentially Knuth's Algorithm S - unsigned len = max(lnga, lngb); + unsigned len = std::max(lnga, lngb); mpn_digit & k = *pborrow; k = 0; mpn_digit r; bool c1, c2; for (unsigned j = 0; j < len; j++) { - mpn_digit const & u_j = (j < lnga) ? a[j] : zero; - mpn_digit const & v_j = (j < lngb) ? b[j] : zero; + mpn_digit u_j = (j < lnga) ? a[j] : 0; + mpn_digit v_j = (j < lngb) ? b[j] : 0; r = u_j - v_j; c1 = r > u_j; c[j] = r - k; c2 = c[j] > r; k = c1 | c2; @@ -112,14 +108,14 @@ bool mpn_manager::mul(mpn_digit const * a, unsigned lnga, c[i] = 0; for (unsigned j = 0; j < lngb; j++) { - mpn_digit const & v_j = b[j]; + mpn_digit v_j = b[j]; if (v_j == 0) { // This branch may be omitted according to Knuth. c[j+lnga] = 0; } else { k = 0; for (i = 0; i < lnga; i++) { - mpn_digit const & u_i = a[i]; + mpn_digit u_i = a[i]; mpn_double_digit t; t = ((mpn_double_digit)u_i * (mpn_double_digit)v_j) + (mpn_double_digit) c[i+j] + @@ -156,15 +152,6 @@ bool mpn_manager::div(mpn_digit const * numer, unsigned lnum, return false; } - bool all_zero = true; - for (unsigned i = 0; i < lden && all_zero; i++) - if (denom[i] != zero) all_zero = false; - - if (all_zero) { - UNREACHABLE(); - return res; - } - SASSERT(denom[lden-1] != 0); if (lnum == 1 && lden == 1) { diff --git a/src/util/mpn.h b/src/util/mpn.h index 7cf3eafb6..285edd091 100644 --- a/src/util/mpn.h +++ b/src/util/mpn.h @@ -53,7 +53,6 @@ public: private: using mpn_sbuffer = sbuffer; - static const mpn_digit zero; void display_raw(std::ostream & out, mpn_digit const * a, unsigned lng) const; unsigned div_normalize(mpn_digit const * numer, unsigned lnum, diff --git a/src/util/mpq.h b/src/util/mpq.h index fa6e8ec75..286c2758d 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -47,7 +47,7 @@ class mpq_manager : public mpz_manager { void reset_denominator(mpq & a) { del(a.m_den); - a.m_den.m_val = 1; + a.m_den.set(1); } void normalize(mpq & a) { diff --git a/src/util/mpz.h b/src/util/mpz.h index 44e3e9c0b..6d1b3449d 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -84,6 +84,7 @@ class mpz { #else typedef mpz_t mpz_type; #endif +protected: int m_val; unsigned m_kind:1; unsigned m_owner:1; @@ -116,6 +117,17 @@ public: unsigned o = m_owner; m_owner = other.m_owner; other.m_owner = o; unsigned k = m_kind; m_kind = other.m_kind; other.m_kind = k; } + + void set(int v) { + m_val = v; + m_kind = mpz_small; + } + + inline bool is_small() const { return m_kind == mpz_small; } + + inline int value() const { SASSERT(is_small()); return m_val; } + + inline int sign() const { SASSERT(!is_small()); return m_val; } }; #ifndef _MP_GMP @@ -242,14 +254,13 @@ class mpz_manager { mpz m_two64; - static int64_t i64(mpz const & a) { return static_cast(a.m_val); } + static int64_t i64(mpz const & a) { return static_cast(a.value()); } void set_big_i64(mpz & c, int64_t v); void set_i64(mpz & c, int64_t v) { if (v >= INT_MIN && v <= INT_MAX) { - c.m_val = static_cast(v); - c.m_kind = mpz_small; + c.set(static_cast(v)); } else { set_big_i64(c, v); @@ -306,25 +317,25 @@ class mpz_manager { void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell, mpz_cell* reserve) { if (is_small(a)) { - if (a.m_val == INT_MIN) { + if (a.value() == INT_MIN) { sign = -1; cell = m_int_min.m_ptr; } else { cell = reserve; cell->m_size = 1; - if (a.m_val < 0) { + if (a.value() < 0) { sign = -1; - cell->m_digits[0] = -a.m_val; + cell->m_digits[0] = -a.value(); } else { sign = 1; - cell->m_digits[0] = a.m_val; + cell->m_digits[0] = a.value(); } } } else { - sign = a.m_val; + sign = a.sign(); cell = a.m_ptr; } } @@ -398,7 +409,7 @@ public: ~mpz_manager(); - static bool is_small(mpz const & a) { return a.m_kind == mpz_small; } + static bool is_small(mpz const & a) { return a.is_small(); } static mpz mk_z(int val) { return mpz(val); } @@ -461,7 +472,7 @@ public: bool eq(mpz const & a, mpz const & b) { if (is_small(a) && is_small(b)) { - return a.m_val == b.m_val; + return a.value() == b.value(); } else { return big_compare(a, b) == 0; @@ -470,7 +481,7 @@ public: bool lt(mpz const& a, int b) { if (is_small(a)) { - return a.m_val < b; + return a.value() < b; } else { return lt(a, mpz(b)); @@ -479,7 +490,7 @@ public: bool lt(mpz const & a, mpz const & b) { if (is_small(a) && is_small(b)) { - return a.m_val < b.m_val; + return a.value() < b.value(); } else { return big_compare(a, b) < 0; @@ -526,8 +537,7 @@ public: void set(mpz & target, mpz const & source) { if (is_small(source)) { - target.m_val = source.m_val; - target.m_kind = mpz_small; + target.set(source.value()); } else { big_set(target, source); @@ -535,8 +545,7 @@ public: } void set(mpz & a, int val) { - a.m_val = val; - a.m_kind = mpz_small; + a.set(val); } void set(mpz & a, unsigned val) { @@ -554,8 +563,7 @@ public: void set(mpz & a, uint64_t val) { if (val < INT_MAX) { - a.m_val = static_cast(val); - a.m_kind = mpz_small; + a.set(static_cast(val)); } else { set_big_ui64(a, val); @@ -574,10 +582,7 @@ public: void reset(mpz & a); void swap(mpz & a, mpz & b) noexcept { - std::swap(a.m_val, b.m_val); - std::swap(a.m_ptr, b.m_ptr); - auto o = a.m_owner; a.m_owner = b.m_owner; b.m_owner = o; - auto k = a.m_kind; a.m_kind = b.m_kind; b.m_kind = k; + a.swap(b); } bool is_uint64(mpz const & a) const; @@ -624,20 +629,20 @@ public: static bool is_one(mpz const & a) { #ifndef _MP_GMP - return is_small(a) && a.m_val == 1; + return is_small(a) && a.value() == 1; #else if (is_small(a)) - return a.m_val == 1; + return a.value() == 1; return mpz_cmp_si(*a.m_ptr, 1) == 0; #endif } static bool is_minus_one(mpz const & a) { #ifndef _MP_GMP - return is_small(a) && a.m_val == -1; + return is_small(a) && a.value() == -1; #else if (is_small(a)) - return a.m_val == -1; + return a.value() == -1; return mpz_cmp_si(*a.m_ptr, -1) == 0; #endif } @@ -712,7 +717,7 @@ public: bool is_even(mpz const & a) { if (is_small(a)) - return !(a.m_val & 0x1); + return !(a.value() & 0x1); #ifndef _MP_GMP return !(0x1 & digits(a)[0]); #else diff --git a/src/util/params.cpp b/src/util/params.cpp index d89026152..700a53109 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -99,25 +99,24 @@ struct param_descrs::imp { return CPK_INVALID; } - bool split_name(symbol const& name, symbol & prefix, symbol & suffix) const { + bool split_name(symbol const& name, std::string_view & prefix, symbol & suffix) const { if (name.is_numerical()) return false; char const* str = name.bare_str(); char const* period = strchr(str,'.'); if (!period) return false; - svector prefix_((unsigned)(period-str), str); - prefix_.push_back(0); - prefix = symbol(prefix_.data()); + prefix = std::string_view(str, period - str); suffix = symbol(period + 1); return true; } param_kind get_kind_in_module(symbol & name) const { param_kind k = get_kind(name); - symbol prefix, suffix; + std::string_view prefix; + symbol suffix; if (k == CPK_INVALID && split_name(name, prefix, suffix)) { k = get_kind(suffix); if (k != CPK_INVALID) { - if (symbol(get_module(suffix)) == prefix) { + if (get_module(suffix) == prefix) { name = suffix; } else { @@ -170,8 +169,8 @@ struct param_descrs::imp { if (names.empty()) return; if (markdown) { - out << " Parameter | Type | Description | Default\n"; - out << " ----------|------|-------------|--------\n"; + out << " Parameter | Type | Description | Default\n" + " ----------|------|-------------|--------\n"; } for (symbol const& name : names) { for (unsigned i = 0; i < indent; i++) out << " "; @@ -197,16 +196,14 @@ struct param_descrs::imp { else out << " (" << d.m_kind << ")"; if (markdown) { - out << " | "; - std::string desc; - for (auto ch : std::string(d.m_descr)) { + out << " | "; + for (auto ch : std::string_view(d.m_descr)) { switch (ch) { - case '<': desc += "<"; break; - case '>': desc += ">"; break; - default: desc.push_back(ch); + case '<': out << "<"; break; + case '>': out << ">"; break; + default: out << ch; break; } } - out << " " << desc; } else if (include_descr) out << " " << d.m_descr; @@ -549,8 +546,7 @@ params_ref::~params_ref() { m_params->dec_ref(); } -params_ref::params_ref(params_ref const & p): - m_params(nullptr) { +params_ref::params_ref(params_ref const & p) { set(p); } diff --git a/src/util/params.h b/src/util/params.h index bc45bdbce..200b4aa2c 100644 --- a/src/util/params.h +++ b/src/util/params.h @@ -32,12 +32,12 @@ class param_descrs; class params_ref { static params_ref g_empty_params_ref; - params * m_params; + params * m_params = nullptr; void init(); void copy_core(params const * p); void set(params_ref const& p); public: - params_ref():m_params(nullptr) {} + params_ref() = default; params_ref(params_ref const & p); ~params_ref(); diff --git a/src/util/rational.h b/src/util/rational.h index f73af0c04..e9924bca7 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -30,6 +30,10 @@ class rational { static synch_mpq_manager & m() { return *g_mpq_manager; } + void display_hex(std::ostream & out, unsigned num_bits) const { SASSERT(is_int()); m().display_hex(out, m_val.numerator(), num_bits); } + + void display_bin(std::ostream& out, unsigned num_bits) const { SASSERT(is_int()); m().display_bin(out, m_val.numerator(), num_bits); } + public: static void initialize(); static void finalize(); @@ -96,7 +100,20 @@ public: void display_smt2(std::ostream & out) const { return m().display_smt2(out, m_val, false); } - void display_hex(std::ostream & out, unsigned num_bits) const { SASSERT(is_int()); return m().display_hex(out, m_val.numerator(), num_bits); } + + struct as_hex_wrapper { + rational const& r; + unsigned bw; + }; + + as_hex_wrapper as_hex(unsigned bw) const { return as_hex_wrapper{*this, bw}; } + + friend inline std::ostream& operator<<(std::ostream& out, as_hex_wrapper const& ab) { + ab.r.display_hex(out, ab.bw); + return out; + } + + struct as_bin_wrapper { rational const& r; @@ -110,7 +127,6 @@ public: return out; } - std::ostream& display_bin(std::ostream& out, unsigned num_bits) const { SASSERT(is_int()); m().display_bin(out, m_val.numerator(), num_bits); return out; } bool is_uint64() const { return m().is_uint64(m_val); } diff --git a/src/util/sat_literal.h b/src/util/sat_literal.h index aeb23bddd..58088e628 100644 --- a/src/util/sat_literal.h +++ b/src/util/sat_literal.h @@ -30,7 +30,7 @@ namespace sat { typedef svector bool_var_vector; - const bool_var null_bool_var = UINT_MAX >> 1; + inline constexpr bool_var null_bool_var = UINT_MAX >> 1; /** \brief The literal b is represented by the value 2*b, and @@ -39,9 +39,7 @@ namespace sat { class literal { unsigned m_val; public: - literal():m_val(null_bool_var << 1) { - SASSERT(var() == null_bool_var && !sign()); - } + constexpr literal(): m_val(null_bool_var << 1) { } explicit literal(bool_var v, bool _sign = false): m_val((v << 1) + static_cast(_sign)) { @@ -49,11 +47,11 @@ namespace sat { SASSERT(sign() == _sign); } - bool_var var() const { + constexpr bool_var var() const { return m_val >> 1; } - bool sign() const { + constexpr bool sign() const { return m_val & 1ul; } @@ -86,7 +84,10 @@ namespace sat { friend bool operator!=(literal const & l1, literal const & l2); }; - const literal null_literal; + inline constexpr literal null_literal; + static_assert(null_literal.var() == null_bool_var); + static_assert(!null_literal.sign()); + using literal_hash = obj_hash; inline literal to_literal(unsigned x) { literal l; l.m_val = x; return l; } diff --git a/src/util/scoped_numeral.h b/src/util/scoped_numeral.h index b90ba640a..f70f5f185 100644 --- a/src/util/scoped_numeral.h +++ b/src/util/scoped_numeral.h @@ -28,8 +28,8 @@ private: numeral m_num; public: _scoped_numeral(Manager & m):m_manager(m) {} - _scoped_numeral(_scoped_numeral const & n):m_manager(n.m_manager) { m().set(m_num, n.m_num); } - _scoped_numeral(_scoped_numeral &&) = default; + _scoped_numeral(_scoped_numeral const& n) :m_manager(n.m_manager) { m().set(m_num, n.m_num); } + _scoped_numeral(_scoped_numeral && n) noexcept: m_manager(n.m_manager) { m().swap(m_num, n.m_num); } ~_scoped_numeral() { m_manager.del(m_num); } Manager & m() const { return m_manager; } diff --git a/src/util/sexpr.cpp b/src/util/sexpr.cpp index e20c56234..dcf427dfe 100644 --- a/src/util/sexpr.cpp +++ b/src/util/sexpr.cpp @@ -26,11 +26,11 @@ Notes: #endif struct sexpr_composite : public sexpr { - unsigned m_num_chilren; + unsigned m_num_children; sexpr * m_children[0]; sexpr_composite(unsigned num_children, sexpr * const * children, unsigned line, unsigned pos): sexpr(kind_t::COMPOSITE, line, pos), - m_num_chilren(num_children) { + m_num_children(num_children) { for (unsigned i = 0; i < num_children; i++) { m_children[i] = children[i]; children[i]->inc_ref(); @@ -107,7 +107,7 @@ std::string const & sexpr::get_string() const { unsigned sexpr::get_num_children() const { SASSERT(is_composite()); - return static_cast(this)->m_num_chilren; + return static_cast(this)->m_num_children; } sexpr * sexpr::get_child(unsigned idx) const { diff --git a/src/util/sstream.h b/src/util/sstream.h deleted file mode 100644 index fba13c5d5..000000000 --- a/src/util/sstream.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright (c) 2018 Microsoft Corporation - -Module Name: - - nat_set.h - -Abstract: - - Wrapper for sstream. - -Author: - - Leonardo de Moura (leonardo) 2013 - -Revision History: - -*/ -#pragma once -#include -#include - -namespace lean { -/** \brief Wrapper for std::ostringstream */ -class sstream { - std::ostringstream m_strm; -public: - std::string str() const { return m_strm.str(); } - template sstream & operator<<(T const & t) { m_strm << t; return *this; } -}; -} diff --git a/src/util/tbv.cpp b/src/util/tbv.cpp index 017ca0eb7..5048267ad 100644 --- a/src/util/tbv.cpp +++ b/src/util/tbv.cpp @@ -142,12 +142,8 @@ void tbv_manager::set(tbv& dst, rational const& r, unsigned hi, unsigned lo) { set(dst, r.get_uint64(), hi, lo); return; } - for (unsigned i = 0; i < hi - lo + 1; ++i) { - if (bitwise_and(r, rational::power_of_two(i)).is_zero()) - set(dst, lo + i, BIT_0); - else - set(dst, lo + i, BIT_1); - } + for (unsigned i = 0; i < hi - lo + 1; ++i) + set(dst, lo + i, r.get_bit(i) ? BIT_1 : BIT_0); } void tbv_manager::set(tbv& dst, tbv const& other, unsigned hi, unsigned lo) { diff --git a/src/util/tptr.h b/src/util/tptr.h index 37b6f64fe..99abe34a7 100644 --- a/src/util/tptr.h +++ b/src/util/tptr.h @@ -21,6 +21,7 @@ Revision History: #include #include "util/machine.h" +#include "util/debug.h" #define TAG_SHIFT PTR_ALIGNMENT #define ALIGNMENT_VALUE (1 << PTR_ALIGNMENT) @@ -42,4 +43,22 @@ Revision History: #define UNBOXINT(PTR) static_cast(reinterpret_cast(PTR) >> PTR_ALIGNMENT) +template +U unbox(T* ptr) { + return static_cast(reinterpret_cast(ptr) >> PTR_ALIGNMENT); +} +template +unsigned get_tag(T* ptr) { + return reinterpret_cast(ptr) & TAG_MASK; +} + +template +T* box(U val, std::uintptr_t tag = 0) { + static_assert( sizeof(T*) >= sizeof(U) + PTR_ALIGNMENT ); + SASSERT_EQ(tag & PTR_MASK, 0); + T* ptr = reinterpret_cast((static_cast(val) << PTR_ALIGNMENT) | tag); + SASSERT_EQ(val, unbox(ptr)); // roundtrip of conversion integer -> pointer -> integer is not actually guaranteed by the C++ standard (but seems fine in practice, as indicated by previous usage of BOXINT/UNBOXINT) + SASSERT_EQ(tag, get_tag(ptr)); + return ptr; +} diff --git a/src/util/union_find.h b/src/util/union_find.h index 7e42e1bba..0c08ac446 100644 --- a/src/util/union_find.h +++ b/src/util/union_find.h @@ -35,7 +35,7 @@ private: _trail_stack m_stack; }; -template +template class union_find { Ctx & m_ctx; trail_stack & m_trail_stack; diff --git a/src/util/vector.h b/src/util/vector.h index aa5ac786d..37b50cc41 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -30,6 +30,7 @@ Revision History: #include #include #include +#include #include "util/memory_manager.h" #include "util/hash.h" #include "util/z3_exception.h" @@ -563,7 +564,7 @@ public: for(; pos != e; ++pos, ++prev) { *prev = std::move(*pos); } - reinterpret_cast(m_data)[SIZE_IDX]--; + pop_back(); } void erase(T const & elem) { @@ -573,6 +574,20 @@ public: } } + /** Erase all elements that satisfy the given predicate. Returns the number of erased elements. */ + template + SZ erase_if(UnaryPredicate should_erase) { + iterator i = begin(); + iterator const e = end(); + for (iterator j = begin(); j != e; ++j) + if (!should_erase(std::as_const(*j))) + *(i++) = std::move(*j); + SZ const count = e - i; + SASSERT_EQ(i - begin(), size() - count); + shrink(size() - count); + return count; + } + void shrink(SZ s) { if (m_data) { SASSERT(s <= reinterpret_cast(m_data)[SIZE_IDX]); @@ -762,7 +777,8 @@ using bool_vector = svector; template inline std::ostream& operator<<(std::ostream& out, svector const& v) { - for (unsigned u : v) out << u << " "; + for (auto const& x : v) + out << x << " "; return out; } diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index eaa5bb5ee..8e08820f6 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -152,7 +152,7 @@ std::string zstring::encode() const { unsigned ch = m_buffer[i]; if (ch < 32 || ch >= 128 || ('\\' == ch && i + 1 < m_buffer.size() && 'u' == m_buffer[i+1])) { _flush(); - strm << "\\u{" << std::hex << ch << std::dec << "}"; + strm << "\\u{" << std::hex << ch << std::dec << '}'; } else { if (offset == 99) @@ -161,7 +161,7 @@ std::string zstring::encode() const { } } _flush(); - return strm.str(); + return std::move(strm).str(); } bool zstring::suffixof(zstring const& other) const {