diff --git a/.github/workflows/NUGET_BUILD_README.md b/.github/workflows/NUGET_BUILD_README.md new file mode 100644 index 000000000..0a53f9bb8 --- /dev/null +++ b/.github/workflows/NUGET_BUILD_README.md @@ -0,0 +1,87 @@ +# NuGet Package Build Workflow + +This document describes the GitHub Actions workflow for building Z3 NuGet packages. + +## Overview + +The NuGet build workflow (`.github/workflows/nuget-build.yml`) creates Microsoft.Z3 NuGet packages for distribution. It builds Z3 for all supported platforms and assembles them into NuGet packages. + +## Triggering the Workflow + +The workflow can be triggered in two ways: + +### 1. Manual Trigger + +You can manually trigger the workflow from the GitHub Actions tab: + +1. Go to the "Actions" tab in the repository +2. Select "Build NuGet Package" workflow +3. Click "Run workflow" +4. Enter the version number (e.g., `4.15.5`) +5. Click "Run workflow" + +### 2. Tag-based Trigger + +The workflow automatically runs when you push a tag with the `z3-` prefix: + +```bash +git tag z3-4.15.5 +git push origin z3-4.15.5 +``` + +## Workflow Structure + +The workflow consists of multiple jobs: + +### Build Jobs + +1. **build-windows-x64**: Builds Windows x64 binaries with .NET support +2. **build-windows-x86**: Builds Windows x86 binaries with .NET support +3. **build-windows-arm64**: Builds Windows ARM64 binaries with .NET support +4. **build-ubuntu**: Builds Linux x64 binaries with .NET support +5. **build-macos-x64**: Builds macOS x64 binaries with .NET support +6. **build-macos-arm64**: Builds macOS ARM64 binaries with .NET support + +### Package Jobs + +1. **package-nuget-x64**: Creates the main NuGet package (Microsoft.Z3.nupkg) with x64, ARM64, Linux, and macOS support +2. **package-nuget-x86**: Creates the x86 NuGet package (Microsoft.Z3.x86.nupkg) + +## Output + +The workflow produces two NuGet packages as artifacts: + +- `Microsoft.Z3.{version}.nupkg` and `Microsoft.Z3.{version}.snupkg` (x64 + multi-platform) +- `Microsoft.Z3.x86.{version}.nupkg` and `Microsoft.Z3.x86.{version}.snupkg` (x86 only) + +These can be downloaded from the workflow run's artifacts section. + +## Key Files + +- `.github/workflows/nuget-build.yml`: The workflow definition +- `scripts/mk_nuget_task.py`: Script that assembles the NuGet package from build artifacts +- `scripts/mk_win_dist.py`: Script for building Windows x86/x64 distributions +- `scripts/mk_win_dist_cmake.py`: Script for building Windows ARM64 distributions +- `scripts/mk_unix_dist.py`: Script for building Linux and macOS distributions + +## Bug Fix + +This workflow includes a fix for a critical bug in `mk_nuget_task.py` where the `replace()` function had incorrect logic that would fail to copy files when the destination already existed. The fix ensures that Microsoft.Z3.dll and related files are always properly included in the NuGet package under `lib/netstandard2.0/`. + +## Development + +To test changes to the NuGet packaging locally, you can: + +1. Build the platform-specific binaries using the appropriate build scripts +2. Collect the resulting ZIP files in a directory +3. Run `mk_nuget_task.py` to assemble the package: + +```bash +python scripts/mk_nuget_task.py [symbols] [x86] +``` + +4. Use the NuGet CLI to pack the package: + +```bash +nuget pack out/Microsoft.Z3.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out +``` diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 5cdaeb67e..bd19add6d 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -22,7 +22,7 @@ jobs: runs-on: windows-latest steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 - run: | diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index c2ea7c860..896cb2192 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Configure CMake and build run: | @@ -32,7 +32,7 @@ jobs: tar -cvf z3-build-${{ matrix.android-abi }}.tar *.jar *.so - name: Archive production artifacts - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: android-build-${{ matrix.android-abi }} path: build/z3-build-${{ matrix.android-abi }}.tar diff --git a/.github/workflows/ask.lock.yml b/.github/workflows/ask.lock.yml index 19f9a99f2..ff908ab9e 100644 --- a/.github/workflows/ask.lock.yml +++ b/.github/workflows/ask.lock.yml @@ -569,7 +569,7 @@ jobs: output: ${{ steps.collect_output.outputs.output }} steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup agent output id: setup_agent_output uses: actions/github-script@v8 @@ -1223,7 +1223,7 @@ jobs: .write(); - name: Upload agentic run info if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw_info.json path: /tmp/aw_info.json @@ -1329,7 +1329,7 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY - name: Upload agentic output file if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: safe_output.jsonl path: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} @@ -2277,7 +2277,7 @@ jobs: await main(); - name: Upload sanitized agent output if: always() && env.GITHUB_AW_AGENT_OUTPUT - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: agent_output.json path: ${{ env.GITHUB_AW_AGENT_OUTPUT }} @@ -2814,7 +2814,7 @@ jobs: main(); - name: Upload agent logs if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: question-answering-researcher.log path: /tmp/question-answering-researcher.log diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 903da1c30..246f7fc40 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -36,10 +36,10 @@ jobs: output: ${{ steps.collect_output.outputs.output }} steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 # Cache configuration from frontmatter processed below - name: Cache (investigation-memory-${{ github.repository }}) - uses: actions/cache@v4 + uses: actions/cache@v5 with: key: investigation-memory-${{ github.repository }} path: | @@ -808,7 +808,7 @@ jobs: .write(); - name: Upload agentic run info if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw_info.json path: /tmp/aw_info.json @@ -911,7 +911,7 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY - name: Upload agentic output file if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: safe_output.jsonl path: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} @@ -1859,7 +1859,7 @@ jobs: await main(); - name: Upload sanitized agent output if: always() && env.GITHUB_AW_AGENT_OUTPUT - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: agent_output.json path: ${{ env.GITHUB_AW_AGENT_OUTPUT }} @@ -2396,7 +2396,7 @@ jobs: main(); - name: Upload agent logs if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ci-failure-doctor.log path: /tmp/ci-failure-doctor.log diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 279bd2b99..618c98660 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Initialize CodeQL uses: github/codeql-action/init@v4 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 2c02dabf2..4bfd0154e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -19,7 +19,7 @@ jobs: COV_DETAILS_PATH: ${{github.workspace}}/cov-details steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Setup run: | @@ -89,13 +89,13 @@ jobs: id: date run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v6 with: name: coverage-${{steps.date.outputs.date}} path: ${{github.workspace}}/coverage.html retention-days: 4 - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v6 with: name: coverage-details-${{steps.date.outputs.date}} path: ${{env.COV_DETAILS_PATH}} diff --git a/.github/workflows/cross-build.yml b/.github/workflows/cross-build.yml index 07b6fdaed..02ffa3017 100644 --- a/.github/workflows/cross-build.yml +++ b/.github/workflows/cross-build.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install cross build tools run: apt update && apt install -y ninja-build cmake python3 g++-11-${{ matrix.arch }}-linux-gnu diff --git a/.github/workflows/daily-backlog-burner.lock.yml b/.github/workflows/daily-backlog-burner.lock.yml index 5dfd11104..e35ffeb88 100644 --- a/.github/workflows/daily-backlog-burner.lock.yml +++ b/.github/workflows/daily-backlog-burner.lock.yml @@ -25,7 +25,7 @@ jobs: output: ${{ steps.collect_output.outputs.output }} steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Configure Git credentials run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" @@ -747,7 +747,7 @@ jobs: .write(); - name: Upload agentic run info if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw_info.json path: /tmp/aw_info.json @@ -856,7 +856,7 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY - name: Upload agentic output file if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: safe_output.jsonl path: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} @@ -1804,7 +1804,7 @@ jobs: await main(); - name: Upload sanitized agent output if: always() && env.GITHUB_AW_AGENT_OUTPUT - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: agent_output.json path: ${{ env.GITHUB_AW_AGENT_OUTPUT }} @@ -2341,7 +2341,7 @@ jobs: main(); - name: Upload agent logs if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: daily-backlog-burner.log path: /tmp/daily-backlog-burner.log @@ -2435,7 +2435,7 @@ jobs: fi - name: Upload git patch if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw.patch path: /tmp/aw.patch @@ -2946,12 +2946,12 @@ jobs: steps: - name: Download patch artifact continue-on-error: true - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: aw.patch path: /tmp/ - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Configure Git credentials diff --git a/.github/workflows/daily-perf-improver.lock.yml b/.github/workflows/daily-perf-improver.lock.yml index 266ef1b2e..0cda573b9 100644 --- a/.github/workflows/daily-perf-improver.lock.yml +++ b/.github/workflows/daily-perf-improver.lock.yml @@ -25,7 +25,7 @@ jobs: output: ${{ steps.collect_output.outputs.output }} steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - id: check_build_steps_file name: Check if action.yml exists run: | @@ -822,7 +822,7 @@ jobs: .write(); - name: Upload agentic run info if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw_info.json path: /tmp/aw_info.json @@ -931,7 +931,7 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY - name: Upload agentic output file if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: safe_output.jsonl path: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} @@ -1879,7 +1879,7 @@ jobs: await main(); - name: Upload sanitized agent output if: always() && env.GITHUB_AW_AGENT_OUTPUT - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: agent_output.json path: ${{ env.GITHUB_AW_AGENT_OUTPUT }} @@ -2416,7 +2416,7 @@ jobs: main(); - name: Upload agent logs if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: daily-perf-improver.log path: /tmp/daily-perf-improver.log @@ -2510,7 +2510,7 @@ jobs: fi - name: Upload git patch if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw.patch path: /tmp/aw.patch @@ -3021,12 +3021,12 @@ jobs: steps: - name: Download patch artifact continue-on-error: true - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: aw.patch path: /tmp/ - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Configure Git credentials diff --git a/.github/workflows/daily-test-improver.lock.yml b/.github/workflows/daily-test-improver.lock.yml index 8c7acc85d..d1f8db3c4 100644 --- a/.github/workflows/daily-test-improver.lock.yml +++ b/.github/workflows/daily-test-improver.lock.yml @@ -25,7 +25,7 @@ jobs: output: ${{ steps.collect_output.outputs.output }} steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - id: check_coverage_steps_file name: Check if action.yml exists run: | @@ -797,7 +797,7 @@ jobs: .write(); - name: Upload agentic run info if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw_info.json path: /tmp/aw_info.json @@ -906,7 +906,7 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY - name: Upload agentic output file if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: safe_output.jsonl path: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} @@ -1854,7 +1854,7 @@ jobs: await main(); - name: Upload sanitized agent output if: always() && env.GITHUB_AW_AGENT_OUTPUT - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: agent_output.json path: ${{ env.GITHUB_AW_AGENT_OUTPUT }} @@ -2391,7 +2391,7 @@ jobs: main(); - name: Upload agent logs if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: daily-test-coverage-improver.log path: /tmp/daily-test-coverage-improver.log @@ -2485,7 +2485,7 @@ jobs: fi - name: Upload git patch if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw.patch path: /tmp/aw.patch @@ -2996,12 +2996,12 @@ jobs: steps: - name: Download patch artifact continue-on-error: true - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: aw.patch path: /tmp/ - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Configure Git credentials diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..e358d82c4 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,109 @@ +name: Documentation + +on: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: "pages" + cancel-in-progress: false + +env: + EM_VERSION: 3.1.73 + +jobs: + build-docs: + name: Build Documentation + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: "lts/*" + + # Setup OCaml via action + - uses: ocaml/setup-ocaml@v3 + with: + ocaml-compiler: 5 + opam-disable-sandboxing: true + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y doxygen graphviz python3 python3-pip + sudo apt-get install -y \ + bubblewrap m4 libgmp-dev pkg-config + + - name: Install required opam packages + run: opam install -y ocamlfind zarith + + - name: Build Z3 natively for Python documentation + run: | + eval $(opam env) + echo "CC: $CC" + echo "CXX: $CXX" + echo "OCAMLFIND: $(which ocamlfind)" + echo "OCAMLC: $(which ocamlc)" + echo "OCAMLOPT: $(which ocamlopt)" + echo "OCAML_VERSION: $(ocamlc -version)" + echo "OCAMLLIB: $OCAMLLIB" + mkdir build-x64 + python3 scripts/mk_make.py --python --ml --build=build-x64 + cd build-x64 + make -j$(nproc) + + - name: Generate Documentation (from doc directory) + working-directory: doc + run: | + eval $(opam env) + python3 mk_api_doc.py --mld --output-dir=api --z3py-package-path=../build-x64/python/z3 --build=../build-x64 + Z3BUILD=build-x64 python3 mk_params_doc.py + mkdir api/html/ml + ocamldoc -html -d api/html/ml -sort -hide Z3 -I $( ocamlfind query zarith ) -I ../build-x64/api/ml ../build-x64/api/ml/z3enums.mli ../build-x64/api/ml/z3.mli + + - name: Setup emscripten + uses: mymindstorm/setup-emsdk@v14 + with: + no-install: true + version: ${{env.EM_VERSION}} + actions-cache-folder: "emsdk-cache" + + - name: Install dependencies + working-directory: src/api/js + run: npm ci + + - name: Build TypeScript + working-directory: src/api/js + run: npm run build:ts + + - name: Build wasm + working-directory: src/api/js + run: | + emsdk install ${EM_VERSION} + emsdk activate ${EM_VERSION} + source $(dirname $(which emsdk))/emsdk_env.sh + which node + which clang++ + npm run build:wasm + + - name: Generate JS Documentation (from doc directory) + working-directory: doc + run: | + eval $(opam env) + python3 mk_api_doc.py --js --output-dir=api --mld --z3py-package-path=../build-x64/python/z3 --build=../build-x64 + + - name: Deploy to z3prover.github.io + uses: peaceiris/actions-gh-pages@v4 + with: + deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} + external_repository: Z3Prover/z3prover.github.io + destination_dir: ./api + publish_branch: master + publish_dir: ./doc/api + user_name: github-actions[bot] + user_email: github-actions[bot]@users.noreply.github.com diff --git a/.github/workflows/labeller.yml b/.github/workflows/labeller.yml index ebe7126cd..240879a48 100644 --- a/.github/workflows/labeller.yml +++ b/.github/workflows/labeller.yml @@ -13,7 +13,7 @@ jobs: genai-issue-labeller: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: pelikhan/action-genai-issue-labeller@v0 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/msvc-static-build-clang-cl.yml b/.github/workflows/msvc-static-build-clang-cl.yml index f8cd8962b..e13b3ddf1 100644 --- a/.github/workflows/msvc-static-build-clang-cl.yml +++ b/.github/workflows/msvc-static-build-clang-cl.yml @@ -14,7 +14,7 @@ jobs: BUILD_TYPE: Release steps: - name: Checkout Repo - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Build run: | diff --git a/.github/workflows/msvc-static-build.yml b/.github/workflows/msvc-static-build.yml index 9b2c7e5a6..f37f9804b 100644 --- a/.github/workflows/msvc-static-build.yml +++ b/.github/workflows/msvc-static-build.yml @@ -14,7 +14,7 @@ jobs: BUILD_TYPE: Release steps: - name: Checkout Repo - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Build run: | diff --git a/.github/workflows/nuget-build.yml b/.github/workflows/nuget-build.yml new file mode 100644 index 000000000..437262253 --- /dev/null +++ b/.github/workflows/nuget-build.yml @@ -0,0 +1,256 @@ +name: Build NuGet Package + +on: + workflow_dispatch: + inputs: + version: + description: 'Version number for the NuGet package (e.g., 4.15.5)' + required: true + default: '4.15.5' + push: + tags: + - 'z3-*' + +permissions: + contents: write + +jobs: + # Build Windows binaries + build-windows-x64: + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + + - name: Build Windows x64 + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 + python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.15.5' }} --zip + + - name: Upload Windows x64 artifact + uses: actions/upload-artifact@v6 + with: + name: windows-x64 + path: dist/*.zip + retention-days: 1 + + build-windows-x86: + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + + - name: Build Windows x86 + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86 + python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.15.5' }} --zip + + - name: Upload Windows x86 artifact + uses: actions/upload-artifact@v6 + with: + name: windows-x86 + path: dist/*.zip + retention-days: 1 + + build-windows-arm64: + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + + - name: Build Windows ARM64 + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 + python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.15.5' }} --zip + + - name: Upload Windows ARM64 artifact + uses: actions/upload-artifact@v6 + with: + name: windows-arm64 + path: build-dist\arm64\dist\*.zip + retention-days: 1 + + build-ubuntu: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + + - name: Build Ubuntu + run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk + + - name: Upload Ubuntu artifact + uses: actions/upload-artifact@v6 + with: + name: ubuntu + path: dist/*.zip + retention-days: 1 + + build-macos-x64: + runs-on: macos-13 + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + + - name: Build macOS x64 + run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk + + - name: Upload macOS x64 artifact + uses: actions/upload-artifact@v6 + with: + name: macos-x64 + path: dist/*.zip + retention-days: 1 + + build-macos-arm64: + runs-on: macos-13 + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + + - name: Build macOS ARM64 + run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64 + + - name: Upload macOS ARM64 artifact + uses: actions/upload-artifact@v6 + with: + name: macos-arm64 + path: dist/*.zip + retention-days: 1 + + # Package NuGet x64 (includes all platforms except x86) + package-nuget-x64: + needs: [build-windows-x64, build-windows-arm64, build-ubuntu, build-macos-x64, build-macos-arm64] + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + + - name: Download all artifacts + uses: actions/download-artifact@v7 + with: + path: packages + + - name: List downloaded artifacts + shell: bash + run: find packages -type f + + - name: Move artifacts to flat directory + shell: bash + run: | + mkdir -p package-files + find packages -name "*.zip" -exec cp {} package-files/ \; + ls -la package-files/ + + - name: Setup NuGet + uses: nuget/setup-nuget@v2 + with: + nuget-version: 'latest' + + - name: Assemble NuGet package + shell: cmd + run: | + cd package-files + python ..\scripts\mk_nuget_task.py . ${{ github.event.inputs.version || '4.15.5' }} https://github.com/Z3Prover/z3 ${{ github.ref_name }} ${{ github.sha }} ${{ github.workspace }} symbols + + - name: Pack NuGet package + shell: cmd + run: | + cd package-files + nuget pack out\Microsoft.Z3.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out + + - name: Upload NuGet package + uses: actions/upload-artifact@v6 + with: + name: nuget-x64 + path: | + package-files/*.nupkg + package-files/*.snupkg + retention-days: 30 + + # Package NuGet x86 + package-nuget-x86: + needs: [build-windows-x86] + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + + - name: Download x86 artifact + uses: actions/download-artifact@v7 + with: + name: windows-x86 + path: packages + + - name: List downloaded artifacts + shell: bash + run: find packages -type f + + - name: Setup NuGet + uses: nuget/setup-nuget@v2 + with: + nuget-version: 'latest' + + - name: Assemble NuGet package + shell: cmd + run: | + cd packages + python ..\scripts\mk_nuget_task.py . ${{ github.event.inputs.version || '4.15.5' }} https://github.com/Z3Prover/z3 ${{ github.ref_name }} ${{ github.sha }} ${{ github.workspace }} symbols x86 + + - name: Pack NuGet package + shell: cmd + run: | + cd packages + nuget pack out\Microsoft.Z3.x86.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out + + - name: Upload NuGet package + uses: actions/upload-artifact@v6 + with: + name: nuget-x86 + path: | + packages/*.nupkg + packages/*.snupkg + retention-days: 30 diff --git a/.github/workflows/ocaml.yaml b/.github/workflows/ocaml.yaml index 9d0917fd4..255e258a3 100644 --- a/.github/workflows/ocaml.yaml +++ b/.github/workflows/ocaml.yaml @@ -17,11 +17,11 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 # Cache ccache (shared across runs) - name: Cache ccache - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.ccache key: ${{ runner.os }}-ccache-${{ github.sha }} @@ -30,7 +30,7 @@ jobs: # Cache opam (compiler + packages) - name: Cache opam - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.opam key: ${{ runner.os }}-opam-${{ matrix.ocaml-version }}-${{ github.sha }} diff --git a/.github/workflows/pr-fix.lock.yml b/.github/workflows/pr-fix.lock.yml index 2e2679e64..323f204ba 100644 --- a/.github/workflows/pr-fix.lock.yml +++ b/.github/workflows/pr-fix.lock.yml @@ -569,7 +569,7 @@ jobs: output: ${{ steps.collect_output.outputs.output }} steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Configure Git credentials run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" @@ -1251,7 +1251,7 @@ jobs: .write(); - name: Upload agentic run info if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw_info.json path: /tmp/aw_info.json @@ -1360,7 +1360,7 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY - name: Upload agentic output file if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: safe_output.jsonl path: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} @@ -2308,7 +2308,7 @@ jobs: await main(); - name: Upload sanitized agent output if: always() && env.GITHUB_AW_AGENT_OUTPUT - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: agent_output.json path: ${{ env.GITHUB_AW_AGENT_OUTPUT }} @@ -2845,7 +2845,7 @@ jobs: main(); - name: Upload agent logs if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: pr-fix.log path: /tmp/pr-fix.log @@ -2939,7 +2939,7 @@ jobs: fi - name: Upload git patch if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: aw.patch path: /tmp/aw.patch @@ -3371,12 +3371,12 @@ jobs: steps: - name: Download patch artifact continue-on-error: true - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: aw.patch path: /tmp/ - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Configure Git credentials diff --git a/.github/workflows/prd.yml b/.github/workflows/prd.yml index 6a53af4f8..c57bd267d 100644 --- a/.github/workflows/prd.yml +++ b/.github/workflows/prd.yml @@ -13,7 +13,7 @@ jobs: generate-pull-request-description: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: pelikhan/action-genai-pull-request-descriptor@v0 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pyodide.yml b/.github/workflows/pyodide.yml index a840b1fad..d0e95e43d 100644 --- a/.github/workflows/pyodide.yml +++ b/.github/workflows/pyodide.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup packages run: sudo apt-get update && sudo apt-get install -y python3-dev python3-pip python3-venv diff --git a/.github/workflows/wasm-release.yml b/.github/workflows/wasm-release.yml index b2bba5126..8da0603f4 100644 --- a/.github/workflows/wasm-release.yml +++ b/.github/workflows/wasm-release.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup node uses: actions/setup-node@v6 diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index b95e86289..6168d9470 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup node uses: actions/setup-node@v6 diff --git a/.github/workflows/wip.yml b/.github/workflows/wip.yml index 54fcf8216..ae3ac1a47 100644 --- a/.github/workflows/wip.yml +++ b/.github/workflows/wip.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d66f8dc4..1ff592e0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -548,21 +548,93 @@ set(Z3_GENERATED_FILE_EXTRA_DEPENDENCIES ) ################################################################################ -# Z3 components, library and executables +# API header files ################################################################################ -include(${PROJECT_SOURCE_DIR}/cmake/z3_add_component.cmake) -include(${PROJECT_SOURCE_DIR}/cmake/z3_append_linker_flag_list_to_target.cmake) -add_subdirectory(src) +# This lists the API header files that are scanned by +# some of the build rules to generate some files needed +# by the build; needs to come before add_subdirectory(src) +set(Z3_API_HEADER_FILES_TO_SCAN + z3_api.h + z3_ast_containers.h + z3_algebraic.h + z3_polynomial.h + z3_rcf.h + z3_fixedpoint.h + z3_optimization.h + z3_fpa.h + z3_spacer.h +) +set(Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN "") +foreach (header_file ${Z3_API_HEADER_FILES_TO_SCAN}) + set(full_path_api_header_file "${CMAKE_CURRENT_SOURCE_DIR}/src/api/${header_file}") + list(APPEND Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN "${full_path_api_header_file}") + if (NOT EXISTS "${full_path_api_header_file}") + message(FATAL_ERROR "API header file \"${full_path_api_header_file}\" does not exist") + endif() +endforeach() ################################################################################ # Create `Z3Config.cmake` and related files for the build tree so clients can # use Z3 via CMake. ################################################################################ include(CMakePackageConfigHelpers) -export(EXPORT Z3_EXPORTED_TARGETS - NAMESPACE z3:: - FILE "${PROJECT_BINARY_DIR}/Z3Targets.cmake" -) + +option(Z3_BUILD_LIBZ3_CORE "Build the core libz3 library" ON) +# Only export targets if we built libz3 +if (Z3_BUILD_LIBZ3_CORE) + ################################################################################ + # Z3 components, library and executables + ################################################################################ + include(${PROJECT_SOURCE_DIR}/cmake/z3_add_component.cmake) + include(${PROJECT_SOURCE_DIR}/cmake/z3_append_linker_flag_list_to_target.cmake) + add_subdirectory(src) + + export(EXPORT Z3_EXPORTED_TARGETS + NAMESPACE z3:: + FILE "${PROJECT_BINARY_DIR}/Z3Targets.cmake" + ) +else() + # When not building libz3, we need to find it + message(STATUS "Not building libz3, will look for pre-installed library") + find_library(Z3_LIBRARY NAMES z3 libz3 + HINTS ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} + PATH_SUFFIXES lib lib64 + ) + if (NOT Z3_LIBRARY) + message(FATAL_ERROR "Could not find pre-installed libz3. Please ensure libz3 is installed or set Z3_BUILD_LIBZ3_CORE=ON") + endif() + message(STATUS "Found libz3: ${Z3_LIBRARY}") + + # Create an imported target for the pre-installed libz3 + add_library(libz3 SHARED IMPORTED) + set_target_properties(libz3 PROPERTIES + IMPORTED_LOCATION "${Z3_LIBRARY}" + ) + # Set include directories for the imported target + target_include_directories(libz3 INTERFACE + ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR} + ) +endif() + +################################################################################ +# Z3 API bindings +################################################################################ +option(Z3_BUILD_PYTHON_BINDINGS "Build Python bindings for Z3" OFF) +if (Z3_BUILD_PYTHON_BINDINGS) + # Validate configuration for Python bindings + if (Z3_BUILD_LIBZ3_CORE) + # Building libz3 together with Python bindings + if (NOT Z3_BUILD_LIBZ3_SHARED) + message(FATAL_ERROR "The python bindings will not work with a static libz3. " + "You either need to disable Z3_BUILD_PYTHON_BINDINGS or enable Z3_BUILD_LIBZ3_SHARED") + endif() + else() + # Using pre-installed libz3 for Python bindings + message(STATUS "Building Python bindings with pre-installed libz3") + endif() + add_subdirectory(src/api/python) +endif() + set(Z3_FIRST_PACKAGE_INCLUDE_DIR "${PROJECT_BINARY_DIR}/src/api") set(Z3_SECOND_PACKAGE_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/src/api") set(Z3_CXX_PACKAGE_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/src/api/c++") @@ -593,12 +665,15 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/z3.pc.cmake.in" # Create `Z3Config.cmake` and related files for install tree so clients can use # Z3 via CMake. ################################################################################ -install(EXPORT - Z3_EXPORTED_TARGETS - FILE "Z3Targets.cmake" - NAMESPACE z3:: - DESTINATION "${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}" -) +# Only install targets if we built libz3 +if (Z3_BUILD_LIBZ3_CORE) + install(EXPORT + Z3_EXPORTED_TARGETS + FILE "Z3Targets.cmake" + NAMESPACE z3:: + DESTINATION "${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}" + ) +endif() set(Z3_INSTALL_TREE_CMAKE_CONFIG_FILE "${PROJECT_BINARY_DIR}/cmake/Z3Config.cmake") set(Z3_FIRST_PACKAGE_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}") set(Z3_SECOND_INCLUDE_DIR "") diff --git a/README-CMake.md b/README-CMake.md index c8fa0faae..26bde8f37 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -410,9 +410,10 @@ The following useful options can be passed to CMake whilst configuring. * ``Python3_EXECUTABLE`` - STRING. The python executable to use during the build. * ``Z3_ENABLE_TRACING_FOR_NON_DEBUG`` - BOOL. If set to ``TRUE`` enable tracing in non-debug builds, if set to ``FALSE`` disable tracing in non-debug builds. Note in debug builds tracing is always enabled. * ``Z3_BUILD_LIBZ3_SHARED`` - BOOL. If set to ``TRUE`` build libz3 as a shared library otherwise build as a static library. +* ``Z3_BUILD_LIBZ3_CORE`` - BOOL. If set to ``TRUE`` (default) build the core libz3 library. If set to ``FALSE``, skip building libz3 and look for a pre-installed library instead. This is useful when building only Python bindings on top of an already-installed libz3. * ``Z3_ENABLE_EXAMPLE_TARGETS`` - BOOL. If set to ``TRUE`` add the build targets for building the API examples. * ``Z3_USE_LIB_GMP`` - BOOL. If set to ``TRUE`` use the GNU multiple precision library. If set to ``FALSE`` use an internal implementation. -* ``Z3_BUILD_PYTHON_BINDINGS`` - BOOL. If set to ``TRUE`` then Z3's python bindings will be built. +* ``Z3_BUILD_PYTHON_BINDINGS`` - BOOL. If set to ``TRUE`` then Z3's python bindings will be built. When ``Z3_BUILD_LIBZ3_CORE`` is ``FALSE``, this will build only the Python bindings using a pre-installed libz3. * ``Z3_INSTALL_PYTHON_BINDINGS`` - BOOL. If set to ``TRUE`` and ``Z3_BUILD_PYTHON_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's Python bindings. * ``Z3_BUILD_DOTNET_BINDINGS`` - BOOL. If set to ``TRUE`` then Z3's .NET bindings will be built. * ``Z3_INSTALL_DOTNET_BINDINGS`` - BOOL. If set to ``TRUE`` and ``Z3_BUILD_DOTNET_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's .NET bindings. @@ -464,6 +465,49 @@ cmake -DCMAKE_BUILD_TYPE=Release -DZ3_ENABLE_TRACING_FOR_NON_DEBUG=FALSE ../ Z3 exposes various language bindings for its API. Below are some notes on building and/or installing these bindings when building Z3 with CMake. +### Python bindings + +#### Building Python bindings with libz3 + +The default behavior when ``Z3_BUILD_PYTHON_BINDINGS=ON`` is to build both the libz3 library +and the Python bindings together: + +``` +mkdir build +cd build +cmake -DZ3_BUILD_PYTHON_BINDINGS=ON -DZ3_BUILD_LIBZ3_SHARED=ON ../ +make +``` + +#### Building only Python bindings (using pre-installed libz3) + +For package managers like conda-forge that want to avoid rebuilding libz3 for each Python version, +you can build only the Python bindings by setting ``Z3_BUILD_LIBZ3_CORE=OFF``. This assumes +libz3 is already installed on your system: + +``` +# First, build and install libz3 (once) +mkdir build-libz3 +cd build-libz3 +cmake -DZ3_BUILD_LIBZ3_SHARED=ON -DCMAKE_INSTALL_PREFIX=/path/to/prefix ../ +make +make install + +# Then, build Python bindings for each Python version (quickly, without rebuilding libz3) +cd .. +mkdir build-py310 +cd build-py310 +cmake -DZ3_BUILD_LIBZ3_CORE=OFF \ + -DZ3_BUILD_PYTHON_BINDINGS=ON \ + -DCMAKE_INSTALL_PREFIX=/path/to/prefix \ + -DPython3_EXECUTABLE=/path/to/python3.10 ../ +make +make install +``` + +This approach significantly reduces build time when packaging for multiple Python versions, +as the expensive libz3 compilation happens only once. + ### Java bindings The CMake build uses the ``FindJava`` and ``FindJNI`` cmake modules to detect the diff --git a/cmake/compiler_warnings.cmake b/cmake/compiler_warnings.cmake index d631ee11a..ddd96c047 100644 --- a/cmake/compiler_warnings.cmake +++ b/cmake/compiler_warnings.cmake @@ -6,7 +6,13 @@ set(GCC_AND_CLANG_WARNINGS "-Wall" ) set(GCC_ONLY_WARNINGS "") -set(CLANG_ONLY_WARNINGS "") +# Disable C++98 compatibility warnings to prevent excessive warning output +# when building with clang-cl or when -Weverything is enabled. +# These warnings are not useful for Z3 since it requires C++20. +set(CLANG_ONLY_WARNINGS + "-Wno-c++98-compat" + "-Wno-c++98-compat-pedantic" +) set(MSVC_WARNINGS "/W3") ################################################################################ diff --git a/doc/mk_params_doc.py b/doc/mk_params_doc.py index 021cab3c3..849ce38bc 100644 --- a/doc/mk_params_doc.py +++ b/doc/mk_params_doc.py @@ -9,7 +9,8 @@ import sys import re import os -BUILD_DIR='../build' +build_env = dict(os.environ) +BUILD_DIR = '../' + build_env.get('Z3BUILD', 'build') OUTPUT_DIRECTORY=os.path.join(os.getcwd(), 'api') def parse_options(): diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index b6c865237..bcbe2be12 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -44,10 +44,20 @@ def classify_package(f, arch): return None def replace(src, dst): + """ + Replace destination file with source file. + + Removes the destination file if it exists, then moves the source file to the destination. + This ensures that the file is always moved, whether or not the destination exists. + + Previous buggy implementation only moved when removal failed, causing files to be + deleted but not replaced when the destination already existed. + """ try: os.remove(dst) except: - shutil.move(src, dst) + pass + shutil.move(src, dst) def unpack(packages, symbols, arch): # unzip files in packages diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 005c90ecb..5245b4c3c 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2686,8 +2686,6 @@ def mk_config(): CPPFLAGS = '%s -DZ3DEBUG -D_DEBUG' % CPPFLAGS else: CXXFLAGS = '%s -O3' % CXXFLAGS - if GPROF: - CXXFLAGS += '-fomit-frame-pointer' CPPFLAGS = '%s -DNDEBUG -D_EXTERNAL_RELEASE' % CPPFLAGS if is_CXX_clangpp(): CXXFLAGS = '%s -Wno-unknown-pragmas -Wno-overloaded-virtual -Wno-unused-value' % CXXFLAGS diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index a86e6536d..4e52e836a 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -14,7 +14,7 @@ stages: displayName: "Mac Build" timeoutInMinutes: 90 pool: - vmImage: "macOS-13" + vmImage: "macOS-latest" steps: - task: PythonScript@0 displayName: Build @@ -43,7 +43,7 @@ stages: - job: MacBuildArm64 displayName: "Mac ARM64 Build" pool: - vmImage: "macOS-13" + vmImage: "macOS-latest" steps: - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 - script: git clone https://github.com/z3prover/z3test z3test @@ -196,8 +196,7 @@ stages: - task: PublishPipelineArtifact@0 inputs: artifactName: 'ManyLinuxPythonBuildArm64' - targetPath: $(Build.ArtifactStagingDirectory) - + targetPath: $(Build.ArtifactStagingDirectory) - template: build-win-signed.yml parameters: diff --git a/scripts/update_api.py b/scripts/update_api.py index 08eeaaf68..f2372918a 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -558,6 +558,8 @@ def param2java(p): return "LongPtr" elif param_type(p) == STRING: return "StringPtr" + elif param_type(p) == BOOL: + return "BoolPtr" else: print("ERROR: unreachable code") assert(False) @@ -623,6 +625,7 @@ def mk_java(java_src, java_dir, package_name): java_native.write(' public static class StringPtr { public String value; }\n') java_native.write(' public static class ObjArrayPtr { public long[] value; }\n') java_native.write(' public static class UIntArrayPtr { public int[] value; }\n') + java_native.write(' public static class BoolPtr { public boolean value; }\n') java_native.write(' public static native void setInternalErrorHandler(long ctx);\n\n') java_native.write(' static {\n') @@ -1086,6 +1089,9 @@ def def_API(name, result, params): elif ty == INT64: log_c.write(" I(0);\n") exe_c.write("in.get_int64_addr(%s)" % i) + elif ty == BOOL: + log_c.write(" I(0);\n") + exe_c.write("in.get_bool_addr(%s)" % i) elif ty == VOID_PTR: log_c.write(" P(0);\n") exe_c.write("in.get_obj_addr(%s)" % i) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8441901e1..2af9a7170 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,29 +1,3 @@ -################################################################################ -# API header files -################################################################################ -# This lists the API header files that are scanned by -# some of the build rules to generate some files needed -# by the build -set(Z3_API_HEADER_FILES_TO_SCAN - z3_api.h - z3_ast_containers.h - z3_algebraic.h - z3_polynomial.h - z3_rcf.h - z3_fixedpoint.h - z3_optimization.h - z3_fpa.h - z3_spacer.h -) -set(Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN "") -foreach (header_file ${Z3_API_HEADER_FILES_TO_SCAN}) - set(full_path_api_header_file "${CMAKE_CURRENT_SOURCE_DIR}/api/${header_file}") - list(APPEND Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN "${full_path_api_header_file}") - if (NOT EXISTS "${full_path_api_header_file}") - message(FATAL_ERROR "API header file \"${full_path_api_header_file}\" does not exist") - endif() -endforeach() - ################################################################################ # Traverse directories each adding a Z3 component ################################################################################ @@ -305,7 +279,7 @@ endif() ################################################################################ cmake_dependent_option(Z3_BUILD_EXECUTABLE "Build the z3 executable" ON - "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) + "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR;Z3_BUILD_LIBZ3_CORE" OFF) if (Z3_BUILD_EXECUTABLE) add_subdirectory(shell) @@ -317,26 +291,13 @@ endif() cmake_dependent_option(Z3_BUILD_TEST_EXECUTABLES "Build test executables" ON - "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) + "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR;Z3_BUILD_LIBZ3_CORE" OFF) if (Z3_BUILD_TEST_EXECUTABLES) add_subdirectory(test) endif() - -################################################################################ -# Z3 API bindings -################################################################################ -option(Z3_BUILD_PYTHON_BINDINGS "Build Python bindings for Z3" OFF) -if (Z3_BUILD_PYTHON_BINDINGS) - if (NOT Z3_BUILD_LIBZ3_SHARED) - message(FATAL_ERROR "The python bindings will not work with a static libz3. " - "You either need to disable Z3_BUILD_PYTHON_BINDINGS or enable Z3_BUILD_LIBZ3_SHARED") - endif() - add_subdirectory(api/python) -endif() - ################################################################################ # .NET bindings ################################################################################ diff --git a/src/api/api_arith.cpp b/src/api/api_arith.cpp index bba2cf0c3..17810a494 100644 --- a/src/api/api_arith.cpp +++ b/src/api/api_arith.cpp @@ -156,8 +156,15 @@ extern "C" { } bool Z3_API Z3_is_algebraic_number(Z3_context c, Z3_ast a) { + Z3_TRY; LOG_Z3_is_algebraic_number(c, a); + RESET_ERROR_CODE(); + if (!is_expr(a)) { + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); + return false; + } return mk_c(c)->autil().is_irrational_algebraic_numeral(to_expr(a)); + Z3_CATCH_RETURN(false); } Z3_ast Z3_API Z3_get_algebraic_number_lower(Z3_context c, Z3_ast a, unsigned precision) { diff --git a/src/api/api_array.cpp b/src/api/api_array.cpp index 6613892df..e0f71f2b7 100644 --- a/src/api/api_array.cpp +++ b/src/api/api_array.cpp @@ -268,7 +268,6 @@ extern "C" { MK_UNARY(Z3_mk_set_complement, mk_c(c)->get_array_fid(), OP_SET_COMPLEMENT, SKIP); MK_BINARY(Z3_mk_set_subset, mk_c(c)->get_array_fid(), OP_SET_SUBSET, SKIP); MK_BINARY(Z3_mk_array_ext, mk_c(c)->get_array_fid(), OP_ARRAY_EXT, SKIP); - MK_BINARY(Z3_mk_set_has_size, mk_c(c)->get_array_fid(), OP_SET_HAS_SIZE, SKIP); Z3_ast Z3_API Z3_mk_as_array(Z3_context c, Z3_func_decl f) { Z3_TRY; diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 36f8cc34f..ff36e87d5 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -1192,8 +1192,6 @@ extern "C" { case OP_SET_SUBSET: return Z3_OP_SET_SUBSET; case OP_AS_ARRAY: return Z3_OP_AS_ARRAY; case OP_ARRAY_EXT: return Z3_OP_ARRAY_EXT; - case OP_SET_CARD: return Z3_OP_SET_CARD; - case OP_SET_HAS_SIZE: return Z3_OP_SET_HAS_SIZE; default: return Z3_OP_INTERNAL; } diff --git a/src/api/api_fpa.cpp b/src/api/api_fpa.cpp index 3c350ed18..c0cfcd079 100644 --- a/src/api/api_fpa.cpp +++ b/src/api/api_fpa.cpp @@ -896,7 +896,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - bool Z3_API Z3_fpa_get_numeral_sign(Z3_context c, Z3_ast t, int * sgn) { + bool Z3_API Z3_fpa_get_numeral_sign(Z3_context c, Z3_ast t, bool * sgn) { Z3_TRY; LOG_Z3_fpa_get_numeral_sign(c, t, sgn); RESET_ERROR_CODE(); @@ -1224,6 +1224,20 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + bool Z3_API Z3_fpa_is_numeral(Z3_context c, Z3_ast t) { + Z3_TRY; + LOG_Z3_fpa_is_numeral(c, t); + RESET_ERROR_CODE(); + api::context * ctx = mk_c(c); + fpa_util & fu = ctx->fpautil(); + if (!is_expr(t)) { + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); + return false; + } + return fu.is_numeral(to_expr(t)); + Z3_CATCH_RETURN(false); + } + bool Z3_API Z3_fpa_is_numeral_nan(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_is_numeral_nan(c, t); diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index c3774dd85..7da23cd6e 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -481,4 +481,22 @@ extern "C" { Z3_CATCH; } + Z3_optimize Z3_API Z3_optimize_translate(Z3_context c, Z3_optimize o, Z3_context target) { + Z3_TRY; + LOG_Z3_optimize_translate(c, o, target); + RESET_ERROR_CODE(); + + // Translate the opt::context to the target manager + opt::context* translated_ctx = to_optimize_ptr(o)->translate(mk_c(target)->m()); + + // Create a new Z3_optimize_ref in the target context + Z3_optimize_ref* result_ref = alloc(Z3_optimize_ref, *mk_c(target)); + result_ref->m_opt = translated_ctx; + mk_c(target)->save_object(result_ref); + + Z3_optimize result = of_optimize(result_ref); + RETURN_Z3(result); + Z3_CATCH_RETURN(nullptr); + } + }; diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index 684e8c617..3bef38f0e 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -385,7 +385,7 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } - int Z3_API Z3_rcf_interval(Z3_context c, Z3_rcf_num a, int * lower_is_inf, int * lower_is_open, Z3_rcf_num * lower, int * upper_is_inf, int * upper_is_open, Z3_rcf_num * upper) { + int Z3_API Z3_rcf_interval(Z3_context c, Z3_rcf_num a, bool * lower_is_inf, bool * lower_is_open, Z3_rcf_num * lower, bool * upper_is_inf, bool * upper_is_open, Z3_rcf_num * upper) { Z3_TRY; LOG_Z3_rcf_interval(c, a, lower_is_inf, lower_is_open, lower, upper_is_inf, upper_is_open, upper); RESET_ERROR_CODE(); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 2acb010cb..23d852000 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -3313,6 +3313,7 @@ namespace z3 { Z3_optimize m_opt; public: + struct translate {}; class handle final { unsigned m_h; public: @@ -3320,6 +3321,12 @@ namespace z3 { unsigned h() const { return m_h; } }; optimize(context& c):object(c) { m_opt = Z3_mk_optimize(c); Z3_optimize_inc_ref(c, m_opt); } + optimize(context & c, optimize const& src, translate): object(c) { + Z3_optimize o = Z3_optimize_translate(src.ctx(), src, c); + check_error(); + m_opt = o; + Z3_optimize_inc_ref(c, m_opt); + } optimize(optimize const & o):object(o), m_opt(o.m_opt) { Z3_optimize_inc_ref(o.ctx(), o.m_opt); } diff --git a/src/api/dotnet/FPNum.cs b/src/api/dotnet/FPNum.cs index e21355f72..f7074ea44 100644 --- a/src/api/dotnet/FPNum.cs +++ b/src/api/dotnet/FPNum.cs @@ -50,8 +50,8 @@ namespace Microsoft.Z3 { get { - int res = 0; - if (Native.Z3_fpa_get_numeral_sign(Context.nCtx, NativeObject, ref res) == 0) + byte res = 0; + if (0 == Native.Z3_fpa_get_numeral_sign(Context.nCtx, NativeObject, ref res)) throw new Z3Exception("Sign is not a Boolean value"); return res != 0; } diff --git a/src/api/dotnet/Log.cs b/src/api/dotnet/Log.cs index a94c29bc6..d63c53c8c 100644 --- a/src/api/dotnet/Log.cs +++ b/src/api/dotnet/Log.cs @@ -41,7 +41,7 @@ namespace Microsoft.Z3 public static bool Open(string filename) { m_is_open = true; - return Native.Z3_open_log(filename) == 1; + return 0 != Native.Z3_open_log(filename); } /// diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index 3bc06de16..3a54df5d9 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -156,7 +156,7 @@ namespace Microsoft.Z3 /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination - /// of the Boolean variables provided using + /// of the Boolean variables provided using /// and the Boolean literals /// provided using with assumptions. /// diff --git a/src/api/java/FPNum.java b/src/api/java/FPNum.java index 813e82889..2995e97e4 100644 --- a/src/api/java/FPNum.java +++ b/src/api/java/FPNum.java @@ -27,10 +27,10 @@ public class FPNum extends FPExpr * @throws Z3Exception */ public boolean getSign() { - Native.IntPtr res = new Native.IntPtr(); + Native.BoolPtr res = new Native.BoolPtr(); if (!Native.fpaGetNumeralSign(getContext().nCtx(), getNativeObject(), res)) throw new Z3Exception("Sign is not a Boolean value"); - return res.value != 0; + return res.value; } /** diff --git a/src/api/java/Log.java b/src/api/java/Log.java index 7dc9a1ef1..f427c5175 100644 --- a/src/api/java/Log.java +++ b/src/api/java/Log.java @@ -36,7 +36,7 @@ public final class Log public static boolean open(String filename) { m_is_open = true; - return Native.openLog(filename) == 1; + return Native.openLog(filename); } /** diff --git a/src/api/js/package-lock.json b/src/api/js/package-lock.json index 16b017d3b..a93b8c8a8 100644 --- a/src/api/js/package-lock.json +++ b/src/api/js/package-lock.json @@ -46,12 +46,15 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -236,19 +239,21 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -263,38 +268,28 @@ } }, "node_modules/@babel/helpers": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", - "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.4", - "@babel/types": "^7.19.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz", - "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -465,26 +460,25 @@ } }, "node_modules/@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -511,19 +505,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/traverse/node_modules/@babel/generator": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", @@ -585,78 +566,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -672,14 +581,14 @@ } }, "node_modules/@babel/types": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", - "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1968,10 +1877,11 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2250,10 +2160,11 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, + "license": "MIT", "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -2505,10 +2416,11 @@ } }, "node_modules/execa/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3645,6 +3557,117 @@ "node": ">=8" } }, + "node_modules/jest-cli": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", + "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-config": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", @@ -5283,110 +5306,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest/node_modules/jest-cli": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", - "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", - "dev": true, - "dependencies": { - "@jest/core": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5394,10 +5313,11 @@ "dev": true }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -5914,10 +5834,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -6068,12 +5989,6 @@ "node": ">=6" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==", - "dev": true - }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -6537,15 +6452,6 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6722,9 +6628,9 @@ } }, "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/api/js/src/high-level/high-level.ts b/src/api/js/src/high-level/high-level.ts index f53f2d8ca..1fd6097df 100644 --- a/src/api/js/src/high-level/high-level.ts +++ b/src/api/js/src/high-level/high-level.ts @@ -1307,10 +1307,6 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new SetImpl(check(Z3.mk_set_difference(contextPtr, a.ast, b.ast))); } - function SetHasSize>(set: SMTSet, size: bigint | number | string | IntNum): Bool { - const a = typeof size === 'object'? Int.sort().cast(size) : Int.sort().cast(size); - return new BoolImpl(check(Z3.mk_set_has_size(contextPtr, set.ast, a.ast))); - } function SetAdd>(set: SMTSet, elem: CoercibleToMap, Name>): SMTSet { const arg = set.elemSort().cast(elem as any); @@ -2644,9 +2640,6 @@ export function createApi(Z3: Z3Core): Z3HighLevel { diff(b: SMTSet): SMTSet { return SetDifference(this, b); } - hasSize(size: string | number | bigint | IntNum): Bool { - return SetHasSize(this, size); - } add(elem: CoercibleToMap, Name>): SMTSet { return SetAdd(this, elem); } @@ -3292,7 +3285,6 @@ export function createApi(Z3: Z3Core): Z3HighLevel { SetUnion, SetIntersect, SetDifference, - SetHasSize, SetAdd, SetDel, SetComplement, @@ -3317,6 +3309,6 @@ export function createApi(Z3: Z3Core): Z3HighLevel { setParam, resetParams, - Context: createContext, + Context: createContext as ContextCtor, }; } diff --git a/src/api/js/src/high-level/types.ts b/src/api/js/src/high-level/types.ts index 3c1ebaa10..bd4f9dcc2 100644 --- a/src/api/js/src/high-level/types.ts +++ b/src/api/js/src/high-level/types.ts @@ -125,6 +125,7 @@ export type CheckSatResult = 'sat' | 'unsat' | 'unknown'; /** @hidden */ export interface ContextCtor { (name: Name, options?: Record): Context; + new (name: Name, options?: Record): Context; } export interface Context { @@ -629,9 +630,6 @@ export interface Context { /** @category Operations */ SetDifference>(a: SMTSet, b: SMTSet): SMTSet; - - /** @category Operations */ - SetHasSize>(set: SMTSet, size: bigint | number | string | IntNum): Bool; /** @category Operations */ SetAdd>(set: SMTSet, elem: CoercibleToMap, Name>): SMTSet; @@ -1649,7 +1647,6 @@ export interface SMTSet[]): SMTSet; diff(b: SMTSet): SMTSet; - hasSize(size: bigint | number | string | IntNum): Bool; add(elem: CoercibleToMap, Name>): SMTSet; del(elem: CoercibleToMap, Name>): SMTSet; diff --git a/src/api/js/src/node.ts b/src/api/js/src/node.ts index 6456d8979..9e503edcd 100644 --- a/src/api/js/src/node.ts +++ b/src/api/js/src/node.ts @@ -11,7 +11,7 @@ export * from './low-level/types.__GENERATED__'; * The main entry point to the Z3 API * * ```typescript - * import { init, sat } from 'z3-solver'; + * import { init } from 'z3-solver'; * * const { Context } = await init(); * const { Solver, Int } = new Context('main'); @@ -22,7 +22,7 @@ export * from './low-level/types.__GENERATED__'; * const solver = new Solver(); * solver.add(x.add(2).le(y.sub(10))); // x + 2 <= y - 10 * - * if (await solver.check() !== sat) { + * if (await solver.check() !== 'sat') { * throw new Error("couldn't find a solution") * } * const model = solver.model(); diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index cc7294aba..86ed05d98 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -15,7 +15,7 @@ type context = Z3native.context module Log = struct let open_ filename = - lbool_of_int (Z3native.open_log filename) = L_TRUE + (Z3native.open_log filename) let close = Z3native.close_log let append = Z3native.append_log end diff --git a/src/api/python/CMakeLists.txt b/src/api/python/CMakeLists.txt index 5da66dfe4..e420c4c04 100644 --- a/src/api/python/CMakeLists.txt +++ b/src/api/python/CMakeLists.txt @@ -70,13 +70,32 @@ else() endif() # Link libz3 into the python directory so bindings work out of the box -add_custom_command(OUTPUT "${z3py_bindings_build_dest}/libz3${CMAKE_SHARED_MODULE_SUFFIX}" - COMMAND "${CMAKE_COMMAND}" "-E" "${LINK_COMMAND}" - "${PROJECT_BINARY_DIR}/libz3${CMAKE_SHARED_MODULE_SUFFIX}" - "${z3py_bindings_build_dest}/libz3${CMAKE_SHARED_MODULE_SUFFIX}" - DEPENDS libz3 - COMMENT "Linking libz3 into python directory" -) +# Handle both built libz3 and pre-installed libz3 +if (TARGET libz3) + # Get the libz3 location - handle both regular and imported targets + get_target_property(LIBZ3_IS_IMPORTED libz3 IMPORTED) + if (LIBZ3_IS_IMPORTED) + # For imported targets, get the IMPORTED_LOCATION + get_target_property(LIBZ3_SOURCE_PATH libz3 IMPORTED_LOCATION) + # No dependency on libz3 target since it's pre-built + set(LIBZ3_DEPENDS "") + else() + # For regular targets, use the build output location + set(LIBZ3_SOURCE_PATH "${PROJECT_BINARY_DIR}/libz3${CMAKE_SHARED_MODULE_SUFFIX}") + set(LIBZ3_DEPENDS libz3) + endif() + + add_custom_command(OUTPUT "${z3py_bindings_build_dest}/libz3${CMAKE_SHARED_MODULE_SUFFIX}" + COMMAND "${CMAKE_COMMAND}" "-E" "${LINK_COMMAND}" + "${LIBZ3_SOURCE_PATH}" + "${z3py_bindings_build_dest}/libz3${CMAKE_SHARED_MODULE_SUFFIX}" + DEPENDS ${LIBZ3_DEPENDS} + COMMENT "Linking libz3 into python directory" + ) +else() + message(FATAL_ERROR "libz3 target not found. Cannot build Python bindings.") +endif() + # Convenient top-level target add_custom_target(build_z3_python_bindings diff --git a/src/api/python/setup.py b/src/api/python/setup.py index 39ae7df72..c51bd8497 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -24,6 +24,7 @@ ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) SRC_DIR_LOCAL = os.path.join(ROOT_DIR, 'core') SRC_DIR_REPO = os.path.join(ROOT_DIR, '..', '..', '..') SRC_DIR = SRC_DIR_LOCAL if os.path.exists(SRC_DIR_LOCAL) else SRC_DIR_REPO +BUILD_DIR = build_env.get('Z3BUILD', 'build') IS_SINGLE_THREADED = False ENABLE_LTO = True @@ -34,7 +35,7 @@ IS_PYODIDE = 'PYODIDE_ROOT' in os.environ and os.environ.get('_PYTHON_HOST_PLATF # determine where binaries are RELEASE_DIR = os.environ.get('PACKAGE_FROM_RELEASE', None) if RELEASE_DIR is None: - BUILD_DIR = os.path.join(SRC_DIR, 'build') # implicit in configure script + BUILD_DIR = os.path.join(SRC_DIR, BUILD_DIR) # implicit in configure script HEADER_DIRS = [os.path.join(SRC_DIR, 'src', 'api'), os.path.join(SRC_DIR, 'src', 'api', 'c++')] RELEASE_METADATA = None if IS_PYODIDE: diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 5a6ce8ab9..a04848631 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -5010,13 +5010,6 @@ def Ext(a, b): _z3_assert(is_array_sort(a) and (is_array(b) or b.is_lambda()), "arguments must be arrays") return _to_expr_ref(Z3_mk_array_ext(ctx.ref(), a.as_ast(), b.as_ast()), ctx) - -def SetHasSize(a, k): - ctx = a.ctx - k = _py2expr(k, ctx) - return _to_expr_ref(Z3_mk_set_has_size(ctx.ref(), a.as_ast(), k.as_ast()), ctx) - - def is_select(a): """Return `True` if `a` is a Z3 array select application. @@ -10039,7 +10032,7 @@ class FPNumRef(FPRef): """ def sign(self): - num = (ctypes.c_int)() + num = ctypes.c_bool() nsign = Z3_fpa_get_numeral_sign(self.ctx.ref(), self.as_ast(), byref(num)) if nsign is False: raise Z3Exception("error retrieving the sign of a numeral.") diff --git a/src/api/python/z3/z3printer.py b/src/api/python/z3/z3printer.py index d7ee17f4a..29d3e0db1 100644 --- a/src/api/python/z3/z3printer.py +++ b/src/api/python/z3/z3printer.py @@ -831,7 +831,7 @@ class Formatter: else: _z3_assert(z3.is_fp_value(a), "expecting FP num ast") r = [] - sgn = c_int(0) + sgn = ctypes.c_bool() sgnb = Z3_fpa_get_numeral_sign(a.ctx_ref(), a.ast, byref(sgn)) exp = Z3_fpa_get_numeral_exponent_string(a.ctx_ref(), a.ast, False) sig = Z3_fpa_get_numeral_significand_string(a.ctx_ref(), a.ast) @@ -861,7 +861,7 @@ class Formatter: else: _z3_assert(z3.is_fp_value(a), "expecting FP num ast") r = [] - sgn = (ctypes.c_int)(0) + sgn = ctypes.c_bool() sgnb = Z3_fpa_get_numeral_sign(a.ctx_ref(), a.ast, byref(sgn)) exp = Z3_fpa_get_numeral_exponent_string(a.ctx_ref(), a.ast, False) sig = Z3_fpa_get_numeral_significand_string(a.ctx_ref(), a.ast) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index c5d3933ca..141c32e5a 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1037,8 +1037,6 @@ typedef enum { Z3_OP_SET_SUBSET, Z3_OP_AS_ARRAY, Z3_OP_ARRAY_EXT, - Z3_OP_SET_HAS_SIZE, - Z3_OP_SET_CARD, // Bit-vectors Z3_OP_BNUM = 0x400, @@ -3316,12 +3314,6 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_as_array(Z3_context c, Z3_func_decl f); - /** - \brief Create predicate that holds if Boolean array \c set has \c k elements set to true. - - def_API('Z3_mk_set_has_size', AST, (_in(CONTEXT), _in(AST), _in(AST))) - */ - Z3_ast Z3_API Z3_mk_set_has_size(Z3_context c, Z3_ast set, Z3_ast k); /**@}*/ @@ -5877,7 +5869,7 @@ extern "C" { \sa Z3_append_log \sa Z3_close_log - extra_API('Z3_open_log', INT, (_in(STRING),)) + extra_API('Z3_open_log', BOOL, (_in(STRING),)) */ bool Z3_API Z3_open_log(Z3_string filename); @@ -7140,7 +7132,7 @@ extern "C" { \brief retrieve the decision depth of Boolean literals (variables or their negations). Assumes a check-sat call and no other calls (to extract models) have been invoked. - def_API('Z3_solver_get_levels', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(UINT), _in_array(3, UINT))) + def_API('Z3_solver_get_levels', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(UINT), _out_array(3, UINT))) */ void Z3_API Z3_solver_get_levels(Z3_context c, Z3_solver s, Z3_ast_vector literals, unsigned sz, unsigned levels[]); diff --git a/src/api/z3_fpa.h b/src/api/z3_fpa.h index 9c4b22153..6bdbdae0e 100644 --- a/src/api/z3_fpa.h +++ b/src/api/z3_fpa.h @@ -1089,6 +1089,22 @@ extern "C" { */ unsigned Z3_API Z3_fpa_get_sbits(Z3_context c, Z3_sort s); + /** + \brief Checks whether a given ast is a floating-point numeral. + + \param c logical context + \param t an ast + + \sa Z3_fpa_is_numeral_nan + \sa Z3_fpa_is_numeral_inf + \sa Z3_fpa_is_numeral_normal + \sa Z3_fpa_is_numeral_subnormal + \sa Z3_fpa_is_numeral_zero + + def_API('Z3_fpa_is_numeral', BOOL, (_in(CONTEXT), _in(AST))) + */ + bool Z3_API Z3_fpa_is_numeral(Z3_context c, Z3_ast t); + /** \brief Checks whether a given floating-point numeral is a NaN. @@ -1220,12 +1236,12 @@ extern "C" { \param sgn the retrieved sign \returns true if \c t corresponds to a floating point numeral, otherwise invokes exception handler or returns false - Remarks: sets \c sgn to 0 if `t' is positive and to 1 otherwise, except for + Remarks: sets \c sgn to \c false if `t' is positive and to \c true otherwise, except for NaN, which is an invalid argument. - def_API('Z3_fpa_get_numeral_sign', BOOL, (_in(CONTEXT), _in(AST), _out(INT))) + def_API('Z3_fpa_get_numeral_sign', BOOL, (_in(CONTEXT), _in(AST), _out(BOOL))) */ - bool Z3_API Z3_fpa_get_numeral_sign(Z3_context c, Z3_ast t, int * sgn); + bool Z3_API Z3_fpa_get_numeral_sign(Z3_context c, Z3_ast t, bool * sgn); /** \brief Return the significand value of a floating-point numeral as a string. diff --git a/src/api/z3_optimization.h b/src/api/z3_optimization.h index 4e585efb2..739dc2307 100644 --- a/src/api/z3_optimization.h +++ b/src/api/z3_optimization.h @@ -379,6 +379,23 @@ extern "C" { void* ctx, Z3_model_eh model_eh); + /** + \brief Copy an optimization context from a source to a target context. + + This function allows translating an optimization context from one Z3_context + to another. This is useful when working with multiple contexts and needing to + transfer optimization problems between them. + + \param c Source context containing the optimization context to translate + \param o The optimization context to translate from the source context + \param target Target context where the optimization context will be created + + \return A new optimization context in the target context with the same state + + def_API('Z3_optimize_translate', OPTIMIZE, (_in(CONTEXT), _in(OPTIMIZE), _in(CONTEXT))) + */ + Z3_optimize Z3_API Z3_optimize_translate(Z3_context c, Z3_optimize o, Z3_context target); + /**@}*/ /**@}*/ diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h index b3842f1b6..4286f1c43 100644 --- a/src/api/z3_rcf.h +++ b/src/api/z3_rcf.h @@ -272,9 +272,9 @@ extern "C" { \pre Z3_rcf_is_algebraic(ctx, a); - def_API('Z3_rcf_interval', INT, (_in(CONTEXT), _in(RCF_NUM), _out(INT), _out(INT), _out(RCF_NUM), _out(INT), _out(INT), _out(RCF_NUM))) + def_API('Z3_rcf_interval', INT, (_in(CONTEXT), _in(RCF_NUM), _out(BOOL), _out(BOOL), _out(RCF_NUM), _out(BOOL), _out(BOOL), _out(RCF_NUM))) */ - int Z3_API Z3_rcf_interval(Z3_context c, Z3_rcf_num a, int * lower_is_inf, int * lower_is_open, Z3_rcf_num * lower, int * upper_is_inf, int * upper_is_open, Z3_rcf_num * upper); + int Z3_API Z3_rcf_interval(Z3_context c, Z3_rcf_num a, bool * lower_is_inf, bool * lower_is_open, Z3_rcf_num * lower, bool * upper_is_inf, bool * upper_is_open, Z3_rcf_num * upper); /** \brief Return the number of sign conditions of an algebraic number. diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index 9272ca0fc..79488f6d1 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -662,6 +662,11 @@ struct z3_replayer::imp { return v.data(); } + bool * get_bool_addr(unsigned pos) { + check_arg(pos, INT64); + return reinterpret_cast(&(m_args[pos].m_int)); + } + int * get_int_addr(unsigned pos) { check_arg(pos, INT64); return reinterpret_cast(&(m_args[pos].m_int)); @@ -790,6 +795,10 @@ void ** z3_replayer::get_obj_array(unsigned pos) const { return m_imp->get_obj_array(pos); } +bool * z3_replayer::get_bool_addr(unsigned pos) { + return m_imp->get_bool_addr(pos); +} + int * z3_replayer::get_int_addr(unsigned pos) { return m_imp->get_int_addr(pos); } diff --git a/src/api/z3_replayer.h b/src/api/z3_replayer.h index 8c77f0e0a..11b761a4d 100644 --- a/src/api/z3_replayer.h +++ b/src/api/z3_replayer.h @@ -53,6 +53,7 @@ public: Z3_symbol * get_symbol_array(unsigned pos) const; void ** get_obj_array(unsigned pos) const; + bool * get_bool_addr(unsigned pos); int * get_int_addr(unsigned pos); int64_t * get_int64_addr(unsigned pos); unsigned * get_uint_addr(unsigned pos); diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index db927e431..3d2bbec17 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -188,8 +188,12 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) { m_to_real_decl = m->mk_func_decl(symbol("to_real"), i, r, func_decl_info(id, OP_TO_REAL)); m->inc_ref(m_to_real_decl); + m_r_to_real_decl = m->mk_func_decl(symbol("to_real"), r, r, func_decl_info(id, OP_TO_REAL)); + m->inc_ref(m_r_to_real_decl); m_to_int_decl = m->mk_func_decl(symbol("to_int"), r, i, func_decl_info(id, OP_TO_INT)); m->inc_ref(m_to_int_decl); + m_i_to_int_decl = m->mk_func_decl(symbol("to_int"), i, i, func_decl_info(id, OP_TO_INT)); + m->inc_ref(m_i_to_int_decl); m_is_int_decl = m->mk_func_decl(symbol("is_int"), r, m->mk_bool_sort(), func_decl_info(id, OP_IS_INT)); m->inc_ref(m_is_int_decl); @@ -311,6 +315,8 @@ void arith_decl_plugin::finalize() { DEC_REF(m_i_rem_decl); DEC_REF(m_to_real_decl); DEC_REF(m_to_int_decl); + DEC_REF(m_r_to_real_decl); + DEC_REF(m_i_to_int_decl); DEC_REF(m_is_int_decl); DEC_REF(m_i_power_decl); DEC_REF(m_r_power_decl); @@ -368,8 +374,8 @@ inline func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, bool is_real) { return m_manager->mk_func_decl(symbol("^0"), m_real_decl, m_real_decl, m_real_decl, func_decl_info(m_family_id, OP_POWER0)); } return m_manager->mk_func_decl(symbol("^0"), m_int_decl, m_int_decl, m_real_decl, func_decl_info(m_family_id, OP_POWER0)); - case OP_TO_REAL: return m_to_real_decl; - case OP_TO_INT: return m_to_int_decl; + case OP_TO_REAL: return is_real ? m_r_to_real_decl : m_to_real_decl; + case OP_TO_INT: return is_real ? m_to_int_decl : m_i_to_int_decl; case OP_IS_INT: return m_is_int_decl; case OP_POWER: return is_real ? m_r_power_decl : m_i_power_decl; case OP_ABS: return is_real ? m_r_abs_decl : m_i_abs_decl; diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 275d39cf1..9dbaeeccd 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -120,11 +120,13 @@ protected: func_decl * m_i_mod_decl; func_decl * m_i_rem_decl; - func_decl * m_to_real_decl; - func_decl * m_to_int_decl; - func_decl * m_is_int_decl; - func_decl * m_r_power_decl; - func_decl * m_i_power_decl; + func_decl * m_to_real_decl = nullptr; + func_decl * m_to_int_decl = nullptr; + func_decl * m_r_to_real_decl = nullptr; + func_decl * m_i_to_int_decl = nullptr; + func_decl * m_is_int_decl = nullptr; + func_decl * m_r_power_decl = nullptr; + func_decl * m_i_power_decl = nullptr; func_decl * m_r_abs_decl; func_decl * m_i_abs_decl; diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index b820b5213..198514671 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -35,9 +35,7 @@ array_decl_plugin::array_decl_plugin(): m_set_complement_sym("complement"), m_set_subset_sym("subset"), m_array_ext_sym("array-ext"), - m_as_array_sym("as-array"), - m_set_has_size_sym("set-has-size"), - m_set_card_sym("card") { + m_as_array_sym("as-array") { } #define ARRAY_SORT_STR "Array" @@ -442,40 +440,6 @@ func_decl * array_decl_plugin::mk_set_subset(unsigned arity, sort * const * doma func_decl_info(m_family_id, OP_SET_SUBSET)); } -func_decl * array_decl_plugin::mk_set_card(unsigned arity, sort * const* domain) { - if (arity != 1) { - m_manager->raise_exception("card takes only one argument"); - return nullptr; - } - - arith_util arith(*m_manager); - if (!is_array_sort(domain[0]) || !m_manager->is_bool(get_array_range(domain[0]))) { - m_manager->raise_exception("card expects an array of Booleans"); - } - sort * int_sort = arith.mk_int(); - return m_manager->mk_func_decl(m_set_card_sym, arity, domain, int_sort, - func_decl_info(m_family_id, OP_SET_CARD)); -} - -func_decl * array_decl_plugin::mk_set_has_size(unsigned arity, sort * const* domain) { - if (arity != 2) { - m_manager->raise_exception("set-has-size takes two arguments"); - return nullptr; - } - m_manager->raise_exception("set-has-size is not supported"); - // domain[0] is a Boolean array, - // domain[1] is Int - arith_util arith(*m_manager); - if (!arith.is_int(domain[1])) { - m_manager->raise_exception("set-has-size expects second argument to be an integer"); - } - if (!is_array_sort(domain[0]) || !m_manager->is_bool(get_array_range(domain[0]))) { - m_manager->raise_exception("set-has-size expects first argument to be an array of Booleans"); - } - sort * bool_sort = m_manager->mk_bool_sort(); - return m_manager->mk_func_decl(m_set_has_size_sym, arity, domain, bool_sort, - func_decl_info(m_family_id, OP_SET_HAS_SIZE)); -} func_decl * array_decl_plugin::mk_as_array(func_decl * f) { vector parameters; @@ -541,10 +505,6 @@ func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters return mk_set_complement(arity, domain); case OP_SET_SUBSET: return mk_set_subset(arity, domain); - case OP_SET_HAS_SIZE: - return mk_set_has_size(arity, domain); - case OP_SET_CARD: - return mk_set_card(arity, domain); case OP_AS_ARRAY: { if (num_parameters != 1 || !parameters[0].is_ast() || diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index 50c9cf5d1..36403f3ca 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -62,8 +62,6 @@ enum array_op_kind { OP_SET_DIFFERENCE, OP_SET_COMPLEMENT, OP_SET_SUBSET, - OP_SET_HAS_SIZE, - OP_SET_CARD, OP_AS_ARRAY, // used for model construction LAST_ARRAY_OP }; @@ -81,8 +79,6 @@ class array_decl_plugin : public decl_plugin { symbol m_set_subset_sym; symbol m_array_ext_sym; symbol m_as_array_sym; - symbol m_set_has_size_sym; - symbol m_set_card_sym; bool check_set_arguments(unsigned arity, sort * const * domain); @@ -110,10 +106,6 @@ class array_decl_plugin : public decl_plugin { func_decl * mk_as_array(func_decl * f); - func_decl* mk_set_has_size(unsigned arity, sort * const* domain); - - func_decl* mk_set_card(unsigned arity, sort * const* domain); - bool is_array_sort(sort* s) const; public: array_decl_plugin(); @@ -173,8 +165,6 @@ public: bool is_complement(expr* n) const { return is_app_of(n, m_fid, OP_SET_COMPLEMENT); } bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); } bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); } - bool is_set_has_size(expr* e) const { return is_app_of(e, m_fid, OP_SET_HAS_SIZE); } - bool is_set_card(expr* e) const { return is_app_of(e, m_fid, OP_SET_CARD); } bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); } bool is_store(func_decl* f) const { return is_decl_of(f, m_fid, OP_STORE); } bool is_const(func_decl* f) const { return is_decl_of(f, m_fid, OP_CONST_ARRAY); } @@ -182,8 +172,6 @@ public: bool is_union(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_UNION); } bool is_intersect(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_INTERSECT); } bool is_as_array(func_decl* f) const { return is_decl_of(f, m_fid, OP_AS_ARRAY); } - bool is_set_has_size(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_HAS_SIZE); } - bool is_set_card(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_CARD); } bool is_default(func_decl* f) const { return is_decl_of(f, m_fid, OP_ARRAY_DEFAULT); } bool is_default(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_DEFAULT); } bool is_subset(expr const* n) const { return is_app_of(n, m_fid, OP_SET_SUBSET); } @@ -307,14 +295,6 @@ public: return m_manager.mk_app(m_fid, OP_SET_UNION, s1, s2); } - app* mk_has_size(expr* set, expr* n) { - return m_manager.mk_app(m_fid, OP_SET_HAS_SIZE, set, n); - } - - app* mk_card(expr* set) { - return m_manager.mk_app(m_fid, OP_SET_CARD, set); - } - func_decl * mk_array_ext(sort* domain, unsigned i); sort * mk_array_sort(sort* dom, sort* range) { return mk_array_sort(1, &dom, range); } diff --git a/src/ast/euf/euf_ac_plugin.cpp b/src/ast/euf/euf_ac_plugin.cpp index e89f18d58..431147097 100644 --- a/src/ast/euf/euf_ac_plugin.cpp +++ b/src/ast/euf/euf_ac_plugin.cpp @@ -1057,7 +1057,6 @@ namespace euf { SASSERT(is_correct_ref_count(dst, dst_counts)); SASSERT(&src_r.m_nodes != &dst); unsigned sz = dst.size(), j = 0; - bool change = false; for (unsigned i = 0; i < sz; ++i) { auto* n = dst[i]; unsigned id = n->id(); diff --git a/src/ast/euf/euf_arith_plugin.cpp b/src/ast/euf/euf_arith_plugin.cpp index 487cc1392..82ce7f2a9 100644 --- a/src/ast/euf/euf_arith_plugin.cpp +++ b/src/ast/euf/euf_arith_plugin.cpp @@ -59,7 +59,6 @@ namespace euf { expr* e = n->get_expr(), * x, * y; // x - y = x + (* -1 y) if (a.is_sub(e, x, y)) { - auto& m = g.get_manager(); auto e1 = a.mk_numeral(rational(-1), a.is_int(x)); auto n1 = g.find(e1) ? g.find(e1) : g.mk(e1, 0, 0, nullptr); auto e2 = a.mk_mul(e1, y); diff --git a/src/ast/euf/ho_matcher.cpp b/src/ast/euf/ho_matcher.cpp index 4a313ee61..696f868b2 100644 --- a/src/ast/euf/ho_matcher.cpp +++ b/src/ast/euf/ho_matcher.cpp @@ -293,8 +293,7 @@ namespace euf { // v - offset |-> t if (is_meta_var(p, wi.pat_offset()) && is_closed(t, 0, wi.term_offset())) { auto v = to_var(p); - auto idx = v->get_idx() - wi.pat_offset(); - SASSERT(!m_subst.get(idx)); // reduce ensures meta variables are not in substitutions + SASSERT(!m_subst.get(v->get_idx() - wi.pat_offset())); // reduce ensures meta variables are not in substitutions add_binding(v, wi.pat_offset(), t); wi.set_done(); return true; diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index f03dcbe1a..a6cbab500 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -246,39 +246,39 @@ void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) result = m_util.mk_fp(sgn, e, s); } -expr_ref fpa2bv_converter::extra_quantify(expr * e) -{ +expr_ref fpa2bv_converter::extra_quantify(expr * e) { used_vars uv; - unsigned nv; - - ptr_buffer new_decl_sorts; - sbuffer new_decl_names; - expr_ref_buffer subst_map(m); uv(e); - nv = uv.get_num_vars(); - subst_map.resize(uv.get_max_found_var_idx_plus_1()); - - if (nv == 0) + if (uv.get_num_vars() == 0) return expr_ref(e, m); - for (unsigned i = 0; i < nv; i++) - { + ptr_vector new_decl_sorts; + svector new_decl_names; + expr_ref_vector subst_map(m); + unsigned nv = uv.get_max_found_var_idx_plus_1(); + subst_map.resize(nv); + + unsigned j = 0; + for (unsigned i = 0; i < nv; i++) { if (uv.contains(i)) { TRACE(fpa2bv, tout << "uv[" << i << "] = " << mk_ismt2_pp(uv.get(i), m) << std::endl; ); sort * s = uv.get(i); - var * v = m.mk_var(i, s); + var * v = m.mk_var(j, s); new_decl_sorts.push_back(s); - new_decl_names.push_back(symbol(i)); + new_decl_names.push_back(symbol(j)); subst_map.set(i, v); + ++j; } } - - expr_ref res(m); - var_subst vsubst(m); - res = vsubst.operator()(e, nv, subst_map.data()); - TRACE(fpa2bv, tout << "subst'd = " << mk_ismt2_pp(res, m) << std::endl; ); - res = m.mk_forall(nv, new_decl_sorts.data(), new_decl_names.data(), res); + SASSERT(!new_decl_sorts.empty()); + + var_subst vsubst(m, false); // use reverse order: var i is at position i. + auto res = vsubst(e, subst_map); + TRACE(fpa2bv, tout << "subst'd = " << mk_ismt2_pp(e, m) << "\n->\n" << mk_ismt2_pp(res, m) << "\n"); + new_decl_sorts.reverse(); // var 0 is at position num_decl_sorts.size() - 1, ... + new_decl_names.reverse(); + res = m.mk_forall(new_decl_sorts.size(), new_decl_sorts.data(), new_decl_names.data(), res); return res; } diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index d5ad70a1f..ab9ac1597 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -1829,6 +1829,10 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { numeral a; expr* x = nullptr; + if (m_util.is_int(arg)) { + result = arg; + return BR_DONE; + } if (m_util.convert_int_numerals_to_real()) return BR_FAILED; @@ -1837,7 +1841,7 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { return BR_DONE; } - if (m_util.is_to_real(arg, x)) { + if (m_util.is_to_real(arg, x) && m_util.is_int(x)) { result = x; return BR_DONE; } @@ -1885,6 +1889,10 @@ br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) { result = m_util.mk_numeral(a, false); return BR_DONE; } + if (m_util.is_real(arg)) { + result = arg; + return BR_DONE; + } // push to_real over OP_ADD, OP_MUL if (m_push_to_real) { if (m_util.is_add(arg) || m_util.is_mul(arg)) { @@ -1909,7 +1917,7 @@ br_status arith_rewriter::mk_is_int(expr * arg, expr_ref & result) { return BR_DONE; } - if (m_util.is_to_real(arg)) { + if (m_util.is_to_real(arg) && m_util.is_int(to_app(arg)->get_arg(0))) { result = m.mk_true(); return BR_DONE; } diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 12eaff20a..849f4f369 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -63,7 +63,7 @@ class bool_rewriter { bool m_elim_ite; ptr_vector m_todo1, m_todo2; unsigned_vector m_counts1, m_counts2; - expr_fast_mark1 m_marked; + expr_mark m_marked; br_status mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result); diff --git a/src/ast/rewriter/recfun_rewriter.cpp b/src/ast/rewriter/recfun_rewriter.cpp index af4e75d7e..c14c6152a 100644 --- a/src/ast/rewriter/recfun_rewriter.cpp +++ b/src/ast/rewriter/recfun_rewriter.cpp @@ -34,6 +34,9 @@ br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * for (unsigned i = 0; i < num_args; ++i) if (!m.is_value(args[i])) safe_to_subst = false; + for (auto t : subterms::all(expr_ref(r, m))) + if (is_uninterp(t)) + return BR_FAILED; // check if there is an argument that is a constructor // such that the recursive function can be partially evaluated. diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index a78338226..1c33b63ba 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -1088,7 +1088,7 @@ namespace euf { verbose_stream() << mk_pp(s->get_expr(), m) << "\n"; } #endif - auto n = m_egraph.find(q); + // auto n = m_egraph.find(q); #if 0 verbose_stream() << "class of " << mk_pp(q, m) << "\n"; for (auto s : euf::enode_class(n)) { diff --git a/src/ast/sls/sls_arith_base.cpp b/src/ast/sls/sls_arith_base.cpp index 4bde7b90b..eeb866ba3 100644 --- a/src/ast/sls/sls_arith_base.cpp +++ b/src/ast/sls/sls_arith_base.cpp @@ -2588,6 +2588,8 @@ namespace sls { template void arith_base::invariant() { + if (m.limit().is_canceled()) + return; for (unsigned v = 0; v < ctx.num_bool_vars(); ++v) { auto ineq = get_ineq(v); if (ineq) @@ -2622,6 +2624,8 @@ namespace sls { }; for (var_t v = 0; v < m_vars.size(); ++v) { if (!eval_is_correct(v)) { + if (m.limit().is_canceled()) + return; report_error(verbose_stream(), v); TRACE(arith, report_error(tout, v)); UNREACHABLE(); @@ -2707,6 +2711,8 @@ namespace sls { void arith_base::update_unchecked(var_t v, num_t const& new_value) { auto& vi = m_vars[v]; auto old_value = value(v); + if (old_value == new_value) + return; IF_VERBOSE(5, verbose_stream() << "update: v" << v << " " << mk_bounded_pp(vi.m_expr, m) << " := " << old_value << " -> " << new_value << "\n"); TRACE(arith, tout << "update: v" << v << " " << mk_bounded_pp(vi.m_expr, m) << " := " << old_value << " -> " << new_value << "\n"); vi.set_value(new_value); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 5513a86ef..aab16efde 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1220,32 +1220,65 @@ bool cmd_context::try_mk_builtin_app(symbol const & s, unsigned num_args, expr * return nullptr != result.get(); } -bool cmd_context::try_mk_declared_app(symbol const & s, unsigned num_args, expr * const * args, - unsigned num_indices, parameter const * indices, sort * range, - expr_ref & result) { +bool cmd_context::try_mk_declared_app(symbol const &s, unsigned num_args, expr *const *args, unsigned num_indices, + parameter const *indices, sort *range, expr_ref &result) { if (!m_func_decls.contains(s)) return false; - func_decls& fs = m_func_decls.find(s); + func_decls &fs = m_func_decls.find(s); if (num_args == 0 && !range) { if (fs.more_than_one()) - throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as ) to disambiguate ", s); - func_decl * f = fs.first(); + throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a " + "qualified expression (as ) to disambiguate ", + s); + func_decl *f = fs.first(); if (!f) return false; - if (f->get_arity() != 0) + if (f->get_arity() != 0) result = array_util(m()).mk_as_array(f); - else + else result = m().mk_const(f); return true; } - func_decl * f = fs.find(m(), num_args, args, range); - if (!f) - return false; - if (well_sorted_check_enabled()) - m().check_sort(f, num_args, args); - result = m().mk_app(f, num_args, args); - return true; + func_decl *f = fs.find(m(), num_args, args, range); + + if (f) { + if (f && well_sorted_check_enabled()) + m().check_sort(f, num_args, args); + result = m().mk_app(f, num_args, args); + return true; + } + + // f could be declared as an array and applied without explicit select + if (num_args > 0 && !range) { + if (fs.more_than_one()) + throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a " + "qualified expression (as ) to disambiguate ", + s); + + func_decl *f = fs.first(); + if (!f) + return false; + if (f->get_arity() != 0) + return false; + array_util au(m()); + auto s = f->get_range(); + if (!au.is_array(s)) + return false; + unsigned sz = get_array_arity(s); + if (sz != num_args) + return false; + for (unsigned i = 0; i < sz; i++) + if (args[i]->get_sort() != get_array_domain(s, i)) + return false; + expr_ref_vector new_args(m()); + new_args.push_back(m().mk_const(f)); + for (unsigned i = 0; i < num_args; i++) + new_args.push_back(args[i]); + result = au.mk_select(new_args.size(), new_args.data()); + return true; + } + return false; } bool cmd_context::try_mk_macro_app(symbol const & s, unsigned num_args, expr * const * args, diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 5c156d38a..729b47782 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -24,6 +24,7 @@ z3_add_component(lp monomial_bounds.cpp nex_creator.cpp nla_basics_lemmas.cpp + nla_coi.cpp nla_common.cpp nla_core.cpp nla_divisions.cpp diff --git a/src/math/lp/dioph_eq.cpp b/src/math/lp/dioph_eq.cpp index a791cbd07..09239395f 100644 --- a/src/math/lp/dioph_eq.cpp +++ b/src/math/lp/dioph_eq.cpp @@ -988,7 +988,6 @@ namespace lp { if (belongs_to_s(ei)) { remove_from_S(ei); } - SASSERT(entry_invariant(ei)); } void find_changed_terms_and_more_changed_rows() { @@ -1099,6 +1098,7 @@ namespace lp { m_changed_f_columns.reset(); m_changed_rows.reset(); m_changed_terms.reset(); + SASSERT(entries_are_ok()); } int get_sign_in_e_row(unsigned ei, unsigned j) const { diff --git a/src/math/lp/nla_coi.cpp b/src/math/lp/nla_coi.cpp new file mode 100644 index 000000000..fcab22021 --- /dev/null +++ b/src/math/lp/nla_coi.cpp @@ -0,0 +1,88 @@ + +/*++ + Copyright (c) 2025 Microsoft Corporation + + Author: + Lev Nachmanson (levnach) + Nikolaj Bjorner (nbjorner) + --*/ + +#include "math/lp/nla_core.h" +#include "math/lp/nla_coi.h" + +namespace nla { + + void coi::init() { + indexed_uint_set visited; + unsigned_vector todo; + vector var2occurs; + m_term_set.reset(); + m_mon_set.reset(); + m_constraint_set.reset(); + m_var_set.reset(); + auto& lra = c.lra_solver(); + + for (auto ci : lra.constraints().indices()) { + auto const& c = lra.constraints()[ci]; + if (c.is_auxiliary()) + continue; + for (auto const& [coeff, v] : c.coeffs()) { + var2occurs.reserve(v + 1); + var2occurs[v].constraints.push_back(ci); + } + } + + for (auto const& m : c.emons()) { + for (auto v : m.vars()) { + var2occurs.reserve(v + 1); + var2occurs[v].monics.push_back(m.var()); + } + } + + for (const auto *t : lra.terms() ) { + for (auto const iv : *t) { + auto v = iv.j(); + var2occurs.reserve(v + 1); + var2occurs[v].terms.push_back(t->j()); + } + } + + for (auto const& m : c.to_refine()) + todo.push_back(m); + + for (unsigned i = 0; i < todo.size(); ++i) { + auto v = todo[i]; + if (visited.contains(v)) + continue; + visited.insert(v); + m_var_set.insert(v); + var2occurs.reserve(v + 1); + for (auto ci : var2occurs[v].constraints) { + m_constraint_set.insert(ci); + auto const& c = lra.constraints()[ci]; + for (auto const& [coeff, w] : c.coeffs()) + todo.push_back(w); + } + for (auto w : var2occurs[v].monics) + todo.push_back(w); + + for (auto ti : var2occurs[v].terms) { + for (auto iv : lra.get_term(ti)) + todo.push_back(iv.j()); + todo.push_back(ti); + } + + if (lra.column_has_term(v)) { + m_term_set.insert(v); + for (auto kv : lra.get_term(v)) + todo.push_back(kv.j()); + } + + if (c.is_monic_var(v)) { + m_mon_set.insert(v); + for (auto w : c.emons()[v]) + todo.push_back(w); + } + } + } +} \ No newline at end of file diff --git a/src/math/lp/nla_coi.h b/src/math/lp/nla_coi.h new file mode 100644 index 000000000..d05f08fbd --- /dev/null +++ b/src/math/lp/nla_coi.h @@ -0,0 +1,43 @@ + +/*++ + Copyright (c) 2025 Microsoft Corporation + + Abstract: + Class for computing the cone of influence for NL constraints. + It includes variables that come from monomials that have incorrect evaluation and + transitively all constraints and variables that are connected. + + Author: + Lev Nachmanson (levnach) + Nikolaj Bjorner (nbjorner) + --*/ + +#pragma once + +namespace nla { + + class core; + + class coi { + core& c; + indexed_uint_set m_mon_set, m_constraint_set; + indexed_uint_set m_term_set, m_var_set; + + struct occurs { + unsigned_vector constraints; + unsigned_vector monics; + unsigned_vector terms; + }; + + public: + coi(core& c) : c(c) {} + + void init(); + + indexed_uint_set const& mons() const { return m_mon_set; } + indexed_uint_set const& constraints() const { return m_constraint_set; } + indexed_uint_set& terms() { return m_term_set; } + indexed_uint_set const &vars() { return m_var_set; } + + }; +} \ No newline at end of file diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 8a8c3c8f8..c58a887c4 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1282,7 +1282,7 @@ void core::add_bounds() { } } -lbool core::check() { +lbool core::check(unsigned level) { lp_settings().stats().m_nla_calls++; TRACE(nla_solver, tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";); lra.get_rid_of_inf_eps(); @@ -1363,7 +1363,7 @@ lbool core::check() { ret = bounded_nlsat(); } - if (no_effect() && params().arith_nl_nra()) { + if (no_effect() && params().arith_nl_nra() && level >= 2) { scoped_limits sl(m_reslim); sl.push_child(&m_nra_lim); params_ref p; @@ -1432,7 +1432,7 @@ bool core::no_lemmas_hold() const { lbool core::test_check() { lra.set_status(lp::lp_status::OPTIMAL); - return check(); + return check(2); } std::unordered_set core::get_vars_of_expr_with_opening_terms(const nex *e ) { diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 6121a79a7..fed6dfe72 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -394,7 +394,7 @@ public: bool conflict_found() const; - lbool check(); + lbool check(unsigned level); lbool check_power(lpvar r, lpvar x, lpvar y); void check_bounded_divisions(); @@ -450,6 +450,12 @@ public: nla_throttle& throttle() { return m_throttle; } const nla_throttle& throttle() const { return m_throttle; } + lp::lar_solver& lra_solver() { return lra; } + + indexed_uint_set const& to_refine() const { + return m_to_refine; + } + }; // end of core struct pp_mon { diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index f0db19649..cbb941882 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -76,6 +76,14 @@ namespace nla { find_nl_cluster(); if (!configure()) return; + + try { + if (propagate_gcd_test()) + return; + } + catch (...) { + + } m_solver.saturate(); TRACE(grobner, m_solver.display(tout)); diff --git a/src/math/lp/nla_pp.cpp b/src/math/lp/nla_pp.cpp index f4bfc1f8f..567171119 100644 --- a/src/math/lp/nla_pp.cpp +++ b/src/math/lp/nla_pp.cpp @@ -343,7 +343,7 @@ std::ostream& core::display_declarations_smt(std::ostream& out) const { out << "); " << val(v) << " = "; rational p(1); for (auto w : m.vars()) - p *= val(v); + p *= val(w); out << p; out << "\n"; } @@ -360,7 +360,6 @@ std::ostream& core::display_constraint_smt(std::ostream& out, unsigned id, lp::l auto k = c.kind(); auto rhs = c.rhs(); auto lhs = c.coeffs(); - auto sz = lhs.size(); rational den = denominator(rhs); for (auto [coeff, v] : lhs) den = lcm(den, denominator(coeff)); diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index dcf2266c1..eb669ab4b 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -46,8 +46,8 @@ namespace nla { bool solver::need_check() { return m_core->has_relevant_monomial(); } - lbool solver::check() { - return m_core->check(); + lbool solver::check(unsigned level) { + return m_core->check(level); } void solver::propagate() { diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 133117149..e6d02e793 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -37,7 +37,7 @@ namespace nla { void push(); void pop(unsigned scopes); bool need_check(); - lbool check(); + lbool check(unsigned level); void propagate(); void simplify() { m_core->simplify(); } lbool check_power(lpvar r, lpvar x, lpvar y); diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index fec10fe0e..dae20dc69 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -9,6 +9,7 @@ #include #include "math/lp/lar_solver.h" #include "math/lp/nra_solver.h" +#include "math/lp/nla_coi.h" #include "nlsat/nlsat_solver.h" #include "math/polynomial/polynomial.h" #include "math/polynomial/algebraic_numbers.h" @@ -25,114 +26,156 @@ typedef nla::mon_eq mon_eq; typedef nla::variable_map_type variable_map_type; struct solver::imp { + lp::lar_solver& lra; reslimit& m_limit; params_ref m_params; u_map m_lp2nl; // map from lar_solver variables to nlsat::solver variables - indexed_uint_set m_term_set; scoped_ptr m_nlsat; scoped_ptr m_values; // values provided by LRA solver scoped_ptr m_tmp1, m_tmp2; + nla::coi m_coi; nla::core& m_nla_core; imp(lp::lar_solver& s, reslimit& lim, params_ref const& p, nla::core& nla_core): lra(s), m_limit(lim), m_params(p), + m_coi(nla_core), m_nla_core(nla_core) {} bool need_check() { return m_nla_core.m_to_refine.size() != 0; } - indexed_uint_set m_mon_set, m_constraint_set; - - struct occurs { - unsigned_vector constraints; - unsigned_vector monics; - unsigned_vector terms; - }; - - void init_cone_of_influence() { - indexed_uint_set visited; - unsigned_vector todo; - vector var2occurs; - m_term_set.reset(); - m_mon_set.reset(); - m_constraint_set.reset(); - - for (auto ci : lra.constraints().indices()) { - auto const& c = lra.constraints()[ci]; - if (c.is_auxiliary()) - continue; - for (auto const& [coeff, v] : c.coeffs()) { - var2occurs.reserve(v + 1); - var2occurs[v].constraints.push_back(ci); - } - } - - for (auto const& m : m_nla_core.emons()) { - for (auto v : m.vars()) { - var2occurs.reserve(v + 1); - var2occurs[v].monics.push_back(m.var()); - } - } - - for (const auto *t : lra.terms() ) { - for (auto const iv : *t) { - auto v = iv.j(); - var2occurs.reserve(v + 1); - var2occurs[v].terms.push_back(t->j()); - } - } - - for (auto const& m : m_nla_core.m_to_refine) - todo.push_back(m); - - for (unsigned i = 0; i < todo.size(); ++i) { - auto v = todo[i]; - if (visited.contains(v)) - continue; - visited.insert(v); - var2occurs.reserve(v + 1); - for (auto ci : var2occurs[v].constraints) { - m_constraint_set.insert(ci); - auto const& c = lra.constraints()[ci]; - for (auto const& [coeff, w] : c.coeffs()) - todo.push_back(w); - } - for (auto w : var2occurs[v].monics) - todo.push_back(w); - - for (auto ti : var2occurs[v].terms) { - for (auto iv : lra.get_term(ti)) - todo.push_back(iv.j()); - todo.push_back(ti); - } - - if (lra.column_has_term(v)) { - m_term_set.insert(v); - for (auto kv : lra.get_term(v)) - todo.push_back(kv.j()); - } - - if (m_nla_core.is_monic_var(v)) { - m_mon_set.insert(v); - for (auto w : m_nla_core.emons()[v]) - todo.push_back(w); - } - } - } - void reset() { m_values = nullptr; m_tmp1 = nullptr; m_tmp2 = nullptr; m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); m_values = alloc(scoped_anum_vector, am()); - m_term_set.reset(); m_lp2nl.reset(); } + // Create polynomial definition for variable v used in setup_assignment_solver. + // Side-effects: updates m_vars2mon when v is a monic variable. + void mk_definition(unsigned v, polynomial_ref_vector &definitions, vector& denominators) { + auto &pm = m_nlsat->pm(); + polynomial::polynomial_ref p(pm); + rational den(1); + if (m_nla_core.emons().is_monic_var(v)) { + auto const &m = m_nla_core.emons()[v]; + for (auto w : m.vars()) { + den = denominators[w] * den; + polynomial_ref pw(definitions.get(w), m_nlsat->pm()); + if (!p) + p = pw; + else + p = p * pw; + } + } + else if (lra.column_has_term(v)) { + for (auto const &[w, coeff] : lra.get_term(v)) + den = lcm(denominator(coeff / denominators[w]), den); + for (auto const &[w, coeff] : lra.get_term(v)) { + auto coeff1 = den * coeff / denominators[w]; + polynomial_ref pw(definitions.get(w), m_nlsat->pm()); + if (!p) + p = constant(coeff1) * pw; + else + p = p + (constant(coeff1) * pw); + } + } + else { + p = pm.mk_polynomial(lp2nl(v)); // nlsat var index equals v (verified above when created) + } + definitions.push_back(p); + denominators.push_back(den); + } + + void setup_solver_poly() { + m_coi.init(); + auto &pm = m_nlsat->pm(); + polynomial_ref_vector definitions(pm); + vector denominators; + for (unsigned v = 0; v < lra.number_of_vars(); ++v) { + if (m_coi.vars().contains(v)) { + auto j = m_nlsat->mk_var(lra.var_is_int(v)); + m_lp2nl.insert(v, j); // we don't really need this. It is going to be the identify map. + mk_definition(v, definitions, denominators); + } + else { + definitions.push_back(nullptr); + denominators.push_back(rational(0)); + } + } + + // we rely on that all information encoded into the tableau is present as a constraint. + for (auto ci : m_coi.constraints()) { + auto &c = lra.constraints()[ci]; + auto &pm = m_nlsat->pm(); + auto k = c.kind(); + auto rhs = c.rhs(); + auto lhs = c.coeffs(); + rational den = denominator(rhs); + // + // let v := p / denominators[v] + // + // sum(coeff[v] * v) k rhs + // == + // sum(coeff[v] * (p / denominators[v])) k rhs + // == + // sum((coeff[v] / denominators[v]) * p) k rhs + // + + + for (auto [coeff, v] : lhs) + den = lcm(den, denominator(coeff / denominators[v])); + polynomial::polynomial_ref p(pm); + p = pm.mk_const(-den * rhs); + + for (auto [coeff, v] : lhs) { + polynomial_ref poly(pm); + poly = definitions.get(v); + poly = poly * constant(den * coeff / denominators[v]); + p = p + poly; + } + add_constraint(p, ci, k); + TRACE(nra, tout << "constraint " << ci << ": " << p << " " << k << " 0\n"; + lra.constraints().display(tout, ci) << "\n"); + } + definitions.reset(); + } + + void setup_solver_terms() { + m_coi.init(); + // add linear inequalities from lra_solver + for (auto ci : m_coi.constraints()) + add_constraint(ci); + + // add polynomial definitions. + for (auto const &m : m_coi.mons()) + add_monic_eq(m_nla_core.emons()[m]); + + // add term definitions. + for (unsigned i : m_coi.terms()) + add_term(i); + } + + + + polynomial::polynomial_ref sub(polynomial::polynomial *a, polynomial::polynomial *b) { + return polynomial_ref(m_nlsat->pm().sub(a, b), m_nlsat->pm()); + } + polynomial::polynomial_ref mul(polynomial::polynomial *a, polynomial::polynomial *b) { + return polynomial_ref(m_nlsat->pm().mul(a, b), m_nlsat->pm()); + } + polynomial::polynomial_ref var(lp::lpvar v) { + return polynomial_ref(m_nlsat->pm().mk_polynomial(lp2nl(v)), m_nlsat->pm()); + } + polynomial::polynomial_ref constant(rational const& r) { + return polynomial_ref(m_nlsat->pm().mk_const(r), m_nlsat->pm()); + } + /** \brief one-shot nlsat check. A one shot checker is the least functionality that can @@ -147,24 +190,14 @@ struct solver::imp { lbool check() { SASSERT(need_check()); reset(); - vector core; - - init_cone_of_influence(); - // add linear inequalities from lra_solver - for (auto ci : m_constraint_set) - add_constraint(ci); + vector core; - // add polynomial definitions. - for (auto const& m : m_mon_set) - add_monic_eq(m_nla_core.emons()[m]); + smt_params_helper p(m_params); - // add term definitions. - for (unsigned i : m_term_set) - add_term(i); + setup_solver_poly(); TRACE(nra, m_nlsat->display(tout)); - smt_params_helper p(m_params); if (p.arith_nl_log()) { static unsigned id = 0; std::stringstream strm; @@ -196,12 +229,14 @@ struct solver::imp { } } m_nlsat->collect_statistics(st); - TRACE(nra, + TRACE(nra, tout << "nra result " << r << "\n"); + CTRACE(nra, false, m_nlsat->display(tout << r << "\n"); display(tout); for (auto [j, x] : m_lp2nl) tout << "j" << j << " := x" << x << "\n";); switch (r) { case l_true: + m_nlsat->restore_order(); m_nla_core.set_use_nra_model(true); lra.init_model(); for (lp::constraint_index ci : lra.constraints().indices()) @@ -223,14 +258,15 @@ struct solver::imp { case l_false: { lp::explanation ex; m_nlsat->get_core(core); - for (auto c : core) { - unsigned idx = static_cast(static_cast(c) - this); - ex.push_back(idx); - TRACE(nra, lra.display_constraint(tout << "ex: " << idx << ": ", idx) << "\n";); - } nla::lemma_builder lemma(m_nla_core, __FUNCTION__); + for (auto c : core) { + unsigned idx = static_cast(static_cast(c) - this); + ex.push_back(idx); + } + lemma &= ex; m_nla_core.set_use_nra_model(true); + TRACE(nra, tout << lemma << "\n"); break; } case l_undef: @@ -272,12 +308,24 @@ struct solver::imp { coeffs.push_back(mpz(1)); coeffs.push_back(mpz(-1)); polynomial::polynomial_ref p(pm.mk_polynomial(2, coeffs.data(), mls), pm); - polynomial::polynomial* ps[1] = { p }; - bool even[1] = { false }; - nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, even); + auto lit = mk_literal(p.get(), lp::lconstraint_kind::EQ); m_nlsat->mk_clause(1, &lit, nullptr); } + nlsat::literal mk_literal(polynomial::polynomial *p, lp::lconstraint_kind k) { + polynomial::polynomial *ps[1] = { p }; + bool is_even[1] = { false }; + switch (k) { + case lp::lconstraint_kind::LE: return ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); + case lp::lconstraint_kind::GE: return ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); + case lp::lconstraint_kind::LT: return m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); + case lp::lconstraint_kind::GT: return m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); + case lp::lconstraint_kind::EQ: return m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); + default: UNREACHABLE(); // unreachable + } + throw default_exception("uexpected operator"); + } + void add_constraint(unsigned idx) { auto& c = lra.constraints()[idx]; auto& pm = m_nlsat->pm(); @@ -297,30 +345,26 @@ struct solver::imp { } rhs *= den; polynomial::polynomial_ref p(pm.mk_linear(sz, coeffs.data(), vars.data(), -rhs), pm); - polynomial::polynomial* ps[1] = { p }; - bool is_even[1] = { false }; + nlsat::literal lit = mk_literal(p.get(), k); + nlsat::assumption a = this + idx; + m_nlsat->mk_clause(1, &lit, a); + } + + nlsat::literal add_constraint(polynomial::polynomial *p, unsigned idx, lp::lconstraint_kind k) { + polynomial::polynomial *ps[1] = {p}; + bool is_even[1] = {false}; nlsat::literal lit; nlsat::assumption a = this + idx; switch (k) { - case lp::lconstraint_kind::LE: - lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); - break; - case lp::lconstraint_kind::GE: - lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); - break; - case lp::lconstraint_kind::LT: - lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); - break; - case lp::lconstraint_kind::GT: - lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); - break; - case lp::lconstraint_kind::EQ: - lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); - break; - default: - UNREACHABLE(); // unreachable + case lp::lconstraint_kind::LE: lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); break; + case lp::lconstraint_kind::GE: lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); break; + case lp::lconstraint_kind::LT: lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); break; + case lp::lconstraint_kind::GT: lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); break; + case lp::lconstraint_kind::EQ: lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); break; + default: UNREACHABLE(); // unreachable } m_nlsat->mk_clause(1, &lit, a); + return lit; } bool check_monic(mon_eq const& m) { @@ -370,7 +414,7 @@ struct solver::imp { for (auto const& m : m_nla_core.emons()) if (any_of(m.vars(), [&](lp::lpvar v) { return m_lp2nl.contains(v); })) add_monic_eq_bound(m); - for (unsigned i : m_term_set) + for (unsigned i : m_coi.terms()) add_term(i); for (auto const& [v, w] : m_lp2nl) { if (lra.column_has_lower_bound(v)) @@ -397,6 +441,7 @@ struct solver::imp { switch (r) { case l_true: + m_nlsat->restore_order(); m_nla_core.set_use_nra_model(true); lra.init_model(); for (lp::constraint_index ci : lra.constraints().indices()) @@ -418,6 +463,7 @@ struct solver::imp { ex.push_back(ci); nla::lemma_builder lemma(m_nla_core, __FUNCTION__); lemma &= ex; + TRACE(nra, tout << lemma << "\n"); break; } case l_undef: @@ -554,8 +600,8 @@ struct solver::imp { 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_has_term(v)) { - m_term_set.insert(v); + if (!m_coi.terms().contains(v) && lra.column_has_term(v)) { + m_coi.terms().insert(v); } } return r; @@ -586,20 +632,56 @@ struct solver::imp { m_nlsat->mk_clause(1, &lit, nullptr); } - nlsat::anum const& value(lp::lpvar v) { - polynomial::var pv; - if (m_lp2nl.find(v, pv)) - return m_nlsat->value(pv); - else { - for (unsigned w = m_values->size(); w <= v; ++w) { - scoped_anum a(am()); - am().set(a, m_nla_core.val(w).to_mpq()); + nlsat::anum const &value(lp::lpvar v) { + init_values(v + 1); + return (*m_values)[v]; + } + + void init_values(unsigned sz) { + if (m_values->size() >= sz) + return; + unsigned w; + scoped_anum a(am()); + for (unsigned v = m_values->size(); v < sz; ++v) { + if (m_nla_core.emons().is_monic_var(v)) { + am().set(a, 1); + auto &m = m_nla_core.emon(v); + + for (auto x : m.vars()) + am().mul(a, (*m_values)[x], a); m_values->push_back(a); - } - return (*m_values)[v]; + } + else if (lra.column_has_term(v)) { + scoped_anum b(am()); + am().set(a, 0); + for (auto const &[w, coeff] : lra.get_term(v)) { + am().set(b, coeff.to_mpq()); + am().mul(b, (*m_values)[w], b); + am().add(a, b, a); + } + m_values->push_back(a); + } + else if (m_lp2nl.find(v, w)) { + m_values->push_back(m_nlsat->value(w)); + } + else { + am().set(a, m_nla_core.val(v).to_mpq()); + m_values->push_back(a); + } } } + + void set_value(lp::lpvar v, rational const& value) { + if (!m_values) + m_values = alloc(scoped_anum_vector, am()); + scoped_anum a(am()); + am().set(a, value.to_mpq()); + while (m_values->size() <= v) + m_values->push_back(a); + am().set((*m_values)[v], a); + } + nlsat::anum_manager& am() { return m_nlsat->am(); } @@ -680,4 +762,8 @@ void solver::updt_params(params_ref& p) { m_imp->updt_params(p); } +void solver::set_value(lp::lpvar v, rational const& value) { + m_imp->set_value(v, value); +} + } diff --git a/src/math/lp/nra_solver.h b/src/math/lp/nra_solver.h index 90f022ba6..b009b3c12 100644 --- a/src/math/lp/nra_solver.h +++ b/src/math/lp/nra_solver.h @@ -59,6 +59,8 @@ namespace nra { nlsat::anum_manager& am(); + void set_value(lp::lpvar v, rational const &value); + scoped_anum& tmp1(); scoped_anum& tmp2(); diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index 42cfd7469..4cdf9c4b8 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -2772,9 +2772,12 @@ namespace algebraic_numbers { return out; } - std::ostream& display_root_smt2(std::ostream & out, numeral const & a) { + template + std::ostream& display_root_common(std::ostream & out, numeral const & a, char const* var_name, bool no_power, Printer&& printer) { + SASSERT(var_name != nullptr); if (is_zero(a)) { - out << "(root-obj x 1)"; + auto poly_printer = [&](std::ostream& dst) { dst << var_name; }; + return printer(out, poly_printer, 1u); } else if (a.is_basic()) { mpq const & v = basic_value(a); @@ -2782,25 +2785,53 @@ namespace algebraic_numbers { qm().set(neg_n, v.numerator()); qm().neg(neg_n); mpz coeffs[2] = { std::move(neg_n), qm().dup(v.denominator()) }; - out << "(root-obj "; - upm().display_smt2(out, 2, coeffs, "x"); - out << " 1)"; // first root of the polynomial d*# - n + auto poly_printer = [&](std::ostream& dst) { + if (no_power) + upm().display_smt2_no_power(dst, 2, coeffs, var_name); + else + upm().display_smt2(dst, 2, coeffs, var_name); + }; + std::ostream& r = printer(out, poly_printer, 1u); // first root of d*x - n qm().del(coeffs[0]); qm().del(coeffs[1]); + return r; } else { algebraic_cell * c = a.to_algebraic(); - out << "(root-obj "; - upm().display_smt2(out, c->m_p_sz, c->m_p, "x"); + auto poly_printer = [&](std::ostream& dst) { + if (no_power) + upm().display_smt2_no_power(dst, c->m_p_sz, c->m_p, var_name); + else + upm().display_smt2(dst, c->m_p_sz, c->m_p, var_name); + }; if (c->m_i == 0) { // undefined c->m_i = upm().get_root_id(c->m_p_sz, c->m_p, lower(c)) + 1; } SASSERT(c->m_i > 0); - out << " " << c->m_i; - out << ")"; + return printer(out, poly_printer, c->m_i); } - return out; + } + + std::ostream& display_root_smt2(std::ostream & out, numeral const & a) { + auto printer = [&](std::ostream& dst, auto const& poly_printer, unsigned idx) -> std::ostream& { + dst << "(root-obj "; + poly_printer(dst); + dst << " " << idx << ")"; + return dst; + }; + return display_root_common(out, a, "x", false, printer); + } + + std::ostream& display_root_smtrat(std::ostream & out, numeral const & a, char const* var_name) { + SASSERT(var_name != nullptr); + auto printer = [&](std::ostream& dst, auto const& poly_printer, unsigned idx) -> std::ostream& { + dst << "(root "; + poly_printer(dst); + dst << " " << idx << " " << var_name << ")"; + return dst; + }; + return display_root_common(out, a, var_name, true, printer); } std::ostream& display_interval(std::ostream & out, numeral const & a) { @@ -3167,6 +3198,10 @@ namespace algebraic_numbers { return m_imp->display_root_smt2(out, a); } + std::ostream& manager::display_root_smtrat(std::ostream & out, numeral const & a, char const* var_name) const { + return m_imp->display_root_smtrat(out, a, var_name); + } + void manager::reset_statistics() { m_imp->reset_statistics(); } diff --git a/src/math/polynomial/algebraic_numbers.h b/src/math/polynomial/algebraic_numbers.h index e2e95367c..88792bbc2 100644 --- a/src/math/polynomial/algebraic_numbers.h +++ b/src/math/polynomial/algebraic_numbers.h @@ -345,6 +345,12 @@ namespace algebraic_numbers { */ std::ostream& display_root_smt2(std::ostream & out, numeral const & a) const; + /** + \brief Display algebraic number using an SMT-RAT style root expression: (root p i x) + where the final argument denotes the variable bound to this root. + */ + std::ostream& display_root_smtrat(std::ostream & out, numeral const & a, char const* var_name) const; + /** \brief Display algebraic number in Mathematica format. */ @@ -495,4 +501,3 @@ inline std::ostream & operator<<(std::ostream & out, interval_pp const & n) { n.m.display_interval(out, n.n); return out; } - diff --git a/src/math/polynomial/upolynomial.cpp b/src/math/polynomial/upolynomial.cpp index a73d3e5fb..241f48b20 100644 --- a/src/math/polynomial/upolynomial.cpp +++ b/src/math/polynomial/upolynomial.cpp @@ -1159,67 +1159,89 @@ namespace upolynomial { } } + static void display_smt2_var_power(std::ostream & out, char const * var_name, unsigned k, bool allow_power) { + SASSERT(k > 0); + if (k == 1) { + out << var_name; + } + else if (allow_power) { + out << "(^ " << var_name << " " << k << ")"; + } + else { + out << "(*"; + for (unsigned i = 0; i < k; ++i) + out << " " << var_name; + out << ")"; + } + } + static void display_smt2_monomial(std::ostream & out, numeral_manager & m, mpz const & n, - unsigned k, char const * var_name) { + unsigned k, char const * var_name, bool allow_power) { if (k == 0) { display_smt2_mumeral(out, m, n); } else if (m.is_one(n)) { - if (k == 1) - out << var_name; - else - out << "(^ " << var_name << " " << k << ")"; + display_smt2_var_power(out, var_name, k, allow_power); } else { out << "(* "; display_smt2_mumeral(out, m, n); out << " "; - if (k == 1) - out << var_name; - else - out << "(^ " << var_name << " " << k << ")"; + display_smt2_var_power(out, var_name, k, allow_power); out << ")"; } } - // Display p as an s-expression - std::ostream& core_manager::display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name) const { + static std::ostream& display_smt2_core(std::ostream & out, core_manager const& cm, unsigned sz, numeral const * p, char const * var_name, bool allow_power) { if (sz == 0) { out << "0"; return out; } if (sz == 1) { - display_smt2_mumeral(out, m(), p[0]); + display_smt2_mumeral(out, cm.m(), p[0]); return out; } unsigned non_zero_idx = UINT_MAX; unsigned num_non_zeros = 0; for (unsigned i = 0; i < sz; i++) { - if (m().is_zero(p[i])) + if (cm.m().is_zero(p[i])) continue; non_zero_idx = i; num_non_zeros ++; } - if (num_non_zeros == 1) { - SASSERT(non_zero_idx != UINT_MAX && non_zero_idx >= 1); - display_smt2_monomial(out, m(), p[non_zero_idx], non_zero_idx, var_name); + if (num_non_zeros == 1 && non_zero_idx != UINT_MAX) { + if (non_zero_idx == 0) { + display_smt2_mumeral(out, cm.m(), p[0]); + return out; + } + display_smt2_monomial(out, cm.m(), p[non_zero_idx], non_zero_idx, var_name, allow_power); + return out; } out << "(+"; unsigned i = sz; while (i > 0) { --i; - if (!m().is_zero(p[i])) { + if (!cm.m().is_zero(p[i])) { out << " "; - display_smt2_monomial(out, m(), p[i], i, var_name); + display_smt2_monomial(out, cm.m(), p[i], i, var_name, allow_power); } } return out << ")"; } + // Display p as an s-expression + std::ostream& core_manager::display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name) const { + return display_smt2_core(out, *this, sz, p, var_name, true); + } + + std::ostream& core_manager::display_smt2_no_power(std::ostream & out, unsigned sz, numeral const * p, char const * var_name) const { + return display_smt2_core(out, *this, sz, p, var_name, false); + } + bool core_manager::eq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2) { if (sz1 != sz2) return false; @@ -3117,4 +3139,3 @@ namespace upolynomial { return out; } }; - diff --git a/src/math/polynomial/upolynomial.h b/src/math/polynomial/upolynomial.h index 2afdbb7b3..7f807c0ae 100644 --- a/src/math/polynomial/upolynomial.h +++ b/src/math/polynomial/upolynomial.h @@ -468,6 +468,7 @@ namespace upolynomial { std::ostream& display_smt2(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { return display_smt2(out, p.size(), p.data(), var_name); } + std::ostream& display_smt2_no_power(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x") const; }; class scoped_set_z { @@ -917,4 +918,3 @@ namespace upolynomial { }; }; - diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 3e3ab2e0f..0e6cc36f0 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -1021,7 +1021,7 @@ namespace realclosure { } static bool is_rational_function(numeral const & a) { - return is_rational_function(a.m_value); + return !is_zero(a) && is_rational_function(a.m_value); } static rational_function_value * to_rational_function(numeral const & a) { @@ -2521,7 +2521,7 @@ namespace realclosure { \brief Return true if a is a rational. */ bool is_rational(numeral const & a) { - return a.m_value->is_rational(); + return is_zero(a) || a.m_value->is_rational(); } @@ -3429,7 +3429,7 @@ namespace realclosure { } } - bool get_interval(numeral const & a, int & lower_is_inf, int & lower_is_open, numeral & lower, int & upper_is_inf, int & upper_is_open, numeral & upper) + bool get_interval(numeral const & a, bool & lower_is_inf, bool & lower_is_open, numeral & lower, bool & upper_is_inf, bool & upper_is_open, numeral & upper) { if (!is_algebraic(a)) return false; @@ -6475,7 +6475,7 @@ namespace realclosure { return m_imp->get_sign_condition_sign(a, i); } - bool manager::get_interval(numeral const & a, int & lower_is_inf, int & lower_is_open, numeral & lower, int & upper_is_inf, int & upper_is_open, numeral & upper) + bool manager::get_interval(numeral const & a, bool & lower_is_inf, bool & lower_is_open, numeral & lower, bool & upper_is_inf, bool & upper_is_open, numeral & upper) { return m_imp->get_interval(a, lower_is_inf, lower_is_open, lower, upper_is_inf, upper_is_open, upper); } diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 12247627b..a1fae3e2b 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -298,7 +298,7 @@ namespace realclosure { int get_sign_condition_sign(numeral const &a, unsigned i); - bool get_interval(numeral const & a, int & lower_is_inf, int & lower_is_open, numeral & lower, int & upper_is_inf, int & upper_is_open, numeral & upper); + bool get_interval(numeral const & a, bool & lower_is_inf, bool & lower_is_open, numeral & lower, bool & upper_is_inf, bool & upper_is_open, numeral & upper); unsigned num_sign_condition_coefficients(numeral const &a, unsigned i); diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 2d3b89928..e92e5629c 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -21,9 +21,12 @@ Revision History: #include "nlsat/nlsat_evaluator.h" #include "math/polynomial/algebraic_numbers.h" #include "util/ref_buffer.h" +#include "util/mpq.h" namespace nlsat { + struct add_all_coeffs_restart {}; + typedef polynomial::polynomial_ref_vector polynomial_ref_vector; typedef ref_buffer polynomial_ref_buffer; @@ -45,7 +48,7 @@ namespace nlsat { bool m_minimize_cores; bool m_factor; bool m_add_all_coeffs; - bool m_signed_project; + bool m_add_zero_disc; bool m_cell_sample; @@ -156,7 +159,7 @@ namespace nlsat { m_full_dimensional = false; m_minimize_cores = false; m_add_all_coeffs = true; - m_signed_project = false; + m_add_zero_disc = true; } std::ostream& display(std::ostream & out, polynomial_ref const & p) const { @@ -279,34 +282,140 @@ namespace nlsat { }; - 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 - { - 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); + struct cell_root_info { + polynomial_ref m_eq; + polynomial_ref m_lower; + polynomial_ref m_upper; + unsigned m_eq_idx; + unsigned m_lower_idx; + unsigned m_upper_idx; + bool m_has_eq; + bool m_has_lower; + bool m_has_upper; + cell_root_info(pmanager & pm): m_eq(pm), m_lower(pm), m_upper(pm) { + reset(); + } + void reset() { + m_eq = nullptr; + m_lower = nullptr; + m_upper = nullptr; + m_eq_idx = m_lower_idx = m_upper_idx = UINT_MAX; + m_has_eq = m_has_lower = m_has_upper = false; + } + }; + + void find_cell_roots(polynomial_ref_vector & ps, var y, cell_root_info & info) { + info.reset(); + SASSERT(m_assignment.is_assigned(y)); + bool lower_inf = true; + bool upper_inf = true; + scoped_anum_vector & roots = m_roots_tmp; + scoped_anum lower(m_am); + scoped_anum upper(m_am); + anum const & y_val = m_assignment.value(y); + TRACE(nlsat_explain, tout << "adding literals for "; display_var(tout, y); tout << " -> "; + m_am.display_decimal(tout, y_val); tout << "\n";); + polynomial_ref p(m_pm); + unsigned sz = ps.size(); + for (unsigned k = 0; k < sz; k++) { + p = ps.get(k); + if (max_var(p) != y) + continue; + roots.reset(); + // Variable y is assigned in m_assignment. We must temporarily unassign it. + // Otherwise, the isolate_roots procedure will assume p is a constant polynomial. + m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots); + unsigned num_roots = roots.size(); + TRACE(nlsat_explain, + tout << "isolated roots for "; display_var(tout, y); + tout << " with polynomial: " << p << "\n"; + for (unsigned ri = 0; ri < num_roots; ++ri) { + m_am.display_decimal(tout << " root[" << (ri+1) << "] = ", roots[ri]); + tout << "\n"; + }); + bool all_lt = true; + for (unsigned i = 0; i < num_roots; i++) { + int s = m_am.compare(y_val, roots[i]); + TRACE(nlsat_explain, + m_am.display_decimal(tout << "comparing root: ", roots[i]); tout << "\n"; + m_am.display_decimal(tout << "with y_val:", y_val); + tout << "\nsign " << s << "\n"; + tout << "poly: " << p << "\n"; + ); + if (s == 0) { + info.m_eq = p; + info.m_eq_idx = i + 1; + info.m_has_eq = true; + return; + } + else if (s < 0) { + if (i > 0) { + int j = i - 1; + if (lower_inf || m_am.lt(lower, roots[j])) { + lower_inf = false; + m_am.set(lower, roots[j]); + info.m_lower = p; + info.m_lower_idx = j + 1; + } + } + if (upper_inf || m_am.lt(roots[i], upper)) { + upper_inf = false; + m_am.set(upper, roots[i]); + info.m_upper = p; + info.m_upper_idx = i + 1; + } + all_lt = false; + break; + } + } + if (all_lt && num_roots > 0) { + int j = num_roots - 1; + if (lower_inf || m_am.lt(lower, roots[j])) { + lower_inf = false; + m_am.set(lower, roots[j]); + info.m_lower = p; + info.m_lower_idx = j + 1; } } } - 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()); - l.neg(); - TRACE(nlsat_explain, tout << "adding (zero assumption) literal:\n"; display(tout, l); tout << "\n";); - add_literal(l); + if (!lower_inf) { + info.m_has_lower = true; + } + if (!upper_inf) { + info.m_has_upper = true; + } } - void add_simple_assumption(atom::kind k, poly * p, bool sign = false) { + void add_zero_assumption(polynomial_ref& p) { + // Build a square-free representative of p so that we can speak about + // a specific root that coincides with the current assignment. + polynomial_ref q(m_pm); + m_pm.square_free(p, q); + if (is_zero(q) || is_const(q)) { + SASSERT(!sign(q)); + TRACE(nlsat_explain, tout << "cannot form zero assumption from constant polynomial " << q << "\n";); + return; + } + var maxx = max_var(q); + SASSERT(maxx != null_var); + if (maxx == null_var) + return; + SASSERT(m_assignment.is_assigned(maxx)); + + polynomial_ref_vector singleton(m_pm); + singleton.push_back(q); + cell_root_info info(m_pm); + find_cell_roots(singleton, maxx, info); + if (!info.m_has_eq) + return; // there are no root functions and therefore no constraints are generated + + TRACE(nlsat_explain, + tout << "adding zero-assumption root literal for "; + display_var(tout, maxx); tout << " using root[" << info.m_eq_idx << "] of " << q << "\n";); + add_root_literal(atom::ROOT_EQ, maxx, info.m_eq_idx, info.m_eq); + } + + void add_assumption(atom::kind k, poly * p, bool sign = false) { SASSERT(k == atom::EQ || k == atom::LT || k == atom::GT); bool is_even = false; bool_var b = m_solver.mk_ineq_atom(k, 1, &p, &is_even); @@ -314,10 +423,6 @@ namespace nlsat { add_literal(l); } - void add_assumption(atom::kind k, poly * p, bool sign = false) { - // TODO: factor - add_simple_assumption(k, p, sign); - } /** \brief Eliminate "vanishing leading coefficients" of p. @@ -349,26 +454,24 @@ namespace nlsat { } lc = m_pm.coeff(p, x, k, reduct); TRACE(nlsat_explain, tout << "lc: " << lc << " reduct: " << reduct << "\n";); - if (!is_zero(lc)) { - if (!::is_zero(sign(lc))) { - TRACE(nlsat_explain, tout << "lc does no vaninsh\n";); - return; - } - TRACE(nlsat_explain, tout << "got a zero sign on lc\n";); - - - // lc is not the zero polynomial, but it vanished in the current interpretation. - // so we keep searching... - TRACE(nlsat_explain, tout << "adding zero assumption for var:"; m_solver.display_var(tout, x); tout << ", degree k:" << k << ", p:" ; display(tout, p) << "\n";); - - add_zero_assumption(lc); + insert_fresh_factors_in_todo(lc); + if (!is_zero(lc) && sign(lc)) { + insert_fresh_factors_in_todo(lc); + TRACE(nlsat_explain, tout << "lc does no vaninsh\n";); + return; } + add_zero_assumption(lc); if (k == 0) { // all coefficients of p vanished in the current interpretation, // and were added as assumptions. p = m_pm.mk_zero(); TRACE(nlsat_explain, tout << "all coefficients of p vanished\n";); - return; + if (m_add_all_coeffs) { + return; + } + TRACE(nlsat_explain, tout << "falling back to add-all-coeffs projection\n";); + m_add_all_coeffs = true; + throw add_all_coeffs_restart(); } k--; p = reduct; @@ -444,13 +547,13 @@ namespace nlsat { SASSERT(max_var(p) < max); // factor p is a lower stage polynomial, so we should add assumption to justify p being eliminated if (s == 0) - add_simple_assumption(atom::EQ, p); // add assumption p = 0 + add_assumption(atom::EQ, p); // add assumption p = 0 else if (a->is_even(i)) - add_simple_assumption(atom::EQ, p, true); // add assumption p != 0 + add_assumption(atom::EQ, p, true); // add assumption p != 0 else if (s < 0) - add_simple_assumption(atom::LT, p); // add assumption p < 0 + add_assumption(atom::LT, p); // add assumption p < 0 else - add_simple_assumption(atom::GT, p); // add assumption p > 0 + add_assumption(atom::GT, p); // add assumption p > 0 } if (s == 0) { bool atom_val = a->get_kind() == atom::EQ; @@ -620,35 +723,6 @@ namespace nlsat { } } -// The monomials have to be square free according to -//"An improved projection operation for cylindrical algebraic decomposition of three-dimensional space", by McCallum, Scott - - bool is_square_free(polynomial_ref_vector &ps, var x) { - if (m_add_all_coeffs) - return false; - polynomial_ref p(m_pm); - polynomial_ref lc_poly(m_pm); - polynomial_ref disc_poly(m_pm); - - for (unsigned i = 0; i < ps.size(); i++) { - p = ps.get(i); - unsigned k_deg = m_pm.degree(p, x); - if (k_deg == 0) - continue; - // p depends on x - disc_poly = discriminant(p, x); // Use global helper - if (sign(disc_poly) == 0) { // Discriminant is zero - TRACE(nlsat_explain, tout << "p is not square free:\n "; - display(tout, p); tout << "\ndiscriminant: "; display(tout, disc_poly) << "\n"; - m_solver.display_assignment(tout) << '\n'; - m_solver.display_var(tout << "x:", x) << '\n'; - ); - - return false; - } - } - return true; - } // If each p from ps is square-free then add the leading coefficents to the projection. // Otherwise, add each coefficient of each p to the projection. @@ -656,7 +730,6 @@ namespace nlsat { polynomial_ref p(m_pm); polynomial_ref coeff(m_pm); - bool sqf = is_square_free(ps, x); // Add the leading or all coeffs, depening on being square-free for (unsigned i = 0; i < ps.size(); i++) { p = ps.get(i); @@ -664,11 +737,11 @@ namespace nlsat { if (k_deg == 0) continue; // p depends on x TRACE(nlsat_explain, tout << "processing poly of degree " << k_deg << " w.r.t x" << x << ": "; display(tout, p) << "\n";); - for (unsigned j_coeff_deg = k_deg; j_coeff_deg >= 1; j_coeff_deg--) { + for (int j_coeff_deg = k_deg; j_coeff_deg >= 0; j_coeff_deg--) { coeff = m_pm.coeff(p, x, j_coeff_deg); TRACE(nlsat_explain, tout << " coeff deg " << j_coeff_deg << ": "; display(tout, coeff) << "\n";); insert_fresh_factors_in_todo(coeff); - if (sqf) + if (!m_add_all_coeffs) break; } } @@ -690,9 +763,6 @@ namespace nlsat { } } - void add_zero_assumption_on_factor(polynomial_ref& f) { - display(std::cout << "zero factors \n", f); - } // this function also explains the value 0, if met bool coeffs_are_zeroes(polynomial_ref &s) { restore_factors _restore(m_factors, m_factors_save); @@ -761,10 +831,8 @@ namespace nlsat { TRACE(nlsat_explain, tout << "done, psc is a constant\n";); return; } - if (is_zero(sign(s))) { - TRACE(nlsat_explain, tout << "psc vanished, adding zero assumption\n";); + if (m_add_zero_disc && !sign(s)) { add_zero_assumption(s); - continue; } TRACE(nlsat_explain, tout << "adding v-psc of\n"; @@ -928,34 +996,12 @@ namespace nlsat { } int ensure_sign(polynomial_ref & p) { -#if 0 - polynomial_ref f(m_pm); - factor(p, m_factors); - m_is_even.reset(); - unsigned num_factors = m_factors.size(); - int s = 1; - for (unsigned i = 0; i < num_factors; i++) { - f = m_factors.get(i); - s *= sign(f); - m_is_even.push_back(false); - } - if (num_factors > 0) { - atom::kind k = atom::EQ; - if (s == 0) k = atom::EQ; - if (s < 0) k = atom::LT; - if (s > 0) k = atom::GT; - bool_var b = m_solver.mk_ineq_atom(k, num_factors, m_factors.c_ptr(), m_is_even.c_ptr()); - add_literal(literal(b, true)); - } - return s; -#else int s = sign(p); if (!is_const(p)) { TRACE(nlsat_explain, tout << p << "\n";); - add_simple_assumption(s == 0 ? atom::EQ : (s < 0 ? atom::LT : atom::GT), p); + add_assumption(s == 0 ? atom::EQ : (s < 0 ? atom::LT : atom::GT), p); } return s; -#endif } /** @@ -984,95 +1030,28 @@ namespace nlsat { UNREACHABLE(); break; } - add_simple_assumption(k, p, lsign); + add_assumption(k, p, lsign); } - void cac_add_cell_lits(polynomial_ref_vector & ps, var y, polynomial_ref_vector & res) { res.reset(); - SASSERT(m_assignment.is_assigned(y)); - bool lower_inf = true; - bool upper_inf = true; - scoped_anum_vector & roots = m_roots_tmp; - scoped_anum lower(m_am); - scoped_anum upper(m_am); - anum const & y_val = m_assignment.value(y); - TRACE(nlsat_explain, tout << "adding literals for "; display_var(tout, y); tout << " -> "; - m_am.display_decimal(tout, y_val); tout << "\n";); - polynomial_ref p_lower(m_pm); - unsigned i_lower = UINT_MAX; - polynomial_ref p_upper(m_pm); - unsigned i_upper = UINT_MAX; - polynomial_ref p(m_pm); - unsigned sz = ps.size(); - for (unsigned k = 0; k < sz; k++) { - p = ps.get(k); - if (max_var(p) != y) - continue; - roots.reset(); - // Variable y is assigned in m_assignment. We must temporarily unassign it. - // Otherwise, the isolate_roots procedure will assume p is a constant polynomial. - m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots); - unsigned num_roots = roots.size(); - bool all_lt = true; - for (unsigned i = 0; i < num_roots; i++) { - int s = m_am.compare(y_val, roots[i]); - TRACE(nlsat_explain, - m_am.display_decimal(tout << "comparing root: ", roots[i]); tout << "\n"; - m_am.display_decimal(tout << "with y_val:", y_val); - tout << "\nsign " << s << "\n"; - tout << "poly: " << p << "\n"; - ); - if (s == 0) { - // y_val == roots[i] - // add literal - // ! (y = root_i(p)) - add_root_literal(atom::ROOT_EQ, y, i+1, p); - res.push_back(p); - return; - } - else if (s < 0) { - // y_val < roots[i] - if (i > 0) { - // y_val > roots[j] - int j = i - 1; - if (lower_inf || m_am.lt(lower, roots[j])) { - lower_inf = false; - m_am.set(lower, roots[j]); - p_lower = p; - i_lower = j + 1; - } - } - if (upper_inf || m_am.lt(roots[i], upper)) { - upper_inf = false; - m_am.set(upper, roots[i]); - p_upper = p; - i_upper = i + 1; - } - all_lt = false; - break; - } - } - if (all_lt && num_roots > 0) { - int j = num_roots - 1; - if (lower_inf || m_am.lt(lower, roots[j])) { - lower_inf = false; - m_am.set(lower, roots[j]); - p_lower = p; - i_lower = j + 1; - } - } + cell_root_info info(m_pm); + find_cell_roots(ps, y, info); + if (info.m_has_eq) { + res.push_back(info.m_eq); + add_root_literal(atom::ROOT_EQ, y, info.m_eq_idx, info.m_eq); + return; } - - if (!lower_inf) { - res.push_back(p_lower); - add_root_literal(m_full_dimensional ? atom::ROOT_GE : atom::ROOT_GT, y, i_lower, p_lower); + if (info.m_has_lower) { + res.push_back(info.m_lower); + add_root_literal(m_full_dimensional ? atom::ROOT_GE : atom::ROOT_GT, y, info.m_lower_idx, info.m_lower); } - if (!upper_inf) { - res.push_back(p_upper); - add_root_literal(m_full_dimensional ? atom::ROOT_LE : atom::ROOT_LT, y, i_upper, p_upper); + if (info.m_has_upper) { + res.push_back(info.m_upper); + add_root_literal(m_full_dimensional ? atom::ROOT_LE : atom::ROOT_LT, y, info.m_upper_idx, info.m_upper); } } + /** Add one or two literals that specify in which cell of variable y the current interpretation is. One literal is added for the cases: @@ -1092,88 +1071,20 @@ namespace nlsat { ! (y > root_i(p1)) or !(y < root_j(p2)) */ void add_cell_lits(polynomial_ref_vector & ps, var y) { - SASSERT(m_assignment.is_assigned(y)); - bool lower_inf = true; - bool upper_inf = true; - scoped_anum_vector & roots = m_roots_tmp; - scoped_anum lower(m_am); - scoped_anum upper(m_am); - anum const & y_val = m_assignment.value(y); - TRACE(nlsat_explain, tout << "adding literals for "; display_var(tout, y); tout << " -> "; - m_am.display_decimal(tout, y_val); tout << "\n";); - polynomial_ref p_lower(m_pm); - unsigned i_lower = UINT_MAX; - polynomial_ref p_upper(m_pm); - unsigned i_upper = UINT_MAX; - polynomial_ref p(m_pm); - unsigned sz = ps.size(); - for (unsigned k = 0; k < sz; k++) { - p = ps.get(k); - if (max_var(p) != y) - continue; - roots.reset(); - // Variable y is assigned in m_assignment. We must temporarily unassign it. - // Otherwise, the isolate_roots procedure will assume p is a constant polynomial. - m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots); - unsigned num_roots = roots.size(); - bool all_lt = true; - for (unsigned i = 0; i < num_roots; i++) { - int s = m_am.compare(y_val, roots[i]); - TRACE(nlsat_explain, - m_am.display_decimal(tout << "comparing root: ", roots[i]); tout << "\n"; - m_am.display_decimal(tout << "with y_val:", y_val); - tout << "\nsign " << s << "\n"; - tout << "poly: " << p << "\n"; - ); - if (s == 0) { - // y_val == roots[i] - // add literal - // ! (y = root_i(p)) - add_root_literal(atom::ROOT_EQ, y, i+1, p); - return; - } - else if (s < 0) { - // y_val < roots[i] - if (i > 0) { - // y_val > roots[j] - int j = i - 1; - if (lower_inf || m_am.lt(lower, roots[j])) { - lower_inf = false; - m_am.set(lower, roots[j]); - p_lower = p; - i_lower = j + 1; - } - } - if (upper_inf || m_am.lt(roots[i], upper)) { - upper_inf = false; - m_am.set(upper, roots[i]); - p_upper = p; - i_upper = i + 1; - } - all_lt = false; - break; - } - } - if (all_lt && num_roots > 0) { - int j = num_roots - 1; - if (lower_inf || m_am.lt(lower, roots[j])) { - lower_inf = false; - m_am.set(lower, roots[j]); - p_lower = p; - i_lower = j + 1; - } - } + cell_root_info info(m_pm); + find_cell_roots(ps, y, info); + if (info.m_has_eq) { + add_root_literal(atom::ROOT_EQ, y, info.m_eq_idx, info.m_eq); + return; } - - if (!lower_inf) { - add_root_literal(m_full_dimensional ? atom::ROOT_GE : atom::ROOT_GT, y, i_lower, p_lower); + if (info.m_has_lower) { + add_root_literal(m_full_dimensional ? atom::ROOT_GE : atom::ROOT_GT, y, info.m_lower_idx, info.m_lower); } - if (!upper_inf) { - add_root_literal(m_full_dimensional ? atom::ROOT_LE : atom::ROOT_LT, y, i_upper, p_upper); + if (info.m_has_upper) { + add_root_literal(m_full_dimensional ? atom::ROOT_LE : atom::ROOT_LT, y, info.m_upper_idx, info.m_upper); } } - /** \brief Return true if all polynomials in ps are univariate in x. */ @@ -1467,16 +1378,49 @@ namespace nlsat { if (max_var(new_lit) < max) { if (m_solver.value(new_lit) == l_true) { + TRACE(nlsat_simplify_bug, + tout << "literal normalized away because it is already true after rewriting:\n"; + display(tout << " original: ", l) << "\n"; + display(tout << " rewritten: ", new_lit) << "\n"; + if (info.m_eq) { + polynomial_ref eq_ref(const_cast(info.m_eq), m_pm); + m_pm.display(tout << " equation used: ", eq_ref, m_solver.display_proc()); + tout << " = 0\n"; + }); new_lit = l; } else { - add_literal(new_lit); + literal assumption = new_lit; + TRACE(nlsat_simplify_bug, + tout << "literal replaced by lower-stage assumption due to rewriting:\n"; + display(tout << " original: ", l) << "\n"; + display(tout << " assumption: ", assumption) << "\n"; + if (info.m_eq) { + polynomial_ref eq_ref(const_cast(info.m_eq), m_pm); + m_pm.display(tout << " equation used: ", eq_ref, m_solver.display_proc()); + tout << " = 0\n"; + }); + add_literal(assumption); new_lit = true_literal; } } else { + literal before = new_lit; + (void)before; new_lit = normalize(new_lit, max); TRACE(nlsat_simplify_core, tout << "simplified literal after normalization:\n"; display(tout, new_lit); tout << " " << m_solver.value(new_lit) << "\n";); + if (new_lit == true_literal || new_lit == false_literal) { + TRACE(nlsat_simplify_bug, + tout << "normalize() turned rewritten literal into constant value:\n"; + display(tout << " original: ", l) << "\n"; + display(tout << " rewritten: ", before) << "\n"; + tout << " result: " << (new_lit == true_literal ? "true" : "false") << "\n"; + if (info.m_eq) { + polynomial_ref eq_ref(const_cast(info.m_eq), m_pm); + m_pm.display(tout << " equation used: ", eq_ref, m_solver.display_proc()); + tout << " = 0\n"; + }); + } } } else { @@ -1648,7 +1592,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 << "after elim_vanishing m_ps:\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";); } @@ -1659,6 +1603,7 @@ namespace nlsat { m_core2.append(num, ls); var max = max_var(num, ls); SASSERT(max != null_var); + TRACE(nlsat_explain, display(tout << "core before normalization\n", m_core2) << "\n";); normalize(m_core2, max); TRACE(nlsat_explain, display(tout << "core after normalization\n", m_core2) << "\n";); simplify(m_core2, max); @@ -1667,6 +1612,7 @@ namespace nlsat { m_core2.reset(); } else { + TRACE(nlsat_explain, display(tout << "core befor normalization\n", m_core2) << "\n";); main(num, ls); } } @@ -1760,71 +1706,99 @@ namespace nlsat { display(tout, num, ls) << "\n"; m_solver.display_assignment(tout); ); - m_result = &result; - process(num, ls); - reset_already_added(); - m_result = nullptr; - TRACE(nlsat_explain, display(tout << "[explain] result\n", result) << "\n";); - CASSERT("nlsat", check_already_added()); + if (max_var(num, ls) == 0 && !m_assignment.is_assigned(0)) { + TRACE(nlsat_explain, tout << "all literals use unassigned max var; returning justification\n";); + result.reset(); + return; + } + unsigned base = result.size(); + while (true) { + try { + m_result = &result; + process(num, ls); + reset_already_added(); + m_result = nullptr; + TRACE(nlsat_explain, display(tout << "[explain] result\n", result) << "\n";); + CASSERT("nlsat", check_already_added()); + break; + } + catch (add_all_coeffs_restart const&) { + TRACE(nlsat_explain, tout << "restarting explanation with all coefficients\n";); + reset_already_added(); + result.shrink(base); + m_result = nullptr; + } + } } void project(var x, unsigned num, literal const * ls, scoped_literal_vector & result) { - - m_result = &result; - svector lits; - TRACE(nlsat, tout << "project x" << x << "\n"; - m_solver.display(tout, num, ls); - m_solver.display(tout);); - -#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); - if (!m_ps.empty()) { - svector renaming; - if (x != mx_var) { - for (var i = 0; i < m_solver.num_vars(); ++i) { - renaming.push_back(i); - } - std::swap(renaming[x], renaming[mx_var]); - m_solver.reorder(renaming.size(), renaming.data()); - TRACE(qe, tout << "x: " << x << " max: " << mx_var << " num_vars: " << m_solver.num_vars() << "\n"; + unsigned base = result.size(); + while (true) { + bool reordered = false; + try { + m_result = &result; + svector lits; + TRACE(nlsat, tout << "project x" << x << "\n"; + m_solver.display(tout, num, ls); m_solver.display(tout);); - } - elim_vanishing(m_ps); - if (m_signed_project) { - signed_project(m_ps, mx_var); - } - else { - project(m_ps, mx_var); - } - reset_already_added(); - m_result = nullptr; - if (x != mx_var) { - m_solver.restore_order(); - } - } - else { - reset_already_added(); - m_result = nullptr; - } - for (unsigned i = 0; i < result.size(); ++i) { - result.set(i, ~result[i]); - } + #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)); - } + 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); + if (!m_ps.empty()) { + svector renaming; + if (x != mx_var) { + for (var i = 0; i < m_solver.num_vars(); ++i) { + renaming.push_back(i); + } + std::swap(renaming[x], renaming[mx_var]); + m_solver.reorder(renaming.size(), renaming.data()); + reordered = true; + TRACE(qe, tout << "x: " << x << " max: " << mx_var << " num_vars: " << m_solver.num_vars() << "\n"; + m_solver.display(tout);); + } + elim_vanishing(m_ps); + project(m_ps, mx_var); + reset_already_added(); + m_result = nullptr; + if (reordered) { + m_solver.restore_order(); + } + } + else { + reset_already_added(); + m_result = nullptr; + } + for (unsigned i = 0; i < result.size(); ++i) { + result.set(i, ~result[i]); + } +#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 + break; + } + catch (add_all_coeffs_restart const&) { + TRACE(nlsat_explain, tout << "restarting projection with all coefficients\n";); + reset_already_added(); + if (reordered) { + m_solver.restore_order(); + } + result.shrink(base); + m_result = nullptr; + } + } } void split_literals(var x, unsigned n, literal const* ls, svector& lits) { @@ -1840,183 +1814,8 @@ namespace nlsat { } } } - - /** - Signed projection. - - Assumptions: - - any variable in ps is at most x. - - root expressions are satisfied (positive literals) - - Effect: - - if x not in p, then - - if sign(p) < 0: p < 0 - - if sign(p) = 0: p = 0 - - if sign(p) > 0: p > 0 - else: - - let roots_j be the roots of p_j or roots_j[i] - - let L = { roots_j[i] | M(roots_j[i]) < M(x) } - - let U = { roots_j[i] | M(roots_j[i]) > M(x) } - - let E = { roots_j[i] | M(roots_j[i]) = M(x) } - - let glb in L, s.t. forall l in L . M(glb) >= M(l) - - let lub in U, s.t. forall u in U . M(lub) <= M(u) - - if root in E, then - - add E x . x = root & x > lb for lb in L - - add E x . x = root & x < ub for ub in U - - add E x . x = root & x = root2 for root2 in E \ { root } - - else - - assume |L| <= |U| (other case is symmetric) - - add E x . lb <= x & x <= glb for lb in L - - add E x . x = glb & x < ub for ub in U - */ - - - void signed_project(polynomial_ref_vector& ps, var x) { - - TRACE(nlsat_explain, tout << "Signed projection\n";); - polynomial_ref p(m_pm); - unsigned eq_index = 0; - bool eq_valid = false; - unsigned eq_degree = 0; - for (unsigned i = 0; i < ps.size(); ++i) { - p = ps.get(i); - int s = sign(p); - if (max_var(p) != x) { - atom::kind k = (s == 0)?(atom::EQ):((s < 0)?(atom::LT):(atom::GT)); - add_simple_assumption(k, p, false); - ps[i] = ps.back(); - ps.pop_back(); - --i; - } - else if (s == 0) { - if (!eq_valid || degree(p, x) < eq_degree) { - eq_index = i; - eq_valid = true; - eq_degree = degree(p, x); - } - } - } - - if (ps.empty()) { - return; - } - - if (ps.size() == 1) { - project_single(x, ps.get(0)); - return; - } - - // ax + b = 0, p(x) > 0 -> - - if (eq_valid) { - p = ps.get(eq_index); - if (degree(p, x) == 1) { - // ax + b = 0 - // let d be maximal degree of x in p. - // p(x) -> a^d*p(-b/a), a - // perform virtual substitution with equality. - solve_eq(x, eq_index, ps); - } - else { - add_zero_assumption(p); - - for (unsigned j = 0; j < ps.size(); ++j) { - if (j == eq_index) - continue; - p = ps.get(j); - int s = sign(p); - atom::kind k = (s == 0)?(atom::EQ):((s < 0)?(atom::LT):(atom::GT)); - add_simple_assumption(k, p, false); - } - } - return; - } - - unsigned num_lub = 0, num_glb = 0; - unsigned glb_index = 0, lub_index = 0; - scoped_anum lub(m_am), glb(m_am), x_val(m_am); - x_val = m_assignment.value(x); - bool glb_valid = false, lub_valid = false; - for (unsigned i = 0; i < ps.size(); ++i) { - p = ps.get(i); - scoped_anum_vector & roots = m_roots_tmp; - roots.reset(); - m_am.isolate_roots(p, undef_var_assignment(m_assignment, x), roots); - for (auto const& r : roots) { - int s = m_am.compare(x_val, r); - SASSERT(s != 0); - - if (s < 0 && (!lub_valid || m_am.lt(r, lub))) { - lub_index = i; - m_am.set(lub, r); - lub_valid = true; - } - - if (s > 0 && (!glb_valid || m_am.lt(glb, r))) { - glb_index = i; - m_am.set(glb, r); - glb_valid = true; - } - if (s < 0) ++num_lub; - if (s > 0) ++num_glb; - } - } - TRACE(nlsat_explain, tout << "glb: " << num_glb << " lub: " << num_lub << "\n" << lub_index << "\n" << glb_index << "\n" << ps << "\n";); - - if (num_lub == 0) { - project_plus_infinity(x, ps); - return; - } - - if (num_glb == 0) { - project_minus_infinity(x, ps); - return; - } - - if (num_lub <= num_glb) { - glb_index = lub_index; - } - - project_pairs(x, glb_index, ps); - } - - void project_plus_infinity(var x, polynomial_ref_vector const& ps) { - polynomial_ref p(m_pm), lc(m_pm); - for (unsigned i = 0; i < ps.size(); ++i) { - p = ps.get(i); - unsigned d = degree(p, x); - lc = m_pm.coeff(p, x, d); - if (!is_const(lc)) { - int s = sign(p); - SASSERT(s != 0); - atom::kind k = (s > 0)?(atom::GT):(atom::LT); - add_simple_assumption(k, lc); - } - } - } - - void project_minus_infinity(var x, polynomial_ref_vector const& ps) { - polynomial_ref p(m_pm), lc(m_pm); - for (unsigned i = 0; i < ps.size(); ++i) { - p = ps.get(i); - unsigned d = degree(p, x); - lc = m_pm.coeff(p, x, d); - if (!is_const(lc)) { - int s = sign(p); - TRACE(nlsat_explain, tout << "degree: " << d << " " << lc << " sign: " << s << "\n";); - SASSERT(s != 0); - atom::kind k; - if (s > 0) { - k = (d % 2 == 0)?(atom::GT):(atom::LT); - } - else { - k = (d % 2 == 0)?(atom::LT):(atom::GT); - } - add_simple_assumption(k, lc); - } - } - } - + + void project_pairs(var x, unsigned idx, polynomial_ref_vector const& ps) { TRACE(nlsat_explain, tout << "project pairs\n";); polynomial_ref p(m_pm); @@ -2041,49 +1840,7 @@ namespace nlsat { project(m_ps2, x); } - void solve_eq(var x, unsigned idx, polynomial_ref_vector const& ps) { - polynomial_ref p(m_pm), A(m_pm), B(m_pm), C(m_pm), D(m_pm), E(m_pm), q(m_pm), r(m_pm); - polynomial_ref_vector As(m_pm), Bs(m_pm); - p = ps.get(idx); - SASSERT(degree(p, x) == 1); - A = m_pm.coeff(p, x, 1); - B = m_pm.coeff(p, x, 0); - As.push_back(m_pm.mk_const(rational(1))); - Bs.push_back(m_pm.mk_const(rational(1))); - B = neg(B); - TRACE(nlsat_explain, tout << "p: " << p << " A: " << A << " B: " << B << "\n";); - // x = B/A - for (unsigned i = 0; i < ps.size(); ++i) { - if (i != idx) { - q = ps.get(i); - unsigned d = degree(q, x); - D = m_pm.mk_const(rational(1)); - E = D; - r = m_pm.mk_zero(); - for (unsigned j = As.size(); j <= d; ++j) { - D = As.back(); As.push_back(A * D); - D = Bs.back(); Bs.push_back(B * D); - } - for (unsigned j = 0; j <= d; ++j) { - // A^d*p0 + A^{d-1}*B*p1 + ... + B^j*A^{d-j}*pj + ... + B^d*p_d - C = m_pm.coeff(q, x, j); - TRACE(nlsat_explain, tout << "coeff: q" << j << ": " << C << "\n";); - if (!is_zero(C)) { - D = As.get(d - j); - E = Bs.get(j); - r = r + D*E*C; - } - } - TRACE(nlsat_explain, tout << "p: " << p << " q: " << q << " r: " << r << "\n";); - ensure_sign(r); - } - else { - ensure_sign(A); - } - } - - } - + void maximize(var x, unsigned num, literal const * ls, scoped_anum& val, bool& unbounded) { svector lits; polynomial_ref p(m_pm); @@ -2143,8 +1900,8 @@ namespace nlsat { m_imp->m_add_all_coeffs = f; } - void explain::set_signed_project(bool f) { - m_imp->m_signed_project = f; + void explain::set_add_zero_disc(bool f) { + m_imp->m_add_zero_disc = f; } void explain::main_operator(unsigned n, literal const * ls, scoped_literal_vector & result) { diff --git a/src/nlsat/nlsat_explain.h b/src/nlsat/nlsat_explain.h index 2c3adfcb2..6ca08e699 100644 --- a/src/nlsat/nlsat_explain.h +++ b/src/nlsat/nlsat_explain.h @@ -45,7 +45,7 @@ namespace nlsat { void set_minimize_cores(bool f); void set_factor(bool f); void set_add_all_coeffs(bool f); - void set_signed_project(bool f); + void set_add_zero_disc(bool f); /** \brief Given a set of literals ls[0], ... ls[n-1] s.t. diff --git a/src/nlsat/nlsat_params.pyg b/src/nlsat/nlsat_params.pyg index b035f4189..2403f94b2 100644 --- a/src/nlsat/nlsat_params.pyg +++ b/src/nlsat/nlsat_params.pyg @@ -9,6 +9,7 @@ def_module_params('nlsat', ('lazy', UINT, 0, "how lazy the solver is."), ('reorder', BOOL, True, "reorder variables."), ('log_lemmas', BOOL, False, "display lemmas as self-contained SMT formulas"), + ('log_lemma_smtrat', BOOL, False, "log lemmas to be readable by smtrat"), ('dump_mathematica', BOOL, False, "display lemmas as matematica"), ('check_lemmas', BOOL, False, "check lemmas on the fly using an independent nlsat solver"), ('simplify_conflicts', BOOL, True, "simplify conflicts using equalities before resolving them in nlsat solver."), @@ -20,5 +21,7 @@ def_module_params('nlsat', ('seed', UINT, 0, "random seed."), ('factor', BOOL, True, "factor polynomials produced during conflict resolution."), ('add_all_coeffs', BOOL, False, "add all polynomial coefficients during projection."), + ('zero_disc', BOOL, False, "add_zero_assumption to the vanishing discriminant."), ('known_sat_assignment_file_name', STRING, "", "the file name of a known solution: used for debugging only") + )) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 084e3a479..1f1e21c93 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -219,9 +219,11 @@ namespace nlsat { unsigned m_random_seed; bool m_inline_vars; bool m_log_lemmas; + bool m_log_lemma_smtrat; bool m_dump_mathematica; bool m_check_lemmas; unsigned m_max_conflicts; + unsigned m_lemma_rlimit; unsigned m_lemma_count; unsigned m_variable_ordering_strategy; bool m_set_0_more; @@ -269,6 +271,7 @@ namespace nlsat { reset_statistics(); mk_true_bvar(); m_lemma_count = 0; + m_lemma_rlimit = 100 * 1000; // one hundred seconds } ~imp() { @@ -295,6 +298,7 @@ namespace nlsat { m_random_seed = p.seed(); m_inline_vars = p.inline_vars(); m_log_lemmas = p.log_lemmas(); + m_log_lemma_smtrat = p.log_lemma_smtrat(); m_dump_mathematica= p.dump_mathematica(); m_check_lemmas = p.check_lemmas(); m_variable_ordering_strategy = p.variable_ordering_strategy(); @@ -307,6 +311,7 @@ namespace nlsat { m_explain.set_minimize_cores(min_cores); m_explain.set_factor(p.factor()); m_explain.set_add_all_coeffs(p.add_all_coeffs()); + m_explain.set_add_zero_disc(p.zero_disc()); m_am.updt_params(p.p); } @@ -750,6 +755,14 @@ namespace nlsat { m_atoms[b] = new_atom; new_atom->m_bool_var = b; m_pm.inc_ref(new_atom->p()); + TRACE(nlsat_solver, + tout << "created root literal b" << b << ": "; + display(tout, literal(b, false)) << "\n"; + tout << " kind: " << k << ", index: " << i << ", variable: x" << x << "\n"; + tout << " polynomial: "; + display_polynomial(tout, new_atom->p(), m_display_var); + tout << "\n"; + ); return b; } @@ -971,8 +984,7 @@ namespace nlsat { lbool val = l_undef; // Arithmetic atom: evaluate directly - var max = a->max_var(); - SASSERT(debug_assignment.is_assigned(max)); + SASSERT(debug_assignment.is_assigned(a->max_var())); val = to_lbool(debug_evaluator.eval(a, l.sign())); SASSERT(val != l_undef); if (val == l_true) @@ -1110,25 +1122,39 @@ namespace nlsat { } } - void log_lemma(std::ostream& out, clause const& cls) { - log_lemma(out, cls.size(), cls.data(), false); + void log_lemma(std::ostream& out, clause const& cls, std::string annotation) { + log_lemma(out, cls.size(), cls.data(), true, annotation); } - void log_lemma(std::ostream& out, unsigned n, literal const* cls, bool is_valid) { - ++m_lemma_count; - out << "(set-logic ALL)\n"; - if (is_valid) { - display_smt2_bool_decls(out); - display_smt2_arith_decls(out); + void log_lemma(std::ostream& out, unsigned n, literal const* cls, bool is_valid, std::string annotation) { + bool_vector used_vars(num_vars(), false); + bool_vector used_bools(usize(m_atoms), false); + var_vector vars; + for (unsigned j = 0; j < n; j++) { + literal lit = cls[j]; + bool_var b = lit.var(); + if (b != null_bool_var && b < used_bools.size()) + used_bools[b] = true; + vars.reset(); + this->vars(lit, vars); + for (var v : vars) + used_vars[v] = true; } - else - display_smt2(out); + display(out << "(echo \"#" << m_lemma_count++ << ":" << annotation << ":", n, cls) << "\")\n"; + if (m_log_lemma_smtrat) + out << "(set-logic NRA)\n"; + else + out << "(set-logic ALL)\n"; + out << "(set-option :rlimit " << m_lemma_rlimit << ")\n"; + if (is_valid) { + display_smt2_bool_decls(out, used_bools); + display_smt2_arith_decls(out, used_vars); + } + 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) { @@ -1152,12 +1178,6 @@ namespace nlsat { TRACE(nlsat_sort, display(tout << "mk_clause:\n", *cls) << "\n";); std::sort(cls->begin(), cls->end(), lit_lt(*this)); TRACE(nlsat, display(tout << " after sort:\n", *cls) << "\n";); - if (learned && m_log_lemmas) { - log_lemma(verbose_stream(), *cls); - } - if (learned && m_check_lemmas) { - check_lemma(cls->size(), cls->data(), false, cls->assumptions()); - } if (learned) m_learned.push_back(cls); else @@ -1553,7 +1573,7 @@ namespace nlsat { unsigned first_undef = UINT_MAX; // position of the first undefined literal interval_set_ref first_undef_set(m_ism); // infeasible region of the first undefined literal interval_set * xk_set = m_infeasible[m_xk]; // current set of infeasible interval for current variable - TRACE(nlsat_inf_set, tout << "m_infeasible["<< debug_get_var_name(m_xk) << "]:"; + TRACE(nlsat_inf_set, tout << "m_infeasible[x"<< m_xk << "]:"; m_ism.display(tout, xk_set) << "\n";); SASSERT(!m_ism.is_full(xk_set)); for (unsigned idx = 0; idx < cls.size(); ++idx) { @@ -1573,7 +1593,7 @@ namespace nlsat { SASSERT(a != nullptr); interval_set_ref curr_set(m_ism); curr_set = m_evaluator.infeasible_intervals(a, l.sign(), &cls); - TRACE(nlsat_inf_set, + TRACE(nlsat_inf_set, tout << "infeasible set for literal: "; display(tout, l); tout << "\n"; m_ism.display(tout, curr_set); tout << "\n"; display(tout << "cls: " , cls) << "\n"; tout << "m_xk:" << m_xk << "(" << debug_get_var_name(m_xk) << ")"<< "\n";); @@ -1599,7 +1619,16 @@ namespace nlsat { TRACE(nlsat_inf_set, tout << "infeasible set + current set = R, skip literal\n"; display(tout, cls) << "\n"; display_assignment_for_clause(tout, cls); - m_ism.display(tout, tmp); tout << "\n"; + m_ism.display(tout, tmp) << "\n"; + literal_vector inf_lits; + ptr_vector inf_clauses; + m_ism.get_justifications(tmp, inf_lits, inf_clauses); + if (!inf_lits.empty()) { + tout << "Interval witnesses:\n"; + for (literal inf_lit : inf_lits) { + display(tout << " ", inf_lit) << "\n"; + } + } ); R_propagate(~l, tmp, false); continue; @@ -1869,6 +1898,14 @@ namespace nlsat { << " :learned " << m_learned.size() << ")\n"); } + void try_reorder() { + gc(); + if (m_stats.m_restarts % 10) + return; + if (m_reordered) + restore_order(); + apply_reorder(); + } lbool search_check() { lbool r = l_undef; @@ -1880,6 +1917,9 @@ namespace nlsat { if (r != l_true) break; ++m_stats.m_restarts; + + try_reorder(); + vector> bounds; for (var x = 0; x < num_vars(); x++) { @@ -1905,13 +1945,6 @@ namespace nlsat { if (bounds.empty()) break; - gc(); - if (m_stats.m_restarts % 10 == 0) { - if (m_reordered) - restore_order(); - apply_reorder(); - } - init_search(); IF_VERBOSE(2, verbose_stream() << "(nlsat-b&b :conflicts " << m_stats.m_conflicts << " :decisions " << m_stats.m_decisions @@ -2197,45 +2230,107 @@ namespace nlsat { display_mathematica_lemma(out, core.size(), core.data(), true); return out; } + + void log_assignment_lemma_smt2(std::ostream& out, lazy_justification const & jst) { + // This lemma is written down only for debug purposes, it does not participate in the algorithm. + // We need to be sure that lazy certifacation is sound on the sample + // In this lemma we do not use literals created by projection + literal_vector core; + bool_vector used_vars(num_vars(), false); + bool_vector used_bools(usize(m_atoms), false); + + var_vector vars; + for (unsigned i = 0; i < jst.num_lits(); ++i) { + literal lit = ~jst.lit(i); + core.push_back(lit); + bool_var b = lit.var(); + if (b != null_bool_var && b < used_bools.size()) + used_bools[b] = true; + vars.reset(); + this->vars(lit, vars); + for (var v : vars) + used_vars[v] = true; + } + std::ostringstream comment; + bool any_var = false; + display_num_assignment(comment, &used_vars); + if (!any_var) + comment << " (none)"; + comment << "; literals:"; + if (jst.num_lits() == 0) { + comment << " (none)"; + } + else { + for (unsigned i = 0; i < jst.num_lits(); ++i) { + comment << " "; + display(comment, jst.lit(i)); + if (i < jst.num_lits() - 1) + comment << " /\\"; + } + } + out << "(echo \"#" << m_lemma_count++ << ":assignment lemma " << comment.str() << "\")\n"; + if (m_log_lemma_smtrat) + out << "(set-logic NRA)\n"; + else + out << "(set-logic ALL)\n"; + + out << "(set-option :rlimit " << m_lemma_rlimit << ")\n"; + display_smt2_bool_decls(out, used_bools); + display_smt2_arith_decls(out, used_vars); + display_bool_assignment(out, false, &used_bools); + display_num_assignment(out, &used_vars); + for (literal lit : core) { + literal asserted = ~lit; + bool is_root = asserted.var() != null_bool_var && + m_atoms[asserted.var()] != nullptr && + m_atoms[asserted.var()]->is_root_atom(); + if (is_root) { + display_root_literal_block(out, asserted, m_display_var); + } + else { + out << "(assert "; + display_smt2(out, asserted); + out << ")\n"; + } + } + out << "(check-sat)\n"; + out << "(reset)\n"; + } void resolve_lazy_justification(bool_var b, lazy_justification const & jst) { + // ++ttt; TRACE(nlsat_resolve, tout << "resolving lazy_justification for b" << b << "\n";); unsigned sz = jst.num_lits(); // Dump lemma as Mathematica formula that must be true, - // if the current interpretation (really) makes the core in jst infeasible. - TRACE(nlsat_mathematica, tout << "assignment lemma\n"; print_out_as_math(tout, jst);); - if (m_dump_mathematica) { -// verbose_stream() << "assignment lemma in matematica\n"; + // if the current interpretation, the sample, makes the core in jst infeasible. + TRACE(nlsat_mathematica, + tout << "assignment lemma\n"; print_out_as_math(tout, jst) << "\n:assignment lemas as smt2\n"; + log_assignment_lemma_smt2(tout, jst);); + if (m_dump_mathematica) print_out_as_math(verbose_stream(), jst) << std::endl; -// verbose_stream() << "\nend of assignment lemma\n"; - } - - - m_lazy_clause.reset(); + m_explain.main_operator(jst.num_lits(), jst.lits(), m_lazy_clause); for (unsigned i = 0; i < sz; i++) m_lazy_clause.push_back(~jst.lit(i)); // lazy clause is a valid clause - TRACE(nlsat_mathematica, display_mathematica_lemma(tout, m_lazy_clause.size(), m_lazy_clause.data());); - if (m_dump_mathematica) { -// verbose_stream() << "lazy clause\n"; - display_mathematica_lemma(verbose_stream(), m_lazy_clause.size(), m_lazy_clause.data()) << std::endl; -// verbose_stream() << "\nend of lazy\n"; - } + TRACE(nlsat_mathematica, tout << "ttt:" << m_lemma_count << "\n"; display_mathematica_lemma(tout, m_lazy_clause.size(), m_lazy_clause.data());); + if (m_dump_mathematica) + display_mathematica_lemma(std::cout, m_lazy_clause.size(), m_lazy_clause.data()) << std::endl; TRACE(nlsat_proof_sk, tout << "theory lemma\n"; display_abst(tout, m_lazy_clause.size(), m_lazy_clause.data()); tout << "\n";); TRACE(nlsat_resolve, tout << "m_xk: " << m_xk << ", "; m_display_var(tout, m_xk) << "\n"; 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_log_lemmas) { + log_assignment_lemma_smt2(std::cout, jst); + log_lemma(verbose_stream(), m_lazy_clause.size(), m_lazy_clause.data(), true, "conflict"); + } if (m_check_lemmas) { check_lemma(m_lazy_clause.size(), m_lazy_clause.data(), false, nullptr); @@ -2486,8 +2581,8 @@ namespace nlsat { 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); + // if (m_log_lemmas) + // log_lemma(std::cout, m_lemma.size(), m_lemma.data(), false); // There are two possibilities: // 1) m_lemma contains only literals from previous stages, and they @@ -2808,7 +2903,7 @@ namespace nlsat { // verbose_stream() << "\npermutation: " << p[0] << " count " << count << " " << m_rlimit.is_canceled() << "\n"; reinit_cache(); SASSERT(num_vars() == sz); - TRACE(nlsat_bool_assignment_bug, tout << "before reset watches\n"; display_bool_assignment(tout);); + TRACE(nlsat_bool_assignment_bug, tout << "before reset watches\n"; display_bool_assignment(tout, false, nullptr);); reset_watches(); assignment new_assignment(m_am); for (var x = 0; x < num_vars(); x++) { @@ -2850,7 +2945,7 @@ namespace nlsat { m_pm.rename(sz, p); for (auto& b : m_bounds) b.x = p[b.x]; - TRACE(nlsat_bool_assignment_bug, tout << "before reinit cache\n"; display_bool_assignment(tout);); + TRACE(nlsat_bool_assignment_bug, tout << "before reinit cache\n"; display_bool_assignment(tout, false, nullptr);); reinit_cache(); m_assignment.swap(new_assignment); reattach_arith_clauses(m_clauses); @@ -3261,9 +3356,34 @@ namespace nlsat { // // ----------------------- - std::ostream& display_num_assignment(std::ostream & out, display_var_proc const & proc) const { + std::ostream& display_num_assignment(std::ostream & out, display_var_proc const & proc, bool_vector const* used_vars = nullptr) const { + bool restrict = used_vars != nullptr; for (var x = 0; x < num_vars(); x++) { - if (m_assignment.is_assigned(x)) { + if (restrict && (x >= used_vars->size() || !(*used_vars)[x])) + continue; + if (!m_assignment.is_assigned(x)) + continue; + if (restrict) { + out << "(assert (= "; + proc(out, x); + out << " "; + if (m_am.is_rational(m_assignment.value(x))) { + mpq q; + m_am.to_rational(m_assignment.value(x), q); + m_am.qm().display_smt2(out, q, false); + } + else if (m_log_lemma_smtrat) { + std::ostringstream var_name; + proc(var_name, x); + std::string name = var_name.str(); + m_am.display_root_smtrat(out, m_assignment.value(x), name.c_str()); + } + else { + m_am.display_root_smt2(out, m_assignment.value(x)); + } + out << "))\n"; + } + else { proc(out, x); out << " -> "; m_am.display_decimal(out, m_assignment.value(x)); @@ -3273,8 +3393,21 @@ namespace nlsat { return out; } - std::ostream& display_bool_assignment(std::ostream & out, bool eval_atoms = false) const { + std::ostream& display_bool_assignment(std::ostream & out, bool eval_atoms = false, bool_vector const* used = nullptr) const { unsigned sz = usize(m_atoms); + if (used != nullptr) { + for (bool_var b = 0; b < sz; b++) { + if (b >= used->size() || !(*used)[b]) + continue; + if (m_atoms[b] != nullptr) + continue; + lbool val = m_bvalues[b]; + if (val == l_undef) + continue; + out << "(assert (= b" << b << " " << (val == l_true ? "true" : "false") << "))\n"; + } + return out; + } if (!eval_atoms) { for (bool_var b = 0; b < sz; b++) { if (m_bvalues[b] == l_undef) @@ -3319,13 +3452,13 @@ namespace nlsat { return !first; } - std::ostream& display_num_assignment(std::ostream & out) const { - return display_num_assignment(out, m_display_var); + std::ostream& display_num_assignment(std::ostream & out, const bool_vector* used_vars=nullptr) const { + return display_num_assignment(out, m_display_var, used_vars); } std::ostream& display_assignment(std::ostream& out, bool eval_atoms = false) const { - display_bool_assignment(out, eval_atoms); - display_num_assignment(out); + display_bool_assignment(out, eval_atoms, nullptr); + display_num_assignment(out, nullptr); return out; } @@ -3529,44 +3662,93 @@ namespace nlsat { } - 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 + std::ostream& display_root_term_smtrat(std::ostream& out, root_atom const& a, display_var_proc const& proc) const { + out << "(root "; + display_polynomial_smt2(out, a.p(), proc); + out << " " << a.i() << " "; + proc(out, a.x()); + out << ")"; + return out; + } + + std::ostream& display_root_atom_smtrat(std::ostream& out, root_atom const& a, display_var_proc const& proc) const { + char const* rel = "="; + switch (a.get_kind()) { + case atom::ROOT_LT: rel = "<"; break; + case atom::ROOT_GT: rel = ">"; break; + case atom::ROOT_LE: rel = "<="; break; + case atom::ROOT_GE: rel = ">="; break; + case atom::ROOT_EQ: rel = "="; break; + default: UNREACHABLE(); break; + } + out << "(" << rel << " "; + proc(out, a.x()); + out << " "; + display_root_term_smtrat(out, a, proc); + out << ")"; + return out; + } + + struct root_poly_subst : public display_var_proc { + display_var_proc const& m_proc; + var m_var; + char const* m_name; + root_poly_subst(display_var_proc const& p, var v, char const* name): + m_proc(p), m_var(v), m_name(name) {} + std::ostream& operator()(std::ostream& dst, var x) const override { + if (x == m_var) + return dst << m_name; + return m_proc(dst, x); + } + }; + + template + std::ostream& display_root_quantified(std::ostream& out, root_atom const& a, display_var_proc const& proc, Printer const& printer) const { + // if (a.i() == 1 && m_pm.degree(a.p(), a.x()) == 1) + // return display_linear_root_smt2(out, a, proc); + + auto mk_y_name = [](unsigned j) { + return std::string("y") + std::to_string(j); + }; + + unsigned idx = a.i(); + SASSERT(idx > 0); + out << "(exists ("; - for (unsigned j = 0; j < a.i(); ++j) { - std::string y = std::string("y") + std::to_string(j); + for (unsigned j = 0; j < idx; ++j) { + auto y = mk_y_name(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"; + out << ")\n (and\n"; + + for (unsigned j = 0; j < idx; ++j) { + auto y = mk_y_name(j); + out << " (= "; + printer(out, y.c_str()); + out << " 0)\n"; } - std::string yn = "y" + std::to_string(a.i() - 1); + for (unsigned j = 0; j + 1 < idx; ++j) { + auto y1 = mk_y_name(j); + auto y2 = mk_y_name(j + 1); + out << " (< " << y1 << " " << y2 << ")\n"; + } - // 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"; + auto y0 = mk_y_name(0); + out << " (forall ((y Real)) (=> (< y " << y0 << ") (not (= "; + printer(out, "y"); + out << " 0))))\n"; + + for (unsigned j = 0; j + 1 < idx; ++j) { + auto y1 = mk_y_name(j); + auto y2 = mk_y_name(j + 1); + out << " (forall ((y Real)) (=> (and (< " << y1 << " y) (< y " << y2 << ")) (not (= "; + printer(out, "y"); + out << " 0))))\n"; } + + std::string yn = mk_y_name(idx - 1); + out << " "; 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; @@ -3575,12 +3757,33 @@ namespace nlsat { case atom::ROOT_EQ: out << "(= "; proc(out, a.x()); out << " " << yn << ")"; break; default: UNREACHABLE(); break; } - out << "))"; + out << "\n )\n)"; return out; -#endif + } + std::ostream& display_root_smt2(std::ostream& out, root_atom const& a, display_var_proc const& proc) const { + if (m_log_lemma_smtrat) + return display_root_atom_smtrat(out, a, proc); + auto inline_printer = [&](std::ostream& dst, char const* y) -> std::ostream& { + root_poly_subst poly_proc(proc, a.x(), y); + return display_polynomial_smt2(dst, a.p(), poly_proc); + }; + return display_root_quantified(out, a, proc, inline_printer); + } - return display_root(out, a, proc); + std::ostream& display_root_literal_block(std::ostream& out, literal lit, display_var_proc const& proc) const { + bool_var b = lit.var(); + SASSERT(m_atoms[b] != nullptr && m_atoms[b]->is_root_atom()); + auto const& a = *to_root_atom(m_atoms[b]); + + out << "(assert "; + if (lit.sign()) + out << "(not "; + display_root_smt2(out, a, proc); + if (lit.sign()) + out << ")"; + out << ")\n"; + return out; } std::ostream& display_root(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { @@ -3998,31 +4201,51 @@ namespace nlsat { return m_display_var(out, j); } - std::ostream& display_smt2_arith_decls(std::ostream & out) const { + std::ostream& display_smt2_arith_decls(std::ostream & out, bool_vector& used_vars) const { unsigned sz = m_is_int.size(); for (unsigned i = 0; i < sz; i++) { - if (is_int(i)) { - out << "(declare-fun "; m_display_var(out, i) << " () Int)\n"; + if (!used_vars[i]) continue; + out << "(declare-fun "; + m_display_var(out, i); + out << " () "; + if (!m_log_lemma_smtrat && is_int(i)) { + out << "Int"; } else { - out << "(declare-fun "; m_display_var(out, i) << " () Real)\n"; + out << "Real"; } + out << ")\n"; } return out; } - std::ostream& display_smt2_bool_decls(std::ostream & out) const { + std::ostream& display_smt2_bool_decls(std::ostream & out, const bool_vector& used_bools) const { unsigned sz = usize(m_atoms); for (unsigned i = 0; i < sz; i++) { - if (m_atoms[i] == nullptr) + if (m_atoms[i] == nullptr && used_bools[i]) out << "(declare-fun b" << i << " () Bool)\n"; } return out; } std::ostream& display_smt2(std::ostream & out) const { - display_smt2_bool_decls(out); - display_smt2_arith_decls(out); + bool_vector used_vars(num_vars(), false); + bool_vector used_bools(usize(m_atoms), false); + var_vector vars; + for (clause* c: m_clauses) { + for (literal lit : *c) { + bool_var b = lit.var(); + if (b != null_bool_var && b < used_bools.size()) + used_bools[b] = true; + vars.reset(); + this->vars(lit, vars); + for (var v : vars) + used_vars[v] = true; + } + } + + display_smt2_bool_decls(out, used_bools); + display_smt2_arith_decls(out, used_vars); out << "(assert (and true\n"; for (clause* c : m_clauses) { display_smt2(out, *c, m_display_var) << "\n"; diff --git a/src/opt/maxcore.cpp b/src/opt/maxcore.cpp index db05926bb..499d0f65e 100644 --- a/src/opt/maxcore.cpp +++ b/src/opt/maxcore.cpp @@ -589,22 +589,6 @@ public: --m_correction_set_size; } trace(); - bool no_hidden_soft = (m_st == s_primal_dual || m_st == s_primal || m_st == s_primal_binary); - if (no_hidden_soft && m_c.num_objectives() == 1 && m_pivot_on_cs && m_csmodel.get() && m_correction_set_size < core.size()) { - exprs cs; - get_current_correction_set(m_csmodel.get(), cs); - m_correction_set_size = cs.size(); - TRACE(opt, tout << "cs " << m_correction_set_size << " " << core.size() << "\n";); - if (m_correction_set_size >= core.size()) - return; - rational w(0); - for (expr* a : m_asms) { - rational w1 = m_asm2weight[a]; - if (w != 0 && w1 != w) return; - w = w1; - } - process_sat(cs); - } } bool get_mus_model(model_ref& mdl) { diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 388befe93..2156df4c9 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -20,6 +20,7 @@ Notes: #include "util/gparams.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" +#include "ast/ast_translation.h" #include "ast/bv_decl_plugin.h" #include "ast/pb_decl_plugin.h" #include "ast/ast_smt_pp.h" @@ -155,6 +156,57 @@ namespace opt { reset_maxsmts(); } + context* context::translate(ast_manager& target_m) { + // Create AST translator + ast_translation translator(m, target_m); + + // Create new context in target manager + context* result = alloc(context, target_m); + + // Copy parameters + result->updt_params(m_params); + + // Set logic + if (m_logic != symbol::null) { + result->set_logic(m_logic); + } + + // Translate hard constraints from scoped state + for (expr* e : m_scoped_state.m_hard) { + result->add_hard_constraint(translator(e)); + } + + // Translate objectives + for (auto const& obj : m_scoped_state.m_objectives) { + if (obj.m_type == O_MAXIMIZE || obj.m_type == O_MINIMIZE) { + // Translate maximize/minimize objectives + app_ref translated_term(to_app(translator(obj.m_term.get())), target_m); + result->add_objective(translated_term, obj.m_type == O_MAXIMIZE); + } + else if (obj.m_type == O_MAXSMT) { + // Translate soft constraints for MaxSMT objectives + for (unsigned i = 0; i < obj.m_terms.size(); ++i) { + result->add_soft_constraint( + translator(obj.m_terms.get(i)), + obj.m_weights[i], + obj.m_id + ); + } + } + } + + // Copy configuration flags + result->m_enable_sat = m_enable_sat; + result->m_enable_sls = m_enable_sls; + result->m_is_clausal = m_is_clausal; + result->m_pp_neat = m_pp_neat; + result->m_pp_wcnf = m_pp_wcnf; + result->m_incremental = m_incremental; + result->m_maxsat_engine = m_maxsat_engine; + + return result; + } + void context::reset_maxsmts() { for (auto& kv : m_maxsmts) { dealloc(kv.m_value); @@ -406,6 +458,7 @@ namespace opt { void context::set_model(model_ref& m) { m_model = m; + m_model_available = true; opt_params optp(m_params); symbol prefix = optp.solution_prefix(); bool model2console = optp.dump_models(); @@ -438,6 +491,8 @@ namespace opt { void context::get_model_core(model_ref& mdl) { + if (!m_model_available) + throw default_exception("model is not available"); mdl = m_model; CTRACE(opt, mdl, tout << *mdl;); fix_model(mdl); @@ -1678,6 +1733,7 @@ namespace opt { m_model.reset(); m_model_fixed.reset(); m_core.reset(); + m_model_available = false; } void context::set_pareto(pareto_base* p) { diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index ed2377bab..2d6c329c0 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -186,7 +186,8 @@ namespace opt { map_t m_maxsmts; scoped_state m_scoped_state; vector m_objectives; - model_ref m_model; + model_ref m_model; + bool m_model_available = false; model_converter_ref m_model_converter; generic_model_converter_ref m_fm; sref_vector m_model_fixed; @@ -209,6 +210,13 @@ namespace opt { public: context(ast_manager& m); ~context() override; + + /** + * \brief Create a clone of the optimization context in a different ast_manager. + * Translates all assertions, objectives, and solver state. + */ + context* translate(ast_manager& target_m); + unsigned add_soft_constraint(expr* f, rational const& w, symbol const& id); unsigned add_objective(app* t, bool is_max); void add_hard_constraint(expr* f); diff --git a/src/params/sat_params.pyg b/src/params/sat_params.pyg index d45b4c0bf..2c76b89c4 100644 --- a/src/params/sat_params.pyg +++ b/src/params/sat_params.pyg @@ -75,15 +75,6 @@ def_module_params('sat', ('anf', BOOL, False, 'enable ANF based simplification in-processing'), ('anf.delay', UINT, 2, 'delay ANF simplification by in-processing round'), ('anf.exlin', BOOL, False, 'enable extended linear simplification'), - ('cut', BOOL, False, 'enable AIG based simplification in-processing'), - ('cut.delay', UINT, 2, 'delay cut simplification by in-processing round'), - ('cut.aig', BOOL, False, 'extract aigs (and ites) from cluases for cut simplification'), - ('cut.lut', BOOL, False, 'extract luts from clauses for cut simplification'), - ('cut.xor', BOOL, False, 'extract xors from clauses for cut simplification'), - ('cut.npn3', BOOL, False, 'extract 3 input functions from clauses for cut simplification'), - ('cut.dont_cares', BOOL, True, 'integrate dont cares with cuts'), - ('cut.redundancies', BOOL, True, 'integrate redundancy checking of cuts'), - ('cut.force', BOOL, False, 'force redoing cut-enumeration until a fixed-point'), ('lookahead.cube.cutoff', SYMBOL, 'depth', 'cutoff type used to create lookahead cubes: depth, freevars, psat, adaptive_freevars, adaptive_psat'), # - depth: the maximal cutoff is fixed to the value of lookahead.cube.depth. # So if the value is 10, at most 1024 cubes will be generated of length 10. diff --git a/src/params/smt_parallel_params.pyg b/src/params/smt_parallel_params.pyg index d1e2e567d..03fbe1cb8 100644 --- a/src/params/smt_parallel_params.pyg +++ b/src/params/smt_parallel_params.pyg @@ -20,9 +20,9 @@ def_module_params('smt_parallel', ('explicit_hardness', BOOL, False, 'use explicit hardness metric for cube'), ('cubetree', BOOL, False, 'use cube tree data structure for storing cubes'), ('searchtree', BOOL, False, 'use search tree implementation (parallel2)'), - ('inprocessing', BOOL, False, 'integrate in-processing as a heuristic simplification'), + ('inprocessing', BOOL, True, 'integrate in-processing as a heuristic simplification'), ('inprocessing_delay', UINT, 0, 'number of undef before invoking simplification'), ('param_tuning', BOOL, False, 'whether to tune params online during solving'), ('enable_parallel_smt', BOOL, True, 'whether to run the parallel solver (set to FALSE to test param tuning only)'), - ('tunable_params', STRING, '', 'comma-separated key=value list for online param tuning, e.g. \"smt.arith.nl.horner=false,smt.arith.nl.delay=8\"') + ('tunable_params', STRING, '', 'comma-separated key=value list for online param tuning, e.g. \\"smt.arith.nl.horner=false,smt.arith.nl.delay=8\\"') )) \ No newline at end of file diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index 66da9d707..db7210c22 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -833,7 +833,6 @@ namespace qe { m_answer_simplify(m), m_trail(m), m_div_mc(nullptr) { - s.m_solver.get_explain().set_signed_project(true); m_nftactic = mk_tseitin_cnf_tactic(m); } diff --git a/src/sat/CMakeLists.txt b/src/sat/CMakeLists.txt index 9d1d8dd7e..e85513e49 100644 --- a/src/sat/CMakeLists.txt +++ b/src/sat/CMakeLists.txt @@ -1,7 +1,6 @@ z3_add_component(sat SOURCES dimacs.cpp - sat_aig_cuts.cpp sat_aig_finder.cpp sat_anf_simplifier.cpp sat_asymm_branch.cpp @@ -12,8 +11,6 @@ z3_add_component(sat sat_clause_use_list.cpp sat_cleaner.cpp sat_config.cpp - sat_cut_simplifier.cpp - sat_cutset.cpp sat_ddfw_wrapper.cpp sat_drat.cpp sat_elim_eqs.cpp @@ -21,7 +18,6 @@ z3_add_component(sat sat_integrity_checker.cpp sat_local_search.cpp sat_lookahead.cpp - sat_lut_finder.cpp sat_model_converter.cpp sat_mus.cpp sat_npn3_finder.cpp diff --git a/src/sat/sat_aig_cuts.cpp b/src/sat/sat_aig_cuts.cpp deleted file mode 100644 index 8fd98bc9e..000000000 --- a/src/sat/sat_aig_cuts.cpp +++ /dev/null @@ -1,886 +0,0 @@ -/*++ - Copyright (c) 2020 Microsoft Corporation - - Module Name: - - sat_aig_cuts.cpp - - Abstract: - - Perform cut-set enumeration to identify equivalences. - - Author: - - Nikolaj Bjorner 2020-01-02 - - --*/ - -#include "util/trace.h" -#include "sat/sat_aig_cuts.h" -#include "sat/sat_solver.h" -#include "sat/sat_lut_finder.h" - -namespace sat { - - aig_cuts::aig_cuts() { - m_cut_set1.init(m_region, m_config.m_max_cutset_size + 1, UINT_MAX); - m_cut_set2.init(m_region, m_config.m_max_cutset_size + 1, UINT_MAX); - m_empty_cuts.init(m_region, m_config.m_max_cutset_size + 1, UINT_MAX); - m_num_cut_calls = 0; - m_num_cuts = 0; - } - - vector const& aig_cuts::operator()() { - if (m_config.m_full) flush_roots(); - unsigned_vector node_ids = filter_valid_nodes(); - TRACE(cut_simplifier, display(tout);); - augment(node_ids); - TRACE(cut_simplifier, display(tout);); - ++m_num_cut_calls; - return m_cuts; - } - - void aig_cuts::augment(unsigned_vector const& ids) { - for (unsigned id : ids) { - if (m_aig[id].empty()) { - continue; - } - IF_VERBOSE(20, m_cuts[id].display(verbose_stream() << "augment " << id << "\nbefore\n")); - for (node const& n : m_aig[id]) { - augment(id, n); - } - -#if 0 - // augment cuts directly - m_cut_save.reset(); - cut_set& cs = m_cuts[id]; - for (cut const& c : cs) { - if (c.size() > 1) m_cut_save.push_back(c); - } - for (cut const& c : m_cut_save) { - lut lut(*this, c); - augment_lut(id, lut, cs); - } -#endif - IF_VERBOSE(20, m_cuts[id].display(verbose_stream() << "after\n")); - } - } - - void aig_cuts::augment(unsigned id, node const& n) { - unsigned nc = n.size(); - m_insertions = 0; - cut_set& cs = m_cuts[id]; - if (!is_touched(id, n)) { - // no-op - } - else if (n.is_var()) { - SASSERT(!n.sign()); - } - else if (n.is_lut()) { - lut lut(*this, n); - augment_lut(id, lut, cs); - } - else if (n.is_ite()) { - augment_ite(id, n, cs); - } - else if (nc == 0) { - augment_aig0(id, n, cs); - } - else if (nc == 1) { - augment_aig1(id, n, cs); - } - else if (nc == 2) { - augment_aig2(id, n, cs); - } - else if (nc <= cut::max_cut_size()) { - augment_aigN(id, n, cs); - } - if (m_insertions > 0) { - touch(id); - } - } - - bool aig_cuts::insert_cut(unsigned v, cut const& c, cut_set& cs) { - if (!cs.insert(m_on_cut_add, m_on_cut_del, c)) { - return true; - } - m_num_cuts++; - if (++m_insertions > max_cutset_size(v)) { - return false; - } - while (cs.size() >= max_cutset_size(v)) { - // never evict the first entry, it is used for the starting point - unsigned idx = 1 + (m_rand() % (cs.size() - 1)); - evict(cs, idx); - } - return true; - } - - void aig_cuts::augment_lut(unsigned v, lut const& n, cut_set& cs) { - IF_VERBOSE(4, n.display(verbose_stream() << "augment_lut " << v << " ") << "\n"); - literal l1 = n.child(0); - VERIFY(&cs != &lit2cuts(l1)); - for (auto const& a : lit2cuts(l1)) { - m_tables[0] = &a; - m_lits[0] = l1; - cut b(a); - augment_lut_rec(v, n, b, 1, cs); - } - } - - void aig_cuts::augment_lut_rec(unsigned v, lut const& n, cut& a, unsigned idx, cut_set& cs) { - if (idx < n.size()) { - literal lit = n.child(idx); - VERIFY(&cs != &lit2cuts(lit)); - for (auto const& b : lit2cuts(lit)) { - cut ab; - if (!ab.merge(a, b)) continue; - m_tables[idx] = &b; - m_lits[idx] = lit; - augment_lut_rec(v, n, ab, idx + 1, cs); - } - return; - } - for (unsigned i = n.size(); i-- > 0; ) { - m_luts[i] = m_tables[i]->shift_table(a); - } - uint64_t r = 0; - SASSERT(a.size() <= 6); - SASSERT(n.size() <= 6); - for (unsigned j = (1u << a.size()); j-- > 0; ) { - unsigned w = 0; - // when computing the output at position j, - // the i'th bit to index into n.lut() is - // based on the j'th output bit in lut[i] - // m_lits[i].sign() tracks if output bit is negated - for (unsigned i = n.size(); i-- > 0; ) { - w |= (((m_luts[i] >> j) ^ (uint64_t)m_lits[i].sign()) & 1u) << i; - } - r |= ((n.table() >> w) & 1u) << j; - } - a.set_table(r); - IF_VERBOSE(8, - verbose_stream() << "lut: " << v << " - " << a << "\n"; - for (unsigned i = 0; i < n.size(); ++i) { - verbose_stream() << m_lits[i] << ": " << *m_tables[i] << "\n"; - }); - insert_cut(v, a, cs); - } - - void aig_cuts::augment_ite(unsigned v, node const& n, cut_set& cs) { - IF_VERBOSE(4, display(verbose_stream() << "augment_ite " << v << " ", n) << "\n"); - literal l1 = child(n, 0); - literal l2 = child(n, 1); - literal l3 = child(n, 2); - VERIFY(&cs != &lit2cuts(l1)); - VERIFY(&cs != &lit2cuts(l2)); - VERIFY(&cs != &lit2cuts(l3)); - for (auto const& a : lit2cuts(l1)) { - for (auto const& b : lit2cuts(l2)) { - cut ab; - if (!ab.merge(a, b)) continue; - for (auto const& c : lit2cuts(l3)) { - cut abc; - if (!abc.merge(ab, c)) continue; - uint64_t t1 = a.shift_table(abc); - uint64_t t2 = b.shift_table(abc); - uint64_t t3 = c.shift_table(abc); - if (l1.sign()) t1 = ~t1; - if (l2.sign()) t2 = ~t2; - if (l3.sign()) t3 = ~t3; - abc.set_table((t1 & t2) | ((~t1) & t3)); - if (n.sign()) abc.negate(); - if (!insert_cut(v, abc, cs)) return; - } - } - } - } - - void aig_cuts::augment_aig0(unsigned v, node const& n, cut_set& cs) { - IF_VERBOSE(4, display(verbose_stream() << "augment_unit " << v << " ", n) << "\n"); - SASSERT(n.is_and() && n.size() == 0); - reset(cs); - cut c; - c.set_table(n.sign() ? 0x0 : 0x1); - push_back(cs, c); - } - - void aig_cuts::augment_aig1(unsigned v, node const& n, cut_set& cs) { - IF_VERBOSE(4, display(verbose_stream() << "augment_aig1 " << v << " ", n) << "\n"); - SASSERT(n.is_and()); - literal lit = child(n, 0); - VERIFY(&cs != &lit2cuts(lit)); - for (auto const& a : lit2cuts(lit)) { - cut c(a); - if (n.sign()) c.negate(); - if (!insert_cut(v, c, cs)) return; - } - } - - void aig_cuts::augment_aig2(unsigned v, node const& n, cut_set& cs) { - IF_VERBOSE(4, display(verbose_stream() << "augment_aig2 " << v << " ", n) << "\n"); - SASSERT(n.is_and() || n.is_xor()); - literal l1 = child(n, 0); - literal l2 = child(n, 1); - VERIFY(&cs != &lit2cuts(l1)); - VERIFY(&cs != &lit2cuts(l2)); - for (auto const& a : lit2cuts(l1)) { - for (auto const& b : lit2cuts(l2)) { - cut c; - if (!c.merge(a, b)) continue; - uint64_t t1 = a.shift_table(c); - uint64_t t2 = b.shift_table(c); - if (l1.sign()) t1 = ~t1; - if (l2.sign()) t2 = ~t2; - uint64_t t3 = n.is_and() ? (t1 & t2) : (t1 ^ t2); - c.set_table(t3); - if (n.sign()) c.negate(); - // validate_aig2(a, b, v, n, c); - if (!insert_cut(v, c, cs)) return; - } - } - } - - void aig_cuts::augment_aigN(unsigned v, node const& n, cut_set& cs) { - IF_VERBOSE(4, display(verbose_stream() << "augment_aigN " << v << " ", n) << "\n"); - m_cut_set1.reset(m_on_cut_del); - SASSERT(n.is_and() || n.is_xor()); - literal lit = child(n, 0); - for (auto const& a : lit2cuts(lit)) { - cut b(a); - if (lit.sign()) { - b.negate(); - } - m_cut_set1.push_back(m_on_cut_add, b); - } - for (unsigned i = 1; i < n.size(); ++i) { - m_cut_set2.reset(m_on_cut_del); - lit = child(n, i); - m_insertions = 0; - for (auto const& a : m_cut_set1) { - for (auto const& b : lit2cuts(lit)) { - cut c; - if (!c.merge(a, b)) continue; - uint64_t t1 = a.shift_table(c); - uint64_t t2 = b.shift_table(c); - if (lit.sign()) t2 = ~t2; - uint64_t t3 = n.is_and() ? (t1 & t2) : (t1 ^ t2); - c.set_table(t3); - if (i + 1 == n.size() && n.sign()) c.negate(); - if (!insert_cut(UINT_MAX, c, m_cut_set2)) goto next_child; - } - } - next_child: - m_cut_set1.swap(m_cut_set2); - } - m_insertions = 0; - for (auto & cut : m_cut_set1) { - // validate_aigN(v, n, cut); - if (!insert_cut(v, cut, cs)) { - break; - } - } - } - - bool aig_cuts::is_touched(bool_var v, node const& n) { - for (unsigned i = 0; i < n.size(); ++i) { - literal lit = m_literals[n.offset() + i]; - if (is_touched(lit)) { - return true; - } - } - return is_touched(v); - } - - void aig_cuts::reserve(unsigned v) { - m_aig.reserve(v + 1); - m_cuts.reserve(v + 1); - m_max_cutset_size.reserve(v + 1, m_config.m_max_cutset_size); - m_last_touched.reserve(v + 1, 0); - } - - void aig_cuts::add_var(unsigned v) { - reserve(v); - if (m_aig[v].empty()) { - m_aig[v].push_back(node(v)); - init_cut_set(v); - touch(v); - } - } - - void aig_cuts::add_node(bool_var v, node const& n) { - for (unsigned i = 0; i < n.size(); ++i) { - reserve(m_literals[i].var()); - if (m_aig[m_literals[i].var()].empty()) { - add_var(m_literals[i].var()); - } - } - if (m_aig[v].empty() || n.is_const()) { - m_aig[v].reset(); - m_aig[v].push_back(n); - on_node_add(v, n); - init_cut_set(v); - if (n.is_const()) { - augment_aig0(v, n, m_cuts[v]); - } - touch(v); - IF_VERBOSE(12, display(verbose_stream() << "add " << v << " == ", n) << "\n"); - } - else if (m_aig[v][0].is_const() || !insert_aux(v, n)) { - m_literals.shrink(m_literals.size() - n.size()); - TRACE(cut_simplifier, tout << "duplicate\n";); - } - SASSERT(!m_aig[v].empty()); - } - - void aig_cuts::add_node(bool_var v, uint64_t lut, unsigned sz, bool_var const* args) { - TRACE(cut_simplifier, tout << v << " == " << cut::table2string(sz, lut) << " " << bool_var_vector(sz, args) << "\n";); - reserve(v); - unsigned offset = m_literals.size(); - node n(lut, sz, offset); - for (unsigned i = 0; i < sz; ++i) { - reserve(args[i]); - m_literals.push_back(literal(args[i], false)); - } - add_node(v, n); - } - - void aig_cuts::add_node(literal head, bool_op op, unsigned sz, literal const* args) { - TRACE(cut_simplifier, tout << head << " == " << op << " " << literal_vector(sz, args) << "\n";); - unsigned v = head.var(); - reserve(v); - unsigned offset = m_literals.size(); - node n(head.sign(), op, sz, offset); - m_literals.append(sz, args); - for (unsigned i = 0; i < sz; ++i) reserve(args[i].var()); - if (op == and_op || op == xor_op) { - std::sort(m_literals.data() + offset, m_literals.data() + offset + sz); - } - add_node(v, n); - } - - void aig_cuts::add_cut(bool_var v, uint64_t lut, bool_var_vector const& args) { - // args can be assumed to be sorted - DEBUG_CODE(for (unsigned i = 0; i + 1 < args.size(); ++i) VERIFY(args[i] < args[i+1]);); - add_var(v); - for (bool_var w : args) add_var(w); - cut c; - for (bool_var w : args) VERIFY(c.add(w)); - c.set_table(lut); - insert_cut(v, c, m_cuts[v]); - } - - - void aig_cuts::set_root(bool_var v, literal r) { - IF_VERBOSE(10, verbose_stream() << "set-root " << v << " -> " << r << "\n"); - m_roots.push_back(std::make_pair(v, r)); - } - - void aig_cuts::flush_roots() { - if (m_roots.empty()) return; - to_root to_root; - for (unsigned i = m_roots.size(); i-- > 0; ) { - bool_var v = m_roots[i].first; - literal r = m_roots[i].second; - reserve(v); - reserve(r.var()); - literal rr = to_root[r.var()]; - to_root[v] = r.sign() ? ~rr : rr; - } - for (unsigned i = 0; i < m_aig.size(); ++i) { - // invalidate nodes that have been rooted - if (to_root[i] != literal(i, false)) { - m_aig[i].reset(); - reset(m_cuts[i]); - } - else { - unsigned j = 0; - for (node & n : m_aig[i]) { - if (flush_roots(i, to_root, n)) { - m_aig[i][j++] = n; - } - } - m_aig[i].shrink(j); - } - } - for (cut_set& cs : m_cuts) { - flush_roots(to_root, cs); - } - m_roots.reset(); - TRACE(cut_simplifier, display(tout);); - } - - bool aig_cuts::flush_roots(bool_var var, to_root const& to_root, node& n) { - bool changed = false; - for (unsigned i = 0; i < n.size(); ++i) { - literal& lit = m_literals[n.offset() + i]; - literal r = to_root[lit.var()]; - if (r != lit) { - changed = true; - lit = lit.sign() ? ~r : r; - } - if (lit.var() == var) { - return false; - } - } - if (changed && (n.is_and() || n.is_xor())) { - std::sort(m_literals.data() + n.offset(), m_literals.data() + n.offset() + n.size()); - } - return true; - } - - void aig_cuts::flush_roots(to_root const& to_root, cut_set& cs) { - for (unsigned j = 0; j < cs.size(); ++j) { - for (unsigned v : cs[j]) { - if (to_root[v] != literal(v, false)) { - evict(cs, j--); - break; - } - } - } - } - - lbool aig_cuts::get_value(bool_var v) const { - return (m_aig[v].size() == 1 && m_aig[v][0].is_const()) ? - (m_aig[v][0].sign() ? l_false : l_true) : - l_undef; - } - - void aig_cuts::init_cut_set(unsigned id) { - SASSERT(m_aig[id].size() == 1); - SASSERT(m_aig[id][0].is_valid()); - auto& cut_set = m_cuts[id]; - reset(cut_set); - cut_set.init(m_region, m_config.m_max_cutset_size + 1, id); - push_back(cut_set, cut(id)); - } - - bool aig_cuts::eq(node const& a, node const& b) { - if (a.is_valid() != b.is_valid()) return false; - if (!a.is_valid()) return true; - if (a.op() != b.op() || a.sign() != b.sign() || a.size() != b.size()) - return false; - for (unsigned i = a.size(); i-- > 0; ) { - if (m_literals[a.offset() + i] != m_literals[b.offset() + i]) - return false; - } - return true; - } - - bool aig_cuts::similar(node const& a, node const& b) { - bool sim = true; - sim = a.is_lut() && !b.is_lut() && a.size() == b.size(); - for (unsigned i = a.size(); sim && i-- > 0; ) { - sim = m_literals[a.offset() + i].var() == m_literals[b.offset() + i].var(); - } - return sim; - } - - bool aig_cuts::insert_aux(unsigned v, node const& n) { - if (!m_config.m_full) return false; - unsigned num_gt = 0, num_eq = 0; - for (node const& n2 : m_aig[v]) { - if (eq(n, n2) || similar(n, n2)) return false; - else if (n.size() < n2.size()) num_gt++; - else if (n.size() == n2.size()) num_eq++; - } - if (m_aig[v].size() < m_config.m_max_aux) { - on_node_add(v, n); - m_aig[v].push_back(n); - touch(v); - return true; - } - if (num_gt > 0) { - unsigned idx = rand() % num_gt; - for (node const& n2 : m_aig[v]) { - if (n.size() < n2.size()) { - if (idx == 0) { - on_node_del(v, m_aig[v][idx]); - on_node_add(v, n); - m_aig[v][idx] = n; - touch(v); - return true; - } - --idx; - } - } - } - if (num_eq > 0) { - unsigned idx = rand() % num_eq; - for (node const& n2 : m_aig[v]) { - if (n.size() == n2.size()) { - if (idx == 0) { - on_node_del(v, m_aig[v][idx]); - on_node_add(v, n); - m_aig[v][idx] = n; - touch(v); - return true; - } - --idx; - } - } - } - return false; - } - - unsigned_vector aig_cuts::filter_valid_nodes() const { - unsigned id = 0; - unsigned_vector result; - for (auto& v : m_aig) { - if (!v.empty()) result.push_back(id); - ++id; - } - return result; - } - - cut_val aig_cuts::eval(node const& n, cut_eval const& env) const { - uint64_t result = 0; - switch (n.op()) { - case var_op: - UNREACHABLE(); - break; - case and_op: - result = ~0ull; - for (unsigned i = 0; i < n.size(); ++i) { - literal u = m_literals[n.offset() + i]; - uint64_t uv = u.sign() ? env[u.var()].m_f : env[u.var()].m_t; - result &= uv; - } - break; - case xor_op: - result = 0ull; - for (unsigned i = 0; i < n.size(); ++i) { - literal u = m_literals[n.offset() + i]; - uint64_t uv = u.sign() ? env[u.var()].m_f : env[u.var()].m_t; - result ^= uv; - } - break; - case ite_op: { - literal u = m_literals[n.offset() + 0]; - literal v = m_literals[n.offset() + 1]; - literal w = m_literals[n.offset() + 2]; - uint64_t uv = u.sign() ? env[u.var()].m_f : env[u.var()].m_t; - uint64_t vv = v.sign() ? env[v.var()].m_f : env[v.var()].m_t; - uint64_t wv = w.sign() ? env[w.var()].m_f : env[w.var()].m_t; - result = (uv & vv) | ((~uv) & wv); - break; - } - default: - UNREACHABLE(); - } - if (n.sign()) result = ~result; - return cut_val(result, ~result); - } - - cut_eval aig_cuts::simulate(unsigned num_rounds) { - cut_eval result; - for (unsigned i = 0; i < m_cuts.size(); ++i) { - uint64_t r = - (uint64_t)m_rand() + ((uint64_t)m_rand() << 16ull) + - ((uint64_t)m_rand() << 32ull) + ((uint64_t)m_rand() << 48ull); - result.push_back(cut_val(r, ~r)); - } - for (unsigned i = 0; i < num_rounds; ++i) { - for (unsigned j = 0; j < m_cuts.size(); ++j) { - cut_set const& cs = m_cuts[j]; - if (cs.size() <= 1) { - if (!m_aig[j].empty() && !m_aig[j][0].is_var()) { - result[j] = eval(m_aig[j][0], result); - } - } - else if (cs.size() > 1) { - cut const& c = cs[1 + (m_rand() % (cs.size() - 1))]; - result[j] = c.eval(result); - } - } - } - return result; - } - - - void aig_cuts::on_node_add(unsigned v, node const& n) { - if (m_on_clause_add) { - node2def(m_on_clause_add, n, literal(v, false)); - } - } - - void aig_cuts::on_node_del(unsigned v, node const& n) { - if (m_on_clause_del) { - node2def(m_on_clause_del, n, literal(v, false)); - } - } - - void aig_cuts::set_on_clause_add(on_clause_t& on_clause_add) { - m_on_clause_add = on_clause_add; - std::function _on_cut_add = - [this](unsigned v, cut const& c) { cut2def(m_on_clause_add, c, literal(v, false)); }; - m_on_cut_add = _on_cut_add; - } - - void aig_cuts::set_on_clause_del(on_clause_t& on_clause_del) { - m_on_clause_del = on_clause_del; - std::function _on_cut_del = - [this](unsigned v, cut const& c) { cut2def(m_on_clause_del, c, literal(v, false)); }; - m_on_cut_del = _on_cut_del; - } - - /** - * Encode the cut (variables and truth-table) in a set of clauses. - * r is the result. - */ - - void aig_cuts::cut2def(on_clause_t& on_clause, cut const& c, literal r) { - IF_VERBOSE(10, verbose_stream() << "cut2def: " << r << " == " << c << "\n"); - VERIFY(r != null_literal); - unsigned sz = c.size(); - unsigned num_assigns = 1 << sz; - for (unsigned i = 0; i < num_assigns; ++i) { - m_clause.reset(); - for (unsigned j = 0; j < sz; ++j) { - literal lit(c[j], 0 != (i & (1ull << j))); - m_clause.push_back(lit); - } - literal rr = r; - if (0 == (c.table() & (1ull << i))) rr.neg(); - m_clause.push_back(rr); - on_clause(m_clause); - } - } - - void aig_cuts::node2def(on_clause_t& on_clause, node const& n, literal r) { - IF_VERBOSE(10, display(verbose_stream() << "node2def " << r << " == ", n) << "\n"); - SASSERT(on_clause); - literal c, t, e; - if (n.sign()) r.neg(); - m_clause.reset(); - unsigned num_comb = 0; - switch (n.op()) { - case var_op: - return; - case and_op: - for (unsigned i = 0; i < n.size(); ++i) { - m_clause.push_back(~r); - m_clause.push_back(m_literals[n.offset() + i]); - on_clause(m_clause); - m_clause.reset(); - } - for (unsigned i = 0; i < n.size(); ++i) { - m_clause.push_back(~m_literals[n.offset()+i]); - } - m_clause.push_back(r); - on_clause(m_clause); - return; - case ite_op: - // r & c => t, r & ~c => e - // ~r & c => ~t, ~r & ~c => ~e - SASSERT(n.size() == 3); - c = m_literals[n.offset()+0]; - t = m_literals[n.offset()+1]; - e = m_literals[n.offset()+2]; - m_clause.push_back(~r, ~c, t); - on_clause(m_clause); - m_clause.reset(); - m_clause.push_back(~r, c, e); - on_clause(m_clause); - m_clause.reset(); - m_clause.push_back(r, ~c, ~t); - on_clause(m_clause); - m_clause.reset(); - m_clause.push_back(r, c, ~e); - on_clause(m_clause); - return; - case xor_op: - // r = a ^ b ^ c - // <=> - // ~r ^ a ^ b ^ c = 1 - if (n.size() > 10) { - throw default_exception("cannot handle large xors"); - } - num_comb = (1 << n.size()); - for (unsigned i = 0; i < num_comb; ++i) { - bool parity = n.size() % 2 == 1; - m_clause.reset(); - for (unsigned j = 0; j < n.size(); ++j) { - literal lit = m_literals[n.offset() + j]; - if (0 == (i & (1 << j))) { - lit.neg(); - } - else { - parity ^= true; - } - m_clause.push_back(lit); - } - m_clause.push_back(parity ? r : ~r); - TRACE(cut_simplifier, tout << "validate: " << m_clause << "\n";); - on_clause(m_clause); - } - return; - case lut_op: - // r = LUT(v0, v1, v2) - num_comb = (1 << n.size()); - for (unsigned i = 0; i < num_comb; ++i) { - m_clause.reset(); - for (unsigned j = 0; j < n.size(); ++j) { - literal lit = m_literals[n.offset() + j]; - if (0 != (i & (1 << j))) lit.neg(); - m_clause.push_back(lit); - } - m_clause.push_back(0 == (n.lut() & (1ull << i)) ? ~r : r); - TRACE(cut_simplifier, tout << n.lut() << " " << m_clause << "\n";); - on_clause(m_clause); - } - return; - default: - UNREACHABLE(); - break; - } - } - - /** - * compile the truth table from c into clauses that define ~v. - * compile definitions for nodes until all inputs have been covered. - * Assume only the first definition for a node is used for all cuts. - */ - void aig_cuts::cut2clauses(on_clause_t& on_clause, unsigned v, cut const& c) { - bool_vector visited(m_aig.size(), false); - for (unsigned u : c) visited[u] = true; - unsigned_vector todo; - todo.push_back(v); - - while (!todo.empty()) { - unsigned u = todo.back(); - todo.pop_back(); - if (visited[u]) { - continue; - } - visited[u] = true; - node const& n = m_aig[u][0]; - node2def(on_clause, n, literal(u, false)); - for (unsigned i = 0; i < n.size(); ++i) { - todo.push_back(m_literals[n.offset()+i].var()); - } - } - cut2def(on_clause, c, literal(v, true)); - } - - /** - * simplify a set of cuts by removing don't cares. - */ - void aig_cuts::simplify() { - uint64_t masks[7]; - for (unsigned i = 0; i <= 6; ++i) { - masks[i] = cut::effect_mask(i); - } - unsigned dont_cares = 0; - for (cut_set & cs : m_cuts) { - for (cut const& c : cs) { - uint64_t t = c.table(); - for (unsigned i = 0; i < std::min(6u, c.size()); ++i) { - uint64_t diff = masks[i] & (t ^ (t >> (1ull << i))); - if (diff == 0ull) { - cut d(c); - d.remove_elem(i); - cs.insert(m_on_cut_add, m_on_cut_del, d); - cs.evict(m_on_cut_del, c); - ++dont_cares; - break; - } - } - } - } - IF_VERBOSE(2, verbose_stream() << "#don't cares " << dont_cares << "\n"); - } - - struct aig_cuts::validator { - aig_cuts& t; - params_ref p; - reslimit lim; - solver s; - unsigned_vector vars; - bool_vector is_var; - - validator(aig_cuts& t):t(t),s(p, lim) { - p.set_bool("cut_simplifier", false); - s.updt_params(p); - } - - void on_clause(literal_vector const& clause) { - IF_VERBOSE(20, verbose_stream() << clause << "\n"); - for (literal lit : clause) { - while (lit.var() >= s.num_vars()) s.mk_var(); - is_var.reserve(lit.var() + 1, false); - if (!is_var[lit.var()]) { vars.push_back(lit.var()); is_var[lit.var()] = true; } - } - s.mk_clause(clause); - } - - void check() { - lbool r = s.check(); - IF_VERBOSE(10, verbose_stream() << "check: " << r << "\n"); - if (r == l_true) { - IF_VERBOSE(0, - std::sort(vars.begin(), vars.end()); - s.display(verbose_stream()); - for (auto v : vars) verbose_stream() << v << " := " << s.get_model()[v] << "\n"; - ); - UNREACHABLE(); - } - } - }; - - void aig_cuts::validate_aig2(cut const& a, cut const& b, unsigned v, node const& n, cut const& c) { - validator val(*this); - on_clause_t on_clause = [&](literal_vector const& clause) { val.on_clause(clause); }; - cut2def(on_clause, a, literal(child(n, 0).var(), false)); - cut2def(on_clause, b, literal(child(n, 1).var(), false)); - cut2def(on_clause, c, literal(v, false)); - node2def(on_clause, n, literal(v, true)); - val.check(); - } - - void aig_cuts::validate_aigN(unsigned v, node const& n, cut const& c) { - IF_VERBOSE(10, verbose_stream() << "validate_aigN " << v << " == " << c << "\n"); - validator val(*this); - on_clause_t on_clause = [&](literal_vector const& clause) { val.on_clause(clause); }; - for (unsigned i = 0; i < n.size(); ++i) { - unsigned w = m_literals[n.offset() + i].var(); - for (cut const& d : m_cuts[w]) { - cut2def(on_clause, d, literal(w, false)); - } - } - cut2def(on_clause, c, literal(v, false)); - node2def(on_clause, n, literal(v, true)); - val.check(); - } - - std::ostream& aig_cuts::display(std::ostream& out) const { - auto ids = filter_valid_nodes(); - for (auto id : ids) { - out << id << " == "; - bool first = true; - for (auto const& n : m_aig[id]) { - if (first) first = false; else out << " "; - display(out, n) << "\n"; - } - m_cuts[id].display(out); - } - return out; - } - - std::ostream& aig_cuts::display(std::ostream& out, node const& n) const { - out << (n.sign() ? "! " : " "); - switch (n.op()) { - case var_op: out << "var "; break; - case and_op: out << "& "; break; - case xor_op: out << "^ "; break; - case ite_op: out << "? "; break; - default: break; - } - for (unsigned i = 0; i < n.size(); ++i) { - out << m_literals[n.offset() + i] << " "; - } - return out; - } - -} - diff --git a/src/sat/sat_aig_cuts.h b/src/sat/sat_aig_cuts.h deleted file mode 100644 index 9e60ce9ec..000000000 --- a/src/sat/sat_aig_cuts.h +++ /dev/null @@ -1,238 +0,0 @@ -/*++ - Copyright (c) 2020 Microsoft Corporation - - Module Name: - - sat_aig_cuts.h - - Abstract: - - Extract AIG definitions from clauses. - Perform cut-set enumeration to identify equivalences. - - AIG extraction is incremental. - It can be called repeatedly. - Initially, a main aig node is inserted - (from initial clauses or the input - clausification in goal2sat). - Then, auxiliary AIG nodes can be inserted - by walking the current set of main and learned - clauses. AIG nodes with fewer arguments are preferred. - - - - Author: - - Nikolaj Bjorner 2020-01-02 - - --*/ - -#pragma once - -#include "sat/sat_cutset.h" -#include "sat/sat_types.h" - -namespace sat { - - enum bool_op { - var_op, - and_op, - ite_op, - xor_op, - lut_op, - no_op - }; - - inline std::ostream& operator<<(std::ostream& out, bool_op op) { - switch (op) { - case var_op: return out << "v"; - case and_op: return out << "&"; - case ite_op: return out << "?"; - case xor_op: return out << "^"; - case lut_op: return out << "#"; - default: return out << ""; - } - } - - class aig_cuts { - public: - typedef std::function on_clause_t; - - struct config { - unsigned m_max_cutset_size; - unsigned m_max_aux; - unsigned m_max_insertions; - bool m_full; - config(): m_max_cutset_size(20), m_max_aux(5), m_max_insertions(20), m_full(true) {} - }; - private: - - // encodes one of var, and, !and, xor, !xor, ite, !ite. - class node { - bool m_sign{ false }; - bool_op m_op{ no_op }; - uint64_t m_lut{ 0 }; - unsigned m_size{ 0 }; - unsigned m_offset{ 0 }; - public: - node(): - m_sign(false), m_op(no_op), m_size(UINT_MAX), m_offset(UINT_MAX) {} - explicit node(unsigned v) : - m_sign(false), m_op(var_op), m_size(0), m_offset(v) {} - explicit node(bool sign, bool_op op, unsigned nc, unsigned o) : - m_sign(sign), m_op(op), m_size(nc), m_offset(o) {} - explicit node(uint64_t lut, unsigned nc, unsigned o): - m_sign(false), m_op(lut_op), m_lut(lut), m_size(nc), m_offset(o) {} - bool is_valid() const { return m_offset != UINT_MAX; } - bool_op op() const { return m_op; } - bool is_var() const { return m_op == var_op; } - bool is_and() const { return m_op == and_op; } - bool is_xor() const { return m_op == xor_op; } - bool is_ite() const { return m_op == ite_op; } - bool is_lut() const { return m_op == lut_op; } - bool is_const() const { return is_and() && size() == 0; } - unsigned var() const { SASSERT(is_var()); return m_offset; } - bool sign() const { return m_sign; } - unsigned size() const { return m_size; } - unsigned offset() const { return m_offset; } - uint64_t lut() const { return m_lut; } - }; - random_gen m_rand; - config m_config; - vector> m_aig; - literal_vector m_literals; - region m_region; - cut_set m_cut_set1, m_cut_set2, m_empty_cuts; - vector m_cuts; - unsigned_vector m_max_cutset_size; - unsigned_vector m_last_touched; - unsigned m_num_cut_calls; - unsigned m_num_cuts; - svector> m_roots; - unsigned m_insertions; - on_clause_t m_on_clause_add, m_on_clause_del; - cut_set::on_update_t m_on_cut_add, m_on_cut_del; - literal_vector m_clause; - cut const* m_tables[6]; - uint64_t m_luts[6]; - literal m_lits[6]; - - class to_root { - literal_vector m_to_root; - void reserve(bool_var v) { - while (v >= m_to_root.size()) { - m_to_root.push_back(literal(m_to_root.size(), false)); - } - } - public: - literal operator[](bool_var v) const { - return (v < m_to_root.size()) ? m_to_root[v] : literal(v, false); - } - literal& operator[](bool_var v) { - reserve(v); - return m_to_root[v]; - } - }; - - class lut { - aig_cuts& a; - node const* n; - cut const* c; - public: - lut(aig_cuts& a, node const& n) : a(a), n(&n), c(nullptr) {} - lut(aig_cuts& a, cut const& c) : a(a), n(nullptr), c(&c) {} - unsigned size() const { return n ? n->size() : c->size(); } - literal child(unsigned idx) const { return n ? a.child(*n, idx) : a.child(*c, idx); } - uint64_t table() const { return n ? n->lut() : c->table(); } - std::ostream& display(std::ostream& out) const { return n ? a.display(out, *n) : out << *c; } - }; - - bool is_touched(bool_var v, node const& n); - bool is_touched(literal lit) const { return is_touched(lit.var()); } - bool is_touched(bool_var v) const { return v < m_last_touched.size() && m_last_touched[v] + m_aig.size() >= m_num_cut_calls * m_aig.size(); } - void reserve(unsigned v); - bool insert_aux(unsigned v, node const& n); - void init_cut_set(unsigned id); - - bool eq(node const& a, node const& b); - bool similar(node const& a, node const& b); - - unsigned_vector filter_valid_nodes() const; - void augment(unsigned_vector const& ids); - void augment(unsigned id, node const& n); - void augment_ite(unsigned v, node const& n, cut_set& cs); - void augment_aig0(unsigned v, node const& n, cut_set& cs); - void augment_aig1(unsigned v, node const& n, cut_set& cs); - void augment_aig2(unsigned v, node const& n, cut_set& cs); - void augment_aigN(unsigned v, node const& n, cut_set& cs); - - - void augment_lut(unsigned v, lut const& n, cut_set& cs); - void augment_lut_rec(unsigned v, lut const& n, cut& a, unsigned idx, cut_set& cs); - - cut_set const& lit2cuts(literal lit) const { return lit.var() < m_cuts.size() ? m_cuts[lit.var()] : m_empty_cuts; } - - bool insert_cut(unsigned v, cut const& c, cut_set& cs); - - void flush_roots(); - bool flush_roots(bool_var var, to_root const& to_root, node& n); - void flush_roots(to_root const& to_root, cut_set& cs); - - cut_val eval(node const& n, cut_eval const& env) const; - lbool get_value(bool_var v) const; - - std::ostream& display(std::ostream& out, node const& n) const; - - literal child(node const& n, unsigned idx) const { SASSERT(!n.is_var()); SASSERT(idx < n.size()); return m_literals[n.offset() + idx]; } - literal child(cut const& n, unsigned idx) const { SASSERT(idx < n.size()); return literal(n[idx], false); } - - void on_node_add(unsigned v, node const& n); - void on_node_del(unsigned v, node const& n); - - void evict(cut_set& cs, unsigned idx) { cs.evict(m_on_cut_del, idx); } - void reset(cut_set& cs) { cs.reset(m_on_cut_del); } - void push_back(cut_set& cs, cut const& c) { cs.push_back(m_on_cut_add, c); } - void shrink(cut_set& cs, unsigned j) { cs.shrink(m_on_cut_del, j); } - - void cut2clauses(on_clause_t& on_clause, unsigned v, cut const& c); - void node2def(on_clause_t& on_clause, node const& n, literal r); - - struct validator; - void validate_cut(unsigned v, cut const& c); - void validate_aig2(cut const& a, cut const& b, unsigned v, node const& n, cut const& c); - void validate_aigN(unsigned v, node const& n, cut const& c); - - void add_node(bool_var v, node const& n); - public: - - aig_cuts(); - void add_var(unsigned v); - void add_node(literal head, bool_op op, unsigned sz, literal const* args); - void add_node(bool_var head, uint64_t lut, unsigned sz, bool_var const* args); - void add_cut(bool_var v, uint64_t lut, bool_var_vector const& args); - void set_root(bool_var v, literal r); - - void set_on_clause_add(on_clause_t& on_clause_add); - void set_on_clause_del(on_clause_t& on_clause_del); - - void inc_max_cutset_size(unsigned v) { m_max_cutset_size.reserve(v + 1, 0); m_max_cutset_size[v] += 10; touch(v); } - unsigned max_cutset_size(unsigned v) const { return v == UINT_MAX ? m_config.m_max_cutset_size : m_max_cutset_size[v]; } - - vector const & operator()(); - unsigned num_cuts() const { return m_num_cuts; } - - void cut2def(on_clause_t& on_clause, cut const& c, literal r); - - void touch(bool_var v) { m_last_touched.reserve(v + 1, false); m_last_touched[v] = v + m_num_cut_calls * m_aig.size(); } - - cut_eval simulate(unsigned num_rounds); - - void simplify(); - - std::ostream& display(std::ostream& out) const; - - }; - -} - - diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 338ea8692..3dfb67f2a 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -109,15 +109,6 @@ namespace sat { m_anf_simplify = p.anf(); m_anf_delay = p.anf_delay(); m_anf_exlin = p.anf_exlin(); - m_cut_simplify = p.cut(); - m_cut_delay = p.cut_delay(); - m_cut_aig = p.cut_aig(); - m_cut_lut = p.cut_lut(); - m_cut_xor = p.cut_xor(); - m_cut_npn3 = p.cut_npn3(); - m_cut_dont_cares = p.cut_dont_cares(); - m_cut_redundancies = p.cut_redundancies(); - m_cut_force = p.cut_force(); m_lookahead_simplify = p.lookahead_simplify(); m_lookahead_double = p.lookahead_double(); m_lookahead_simplify_bca = p.lookahead_simplify_bca(); diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index d032e64a1..83241fe88 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -119,15 +119,6 @@ namespace sat { bool m_local_search; local_search_mode m_local_search_mode; bool m_local_search_dbg_flips; - bool m_cut_simplify; - unsigned m_cut_delay; - bool m_cut_aig; - bool m_cut_lut; - bool m_cut_xor; - bool m_cut_npn3; - bool m_cut_dont_cares; - bool m_cut_redundancies; - bool m_cut_force; bool m_anf_simplify; unsigned m_anf_delay; bool m_anf_exlin; diff --git a/src/sat/sat_cut_simplifier.cpp b/src/sat/sat_cut_simplifier.cpp deleted file mode 100644 index 0125a7af1..000000000 --- a/src/sat/sat_cut_simplifier.cpp +++ /dev/null @@ -1,755 +0,0 @@ -/*++ - Copyright (c) 2020 Microsoft Corporation - - Module Name: - - sat_cut_simplifier.cpp - - Abstract: - - extract AIG definitions from clauses - Perform cut-set enumeration to identify equivalences. - - Author: - - Nikolaj Bjorner 2020-01-02 - - --*/ - -#include "sat/sat_cut_simplifier.h" -#include "sat/sat_xor_finder.h" -#include "sat/sat_lut_finder.h" -#include "sat/sat_npn3_finder.h" -#include "sat/sat_elim_eqs.h" - -namespace sat { - - struct cut_simplifier::report { - cut_simplifier& s; - stopwatch m_watch; - unsigned m_num_eqs, m_num_units, m_num_cuts, m_num_learned_implies; - - report(cut_simplifier& s): s(s) { - m_watch.start(); - m_num_eqs = s.m_stats.m_num_eqs; - m_num_units = s.m_stats.m_num_units; - m_num_cuts = s.m_stats.m_num_cuts; - m_num_learned_implies = s.m_stats.m_num_learned_implies; - } - ~report() { - unsigned ne = s.m_stats.m_num_eqs - m_num_eqs; - unsigned nu = s.m_stats.m_num_units - m_num_units; - unsigned nc = s.m_stats.m_num_cuts - m_num_cuts; - unsigned ni = s.m_stats.m_num_learned_implies - m_num_learned_implies; - IF_VERBOSE(2, - verbose_stream() << "(sat.cut-simplifier"; - if (nu > 0) verbose_stream() << " :num-units " << nu; - if (ne > 0) verbose_stream() << " :num-eqs " << ne; - if (ni > 0) verbose_stream() << " :num-bin " << ni; - if (nc > 0) verbose_stream() << " :num-cuts " << nc; - verbose_stream() << " :mb " << mem_stat() << m_watch << ")\n"); - } - }; - - struct cut_simplifier::validator { - solver& _s; - params_ref p; - literal_vector m_assumptions; - - validator(solver& _s, params_ref const& p): _s(_s), p(p) { - } - - void validate(unsigned n, literal const* clause) { - validate(literal_vector(n, clause)); - } - - void validate(literal_vector const& clause) { - if (clause.size() == 2 && clause[0] == ~clause[1]) return; - solver s(p, _s.rlimit()); - s.copy(_s, false); - IF_VERBOSE(10, verbose_stream() << "validate: " << clause << "\n"); - m_assumptions.reset(); - for (literal lit : clause) m_assumptions.push_back(~lit); - lbool r = s.check(clause.size(), m_assumptions.data()); - if (r != l_false) { - IF_VERBOSE(0, - verbose_stream() << "not validated: " << clause << "\n"; - s.display(verbose_stream());); - UNREACHABLE(); - } - } - }; - - void cut_simplifier::ensure_validator() { - if (!m_validator) { - params_ref p; - p.set_bool("aig", false); - p.set_bool("drat.check_unsat", false); - p.set_sym("drat.file", symbol()); - p.set_uint("max_conflicts", 10000); - m_validator = alloc(validator, s, p); - } - } - - cut_simplifier::cut_simplifier(solver& _s): - s(_s), - m_trail_size(0), - m_validator(nullptr) { - if (s.get_config().m_drat) { - std::function _on_add = - [this](literal_vector const& clause) { s.m_drat.add(clause); }; - std::function _on_del = - [this](literal_vector const& clause) { s.m_drat.del(clause); }; - m_aig_cuts.set_on_clause_add(_on_add); - m_aig_cuts.set_on_clause_del(_on_del); - } - else if (m_config.m_validate_cuts) { - ensure_validator(); - std::function _on_add = - [this](literal_vector const& clause) { - m_validator->validate(clause); - }; - m_aig_cuts.set_on_clause_add(_on_add); - } - } - - cut_simplifier::~cut_simplifier() { - dealloc(m_validator); - } - - void cut_simplifier::add_and(literal head, unsigned sz, literal const* lits) { - m_aig_cuts.add_node(head, and_op, sz, lits); - for (unsigned i = 0; i < sz; ++i) VERIFY(head.var() != lits[i].var()); - m_stats.m_num_ands++; - } - - // head == l1 or l2 or l3 - // <=> - // ~head == ~l1 and ~l2 and ~l3 - void cut_simplifier::add_or(literal head, unsigned sz, literal const* lits) { - m_lits.reset(); - m_lits.append(sz, lits); - for (unsigned i = 0; i < sz; ++i) m_lits[i].neg(); - m_aig_cuts.add_node(~head, and_op, sz, m_lits.data()); - m_stats.m_num_ands++; - } - - void cut_simplifier::add_xor(literal head, unsigned sz, literal const* lits) { - m_aig_cuts.add_node(head, xor_op, sz, lits); - m_stats.m_num_xors++; - } - - void cut_simplifier::add_ite(literal head, literal c, literal t, literal e) { - literal lits[3] = { c, t, e }; - m_aig_cuts.add_node(head, ite_op, 3, lits); - m_stats.m_num_ites++; - } - - void cut_simplifier::add_iff(literal head, literal l1, literal l2) { - literal lits[2] = { l1, ~l2 }; - m_aig_cuts.add_node(head, xor_op, 2, lits); - m_stats.m_num_xors++; - } - - void cut_simplifier::set_root(bool_var v, literal r) { - m_aig_cuts.set_root(v, r); - } - - void cut_simplifier::operator()() { - - bool force = s.m_config.m_cut_force; - report _report(*this); - TRACE(cut_simplifier, s.display(tout);); - unsigned n = 0, i = 0; - ++m_stats.m_num_calls; - do { - n = m_stats.m_num_eqs + m_stats.m_num_units; - clauses2aig(); - aig2clauses(); - ++i; - } - while (((force && i < 5) || i*i < m_stats.m_num_calls) && n < m_stats.m_num_eqs + m_stats.m_num_units); - } - - /** - \brief extract AIG definitions from clauses - Ensure that they are sorted and variables have unique definitions. - */ - void cut_simplifier::clauses2aig() { - - for (; m_config.m_enable_units && m_trail_size < s.init_trail_size(); ++m_trail_size) { - literal lit = s.trail_literal(m_trail_size); - m_aig_cuts.add_node(lit, and_op, 0, nullptr); - } - - clause_vector clauses(s.clauses()); - if (m_config.m_learned2aig) clauses.append(s.learned()); - - std::function on_and = - [&,this](literal head, literal_vector const& ands) { - m_aig_cuts.add_node(head, and_op, ands.size(), ands.data()); - m_stats.m_xands++; - }; - std::function on_ite = - [&,this](literal head, literal c, literal t, literal e) { - literal args[3] = { c, t, e }; - m_aig_cuts.add_node(head, ite_op, 3, args); - m_stats.m_xites++; - }; - if (s.m_config.m_cut_aig) { - aig_finder af(s); - af.set(on_and); - af.set(on_ite); - af(clauses); - } - - - std::function on_xor = - [&,this](literal_vector const& xors) { - SASSERT(xors.size() > 1); - unsigned max_level = xors.back().var(); - unsigned index = xors.size() - 1; - for (unsigned i = index; i-- > 0; ) { - literal l = xors[i]; - if (l.var() > max_level) { - max_level = l.var(); - index = i; - } - } - // head + t1 + t2 + .. = 1 - // <=> - // ~head = t1 + t2 + .. - literal head = ~xors[index]; - TRACE(cut_simplifier, tout << xors << "\n";); - unsigned sz = xors.size() - 1; - m_lits.reset(); - for (unsigned i = xors.size(); i-- > 0; ) { - if (i != index) - m_lits.push_back(xors[i]); - } - m_aig_cuts.add_node(head, xor_op, sz, m_lits.data()); - m_lits.reset(); - m_stats.m_xxors++; - }; - if (s.m_config.m_cut_xor) { - xor_finder xf(s); - xf.set(on_xor); - xf(clauses); - } - - std::function on_lut = - [&,this](uint64_t lut, bool_var_vector const& vars, bool_var v) { - m_stats.m_xluts++; - // m_aig_cuts.add_cut(v, lut, vars); - m_aig_cuts.add_node(v, lut, vars.size(), vars.data()); - }; - - if (s.m_config.m_cut_npn3) { - npn3_finder nf(s); - // TBD: stubs for npn3 - // question: perhaps just use a LUT interface? - // nf.set_on_mux - // nf.set_on_maj - // nf.set_on_orand - // nf.set_on_and - // nf.set_on_xor - // nf.set_on_andxor - // nf.set_on_xorand - // nf.set_on_gamble - // nf.set_on_onehot - // nf.set_on_dot - // nf(clauses); - } - - if (s.m_config.m_cut_lut) { - lut_finder lf(s); - lf.set(on_lut); - lf(clauses); - } - -#if 0 - statistics st; - collect_statistics(st); - st.display(std::cout); - exit(0); -#endif - } - - void cut_simplifier::aig2clauses() { - vector const& cuts = m_aig_cuts(); - m_stats.m_num_cuts = m_aig_cuts.num_cuts(); - add_dont_cares(cuts); - cuts2equiv(cuts); - cuts2implies(cuts); - simulate_eqs(); - } - - void cut_simplifier::cuts2equiv(vector const& cuts) { - map cut2id; - bool new_eq = false; - union_find_default_ctx ctx; - union_find<> uf(ctx); - - for (unsigned i = 2*s.num_vars(); i--> 0; ) uf.mk_var(); - auto add_eq = [&](literal l1, literal l2) { - uf.merge(l1.index(), l2.index()); - uf.merge((~l1).index(), (~l2).index()); - new_eq = true; - }; - - for (unsigned i = cuts.size(); i-- > 0; ) { - literal u(i, false); - for (auto& c : cuts[i]) { - unsigned j = 0; - cut nc(c); - nc.negate(); - if (m_config.m_enable_units && c.is_true()) { - assign_unit(c, u); - } - else if (m_config.m_enable_units && c.is_false()) { - assign_unit(nc, ~u); - } - else if (cut2id.find(&c, j)) { - literal v(j, false); - assign_equiv(c, u, v); - add_eq(u, v); - } - else if (cut2id.find(&nc, j)) { - literal v(j, true); - assign_equiv(c, u, v); - add_eq(u, v); - } - else { - cut2id.insert(&c, i); - } - } - } - if (new_eq) { - uf2equiv(uf); - } - } - - void cut_simplifier::assign_unit(cut const& c, literal lit) { - if (s.value(lit) != l_undef) - return; - IF_VERBOSE(10, verbose_stream() << "new unit " << lit << "\n"); - validate_unit(lit); - certify_unit(lit, c); - s.assign_unit(lit); - ++m_stats.m_num_units; - } - - void cut_simplifier::assign_equiv(cut const& c, literal u, literal v) { - if (u.var() == v.var()) return; - IF_VERBOSE(10, verbose_stream() << u << " " << v << " " << c << "\n";); - TRACE(cut_simplifier, tout << u << " == " << v << "\n";); - certify_equivalence(u, v, c); - validate_eq(u, v); - } - - /** - * Convert a union-find over literals into input for eim_eqs. - */ - void cut_simplifier::uf2equiv(union_find<> const& uf) { - union_find_default_ctx ctx; - union_find<> uf2(ctx); - bool new_eq = false; - for (unsigned i = 2*s.num_vars(); i--> 0; ) uf2.mk_var(); - // extract equivalences over non-eliminated literals. - for (unsigned idx = 0; idx < uf.get_num_vars(); ++idx) { - if (!uf.is_root(idx) || 1 == uf.size(idx)) continue; - literal root = null_literal; - unsigned first = idx; - do { - literal lit = to_literal(idx); - if (!s.was_eliminated(lit)) { - if (root == null_literal) { - root = lit; - } - else { - uf2.merge(lit.index(), root.index()); - new_eq = true; - ++m_stats.m_num_eqs; - } - } - idx = uf.next(idx); - } - while (first != idx); - } - for (unsigned i = s.num_vars(); i-- > 0; ) { - literal lit(i, false); - if (uf2.find(lit.index()) == uf2.find((~lit).index())) { - s.set_conflict(); - return; - } - } - if (new_eq) { - elim_eqs elim(s); - elim(uf2); - } - } - - /** - * Extract binary clauses from cuts. - * A bit encoding of a LUT of u - * that sets a subset of bits for LUT' of v establishes - * that u implies v. - */ - void cut_simplifier::cuts2implies(vector const& cuts) { - if (!m_config.m_learn_implies) return; - vector>> var_tables; - map cut2tables; - unsigned j = 0; - big big(s.rand()); - big.init(s, true); - for (auto const& cs : cuts) { - if (s.was_eliminated(cs.var())) - continue; - for (auto const& c : cs) { - if (c.is_false() || c.is_true()) - continue; - if (!cut2tables.find(&c, j)) { - j = var_tables.size(); - var_tables.push_back(vector>()); - cut2tables.insert(&c, j); - } - var_tables[j].push_back(std::make_pair(cs.var(), &c)); - } - } - for (unsigned i = 0; i < var_tables.size(); ++i) { - auto const& vt = var_tables[i]; - for (unsigned j = 0; j < vt.size(); ++j) { - literal u(vt[j].first, false); - cut const& c1 = *vt[j].second; - cut nc1(c1); - nc1.negate(); - uint64_t t1 = c1.table(); - uint64_t n1 = nc1.table(); - for (unsigned k = j + 1; k < vt.size(); ++k) { - literal v(vt[k].first, false); - cut const& c2 = *vt[k].second; - uint64_t t2 = c2.table(); - uint64_t n2 = c2.ntable(); - if (t1 == t2 || t1 == n2) { - // already handled - } - else if ((t1 | t2) == t2) { - learn_implies(big, c1, u, v); - } - else if ((t1 | n2) == n2) { - learn_implies(big, c1, u, ~v); - } - else if ((n1 | t2) == t2) { - learn_implies(big, nc1, ~u, v); - } - else if ((n1 | n2) == n2) { - learn_implies(big, nc1, ~u, ~v); - } - } - } - } - } - - void cut_simplifier::learn_implies(big& big, cut const& c, literal u, literal v) { - if (u == ~v) { - assign_unit(c, v); - return; - } - if (u == v) { - return; - } - bin_rel q, p(~u, v); - if (m_bins.find(p, q) && q.op != op_code::none) - return; - if (big.connected(u, v)) - return; - for (auto const& w : s.get_wlist(u)) - if (w.is_binary_clause() && v == w.get_literal()) - return; - certify_implies(u, v, c); - s.mk_clause(~u, v, sat::status::redundant()); - // m_bins owns reference to ~u or v created by certify_implies - m_bins.insert(p); - ++m_stats.m_num_learned_implies; - } - - void cut_simplifier::simulate_eqs() { - if (!m_config.m_simulate_eqs) return; - auto var2val = m_aig_cuts.simulate(4); - - // Assign higher cutset budgets to equality candidates that come from simulation - // touch them to trigger recomputation of cutsets. - u64_map val2lit; - unsigned i = 0, num_eqs = 0; - for (cut_val val : var2val) { - if (!s.was_eliminated(i) && s.value(i) == l_undef) { - literal u(i, false), v; - if (val2lit.find(val.m_t, v)) { - - m_aig_cuts.inc_max_cutset_size(i); - m_aig_cuts.inc_max_cutset_size(v.var()); - num_eqs++; - } - else if (val2lit.find(val.m_f, v)) { - m_aig_cuts.inc_max_cutset_size(i); - m_aig_cuts.inc_max_cutset_size(v.var()); - num_eqs++; - } - else { - val2lit.insert(val.m_t, u); - val2lit.insert(val.m_f, ~u); - } - } - ++i; - } - IF_VERBOSE(2, verbose_stream() << "(sat.cut-simplifier num simulated eqs " << num_eqs << ")\n"); - } - - void cut_simplifier::track_binary(bin_rel const& p) { - if (!s.m_config.m_drat) - return; - literal u, v; - p.to_binary(u, v); - track_binary(u, v); - } - - void cut_simplifier::untrack_binary(bin_rel const& p) { - if (!s.m_config.m_drat) - return; - literal u, v; - p.to_binary(u, v); - untrack_binary(u, v); - } - - void cut_simplifier::track_binary(literal u, literal v) { - if (s.m_config.m_drat) { - s.m_drat.add(u, v, sat::status::redundant()); - } - } - - void cut_simplifier::untrack_binary(literal u, literal v) { - if (s.m_config.m_drat) { - s.m_drat.del(u, v); - } - } - - void cut_simplifier::certify_unit(literal u, cut const& c) { - certify_implies(~u, u, c); - } - - /** - * Equivalences modulo cuts are not necessarily DRAT derivable. - * To ensure that there is a DRAT derivation we create all resolvents - * of the LUT clauses until deriving binary u or ~v and ~u or v. - * each resolvent is DRAT derivable because there are two previous lemmas that - * contain complementary literals. - */ - void cut_simplifier::certify_equivalence(literal u, literal v, cut const& c) { - certify_implies(u, v, c); - certify_implies(v, u, c); - } - - /** - * certify that u implies v, where c is the cut for u. - * Then every position in c where u is true, it has to be - * the case that v is too. - * Where u is false, v can have any value. - * Thus, for every clause C or u', where u' is u or ~u, - * it follows that C or ~u or v - */ - void cut_simplifier::certify_implies(literal u, literal v, cut const& c) { - if (!s.m_config.m_drat) return; - - vector clauses; - std::function on_clause = - [&,this](literal_vector const& clause) { - SASSERT(clause.back().var() == u.var()); - clauses.push_back(clause); - clauses.back().back() = ~u; - if (~u != v) clauses.back().push_back(v); - s.m_drat.add(clauses.back()); - }; - m_aig_cuts.cut2def(on_clause, c, u); - - // create all resolvents over C. C is assumed to - // contain all combinations of some set of literals. - unsigned i = 0, sz = clauses.size(); - while (sz - i > 1) { - SASSERT((sz & (sz - 1)) == 0 && "sz is a power of 2"); - for (; i < sz; ++i) { - auto const& clause = clauses[i]; - if (clause[0].sign()) { - literal_vector cl(clause.size() - 1, clause.data() + 1); - clauses.push_back(cl); - s.m_drat.add(cl); - } - } - i = sz; - sz = clauses.size(); - } - - IF_VERBOSE(10, for (auto const& clause : clauses) verbose_stream() << clause << "\n";); - - // once we established equivalence, don't need auxiliary clauses for DRAT. - clauses.pop_back(); - for (auto const& clause : clauses) { - s.m_drat.del(clause); - } - } - - void cut_simplifier::add_dont_cares(vector const& cuts) { - if (s.m_config.m_cut_dont_cares) { - cuts2bins(cuts); - bins2dont_cares(); - dont_cares2cuts(cuts); - } - if (s.m_config.m_cut_redundancies) { - m_aig_cuts.simplify(); - } - } - - /** - * Collect binary relations between variables that occur in cut sets. - */ - void cut_simplifier::cuts2bins(vector const& cuts) { - svector dcs; - for (auto const& p : m_bins) - if (p.op != op_code::none) - dcs.push_back(p); - m_bins.reset(); - for (auto const& cs : cuts) - for (auto const& c : cs) - for (unsigned i = c.size(); i-- > 0; ) - for (unsigned j = i; j-- > 0; ) - m_bins.insert(bin_rel(c[j],c[i])); - - // don't lose previous don't cares - for (auto const& p : dcs) { - if (m_bins.contains(p)) { - m_bins.insert(p); - } - else { - untrack_binary(p); - } - } - } - - /** - * Compute masks for binary relations. - */ - void cut_simplifier::bins2dont_cares() { - big b(s.rand()); - b.init(s, true); - for (auto& p : m_bins) { - if (p.op != op_code::none) continue; - literal u(p.u, false), v(p.v, false); - // u -> v, then u & ~v is impossible - if (b.connected(u, v)) { - p.op = op_code::pn; - } - else if (b.connected(u, ~v)) { - p.op = op_code::pp; - } - else if (b.connected(~u, v)) { - p.op = op_code::nn; - } - else if (b.connected(~u, ~v)) { - p.op = op_code::np; - } - if (p.op != op_code::none) { - track_binary(p); - } - } - IF_VERBOSE(2, { - unsigned n = 0; for (auto const& p : m_bins) if (p.op != op_code::none) ++n; - verbose_stream() << n << " / " << m_bins.size() << " don't cares\n"; - }); - } - - /** - * Loop over cuts, if it is possible to add a new don't care combination - * to a cut, then ensure that the variable is "touched" so that it participates - * in the next propagation. - */ - void cut_simplifier::dont_cares2cuts(vector const& cuts) { - for (auto& cs : cuts) { - for (auto const& c : cs) { - if (add_dont_care(c)) { - m_aig_cuts.touch(cs.var()); - m_stats.m_num_dont_care_reductions++; - } - } - } - } - - /** - * compute masks for position i, j and op-code p.op - * For the don't care combination false, false, the first don't care - * position is 0. If it is true, false, the first don't care position - * is the position that encodes the first occurrence where i is true. - * It is 2^i. Cases for false, true and true, true are similar. - * Don't care positions are spaced apart by 2^{j+1}, - * where j is the second variable position. - */ - uint64_t cut_simplifier::op2dont_care(unsigned i, unsigned j, bin_rel const& p) { - SASSERT(i < j && j < 6); - if (p.op == op_code::none) return 0ull; - // first position of mask is offset into output bits contributed by i and j - bool i_is_0 = (p.op == op_code::np || p.op == op_code::nn); - bool j_is_0 = (p.op == op_code::pn || p.op == op_code::nn); - uint64_t first = (i_is_0 ? 0 : (1 << i)) + (j_is_0 ? 0 : (1 << j)); - uint64_t inc = 1ull << (j + 1); - uint64_t r = 1ull << first; - while (inc < 64ull) { r |= (r << inc); inc *= 2; } - return r; - } - - /** - * Apply obtained dont_cares to cut sets. - * The don't care bits are added to the LUT, so that the - * output is always 1 on don't care combinations. - */ - bool cut_simplifier::add_dont_care(cut const & c) { - uint64_t dc = 0; - for (unsigned i = 0; i < c.size(); ++i) { - for (unsigned j = i + 1; j < c.size(); ++j) { - bin_rel p(c[i], c[j]); - if (m_bins.find(p, p) && p.op != op_code::none) { - dc |= op2dont_care(i, j, p); - } - } - } - return (dc != c.dont_care()) && (c.add_dont_care(dc), true); - } - - void cut_simplifier::collect_statistics(statistics& st) const { - st.update("sat-cut.eqs", m_stats.m_num_eqs); - st.update("sat-cut.cuts", m_stats.m_num_cuts); - st.update("sat-cut.ands", m_stats.m_num_ands); - st.update("sat-cut.ites", m_stats.m_num_ites); - st.update("sat-cut.xors", m_stats.m_num_xors); - st.update("sat-cut.xands", m_stats.m_xands); - st.update("sat-cut.xites", m_stats.m_xites); - st.update("sat-cut.xxors", m_stats.m_xxors); - st.update("sat-cut.xluts", m_stats.m_xluts); - st.update("sat-cut.dc-reduce", m_stats.m_num_dont_care_reductions); - } - - void cut_simplifier::validate_unit(literal lit) { - if (!m_config.m_validate_lemmas) return; - ensure_validator(); - m_validator->validate(1, &lit); - } - - void cut_simplifier::validate_eq(literal a, literal b) { - if (!m_config.m_validate_lemmas) return; - ensure_validator(); - literal lits1[2] = { a, ~b }; - literal lits2[2] = { ~a, b }; - m_validator->validate(2, lits1); - m_validator->validate(2, lits2); - } - - -} - diff --git a/src/sat/sat_cut_simplifier.h b/src/sat/sat_cut_simplifier.h deleted file mode 100644 index aae5d4cfe..000000000 --- a/src/sat/sat_cut_simplifier.h +++ /dev/null @@ -1,174 +0,0 @@ -/*++ - Copyright (c) 2020 Microsoft Corporation - - Module Name: - - sat_cut_simplifier.h - - Abstract: - - extract AIG definitions from clauses - Perform cut-set enumeration to identify equivalences. - - Author: - - Nikolaj Bjorner 2020-01-02 - - --*/ - -#pragma once - -#include "util/union_find.h" -#include "sat/sat_aig_finder.h" -#include "sat/sat_aig_cuts.h" - -namespace sat { - - class cut_simplifier { - public: - struct stats { - unsigned m_num_eqs, m_num_units, m_num_cuts, m_num_xors, m_num_ands, m_num_ites; - unsigned m_xxors, m_xands, m_xites, m_xluts; // extrated gates - unsigned m_num_calls, m_num_dont_care_reductions, m_num_learned_implies; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - struct config { - bool m_enable_units; // enable learning units - bool m_enable_dont_cares; // enable applying don't cares to LUTs - bool m_learn_implies; // learn binary clauses - bool m_learned2aig; // add learned clauses to AIGs used by cut-set enumeration - bool m_validate_cuts; // enable direct validation of generated cuts - bool m_validate_lemmas; // enable direct validation of learned lemmas - bool m_simulate_eqs; // use symbolic simulation to control size of cutsets. - config(): - m_enable_units(true), - m_enable_dont_cares(true), - m_learn_implies(false), - m_learned2aig(true), - m_validate_cuts(false), - m_validate_lemmas(false), - m_simulate_eqs(false) {} - }; - private: - struct report; - struct validator; - - /** - * collect pairs of literal combinations that are impossible - * base on binary implication graph queries. Apply the masks - * on cut sets so to allow detecting equivalences modulo - * implications. - * - * The encoding is as follows: - * a or b -> op = nn because (~a & ~b) is a don't care - * ~a or b -> op = pn because (a & ~b) is a don't care - * a or ~b -> op = np because (~a & b) is a don't care - * ~a or ~b -> op = pp because (a & b) is a don't care - * - */ - - enum class op_code { pp, pn, np, nn, none }; - - struct bin_rel { - unsigned u, v; - op_code op; - bin_rel(unsigned _u, unsigned _v): u(_u), v(_v), op(op_code::none) { - if (u > v) std::swap(u, v); - } - // convert binary clause into a bin-rel - bin_rel(literal _u, literal _v): u(_u.var()), v(_v.var()), op(op_code::none) { - if (_u.sign() && _v.sign()) op = op_code::pp; - else if (_u.sign()) op = op_code::pn; - else if (_v.sign()) op = op_code::np; - else op = op_code::nn; - if (u > v) { - std::swap(u, v); - if (op == op_code::np) op = op_code::pn; - else if (op == op_code::pn) op = op_code::np; - } - } - bin_rel(): u(UINT_MAX), v(UINT_MAX), op(op_code::none) {} - - struct hash { - unsigned operator()(bin_rel const& p) const { - return p.u + 65599*p.v; // Weinberger's should be a bit cheaper mk_mix(p.u, p.v, 1); - } - }; - struct eq { - bool operator()(bin_rel const& a, bin_rel const& b) const { - return a.u == b.u && a.v == b.v; - } - }; - void to_binary(literal& lu, literal& lv) const { - switch (op) { - case op_code::pp: lu = literal(u, true); lv = literal(v, true); break; - case op_code::pn: lu = literal(u, true); lv = literal(v, false); break; - case op_code::np: lu = literal(u, false); lv = literal(v, true); break; - case op_code::nn: lu = literal(u, false); lv = literal(v, false); break; - default: UNREACHABLE(); break; - } - } - }; - - - solver& s; - stats m_stats; - config m_config; - aig_cuts m_aig_cuts; - unsigned m_trail_size; - literal_vector m_lits; - validator* m_validator; - hashtable m_bins; - - void clauses2aig(); - void aig2clauses(); - void simulate_eqs(); - void cuts2equiv(vector const& cuts); - void cuts2implies(vector const& cuts); - void uf2equiv(union_find<> const& uf); - void assign_unit(cut const& c, literal lit); - void assign_equiv(cut const& c, literal u, literal v); - void learn_implies(big& big, cut const& c, literal u, literal v); - void ensure_validator(); - void validate_unit(literal lit); - void validate_eq(literal a, literal b); - void certify_unit(literal u, cut const& c); - void certify_implies(literal u, literal v, cut const& c); - void certify_equivalence(literal u, literal v, cut const& c); - void track_binary(literal u, literal v); - void untrack_binary(literal u, literal v); - void track_binary(bin_rel const& p); - void untrack_binary(bin_rel const& p); - - - void add_dont_cares(vector const& cuts); - void cuts2bins(vector const& cuts); - void bins2dont_cares(); - void dont_cares2cuts(vector const& cuts); - bool add_dont_care(cut const & c); - uint64_t op2dont_care(unsigned i, unsigned j, bin_rel const& p); - - public: - cut_simplifier(solver& s); - ~cut_simplifier(); - void operator()(); - void collect_statistics(statistics& st) const; - - /** - * The clausifier may know that some literal is defined as a - * function of other literals. This API is exposed so that - * the clausifier can instrument the simplifier with an initial - * AIG. - * set_root is issued from the equivalence finder. - */ - void add_and(literal head, unsigned sz, literal const* args); - void add_or(literal head, unsigned sz, literal const* args); - void add_xor(literal head, unsigned sz, literal const* args); - void add_ite(literal head, literal c, literal t, literal e); - void add_iff(literal head, literal l1, literal l2); - void set_root(bool_var v, literal r); - }; -} - - diff --git a/src/sat/sat_cutset.cpp b/src/sat/sat_cutset.cpp deleted file mode 100644 index 2d31bcf14..000000000 --- a/src/sat/sat_cutset.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/*++ - Copyright (c) 2020 Microsoft Corporation - - Module Name: - - sat_cutset.cpp - - Author: - - Nikolaj Bjorner 2020-01-02 - - --*/ - - -#include "util/hashtable.h" -#include "sat/sat_cutset.h" -#include "sat/sat_cutset_compute_shift.h" -#include -#include - - -namespace sat { - - /** - \brief - if c is subsumed by a member in cut_set, then c is not inserted. - otherwise, remove members that c subsumes. - Note that the cut_set maintains invariant that elements don't subsume each-other. - - TBD: this is a bottleneck. - Ideas: - - add Bloom filter to is_subset_of operation. - - pre-allocate fixed array instead of vector for cut_set to avoid overhead for memory allocation. - */ - - bool cut_set::insert(on_update_t& on_add, on_update_t& on_del, cut const& c) { - unsigned i = 0, k = m_size; - for (; i < k; ++i) { - cut const& a = (*this)[i]; - if (a.subset_of(c)) { - return false; - } - if (c.subset_of(a)) { - std::swap(m_cuts[i--], m_cuts[--k]); - } - } - // for DRAT make sure to add new element before removing old cuts - // the new cut may need to be justified relative to the old cut - push_back(on_add, c); - std::swap(m_cuts[i++], m_cuts[m_size-1]); - shrink(on_del, i); - return true; - } - - bool cut_set::no_duplicates() const { - hashtable table; - for (auto const& cut : *this) { - VERIFY(!table.contains(&cut)); - table.insert(&cut); - } - return true; - } - - std::ostream& cut_set::display(std::ostream& out) const { - for (auto const& cut : *this) { - cut.display(out) << "\n"; - } - return out; - } - - - void cut_set::shrink(on_update_t& on_del, unsigned j) { - if (m_var != UINT_MAX && on_del) { - for (unsigned i = j; i < m_size; ++i) { - on_del(m_var, m_cuts[i]); - } - } - m_size = j; - } - - void cut_set::push_back(on_update_t& on_add, cut const& c) { - SASSERT(m_max_size > 0); - if (!m_cuts) { - m_cuts = new (*m_region) cut[m_max_size]; - } - if (m_size == m_max_size) { - m_max_size *= 2; - cut* new_cuts = new (*m_region) cut[m_max_size]; - std::uninitialized_copy(m_cuts, m_cuts + m_size, new_cuts); - m_cuts = new_cuts; - } - if (m_var != UINT_MAX && on_add) on_add(m_var, c); - m_cuts[m_size++] = c; - } - - void cut_set::evict(on_update_t& on_del, cut const& c) { - for (unsigned i = 0; i < m_size; ++i) { - if (m_cuts[i] == c) { - evict(on_del, i); - break; - } - } - } - - void cut_set::evict(on_update_t& on_del, unsigned idx) { - if (m_var != UINT_MAX && on_del) on_del(m_var, m_cuts[idx]); - m_cuts[idx] = m_cuts[--m_size]; - } - - void cut_set::init(region& r, unsigned max_sz, unsigned v) { - m_var = v; - m_size = 0; - SASSERT(!m_region || m_cuts); - VERIFY(!m_region || m_max_size > 0); - if (!m_region) { - m_max_size = 2; // max_sz; - m_region = &r; - m_cuts = nullptr; - } - } - - /** - \brief shift table 'a' by adding elements from 'c'. - a.shift_table(c) - - \pre 'a' is a subset of 'c'. - - Let 't' be the table for 'a'. - - i'th bit in t is function of indices x0*2^0 + x2*2^1 = i - i'th bit in t' is function of indices x0*2^0 + x1*2^1 + x2*2^2 = i - - i -> assignment to coefficients in c, - -> assignment to coefficients in a - -> compute j, - -> t'[i] <- t[j] - - This is still time consuming: - Ideas: - - pre-compute some shift operations. - - use strides on some common cases. - - what ABC does? - */ - uint64_t cut::shift_table(cut const& c) const { - SASSERT(subset_of(c)); - unsigned index = 0; - for (unsigned i = 0, j = 0, x = (*this)[i], y = c[j]; x != UINT_MAX; ) { - if (x == y) { - index |= (1 << j); - x = (*this)[++i]; - } - y = c[++j]; - } - index |= (1 << c.m_size); - return compute_shift(table(), index); - } - - bool cut::operator==(cut const& other) const { - return table() == other.table() && dom_eq(other); - } - - unsigned cut::hash() const { - return get_composite_hash(*this, m_size, - [](cut const& c) { return (unsigned)c.table(); }, - [](cut const& c, unsigned i) { return c[i]; }); - } - - unsigned cut::dom_hash() const { - return get_composite_hash(*this, m_size, - [](cut const& c) { return 3; }, - [](cut const& c, unsigned i) { return c[i]; }); - } - - bool cut::dom_eq(cut const& other) const { - if (m_size != other.m_size) return false; - for (unsigned i = 0; i < m_size; ++i) { - if ((*this)[i] != other[i]) return false; - } - return true; - } - - /** - * \brief create the masks - * i = 0: 101010101010101 - * i = 1: 1100110011001100 - * i = 2: 1111000011110000 - * i = 3: 111111110000000011111111 - */ - - uint64_t cut::effect_mask(unsigned i) { - SASSERT(i <= 6); - uint64_t m = 0; - if (i == 6) { - m = ~((uint64_t)0); - } - else { - m = (1ull << (1u << i)) - 1; // i = 0: m = 1 - unsigned w = 1u << (i + 1); // i = 0: w = 2 - while (w < 64) { - m |= (m << w); // i = 0: m = 1 + 4 - w *= 2; - } - } - return m; - } - - /** - remove element from cut as it is deemed a don't care - */ - void cut::remove_elem(unsigned i) { - for (unsigned j = i + 1; j < m_size; ++j) { - m_elems[j-1] = m_elems[j]; - } - --m_size; - uint64_t m = effect_mask(i); - uint64_t t = 0; - for (unsigned j = 0, offset = 0; j < 64; ++j) { - if (0 != (m & (1ull << j))) { - t |= ((m_table >> j) & 1u) << offset; - ++offset; - } - } - m_table = t; - m_dont_care = 0; - unsigned f = 0; - for (unsigned e : *this) { - f |= (1u << (e & 0x1F)); - } - m_filter = f; - } - - /** - sat-sweep evaluation. Given 64 bits worth of possible values per variable, - find possible values for function table encoded by cut. - */ - cut_val cut::eval(cut_eval const& env) const { - cut_val v; - uint64_t t = table(); - uint64_t n = table(); - unsigned sz = size(); - if (sz == 1 && t == 2) { - return env[m_elems[0]]; - } - for (unsigned i = 0; i < 64; ++i) { - unsigned offset = 0; - for (unsigned j = 0; j < sz; ++j) { - offset |= (((env[m_elems[j]].m_t >> i) & 0x1) << j); - } - v.m_t |= ((t >> offset) & 0x1) << i; - v.m_f |= ((n >> offset) & 0x1) << i; - } - return v; - } - - std::ostream& cut::display(std::ostream& out) const { - out << "{"; - for (unsigned i = 0; i < m_size; ++i) { - out << (*this)[i]; - if (i + 1 < m_size) out << " "; - } - out << "} "; - display_table(out, m_size, table()); - return out; - } - - std::ostream& cut::display_table(std::ostream& out, unsigned num_input, uint64_t table) { - for (unsigned i = 0; i < (1u << num_input); ++i) { - if (0 != (table & (1ull << i))) out << "1"; else out << "0"; - } - return out; - } - - std::string cut::table2string(unsigned num_input, uint64_t table) { - std::ostringstream strm; - display_table(strm, num_input, table); - return std::move(strm).str(); - } - - -} diff --git a/src/sat/sat_cutset.h b/src/sat/sat_cutset.h deleted file mode 100644 index f8451d412..000000000 --- a/src/sat/sat_cutset.h +++ /dev/null @@ -1,201 +0,0 @@ -/*++ - Copyright (c) 2020 Microsoft Corporation - - Module Name: - - sat_cutset.cpp - - Author: - - Nikolaj Bjorner 2020-01-02 - - --*/ - -#pragma once -#include "util/region.h" -#include "util/debug.h" -#include "util/util.h" -#include "util/lbool.h" -#include "util/vector.h" -#include -#include -#include - -namespace sat { - - struct cut_val { - cut_val():m_t(0ull), m_f(0ull) {} - cut_val(uint64_t t, uint64_t f): m_t(t), m_f(f) {} - uint64_t m_t, m_f; - }; - - typedef svector cut_eval; - - class cut { - unsigned m_filter; - unsigned m_size; - unsigned m_elems[5]; - uint64_t m_table; - mutable uint64_t m_dont_care; - - uint64_t table_mask() const { return (1ull << (1ull << m_size)) - 1ull; } - - public: - cut(): m_filter(0), m_size(0), m_table(0), m_dont_care(0) { - m_elems[0] = m_elems[1] = m_elems[2] = m_elems[3] = m_elems[4] = 0; - } - - cut(unsigned id): m_filter(1u << (id & 0x1F)), m_size(1), m_table(2), m_dont_care(0) { - m_elems[0] = id; - m_elems[1] = m_elems[2] = m_elems[3] = m_elems[4] = 0; - } - - cut_val eval(cut_eval const& env) const; - - unsigned size() const { return m_size; } - - unsigned filter() const { return m_filter; } - - static unsigned max_cut_size() { return 5; } - - unsigned const* begin() const { return m_elems; } - unsigned const* end() const { return m_elems + m_size; } - - bool add(unsigned i) { - if (m_size >= max_cut_size()) { - return false; - } - else { - m_elems[m_size++] = i; - m_filter |= (1u << (i & 0x1F)); - return true; - } - } - void negate() { set_table(~m_table); } - void set_table(uint64_t t) { m_table = t & table_mask(); } - uint64_t table() const { return (m_table | m_dont_care) & table_mask(); } - uint64_t ntable() const { return (~m_table | m_dont_care) & table_mask(); } - - uint64_t dont_care() const { return m_dont_care; } - void add_dont_care(uint64_t t) const { m_dont_care |= t; } - - bool is_true() const { return 0 == (table_mask() & ~table()); } - bool is_false() const { return 0 == (table_mask() & ~m_dont_care & m_table); } - - bool operator==(cut const& other) const; - bool operator!=(cut const& other) const { return !(*this == other); } - unsigned hash() const; - unsigned dom_hash() const; - bool dom_eq(cut const& other) const; - struct eq_proc { - bool operator()(cut const& a, cut const& b) const { return a == b; } - bool operator()(cut const* a, cut const* b) const { return *a == *b; } - }; - struct hash_proc { - unsigned operator()(cut const& a) const { return a.hash(); } - unsigned operator()(cut const* a) const { return a->hash(); } - }; - - struct dom_eq_proc { - bool operator()(cut const& a, cut const& b) const { return a.dom_eq(b); } - bool operator()(cut const* a, cut const* b) const { return a->dom_eq(*b); } - }; - - struct dom_hash_proc { - unsigned operator()(cut const& a) const { return a.dom_hash(); } - unsigned operator()(cut const* a) const { return a->dom_hash(); } - }; - - unsigned operator[](unsigned idx) const { - return (idx >= m_size) ? UINT_MAX : m_elems[idx]; - } - - uint64_t shift_table(cut const& other) const; - - bool merge(cut const& a, cut const& b) { - unsigned i = 0, j = 0; - unsigned x = a[i]; - unsigned y = b[j]; - while (x != UINT_MAX || y != UINT_MAX) { - if (!add(std::min(x, y))) { - return false; - } - if (x < y) { - x = a[++i]; - } - else if (y < x) { - y = b[++j]; - } - else { - x = a[++i]; - y = b[++j]; - } - } - return true; - } - - bool subset_of(cut const& other) const { - if (other.m_filter != (m_filter | other.m_filter)) { - return false; - } - unsigned i = 0; - unsigned other_id = other[i]; - for (unsigned id : *this) { - while (id > other_id) { - other_id = other[++i]; - } - if (id != other_id) return false; - other_id = other[++i]; - } - return true; - } - - void remove_elem(unsigned i); - - static uint64_t effect_mask(unsigned i); - - std::ostream& display(std::ostream& out) const; - - static std::ostream& display_table(std::ostream& out, unsigned num_input, uint64_t table); - - static std::string table2string(unsigned num_input, uint64_t table); - }; - - class cut_set { - unsigned m_var; - region* m_region; - unsigned m_size; - unsigned m_max_size; - cut * m_cuts; - public: - typedef std::function on_update_t; - - cut_set(): m_var(UINT_MAX), m_region(nullptr), m_size(0), m_max_size(0), m_cuts(nullptr) {} - void init(region& r, unsigned max_sz, unsigned v); - bool insert(on_update_t& on_add, on_update_t& on_del, cut const& c); - bool no_duplicates() const; - unsigned var() const { return m_var; } - unsigned size() const { return m_size; } - cut const * begin() const { return m_cuts; } - cut const * end() const { return m_cuts + m_size; } - cut const & back() { return m_cuts[m_size-1]; } - void push_back(on_update_t& on_add, cut const& c); - void reset(on_update_t& on_del) { shrink(on_del, 0); } - cut const & operator[](unsigned idx) const { return m_cuts[idx]; } - void shrink(on_update_t& on_del, unsigned j); - void swap(cut_set& other) noexcept { - std::swap(m_var, other.m_var); - std::swap(m_size, other.m_size); - std::swap(m_max_size, other.m_max_size); - std::swap(m_cuts, other.m_cuts); - } - void evict(on_update_t& on_del, unsigned idx); - void evict(on_update_t& on_del, cut const& c); - - std::ostream& display(std::ostream& out) const; - }; - - inline std::ostream& operator<<(std::ostream& out, cut const& c) { return c.display(out); } - inline std::ostream& operator<<(std::ostream& out, cut_set const& cs) { return cs.display(out); } - -} diff --git a/src/sat/sat_cutset_compute_shift.h b/src/sat/sat_cutset_compute_shift.h deleted file mode 100644 index 45d2e1de8..000000000 --- a/src/sat/sat_cutset_compute_shift.h +++ /dev/null @@ -1,939 +0,0 @@ -/*++ - Copyright (c) 2020 Microsoft Corporation - - Module Name: - - sat_cutset_compute_shift.h - - Author: - - Nikolaj Bjorner 2020-01-02 - - Notes: - - shifts truth table x using 'code'. - code encodes a mapping from bit-positions of the - input truth table encoded with x into bit-positions - in the output truth table. - The truth table covers up to 6 inputs, which fits in 64 bits. - - --*/ -#pragma once - -static uint64_t compute_shift(uint64_t x, unsigned code) { - switch (code) { -#define _x0 (x & 1ull) -#define _x1 _x0 - case 1: return _x1; -#define _x2 (_x1 | (_x1 << 1ull)) - case 2: return _x2; -#define _x3 (x & 3ull) -#define _x4 _x3 - case 3: return _x4; -#define _x5 (_x2 | (_x2 << 2ull)) - case 4: return _x5; -#define _x6 (_x4 | (_x4 << 2ull)) - case 5: return _x6; -#define _x7 (x & 2ull) -#define _x8 (_x7 << 1ull) -#define _x9 (_x8 | (_x8 << 1ull)) -#define _x10 (_x2 | _x9) - case 6: return _x10; -#define _x11 (x & 15ull) -#define _x12 _x11 - case 7: return _x12; -#define _x13 (_x5 | (_x5 << 4ull)) - case 8: return _x13; -#define _x14 (_x6 | (_x6 << 4ull)) - case 9: return _x14; -#define _x15 (_x10 | (_x10 << 4ull)) - case 10: return _x15; -#define _x16 (_x12 | (_x12 << 4ull)) - case 11: return _x16; -#define _x17 (_x7 << 3ull) -#define _x18 (_x17 | (_x17 << 1ull)) -#define _x19 (_x18 | (_x18 << 2ull)) -#define _x20 (_x5 | _x19) - case 12: return _x20; -#define _x21 (x & 12ull) -#define _x22 (_x21 << 2ull) -#define _x23 (_x22 | (_x22 << 2ull)) -#define _x24 (_x6 | _x23) - case 13: return _x24; -#define _x25 (x & 4ull) -#define _x26 (_x25 << 2ull) -#define _x27 (_x26 | (_x26 << 1ull)) -#define _x28 (x & 8ull) -#define _x29 (_x28 << 3ull) -#define _x30 (_x29 | (_x29 << 1ull)) -#define _x31 (_x27 | _x30) -#define _x32 (_x10 | _x31) - case 14: return _x32; -#define _x33 (x & 255ull) -#define _x34 _x33 - case 15: return _x34; -#define _x35 (_x13 | (_x13 << 8ull)) - case 16: return _x35; -#define _x36 (_x14 | (_x14 << 8ull)) - case 17: return _x36; -#define _x37 (_x15 | (_x15 << 8ull)) - case 18: return _x37; -#define _x38 (_x16 | (_x16 << 8ull)) - case 19: return _x38; -#define _x39 (_x20 | (_x20 << 8ull)) - case 20: return _x39; -#define _x40 (_x24 | (_x24 << 8ull)) - case 21: return _x40; -#define _x41 (_x32 | (_x32 << 8ull)) - case 22: return _x41; -#define _x42 (_x34 | (_x34 << 8ull)) - case 23: return _x42; -#define _x43 (_x7 << 7ull) -#define _x44 (_x43 | (_x43 << 1ull)) -#define _x45 (_x44 | (_x44 << 2ull)) -#define _x46 (_x45 | (_x45 << 4ull)) -#define _x47 (_x13 | _x46) - case 24: return _x47; -#define _x48 (_x21 << 6ull) -#define _x49 (_x48 | (_x48 << 2ull)) -#define _x50 (_x49 | (_x49 << 4ull)) -#define _x51 (_x14 | _x50) - case 25: return _x51; -#define _x52 (_x25 << 6ull) -#define _x53 (_x52 | (_x52 << 1ull)) -#define _x54 (_x28 << 7ull) -#define _x55 (_x54 | (_x54 << 1ull)) -#define _x56 (_x53 | _x55) -#define _x57 (_x56 | (_x56 << 4ull)) -#define _x58 (_x15 | _x57) - case 26: return _x58; -#define _x59 (x & 240ull) -#define _x60 (_x59 << 4ull) -#define _x61 (_x60 | (_x60 << 4ull)) -#define _x62 (_x16 | _x61) - case 27: return _x62; -#define _x63 (_x53 | (_x53 << 2ull)) -#define _x64 (_x28 << 9ull) -#define _x65 (_x64 | (_x64 << 1ull)) -#define _x66 (_x65 | (_x65 << 2ull)) -#define _x67 (_x63 | _x66) -#define _x68 (_x20 | _x67) - case 28: return _x68; -#define _x69 (x & 48ull) -#define _x70 (_x69 << 4ull) -#define _x71 (_x70 | (_x70 << 2ull)) -#define _x72 (x & 192ull) -#define _x73 (_x72 << 6ull) -#define _x74 (_x73 | (_x73 << 2ull)) -#define _x75 (_x71 | _x74) -#define _x76 (_x24 | _x75) - case 29: return _x76; -#define _x77 (x & 16ull) -#define _x78 (_x77 << 4ull) -#define _x79 (_x78 | (_x78 << 1ull)) -#define _x80 (x & 32ull) -#define _x81 (_x80 << 5ull) -#define _x82 (_x81 | (_x81 << 1ull)) -#define _x83 (_x79 | _x82) -#define _x84 (x & 64ull) -#define _x85 (_x84 << 6ull) -#define _x86 (_x85 | (_x85 << 1ull)) -#define _x87 (x & 128ull) -#define _x88 (_x87 << 7ull) -#define _x89 (_x88 | (_x88 << 1ull)) -#define _x90 (_x86 | _x89) -#define _x91 (_x83 | _x90) -#define _x92 (_x32 | _x91) - case 30: return _x92; -#define _x93 (x & 65535ull) -#define _x94 _x93 - case 31: return _x94; -#define _x95 (_x35 | (_x35 << 16ull)) - case 32: return _x95; -#define _x96 (_x36 | (_x36 << 16ull)) - case 33: return _x96; -#define _x97 (_x37 | (_x37 << 16ull)) - case 34: return _x97; -#define _x98 (_x38 | (_x38 << 16ull)) - case 35: return _x98; -#define _x99 (_x39 | (_x39 << 16ull)) - case 36: return _x99; -#define _x100 (_x40 | (_x40 << 16ull)) - case 37: return _x100; -#define _x101 (_x41 | (_x41 << 16ull)) - case 38: return _x101; -#define _x102 (_x42 | (_x42 << 16ull)) - case 39: return _x102; -#define _x103 (_x47 | (_x47 << 16ull)) - case 40: return _x103; -#define _x104 (_x51 | (_x51 << 16ull)) - case 41: return _x104; -#define _x105 (_x58 | (_x58 << 16ull)) - case 42: return _x105; -#define _x106 (_x62 | (_x62 << 16ull)) - case 43: return _x106; -#define _x107 (_x68 | (_x68 << 16ull)) - case 44: return _x107; -#define _x108 (_x76 | (_x76 << 16ull)) - case 45: return _x108; -#define _x109 (_x92 | (_x92 << 16ull)) - case 46: return _x109; -#define _x110 (_x94 | (_x94 << 16ull)) - case 47: return _x110; -#define _x111 (_x7 << 15ull) -#define _x112 (_x111 | (_x111 << 1ull)) -#define _x113 (_x112 | (_x112 << 2ull)) -#define _x114 (_x113 | (_x113 << 4ull)) -#define _x115 (_x114 | (_x114 << 8ull)) -#define _x116 (_x35 | _x115) - case 48: return _x116; -#define _x117 (_x21 << 14ull) -#define _x118 (_x117 | (_x117 << 2ull)) -#define _x119 (_x118 | (_x118 << 4ull)) -#define _x120 (_x119 | (_x119 << 8ull)) -#define _x121 (_x36 | _x120) - case 49: return _x121; -#define _x122 (_x25 << 14ull) -#define _x123 (_x122 | (_x122 << 1ull)) -#define _x124 (_x28 << 15ull) -#define _x125 (_x124 | (_x124 << 1ull)) -#define _x126 (_x123 | _x125) -#define _x127 (_x126 | (_x126 << 4ull)) -#define _x128 (_x127 | (_x127 << 8ull)) -#define _x129 (_x37 | _x128) - case 50: return _x129; -#define _x130 (_x59 << 12ull) -#define _x131 (_x130 | (_x130 << 4ull)) -#define _x132 (_x131 | (_x131 << 8ull)) -#define _x133 (_x38 | _x132) - case 51: return _x133; -#define _x134 (_x123 | (_x123 << 2ull)) -#define _x135 (_x28 << 17ull) -#define _x136 (_x135 | (_x135 << 1ull)) -#define _x137 (_x136 | (_x136 << 2ull)) -#define _x138 (_x134 | _x137) -#define _x139 (_x138 | (_x138 << 8ull)) -#define _x140 (_x39 | _x139) - case 52: return _x140; -#define _x141 (_x69 << 12ull) -#define _x142 (_x141 | (_x141 << 2ull)) -#define _x143 (_x72 << 14ull) -#define _x144 (_x143 | (_x143 << 2ull)) -#define _x145 (_x142 | _x144) -#define _x146 (_x145 | (_x145 << 8ull)) -#define _x147 (_x40 | _x146) - case 53: return _x147; -#define _x148 (_x77 << 12ull) -#define _x149 (_x148 | (_x148 << 1ull)) -#define _x150 (_x80 << 13ull) -#define _x151 (_x150 | (_x150 << 1ull)) -#define _x152 (_x149 | _x151) -#define _x153 (_x84 << 14ull) -#define _x154 (_x153 | (_x153 << 1ull)) -#define _x155 (_x87 << 15ull) -#define _x156 (_x155 | (_x155 << 1ull)) -#define _x157 (_x154 | _x156) -#define _x158 (_x152 | _x157) -#define _x159 (_x158 | (_x158 << 8ull)) -#define _x160 (_x41 | _x159) - case 54: return _x160; -#define _x161 (x & 65280ull) -#define _x162 (_x161 << 8ull) -#define _x163 (_x162 | (_x162 << 8ull)) -#define _x164 (_x42 | _x163) - case 55: return _x164; -#define _x165 (_x134 | (_x134 << 4ull)) -#define _x166 (_x28 << 21ull) -#define _x167 (_x166 | (_x166 << 1ull)) -#define _x168 (_x167 | (_x167 << 2ull)) -#define _x169 (_x168 | (_x168 << 4ull)) -#define _x170 (_x165 | _x169) -#define _x171 (_x47 | _x170) - case 56: return _x171; -#define _x172 (_x142 | (_x142 << 4ull)) -#define _x173 (_x72 << 18ull) -#define _x174 (_x173 | (_x173 << 2ull)) -#define _x175 (_x174 | (_x174 << 4ull)) -#define _x176 (_x172 | _x175) -#define _x177 (_x51 | _x176) - case 57: return _x177; -#define _x178 (_x152 | (_x152 << 4ull)) -#define _x179 (_x84 << 18ull) -#define _x180 (_x179 | (_x179 << 1ull)) -#define _x181 (_x87 << 19ull) -#define _x182 (_x181 | (_x181 << 1ull)) -#define _x183 (_x180 | _x182) -#define _x184 (_x183 | (_x183 << 4ull)) -#define _x185 (_x178 | _x184) -#define _x186 (_x58 | _x185) - case 58: return _x186; -#define _x187 (x & 3840ull) -#define _x188 (_x187 << 8ull) -#define _x189 (_x188 | (_x188 << 4ull)) -#define _x190 (x & 61440ull) -#define _x191 (_x190 << 12ull) -#define _x192 (_x191 | (_x191 << 4ull)) -#define _x193 (_x189 | _x192) -#define _x194 (_x62 | _x193) - case 59: return _x194; -#define _x195 (_x149 | (_x149 << 2ull)) -#define _x196 (_x80 << 15ull) -#define _x197 (_x196 | (_x196 << 1ull)) -#define _x198 (_x197 | (_x197 << 2ull)) -#define _x199 (_x195 | _x198) -#define _x200 (_x180 | (_x180 << 2ull)) -#define _x201 (_x87 << 21ull) -#define _x202 (_x201 | (_x201 << 1ull)) -#define _x203 (_x202 | (_x202 << 2ull)) -#define _x204 (_x200 | _x203) -#define _x205 (_x199 | _x204) -#define _x206 (_x68 | _x205) - case 60: return _x206; -#define _x207 (x & 768ull) -#define _x208 (_x207 << 8ull) -#define _x209 (_x208 | (_x208 << 2ull)) -#define _x210 (x & 3072ull) -#define _x211 (_x210 << 10ull) -#define _x212 (_x211 | (_x211 << 2ull)) -#define _x213 (_x209 | _x212) -#define _x214 (x & 12288ull) -#define _x215 (_x214 << 12ull) -#define _x216 (_x215 | (_x215 << 2ull)) -#define _x217 (x & 49152ull) -#define _x218 (_x217 << 14ull) -#define _x219 (_x218 | (_x218 << 2ull)) -#define _x220 (_x216 | _x219) -#define _x221 (_x213 | _x220) -#define _x222 (_x76 | _x221) - case 61: return _x222; -#define _x223 (x & 256ull) -#define _x224 (_x223 << 8ull) -#define _x225 (_x224 | (_x224 << 1ull)) -#define _x226 (x & 512ull) -#define _x227 (_x226 << 9ull) -#define _x228 (_x227 | (_x227 << 1ull)) -#define _x229 (_x225 | _x228) -#define _x230 (x & 1024ull) -#define _x231 (_x230 << 10ull) -#define _x232 (_x231 | (_x231 << 1ull)) -#define _x233 (x & 2048ull) -#define _x234 (_x233 << 11ull) -#define _x235 (_x234 | (_x234 << 1ull)) -#define _x236 (_x232 | _x235) -#define _x237 (_x229 | _x236) -#define _x238 (x & 4096ull) -#define _x239 (_x238 << 12ull) -#define _x240 (_x239 | (_x239 << 1ull)) -#define _x241 (x & 8192ull) -#define _x242 (_x241 << 13ull) -#define _x243 (_x242 | (_x242 << 1ull)) -#define _x244 (_x240 | _x243) -#define _x245 (x & 16384ull) -#define _x246 (_x245 << 14ull) -#define _x247 (_x246 | (_x246 << 1ull)) -#define _x248 (x & 32768ull) -#define _x249 (_x248 << 15ull) -#define _x250 (_x249 | (_x249 << 1ull)) -#define _x251 (_x247 | _x250) -#define _x252 (_x244 | _x251) -#define _x253 (_x237 | _x252) -#define _x254 (_x92 | _x253) - case 62: return _x254; -#define _x255 (x & 4294967295ull) -#define _x256 _x255 - case 63: return _x256; -#define _x257 (_x95 | (_x95 << 32ull)) - case 64: return _x257; -#define _x258 (_x96 | (_x96 << 32ull)) - case 65: return _x258; -#define _x259 (_x97 | (_x97 << 32ull)) - case 66: return _x259; -#define _x260 (_x98 | (_x98 << 32ull)) - case 67: return _x260; -#define _x261 (_x99 | (_x99 << 32ull)) - case 68: return _x261; -#define _x262 (_x100 | (_x100 << 32ull)) - case 69: return _x262; -#define _x263 (_x101 | (_x101 << 32ull)) - case 70: return _x263; -#define _x264 (_x102 | (_x102 << 32ull)) - case 71: return _x264; -#define _x265 (_x103 | (_x103 << 32ull)) - case 72: return _x265; -#define _x266 (_x104 | (_x104 << 32ull)) - case 73: return _x266; -#define _x267 (_x105 | (_x105 << 32ull)) - case 74: return _x267; -#define _x268 (_x106 | (_x106 << 32ull)) - case 75: return _x268; -#define _x269 (_x107 | (_x107 << 32ull)) - case 76: return _x269; -#define _x270 (_x108 | (_x108 << 32ull)) - case 77: return _x270; -#define _x271 (_x109 | (_x109 << 32ull)) - case 78: return _x271; -#define _x272 (_x110 | (_x110 << 32ull)) - case 79: return _x272; -#define _x273 (_x116 | (_x116 << 32ull)) - case 80: return _x273; -#define _x274 (_x121 | (_x121 << 32ull)) - case 81: return _x274; -#define _x275 (_x129 | (_x129 << 32ull)) - case 82: return _x275; -#define _x276 (_x133 | (_x133 << 32ull)) - case 83: return _x276; -#define _x277 (_x140 | (_x140 << 32ull)) - case 84: return _x277; -#define _x278 (_x147 | (_x147 << 32ull)) - case 85: return _x278; -#define _x279 (_x160 | (_x160 << 32ull)) - case 86: return _x279; -#define _x280 (_x164 | (_x164 << 32ull)) - case 87: return _x280; -#define _x281 (_x171 | (_x171 << 32ull)) - case 88: return _x281; -#define _x282 (_x177 | (_x177 << 32ull)) - case 89: return _x282; -#define _x283 (_x186 | (_x186 << 32ull)) - case 90: return _x283; -#define _x284 (_x194 | (_x194 << 32ull)) - case 91: return _x284; -#define _x285 (_x206 | (_x206 << 32ull)) - case 92: return _x285; -#define _x286 (_x222 | (_x222 << 32ull)) - case 93: return _x286; -#define _x287 (_x254 | (_x254 << 32ull)) - case 94: return _x287; -#define _x288 (_x256 | (_x256 << 32ull)) - case 95: return _x288; -#define _x289 (_x7 << 31ull) -#define _x290 (_x289 | (_x289 << 1ull)) -#define _x291 (_x290 | (_x290 << 2ull)) -#define _x292 (_x291 | (_x291 << 4ull)) -#define _x293 (_x292 | (_x292 << 8ull)) -#define _x294 (_x293 | (_x293 << 16ull)) -#define _x295 (_x95 | _x294) - case 96: return _x295; -#define _x296 (_x21 << 30ull) -#define _x297 (_x296 | (_x296 << 2ull)) -#define _x298 (_x297 | (_x297 << 4ull)) -#define _x299 (_x298 | (_x298 << 8ull)) -#define _x300 (_x299 | (_x299 << 16ull)) -#define _x301 (_x96 | _x300) - case 97: return _x301; -#define _x302 (_x25 << 30ull) -#define _x303 (_x302 | (_x302 << 1ull)) -#define _x304 (_x28 << 31ull) -#define _x305 (_x304 | (_x304 << 1ull)) -#define _x306 (_x303 | _x305) -#define _x307 (_x306 | (_x306 << 4ull)) -#define _x308 (_x307 | (_x307 << 8ull)) -#define _x309 (_x308 | (_x308 << 16ull)) -#define _x310 (_x97 | _x309) - case 98: return _x310; -#define _x311 (_x59 << 28ull) -#define _x312 (_x311 | (_x311 << 4ull)) -#define _x313 (_x312 | (_x312 << 8ull)) -#define _x314 (_x313 | (_x313 << 16ull)) -#define _x315 (_x98 | _x314) - case 99: return _x315; -#define _x316 (_x303 | (_x303 << 2ull)) -#define _x317 (_x28 << 33ull) -#define _x318 (_x317 | (_x317 << 1ull)) -#define _x319 (_x318 | (_x318 << 2ull)) -#define _x320 (_x316 | _x319) -#define _x321 (_x320 | (_x320 << 8ull)) -#define _x322 (_x321 | (_x321 << 16ull)) -#define _x323 (_x99 | _x322) - case 100: return _x323; -#define _x324 (_x69 << 28ull) -#define _x325 (_x324 | (_x324 << 2ull)) -#define _x326 (_x72 << 30ull) -#define _x327 (_x326 | (_x326 << 2ull)) -#define _x328 (_x325 | _x327) -#define _x329 (_x328 | (_x328 << 8ull)) -#define _x330 (_x329 | (_x329 << 16ull)) -#define _x331 (_x100 | _x330) - case 101: return _x331; -#define _x332 (_x77 << 28ull) -#define _x333 (_x332 | (_x332 << 1ull)) -#define _x334 (_x80 << 29ull) -#define _x335 (_x334 | (_x334 << 1ull)) -#define _x336 (_x333 | _x335) -#define _x337 (_x84 << 30ull) -#define _x338 (_x337 | (_x337 << 1ull)) -#define _x339 (_x87 << 31ull) -#define _x340 (_x339 | (_x339 << 1ull)) -#define _x341 (_x338 | _x340) -#define _x342 (_x336 | _x341) -#define _x343 (_x342 | (_x342 << 8ull)) -#define _x344 (_x343 | (_x343 << 16ull)) -#define _x345 (_x101 | _x344) - case 102: return _x345; -#define _x346 (_x161 << 24ull) -#define _x347 (_x346 | (_x346 << 8ull)) -#define _x348 (_x347 | (_x347 << 16ull)) -#define _x349 (_x102 | _x348) - case 103: return _x349; -#define _x350 (_x316 | (_x316 << 4ull)) -#define _x351 (_x28 << 37ull) -#define _x352 (_x351 | (_x351 << 1ull)) -#define _x353 (_x352 | (_x352 << 2ull)) -#define _x354 (_x353 | (_x353 << 4ull)) -#define _x355 (_x350 | _x354) -#define _x356 (_x355 | (_x355 << 16ull)) -#define _x357 (_x103 | _x356) - case 104: return _x357; -#define _x358 (_x325 | (_x325 << 4ull)) -#define _x359 (_x72 << 34ull) -#define _x360 (_x359 | (_x359 << 2ull)) -#define _x361 (_x360 | (_x360 << 4ull)) -#define _x362 (_x358 | _x361) -#define _x363 (_x362 | (_x362 << 16ull)) -#define _x364 (_x104 | _x363) - case 105: return _x364; -#define _x365 (_x336 | (_x336 << 4ull)) -#define _x366 (_x84 << 34ull) -#define _x367 (_x366 | (_x366 << 1ull)) -#define _x368 (_x87 << 35ull) -#define _x369 (_x368 | (_x368 << 1ull)) -#define _x370 (_x367 | _x369) -#define _x371 (_x370 | (_x370 << 4ull)) -#define _x372 (_x365 | _x371) -#define _x373 (_x372 | (_x372 << 16ull)) -#define _x374 (_x105 | _x373) - case 106: return _x374; -#define _x375 (_x187 << 24ull) -#define _x376 (_x375 | (_x375 << 4ull)) -#define _x377 (_x190 << 28ull) -#define _x378 (_x377 | (_x377 << 4ull)) -#define _x379 (_x376 | _x378) -#define _x380 (_x379 | (_x379 << 16ull)) -#define _x381 (_x106 | _x380) - case 107: return _x381; -#define _x382 (_x333 | (_x333 << 2ull)) -#define _x383 (_x80 << 31ull) -#define _x384 (_x383 | (_x383 << 1ull)) -#define _x385 (_x384 | (_x384 << 2ull)) -#define _x386 (_x382 | _x385) -#define _x387 (_x367 | (_x367 << 2ull)) -#define _x388 (_x87 << 37ull) -#define _x389 (_x388 | (_x388 << 1ull)) -#define _x390 (_x389 | (_x389 << 2ull)) -#define _x391 (_x387 | _x390) -#define _x392 (_x386 | _x391) -#define _x393 (_x392 | (_x392 << 16ull)) -#define _x394 (_x107 | _x393) - case 108: return _x394; -#define _x395 (_x207 << 24ull) -#define _x396 (_x395 | (_x395 << 2ull)) -#define _x397 (_x210 << 26ull) -#define _x398 (_x397 | (_x397 << 2ull)) -#define _x399 (_x396 | _x398) -#define _x400 (_x214 << 28ull) -#define _x401 (_x400 | (_x400 << 2ull)) -#define _x402 (_x217 << 30ull) -#define _x403 (_x402 | (_x402 << 2ull)) -#define _x404 (_x401 | _x403) -#define _x405 (_x399 | _x404) -#define _x406 (_x405 | (_x405 << 16ull)) -#define _x407 (_x108 | _x406) - case 109: return _x407; -#define _x408 (_x223 << 24ull) -#define _x409 (_x408 | (_x408 << 1ull)) -#define _x410 (_x226 << 25ull) -#define _x411 (_x410 | (_x410 << 1ull)) -#define _x412 (_x409 | _x411) -#define _x413 (_x230 << 26ull) -#define _x414 (_x413 | (_x413 << 1ull)) -#define _x415 (_x233 << 27ull) -#define _x416 (_x415 | (_x415 << 1ull)) -#define _x417 (_x414 | _x416) -#define _x418 (_x412 | _x417) -#define _x419 (_x238 << 28ull) -#define _x420 (_x419 | (_x419 << 1ull)) -#define _x421 (_x241 << 29ull) -#define _x422 (_x421 | (_x421 << 1ull)) -#define _x423 (_x420 | _x422) -#define _x424 (_x245 << 30ull) -#define _x425 (_x424 | (_x424 << 1ull)) -#define _x426 (_x248 << 31ull) -#define _x427 (_x426 | (_x426 << 1ull)) -#define _x428 (_x425 | _x427) -#define _x429 (_x423 | _x428) -#define _x430 (_x418 | _x429) -#define _x431 (_x430 | (_x430 << 16ull)) -#define _x432 (_x109 | _x431) - case 110: return _x432; -#define _x433 (x & 4294901760ull) -#define _x434 (_x433 << 16ull) -#define _x435 (_x434 | (_x434 << 16ull)) -#define _x436 (_x110 | _x435) - case 111: return _x436; -#define _x437 (_x350 | (_x350 << 8ull)) -#define _x438 (_x28 << 45ull) -#define _x439 (_x438 | (_x438 << 1ull)) -#define _x440 (_x439 | (_x439 << 2ull)) -#define _x441 (_x440 | (_x440 << 4ull)) -#define _x442 (_x441 | (_x441 << 8ull)) -#define _x443 (_x437 | _x442) -#define _x444 (_x116 | _x443) - case 112: return _x444; -#define _x445 (_x358 | (_x358 << 8ull)) -#define _x446 (_x72 << 42ull) -#define _x447 (_x446 | (_x446 << 2ull)) -#define _x448 (_x447 | (_x447 << 4ull)) -#define _x449 (_x448 | (_x448 << 8ull)) -#define _x450 (_x445 | _x449) -#define _x451 (_x121 | _x450) - case 113: return _x451; -#define _x452 (_x365 | (_x365 << 8ull)) -#define _x453 (_x84 << 42ull) -#define _x454 (_x453 | (_x453 << 1ull)) -#define _x455 (_x87 << 43ull) -#define _x456 (_x455 | (_x455 << 1ull)) -#define _x457 (_x454 | _x456) -#define _x458 (_x457 | (_x457 << 4ull)) -#define _x459 (_x458 | (_x458 << 8ull)) -#define _x460 (_x452 | _x459) -#define _x461 (_x129 | _x460) - case 114: return _x461; -#define _x462 (_x376 | (_x376 << 8ull)) -#define _x463 (_x190 << 36ull) -#define _x464 (_x463 | (_x463 << 4ull)) -#define _x465 (_x464 | (_x464 << 8ull)) -#define _x466 (_x462 | _x465) -#define _x467 (_x133 | _x466) - case 115: return _x467; -#define _x468 (_x386 | (_x386 << 8ull)) -#define _x469 (_x454 | (_x454 << 2ull)) -#define _x470 (_x87 << 45ull) -#define _x471 (_x470 | (_x470 << 1ull)) -#define _x472 (_x471 | (_x471 << 2ull)) -#define _x473 (_x469 | _x472) -#define _x474 (_x473 | (_x473 << 8ull)) -#define _x475 (_x468 | _x474) -#define _x476 (_x140 | _x475) - case 116: return _x476; -#define _x477 (_x399 | (_x399 << 8ull)) -#define _x478 (_x214 << 36ull) -#define _x479 (_x478 | (_x478 << 2ull)) -#define _x480 (_x217 << 38ull) -#define _x481 (_x480 | (_x480 << 2ull)) -#define _x482 (_x479 | _x481) -#define _x483 (_x482 | (_x482 << 8ull)) -#define _x484 (_x477 | _x483) -#define _x485 (_x147 | _x484) - case 117: return _x485; -#define _x486 (_x418 | (_x418 << 8ull)) -#define _x487 (_x238 << 36ull) -#define _x488 (_x487 | (_x487 << 1ull)) -#define _x489 (_x241 << 37ull) -#define _x490 (_x489 | (_x489 << 1ull)) -#define _x491 (_x488 | _x490) -#define _x492 (_x245 << 38ull) -#define _x493 (_x492 | (_x492 << 1ull)) -#define _x494 (_x248 << 39ull) -#define _x495 (_x494 | (_x494 << 1ull)) -#define _x496 (_x493 | _x495) -#define _x497 (_x491 | _x496) -#define _x498 (_x497 | (_x497 << 8ull)) -#define _x499 (_x486 | _x498) -#define _x500 (_x160 | _x499) - case 118: return _x500; -#define _x501 (x & 16711680ull) -#define _x502 (_x501 << 16ull) -#define _x503 (_x502 | (_x502 << 8ull)) -#define _x504 (x & 4278190080ull) -#define _x505 (_x504 << 24ull) -#define _x506 (_x505 | (_x505 << 8ull)) -#define _x507 (_x503 | _x506) -#define _x508 (_x164 | _x507) - case 119: return _x508; -#define _x509 (_x382 | (_x382 << 4ull)) -#define _x510 (_x80 << 35ull) -#define _x511 (_x510 | (_x510 << 1ull)) -#define _x512 (_x511 | (_x511 << 2ull)) -#define _x513 (_x512 | (_x512 << 4ull)) -#define _x514 (_x509 | _x513) -#define _x515 (_x469 | (_x469 << 4ull)) -#define _x516 (_x87 << 49ull) -#define _x517 (_x516 | (_x516 << 1ull)) -#define _x518 (_x517 | (_x517 << 2ull)) -#define _x519 (_x518 | (_x518 << 4ull)) -#define _x520 (_x515 | _x519) -#define _x521 (_x514 | _x520) -#define _x522 (_x171 | _x521) - case 120: return _x522; -#define _x523 (_x396 | (_x396 << 4ull)) -#define _x524 (_x210 << 30ull) -#define _x525 (_x524 | (_x524 << 2ull)) -#define _x526 (_x525 | (_x525 << 4ull)) -#define _x527 (_x523 | _x526) -#define _x528 (_x479 | (_x479 << 4ull)) -#define _x529 (_x217 << 42ull) -#define _x530 (_x529 | (_x529 << 2ull)) -#define _x531 (_x530 | (_x530 << 4ull)) -#define _x532 (_x528 | _x531) -#define _x533 (_x527 | _x532) -#define _x534 (_x177 | _x533) - case 121: return _x534; -#define _x535 (_x412 | (_x412 << 4ull)) -#define _x536 (_x230 << 30ull) -#define _x537 (_x536 | (_x536 << 1ull)) -#define _x538 (_x233 << 31ull) -#define _x539 (_x538 | (_x538 << 1ull)) -#define _x540 (_x537 | _x539) -#define _x541 (_x540 | (_x540 << 4ull)) -#define _x542 (_x535 | _x541) -#define _x543 (_x491 | (_x491 << 4ull)) -#define _x544 (_x245 << 42ull) -#define _x545 (_x544 | (_x544 << 1ull)) -#define _x546 (_x248 << 43ull) -#define _x547 (_x546 | (_x546 << 1ull)) -#define _x548 (_x545 | _x547) -#define _x549 (_x548 | (_x548 << 4ull)) -#define _x550 (_x543 | _x549) -#define _x551 (_x542 | _x550) -#define _x552 (_x186 | _x551) - case 122: return _x552; -#define _x553 (x & 983040ull) -#define _x554 (_x553 << 16ull) -#define _x555 (_x554 | (_x554 << 4ull)) -#define _x556 (x & 15728640ull) -#define _x557 (_x556 << 20ull) -#define _x558 (_x557 | (_x557 << 4ull)) -#define _x559 (_x555 | _x558) -#define _x560 (x & 251658240ull) -#define _x561 (_x560 << 24ull) -#define _x562 (_x561 | (_x561 << 4ull)) -#define _x563 (x & 4026531840ull) -#define _x564 (_x563 << 28ull) -#define _x565 (_x564 | (_x564 << 4ull)) -#define _x566 (_x562 | _x565) -#define _x567 (_x559 | _x566) -#define _x568 (_x194 | _x567) - case 123: return _x568; -#define _x569 (_x409 | (_x409 << 2ull)) -#define _x570 (_x226 << 27ull) -#define _x571 (_x570 | (_x570 << 1ull)) -#define _x572 (_x571 | (_x571 << 2ull)) -#define _x573 (_x569 | _x572) -#define _x574 (_x537 | (_x537 << 2ull)) -#define _x575 (_x233 << 33ull) -#define _x576 (_x575 | (_x575 << 1ull)) -#define _x577 (_x576 | (_x576 << 2ull)) -#define _x578 (_x574 | _x577) -#define _x579 (_x573 | _x578) -#define _x580 (_x488 | (_x488 << 2ull)) -#define _x581 (_x241 << 39ull) -#define _x582 (_x581 | (_x581 << 1ull)) -#define _x583 (_x582 | (_x582 << 2ull)) -#define _x584 (_x580 | _x583) -#define _x585 (_x545 | (_x545 << 2ull)) -#define _x586 (_x248 << 45ull) -#define _x587 (_x586 | (_x586 << 1ull)) -#define _x588 (_x587 | (_x587 << 2ull)) -#define _x589 (_x585 | _x588) -#define _x590 (_x584 | _x589) -#define _x591 (_x579 | _x590) -#define _x592 (_x206 | _x591) - case 124: return _x592; -#define _x593 (x & 196608ull) -#define _x594 (_x593 << 16ull) -#define _x595 (_x594 | (_x594 << 2ull)) -#define _x596 (x & 786432ull) -#define _x597 (_x596 << 18ull) -#define _x598 (_x597 | (_x597 << 2ull)) -#define _x599 (_x595 | _x598) -#define _x600 (x & 3145728ull) -#define _x601 (_x600 << 20ull) -#define _x602 (_x601 | (_x601 << 2ull)) -#define _x603 (x & 12582912ull) -#define _x604 (_x603 << 22ull) -#define _x605 (_x604 | (_x604 << 2ull)) -#define _x606 (_x602 | _x605) -#define _x607 (_x599 | _x606) -#define _x608 (x & 50331648ull) -#define _x609 (_x608 << 24ull) -#define _x610 (_x609 | (_x609 << 2ull)) -#define _x611 (x & 201326592ull) -#define _x612 (_x611 << 26ull) -#define _x613 (_x612 | (_x612 << 2ull)) -#define _x614 (_x610 | _x613) -#define _x615 (x & 805306368ull) -#define _x616 (_x615 << 28ull) -#define _x617 (_x616 | (_x616 << 2ull)) -#define _x618 (x & 3221225472ull) -#define _x619 (_x618 << 30ull) -#define _x620 (_x619 | (_x619 << 2ull)) -#define _x621 (_x617 | _x620) -#define _x622 (_x614 | _x621) -#define _x623 (_x607 | _x622) -#define _x624 (_x222 | _x623) - case 125: return _x624; -#define _x625 (x & 65536ull) -#define _x626 (_x625 << 16ull) -#define _x627 (_x626 | (_x626 << 1ull)) -#define _x628 (x & 131072ull) -#define _x629 (_x628 << 17ull) -#define _x630 (_x629 | (_x629 << 1ull)) -#define _x631 (_x627 | _x630) -#define _x632 (x & 262144ull) -#define _x633 (_x632 << 18ull) -#define _x634 (_x633 | (_x633 << 1ull)) -#define _x635 (x & 524288ull) -#define _x636 (_x635 << 19ull) -#define _x637 (_x636 | (_x636 << 1ull)) -#define _x638 (_x634 | _x637) -#define _x639 (_x631 | _x638) -#define _x640 (x & 1048576ull) -#define _x641 (_x640 << 20ull) -#define _x642 (_x641 | (_x641 << 1ull)) -#define _x643 (x & 2097152ull) -#define _x644 (_x643 << 21ull) -#define _x645 (_x644 | (_x644 << 1ull)) -#define _x646 (_x642 | _x645) -#define _x647 (x & 4194304ull) -#define _x648 (_x647 << 22ull) -#define _x649 (_x648 | (_x648 << 1ull)) -#define _x650 (x & 8388608ull) -#define _x651 (_x650 << 23ull) -#define _x652 (_x651 | (_x651 << 1ull)) -#define _x653 (_x649 | _x652) -#define _x654 (_x646 | _x653) -#define _x655 (_x639 | _x654) -#define _x656 (x & 16777216ull) -#define _x657 (_x656 << 24ull) -#define _x658 (_x657 | (_x657 << 1ull)) -#define _x659 (x & 33554432ull) -#define _x660 (_x659 << 25ull) -#define _x661 (_x660 | (_x660 << 1ull)) -#define _x662 (_x658 | _x661) -#define _x663 (x & 67108864ull) -#define _x664 (_x663 << 26ull) -#define _x665 (_x664 | (_x664 << 1ull)) -#define _x666 (x & 134217728ull) -#define _x667 (_x666 << 27ull) -#define _x668 (_x667 | (_x667 << 1ull)) -#define _x669 (_x665 | _x668) -#define _x670 (_x662 | _x669) -#define _x671 (x & 268435456ull) -#define _x672 (_x671 << 28ull) -#define _x673 (_x672 | (_x672 << 1ull)) -#define _x674 (x & 536870912ull) -#define _x675 (_x674 << 29ull) -#define _x676 (_x675 | (_x675 << 1ull)) -#define _x677 (_x673 | _x676) -#define _x678 (x & 1073741824ull) -#define _x679 (_x678 << 30ull) -#define _x680 (_x679 | (_x679 << 1ull)) -#define _x681 (x & 2147483648ull) -#define _x682 (_x681 << 31ull) -#define _x683 (_x682 | (_x682 << 1ull)) -#define _x684 (_x680 | _x683) -#define _x685 (_x677 | _x684) -#define _x686 (_x670 | _x685) -#define _x687 (_x655 | _x686) -#define _x688 (_x254 | _x687) - case 126: return _x688; - case 127: return x; - default: - UNREACHABLE(); - return 0; - } -} - - -#if 0 - -def consecutive(S): - for k in range(len(S)-1): - if S[k] + 1 != S[k+1]: - return False - return True - -def shift(x, k): - if k == 0: - return x - if k < 0: - return "(%s >> %dull)" % (x,-k) - return "(%s << %dull)" % (x, k) - -def hash(r, hashcons): - if r in hashcons: - return hashcons[r] - id = "_x%d" % len(hashcons) - print ("#define %s %s" % (id, r)) - hashcons[r] = id - return id - -def compile(S, offset, hashcons): - if consecutive(S): - k = S[0] - l = len(S) - if l == 64: - return "x" - mask = ((1 << l)-1) << k - return hash(shift(hash("(x & %dull)" % mask, hashcons), offset - k), hashcons) - l2 = len(S) >> 1 - S1 = S[0:l2] - S2 = S[l2:] - if S1 == S2: - r1 = compile(S1, offset, hashcons) - return hash("(%s | (%s << %dull))" % (r1, r1, l2), hashcons) - r1 = compile(S1, offset, hashcons) - r2 = compile(S2, offset + l2, hashcons) - return hash("(%s | %s)" % (r1, r2), hashcons) - -def mems2index(mems, bound): - k = 0 - i = 0 - for m in mems: - if m: - k |= (1 << i) - i += 1 - k |= (1 << i) - return k - -def precompute(mems, bound, hashcons): - K = 0 - j = 0 - coeff = {} - deficit = {} - for m in mems: - if m: - coeff[K] = (1 << j) - deficit[K] = j - K - K += 1 - j += 1 - indices = [] - for j in range(1 << len(mems)): - k = 0 - for i in range(K): - if 0 != (j & coeff[i]): - k += (1 << i) - indices += [k] - idx = mems2index(mems, bound) - instr = compile(indices, 0, hashcons) - print(" case %d: return %s;" % (idx, instr)) - -def create_mems(mems, n): - if n == 0: - return ([], mems) - prefix, m1 = create_mems(mems, n - 1) - m2 = [m + [False] for m in m1] - m3 = [m + [True] for m in m1] - return prefix + m1, m2 + m3 - -def combinations(n, m, hashcons): - prefix, S = create_mems([[]], 6) - mems = prefix + S - for mem in mems: - precompute(mem, m, hashcons) - -hashcons = {} -combinations(7, 7, hashcons) - -#endif - diff --git a/src/sat/sat_drat.h b/src/sat/sat_drat.h index 7c39f5f41..2836d1130 100644 --- a/src/sat/sat_drat.h +++ b/src/sat/sat_drat.h @@ -21,7 +21,8 @@ Notes: --*/ #pragma once -#include "sat_types.h" +#include "sat/sat_types.h" +#include "sat/sat_clause.h" namespace sat { class justification; diff --git a/src/sat/sat_elim_eqs.cpp b/src/sat/sat_elim_eqs.cpp index 8ec2992c9..05e0fc3a8 100644 --- a/src/sat/sat_elim_eqs.cpp +++ b/src/sat/sat_elim_eqs.cpp @@ -229,9 +229,6 @@ namespace sat { literal r = roots[v]; SASSERT(v != r.var()); - if (m_solver.m_cut_simplifier) - m_solver.m_cut_simplifier->set_root(v, r); - bool set_root = m_solver.set_root(l, r); TRACE(elim_eqs, tout << l << " " << r << "\n";); if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !set_root))) { diff --git a/src/sat/sat_lut_finder.cpp b/src/sat/sat_lut_finder.cpp deleted file mode 100644 index 0e683eade..000000000 --- a/src/sat/sat_lut_finder.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/*++ - Copyright (c) 2020 Microsoft Corporation - - Module Name: - - sat_lut_finder.cpp - - Abstract: - - lut finder - - Author: - - Nikolaj Bjorner 2020-01-02 - - Notes: - - - --*/ - -#include "sat/sat_lut_finder.h" -#include "sat/sat_solver.h" - -namespace sat { - - void lut_finder::operator()(clause_vector& clauses) { - m_removed_clauses.reset(); - unsigned max_size = m_max_lut_size; - // we better have enough bits in the combination mask to - // handle clauses up to max_size. - // max_size = 5 -> 32 bits - // max_size = 6 -> 64 bits - SASSERT(sizeof(m_combination)*8 >= (1ull << static_cast(max_size))); - init_clause_filter(); - for (unsigned i = 0; i <= 6; ++i) { - m_masks[i] = cut::effect_mask(i); - } - m_var_position.resize(s.num_vars()); - for (clause* cp : clauses) { - cp->unmark_used(); - } - for (; max_size > 2; --max_size) { - for (clause* cp : clauses) { - clause& c = *cp; - if (c.size() == max_size && !c.was_removed() && !c.is_learned() && !c.was_used()) { - check_lut(c); - } - } - } - m_clause_filters.clear(); - - for (clause* cp : clauses) cp->unmark_used(); - for (clause* cp : m_removed_clauses) cp->mark_used(); - std::function not_used = [](clause* cp) { return !cp->was_used(); }; - clauses.filter_update(not_used); - } - - void lut_finder::check_lut(clause& c) { - SASSERT(c.size() > 2); - unsigned filter = get_clause_filter(c); - s.init_visited(); - unsigned mask = 0, i = 0; - m_vars.reset(); - m_clause.reset(); - for (literal l : c) { - m_clause.push_back(l); - } - // ensure that variables in returned LUT are sorted - std::sort(m_clause.begin(), m_clause.end()); - for (literal l : m_clause) { - m_vars.push_back(l.var()); - m_var_position[l.var()] = i; - s.mark_visited(l.var()); - mask |= (l.sign() << (i++)); - } - m_clauses_to_remove.reset(); - m_clauses_to_remove.push_back(&c); - m_combination = 0; - m_num_combinations = 0; - set_combination(mask); - c.mark_used(); - for (literal l : c) { - for (auto const& cf : m_clause_filters[l.var()]) { - if ((filter == (filter | cf.m_filter)) && - !cf.m_clause->was_used() && - extract_lut(*cf.m_clause)) { - add_lut(); - return; - } - } - // TBD: replace by BIG - // loop over binary clauses in watch list - for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { - if (extract_lut(~l, w.get_literal())) { - add_lut(); - return; - } - } - } - l.neg(); - for (watched const & w : s.get_wlist(l)) { - if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { - if (extract_lut(~l, w.get_literal())) { - add_lut(); - return; - } - } - } - } - } - - void lut_finder::add_lut() { - DEBUG_CODE(for (clause* cp : m_clauses_to_remove) VERIFY(cp->was_used());); - m_removed_clauses.append(m_clauses_to_remove); - bool_var v; - uint64_t lut = convert_combination(m_vars, v); - TRACE(aig_simplifier, - for (clause* cp : m_clauses_to_remove) { - tout << *cp << "\n" << v << ": " << m_vars << "\n"; - } - display_mask(tout, lut, 1u << m_vars.size()) << "\n";); - m_on_lut(lut, m_vars, v); - } - - bool lut_finder::extract_lut(literal l1, literal l2) { - SASSERT(s.m_visited.is_visited(l1.var())); - SASSERT(s.m_visited.is_visited(l2.var())); - m_missing.reset(); - unsigned mask = 0; - for (unsigned i = 0; i < m_vars.size(); ++i) { - if (m_vars[i] == l1.var()) { - mask |= (l1.sign() << i); - } - else if (m_vars[i] == l2.var()) { - mask |= (l2.sign() << i); - } - else { - m_missing.push_back(i); - } - } - return update_combinations(mask); - } - - bool lut_finder::extract_lut(clause& c2) { - for (literal l : c2) { - if (!s.is_visited(l.var())) - return false; - } - if (c2.size() == m_vars.size()) { - m_clauses_to_remove.push_back(&c2); - c2.mark_used(); - } - // insert missing - unsigned mask = 0; - m_missing.reset(); - SASSERT(c2.size() <= m_vars.size()); - for (unsigned i = 0; i < m_vars.size(); ++i) { - m_clause[i] = null_literal; - } - for (literal l : c2) { - unsigned pos = m_var_position[l.var()]; - m_clause[pos] = l; - } - for (unsigned j = 0; j < m_vars.size(); ++j) { - literal lit = m_clause[j]; - if (lit == null_literal) { - m_missing.push_back(j); - } - else { - mask |= (m_clause[j].sign() << j); - } - } - return update_combinations(mask); - } - - void lut_finder::set_combination(unsigned mask) { - if (!get_combination(mask)) { - m_combination |= (1ull << mask); - m_num_combinations++; - } - } - - bool lut_finder::update_combinations(unsigned mask) { - unsigned num_missing = m_missing.size(); - for (unsigned k = 0; k < (1ul << num_missing); ++k) { - unsigned mask2 = mask; - for (unsigned i = 0; i < num_missing; ++i) { - if ((k & (1 << i)) != 0) { - mask2 |= 1ul << m_missing[i]; - } - } - set_combination(mask2); - } - return lut_is_defined(m_vars.size()); - } - - bool lut_finder::lut_is_defined(unsigned sz) { - if (m_num_combinations < (1ull << (sz/2))) - return false; - for (unsigned i = sz; i-- > 0; ) { - if (lut_is_defined(i, sz)) - return true; - } - return false; - } - - /** - * \brief check if all output combinations for variable i are defined. - */ - bool lut_finder::lut_is_defined(unsigned i, unsigned sz) { - uint64_t c = m_combination | (m_combination >> (1ull << (uint64_t)i)); - uint64_t m = m_masks[i]; - if (sz < 6) m &= ((1ull << (1ull << sz)) - 1); - return (c & m) == m; - } - - /** - * find variable where it is defined - * convert bit-mask to truth table for that variable. - * remove variable from vars, - * return truth table. - */ - - uint64_t lut_finder::convert_combination(bool_var_vector& vars, bool_var& v) { - SASSERT(lut_is_defined(vars.size())); - unsigned i = 0; - for (i = vars.size(); i-- > 0; ) { - if (lut_is_defined(i, vars.size())) { - break; - } - } - SASSERT(i < vars.size()); - v = vars[i]; - vars.erase(v); - uint64_t r = 0; - uint64_t m = m_masks[i]; - unsigned offset = 0; - // example, if i = 2, then we are examining - // how m_combination evaluates at position xy0uv - // If it evaluates to 0, then it has to evaluate to 1 on position xy1uv - // Offset keeps track of the value of xyuv - // - for (unsigned j = 0; j < 64; ++j) { - if (0 != (m & (1ull << j))) { - if (0 != (m_combination & (1ull << j))) { - r |= 1ull << offset; - } - ++offset; - } - } - return r; - } - - void lut_finder::init_clause_filter() { - m_clause_filters.reset(); - m_clause_filters.resize(s.num_vars()); - init_clause_filter(s.m_clauses); - init_clause_filter(s.m_learned); - } - - void lut_finder::init_clause_filter(clause_vector& clauses) { - for (clause* cp : clauses) { - clause& c = *cp; - if (c.size() <= m_max_lut_size && s.all_distinct(c)) { - clause_filter cf(get_clause_filter(c), cp); - for (literal l : c) { - m_clause_filters[l.var()].push_back(cf); - } - } - } - } - - unsigned lut_finder::get_clause_filter(clause const& c) { - unsigned filter = 0; - for (literal l : c) { - filter |= 1 << ((l.var() % 32)); - } - return filter; - } - - std::ostream& lut_finder::display_mask(std::ostream& out, uint64_t mask, unsigned sz) const { - for (unsigned i = 0; i < sz; ++i) { - out << ((0 != (((mask >> i)) & 0x1)) ? "1" : "0"); - } - return out; - } - -} diff --git a/src/sat/sat_lut_finder.h b/src/sat/sat_lut_finder.h deleted file mode 100644 index d51f40388..000000000 --- a/src/sat/sat_lut_finder.h +++ /dev/null @@ -1,79 +0,0 @@ -/*++ - Copyright (c) 2020 Microsoft Corporation - - Module Name: - - sat_lut_finder.h - - Abstract: - - lut finder - - Author: - - Nikolaj Bjorner 2020-02-03 - - Notes: - - Find LUT with small input fan-ins - - --*/ - -#pragma once - -#include "util/params.h" -#include "util/statistics.h" -#include "sat/sat_clause.h" -#include "sat/sat_types.h" -#include "sat/sat_solver.h" - -namespace sat { - - class lut_finder { - solver& s; - struct clause_filter { - unsigned m_filter; - clause* m_clause; - clause_filter(unsigned f, clause* cp): - m_filter(f), m_clause(cp) {} - }; - unsigned m_max_lut_size; - vector> m_clause_filters; // index of clauses. - uint64_t m_combination; // bit-mask of parities that have been found - unsigned m_num_combinations; - clause_vector m_clauses_to_remove; // remove clauses that become luts - unsigned_vector m_var_position; // position of var in main clause - bool_var_vector m_vars; // reference to variables being tested for LUT - literal_vector m_clause; // reference clause with literals sorted according to main clause - unsigned_vector m_missing; // set of indices not occurring in clause. - uint64_t m_masks[7]; - clause_vector m_removed_clauses; - std::function const& vars, bool_var v)> m_on_lut; - - void set_combination(unsigned mask); - inline bool get_combination(unsigned mask) const { return (m_combination & (1ull << mask)) != 0; } - bool lut_is_defined(unsigned sz); - bool lut_is_defined(unsigned i, unsigned sz); - uint64_t convert_combination(bool_var_vector& vars, bool_var& v); - void check_lut(clause& c); - void add_lut(); - bool extract_lut(literal l1, literal l2); - bool extract_lut(clause& c2); - bool update_combinations(unsigned mask); - void init_clause_filter(); - void init_clause_filter(clause_vector& clauses); - unsigned get_clause_filter(clause const& c); - std::ostream& display_mask(std::ostream& out, uint64_t mask, unsigned sz) const; - - public: - lut_finder(solver& s) : s(s), m_max_lut_size(5) { - memset(m_masks, 0, sizeof(uint64_t)*7); - } - - void set(std::function& f) { m_on_lut = f; } - - unsigned max_lut_size() const { return m_max_lut_size; } - void operator()(clause_vector& clauses); - - }; -} diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 4011b27ca..5c85d087a 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -32,7 +32,6 @@ Revision History: #include "sat/sat_ddfw_wrapper.h" #include "sat/sat_prob.h" #include "sat/sat_anf_simplifier.h" -#include "sat/sat_cut_simplifier.h" #if defined(_MSC_VER) && !defined(_M_ARM) && !defined(_M_ARM64) # include #endif @@ -2105,11 +2104,7 @@ namespace sat { anf(); anf.collect_statistics(m_aux_stats); // TBD: throttle anf_delay based on yield - } - - if (m_cut_simplifier && m_simplifications > m_config.m_cut_delay && !inconsistent()) { - (*m_cut_simplifier)(); - } + } if (m_config.m_inprocess_out.is_non_empty_string()) { std::ofstream fout(m_config.m_inprocess_out.str()); @@ -3707,7 +3702,6 @@ namespace sat { SASSERT(new_v + 1 == m_justification.size()); // there are no active variables that have higher values literal lit = literal(new_v, false); m_user_scope_literals.push_back(lit); - m_cut_simplifier = nullptr; // for simplicity, wipe it out if (m_ext) m_ext->user_push(); TRACE(sat, tout << "user_push: " << lit << "\n";); @@ -3766,9 +3760,6 @@ namespace sat { m_slow_glue_backup.set_alpha(m_config.m_slow_glue_avg); m_trail_avg.set_alpha(m_config.m_slow_glue_avg); - if (m_config.m_cut_simplify && !m_cut_simplifier && m_user_scope_literals.empty()) { - m_cut_simplifier = alloc(cut_simplifier, *this); - } } void solver::collect_param_descrs(param_descrs & d) { @@ -3788,7 +3779,6 @@ namespace sat { m_probing.collect_statistics(st); if (m_ext) m_ext->collect_statistics(st); if (m_local_search) m_local_search->collect_statistics(st); - if (m_cut_simplifier) m_cut_simplifier->collect_statistics(st); st.copy(m_aux_stats); } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index da81c15c7..9aa00ae47 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -39,7 +39,6 @@ Revision History: #include "sat/sat_simplifier.h" #include "sat/sat_scc.h" #include "sat/sat_asymm_branch.h" -#include "sat/sat_cut_simplifier.h" #include "sat/sat_probing.h" #include "sat/sat_mus.h" #include "sat/sat_drat.h" @@ -97,7 +96,6 @@ namespace sat { config m_config; stats m_stats; scoped_ptr m_ext; - scoped_ptr m_cut_simplifier; parallel* m_par; drat m_drat; // DRAT for generating proofs clause_allocator m_cls_allocator[2]; @@ -222,7 +220,6 @@ namespace sat { friend class scc; friend class pb::solver; friend class anf_simplifier; - friend class cut_simplifier; friend class parallel; friend class lookahead; friend class local_search; @@ -450,7 +447,6 @@ namespace sat { bool is_incremental() const { return m_config.m_incremental; } extension* get_extension() const override { return m_ext.get(); } void set_extension(extension* e) override; - cut_simplifier* get_cut_simplifier() override { return m_cut_simplifier.get(); } bool set_root(literal l, literal r); void flush_roots(); typedef std::pair bin_clause; diff --git a/src/sat/sat_solver_core.h b/src/sat/sat_solver_core.h index 5c8b7e315..cc0e6e023 100644 --- a/src/sat/sat_solver_core.h +++ b/src/sat/sat_solver_core.h @@ -23,7 +23,6 @@ Revision History: namespace sat { - class cut_simplifier; class extension; class solver_core { @@ -58,8 +57,6 @@ namespace sat { // hooks for extension solver. really just ba_solver atm. virtual extension* get_extension() const { return nullptr; } virtual void set_extension(extension* e) { if (e) throw default_exception("optional API not supported"); } - - virtual cut_simplifier* get_cut_simplifier() { return nullptr; } }; }; diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 1695f5e41..0fab5105c 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1000,6 +1000,7 @@ namespace arith { } sat::check_result solver::check() { + unsigned level = 2; force_push(); m_model_is_initialized = false; IF_VERBOSE(12, verbose_stream() << "final-check " << lp().get_status() << "\n"); @@ -1042,7 +1043,7 @@ namespace arith { if (!check_delayed_eqs()) return sat::check_result::CR_CONTINUE; - switch (check_nla()) { + switch (check_nla(level)) { case l_true: m_use_nra_model = true; break; @@ -1498,7 +1499,7 @@ namespace arith { } - lbool solver::check_nla() { + lbool solver::check_nla(unsigned level) { if (!m.inc()) { TRACE(arith, tout << "canceled\n";); return l_undef; @@ -1509,7 +1510,7 @@ namespace arith { if (!m_nla->need_check()) return l_true; - lbool r = m_nla->check(); + lbool r = m_nla->check(level); switch (r) { case l_false: add_lemmas(); diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index c11967869..29fa900ba 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -407,7 +407,7 @@ namespace arith { lbool make_feasible(); bool check_delayed_eqs(); lbool check_lia(); - lbool check_nla(); + lbool check_nla(unsigned level); bool check_bv_terms(); bool check_bv_term(app* n); void add_lemmas(); diff --git a/src/sat/smt/array_internalize.cpp b/src/sat/smt/array_internalize.cpp index 225d5d932..5ada91ac7 100644 --- a/src/sat/smt/array_internalize.cpp +++ b/src/sat/smt/array_internalize.cpp @@ -137,10 +137,6 @@ namespace array { add_equiv(eq, sub); break; } - case OP_SET_HAS_SIZE: - case OP_SET_CARD: - ctx.unhandled_function(n->get_decl()); - break; default: UNREACHABLE(); break; @@ -184,10 +180,6 @@ namespace array { break; case OP_SET_SUBSET: break; - case OP_SET_HAS_SIZE: - case OP_SET_CARD: - ctx.unhandled_function(n->get_decl()); - break; default: UNREACHABLE(); break; diff --git a/src/sat/smt/array_solver.h b/src/sat/smt/array_solver.h index 0a7c854fd..fce3efaac 100644 --- a/src/sat/smt/array_solver.h +++ b/src/sat/smt/array_solver.h @@ -16,6 +16,7 @@ Author: --*/ #pragma once +#include "util/union_find.h" #include "ast/ast_trail.h" #include "sat/smt/sat_th.h" #include "ast/array_decl_plugin.h" diff --git a/src/sat/smt/bv_solver.h b/src/sat/smt/bv_solver.h index 9cbee87f9..e059fd12f 100644 --- a/src/sat/smt/bv_solver.h +++ b/src/sat/smt/bv_solver.h @@ -16,6 +16,7 @@ Author: --*/ #pragma once +#include "util/union_find.h" #include "sat/smt/sat_th.h" #include "sat/smt/bv_ackerman.h" #include "ast/rewriter/bit_blaster/bit_blaster.h" diff --git a/src/sat/smt/dt_solver.h b/src/sat/smt/dt_solver.h index 02f1300b8..514e9f79d 100644 --- a/src/sat/smt/dt_solver.h +++ b/src/sat/smt/dt_solver.h @@ -16,6 +16,7 @@ Author: --*/ #pragma once +#include "util/union_find.h" #include "sat/smt/sat_th.h" #include "ast/datatype_decl_plugin.h" #include "ast/array_decl_plugin.h" diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 1d9a72f79..9b620694c 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -644,6 +644,8 @@ namespace euf { return m_egraph.find(m.mk_false()); } + // NB. revisit this for interleaving qsolver with theory solvers based on priorities of + // activities such as calling nlsat as a final check. sat::check_result solver::check() { ++m_stats.m_final_checks; TRACE(euf, s().display(tout);); diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index bf0853c20..abc112592 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -38,7 +38,6 @@ Notes: #include "model/model_v2_pp.h" #include "tactic/tactic.h" #include "ast/converters/generic_model_converter.h" -#include "sat/sat_cut_simplifier.h" #include "sat/sat_drat.h" #include "sat/tactic/goal2sat.h" #include "sat/smt/pb_solver.h" @@ -76,7 +75,6 @@ struct goal2sat::imp : public sat::sat_internalizer { bool m_default_external; bool m_euf = false; bool m_top_level = false; - sat::literal_vector aig_lits; imp(ast_manager & _m, params_ref const & p, sat::solver_core & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external): m(_m), @@ -91,10 +89,6 @@ struct goal2sat::imp : public sat::sat_internalizer { updt_params(p); } - sat::cut_simplifier* aig() { - return m_solver.get_cut_simplifier(); - } - void updt_params(params_ref const & p) { sat_params sp(p); m_ite_extra = p.get_bool("ite_extra", true); @@ -440,16 +434,11 @@ struct goal2sat::imp : public sat::sat_internalizer { m_result_stack.push_back(~l); lits = m_result_stack.end() - num - 1; - if (aig()) { - aig_lits.reset(); - aig_lits.append(num, lits); - } + // remark: mk_clause may perform destructive updated to lits. // I have to execute it after the binary mk_clause above. mk_clause(num+1, lits, mk_tseitin(num+1, lits)); - if (aig()) - aig()->add_or(l, num, aig_lits.data()); - + m_solver.set_phase(~l); m_result_stack.shrink(old_sz); if (sign) @@ -497,14 +486,7 @@ struct goal2sat::imp : public sat::sat_internalizer { } m_result_stack.push_back(l); lits = m_result_stack.end() - num - 1; - if (aig()) { - aig_lits.reset(); - aig_lits.append(num, lits); - } - mk_clause(num+1, lits, mk_tseitin(num+1, lits)); - if (aig()) { - aig()->add_and(l, num, aig_lits.data()); - } + mk_clause(num+1, lits, mk_tseitin(num+1, lits)); m_solver.set_phase(l); if (sign) l.neg(); @@ -546,7 +528,6 @@ struct goal2sat::imp : public sat::sat_internalizer { mk_clause(~t, ~e, l, mk_tseitin(~t, ~e, l)); mk_clause(t, e, ~l, mk_tseitin(t, e, ~l)); } - if (aig()) aig()->add_ite(l, c, t, e); if (sign) l.neg(); @@ -645,7 +626,6 @@ struct goal2sat::imp : public sat::sat_internalizer { mk_clause(~l, ~l1, l2, mk_tseitin(~l, ~l1, l2)); mk_clause(l, l1, l2, mk_tseitin(l, l1, l2)); mk_clause(l, ~l1, ~l2, mk_tseitin(l, ~l1, ~l2)); - if (aig()) aig()->add_iff(l, l1, l2); cache(t, l); if (sign) diff --git a/src/sat/tactic/sat2goal.cpp b/src/sat/tactic/sat2goal.cpp index ab8b8d8ee..95446ee04 100644 --- a/src/sat/tactic/sat2goal.cpp +++ b/src/sat/tactic/sat2goal.cpp @@ -38,7 +38,6 @@ Notes: #include "model/model_v2_pp.h" #include "tactic/tactic.h" #include "ast/converters/generic_model_converter.h" -#include "sat/sat_cut_simplifier.h" #include "sat/sat_drat.h" #include "sat/tactic/sat2goal.h" #include "sat/smt/pb_solver.h" diff --git a/src/smt/CMakeLists.txt b/src/smt/CMakeLists.txt index 01e3a9254..98e79f484 100644 --- a/src/smt/CMakeLists.txt +++ b/src/smt/CMakeLists.txt @@ -49,7 +49,6 @@ z3_add_component(smt smt_value_sort.cpp smt2_extra_cmds.cpp theory_arith.cpp - theory_array_bapa.cpp theory_array_base.cpp theory_array.cpp theory_array_full.cpp diff --git a/src/smt/smt_arith_value.cpp b/src/smt/smt_arith_value.cpp index 068f401a0..bc512350d 100644 --- a/src/smt/smt_arith_value.cpp +++ b/src/smt/smt_arith_value.cpp @@ -157,10 +157,10 @@ namespace smt { return expr_ref(e, m); } - final_check_status arith_value::final_check() { + final_check_status arith_value::final_check(unsigned level) { family_id afid = a.get_family_id(); theory * th = m_ctx->get_theory(afid); - return th->final_check_eh(); + return th->final_check_eh(level); } }; diff --git a/src/smt/smt_arith_value.h b/src/smt/smt_arith_value.h index d802dcb18..09bd03d29 100644 --- a/src/smt/smt_arith_value.h +++ b/src/smt/smt_arith_value.h @@ -47,6 +47,6 @@ namespace smt { expr_ref get_lo(expr* e) const; expr_ref get_up(expr* e) const; expr_ref get_fixed(expr* e) const; - final_check_status final_check(); + final_check_status final_check(unsigned ); }; }; diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index e8f82b44e..91b952cc8 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -4127,16 +4127,18 @@ namespace smt { unsigned old_idx = m_final_check_idx; unsigned num_th = m_theory_set.size(); unsigned range = num_th + 1; + unsigned level = 1, max_level = 1; final_check_status result = FC_DONE; failure f = OK; - do { + while (true) { TRACE(final_check_step, tout << "processing: " << m_final_check_idx << ", result: " << result << "\n";); final_check_status ok; if (m_final_check_idx < num_th) { theory * th = m_theory_set[m_final_check_idx]; IF_VERBOSE(100, verbose_stream() << "(smt.final-check \"" << th->get_name() << "\")\n";); - ok = th->final_check_eh(); + ok = th->final_check_eh(level); + max_level = std::max(max_level, th->num_final_check_levels()); TRACE(final_check_step, tout << "final check '" << th->get_name() << " ok: " << ok << " inconsistent " << inconsistent() << "\n";); if (get_cancel_flag()) { f = CANCELED; @@ -4144,7 +4146,8 @@ namespace smt { } else if (ok == FC_GIVEUP) { f = THEORY; - m_incomplete_theories.push_back(th); + if (!m_incomplete_theories.contains(th)) + m_incomplete_theories.push_back(th); } } else { @@ -4163,10 +4166,13 @@ namespace smt { break; case FC_CONTINUE: return FC_CONTINUE; - break; + } + if (m_final_check_idx == old_idx) { + if (level >= max_level || result == FC_DONE || can_propagate()) + break; + ++level; } } - while (m_final_check_idx != old_idx); TRACE(final_check_step, tout << "result: " << result << "\n";); diff --git a/src/smt/smt_parallel.cpp b/src/smt/smt_parallel.cpp index 2a5869cd0..53019ee9e 100644 --- a/src/smt/smt_parallel.cpp +++ b/src/smt/smt_parallel.cpp @@ -27,9 +27,9 @@ Author: #include "solver/solver_preprocess.h" #include "params/smt_parallel_params.hpp" - #include #include +#include class bounded_pp_exprs { expr_ref_vector const &es; @@ -66,8 +66,6 @@ namespace smt { namespace smt { lbool parallel::param_generator::run_prefix_step() { - if (m.limit().is_canceled()) - return l_undef; IF_VERBOSE(1, verbose_stream() << " PARAM TUNER running prefix step with conflicts " << m_max_prefix_conflicts <<"\n"); ctx->get_fparams().m_max_conflicts = m_max_prefix_conflicts; ctx->get_fparams().m_threads = 1; @@ -126,12 +124,12 @@ namespace smt { if (m.limit().is_canceled()) return; // the conflicts and decisions are cumulative over all cube replays inside the probe_ctx + probe_ctx->get_fparams().m_threads = 1; lbool r = probe_ctx->check(cube.size(), cube.data(), true); IF_VERBOSE(2, verbose_stream() << " PARAM TUNER " << i << ": cube replay result " << r << "\n"); } - unsigned conflicts = probe_ctx->m_stats.m_num_conflicts; - unsigned decisions = probe_ctx->m_stats.m_num_decisions; - double score = conflicts + decisions; + + double score = score_probe(*probe_ctx); if (i == 0) { best_score = score; @@ -158,39 +156,33 @@ namespace smt { void parallel::param_generator::init_rdl_param_state() { smt_params_helper smtp(m_p); - m_param_state.push_back({symbol("smt.arith.auto_config_simplex"), smtp.arith_auto_config_simplex()}); m_param_state.push_back({symbol("smt.arith.bprop_on_pivoted_rows"), smtp.arith_bprop_on_pivoted_rows()}); m_param_state.push_back({symbol("smt.arith.eager_eq_axioms"), smtp.arith_eager_eq_axioms()}); - m_param_state.push_back({symbol("smt.arith.greatest_error_pivot"), smtp.arith_greatest_error_pivot()}); - m_param_state.push_back({symbol("smt.arith.propagate_eqs"), smtp.arith_propagate_eqs()}); - m_param_state.push_back( - {symbol("smt.arith.propagation_mode"), unsigned_value({smtp.arith_propagation_mode(), 0, 2})}); - m_param_state.push_back({symbol("smt.arith.random_initial_value"), smtp.arith_random_initial_value()}); - m_param_state.push_back({symbol("smt.arith.rep_freq"), unsigned_value({smtp.arith_rep_freq(), 0, 100})}); - m_param_state.push_back( - {symbol("smt.arith.simplex_strategy"), unsigned_value({smtp.arith_simplex_strategy(), 0, 2})}); m_param_state.push_back({symbol("smt.delay_units"), smtp.delay_units()}); - m_param_state.push_back( - {symbol("smt.delay_units_threshold"), unsigned_value({smtp.delay_units_threshold(), 16, 64})}); - m_param_state.push_back({symbol("smt.lemma_gc_strategy"), unsigned_value({smtp.lemma_gc_strategy(), 0, 3})}); }; + void parallel::param_generator::init_nia_param_state() { + smt_params_helper smtp(m_p); + m_param_state.push_back({symbol("smt.arith.nl.delay"), unsigned_value({smtp.arith_nl_delay(), 5, 10})}); + m_param_state.push_back({symbol("smt.arith.nl.branching"), smtp.arith_nl_branching()}); + m_param_state.push_back({symbol("smt.arith.nl.grobner"), smtp.arith_nl_grobner()}); + }; + + void parallel::param_generator::init_lia_param_state() { + smt_params_helper smtp(m_p); + m_param_state.push_back({symbol("arith.branch_cut_ratio"), unsigned_value({smtp.arith_branch_cut_ratio(), 1, 8})}); + m_param_state.push_back({symbol("arith.int_eq_branch"), smtp.arith_int_eq_branch()}); + m_param_state.push_back({symbol("arith.ignore_int"), smtp.arith_ignore_int()}); + } + void parallel::param_generator::init_param_state() { if (p.ctx.m_setup.get_logic() == "QF_RDL") { init_rdl_param_state(); - return; + } else if (p.ctx.m_setup.get_logic() == "QF_NIA") { + init_nia_param_state(); + } else if (p.ctx.m_setup.get_logic() == "QF_LIA") { + init_lia_param_state(); } - smt_params_helper smtp(m_p); - m_param_state.push_back({symbol("smt.arith.nl.branching"), smtp.arith_nl_branching()}); - m_param_state.push_back({symbol("smt.arith.nl.cross_nested"), smtp.arith_nl_cross_nested()}); - m_param_state.push_back({symbol("smt.arith.nl.delay"), unsigned_value({smtp.arith_nl_delay(), 5, 10})}); - m_param_state.push_back({symbol("smt.arith.nl.expensive_patching"), smtp.arith_nl_expensive_patching()}); - m_param_state.push_back({symbol("smt.arith.nl.gb"), smtp.arith_nl_grobner()}); - m_param_state.push_back({symbol("smt.arith.nl.horner"), smtp.arith_nl_horner()}); - m_param_state.push_back({symbol("smt.arith.nl.horner_frequency"), unsigned_value({smtp.arith_nl_horner_frequency(), 2, 6})}); - m_param_state.push_back({symbol("smt.arith.nl.optimize_bounds"), smtp.arith_nl_optimize_bounds()}); - m_param_state.push_back({symbol("smt.arith.nl.propagate_linear_monomials"), smtp.arith_nl_propagate_linear_monomials()}); - m_param_state.push_back({symbol("smt.arith.nl.tangents"), smtp.arith_nl_tangents()}); }; params_ref parallel::param_generator::apply_param_values(param_values const &pv) { @@ -242,7 +234,6 @@ namespace smt { while (!m.limit().is_canceled()) { IF_VERBOSE(1, verbose_stream() << " PARAM TUNER running protocol iteration\n"); - ctx->get_fparams().m_max_conflicts = m_max_prefix_conflicts; lbool r = run_prefix_step(); if (m.limit().is_canceled()) @@ -289,20 +280,20 @@ namespace smt { collect_shared_clauses(); check_cube_start: + if (m.limit().is_canceled()) { + b.set_exception("context cancelled"); + return; + } LOG_WORKER(1, " CUBE SIZE IN MAIN LOOP: " << cube.size() << "\n"); // apply current best param state from batch manager params_ref p; b.get_param_state(p); ctx->updt_params(p); + ctx->get_fparams().m_threads = 1; lbool r = check_cube(cube); - if (m.limit().is_canceled()) { - b.set_exception("context cancelled"); - return; - } - switch (r) { case l_undef: { update_max_thread_conflicts(); @@ -338,6 +329,10 @@ namespace smt { LOG_WORKER(1, " found unsat cube\n"); b.backtrack(m_l2g, unsat_core, node); + + if (m_config.m_share_conflicts) + b.collect_clause(m_l2g, id, mk_not(mk_and(unsat_core))); + break; } } @@ -347,19 +342,23 @@ namespace smt { } parallel::worker::worker(unsigned id, parallel &p, expr_ref_vector const &_asms) - : id(id), p(p), b(p.m_batch_manager), m_smt_params(p.ctx.get_fparams()), asms(m), m_g2l(p.ctx.m, m), + : id(id), p(p), b(p.m_batch_manager), asms(m), m_smt_params(p.ctx.get_fparams()), m_g2l(p.ctx.m, m), m_l2g(m, p.ctx.m) { for (auto e : _asms) asms.push_back(m_g2l(e)); LOG_WORKER(1, " created with " << asms.size() << " assumptions\n"); - m_smt_params.m_preprocess = false; ctx = alloc(context, m, m_smt_params, p.ctx.get_params()); + ctx->set_logic(p.ctx.m_setup.get_logic()); context::copy(p.ctx, *ctx, true); ctx->set_random_seed(id + m_smt_params.m_random_seed); // don't share initial units ctx->pop_to_base_lvl(); m_num_shared_units = ctx->assigned_literals().size(); m_num_initial_atoms = ctx->get_num_bool_vars(); + ctx->get_fparams().m_preprocess = false; // avoid preprocessing lemmas that are exchanged + + smt_parallel_params pp(p.ctx.m_params); + m_config.m_inprocessing = pp.inprocessing(); } parallel::param_generator::param_generator(parallel& p) @@ -369,24 +368,30 @@ namespace smt { SASSERT(p.ctx.get_fparams().m_threads == 1); ctx = alloc(context, m, p.ctx.get_fparams(), m_p); context::copy(p.ctx, *ctx, true); - // don't share initial units - ctx->pop_to_base_lvl(); init_param_state(); + if (p.ctx.m_setup.get_logic() == "QF_RDL") { + m_max_prefix_conflicts = 100; + } IF_VERBOSE(1, verbose_stream() << "Initialized parameter generator\n"); } void parallel::worker::share_units() { // Collect new units learned locally by this worker and send to batch manager - ctx->pop_to_base_lvl(); + unsigned sz = ctx->assigned_literals().size(); for (unsigned j = m_num_shared_units; j < sz; ++j) { // iterate only over new literals since last sync literal lit = ctx->assigned_literals()[j]; + + // filter by assign level: do not pop to base level as this destroys the current search state + if (ctx->get_assign_level(lit) > ctx->m_base_lvl) + continue; + if (!ctx->is_relevant(lit.var()) && m_config.m_share_units_relevant_only) continue; if (m_config.m_share_units_initial_only && lit.var() >= m_num_initial_atoms) { - LOG_WORKER(2, " Skipping non-initial unit: " << lit.var() << "\n"); - continue; // skip non-iniial atoms if configured to do so + LOG_WORKER(4, " Skipping non-initial unit: " << lit.var() << "\n"); + continue; // skip non-initial atoms if configured to do so } expr_ref e(ctx->bool_var2expr(lit.var()), ctx->m); // turn literal into a Boolean expression @@ -496,6 +501,9 @@ namespace smt { IF_VERBOSE(1, m_search_tree.display(verbose_stream() << bounded_pp_exprs(core) << "\n");); if (m_search_tree.is_closed()) { m_state = state::is_unsat; + SASSERT(p.ctx.m_unsat_core.empty()); + for (auto e : m_search_tree.get_core_from_root()) + p.ctx.m_unsat_core.push_back(e); cancel_background_threads(); } } @@ -513,6 +521,8 @@ namespace smt { // node->get_status() == status::active // and depth is 'high' enough // then ignore split, and instead set the status of node to open. + ++m_stats.m_num_cubes; + m_stats.m_max_cube_depth = std::max(m_stats.m_max_cube_depth, node->depth() + 1); m_search_tree.split(node, lit, nlit); } @@ -536,7 +546,7 @@ namespace smt { // iterate over new clauses and assert them in the local context for (expr *e : new_clauses) { ctx->assert_expr(e); - LOG_WORKER(2, " asserting shared clause: " << mk_bounded_pp(e, m, 3) << "\n"); + LOG_WORKER(4, " asserting shared clause: " << mk_bounded_pp(e, m, 3) << "\n"); } } diff --git a/src/smt/smt_parallel.h b/src/smt/smt_parallel.h index 6d714a306..8ad539bd2 100644 --- a/src/smt/smt_parallel.h +++ b/src/smt/smt_parallel.h @@ -36,8 +36,8 @@ namespace smt { class parallel { context& ctx; unsigned num_threads; - bool m_should_tune_params; - bool m_should_run_parallel; + bool m_should_tune_params = true; + bool m_should_run_parallel = true; struct shared_clause { unsigned source_worker_id; @@ -62,7 +62,7 @@ namespace smt { ast_manager& m; parallel& p; std::mutex mux; - state m_state = state::is_running; + state m_state; stats m_stats; params_ref m_param_state; using node = search_tree::node; @@ -137,8 +137,8 @@ namespace smt { ast_manager m; scoped_ptr ctx; - unsigned N = 4; // number of prefix permutations to test (including current) - unsigned m_max_prefix_conflicts = 1000; // todo- maybe make this adaptive to grow up to 1000 but start with a smaller base + unsigned N = 5; // number of prefix permutations to test (including current) + unsigned m_max_prefix_conflicts = 1000; scoped_ptr m_prefix_solver; vector m_recorded_cubes; @@ -148,6 +148,8 @@ namespace smt { params_ref apply_param_values(param_values const &pv); void init_param_state(); + void init_nia_param_state(); + void init_lia_param_state(); void init_rdl_param_state(); param_values mutate_param_state(); void print_param_values(param_values const &pv) { @@ -164,6 +166,35 @@ namespace smt { IF_VERBOSE(1, verbose_stream() << "\n"); } + double score_probe(context& probe_ctx) { + auto& st = probe_ctx.m_stats; + auto logic = p.ctx.m_setup.get_logic(); + + if (logic == "QF_RDL") { + return + st.m_num_conflicts + + 4.0 * st.m_num_final_checks + - 6.0 * st.m_num_minimized_lits; + } + else if (logic == "QF_LIA") { + return + 2.0 * st.m_num_conflicts + + 1.5 * st.m_num_decisions + + 0.5 * st.m_num_restarts + - 1.0 * st.m_num_add_eq; + } + else if (logic == "QF_LIA") { + return + 4.0 * st.m_num_final_checks + + 2.0 * st.m_num_conflicts + - 1.5 * st.m_num_minimized_lits; + } else { + return + st.m_num_conflicts + + st.m_num_decisions; + } + } + public: param_generator(parallel &p); lbool run_prefix_step(); @@ -180,6 +211,7 @@ namespace smt { struct config { unsigned m_threads_max_conflicts = 1000; bool m_share_units = true; + bool m_share_conflicts = true; bool m_share_units_relevant_only = true; bool m_share_units_initial_only = true; double m_max_conflict_mul = 1.5; diff --git a/src/smt/smt_relevancy.cpp b/src/smt/smt_relevancy.cpp index 48fa3657d..80fc9bc8d 100644 --- a/src/smt/smt_relevancy.cpp +++ b/src/smt/smt_relevancy.cpp @@ -385,7 +385,6 @@ namespace smt { case l_undef: break; case l_true: { - expr* true_arg = nullptr; auto arg0 = n->get_arg(0); auto arg1 = n->get_arg(1); if (m_context.find_assignment(arg0) == l_false) { diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index d655316ed..a27bc99f2 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -510,7 +510,7 @@ namespace smt { TRACE(setup, tout << "AUFLIA\n";); m_params.setup_AUFLIA(simple_array); TRACE(setup, tout << "max_eager_multipatterns: " << m_params.m_qi_max_eager_multipatterns << "\n";); - m_context.register_plugin(alloc(smt::theory_i_arith, m_context)); + setup_i_arith(); setup_arrays(); } @@ -646,7 +646,7 @@ namespace smt { m_context.register_plugin(alloc(smt::theory_idl, m_context)); else m_context.register_plugin(alloc(smt::theory_rdl, m_context)); - } + } break; case arith_solver_id::AS_DENSE_DIFF_LOGIC: m_params.m_arith_eq2ineq = true; diff --git a/src/smt/smt_theory.h b/src/smt/smt_theory.h index 8e23a6644..6158421a2 100644 --- a/src/smt/smt_theory.h +++ b/src/smt/smt_theory.h @@ -315,10 +315,21 @@ namespace smt { a truth value to all boolean variables and no inconsistency was detected. */ - virtual final_check_status final_check_eh() { + virtual final_check_status final_check_eh(unsigned level) { return FC_DONE; } + /** + * \brief This method signals the number of priority levels a theory supports for final checks. + * The first level are for the cheapest final check invocations. + * The levels after that are for more expensive final checks. + * This approach emulates a priority queue of actions taken at final check where the expensive + * checks are deferred. + */ + virtual unsigned num_final_check_levels() const { + return 1; + } + /** \brief Parametric theories (e.g. Arrays) should implement this method. See example in context::is_shared diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 3cfb870a1..f1ec345b1 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -679,7 +679,7 @@ namespace smt { */ bool m_liberal_final_check = true; final_check_status final_check_core(); - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; bool can_propagate() override; void propagate() override; diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 0a90495c7..44e373764 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -1535,7 +1535,7 @@ namespace smt { } template - final_check_status theory_arith::final_check_eh() { + final_check_status theory_arith::final_check_eh(unsigned level) { TRACE(arith_eq_adapter_info, m_arith_eq_adapter.display_already_processed(tout);); TRACE(arith, display(tout);); diff --git a/src/smt/theory_array.cpp b/src/smt/theory_array.cpp index 4da9e5bca..816e542e0 100644 --- a/src/smt/theory_array.cpp +++ b/src/smt/theory_array.cpp @@ -359,7 +359,7 @@ namespace smt { SASSERT(m_find.get_num_vars() == get_num_vars()); } - final_check_status theory_array::final_check_eh() { + final_check_status theory_array::final_check_eh(unsigned) { m_final_check_idx++; final_check_status r = FC_DONE; if (m_params.m_array_lazy_ieq) { @@ -407,10 +407,45 @@ namespace smt { var_data * d = m_var_data[v]; if (d->m_prop_upward && instantiate_axiom2b_for(v)) r = FC_CONTINUE; - } + } return r; } + + bool theory_array::has_unitary_domain(app *array_term) { + SASSERT(is_array_sort(array_term)); + sort *s = array_term->get_sort(); + unsigned dim = get_dimension(s); + parameter const *params = s->get_info()->get_parameters(); + for (unsigned i = 0; i < dim; ++i) { + SASSERT(params[i].is_ast()); + sort *d = to_sort(params[i].get_ast()); + if (d->is_infinite() || d->is_very_big() || 1 != d->get_num_elements().size()) + return false; + } + return true; + } + + bool theory_array::has_large_domain(app *array_term, rational& sz) { + SASSERT(is_array_sort(array_term)); + sort *s = array_term->get_sort(); + unsigned dim = get_dimension(s); + parameter const *params = s->get_info()->get_parameters(); + sz = rational(1); + for (unsigned i = 0; i < dim; ++i) { + SASSERT(params[i].is_ast()); + sort *d = to_sort(params[i].get_ast()); + if (d->is_infinite() || d->is_very_big()) { + return true; + } + sz *= rational(d->get_num_elements().size(), rational::ui64()); + if (sz >= rational(1 << 14)) { + return true; + } + } + return false; + } + final_check_status theory_array::mk_interface_eqs_at_final_check() { unsigned n = mk_interface_eqs(); m_stats.m_num_eq_splits += n; diff --git a/src/smt/theory_array.h b/src/smt/theory_array.h index 9fc9dd44d..6e840e342 100644 --- a/src/smt/theory_array.h +++ b/src/smt/theory_array.h @@ -62,7 +62,7 @@ namespace smt { void relevant_eh(app * n) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; void reset_eh() override; void init_search_eh() override; @@ -90,6 +90,8 @@ namespace smt { virtual final_check_status assert_delayed_axioms(); final_check_status mk_interface_eqs_at_final_check(); + bool has_large_domain(app *array_term, rational& domain_size); + bool has_unitary_domain(app *array_term); static void display_ids(std::ostream & out, unsigned n, enode * const * v); public: diff --git a/src/smt/theory_array_bapa.cpp b/src/smt/theory_array_bapa.cpp deleted file mode 100644 index 72aea761d..000000000 --- a/src/smt/theory_array_bapa.cpp +++ /dev/null @@ -1,644 +0,0 @@ -/*++ -Copyright (c) 2019 Microsoft Corporation - -Module Name: - - theory_array_bapa.cpp - -Abstract: - - Saturation procedure for BAPA predicates. - Assume there is a predicate - - Size(S, n) for S : Array(T, Bool) and n : Int - - The predicate is true if S is a set of size n. - - - Size(S, n), Size(T, m) - S, T are intersecting. n != m or S != T -D --------------------------------------------------------- - Size(S, n) => Size(S\T, k1), Size(S n T, k2), n = k1 + k2 - Size(T, m) => Size(T\S, k3), SIze(S n T, k2), m = k2 + k3 - - Size(S, n) -P -------------------- - Size(S, n) => n >= 0 - - Size(S, n), is infinite domain -B ------------------------------ - Size(S, n) => default(S) = false - - Size(S, n), Size(S, m) -F -------------------------------- - Size(S, n), Size(S, m) => n = m - - Fixing values during final check: - Size(S, n) -V ------------------- - assume value(n) = n - - Size(S, n), S[i1], ..., S[ik] -O ------------------------------- - ~distinct(i1, ... ik) or n >= k - - Size(S,n) -Ak -------------------------------------------------- - S[i1] & .. & S[ik] & distinct(i1, .., ik) or n < k - -Q: Is this sufficient? Axiom A1 could be adjusted to add new elements i' until there are k witnesses for Size(S, k). -This is quite bad when k is very large. Instead rely on stably infiniteness or other domain properties of the theories. - -When A is finite domain, or there are quantifiers there could be constraints that force domain sizes so domain sizes may have -to be enforced. A succinct way would be through domain comprehension assertions. - -Finite domains: - - Size(S, n), is finite domain - ---------------------------- - S <= |A| - - Size(S, n), !S[i1], .... !S[ik], S is finite domain - ---------------------------------------------------------- - default(S) = false or ~distinct(i1,..,ik) or |A| - k <= n - - - ~Size(S, m) is negative on all occurrences, S is finite domain - --------------------------------------------------------------- - Size(S, n) n fresh. - - Model construction for infinite domains when all Size(S, m) are negative for S. - -Author: - - Nikolaj Bjorner 2019-04-13 - -Revision History: - - */ - -#include "ast/ast_util.h" -#include "ast/ast_pp.h" -#include "ast/rewriter/array_rewriter.h" -#include "smt/smt_context.h" -#include "smt/smt_arith_value.h" -#include "smt/theory_array_full.h" -#include "smt/theory_array_bapa.h" - -#if 0 -- set of native select terms that are true -- set of auxiliary select terms. -- n1, n2, n3, n4. -- a1, a2, a3, a4, a5. -- -- add select terms, such that first -#endif - -namespace smt { - - class theory_array_bapa::imp { - struct sz_info { - bool m_is_leaf = true; // has it been split into disjoint subsets already? - rational m_size = rational::minus_one(); // set to >= integer if fixed in final check, otherwise -1 - obj_map m_selects; - }; - - typedef std::pair func_decls; - - ast_manager& m; - theory_array_full& th; - arith_util m_arith; - array_util m_autil; - th_rewriter m_rw; - arith_value m_arith_value; - ast_ref_vector m_pinned; - obj_map m_sizeof; - obj_map m_size_limit; - obj_map m_index_skolems; - obj_map m_size_limit_sort2skolems; - unsigned m_max_set_enumeration; - - context& ctx() { return th.get_context(); } - - void reset() { - for (auto& kv : m_sizeof) { - dealloc(kv.m_value); - } - } - - bool is_true(expr* e) { return is_true(ctx().get_literal(e)); } - bool is_true(enode* e) { return is_true(e->get_expr()); } - bool is_true(literal l) { return ctx().is_relevant(l) && ctx().get_assignment(l) == l_true; } - bool is_leaf(sz_info& i) const { return i.m_is_leaf; } - bool is_leaf(sz_info* i) const { return is_leaf(*i); } - enode* get_root(expr* e) { return ctx().get_enode(e)->get_root(); } - bool is_select(enode* n) { return th.is_select(n); } - app_ref mk_select(expr* a, expr* i) { expr* args[2] = { a, i }; return app_ref(m_autil.mk_select(2, args), m); } - literal get_literal(expr* e) { return ctx().get_literal(e); } - literal mk_literal(expr* e) { expr_ref _e(e, m); if (!ctx().e_internalized(e)) ctx().internalize(e, false); literal lit = get_literal(e); ctx().mark_as_relevant(lit); return lit; } - literal mk_eq(expr* a, expr* b) { - expr_ref _a(a, m), _b(b, m); - literal lit = th.mk_eq(a, b, false); - ctx().mark_as_relevant(lit); - return lit; - } - void mk_th_axiom(literal l1, literal l2) { - literal lits[2] = { l1, l2 }; - mk_th_axiom(2, lits); - } - void mk_th_axiom(literal l1, literal l2, literal l3) { - literal lits[3] = { l1, l2, l3 }; - mk_th_axiom(3, lits); - } - void mk_th_axiom(unsigned n, literal* lits) { - TRACE(card, ctx().display_literals_verbose(tout, n, lits) << "\n";); - IF_VERBOSE(10, ctx().display_literals_verbose(verbose_stream(), n, lits) << "\n"); - ctx().mk_th_axiom(th.get_id(), n, lits); - } - - void update_indices() { - for (auto const& kv : m_sizeof) { - app* k = kv.m_key; - sz_info& v = *kv.m_value; - v.m_selects.reset(); - if (is_true(k) && is_leaf(v)) { - enode* set = get_root(k->get_arg(0)); - for (enode* parent : enode::parents(set)) { - if (is_select(parent) && parent->get_arg(0)->get_root() == set) { - if (is_true(parent)) { - v.m_selects.insert(parent->get_arg(1)->get_root(), parent->get_expr()); - } - } - } - } - } - } - - /** - F: Size(S, k1) & Size(S, k2) => k1 = k2 - */ - lbool ensure_functional() { - lbool result = l_true; - obj_map parents; - for (auto const& kv : m_sizeof) { - app* sz1 = kv.m_key; - if (!is_true(sz1)) { - continue; - } - enode* r = get_root(sz1->get_arg(0)); - app* sz2 = nullptr; - if (parents.find(r, sz2)) { - expr* k1 = sz1->get_arg(1); - expr* k2 = sz2->get_arg(1); - if (get_root(k1) != get_root(k2)) { - mk_th_axiom(~get_literal(sz1), ~get_literal(sz2), mk_eq(k1, k2)); - result = l_false; - } - } - else { - parents.insert(r, sz1); - } - } - return result; - } - - /** - Enforce D - */ - lbool ensure_disjoint() { - auto i = m_sizeof.begin(), end = m_sizeof.end(); - for (; i != end; ++i) { - auto& kv = *i; - if (!kv.m_value->m_is_leaf) { - continue; - } - for (auto j = i; ++j != end; ) { - if (j->m_value->m_is_leaf && !ensure_disjoint(i->m_key, j->m_key)) { - return l_false; - } - } - } - return l_true; - } - - bool ensure_disjoint(app* sz1, app* sz2) { - sz_info& i1 = *m_sizeof[sz1]; - sz_info& i2 = *m_sizeof[sz2]; - SASSERT(i1.m_is_leaf); - SASSERT(i2.m_is_leaf); - expr* s = sz1->get_arg(0); - expr* t = sz2->get_arg(0); - if (s->get_sort() != t->get_sort()) { - return true; - } - enode* r1 = get_root(s); - enode* r2 = get_root(t); - if (r1 == r2) { - return true; - } - if (!ctx().is_diseq(r1, r2) && ctx().assume_eq(r1, r2)) { - return false; - } - if (do_intersect(i1.m_selects, i2.m_selects)) { - add_disjoint(sz1, sz2); - return false; - } - return true; - } - - bool do_intersect(obj_map const& s, obj_map const& t) const { - if (s.size() > t.size()) { - return do_intersect(t, s); - } - for (auto const& idx : s) - if (t.contains(idx.m_key)) - return true; - return false; - } - - void add_disjoint(app* sz1, app* sz2) { - sz_info& i1 = *m_sizeof[sz1]; - sz_info& i2 = *m_sizeof[sz2]; - SASSERT(i1.m_is_leaf); - SASSERT(i2.m_is_leaf); - expr* t = sz1->get_arg(0); - expr* s = sz2->get_arg(0); - expr_ref tms = mk_subtract(t, s); - expr_ref smt = mk_subtract(s, t); - expr_ref tns = mk_intersect(t, s); -#if 0 - std::cout << tms << "\n"; - std::cout << smt << "\n"; - std::cout << tns << "\n"; -#endif -#if 0 - if (tns == sz1) { - std::cout << "SEEN " << tms << "\n"; - } - if (tns == sz2) { - std::cout << "SEEN " << smt << "\n"; - } -#endif - ctx().push_trail(value_trail(i1.m_is_leaf, false)); - ctx().push_trail(value_trail(i2.m_is_leaf, false)); - expr_ref k1(m), k2(m), k3(m); - expr_ref sz_tms(m), sz_tns(m), sz_smt(m); - k1 = m_autil.mk_card(tms); - k2 = m_autil.mk_card(tns); - k3 = m_autil.mk_card(smt); - sz_tms = m_autil.mk_has_size(tms, k1); - sz_tns = m_autil.mk_has_size(tns, k2); - sz_smt = m_autil.mk_has_size(smt, k3); - propagate(sz1, sz_tms); - propagate(sz1, sz_tns); - propagate(sz2, sz_smt); - propagate(sz2, sz_tns); - propagate(sz1, mk_eq(k1 + k2, sz1->get_arg(1))); - propagate(sz2, mk_eq(k3 + k2, sz2->get_arg(1))); - } - - expr_ref mk_subtract(expr* t, expr* s) { - expr_ref d(m_autil.mk_setminus(t, s), m); - m_rw(d); - return d; - } - - expr_ref mk_intersect(expr* t, expr* s) { - expr_ref i(m_autil.mk_intersection(t, s), m); - m_rw(i); - return i; - } - - void propagate(expr* assumption, expr* conseq) { - propagate(assumption, mk_literal(conseq)); - } - - void propagate(expr* assumption, literal conseq) { - mk_th_axiom(~mk_literal(assumption), conseq); - } - - /** - Enforce V - */ - lbool ensure_values_assigned() { - lbool result = l_true; - for (auto const& kv : m_sizeof) { - app* k = kv.m_key; - sz_info& i = *kv.m_value; - if (is_leaf(&i)) { - rational value; - expr* sz = k->get_arg(1); - if (!m_arith_value.get_value(sz, value)) { - return l_undef; - } - literal lit = mk_eq(sz, m_arith.mk_int(value)); - if (lit != true_literal && is_true(lit)) { - ctx().push_trail(value_trail(i.m_size, value)); - continue; - } - ctx().set_true_first_flag(lit.var()); - result = l_false; - } - } - return result; - } - - /** - Enforce Ak, - */ - lbool ensure_non_empty() { - for (auto const& kv : m_sizeof) { - sz_info& i = *kv.m_value; - app* set_sz = kv.m_key; - if (is_true(set_sz) && is_leaf(i) && i.m_selects.size() < i.m_size) { - expr* set = set_sz->get_arg(0); - expr_ref le(m_arith.mk_le(set_sz->get_arg(1), m_arith.mk_int(0)), m); - literal le_lit = mk_literal(le); - literal sz_lit = mk_literal(set_sz); - for (unsigned k = i.m_selects.size(); rational(k) < i.m_size; ++k) { - expr_ref idx = mk_index_skolem(set_sz, set, k); - app_ref sel(mk_select(set, idx), m); - mk_th_axiom(~sz_lit, le_lit, mk_literal(sel)); - TRACE(card, tout << idx << " " << sel << " " << i.m_size << "\n";); - } - return l_false; - } - } - return l_true; - } - - // create skolem function that is injective on integers (ensures uniqueness). - expr_ref mk_index_skolem(app* sz, expr* a, unsigned n) { - func_decls fg; - sort* s = a->get_sort(); - if (!m_index_skolems.find(s, fg)) { - sort* idx_sort = get_array_domain(s, 0); - sort* dom1[2] = { s, m_arith.mk_int() }; - sort* dom2[1] = { idx_sort }; - func_decl* f = m.mk_fresh_func_decl("to-index", "", 2, dom1, idx_sort); - func_decl* g = m.mk_fresh_func_decl("from-index", "", 1, dom2, m_arith.mk_int()); - fg = std::make_pair(f, g); - m_index_skolems.insert(s, fg); - m_pinned.push_back(f); - m_pinned.push_back(g); - m_pinned.push_back(s); - } - expr_ref nV(m_arith.mk_int(n), m); - expr_ref result(m.mk_app(fg.first, a, nV), m); - expr_ref le(m_arith.mk_le(sz->get_arg(1), nV), m); - expr_ref fr(m.mk_app(fg.second, result), m); - // set-has-size(a, k) => k <= n or g(f(a,n)) = n - mk_th_axiom(~mk_literal(sz), mk_literal(le), mk_eq(nV, fr)); - return result; - } - - - /** - Enforce O - */ - lbool ensure_no_overflow() { - for (auto const& kv : m_sizeof) { - if (is_true(kv.m_key) && is_leaf(kv.m_value)) { - lbool r = ensure_no_overflow(kv.m_key, *kv.m_value); - if (r != l_true) return r; - } - } - return l_true; - } - - lbool ensure_no_overflow(app* sz, sz_info& info) { - SASSERT(!info.m_size.is_neg()); - if (info.m_size < info.m_selects.size()) { - for (auto i = info.m_selects.begin(), e = info.m_selects.end(); i != e; ++i) { - for (auto j = i; ++j != e; ) { - if (ctx().assume_eq(i->m_key, j->m_key)) { - return l_false; - } - } - } - // if all is exhausted, then add axiom: set-has-size(s, n) & s[indices] & all-diff(indices) => n >= |indices| - literal_vector lits; - lits.push_back(~mk_literal(sz)); - for (auto const& kv : info.m_selects) { - lits.push_back(~mk_literal(kv.m_value)); - } - if (info.m_selects.size() > 1) { - ptr_vector args; - for (auto const& kv : info.m_selects) { - args.push_back(kv.m_key->get_expr()); - } - if (info.m_selects.size() == 2) { - lits.push_back(mk_eq(args[0], args[1])); - } - else { - expr_ref diff(m.mk_distinct_expanded(args.size(), args.data()), m); - lits.push_back(~mk_literal(diff)); - } - } - expr_ref ge(m_arith.mk_ge(sz->get_arg(1), m_arith.mk_int(info.m_selects.size())), m); - lits.push_back(mk_literal(ge)); - mk_th_axiom(lits.size(), lits.data()); - return l_false; - } - return l_true; - } - - class remove_sz : public trail { - ast_manager& m; - obj_map & m_table; - app* m_obj; - public: - remove_sz(ast_manager& m, obj_map& tab, app* t): m(m), m_table(tab), m_obj(t) { } - void undo() override { m.dec_ref(m_obj); dealloc(m_table[m_obj]); m_table.remove(m_obj); } - }; - - std::ostream& display(std::ostream& out) { - for (auto const& kv : m_sizeof) { - display(out << mk_pp(kv.m_key, m) << ": ", *kv.m_value); - } - return out; - } - - std::ostream& display(std::ostream& out, sz_info& sz) { - return out << (sz.m_is_leaf ? "leaf": "") << " size: " << sz.m_size << " selects: " << sz.m_selects.size() << "\n"; - } - - public: - imp(theory_array_full& th): - m(th.get_manager()), - th(th), - m_arith(m), - m_autil(m), - m_rw(m), - m_arith_value(m), - m_pinned(m) - { - context& ctx = th.get_context(); - m_arith_value.init(&ctx); - m_max_set_enumeration = 4; - } - - ~imp() { - reset(); - } - - void internalize_term(app* term) { - if (th.is_set_has_size(term)) { - internalize_size(term); - } - else if (th.is_set_card(term)) { - internalize_card(term); - } - } - - /** - * Size(S, n) => n >= 0, default(S) = false - */ - void internalize_size(app* term) { - SASSERT(ctx().e_internalized(term)); - literal lit = mk_literal(term); - expr* s = term->get_arg(0); - expr* n = term->get_arg(1); - mk_th_axiom(~lit, mk_literal(m_arith.mk_ge(n, m_arith.mk_int(0)))); - sort_size const& sz = s->get_sort()->get_num_elements(); - if (sz.is_infinite()) { - mk_th_axiom(~lit, mk_eq(th.mk_default(s), m.mk_false())); - } - else { - warning_msg("correct handling of finite domains is TBD"); - // add upper bound on size of set. - // add case where default(S) = true, and add negative elements. - } - m_sizeof.insert(term, alloc(sz_info)); - m_size_limit.insert(s, rational(2)); - assert_size_limit(s, n); - m.inc_ref(term); - ctx().push_trail(remove_sz(m, m_sizeof, term)); - } - - /** - \brief whenever there is a cardinality function, it includes an axiom - that entails the set is finite. - */ - void internalize_card(app* term) { - SASSERT(ctx().e_internalized(term)); - app_ref has_size(m_autil.mk_has_size(term->get_arg(0), term), m); - literal lit = mk_literal(has_size); - ctx().assign(lit, nullptr); - } - - lbool trace_call(char const* msg, lbool r) { - if (r != l_true) { - IF_VERBOSE(2, verbose_stream() << msg << "\n"); - } - return r; - } - - final_check_status final_check() { - final_check_status st = m_arith_value.final_check(); - if (st != FC_DONE) return st; - lbool r = trace_call("ensure_functional", ensure_functional()); - if (r == l_true) update_indices(); - if (r == l_true) r = trace_call("ensure_disjoint", ensure_disjoint()); - if (r == l_true) r = trace_call("ensure_values_assigned", ensure_values_assigned()); - if (r == l_true) r = trace_call("ensure_non_empty", ensure_non_empty()); - if (r == l_true) r = trace_call("ensure_no_overflow", ensure_no_overflow()); - CTRACE(card, r != l_true, display(tout);); - switch (r) { - case l_true: - return FC_DONE; - case l_false: - return FC_CONTINUE; - case l_undef: - return FC_GIVEUP; - } - return FC_GIVEUP; - } - - void init_model() { - for (auto const& kv : m_sizeof) { - sz_info& i = *kv.m_value; - app* sz = kv.m_key; - if (is_true(sz) && is_leaf(i) && rational(i.m_selects.size()) != i.m_size) { - warning_msg("models for BAPA is TBD"); - break; - } - } - } - - bool should_research(expr_ref_vector & unsat_core) { - expr* set, *sz; - for (auto & e : unsat_core) { - if (is_app(e) && is_size_limit(to_app(e), set, sz)) { - inc_size_limit(set, sz); - return true; - } - } - return false; - } - - void inc_size_limit(expr* set, expr* sz) { - IF_VERBOSE(2, verbose_stream() << "inc value " << mk_pp(set, m) << "\n"); - m_size_limit[set] *= rational(2); - assert_size_limit(set, sz); - } - - bool is_size_limit(app* e, expr*& set, expr*& sz) { - func_decl* d = nullptr; - if (e->get_num_args() > 0 && m_size_limit_sort2skolems.find(e->get_arg(0)->get_sort(), d) && d == e->get_decl()) { - set = e->get_arg(0); - sz = e->get_arg(1); - return true; - } - else { - return false; - } - } - - // has-size(s,n) & size-limit(s, n, k) => n <= k - - app_ref mk_size_limit(expr* set, expr* sz) { - func_decl* sk = nullptr; - sort* s = set->get_sort(); - if (!m_size_limit_sort2skolems.find(s, sk)) { - sort* dom[3] = { s, m_arith.mk_int(), m_arith.mk_int() }; - sk = m.mk_fresh_func_decl("value-limit", "", 3, dom, m.mk_bool_sort()); - m_pinned.push_back(sk); - m_size_limit_sort2skolems.insert(s, sk); - } - return app_ref(m.mk_app(sk, set, sz, m_arith.mk_int(m_size_limit[set])), m); - } - - void assert_size_limit(expr* set, expr* sz) { - app_ref set_sz(m_autil.mk_has_size(set, sz), m); - app_ref lim(m_arith.mk_int(m_size_limit[set]), m); - app_ref size_limit = mk_size_limit(set, sz); - mk_th_axiom(~mk_literal(set_sz), ~mk_literal(size_limit), mk_literal(m_arith.mk_le(sz, lim))); - } - - void add_theory_assumptions(expr_ref_vector & assumptions) { - for (auto const& kv : m_sizeof) { - expr* set = kv.m_key->get_arg(0); - expr* sz = kv.m_key->get_arg(1); - assumptions.push_back(mk_size_limit(set, sz)); - } - TRACE(card, tout << "ASSUMPTIONS: " << assumptions << "\n";); - } - - }; - - theory_array_bapa::theory_array_bapa(theory_array_full& th) { m_imp = alloc(imp, th); } - - theory_array_bapa::~theory_array_bapa() { dealloc(m_imp); } - - void theory_array_bapa::internalize_term(app* term) { m_imp->internalize_term(term); } - - final_check_status theory_array_bapa::final_check() { return m_imp->final_check(); } - - void theory_array_bapa::init_model() { m_imp->init_model(); } - - bool theory_array_bapa::should_research(expr_ref_vector & unsat_core) { return m_imp->should_research(unsat_core); } - - void theory_array_bapa::add_theory_assumptions(expr_ref_vector & assumptions) { m_imp->add_theory_assumptions(assumptions); } - -} diff --git a/src/smt/theory_array_bapa.h b/src/smt/theory_array_bapa.h deleted file mode 100644 index 98fcdd148..000000000 --- a/src/smt/theory_array_bapa.h +++ /dev/null @@ -1,43 +0,0 @@ -/*++ -Copyright (c) 2019 Microsoft Corporation - -Module Name: - - theory_array_bapa.h - -Abstract: - - - -Author: - - Nikolaj Bjorner 2019-04-13 - -Revision History: - ---*/ -#pragma once - -#include "ast/ast.h" -#include "smt/smt_theory.h" - -namespace smt { - - class theory_array_full; - - class theory_array_bapa { - class imp; - imp* m_imp; - public: - theory_array_bapa(theory_array_full& th); - ~theory_array_bapa(); - void internalize_term(app* term); - final_check_status final_check(); - void init_model(); - bool should_research(expr_ref_vector & unsat_core); - void add_theory_assumptions(expr_ref_vector & assumptions); - }; - -}; - - diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index 9d1775a3b..6e04c0290 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -681,7 +681,6 @@ namespace smt { collect_defaults(); collect_selects(); propagate_selects(); - if (m_bapa) m_bapa->init_model(); } /** @@ -699,7 +698,7 @@ namespace smt { if (!ctx.is_relevant(n)) continue; - if (is_store(n) || is_const(n) || is_default(n) || is_set_has_size(n)) + if (is_store(n) || is_const(n) || is_default(n)) return false; } return true; diff --git a/src/smt/theory_array_base.h b/src/smt/theory_array_base.h index 58d143ff1..9a6a6a173 100644 --- a/src/smt/theory_array_base.h +++ b/src/smt/theory_array_base.h @@ -19,14 +19,12 @@ Revision History: #pragma once #include "smt/smt_theory.h" -#include "smt/theory_array_bapa.h" #include "ast/array_decl_plugin.h" #include "model/array_factory.h" namespace smt { class theory_array_base : public theory { - friend class theory_array_bapa; protected: bool m_found_unsupported_op; unsigned m_array_weak_head; @@ -47,8 +45,6 @@ namespace smt { bool is_as_array(app const * n) const { return n->is_app_of(get_id(), OP_AS_ARRAY); } bool is_array_sort(sort const* s) const { return s->is_sort_of(get_id(), ARRAY_SORT); } bool is_array_sort(app const* n) const { return is_array_sort(n->get_sort()); } - bool is_set_has_size(app const* n) const { return n->is_app_of(get_id(), OP_SET_HAS_SIZE); } - bool is_set_card(app const* n) const { return n->is_app_of(get_id(), OP_SET_CARD); } bool is_store(enode const * n) const { return is_store(n->get_expr()); } bool is_map(enode const* n) const { return is_map(n->get_expr()); } @@ -57,8 +53,6 @@ namespace smt { bool is_as_array(enode const * n) const { return is_as_array(n->get_expr()); } bool is_default(enode const* n) const { return is_default(n->get_expr()); } bool is_array_sort(enode const* n) const { return is_array_sort(n->get_expr()); } - bool is_set_has_size(enode const* n) const { return is_set_has_size(n->get_expr()); } - bool is_set_carde(enode const* n) const { return is_set_card(n->get_expr()); } bool is_select_arg(enode* r); app * mk_select(unsigned num_args, expr * const * args); @@ -74,7 +68,6 @@ namespace smt { enode_pair_vector m_axiom2_todo; enode_pair_vector m_extensionality_todo; enode_pair_vector m_congruent_todo; - scoped_ptr m_bapa; void assert_axiom(unsigned num_lits, literal * lits); void assert_axiom(literal l1, literal l2); diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 40220e830..530a13524 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -271,7 +271,7 @@ namespace smt { return theory_array::internalize_term(n); } - if (!is_const(n) && !is_default(n) && !is_map(n) && !is_as_array(n) && !is_set_has_size(n) && !is_set_card(n)) { + if (!is_const(n) && !is_default(n) && !is_map(n) && !is_as_array(n)) { if (!is_array_ext(n)) found_unsupported_op(n); return false; @@ -295,12 +295,6 @@ namespace smt { mk_var(arg0); } } - else if (is_set_has_size(n) || is_set_card(n)) { - if (!m_bapa) { - m_bapa = alloc(theory_array_bapa, *this); - } - m_bapa->internalize_term(n); - } enode* node = ctx.get_enode(n); if (!is_attached_to_var(node)) { @@ -339,6 +333,8 @@ namespace smt { SASSERT(n->get_num_args() == 2); instantiate_extensionality(ctx.get_enode(n->get_arg(0)), ctx.get_enode(n->get_arg(1))); } + if (!ctx.relevancy()) + relevant_eh(n); return true; } @@ -449,11 +445,10 @@ namespace smt { } bool theory_array_full::should_research(expr_ref_vector & unsat_core) { - return m_bapa && m_bapa->should_research(unsat_core); + return false; } void theory_array_full::add_theory_assumptions(expr_ref_vector & assumptions) { - if (m_bapa) m_bapa->add_theory_assumptions(assumptions); } // @@ -572,24 +567,6 @@ namespace smt { bool theory_array_full::instantiate_default_as_array_axiom(enode* arr) { return false; -#if 0 - if (!ctx.add_fingerprint(this, m_default_as_array_fingerprint, 1, &arr)) { - return false; - } - m_stats.m_num_default_as_array_axiom++; - SASSERT(is_as_array(arr)); - TRACE(array, tout << mk_bounded_pp(arr->get_owner(), m) << "\n";); - expr* def = mk_default(arr->get_owner()); - func_decl * f = array_util(m).get_as_array_func_decl(arr->get_owner()); - ptr_vector args; - for (unsigned i = 0; i < f->get_arity(); ++i) { - args.push_back(mk_epsilon(f->get_domain(i))); - } - expr_ref val(m.mk_app(f, args.size(), args.c_ptr()), m); - ctx.internalize(def, false); - ctx.internalize(val.get(), false); - return try_assign_eq(val.get(), def); -#endif } bool theory_array_full::instantiate_default_lambda_def_axiom(enode* arr) { @@ -619,41 +596,6 @@ namespace smt { return try_assign_eq(val.get(), def); } - - bool theory_array_full::has_unitary_domain(app* array_term) { - SASSERT(is_array_sort(array_term)); - sort* s = array_term->get_sort(); - unsigned dim = get_dimension(s); - parameter const * params = s->get_info()->get_parameters(); - for (unsigned i = 0; i < dim; ++i) { - SASSERT(params[i].is_ast()); - sort* d = to_sort(params[i].get_ast()); - if (d->is_infinite() || d->is_very_big() || 1 != d->get_num_elements().size()) - return false; - } - return true; - } - - bool theory_array_full::has_large_domain(app* array_term) { - SASSERT(is_array_sort(array_term)); - sort* s = array_term->get_sort(); - unsigned dim = get_dimension(s); - parameter const * params = s->get_info()->get_parameters(); - rational sz(1); - for (unsigned i = 0; i < dim; ++i) { - SASSERT(params[i].is_ast()); - sort* d = to_sort(params[i].get_ast()); - if (d->is_infinite() || d->is_very_big()) { - return true; - } - sz *= rational(d->get_num_elements().size(),rational::ui64()); - if (sz >= rational(1 << 14)) { - return true; - } - } - return false; - } - // // Assert axiom: // select(const v, i_1, ..., i_n) = v @@ -744,11 +686,12 @@ namespace smt { def2 = mk_default(store_app->get_arg(0)); bool is_new = false; + rational sz; if (has_unitary_domain(store_app)) { def2 = store_app->get_arg(num_args - 1); } - else if (!has_large_domain(store_app)) { + else if (!has_large_domain(store_app, sz)) { // // let A = store(B, i, v) // @@ -757,16 +700,20 @@ namespace smt { // default(B) = B[epsilon] // // - expr_ref_vector args1(m), args2(m); + expr_ref_vector args1(m), args2(m), args3(m), args4(m); args1.push_back(store_app); args2.push_back(store_app->get_arg(0)); + args3.push_back(store_app); + args4.push_back(store_app->get_arg(0)); for (unsigned i = 1; i + 1 < num_args; ++i) { expr* arg = store_app->get_arg(i); sort* srt = arg->get_sort(); - auto ep = mk_epsilon(srt); - args1.push_back(ep.first); - args2.push_back(ep.first); + auto [ep, diag] = mk_epsilon(srt); + args1.push_back(ep); + args2.push_back(ep); + args3.push_back(m.mk_app(diag, arg)); + args4.push_back(m.mk_app(diag, arg)); } app_ref sel1(m), sel2(m); sel1 = mk_select(args1); @@ -774,6 +721,10 @@ namespace smt { ctx.internalize(def1, false); ctx.internalize(def2, false); is_new = try_assign_eq(def1, sel1) || try_assign_eq(def2, sel2); + sel1 = mk_select(args3); + sel2 = mk_select(args4); + is_new = try_assign_eq(sel1, sel2) || is_new; + return is_new; } @@ -782,18 +733,18 @@ namespace smt { return try_assign_eq(def1, def2) || is_new; } - std::pair theory_array_full::mk_epsilon(sort* s) { - app* eps = nullptr; - func_decl* diag = nullptr; + std::pair theory_array_full::mk_epsilon(sort *s) { + app *eps = nullptr; + func_decl *diag = nullptr; if (!m_sort2epsilon.find(s, eps)) { eps = m.mk_fresh_const("epsilon", s); - m_trail_stack.push(ast2ast_trail(m_sort2epsilon, s, eps)); + m_trail_stack.push(ast2ast_trail(m_sort2epsilon, s, eps)); } if (!m_sort2diag.find(s, diag)) { diag = m.mk_fresh_func_decl("diag", 1, &s, s); - m_trail_stack.push(ast2ast_trail(m_sort2diag, s, diag)); + m_trail_stack.push(ast2ast_trail(m_sort2diag, s, diag)); } - return std::make_pair(eps, diag); + return {eps, diag}; } final_check_status theory_array_full::assert_delayed_axioms() { @@ -814,9 +765,6 @@ namespace smt { } } } - if (r == FC_DONE && m_bapa) { - r = m_bapa->final_check(); - } bool should_giveup = m_found_unsupported_op || has_propagate_up_trail() || has_non_beta_as_array(); if (r == FC_DONE && should_giveup) r = FC_GIVEUP; @@ -826,14 +774,14 @@ namespace smt { bool theory_array_full::has_non_beta_as_array() { for (enode* n : m_as_array) { for (enode* p : n->get_parents()) - if (!ctx.is_beta_redex(p, n)) { + if (ctx.is_relevant(p) && !ctx.is_beta_redex(p, n)) { TRACE(array, tout << "not a beta redex " << enode_pp(p, ctx) << "\n"); return true; } } for (enode* n : m_lambdas) for (enode* p : n->get_parents()) - if (!is_default(p) && !ctx.is_beta_redex(p, n)) { + if (ctx.is_relevant(p) && !is_default(p) && !ctx.is_beta_redex(p, n)) { TRACE(array, tout << "lambda is not a beta redex " << enode_pp(p, ctx) << "\n"); return true; } diff --git a/src/smt/theory_array_full.h b/src/smt/theory_array_full.h index 5142e022d..1a5b72814 100644 --- a/src/smt/theory_array_full.h +++ b/src/smt/theory_array_full.h @@ -82,8 +82,7 @@ namespace smt { bool instantiate_default_lambda_def_axiom(enode* arr); bool instantiate_parent_stores_default(theory_var v); - bool has_large_domain(app* array_term); - bool has_unitary_domain(app* array_term); + std::pair mk_epsilon(sort* s); enode_vector m_as_array; enode_vector m_lambdas; diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 7b0afd7e1..f1749df4f 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -1450,7 +1450,7 @@ namespace smt { << num_scopes << " = " << (ctx.get_scope_level() - num_scopes) << "\n");); } - final_check_status theory_bv::final_check_eh() { + final_check_status theory_bv::final_check_eh(unsigned level) { SASSERT(check_invariant()); if (m_approximates_large_bvs) { return FC_GIVEUP; diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h index f46a9ce70..5ba10442d 100644 --- a/src/smt/theory_bv.h +++ b/src/smt/theory_bv.h @@ -244,7 +244,7 @@ namespace smt { void relevant_eh(app * n) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; void reset_eh() override; bool include_func_interp(func_decl* f) override; svector m_merge_aux[2]; //!< auxiliary vector used in merge_zero_one_bits diff --git a/src/smt/theory_char.h b/src/smt/theory_char.h index 8be817cda..a4078a74e 100644 --- a/src/smt/theory_char.h +++ b/src/smt/theory_char.h @@ -75,7 +75,7 @@ namespace smt { bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void display(std::ostream& out) const override {} - final_check_status final_check_eh() override { return final_check() ? FC_DONE : FC_CONTINUE; } + final_check_status final_check_eh(unsigned) override { return final_check() ? FC_DONE : FC_CONTINUE; } void init_model(model_generator & mg) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; void collect_statistics(::statistics& st) const override; diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp index d541781ea..b4a3ed4db 100644 --- a/src/smt/theory_datatype.cpp +++ b/src/smt/theory_datatype.cpp @@ -476,7 +476,7 @@ namespace smt { SASSERT(m_find.get_num_vars() == get_num_vars()); } - final_check_status theory_datatype::final_check_eh() { + final_check_status theory_datatype::final_check_eh(unsigned level) { force_push(); int num_vars = get_num_vars(); final_check_status r = FC_DONE; diff --git a/src/smt/theory_datatype.h b/src/smt/theory_datatype.h index 8a61ce5bd..dfc06ae69 100644 --- a/src/smt/theory_datatype.h +++ b/src/smt/theory_datatype.h @@ -126,7 +126,7 @@ namespace smt { void relevant_eh(app * n) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; void reset_eh() override; void restart_eh() override { m_util.reset(); } bool is_shared(theory_var v) const override; diff --git a/src/smt/theory_dense_diff_logic.h b/src/smt/theory_dense_diff_logic.h index 45ec93c08..8c2d62aa9 100644 --- a/src/smt/theory_dense_diff_logic.h +++ b/src/smt/theory_dense_diff_logic.h @@ -230,7 +230,7 @@ namespace smt { void restart_eh() override; void init_search_eh() override; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; bool can_propagate() override; void propagate() override; diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index 96ac78d99..5c351528e 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -387,7 +387,7 @@ namespace smt { } template - final_check_status theory_dense_diff_logic::final_check_eh() { + final_check_status theory_dense_diff_logic::final_check_eh(unsigned level) { init_model(); if (assume_eqs(m_var_value_table)) return FC_CONTINUE; diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 9a818bc10..720cdb9bb 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -269,7 +269,7 @@ namespace smt { m_arith_eq_adapter.init_search_eh(); } - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; bool is_shared(theory_var v) const override { return false; diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 18ac30fbc..30251092c 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -368,7 +368,7 @@ void theory_diff_logic::pop_scope_eh(unsigned num_scopes) { } template -final_check_status theory_diff_logic::final_check_eh() { +final_check_status theory_diff_logic::final_check_eh(unsigned level) { if (can_propagate()) { propagate_core(); diff --git a/src/smt/theory_dummy.cpp b/src/smt/theory_dummy.cpp index 097d7f1ad..7f8af5a9d 100644 --- a/src/smt/theory_dummy.cpp +++ b/src/smt/theory_dummy.cpp @@ -62,7 +62,7 @@ namespace smt { theory::reset_eh(); } - final_check_status theory_dummy::final_check_eh() { + final_check_status theory_dummy::final_check_eh(unsigned) { return m_theory_exprs ? FC_GIVEUP : FC_DONE; } diff --git a/src/smt/theory_dummy.h b/src/smt/theory_dummy.h index 817d1fda1..de77f292d 100644 --- a/src/smt/theory_dummy.h +++ b/src/smt/theory_dummy.h @@ -38,7 +38,7 @@ namespace smt { bool use_diseqs() const override; void new_diseq_eh(theory_var v1, theory_var v2) override; void reset_eh() override; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; bool build_models() const override { return false; } diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index f7be2be55..48885d80c 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -501,7 +501,7 @@ namespace smt { theory::reset_eh(); } - final_check_status theory_fpa::final_check_eh() { + final_check_status theory_fpa::final_check_eh(unsigned level) { TRACE(t_fpa, tout << "final_check_eh\n";); SASSERT(m_converter.m_extra_assertions.empty()); return FC_DONE; diff --git a/src/smt/theory_fpa.h b/src/smt/theory_fpa.h index 262a239dd..badce4e2a 100644 --- a/src/smt/theory_fpa.h +++ b/src/smt/theory_fpa.h @@ -89,7 +89,7 @@ namespace smt { bool m_is_initialized; obj_hashtable m_is_added_to_model; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void apply_sort_cnstr(enode * n, sort * s) override; diff --git a/src/smt/theory_intblast.cpp b/src/smt/theory_intblast.cpp index c6c94958e..d238ae60d 100644 --- a/src/smt/theory_intblast.cpp +++ b/src/smt/theory_intblast.cpp @@ -38,7 +38,7 @@ namespace smt { theory_intblast::~theory_intblast() {} - final_check_status theory_intblast::final_check_eh() { + final_check_status theory_intblast::final_check_eh(unsigned) { for (auto e : m_translator.bv2int()) { auto* n = ctx.get_enode(e); auto* r1 = n->get_arg(0)->get_root(); diff --git a/src/smt/theory_intblast.h b/src/smt/theory_intblast.h index b822593b7..1a2e2c78d 100644 --- a/src/smt/theory_intblast.h +++ b/src/smt/theory_intblast.h @@ -54,7 +54,7 @@ namespace smt { char const* get_name() const override { return "bv-intblast"; } smt::theory* mk_fresh(context* new_ctx) override { return alloc(theory_intblast, *new_ctx); } - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; void display(std::ostream& out) const override {} bool can_propagate() override; void propagate() override; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 59c750306..74225b7b2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1626,7 +1626,7 @@ public: return FC_GIVEUP; } - final_check_status final_check_eh() { + final_check_status final_check_eh(unsigned level) { if (propagate_core()) return FC_CONTINUE; m_model_is_initialized = false; @@ -1654,7 +1654,7 @@ public: break; } - switch (check_nla()) { + switch (check_nla(level)) { case FC_DONE: break; case FC_CONTINUE: @@ -2043,11 +2043,11 @@ public: ctx().set_true_first_flag(lit.var()); } - final_check_status check_nla_continue() { + final_check_status check_nla_continue(unsigned level) { #if Z3DEBUG flet f(lp().validate_blocker(), true); #endif - lbool r = m_nla->check(); + lbool r = m_nla->check(level); switch (r) { case l_false: add_lemmas(); @@ -2059,7 +2059,7 @@ public: } } - final_check_status check_nla() { + final_check_status check_nla(unsigned level) { // TODO - enable or remove if found useful internals are corrected: // lp::lar_solver::scoped_auxiliary _sa(lp()); // new atoms are auxilairy and are not used in nra_solver if (!m.inc()) { @@ -2071,7 +2071,7 @@ public: return FC_DONE; if (!m_nla->need_check()) return FC_DONE; - return check_nla_continue(); + return check_nla_continue(level); } /** @@ -3896,6 +3896,7 @@ public: } theory_lra::inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) { + unsigned level = 2; lp::impq term_max; lp::lp_status st; lpvar vi = 0; @@ -3922,7 +3923,7 @@ public: lp().restore_x(); } if (m_nla && (st == lp::lp_status::OPTIMAL || st == lp::lp_status::UNBOUNDED)) { - switch (check_nla()) { + switch (check_nla(level)) { case FC_DONE: st = lp::lp_status::FEASIBLE; break; @@ -4289,8 +4290,8 @@ void theory_lra::relevant_eh(app* e) { void theory_lra::init_search_eh() { m_imp->init_search_eh(); } -final_check_status theory_lra::final_check_eh() { - return m_imp->final_check_eh(); +final_check_status theory_lra::final_check_eh(unsigned level) { + return m_imp->final_check_eh(level); } bool theory_lra::is_shared(theory_var v) const { return m_imp->is_shared(v); diff --git a/src/smt/theory_lra.h b/src/smt/theory_lra.h index 64d200506..bbbc51ffe 100644 --- a/src/smt/theory_lra.h +++ b/src/smt/theory_lra.h @@ -63,7 +63,11 @@ namespace smt { void init_search_eh() override; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; + + unsigned num_final_check_levels() const override { + return 2; + } bool is_shared(theory_var v) const override; diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 32670bd63..196f370c4 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -985,7 +985,7 @@ namespace smt { UNREACHABLE(); } - final_check_status theory_pb::final_check_eh() { + final_check_status theory_pb::final_check_eh(unsigned level) { TRACE(pb, display(tout);); DEBUG_CODE(validate_final_check();); return FC_DONE; diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h index 353f4aeeb..96e1c96bd 100644 --- a/src/smt/theory_pb.h +++ b/src/smt/theory_pb.h @@ -417,7 +417,7 @@ namespace smt { void new_diseq_eh(theory_var v1, theory_var v2) override { } bool use_diseqs() const override { return false; } bool build_models() const override { return false; } - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; void reset_eh() override; void assign_eh(bool_var v, bool is_true) override; void init_search_eh() override; diff --git a/src/smt/theory_polymorphism.h b/src/smt/theory_polymorphism.h index 4c64a0a9c..8fd88c69b 100644 --- a/src/smt/theory_polymorphism.h +++ b/src/smt/theory_polymorphism.h @@ -66,7 +66,7 @@ namespace smt { ctx.internalize_assertions(); } - final_check_status final_check_eh() override { + final_check_status final_check_eh(unsigned) override { if (m_inst.pending()) ctx.assign(~mk_literal(m_assumption), nullptr); return FC_DONE; diff --git a/src/smt/theory_recfun.cpp b/src/smt/theory_recfun.cpp index 4247dcb2a..9f5d54d43 100644 --- a/src/smt/theory_recfun.cpp +++ b/src/smt/theory_recfun.cpp @@ -405,7 +405,7 @@ namespace smt { ctx.mk_th_axiom(get_id(), clause); } - final_check_status theory_recfun::final_check_eh() { + final_check_status theory_recfun::final_check_eh(unsigned level) { if (can_propagate()) { TRACEFN("final\n"); propagate(); diff --git a/src/smt/theory_recfun.h b/src/smt/theory_recfun.h index 7ca25f917..25e77a469 100644 --- a/src/smt/theory_recfun.h +++ b/src/smt/theory_recfun.h @@ -92,7 +92,7 @@ namespace smt { void reset_eh() override; void relevant_eh(app * n) override; char const * get_name() const override; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; void assign_eh(bool_var v, bool is_true) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 2a70f25d8..36caebf0a 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -318,7 +318,7 @@ struct scoped_enable_trace { } }; -final_check_status theory_seq::final_check_eh() { +final_check_status theory_seq::final_check_eh(unsigned level) { if (!m_has_seq) { return FC_DONE; } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index a6539bded..093cd04b4 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -379,7 +379,7 @@ namespace smt { obj_hashtable m_fixed; // string variables that are fixed length. obj_hashtable m_is_digit; // expressions that have been constrained to be digits - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; bool internalize_atom(app* atom, bool) override; bool internalize_term(app*) override; void internalize_eq_eh(app * atom, bool_var v) override; diff --git a/src/smt/theory_seq_empty.h b/src/smt/theory_seq_empty.h index 5562ba01b..9571f46b7 100644 --- a/src/smt/theory_seq_empty.h +++ b/src/smt/theory_seq_empty.h @@ -27,7 +27,7 @@ namespace smt { class theory_seq_empty : public theory { bool m_used; - final_check_status final_check_eh() override { return m_used?FC_GIVEUP:FC_DONE; } + final_check_status final_check_eh(unsigned) override { return m_used?FC_GIVEUP:FC_DONE; } bool internalize_atom(app*, bool) override { if (!m_used) { get_context().push_trail(value_trail(m_used)); m_used = true; } return false; } bool internalize_term(app*) override { return internalize_atom(nullptr,false); } void new_eq_eh(theory_var, theory_var) override { } diff --git a/src/smt/theory_sls.cpp b/src/smt/theory_sls.cpp index 0ce329046..8551661c2 100644 --- a/src/smt/theory_sls.cpp +++ b/src/smt/theory_sls.cpp @@ -241,7 +241,7 @@ namespace smt { } } - final_check_status theory_sls::final_check_eh() { + final_check_status theory_sls::final_check_eh(unsigned) { if (!m_smt_plugin) return FC_DONE; ++m_after_resolve_decide_count; diff --git a/src/smt/theory_sls.h b/src/smt/theory_sls.h index b0407cbdb..e8d9b22b4 100644 --- a/src/smt/theory_sls.h +++ b/src/smt/theory_sls.h @@ -118,7 +118,7 @@ namespace smt { void new_eq_eh(theory_var v1, theory_var v2) override {} void new_diseq_eh(theory_var v1, theory_var v2) override {} void restart_eh() override; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; // sls::smt_context interface ast_manager& get_manager() override { return m; } diff --git a/src/smt/theory_special_relations.cpp b/src/smt/theory_special_relations.cpp index 9daf3ab2e..aec069a02 100644 --- a/src/smt/theory_special_relations.cpp +++ b/src/smt/theory_special_relations.cpp @@ -186,7 +186,7 @@ namespace smt { } } - final_check_status theory_special_relations::final_check_eh() { + final_check_status theory_special_relations::final_check_eh(unsigned) { TRACE(special_relations, tout << "\n";); for (auto const& kv : m_relations) { lbool r = final_check(*kv.m_value); diff --git a/src/smt/theory_special_relations.h b/src/smt/theory_special_relations.h index 65ce17907..085ecfe74 100644 --- a/src/smt/theory_special_relations.h +++ b/src/smt/theory_special_relations.h @@ -187,7 +187,7 @@ namespace smt { void new_diseq_eh(theory_var v1, theory_var v2) override {} bool use_diseqs() const override { return false; } bool build_models() const override { return true; } - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; void reset_eh() override; void assign_eh(bool_var v, bool is_true) override; void init_search_eh() override {} diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index d8adbdb70..a2eaeb9a8 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -157,7 +157,7 @@ theory * theory_user_propagator::mk_fresh(context * new_ctx) { return th; } -final_check_status theory_user_propagator::final_check_eh() { +final_check_status theory_user_propagator::final_check_eh(unsigned level) { if (!(bool)m_final_eh) return FC_DONE; force_push(); diff --git a/src/smt/theory_user_propagator.h b/src/smt/theory_user_propagator.h index 5e8d3878c..439ffdb7e 100644 --- a/src/smt/theory_user_propagator.h +++ b/src/smt/theory_user_propagator.h @@ -152,7 +152,7 @@ namespace smt { void new_diseq_eh(theory_var v1, theory_var v2) override { if (m_diseq_eh) force_push(), m_diseq_eh(m_user_context, this, var2expr(v1), var2expr(v2)); } bool use_diseqs() const override { return ((bool)m_diseq_eh); } bool build_models() const override { return false; } - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned) override; void reset_eh() override {} void assign_eh(bool_var v, bool is_true) override { } void init_search_eh() override {} diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h index 99344eb31..a917910e9 100644 --- a/src/smt/theory_utvpi.h +++ b/src/smt/theory_utvpi.h @@ -245,7 +245,7 @@ namespace smt { m_arith_eq_adapter.init_search_eh(); } - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned level) override; bool is_shared(th_var v) const override { return false; diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 5f056784f..9086f13aa 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -394,7 +394,7 @@ namespace smt { } template - final_check_status theory_utvpi::final_check_eh() { + final_check_status theory_utvpi::final_check_eh(unsigned level) { SASSERT(is_consistent()); if (can_propagate()) { propagate(); diff --git a/src/smt/theory_wmaxsat.cpp b/src/smt/theory_wmaxsat.cpp index 13e69da5d..fdc1ebfb2 100644 --- a/src/smt/theory_wmaxsat.cpp +++ b/src/smt/theory_wmaxsat.cpp @@ -176,7 +176,7 @@ namespace smt { } } - final_check_status theory_wmaxsat::final_check_eh() { + final_check_status theory_wmaxsat::final_check_eh(unsigned level) { if (m_normalize) normalize(); TRACE(opt, tout << "cost: " << m_zcost << " min cost: " << m_zmin_cost << "\n";); return FC_DONE; diff --git a/src/smt/theory_wmaxsat.h b/src/smt/theory_wmaxsat.h index 9cac6b96b..65461eb70 100644 --- a/src/smt/theory_wmaxsat.h +++ b/src/smt/theory_wmaxsat.h @@ -83,7 +83,7 @@ namespace smt { void init_search_eh() override; void assign_eh(bool_var v, bool is_true) override; - final_check_status final_check_eh() override; + final_check_status final_check_eh(unsigned level) override; bool use_diseqs() const override { return false; } diff --git a/src/test/api.cpp b/src/test/api.cpp index 560dd1121..d047d2881 100644 --- a/src/test/api.cpp +++ b/src/test/api.cpp @@ -107,8 +107,62 @@ static void test_mk_distinct() { } +void test_optimize_translate() { + Z3_config cfg1 = Z3_mk_config(); + Z3_context ctx1 = Z3_mk_context(cfg1); + Z3_del_config(cfg1); + + // Create optimization context in first context + Z3_optimize opt1 = Z3_mk_optimize(ctx1); + Z3_optimize_inc_ref(ctx1, opt1); + + // Add some constraints + Z3_sort int_sort = Z3_mk_int_sort(ctx1); + Z3_symbol x_sym = Z3_mk_string_symbol(ctx1, "x"); + Z3_ast x = Z3_mk_const(ctx1, x_sym, int_sort); + + Z3_ast zero = Z3_mk_int(ctx1, 0, int_sort); + Z3_ast constraint = Z3_mk_gt(ctx1, x, zero); // x > 0 + + Z3_optimize_assert(ctx1, opt1, constraint); + + // Add an objective to maximize x + Z3_optimize_maximize(ctx1, opt1, x); + + // Create second context + Z3_config cfg2 = Z3_mk_config(); + Z3_context ctx2 = Z3_mk_context(cfg2); + Z3_del_config(cfg2); + + // Translate optimize context to second context + Z3_optimize opt2 = Z3_optimize_translate(ctx1, opt1, ctx2); + Z3_optimize_inc_ref(ctx2, opt2); + + // Check sat in the translated context + Z3_lbool result = Z3_optimize_check(ctx2, opt2, 0, nullptr); + + ENSURE(result == Z3_L_TRUE); + + // Verify we can get assertions from translated context + Z3_ast_vector assertions = Z3_optimize_get_assertions(ctx2, opt2); + unsigned num_assertions = Z3_ast_vector_size(ctx2, assertions); + ENSURE(num_assertions == 1); + + // Verify we can get objectives from translated context + Z3_ast_vector objectives = Z3_optimize_get_objectives(ctx2, opt2); + unsigned num_objectives = Z3_ast_vector_size(ctx2, objectives); + ENSURE(num_objectives == 1); + + // Clean up + Z3_optimize_dec_ref(ctx2, opt2); + Z3_optimize_dec_ref(ctx1, opt1); + Z3_del_context(ctx2); + Z3_del_context(ctx1); +} + void tst_api() { test_apps(); test_bvneg(); test_mk_distinct(); + test_optimize_translate(); } diff --git a/src/test/main.cpp b/src/test/main.cpp index 005b7ab59..0af83844d 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -227,6 +227,7 @@ int main(int argc, char ** argv) { TST(prime_generator); TST(permutation); TST(nlsat); + TST(nlsat_mv); TST(zstring); if (test_all) return 0; TST(ext_numeral); diff --git a/src/test/nlsat.cpp b/src/test/nlsat.cpp index 8b283247d..3715bf69d 100644 --- a/src/test/nlsat.cpp +++ b/src/test/nlsat.cpp @@ -25,6 +25,7 @@ Notes: #include "math/polynomial/polynomial_cache.h" #include "util/rlimit.h" #include +#include nlsat::interval_set_ref tst_interval(nlsat::interval_set_ref const & s1, nlsat::interval_set_ref const & s2, @@ -330,6 +331,16 @@ static void project_fa(nlsat::solver& s, nlsat::explain& ex, nlsat::var x, unsig std::cout << ")\n"; } +static bool literal_holds(nlsat::solver& s, nlsat::evaluator& eval, nlsat::literal l) { + if (l == nlsat::true_literal) + return true; + if (l == nlsat::false_literal) + return false; + nlsat::atom* a = s.bool_var2atom(l.var()); + ENSURE(a != nullptr); + return eval.eval(a, l.sign()); +} + static nlsat::literal mk_gt(nlsat::solver& s, nlsat::poly* p) { nlsat::poly * _p[1] = { p }; bool is_even[1] = { false }; @@ -349,6 +360,67 @@ static nlsat::literal mk_eq(nlsat::solver& s, nlsat::poly* p) { return s.mk_ineq_literal(nlsat::atom::EQ, 1, _p, is_even); } +static void set_assignment_value(nlsat::assignment& as, anum_manager& am, nlsat::var v, rational const& val) { + scoped_anum tmp(am); + am.set(tmp, val.to_mpq()); + as.set(v, tmp); +} + +static void tst_vandermond() { + params_ref ps; + reslimit rlim; + nlsat::solver s(rlim, ps, false); + nlsat::pmanager& pm = s.pm(); + anum_manager & am = s.am(); + nlsat::assignment as(am); + scoped_anum zero(am), one(am), two(am), three(am); + nlsat::explain& ex = s.get_explain(); + + nlsat::var x0 = s.mk_var(false); + nlsat::var x1 = s.mk_var(false); + nlsat::var x2 = s.mk_var(false); + nlsat::var x3 = s.mk_var(false); + am.set(one, 1); + am.set(two, 2); + as.set(x0, one); + as.set(x1, two); + as.set(x2, three); + polynomial_ref _x0(pm), _x1(pm), _x2(pm); + _x0 = pm.mk_polynomial(x0); + _x1 = pm.mk_polynomial(x1); + _x2 = pm.mk_polynomial(x2); + + polynomial_ref x0_sq(pm), x1_sq(pm), x2_sq(pm); + x0_sq = _x0 * _x0; + x1_sq = _x1 * _x1; + x2_sq = _x2 * _x2; + + polynomial_ref vandermonde_flat(pm); + vandermonde_flat = + (_x1 * x2_sq) - + (x1_sq * _x2) + + (_x0 * x1_sq) - + (x0_sq * _x1) + + (x0_sq * _x2) - + (_x0 * x2_sq); + + polynomial_ref vandermonde_factored(pm); + vandermonde_factored = (_x1 - _x0) * (_x2 - _x0) * (_x2 - _x1); + std::cout << "vandermonde_factored:" << vandermonde_factored << "\n"; + polynomial_ref diff(pm); + diff = vandermonde_flat - vandermonde_factored; + ENSURE(pm.is_zero(diff.get())); + + pm.display(std::cout << "vandermonde(flat): ", vandermonde_flat); + std::cout << "\n"; + nlsat::scoped_literal_vector lits(s); + lits.push_back(mk_gt(s, vandermonde_flat)); + s.set_rvalues(as); + project(s, ex, x2, lits.size(), lits.data()); + as.set(x2, (one + two)/2); + std::cout << am.eval_sign_at(vandermonde_flat, as) << "\n"; +;} + static void tst6() { params_ref ps; reslimit rlim; @@ -560,16 +632,12 @@ static void tst9() { #define TEST_ON_OFF() \ std::cout << "Off "; \ - ex.set_signed_project(false); \ project(s, ex, _x, lits.size()-1, lits.data()); \ std::cout << "On "; \ - ex.set_signed_project(true); \ project(s, ex, _x, lits.size()-1, lits.data()); \ std::cout << "Off "; \ - ex.set_signed_project(false); \ project(s, ex, _x, lits.size(), lits.data()); \ std::cout << "On "; \ - ex.set_signed_project(true); \ project(s, ex, _x, lits.size(), lits.data()) \ TEST_ON_OFF(); @@ -717,6 +785,107 @@ static void tst10() { std::cout << "\n"; } +void tst_nlsat_mv() { + params_ref ps; + reslimit rlim; + nlsat::solver s(rlim, ps, false); + anum_manager & am = s.am(); + nlsat::pmanager & pm = s.pm(); + nlsat::assignment assignment(am); + nlsat::explain& ex = s.get_explain(); + + tst_vandermond(); + return; + + // Regression: reproduce lemma 114 where main_operator adds spurious bounds. + nlsat::var x0 = s.mk_var(false); + nlsat::var x1 = s.mk_var(false); + + polynomial_ref _x0(pm), _x1(pm); + _x0 = pm.mk_polynomial(x0); + _x1 = pm.mk_polynomial(x1); + + polynomial_ref x0_sq(pm), x0_cu(pm), x0_4(pm), x0_5(pm); + x0_sq = _x0 * _x0; + x0_cu = x0_sq * _x0; + x0_4 = x0_cu * _x0; + x0_5 = x0_4 * _x0; + + polynomial_ref x1_sq(pm), x1_cu(pm), x1_4(pm), x1_5(pm); + x1_sq = _x1 * _x1; + x1_cu = x1_sq * _x1; + x1_4 = x1_cu * _x1; + x1_5 = x1_4 * _x1; + + polynomial_ref root_arg(pm); + root_arg = + x1_5 + + (_x0 * x1_4) - + (18 * x1_4) - + (2 * x0_sq * x1_cu) - + (2 * x0_cu * x1_sq) + + (36 * x0_sq * x1_sq) + + (1296 * _x0 * x1_sq) + + (864 * x1_sq) + + (x0_4 * _x1) + + (1296 * x0_sq * _x1) + + (6048 * _x0 * _x1) + + x0_5 - + (18 * x0_4) + + (864 * x0_sq); + // should be (x1^5 + x0 x1^4 - 18 x1^4 - 2 x0^2 x1^3 - 2 x0^3 x1^2 + 36 x0^2 x1^2 + 1296 x0 x1^2 + 864 x1^2 + x0^4 x1 + 1296 x0^2 x1 + 6048 x0 x1 + x0^5 - 18 x0^4 + 864 x0^2) + std::cout << "big poly:" << root_arg << std::endl; + nlsat::literal x1_gt_0 = mk_gt(s, _x1); + nlsat::bool_var root_gt = s.mk_root_atom(nlsat::atom::ROOT_GT, x1, 3, root_arg.get()); + nlsat::literal x1_gt_root(root_gt, false); + + nlsat::scoped_literal_vector lits(s); + lits.push_back(x1_gt_0); + lits.push_back(~x1_gt_root); // !(x1 > root[3](root_arg)) + + scoped_anum one(am), one_dup(am); + am.set(one, 1); + assignment.set(x0, one); + s.set_rvalues(assignment); + + nlsat::scoped_literal_vector result(s); + ex.main_operator(lits.size(), lits.data(), result); + + std::cout << "main_operator root regression core:\n"; + s.display(std::cout, lits.size(), lits.data()); + s.display(std::cout << "\n==>\n", result.size(), result.data()); + std::cout << "\n"; + + // Assign x1 only after the lemma is produced. + am.set(one_dup, 1); + assignment.set(x1, one_dup); + s.set_rvalues(assignment); + + small_object_allocator allocator; + nlsat::evaluator eval(s, assignment, pm, allocator); + std::cout << "input literal values at x0 = 1, x1 = 1:\n"; + for (nlsat::literal l : lits) { + nlsat::atom* a = s.bool_var2atom(l.var()); + if (!a) { + std::cout << "conversion bug\n"; + } + bool value = a ? eval.eval(a, l.sign()) : false; + s.display(std::cout << " ", l); + std::cout << " -> " << (value ? "true" : "false") << "\n"; + } + std::cout << "new literal values at x0 = 1, x1 = 1:\n"; + for (nlsat::literal l : result) { + nlsat::atom* a = s.bool_var2atom(l.var()); + bool value = a ? eval.eval(a, l.sign()) : false; + if (!a) { + std::cout << "conversion bug\n"; + } + s.display(std::cout << " ", l); + std::cout << " -> " << (value ? "true" : "false") << "\n"; + } + std::cout << "\n"; +} + static void tst11() { params_ref ps; reslimit rlim; @@ -791,6 +960,7 @@ x7 := 1 } void tst_nlsat() { + std::cout << "------------------\n"; tst11(); std::cout << "------------------\n"; return; diff --git a/src/test/zstring.cpp b/src/test/zstring.cpp index bd79d873f..e77aac15c 100644 --- a/src/test/zstring.cpp +++ b/src/test/zstring.cpp @@ -21,6 +21,42 @@ static void tst_ascii_roundtrip() { } } +// Test that control characters are properly escaped. +static void tst_control_chars_escaped() { + // Test DEL character (0x7F / 127) + zstring del_char(0x7Fu); + std::string del_encoded = del_char.encode(); + bool del_ok = del_encoded == "\\u{7f}"; + + if (!del_ok) { + std::cout << "Failed to escape DEL character (0x7F): got '" << del_encoded + << "', expected '\\u{7f}'\n" << std::flush; + ENSURE(del_ok); + } + + // Test a few other control characters below 0x20 + zstring null_char(0x00u); + std::string null_encoded = null_char.encode(); + bool null_ok = null_encoded == "\\u{0}"; + + if (!null_ok) { + std::cout << "Failed to escape NULL character (0x00): got '" << null_encoded + << "', expected '\\u{0}'\n" << std::flush; + ENSURE(null_ok); + } + + zstring tab_char(0x09u); + std::string tab_encoded = tab_char.encode(); + bool tab_ok = tab_encoded == "\\u{9}"; + + if (!tab_ok) { + std::cout << "Failed to escape TAB character (0x09): got '" << tab_encoded + << "', expected '\\u{9}'\n" << std::flush; + ENSURE(tab_ok); + } +} + void tst_zstring() { tst_ascii_roundtrip(); + tst_control_chars_escaped(); } diff --git a/src/util/search_tree.h b/src/util/search_tree.h index 7c51d138d..ae70bd675 100644 --- a/src/util/search_tree.h +++ b/src/util/search_tree.h @@ -73,14 +73,17 @@ namespace search_tree { m_status = status::open; } - node *left() const { - return m_left; - } - node *right() const { - return m_right; - } - node *parent() const { - return m_parent; + node* left() const { return m_left; } + node* right() const { return m_right; } + node* parent() const { return m_parent; } + unsigned depth() const { + unsigned d = 0; + node* p = m_parent; + while (p) { + ++d; + p = p->parent(); + } + return d; } node *find_active_node() { diff --git a/src/util/trace.cpp b/src/util/trace.cpp index 653a29924..c9b715d5d 100644 --- a/src/util/trace.cpp +++ b/src/util/trace.cpp @@ -105,17 +105,42 @@ static const tag_info* get_tag_infos() { } -static bool has_overlap(char const* s, char const* t) { - if (s[0] == t[0]) - return true; - return false; +static size_t levenshtein_distance(const char* s, const char* t) { + size_t len_s = strlen(s); + size_t len_t = strlen(t); + std::vector prev(len_t + 1), curr(len_t + 1); + + for (size_t j = 0; j <= len_t; ++j) + prev[j] = j; + + for (size_t i = 1; i <= len_s; ++i) { + curr[0] = i; + for (size_t j = 1; j <= len_t; ++j) { + size_t cost = s[i - 1] == t[j - 1] ? 0 : 1; + curr[j] = std::min({ prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost }); + } + prev.swap(curr); + } + return prev[len_t]; +} + +static bool has_overlap(char const* s, char const* t, size_t k) { + // Consider overlap if Levenshtein distance is <= k + return levenshtein_distance(s, t) <= k; } void enable_trace(const char * tag_str) { TraceTag tag = find_trace_tag_by_string(tag_str); + size_t k = strlen(tag_str); + + + if (tag == TraceTag::Count) { warning_msg("trace tag '%s' does not exist", tag_str); -#define X(tag_class, tag, desc) if (has_overlap(#tag, tag_str)) warning_msg("did you mean '%s'?", #tag); +#define X(tag_class, tag, desc) k = std::min(levenshtein_distance(#tag, tag_str), k); +#include "util/trace_tags.def" +#undef X +#define X(tag_class, tag, desc) if (has_overlap(#tag, tag_str, k + 2)) warning_msg("did you mean '%s'?", #tag); #include "util/trace_tags.def" #undef X return; diff --git a/src/util/trace_tags.def b/src/util/trace_tags.def index ffa631d7a..7d8c0928a 100644 --- a/src/util/trace_tags.def +++ b/src/util/trace_tags.def @@ -708,6 +708,7 @@ X(Global, nlsat_resolve_done, "nlsat resolve done") X(Global, nlsat_root, "nlsat root") X(Global, nlsat_simpilfy_core, "nlsat simpilfy core") X(Global, nlsat_simplify_core, "nlsat simplify core") +X(Global, nlsat_simplify_bug, "nlsat simplify bug") X(Global, nlsat_smt2, "nlsat smt2") X(Global, nlsat_solver, "nlsat solver") X(Global, nlsat_sort, "nlsat sort") diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index 8e08820f6..f60b8d946 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -150,7 +150,7 @@ std::string zstring::encode() const { #define _flush() if (offset > 0) { buffer[offset] = 0; strm << buffer; offset = 0; } for (unsigned i = 0; i < m_buffer.size(); ++i) { unsigned ch = m_buffer[i]; - if (ch < 32 || ch >= 128 || ('\\' == ch && i + 1 < m_buffer.size() && 'u' == m_buffer[i+1])) { + if (ch < 32 || ch >= 127 || ('\\' == ch && i + 1 < m_buffer.size() && 'u' == m_buffer[i+1])) { _flush(); strm << "\\u{" << std::hex << ch << std::dec << '}'; }