diff --git a/.github/actions/benchexec-report/action.yml b/.github/actions/benchexec-report/action.yml index 2fc30c965c..ea031af136 100644 --- a/.github/actions/benchexec-report/action.yml +++ b/.github/actions/benchexec-report/action.yml @@ -48,7 +48,7 @@ runs: then printf "\n| $task | " >> $GITHUB_OUTPUT fi lasttask="$task" - printf " $emoji ($correct / $incorrect / $all) [HTML](https://theta.mit.bme.hu/benchmark-results/${{ github.head_ref }}/$i/$(ls *.html))/[CSV](https://theta.mit.bme.hu/benchmark-results/${{ github.head_ref }}/$i/$(ls *.csv)) | " >> $GITHUB_OUTPUT + printf " $emoji ($correct / $incorrect / $all) [HTML](https://theta.mit.bme.hu/benchmark-results/${{ github.ref }}/$i/$(ls *.html))/[CSV](https://theta.mit.bme.hu/benchmark-results/${{ github.ref }}/$i/$(ls *.csv)) | " >> $GITHUB_OUTPUT popd else rm -rf $i @@ -68,7 +68,7 @@ runs: with: branch: gh-pages folder: artifacts - target-folder: benchmark-results/${{ github.head_ref }}/ + target-folder: benchmark-results/${{ github.ref }}/ single-commit: true - name: Comment on PR if: github.event_name == 'pull_request' diff --git a/.github/actions/benchexec-test/action.yml b/.github/actions/benchexec-test/action.yml index d0f198ec4c..8444c92201 100644 --- a/.github/actions/benchexec-test/action.yml +++ b/.github/actions/benchexec-test/action.yml @@ -49,23 +49,21 @@ runs: run: | unzip release/Theta.zip sed -i 's/ --input \$1/ --portfolio ${{ inputs.portfolio }} --input \$1/g' Theta/theta-start.sh - - name: Cut setfile if too long - id: setfile - shell: bash - run: | - cd sv-benchmarks/c - for i in $(sed 's/#.*$//g' ${{ inputs.task }}.set); do find . -wholename ./$i; done | while read line; do grep "${line#./}" $GITHUB_ACTION_PATH/unsupported.txt >/dev/null 2>/dev/null || test -z $(yq e '.properties.[] | select(.property_file == "../properties/unreach-call.prp")' $line) >/dev/null 2>/dev/null || echo $(echo $line | sha1sum | awk ' { print $1 } ') $line ; done | sort -k1 | awk ' { $1=""; print $0 } ' | awk '{$1=$1};1' > all-files.txt - head -n${{ inputs.test_number }} all-files.txt > ${{ inputs.task }}.set - echo "length=$(cat ${{ inputs.task }}.set | wc -l)" >> "$GITHUB_OUTPUT" - cat ${{ inputs.task }}.set +# - name: Cut setfile if too long +# id: setfile +# shell: bash +# run: | +# cd sv-benchmarks/c +# for i in $(sed 's/#.*$//g' ${{ inputs.task }}.set); do find . -wholename ./$i; done | while read line; do grep "${line#./}" $GITHUB_ACTION_PATH/unsupported.txt >/dev/null 2>/dev/null || test -z $(yq e '.properties.[] | select(.property_file == "../properties/unreach-call.prp")' $line) >/dev/null 2>/dev/null || echo $(echo $line | sha1sum | awk ' { print $1 } ') $line ; done | sort -k1 | awk ' { $1=""; print $0 } ' | awk '{$1=$1};1' > all-files.txt +# head -n${{ inputs.test_number }} all-files.txt > ${{ inputs.task }}.set +# echo "length=$(cat ${{ inputs.task }}.set | wc -l)" >> "$GITHUB_OUTPUT" +# cat ${{ inputs.task }}.set - name: Run benchexec shell: bash - if: steps.setfile.outputs.length != '0' run: | - benchexec xml/theta.xml -n 2 --no-container --tool-directory Theta -t ${{ inputs.task }} + timeout 300 benchexec xml/theta.xml -n 2 --no-container --tool-directory Theta -t ${{ inputs.task }} || true - name: Upload results uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 - if: steps.setfile.outputs.length != '0' with: name: BenchexecResults-${{ inputs.task }}-${{ inputs.portfolio }} path: results diff --git a/.github/actions/benchexec-test/theta.xml b/.github/actions/benchexec-test/theta.xml index 2ce7d4bc50..e2a7455cee 100644 --- a/.github/actions/benchexec-test/theta.xml +++ b/.github/actions/benchexec-test/theta.xml @@ -11,90 +11,90 @@ - - ../sv-benchmarks/c/ReachSafety-Arrays.set + + ../sv-benchmarks/c/Arrays.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-BitVectors.set + + ../sv-benchmarks/c/BitVectors.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-ControlFlow.set + + ../sv-benchmarks/c/ControlFlow.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-ECA.set + + ../sv-benchmarks/c/ECA.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-Floats.set + + ../sv-benchmarks/c/Floats.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-Heap.set + + ../sv-benchmarks/c/Heap.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-Loops.set + + ../sv-benchmarks/c/Loops.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-ProductLines.set + + ../sv-benchmarks/c/ProductLines.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-Recursive.set + + ../sv-benchmarks/c/Recursive.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-Sequentialized.set + + ../sv-benchmarks/c/Sequentialized.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-XCSP.set + + ../sv-benchmarks/c/XCSP.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-Combinations.set + + ../sv-benchmarks/c/Combinations.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-Hardware.set + + ../sv-benchmarks/c/Hardware.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-Hardness.set + + ../sv-benchmarks/c/Hardness.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ReachSafety-Fuzzle.set + + ../sv-benchmarks/c/Fuzzle.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/ConcurrencySafety-Main.set + + ../sv-benchmarks/c/Concurrency.set ../sv-benchmarks/c/properties/unreach-call.prp - - ../sv-benchmarks/c/NoDataRace-Main.set + + ../sv-benchmarks/c/Concurrency.set ../sv-benchmarks/c/properties/no-data-race.prp - ../sv-benchmarks/c/ConcurrencySafety-NoOverflows.set + ../sv-benchmarks/c/Concurrency.set ../sv-benchmarks/c/properties/no-overflow.prp - ../sv-benchmarks/c/ConcurrencySafety-MemSafety.set + ../sv-benchmarks/c/ConcurrencySafety.set ../sv-benchmarks/c/properties/valid-memsafety.prp diff --git a/.github/actions/cache-build/action.yml b/.github/actions/cache-build/action.yml index 277be1eb47..b1c7d0945d 100644 --- a/.github/actions/cache-build/action.yml +++ b/.github/actions/cache-build/action.yml @@ -6,13 +6,10 @@ inputs: runs: using: "composite" steps: -# - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 -# if: runner.os == 'Linux' -# id: cache -# with: -# path: . -# key: ${{ runner.os }}-${{github.sha}} - + - name: fetch + shell: bash + run: | + git fetch origin - name: build gradle uses: gradle/gradle-build-action@40b6781dcdec2762ad36556682ac74e31030cfe2 # v2.5.1 with: diff --git a/.github/actions/check-formatting/action.yml b/.github/actions/check-formatting/action.yml index 89ad28ffd8..72cf9f45cb 100644 --- a/.github/actions/check-formatting/action.yml +++ b/.github/actions/check-formatting/action.yml @@ -6,8 +6,6 @@ runs: - name: Checkout code uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Check for formatting - uses: leventeBajczi/intellij-idea-format@v1.0 + uses: ./.github/actions/cache-build with: - settings-file: "./.idea/codeStyles/Project.xml" - file-mask: "*.java,*.kt" - additional-options: "-dry" \ No newline at end of file + arguments: spotlessCheck \ No newline at end of file diff --git a/.github/workflows/check-copyright.yml b/.github/workflows/check-copyright.yml index c441a8d667..7eb1f23215 100644 --- a/.github/workflows/check-copyright.yml +++ b/.github/workflows/check-copyright.yml @@ -7,7 +7,7 @@ on: permissions: write-all concurrency: - group: copyright-${{ github.head_ref }} + group: copyright-${{ github.ref }} cancel-in-progress: true jobs: @@ -17,4 +17,21 @@ jobs: - name: Checkout repository uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Check copyright - uses: ./.github/actions/check-copyright \ No newline at end of file + uses: ./.github/actions/check-copyright + - name: Comment + if: failure() && github.event_name == 'pull_request' + uses: thollander/actions-comment-pull-request@dadb7667129e23f12ca3925c90dc5cd7121ab57e + continue-on-error: true # if we are in a fork, this will fail, but we don't care (tables will be missing) + with: + comment_tag: 'copyright' + mode: 'recreate' + message: | + :exclamation: Please run `./gradlew spotlessApply` on your branch to fix copyright headers. + + - name: Delete Comment + if: github.event_name == 'pull_request' + uses: thollander/actions-comment-pull-request@dadb7667129e23f12ca3925c90dc5cd7121ab57e + continue-on-error: true # if we are in a fork, this will fail, but we don't care (tables will be missing) + with: + comment_tag: 'copyright' + mode: 'delete' \ No newline at end of file diff --git a/.github/workflows/check-formatting.yml b/.github/workflows/check-formatting.yml index 213bdcb52d..02647f3a04 100644 --- a/.github/workflows/check-formatting.yml +++ b/.github/workflows/check-formatting.yml @@ -7,7 +7,7 @@ on: permissions: write-all concurrency: - group: formatting-${{ github.head_ref }} + group: formatting-${{ github.ref }} cancel-in-progress: true jobs: @@ -16,5 +16,27 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup java 17 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: temurin + java-version: 17 - name: Checking formatting - uses: ./.github/actions/check-formatting \ No newline at end of file + uses: ./.github/actions/check-formatting + - name: Comment + if: failure() && github.event_name == 'pull_request' + uses: thollander/actions-comment-pull-request@dadb7667129e23f12ca3925c90dc5cd7121ab57e + continue-on-error: true # if we are in a fork, this will fail, but we don't care (tables will be missing) + with: + comment_tag: 'reformat' + mode: 'recreate' + message: | + :exclamation: Please run `./gradlew spotlessApply` on your branch to fix formatting. + + - name: Delete Comment + if: github.event_name == 'pull_request' + uses: thollander/actions-comment-pull-request@dadb7667129e23f12ca3925c90dc5cd7121ab57e + continue-on-error: true # if we are in a fork, this will fail, but we don't care (tables will be missing) + with: + comment_tag: 'reformat' + mode: 'delete' \ No newline at end of file diff --git a/.github/workflows/check-version.yml b/.github/workflows/check-version.yml index 343ed50f53..83cbf94600 100644 --- a/.github/workflows/check-version.yml +++ b/.github/workflows/check-version.yml @@ -4,7 +4,7 @@ on: types: [opened, synchronize, reopened] concurrency: - group: version-${{ github.head_ref }} + group: version-${{ github.ref }} cancel-in-progress: true jobs: @@ -35,4 +35,21 @@ jobs: if: ${{ steps.new_version.outputs.version == steps.master_version.outputs.version }} run: | echo "New version ${{ steps.new_version.outputs.version }} is NOT OK" - exit 1 \ No newline at end of file + exit 1 + - name: Comment + if: failure() && github.event_name == 'pull_request' + uses: thollander/actions-comment-pull-request@dadb7667129e23f12ca3925c90dc5cd7121ab57e + continue-on-error: true # if we are in a fork, this will fail, but we don't care (tables will be missing) + with: + comment_tag: 'version' + mode: 'recreate' + message: | + :exclamation: Please modify `build.gradle.kts` to contain a later version than ${{ steps.master_version.outputs.version }}. Current version is ${{ steps.new_version.outputs.version }}. + + - name: Delete Comment + if: github.event_name == 'pull_request' + uses: thollander/actions-comment-pull-request@dadb7667129e23f12ca3925c90dc5cd7121ab57e + continue-on-error: true # if we are in a fork, this will fail, but we don't care (tables will be missing) + with: + comment_tag: 'version' + mode: 'delete' \ No newline at end of file diff --git a/.github/workflows/linux-build-test-deploy.yml b/.github/workflows/linux-build-test-deploy.yml index c670c7f497..4c101aca95 100644 --- a/.github/workflows/linux-build-test-deploy.yml +++ b/.github/workflows/linux-build-test-deploy.yml @@ -13,7 +13,7 @@ on: permissions: write-all concurrency: - group: deploy-${{ github.head_ref }}-${{ github.event_name }} + group: deploy-${{ github.ref }}-${{ github.event_name }} cancel-in-progress: true jobs: @@ -49,7 +49,24 @@ jobs: test-benchexec: strategy: matrix: - task: [ReachSafety-Arrays, ReachSafety-BitVectors, ReachSafety-ControlFlow, ReachSafety-ECA, ReachSafety-Floats, ReachSafety-Heap, ReachSafety-Loops, ReachSafety-ProductLines, ReachSafety-Recursive, ReachSafety-Sequentialized, ReachSafety-XCSP, ReachSafety-Combinations, ReachSafety-Hardware, ConcurrencySafety-Main, NoDataRace-Main, ConcurrencySafety-NoOverflows, ConcurrencySafety-MemSafety] + task: + - Arrays + - BitVectors + - ControlFlow + - ECA + - Floats + - Heap + - Loops + - ProductLines + - Recursive + - Sequentialized + - XCSP + - Combinations + - Hardware + - Concurrency + - NoDataRace + - ConcurrencySafety-NoOverflows + - ConcurrencySafety-MemSafety portfolio: [CEGAR, BOUNDED, HORN] runs-on: ubuntu-latest needs: create-archives @@ -88,6 +105,11 @@ jobs: with: name: "EmergenTheta" inputflag: "--algorithm EMERGENT" + - name: Create thorn.zip + uses: ./.github/actions/build-archive + with: + name: "Thorn" + inputflag: "--algorithm HORN" javadoc: diff --git a/.github/workflows/mac-build-test.yml b/.github/workflows/mac-build-test.yml index 4a0f2183b0..71994579a5 100644 --- a/.github/workflows/mac-build-test.yml +++ b/.github/workflows/mac-build-test.yml @@ -7,7 +7,7 @@ on: permissions: write-all concurrency: - group: mac-${{ github.head_ref }} + group: mac-${{ github.ref }} cancel-in-progress: true jobs: diff --git a/.github/workflows/reapply_copyright.yml b/.github/workflows/reapply_copyright.yml deleted file mode 100644 index 3acc9e3876..0000000000 --- a/.github/workflows/reapply_copyright.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: Reapply Copyright - -on: - workflow_dispatch: - inputs: - file_mask: - description: 'File mask to reapply license header (argument to find)' - required: true - default: '-name "*.java" -o -name "*.kt" -o -name "*.kts"' - direct-commit: - type: boolean - default: false - description: Commit directly to source (instead of opening a PR) - -jobs: - copyright_checker: - runs-on: ubuntu-latest - steps: - - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - id: generate-token - with: - app_id: ${{ secrets.APP_ID }} - private_key: ${{ secrets.APP_PRIVATE_KEY }} - - - name: Checkout repository - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - with: - token: ${{ steps.generate-token.outputs.token }} - - - name: Run Copyright Checker - shell: bash {0} - run: | - set +x - - for file in $(find . -type f \( ${{ inputs.file_mask }} \)); do - last_modified_year=$(git log -1 --format=%ad --date=format:%Y "$file") - echo "Checking file $file, last modified $last_modified_year" - - header_exists=$(head -n 2 "$file" | tail -n 1 | grep -c "Budapest University of Technology") - - echo "Header exists for $file: $header_exists" - - if [[ $header_exists -eq 0 ]]; then - echo "Adding copyright header to $file" - cat ./doc/copyright-header.txt "$file" > tmp_026204264 - mv tmp_026204264 "$file" - fi - - header_year=$(head -n 2 "$file" | tail -n 1 | grep -o -E '[0-9]{4}') - - echo "Header year for $file: $header_year" - - if [[ "$header_year" != "$last_modified_year" ]]; then - echo "Updating copyright year to $last_modified_year in $file (was $header_year)" - sed -i "s/Copyright [0-9]\{4\}/Copyright $last_modified_year/g" "$file" - fi - done - - - name: Create Pull Request - if: ${{ !inputs.direct-commit }} - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 - with: - commit-message: "Reapplied copyright" - branch: "copyright-reapply" - title: '[AutoPR] Reaplied copyright' - token: ${{ steps.generate-token.outputs.token }} - committer: ThetaBotMaintainer[bot] <139346997+ThetaBotMaintainer[bot]@users.noreply.github.com> - author: ThetaBotMaintainer[bot] <139346997+ThetaBotMaintainer[bot]@users.noreply.github.com> - - - name: Commit changes - if: ${{ inputs.direct-commit }} - uses: stefanzweifel/git-auto-commit-action@3ea6ae190baf489ba007f7c92608f33ce20ef04a - with: - commit_message: "Reapplied copyright" - commit_user_name: ThetaBotMaintainer[bot] - commit_user_email: 139346997+ThetaBotMaintainer[bot]@users.noreply.github.com - commit_author: ThetaBotMaintainer[bot] <139346997+ThetaBotMaintainer[bot]@users.noreply.github.com> \ No newline at end of file diff --git a/.github/workflows/reformat-code.yml b/.github/workflows/reformat-code.yml index fe6fc24d9f..29411d1f37 100644 --- a/.github/workflows/reformat-code.yml +++ b/.github/workflows/reformat-code.yml @@ -3,10 +3,6 @@ name: Reformat code on: workflow_dispatch: inputs: - file_mask: - description: 'File mask to reformat' - required: true - default: '*.java,*.kts,*.kt' direct-commit: type: boolean default: false @@ -28,10 +24,9 @@ jobs: token: ${{ steps.generate-token.outputs.token }} - name: Do reformat - uses: leventeBajczi/intellij-idea-format@v1.0 + uses: ./.github/actions/cache-build with: - settings-file: "./.idea/codeStyles/Project.xml" - file-mask: ${{ inputs.file_mask }} + arguments: spotlessApply - name: Create Pull Request if: ${{ !inputs.direct-commit }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index c291bdc97c..c66a14c329 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -8,7 +8,7 @@ on: permissions: read-all concurrency: - group: sonar-${{ github.head_ref }} + group: sonar-${{ github.ref }} cancel-in-progress: true jobs: diff --git a/.github/workflows/win-build-test.yml b/.github/workflows/win-build-test.yml index 1288984285..7fc33a46f5 100644 --- a/.github/workflows/win-build-test.yml +++ b/.github/workflows/win-build-test.yml @@ -7,7 +7,7 @@ on: permissions: write-all concurrency: - group: win-${{ github.head_ref }} + group: win-${{ github.ref }} cancel-in-progress: true jobs: diff --git a/README.md b/README.md index 80e602f755..7044ff4049 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ [![Check formatting](https://github.com/ftsrg/theta/actions/workflows/check-formatting.yml/badge.svg)](https://github.com/ftsrg/theta/actions/workflows/check-formatting.yml) [![Apache 2.0 License](https://img.shields.io/badge/license-Apache--2-brightgreen.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0) -![Theta logo](https://raw.githubusercontent.com/ftsrg/theta/master/doc/theta-logo.png) +Theta logo ## About diff --git a/build.gradle.kts b/build.gradle.kts index 741094252d..06036a38d7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + plugins { base id("jacoco-common") @@ -28,7 +29,7 @@ buildscript { allprojects { group = "hu.bme.mit.theta" - version = "6.5.1" + version = "6.6.4" apply(from = rootDir.resolve("gradle/shared-with-buildSrc/mirrors.gradle.kts")) } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 093b423f12..7b1e3380f6 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -30,6 +30,7 @@ apply(from = rootDir.resolve("../gradle/shared-with-buildSrc/mirrors.gradle.kts" val kotlinVersion: String by project val shadowVersion: String by project +val spotlessVersion: String by project // https://github.com/gradle/kotlin-dsl/issues/430#issuecomment-414768887 fun gradlePlugin(id: String, version: String): String = "$id:$id.gradle.plugin:$version" @@ -38,6 +39,7 @@ dependencies { compileOnly(gradleKotlinDsl()) implementation(kotlin("gradle-plugin", kotlinVersion)) implementation(gradlePlugin("com.github.johnrengelman.shadow", shadowVersion)) + implementation(gradlePlugin("com.diffplug.spotless", spotlessVersion)) } // Force the embeddable Kotlin compiler version to be the selected kotlinVersion. @@ -103,4 +105,4 @@ tasks { named("compileKotlin", KotlinCompile::class) { dependsOn(generateVersions) } -} +} \ No newline at end of file diff --git a/buildSrc/gradle.properties b/buildSrc/gradle.properties index e9c71bc25e..77da25281b 100644 --- a/buildSrc/gradle.properties +++ b/buildSrc/gradle.properties @@ -39,4 +39,5 @@ deltaCollectionsVersion=0.0.1 gsonVersion=2.9.1 javasmtVersion=4.1.1 sosylabVersion=0.3000-569-g89796f98 -cliktVersion=4.4.0 \ No newline at end of file +cliktVersion=4.4.0 +spotlessVersion=6.25.0 \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/java-common.gradle.kts b/buildSrc/src/main/kotlin/java-common.gradle.kts index 96ffbd8ec6..838b4d4f41 100644 --- a/buildSrc/src/main/kotlin/java-common.gradle.kts +++ b/buildSrc/src/main/kotlin/java-common.gradle.kts @@ -17,6 +17,7 @@ plugins { java id("jacoco-common") id("maven-artifact") + id("com.diffplug.spotless") } dependencies { @@ -65,3 +66,46 @@ tasks { jvmArgs("-Xss5m", "-Xms512m", "-Xmx1g") } } + +spotless { + ratchetFrom("origin/master") + + isEnforceCheck = false + + val year = "\$YEAR" // you can't escape $ in raw strings.. + val licenseHeader = """ /* + * Copyright $year Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */""".trimIndent() + + + java { + importOrder("java|javax", "hu.bme.", "") + removeUnusedImports() + googleJavaFormat("1.24.0").aosp().reflowLongStrings() + formatAnnotations() + + licenseHeader(licenseHeader) + } + kotlin { + ktfmt("0.51").googleStyle() + + licenseHeader(licenseHeader) + } + kotlinGradle { + target("*.gradle.kts") // default target for kotlinGradle + + ktlint() + } +} \ No newline at end of file diff --git a/doc/theta-logo.svg b/doc/theta-logo.svg new file mode 100644 index 0000000000..8705b0a3b4 --- /dev/null +++ b/doc/theta-logo.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + diff --git a/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/config/CfaConfigBuilder.java b/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/config/CfaConfigBuilder.java index 7427f38b20..37c30260ea 100644 --- a/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/config/CfaConfigBuilder.java +++ b/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/config/CfaConfigBuilder.java @@ -15,16 +15,19 @@ */ package hu.bme.mit.theta.cfa.analysis.config; +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.True; + import hu.bme.mit.theta.analysis.*; +import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators.ArgNodeComparator; -import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.BasicAbstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker; -import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgCegarChecker; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgRefiner; +import hu.bme.mit.theta.analysis.algorithm.cegar.BasicArgAbstractor; import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterion; import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterions; import hu.bme.mit.theta.analysis.expl.*; @@ -52,14 +55,10 @@ import hu.bme.mit.theta.common.logging.NullLogger; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverFactory; - import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; -import static com.google.common.base.Preconditions.checkState; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.True; - public class CfaConfigBuilder { private final SolverFactory abstractionSolverFactory; @@ -75,16 +74,19 @@ public class CfaConfigBuilder { private InitPrec initPrec = InitPrec.EMPTY; private PruneStrategy pruneStrategy = PruneStrategy.LAZY; - public CfaConfigBuilder(final Domain domain, final Refinement refinement, - final SolverFactory solverFactory) { + public CfaConfigBuilder( + final Domain domain, final Refinement refinement, final SolverFactory solverFactory) { this.domain = domain; this.refinement = refinement; this.abstractionSolverFactory = solverFactory; this.refinementSolverFactory = solverFactory; } - public CfaConfigBuilder(final Domain domain, final Refinement refinement, - final SolverFactory abstractionSolverFactory, final SolverFactory refinementSolverFactory) { + public CfaConfigBuilder( + final Domain domain, + final Refinement refinement, + final SolverFactory abstractionSolverFactory, + final SolverFactory refinementSolverFactory) { this.domain = domain; this.refinement = refinement; this.abstractionSolverFactory = abstractionSolverFactory; @@ -131,12 +133,13 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { return this; } - public CfaConfig build(final CFA cfa, - final CFA.Loc errLoc) { + public CfaConfig build( + final CFA cfa, final CFA.Loc errLoc) { if (domain == Domain.EXPL) { return (new ExplStrategy(cfa)).buildConfig(errLoc); } - if (domain == Domain.PRED_BOOL || domain == Domain.PRED_CART + if (domain == Domain.PRED_BOOL + || domain == Domain.PRED_CART || domain == Domain.PRED_SPLIT) { return (new PredStrategy(cfa)).buildConfig(errLoc); } @@ -166,18 +169,53 @@ public enum Domain { public enum Refinement { FW_BIN_ITP { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceFwBinItpChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createItpSolver()), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceFwBinItpChecker.create( + True(), + True(), + builderStrategy.getRefinementSolverFactory().createItpSolver()), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, BW_BIN_ITP { + }, + BW_BIN_ITP { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceBwBinItpChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createItpSolver()), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + True(), + True(), + builderStrategy.getRefinementSolverFactory().createItpSolver()), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, SEQ_ITP { + }, + SEQ_ITP { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceSeqItpChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createItpSolver()), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceSeqItpChecker.create( + True(), + True(), + builderStrategy.getRefinementSolverFactory().createItpSolver()), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } }, MULTI_SEQ { @@ -187,60 +225,229 @@ public StopCriterion getStopCriterion() { } @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return MultiExprTraceRefiner.create(ExprTraceSeqItpChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createItpSolver()), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return MultiExprTraceRefiner.create( + ExprTraceSeqItpChecker.create( + True(), + True(), + builderStrategy.getRefinementSolverFactory().createItpSolver()), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } }, UNSAT_CORE { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceUnsatCoreChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getVarsRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceUnsatCoreChecker.create( + True(), + True(), + builderStrategy.getRefinementSolverFactory().createUCSolver()), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getVarsRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, UCB { + }, + UCB { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceUCBChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceUCBChecker.create( + True(), + True(), + builderStrategy.getRefinementSolverFactory().createUCSolver()), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } }, NWT_WP { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceNewtonChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()).withoutIT().withSP().withoutLV(), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceNewtonChecker.create( + True(), + True(), + builderStrategy + .getRefinementSolverFactory() + .createUCSolver()) + .withoutIT() + .withSP() + .withoutLV(), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, NWT_SP { + }, + NWT_SP { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceNewtonChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()).withoutIT().withWP().withoutLV(), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceNewtonChecker.create( + True(), + True(), + builderStrategy + .getRefinementSolverFactory() + .createUCSolver()) + .withoutIT() + .withWP() + .withoutLV(), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, NWT_WP_LV { + }, + NWT_WP_LV { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceNewtonChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()).withoutIT().withWP().withLV(), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceNewtonChecker.create( + True(), + True(), + builderStrategy + .getRefinementSolverFactory() + .createUCSolver()) + .withoutIT() + .withWP() + .withLV(), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, NWT_SP_LV { + }, + NWT_SP_LV { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceNewtonChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()).withoutIT().withSP().withLV(), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceNewtonChecker.create( + True(), + True(), + builderStrategy + .getRefinementSolverFactory() + .createUCSolver()) + .withoutIT() + .withSP() + .withLV(), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, NWT_IT_WP { + }, + NWT_IT_WP { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceNewtonChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()).withIT().withWP().withoutLV(), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceNewtonChecker.create( + True(), + True(), + builderStrategy + .getRefinementSolverFactory() + .createUCSolver()) + .withIT() + .withWP() + .withoutLV(), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, NWT_IT_SP { + }, + NWT_IT_SP { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceNewtonChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()).withIT().withSP().withoutLV(), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceNewtonChecker.create( + True(), + True(), + builderStrategy + .getRefinementSolverFactory() + .createUCSolver()) + .withIT() + .withSP() + .withoutLV(), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, NWT_IT_WP_LV { + }, + NWT_IT_WP_LV { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceNewtonChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()).withIT().withWP().withLV(), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceNewtonChecker.create( + True(), + True(), + builderStrategy + .getRefinementSolverFactory() + .createUCSolver()) + .withIT() + .withWP() + .withLV(), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } - }, NWT_IT_SP_LV { + }, + NWT_IT_SP_LV { @Override - public Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy) { - return SingleExprTraceRefiner.create(ExprTraceNewtonChecker.create(True(), True(), builderStrategy.getRefinementSolverFactory().createUCSolver()).withIT().withSP().withLV(), builderStrategy.getPrecGranularity().createRefiner(builderStrategy.getItpRefToPrec()), builderStrategy.getPruneStrategy(), builderStrategy.getLogger()); + public + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy) { + return SingleExprTraceRefiner.create( + ExprTraceNewtonChecker.create( + True(), + True(), + builderStrategy + .getRefinementSolverFactory() + .createUCSolver()) + .withIT() + .withSP() + .withLV(), + builderStrategy + .getPrecGranularity() + .createRefiner(builderStrategy.getItpRefToPrec()), + builderStrategy.getPruneStrategy(), + builderStrategy.getLogger()); } }; @@ -248,24 +455,25 @@ public StopCriterion getStopCriterion() { return StopCriterions.firstCex(); } - public abstract Refiner, CfaAction, CfaPrec

> getRefiner(BuilderStrategy builderStrategy); - + public abstract + ArgRefiner, CfaAction, CfaPrec

> getRefiner( + BuilderStrategy builderStrategy); } public enum Search { BFS { @Override public ArgNodeComparator getComp(final CFA cfa, final CFA.Loc errLoc) { - return ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), - ArgNodeComparators.bfs()); + return ArgNodeComparators.combine( + ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs()); } }, DFS { @Override public ArgNodeComparator getComp(final CFA cfa, final CFA.Loc errLoc) { - return ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), - ArgNodeComparators.dfs()); + return ArgNodeComparators.combine( + ArgNodeComparators.targetFirst(), ArgNodeComparators.dfs()); } }, @@ -277,7 +485,6 @@ public ArgNodeComparator getComp(final CFA cfa, final CFA.Loc errLoc) { }; public abstract ArgNodeComparator getComp(CFA cfa, CFA.Loc errLoc); - } public enum PredSplit { @@ -302,8 +509,9 @@ public

CfaPrec

createPrec(final P innerPrec) { } @Override - public PrecRefiner, A, CfaPrec

, R> createRefiner( - final RefutationToPrec refToPrec) { + public + PrecRefiner, A, CfaPrec

, R> createRefiner( + final RefutationToPrec refToPrec) { return GlobalCfaPrecRefiner.create(refToPrec); } @@ -320,8 +528,9 @@ public

CfaPrec

createPrec(final P innerPrec) { } @Override - public PrecRefiner, A, CfaPrec

, R> createRefiner( - final RefutationToPrec refToPrec) { + public + PrecRefiner, A, CfaPrec

, R> createRefiner( + final RefutationToPrec refToPrec) { return LocalCfaPrecRefiner.create(refToPrec); } @@ -333,8 +542,10 @@ public CfaPrec assumePrecs(CFA cfa) { public abstract

CfaPrec

createPrec(P innerPrec); - public abstract PrecRefiner, A, CfaPrec

, R> createRefiner( - RefutationToPrec refToPrec); + public abstract < + S extends ExprState, A extends Action, P extends Prec, R extends Refutation> + PrecRefiner, A, CfaPrec

, R> createRefiner( + RefutationToPrec refToPrec); public abstract CfaPrec assumePrecs(CFA cfa); } @@ -358,19 +569,37 @@ public CfaLts getLts(CFA.Loc errorLoc) { } public enum InitPrec { - EMPTY, ALLVARS, ALLASSUMES + EMPTY, + ALLVARS, + ALLASSUMES } public abstract class BuilderStrategy { - protected static final String UNSUPPORTED_CONFIG_VALUE = "Builder strategy %s does not support configuration value %s as %s"; + protected static final String UNSUPPORTED_CONFIG_VALUE = + "Builder strategy %s does not support configuration value %s as %s"; protected final CFA cfa; @SuppressWarnings("java:S1699") protected BuilderStrategy(CFA cfa) { - checkState(getSupportedDomains().contains(domain), UNSUPPORTED_CONFIG_VALUE, getClass().getSimpleName(), domain, "domain"); - checkState(getSupportedRefinements().contains(refinement), UNSUPPORTED_CONFIG_VALUE, getClass().getSimpleName(), refinement, "refinement"); - checkState(getSupportedInitPrecs().contains(initPrec), UNSUPPORTED_CONFIG_VALUE, getClass().getSimpleName(), initPrec, "initial precision"); + checkState( + getSupportedDomains().contains(domain), + UNSUPPORTED_CONFIG_VALUE, + getClass().getSimpleName(), + domain, + "domain"); + checkState( + getSupportedRefinements().contains(refinement), + UNSUPPORTED_CONFIG_VALUE, + getClass().getSimpleName(), + refinement, + "refinement"); + checkState( + getSupportedInitPrecs().contains(initPrec), + UNSUPPORTED_CONFIG_VALUE, + getClass().getSimpleName(), + initPrec, + "initial precision"); this.cfa = cfa; } @@ -389,7 +618,10 @@ public CfaAnalysis getAnalysis() { public abstract RefutationToPrec getItpRefToPrec(); public RefutationToPrec getVarsRefToPrec() { - throw new UnsupportedOperationException(String.format("Builder strategy %s can not provide Vars refutation to precision", getClass().getSimpleName())); + throw new UnsupportedOperationException( + String.format( + "Builder strategy %s can not provide Vars refutation to precision", + getClass().getSimpleName())); } protected SolverFactory getRefinementSolverFactory() { @@ -421,22 +653,30 @@ public CfaLts getLts() { public CfaConfig, CfaAction, CfaPrec

> buildConfig(CFA.Loc errLoc) { final Predicate> target = new CfaErrorlocPredicate<>(errLoc); final Analysis, CfaAction, CfaPrec

> analysis = getAnalysis(); - final ArgBuilder, CfaAction, CfaPrec

> argBuilder = ArgBuilder.create( - getLts(errLoc), analysis, target, - true); - final Abstractor, CfaAction, CfaPrec

> abstractor = BasicAbstractor.builder( - argBuilder) - .waitlist(PriorityWaitlist.create(search.getComp(cfa, errLoc))) - .stopCriterion(refinement.getStopCriterion()) - .logger(logger).build(); - final Refiner, CfaAction, CfaPrec

> refiner = refinement.getRefiner(this); - final SafetyChecker, CfaAction>, Trace, CfaAction>, CfaPrec

> checker = CegarChecker.create( - abstractor, refiner, - logger); + final ArgBuilder, CfaAction, CfaPrec

> argBuilder = + ArgBuilder.create(getLts(errLoc), analysis, target, true); + final ArgAbstractor, CfaAction, CfaPrec

> abstractor = + BasicArgAbstractor.builder(argBuilder) + .waitlist(PriorityWaitlist.create(search.getComp(cfa, errLoc))) + .stopCriterion(refinement.getStopCriterion()) + .logger(logger) + .build(); + final ArgRefiner, CfaAction, CfaPrec

> refiner = + refinement.getRefiner(this); + final SafetyChecker< + ARG, CfaAction>, Trace, CfaAction>, CfaPrec

> + checker = ArgCegarChecker.create(abstractor, refiner, logger); return CfaConfig.create(checker, createInitPrec()); } - public MultiAnalysisSide, S, CfaState, CfaAction, CfaPrec

, CfaPrec> getMultiSide() { + public MultiAnalysisSide< + CfaState, + S, + CfaState, + CfaAction, + CfaPrec

, + CfaPrec> + getMultiSide() { return new MultiAnalysisSide<>( getAnalysis(), CfaControlInitFuncKt.cfaControlInitFunc(cfa.getInitLoc()), @@ -470,7 +710,8 @@ public Set getSupportedInitPrecs() { @Override public Analysis getDataAnalysis() { - return ExplStmtAnalysis.create(abstractionSolverFactory.createSolver(), True(), maxEnum); + return ExplStmtAnalysis.create( + abstractionSolverFactory.createSolver(), True(), maxEnum); } @Override @@ -489,7 +730,12 @@ public CfaPrec createInitPrec() { case EMPTY -> precGranularity.createPrec(ExplPrec.empty()); case ALLVARS -> precGranularity.createPrec(ExplPrec.of(cfa.getVars())); default -> - throw new UnsupportedOperationException(String.format(UNSUPPORTED_CONFIG_VALUE, getClass().getSimpleName(), initPrec, "initial precision")); + throw new UnsupportedOperationException( + String.format( + UNSUPPORTED_CONFIG_VALUE, + getClass().getSimpleName(), + initPrec, + "initial precision")); }; } } @@ -520,8 +766,7 @@ public Set getSupportedRefinements() { Refinement.NWT_IT_SP, Refinement.NWT_IT_WP, Refinement.NWT_IT_SP_LV, - Refinement.NWT_IT_WP_LV - ); + Refinement.NWT_IT_WP_LV); } @Override @@ -546,9 +791,13 @@ public CfaPrec createInitPrec() { case EMPTY -> precGranularity.createPrec(PredPrec.of()); case ALLASSUMES -> precGranularity.assumePrecs(cfa); default -> - throw new UnsupportedOperationException(String.format(UNSUPPORTED_CONFIG_VALUE, getClass().getSimpleName(), initPrec, "initial precision")); + throw new UnsupportedOperationException( + String.format( + UNSUPPORTED_CONFIG_VALUE, + getClass().getSimpleName(), + initPrec, + "initial precision")); }; } } - } diff --git a/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/impact/CfaPredImpactCheckerTest.java b/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/impact/CfaPredImpactCheckerTest.java index 4204a3785d..111600967f 100644 --- a/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/impact/CfaPredImpactCheckerTest.java +++ b/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/impact/CfaPredImpactCheckerTest.java @@ -17,60 +17,65 @@ import static org.junit.Assert.assertTrue; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; - import hu.bme.mit.theta.analysis.Trace; import hu.bme.mit.theta.analysis.algorithm.SafetyResult; -import hu.bme.mit.theta.analysis.pred.PredState; -import hu.bme.mit.theta.cfa.analysis.CfaAction; -import hu.bme.mit.theta.cfa.analysis.CfaState; -import hu.bme.mit.theta.solver.Solver; -import org.junit.Test; - import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgChecker; import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.analysis.expr.ExprState; +import hu.bme.mit.theta.analysis.pred.PredState; import hu.bme.mit.theta.analysis.unit.UnitPrec; import hu.bme.mit.theta.analysis.utils.ArgVisualizer; import hu.bme.mit.theta.cfa.CFA; +import hu.bme.mit.theta.cfa.analysis.CfaAction; +import hu.bme.mit.theta.cfa.analysis.CfaState; import hu.bme.mit.theta.cfa.analysis.lts.CfaLbeLts; import hu.bme.mit.theta.cfa.dsl.CfaDslManager; import hu.bme.mit.theta.common.visualization.writer.GraphvizWriter; import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import org.junit.Test; public final class CfaPredImpactCheckerTest { @Test public void test() throws FileNotFoundException, IOException { // Arrange - final CFA cfa = CfaDslManager.createCfa( - new FileInputStream("src/test/resources/counter5_true.cfa")); + final CFA cfa = + CfaDslManager.createCfa( + new FileInputStream("src/test/resources/counter5_true.cfa")); final Solver abstractionSolver = Z3LegacySolverFactory.getInstance().createSolver(); final ItpSolver refinementSolver = Z3LegacySolverFactory.getInstance().createItpSolver(); - final PredImpactChecker checker = PredImpactChecker.create( - CfaLbeLts.of(cfa.getErrorLoc().get()), cfa.getInitLoc(), - l -> l.equals(cfa.getErrorLoc().get()), abstractionSolver, refinementSolver); + final PredImpactChecker checker = + PredImpactChecker.create( + CfaLbeLts.of(cfa.getErrorLoc().get()), + cfa.getInitLoc(), + l -> l.equals(cfa.getErrorLoc().get()), + abstractionSolver, + refinementSolver); // Act - final SafetyResult, CfaAction>, Trace, CfaAction>> status = checker.check( - UnitPrec.getInstance()); + final SafetyResult< + ARG, CfaAction>, Trace, CfaAction>> + status = checker.check(UnitPrec.getInstance()); // Assert assertTrue(status.isSafe()); - final ARG arg = status.getWitness(); + final ARG arg = status.getProof(); arg.minimize(); final ArgChecker argChecker = ArgChecker.create(abstractionSolver); assertTrue(argChecker.isWellLabeled(arg)); System.out.println( - GraphvizWriter.getInstance().writeString(ArgVisualizer.getDefault().visualize(arg))); + GraphvizWriter.getInstance() + .writeString(ArgVisualizer.getDefault().visualize(arg))); } -} \ No newline at end of file +} diff --git a/subprojects/cfa/cfa-cli/src/main/java/hu/bme/mit/theta/cfa/cli/CfaCli.java b/subprojects/cfa/cfa-cli/src/main/java/hu/bme/mit/theta/cfa/cli/CfaCli.java index 024911743e..eda6c15702 100644 --- a/subprojects/cfa/cfa-cli/src/main/java/hu/bme/mit/theta/cfa/cli/CfaCli.java +++ b/subprojects/cfa/cfa-cli/src/main/java/hu/bme/mit/theta/cfa/cli/CfaCli.java @@ -15,6 +15,8 @@ */ package hu.bme.mit.theta.cfa.cli; +import static com.google.common.base.Preconditions.checkNotNull; + import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; @@ -27,8 +29,6 @@ import hu.bme.mit.theta.analysis.algorithm.bounded.MonolithicExpr; import hu.bme.mit.theta.analysis.algorithm.cegar.CegarStatistics; import hu.bme.mit.theta.analysis.expl.ExplState; -import hu.bme.mit.theta.analysis.expr.ExprAction; -import hu.bme.mit.theta.analysis.expr.ExprState; import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy; import hu.bme.mit.theta.cfa.CFA; import hu.bme.mit.theta.cfa.analysis.CfaAction; @@ -37,14 +37,7 @@ import hu.bme.mit.theta.cfa.analysis.CfaTraceConcretizer; import hu.bme.mit.theta.cfa.analysis.config.CfaConfig; import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder; -import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Algorithm; -import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Domain; -import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Encoding; -import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.InitPrec; -import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.PrecGranularity; -import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.PredSplit; -import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Refinement; -import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Search; +import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.*; import hu.bme.mit.theta.cfa.analysis.utils.CfaVisualizer; import hu.bme.mit.theta.cfa.dsl.CfaDslManager; import hu.bme.mit.theta.common.CliUtils; @@ -62,28 +55,21 @@ import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager; import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory; import hu.bme.mit.theta.solver.z3legacy.Z3SolverManager; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.io.StringWriter; +import java.io.*; import java.nio.file.Path; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * A command line interface for running a CEGAR configuration on a CFA. - */ +/** A command line interface for running a CEGAR configuration on a CFA. */ public class CfaCli { private static final String JAR_NAME = "theta-cfa-cli.jar"; private final String[] args; private final TableWriter writer; - @Parameter(names = {"--algorithm"}, description = "Algorithm") + + @Parameter( + names = {"--algorithm"}, + description = "Algorithm") Algorithm algorithm = Algorithm.CEGAR; @Parameter(names = "--domain", description = "Abstract domain") @@ -95,16 +81,34 @@ public class CfaCli { @Parameter(names = "--search", description = "Search strategy") Search search = Search.BFS; - @Parameter(names = "--predsplit", description = "Predicate splitting (for predicate abstraction)") + @Parameter( + names = "--predsplit", + description = "Predicate splitting (for predicate abstraction)") PredSplit predSplit = PredSplit.WHOLE; - @Parameter(names = "--solver", description = "Sets the underlying SMT solver to use for both the abstraction and the refinement process. Enter in format :, see theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy z3 solver.") + @Parameter( + names = "--solver", + description = + "Sets the underlying SMT solver to use for both the abstraction and the" + + " refinement process. Enter in format :, see" + + " theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy" + + " z3 solver.") String solver = "Z3"; - @Parameter(names = "--abstraction-solver", description = "Sets the underlying SMT solver to use for the abstraction process. Enter in format :, see theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy z3 solver.") + @Parameter( + names = "--abstraction-solver", + description = + "Sets the underlying SMT solver to use for the abstraction process. Enter in" + + " format :, see theta-smtlib-cli.jar for" + + " more details. Enter \"Z3\" to use the legacy z3 solver.") String abstractionSolver; - @Parameter(names = "--refinement-solver", description = "Sets the underlying SMT solver to use for the refinement process. Enter in format :, see theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy z3 solver.") + @Parameter( + names = "--refinement-solver", + description = + "Sets the underlying SMT solver to use for the refinement process. Enter in" + + " format :, see theta-smtlib-cli.jar for" + + " more details. Enter \"Z3\" to use the legacy z3 solver.") String refinementSolver; @Parameter(names = "--home", description = "The path of the solver registry") @@ -122,13 +126,17 @@ public class CfaCli { @Parameter(names = "--encoding", description = "Block encoding") Encoding encoding = Encoding.LBE; - @Parameter(names = "--maxenum", description = "Maximal number of explicitly enumerated successors (0: unlimited)") + @Parameter( + names = "--maxenum", + description = "Maximal number of explicitly enumerated successors (0: unlimited)") Integer maxEnum = 10; @Parameter(names = "--initprec", description = "Initial precision of abstraction") InitPrec initPrec = InitPrec.EMPTY; - @Parameter(names = "--prunestrategy", description = "Strategy for pruning the ARG after refinement") + @Parameter( + names = "--prunestrategy", + description = "Strategy for pruning the ARG after refinement") PruneStrategy pruneStrategy = PruneStrategy.LAZY; @Parameter(names = "--loglevel", description = "Detailedness of logging") @@ -140,13 +148,20 @@ public class CfaCli { @Parameter(names = "--cex", description = "Write concrete counterexample to a file") String cexfile = null; - @Parameter(names = "--header", description = "Print only a header (for benchmarks)", help = true) + @Parameter( + names = "--header", + description = "Print only a header (for benchmarks)", + help = true) boolean headerOnly = false; - @Parameter(names = "--visualize", description = "Visualize CFA to this file without running the algorithm") + @Parameter( + names = "--visualize", + description = "Visualize CFA to this file without running the algorithm") String visualize = null; - @Parameter(names = "--metrics", description = "Print metrics about the CFA without running the algorithm") + @Parameter( + names = "--metrics", + description = "Print metrics about the CFA without running the algorithm") boolean metrics = false; @Parameter(names = "--stacktrace", description = "Print full stack trace in case of exception") @@ -241,13 +256,19 @@ private void run() { final SafetyResult> status; if (algorithm == Algorithm.CEGAR) { - final CfaConfig configuration = buildConfiguration(cfa, errLoc, abstractionSolverFactory, refinementSolverFactory); + final CfaConfig configuration = + buildConfiguration( + cfa, errLoc, abstractionSolverFactory, refinementSolverFactory); status = check(configuration); - } else if (algorithm == Algorithm.BMC || algorithm == Algorithm.KINDUCTION || algorithm == Algorithm.IMC) { - final BoundedChecker checker = buildBoundedChecker(cfa, abstractionSolverFactory); + } else if (algorithm == Algorithm.BMC + || algorithm == Algorithm.KINDUCTION + || algorithm == Algorithm.IMC) { + final BoundedChecker checker = + buildBoundedChecker(cfa, abstractionSolverFactory); status = checker.check(null); } else { - throw new UnsupportedOperationException("Algorithm " + algorithm + " not supported"); + throw new UnsupportedOperationException( + "Algorithm " + algorithm + " not supported"); } sw.stop(); @@ -262,8 +283,18 @@ private void run() { } private void printHeader() { - Stream.of("Result", "TimeMs", "AlgoTimeMs", "AbsTimeMs", "RefTimeMs", "Iterations", - "ArgSize", "ArgDepth", "ArgMeanBranchFactor", "CexLen").forEach(writer::cell); + Stream.of( + "Result", + "TimeMs", + "AlgoTimeMs", + "AbsTimeMs", + "RefTimeMs", + "Iterations", + "ArgSize", + "ArgDepth", + "ArgMeanBranchFactor", + "CexLen") + .forEach(writer::cell); writer.newRow(); } @@ -277,67 +308,89 @@ private CFA loadModel() throws Exception { } } - private CfaConfig buildConfiguration(final CFA cfa, final CFA.Loc errLoc, - final SolverFactory abstractionSolverFactory, final SolverFactory refinementSolverFactory) + private CfaConfig buildConfiguration( + final CFA cfa, + final CFA.Loc errLoc, + final SolverFactory abstractionSolverFactory, + final SolverFactory refinementSolverFactory) throws Exception { try { - return new CfaConfigBuilder(domain, refinement, abstractionSolverFactory, - refinementSolverFactory) - .precGranularity(precGranularity).search(search) - .predSplit(predSplit).encoding(encoding).maxEnum(maxEnum).initPrec(initPrec) - .pruneStrategy(pruneStrategy).logger(logger).build(cfa, errLoc); + return new CfaConfigBuilder( + domain, refinement, abstractionSolverFactory, refinementSolverFactory) + .precGranularity(precGranularity) + .search(search) + .predSplit(predSplit) + .encoding(encoding) + .maxEnum(maxEnum) + .initPrec(initPrec) + .pruneStrategy(pruneStrategy) + .logger(logger) + .build(cfa, errLoc); } catch (final Exception ex) { throw new Exception("Could not create configuration: " + ex.getMessage(), ex); } } - private BoundedChecker buildBoundedChecker(final CFA cfa, final SolverFactory abstractionSolverFactory) { + private BoundedChecker buildBoundedChecker( + final CFA cfa, final SolverFactory abstractionSolverFactory) { final MonolithicExpr monolithicExpr = CfaToMonolithicExprKt.toMonolithicExpr(cfa); final BoundedChecker checker; switch (algorithm) { - case BMC -> checker = BoundedCheckerBuilderKt.buildBMC( - monolithicExpr, - abstractionSolverFactory.createSolver(), - val -> CfaToMonolithicExprKt.valToState(cfa, val), - (val1, val2) -> CfaToMonolithicExprKt.valToAction(cfa, val1, val2), - logger - ); - case KINDUCTION -> checker = BoundedCheckerBuilderKt.buildKIND( - monolithicExpr, - abstractionSolverFactory.createSolver(), - abstractionSolverFactory.createSolver(), - val -> CfaToMonolithicExprKt.valToState(cfa, val), - (val1, val2) -> CfaToMonolithicExprKt.valToAction(cfa, val1, val2), - logger - ); - case IMC -> checker = BoundedCheckerBuilderKt.buildIMC( - monolithicExpr, - abstractionSolverFactory.createSolver(), - abstractionSolverFactory.createItpSolver(), - val -> CfaToMonolithicExprKt.valToState(cfa, val), - (val1, val2) -> CfaToMonolithicExprKt.valToAction(cfa, val1, val2), - logger - ); + case BMC -> + checker = + BoundedCheckerBuilderKt.buildBMC( + monolithicExpr, + abstractionSolverFactory.createSolver(), + val -> CfaToMonolithicExprKt.valToState(cfa, val), + (val1, val2) -> + CfaToMonolithicExprKt.valToAction(cfa, val1, val2), + logger); + case KINDUCTION -> + checker = + BoundedCheckerBuilderKt.buildKIND( + monolithicExpr, + abstractionSolverFactory.createSolver(), + abstractionSolverFactory.createSolver(), + val -> CfaToMonolithicExprKt.valToState(cfa, val), + (val1, val2) -> + CfaToMonolithicExprKt.valToAction(cfa, val1, val2), + logger); + case IMC -> + checker = + BoundedCheckerBuilderKt.buildIMC( + monolithicExpr, + abstractionSolverFactory.createSolver(), + abstractionSolverFactory.createItpSolver(), + val -> CfaToMonolithicExprKt.valToState(cfa, val), + (val1, val2) -> + CfaToMonolithicExprKt.valToAction(cfa, val1, val2), + logger); default -> - throw new UnsupportedOperationException("Algorithm " + algorithm + " not supported"); + throw new UnsupportedOperationException( + "Algorithm " + algorithm + " not supported"); } return checker; } - private SafetyResult, ? extends Trace> check(CfaConfig configuration) throws Exception { + private SafetyResult, ? extends Trace> check( + CfaConfig configuration) throws Exception { try { return configuration.check(); } catch (final Exception ex) { String message = ex.getMessage() == null ? "(no message)" : ex.getMessage(); throw new Exception( - "Error while running algorithm: " + ex.getClass().getSimpleName() + " " + message, + "Error while running algorithm: " + + ex.getClass().getSimpleName() + + " " + + message, ex); } } - private void printResult(final SafetyResult> status, final long totalTimeMs) { - final CegarStatistics stats = (CegarStatistics) - status.getStats().orElse(new CegarStatistics(0, 0, 0, 0)); + private void printResult( + final SafetyResult> status, final long totalTimeMs) { + final CegarStatistics stats = + (CegarStatistics) status.getStats().orElse(new CegarStatistics(0, 0, 0, 0)); if (benchmarkMode) { writer.cell(status.isSafe()); writer.cell(totalTimeMs); @@ -345,7 +398,7 @@ private void printResult(final SafetyResult> status, fi writer.cell(stats.getAbstractorTimeMs()); writer.cell(stats.getRefinerTimeMs()); writer.cell(stats.getIterations()); - if (status.getWitness() instanceof ARG arg) { + if (status.getProof() instanceof ARG arg) { writer.cell(arg.size()); writer.cell(arg.getDepth()); writer.cell(arg.getMeanBranchingFactor()); @@ -369,7 +422,10 @@ private void printError(final Throwable ex) { writer.cell("[EX] " + ex.getClass().getSimpleName() + ": " + message); writer.newRow(); } else { - logger.write(Level.RESULT, "%s occurred, message: %s%n", ex.getClass().getSimpleName(), + logger.write( + Level.RESULT, + "%s occurred, message: %s%n", + ex.getClass().getSimpleName(), message); if (stacktrace) { final StringWriter errors = new StringWriter(); @@ -382,9 +438,10 @@ private void printError(final Throwable ex) { } private void writeCex(final SafetyResult.Unsafe status) throws FileNotFoundException { - @SuppressWarnings("unchecked") final Trace, CfaAction> trace = (Trace, CfaAction>) status.getCex(); - final Trace, CfaAction> concrTrace = CfaTraceConcretizer.concretize( - trace, Z3LegacySolverFactory.getInstance()); + @SuppressWarnings("unchecked") + final Trace, CfaAction> trace = (Trace, CfaAction>) status.getCex(); + final Trace, CfaAction> concrTrace = + CfaTraceConcretizer.concretize(trace, Z3LegacySolverFactory.getInstance()); final File file = new File(cexfile); PrintWriter printWriter = null; try { diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Checker.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Checker.java index e1a86bee69..afed36eab9 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Checker.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Checker.java @@ -15,8 +15,7 @@ */ package hu.bme.mit.theta.analysis.algorithm; -public interface Checker { - - Result check(I input); +public interface Checker { + Result check(I input); } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/EmptyWitness.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/EmptyProof.java similarity index 78% rename from subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/EmptyWitness.java rename to subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/EmptyProof.java index 49794dd84e..1c8d976149 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/EmptyWitness.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/EmptyProof.java @@ -15,15 +15,13 @@ */ package hu.bme.mit.theta.analysis.algorithm; -public class EmptyWitness implements Witness { +public class EmptyProof implements Proof { - private final static EmptyWitness empty = new EmptyWitness(); + private static final EmptyProof empty = new EmptyProof(); - private EmptyWitness() { - } + private EmptyProof() {} - public static EmptyWitness getInstance() { + public static EmptyProof getInstance() { return empty; } - } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Witness.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Proof.java similarity index 95% rename from subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Witness.java rename to subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Proof.java index ff6157e1e3..3668ab6315 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Witness.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Proof.java @@ -15,5 +15,4 @@ */ package hu.bme.mit.theta.analysis.algorithm; -public interface Witness { -} +public interface Proof {} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Result.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Result.java index 0c81bf5836..438edde295 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Result.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/Result.java @@ -17,10 +17,9 @@ import java.util.Optional; -public interface Result { +public interface Result { - W getWitness(); + Pr getProof(); Optional getStats(); - } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/SafetyChecker.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/SafetyChecker.java index 4f13fac64c..c716c1dc24 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/SafetyChecker.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/SafetyChecker.java @@ -15,19 +15,15 @@ */ package hu.bme.mit.theta.analysis.algorithm; -import hu.bme.mit.theta.analysis.*; -import hu.bme.mit.theta.analysis.algorithm.Checker; -import hu.bme.mit.theta.analysis.algorithm.SafetyResult; -import hu.bme.mit.theta.analysis.algorithm.arg.ARG; +import hu.bme.mit.theta.analysis.Cex; @FunctionalInterface -public interface SafetyChecker extends Checker { +public interface SafetyChecker extends Checker { @Override - SafetyResult check(final I input); + SafetyResult check(final I input); - default SafetyResult check() { + default SafetyResult check() { return check(null); } - } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/SafetyResult.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/SafetyResult.java index 6ee1284600..7e3af4eb5d 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/SafetyResult.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/SafetyResult.java @@ -14,33 +14,28 @@ * limitations under the License. */ package hu.bme.mit.theta.analysis.algorithm; - -import hu.bme.mit.theta.analysis.Action; import hu.bme.mit.theta.analysis.Cex; -import hu.bme.mit.theta.analysis.State; import hu.bme.mit.theta.common.Utils; - import java.util.Optional; - import static com.google.common.base.Preconditions.checkNotNull; -public abstract class SafetyResult implements Result { - private final W witness; +public abstract class SafetyResult implements Result { + private final Pr proof; private final Optional stats; - private SafetyResult(final W witness, final Optional stats) { - this.witness = checkNotNull(witness); + private SafetyResult(final Pr proof, final Optional stats) { + this.proof = checkNotNull(proof); this.stats = checkNotNull(stats); } private SafetyResult() { - this.witness = null; + this.proof = null; this.stats = Optional.empty(); } @Override - public W getWitness() { - return witness; + public Pr getProof() { + return proof; } @Override @@ -48,28 +43,30 @@ public Optional getStats() { return stats; } - public static Safe safe(final W witness) { + public static Safe safe(final Pr witness) { return new Safe<>(witness, Optional.empty()); } - public static Unsafe unsafe(final C cex, final W witness) { + public static Unsafe unsafe( + final C cex, final Pr witness) { return new Unsafe<>(cex, witness, Optional.empty()); } - public static Unknown unknown() { + public static Unknown unknown() { return new Unknown<>(); } - public static Safe safe(final W witness, final Statistics stats) { + public static Safe safe( + final Pr witness, final Statistics stats) { return new Safe<>(witness, Optional.of(stats)); } - public static Unsafe unsafe(final C cex, final W witness, - final Statistics stats) { + public static Unsafe unsafe( + final C cex, final Pr witness, final Statistics stats) { return new Unsafe<>(cex, witness, Optional.of(stats)); } - public static Unknown unknown(final Statistics stats) { + public static Unknown unknown(final Statistics stats) { return new Unknown<>(Optional.of(stats)); } @@ -77,15 +74,15 @@ public static Unknown unknown(final Sta public abstract boolean isUnsafe(); - public abstract Safe asSafe(); + public abstract Safe asSafe(); - public abstract Unsafe asUnsafe(); + public abstract Unsafe asUnsafe(); //// - public static final class Safe extends SafetyResult { - private Safe(final W witness, final Optional stats) { - super(witness, stats); + public static final class Safe extends SafetyResult { + private Safe(final Pr proof, final Optional stats) { + super(proof, stats); } @Override @@ -99,28 +96,32 @@ public boolean isUnsafe() { } @Override - public Safe asSafe() { + public Safe asSafe() { return this; } @Override - public Unsafe asUnsafe() { + public Unsafe asUnsafe() { throw new ClassCastException( - "Cannot cast " + Safe.class.getSimpleName() + " to " + Unsafe.class.getSimpleName()); + "Cannot cast " + + Safe.class.getSimpleName() + + " to " + + Unsafe.class.getSimpleName()); } @Override public String toString() { - return Utils.lispStringBuilder(SafetyResult.class.getSimpleName()).add(Safe.class.getSimpleName()) + return Utils.lispStringBuilder(SafetyResult.class.getSimpleName()) + .add(Safe.class.getSimpleName()) .toString(); } } - public static final class Unsafe extends SafetyResult { + public static final class Unsafe extends SafetyResult { private final C cex; - private Unsafe(final C cex, final W witness, final Optional stats) { - super(witness, stats); + private Unsafe(final C cex, final Pr proof, final Optional stats) { + super(proof, stats); this.cex = checkNotNull(cex); } @@ -139,24 +140,29 @@ public boolean isUnsafe() { } @Override - public Safe asSafe() { + public Safe asSafe() { throw new ClassCastException( - "Cannot cast " + Unsafe.class.getSimpleName() + " to " + Safe.class.getSimpleName()); + "Cannot cast " + + Unsafe.class.getSimpleName() + + " to " + + Safe.class.getSimpleName()); } @Override - public Unsafe asUnsafe() { + public Unsafe asUnsafe() { return this; } @Override public String toString() { - return Utils.lispStringBuilder(SafetyResult.class.getSimpleName()).add(Unsafe.class.getSimpleName()) - .add("Trace length: " + cex.length()).toString(); + return Utils.lispStringBuilder(SafetyResult.class.getSimpleName()) + .add(Unsafe.class.getSimpleName()) + .add("Trace length: " + cex.length()) + .toString(); } } - public static final class Unknown extends SafetyResult { + public static final class Unknown extends SafetyResult { public Unknown() { super(); @@ -177,12 +183,12 @@ public boolean isUnsafe() { } @Override - public Safe asSafe() { + public Safe asSafe() { return null; } @Override - public Unsafe asUnsafe() { + public Unsafe asUnsafe() { return null; } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/arg/ARG.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/arg/ARG.java index b43fb37e35..9f2b3c3ae0 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/arg/ARG.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/arg/ARG.java @@ -15,28 +15,22 @@ */ package hu.bme.mit.theta.analysis.algorithm.arg; +import static com.google.common.base.Preconditions.*; +import static java.util.stream.Collectors.toList; + import hu.bme.mit.theta.analysis.Action; import hu.bme.mit.theta.analysis.PartialOrd; import hu.bme.mit.theta.analysis.State; -import hu.bme.mit.theta.analysis.algorithm.Witness; +import hu.bme.mit.theta.analysis.algorithm.Proof; import hu.bme.mit.theta.analysis.algorithm.arg.debug.ARGWebDebugger; import hu.bme.mit.theta.common.container.Containers; - import java.util.Collection; import java.util.Optional; import java.util.OptionalInt; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static java.util.stream.Collectors.toList; - -/** - * Represents an abstract reachability graph (ARG). See the related class - * ArgBuilder. - */ -public final class ARG implements Witness { +/** Represents an abstract reachability graph (ARG). See the related class ArgBuilder. */ +public final class ARG implements Proof { private final Collection> initNodes; public boolean initialized; // Set by ArgBuilder @@ -49,7 +43,8 @@ private ARG(final PartialOrd partialOrd) { this.initialized = false; } - public static ARG create(final PartialOrd partialOrd) { + public static ARG create( + final PartialOrd partialOrd) { return new ARG<>(partialOrd); } @@ -75,7 +70,6 @@ public Stream> getIncompleteNodes() { return getInitNodes().flatMap(ArgNode::unexcludedDescendants).filter(n -> !n.isExpanded()); } - PartialOrd getPartialOrd() { return partialOrd; } @@ -83,24 +77,19 @@ PartialOrd getPartialOrd() { //// /** - * Checks if the ARG is complete, i.e., whether it is initialized and all of - * its nodes are complete. + * Checks if the ARG is complete, i.e., whether it is initialized and all of its nodes are + * complete. */ public boolean isComplete() { return isInitialized() && getNodes().allMatch(ArgNode::isComplete); } - /** - * Checks if the ARG is safe, i.e., whether all of its nodes are safe. - */ + /** Checks if the ARG is safe, i.e., whether all of its nodes are safe. */ public boolean isSafe() { return getNodes().allMatch(ArgNode::isSafe); } - /** - * Checks if the ARG is initialized, i.e., all of its initial nodes are - * present. - */ + /** Checks if the ARG is initialized, i.e., all of its initial nodes are present. */ public boolean isInitialized() { return initialized; } @@ -115,8 +104,8 @@ public ArgNode createInitNode(final S initState, final boolean target) { return initNode; } - public ArgNode createSuccNode(final ArgNode node, final A action, final S succState, - final boolean target) { + public ArgNode createSuccNode( + final ArgNode node, final A action, final S succState, final boolean target) { checkNotNull(node); checkNotNull(action); checkNotNull(succState); @@ -133,7 +122,8 @@ private ArgNode createNode(final S state, final int depth, final boolean t return node; } - private ArgEdge createEdge(final ArgNode source, final A action, final ArgNode target) { + private ArgEdge createEdge( + final ArgNode source, final A action, final ArgNode target) { final ArgEdge edge = new ArgEdge<>(source, action, target); source.outEdges.add(edge); target.inEdge = Optional.of(edge); @@ -141,9 +131,7 @@ private ArgEdge createEdge(final ArgNode source, final A action, fin return edge; } - /** - * Removes a node along with its subtree. - */ + /** Removes a node along with its subtree. */ public void prune(final ArgNode node) { checkNotNull(node); checkArgument(node.arg == this, "Node does not belong to this ARG"); @@ -162,17 +150,13 @@ public void prune(final ArgNode node) { node.descendants().forEach(ArgNode::clearCoveredNodes); } - /** - * Prune the whole ARG, making it uninitialized. - */ + /** Prune the whole ARG, making it uninitialized. */ public void pruneAll() { initNodes.clear(); this.initialized = false; } - /** - * Marks the node for reexpanding without pruning it. - */ + /** Marks the node for reexpanding without pruning it. */ public void markForReExpansion(final ArgNode node) { node.expanded = false; } @@ -192,24 +176,19 @@ private void minimizeSubTree(final ArgNode node) { //// - /** - * Gets all counterexamples, i.e., traces leading to target nodes. - */ + /** Gets all counterexamples, i.e., traces leading to target nodes. */ public Stream> getCexs() { return getUnsafeNodes().map(ArgTrace::to); } - /** - * Gets the size of the ARG, i.e., the number of nodes. - */ + /** Gets the size of the ARG, i.e., the number of nodes. */ public long size() { return getNodes().count(); } /** - * Gets the depth of the ARG, i.e., the maximal depth of its nodes. Depth - * starts (at the initial nodes) from 0. Depth is undefined for an empty - * ARG. + * Gets the depth of the ARG, i.e., the maximal depth of its nodes. Depth starts (at the initial + * nodes) from 0. Depth is undefined for an empty ARG. */ public int getDepth() { final OptionalInt maxOpt = getNodes().mapToInt(ArgNode::getDepth).max(); @@ -217,12 +196,11 @@ public int getDepth() { return maxOpt.getAsInt(); } - /** - * Gets the mean branching factor of the expanded nodes. - */ + /** Gets the mean branching factor of the expanded nodes. */ public double getMeanBranchingFactor() { final Stream> nodesToCalculate = getNodes().filter(ArgNode::isExpanded); - final double mean = nodesToCalculate.mapToDouble(n -> n.getOutEdges().count()).average().orElse(0); + final double mean = + nodesToCalculate.mapToDouble(n -> n.getOutEdges().count()).average().orElse(0); return mean; } } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/bounded/BoundedChecker.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/bounded/BoundedChecker.kt index 72b90fe53f..2c1273f87f 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/bounded/BoundedChecker.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/bounded/BoundedChecker.kt @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.bounded import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness +import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.expr.ExprAction @@ -43,228 +42,246 @@ import java.util.* * @param The state type, must inherit from ExprState. * @param The action type, must inherit from StmtAction. * @param monolithicExpr The monolithic expression to be checked - * @param shouldGiveUp A function determining whether to give up checking based on a given iteration count. Use this - * to implement custom timeout or thread interruption checking subroutines. + * @param shouldGiveUp A function determining whether to give up checking based on a given iteration + * count. Use this to implement custom timeout or thread interruption checking subroutines. * @param bmcSolver The solver for bounded model checking. - * @param bmcEnabled A function determining whether bounded model checking is enabled. Cannot be disabled per-iteration. - * Use the capabilities of the lambda parameter to decide on enabledness based on external factors, - * such as available memory or time limit remaining. + * @param bmcEnabled A function determining whether bounded model checking is enabled. Cannot be + * disabled per-iteration. Use the capabilities of the lambda parameter to decide on enabledness + * based on external factors, such as available memory or time limit remaining. * @param lfPathOnly A function determining whether to consider only loop-free paths. * @param itpSolver The solver for interpolation, used in IMC. * @param imcEnabled A function determining whether IMC is enabled. Can be different per-iteration. * @param indSolver The solver for induction checking in KIND. * @param kindEnabled A function determining whether k-induction (KIND) is enabled. - * @param valToState A function mapping valuations to expression states, used to construct a counterexample. - * @param biValToAction A function mapping pairs of valuations to statements, used to construct a counterexample. + * @param valToState A function mapping valuations to expression states, used to construct a + * counterexample. + * @param biValToAction A function mapping pairs of valuations to statements, used to construct a + * counterexample. * @param logger The logger for logging. */ -class BoundedChecker @JvmOverloads constructor( - private val monolithicExpr: MonolithicExpr, - private val shouldGiveUp: (Int) -> Boolean = { false }, - private val bmcSolver: Solver? = null, - private val bmcEnabled: () -> Boolean = { bmcSolver != null }, - private val lfPathOnly: () -> Boolean = { true }, - private val itpSolver: ItpSolver? = null, - private val imcEnabled: (Int) -> Boolean = { itpSolver != null }, - private val indSolver: Solver? = null, - private val kindEnabled: (Int) -> Boolean = { indSolver != null }, - private val valToState: (Valuation) -> S, - private val biValToAction: (Valuation, Valuation) -> A, - private val logger: Logger, -) : SafetyChecker, UnitPrec> { - - private val vars = monolithicExpr.vars() - private val unfoldedInitExpr = PathUtils.unfold(monolithicExpr.initExpr, VarIndexingFactory.indexing(0)) - private val unfoldedPropExpr = { i: VarIndexing -> PathUtils.unfold(monolithicExpr.propExpr, i) } - private val indices = mutableListOf(monolithicExpr.initOffsetIndex) - private val exprs = mutableListOf>() - private var kindLastIterLookup = 0 - private var iteration = 0 - - init { - check(bmcSolver != itpSolver || bmcSolver == null) { "Use distinct solvers for BMC and IMC!" } - check(bmcSolver != indSolver || bmcSolver == null) { "Use distinct solvers for BMC and KInd!" } - check(itpSolver != indSolver || itpSolver == null) { "Use distinct solvers for IMC and KInd!" } - } - - override fun check(prec: UnitPrec?): SafetyResult> { - - iteration = 0 - - val isBmcEnabled = bmcEnabled() // we don't allow per-iteration setting of bmc enabledness - bmcSolver?.add(unfoldedInitExpr) - - while (!shouldGiveUp(iteration)) { - iteration++ - logger.write(Logger.Level.MAINSTEP, "Starting iteration $iteration\n") - - exprs.add(PathUtils.unfold(monolithicExpr.transExpr, indices.last())) - - indices.add(indices.last().add(monolithicExpr.transOffsetIndex)) - - if (isBmcEnabled) { - bmc()?.let { return it } - } +class BoundedChecker +@JvmOverloads +constructor( + private val monolithicExpr: MonolithicExpr, + private val shouldGiveUp: (Int) -> Boolean = { false }, + private val bmcSolver: Solver? = null, + private val bmcEnabled: () -> Boolean = { bmcSolver != null }, + private val lfPathOnly: () -> Boolean = { true }, + private val itpSolver: ItpSolver? = null, + private val imcEnabled: (Int) -> Boolean = { itpSolver != null }, + private val indSolver: Solver? = null, + private val kindEnabled: (Int) -> Boolean = { indSolver != null }, + private val valToState: (Valuation) -> S, + private val biValToAction: (Valuation, Valuation) -> A, + private val logger: Logger, +) : SafetyChecker, UnitPrec> { + + private val vars = monolithicExpr.vars() + private val unfoldedInitExpr = + PathUtils.unfold(monolithicExpr.initExpr, VarIndexingFactory.indexing(0)) + private val unfoldedPropExpr = { i: VarIndexing -> PathUtils.unfold(monolithicExpr.propExpr, i) } + private val indices = mutableListOf(monolithicExpr.initOffsetIndex) + private val exprs = mutableListOf>() + private var kindLastIterLookup = 0 + private var iteration = 0 + + init { + check(bmcSolver != itpSolver || bmcSolver == null) { "Use distinct solvers for BMC and IMC!" } + check(bmcSolver != indSolver || bmcSolver == null) { "Use distinct solvers for BMC and KInd!" } + check(itpSolver != indSolver || itpSolver == null) { "Use distinct solvers for IMC and KInd!" } + } + + override fun check(prec: UnitPrec?): SafetyResult> { + + iteration = 0 + + val isBmcEnabled = bmcEnabled() // we don't allow per-iteration setting of bmc enabledness + bmcSolver?.add(unfoldedInitExpr) + + while (!shouldGiveUp(iteration)) { + iteration++ + logger.write(Logger.Level.MAINSTEP, "Starting iteration $iteration\n") + + exprs.add(PathUtils.unfold(monolithicExpr.transExpr, indices.last())) + + indices.add(indices.last().add(monolithicExpr.transOffsetIndex)) + + if (isBmcEnabled) { + bmc()?.let { + return it + } + } - if (kindEnabled(iteration)) { - if (!isBmcEnabled) { - error("Bad configuration: induction check should always be preceded by a BMC/SAT check") - } - kind()?.let { return it } - kindLastIterLookup = iteration - } + if (kindEnabled(iteration)) { + if (!isBmcEnabled) { + error("Bad configuration: induction check should always be preceded by a BMC/SAT check") + } + kind()?.let { + return it + } + kindLastIterLookup = iteration + } - if (imcEnabled(iteration)) { - itp()?.let { return it } - } + if (imcEnabled(iteration)) { + itp()?.let { + return it } - return SafetyResult.unknown(BoundedStatistics(iteration)) + } } - - private fun bmc(): SafetyResult>? { - val bmcSolver = this.bmcSolver!! - logger.write(Logger.Level.MAINSTEP, "\tStarting BMC\n") - - bmcSolver.add(exprs.last()) - - if (lfPathOnly()) { // indices contains currIndex as last() - for (indexing in indices) { - if (indexing != indices.last()) { - val allVarsSame = And(vars.map { - Eq(PathUtils.unfold(it.ref, indexing), PathUtils.unfold(it.ref, indices.last())) - }) - bmcSolver.add(Not(allVarsSame)) - } - } - - if (bmcSolver.check().isUnsat) { - logger.write(Logger.Level.MAINSTEP, "Safety proven in BMC step\n") - return SafetyResult.safe(EmptyWitness.getInstance(), BoundedStatistics(iteration)) - } + return SafetyResult.unknown(BoundedStatistics(iteration)) + } + + private fun bmc(): SafetyResult>? { + val bmcSolver = this.bmcSolver!! + logger.write(Logger.Level.MAINSTEP, "\tStarting BMC\n") + + bmcSolver.add(exprs.last()) + + if (lfPathOnly()) { // indices contains currIndex as last() + for (indexing in indices) { + if (indexing != indices.last()) { + val allVarsSame = + And( + vars.map { + Eq(PathUtils.unfold(it.ref, indexing), PathUtils.unfold(it.ref, indices.last())) + } + ) + bmcSolver.add(Not(allVarsSame)) } + } - return WithPushPop(bmcSolver).use { - bmcSolver.add(Not(unfoldedPropExpr(indices.last()))) - - if (bmcSolver.check().isSat) { - val trace = getTrace(bmcSolver.model) - logger.write(Logger.Level.MAINSTEP, "CeX found in BMC step (length ${trace.length()})\n") - SafetyResult.unsafe(trace, EmptyWitness.getInstance(), BoundedStatistics(iteration)) - } else null - } + if (bmcSolver.check().isUnsat) { + logger.write(Logger.Level.MAINSTEP, "Safety proven in BMC step\n") + return SafetyResult.safe(EmptyProof.getInstance(), BoundedStatistics(iteration)) + } } - private fun kind(): SafetyResult>? { - val indSolver = this.indSolver!! + return WithPushPop(bmcSolver).use { + bmcSolver.add(Not(unfoldedPropExpr(indices.last()))) - logger.write(Logger.Level.MAINSTEP, "\tStarting k-induction\n") + if (bmcSolver.check().isSat) { + val trace = getTrace(bmcSolver.model) + logger.write(Logger.Level.MAINSTEP, "CeX found in BMC step (length ${trace.length()})\n") + SafetyResult.unsafe(trace, EmptyProof.getInstance(), BoundedStatistics(iteration)) + } else null + } + } - exprs.subList(kindLastIterLookup, exprs.size).forEach { indSolver.add(it) } - indices.subList(kindLastIterLookup, indices.size - 1).forEach { indSolver.add(unfoldedPropExpr(it)) } + private fun kind(): SafetyResult>? { + val indSolver = this.indSolver!! - return WithPushPop(indSolver).use { - indSolver.add(Not(unfoldedPropExpr(indices.last()))) + logger.write(Logger.Level.MAINSTEP, "\tStarting k-induction\n") - if (indSolver.check().isUnsat) { - logger.write(Logger.Level.MAINSTEP, "Safety proven in k-induction step\n") - SafetyResult.safe(EmptyWitness.getInstance(), BoundedStatistics(iteration)) - } else null - } + exprs.subList(kindLastIterLookup, exprs.size).forEach { indSolver.add(it) } + indices.subList(kindLastIterLookup, indices.size - 1).forEach { + indSolver.add(unfoldedPropExpr(it)) } - private fun itp(): SafetyResult>? { - val itpSolver = this.itpSolver!! - logger.write(Logger.Level.MAINSTEP, "\tStarting IMC\n") - - itpSolver.push() - - val a = itpSolver.createMarker() - val b = itpSolver.createMarker() - val pattern = itpSolver.createBinPattern(a, b) - - itpSolver.push() - - itpSolver.add(a, unfoldedInitExpr) - itpSolver.add(a, exprs[0]) - itpSolver.add(b, exprs.subList(1, exprs.size)) - - if (lfPathOnly()) { // indices contains currIndex as last() - itpSolver.push() - for (indexing in indices) { - if (indexing != indices.last()) { - val allVarsSame = And(vars.map { - Eq(PathUtils.unfold(it.ref, indexing), PathUtils.unfold(it.ref, indices.last())) - }) - itpSolver.add(a, Not(allVarsSame)) - } - } - - if (itpSolver.check().isUnsat) { - itpSolver.pop() - itpSolver.pop() - logger.write(Logger.Level.MAINSTEP, "Safety proven in IMC/BMC step\n") - return SafetyResult.safe(EmptyWitness.getInstance(), BoundedStatistics(iteration)) - } - itpSolver.pop() + return WithPushPop(indSolver).use { + indSolver.add(Not(unfoldedPropExpr(indices.last()))) + + if (indSolver.check().isUnsat) { + logger.write(Logger.Level.MAINSTEP, "Safety proven in k-induction step\n") + SafetyResult.safe(EmptyProof.getInstance(), BoundedStatistics(iteration)) + } else null + } + } + + private fun itp(): SafetyResult>? { + val itpSolver = this.itpSolver!! + logger.write(Logger.Level.MAINSTEP, "\tStarting IMC\n") + + itpSolver.push() + + val a = itpSolver.createMarker() + val b = itpSolver.createMarker() + val pattern = itpSolver.createBinPattern(a, b) + + itpSolver.push() + + itpSolver.add(a, unfoldedInitExpr) + itpSolver.add(a, exprs[0]) + itpSolver.add(b, exprs.subList(1, exprs.size)) + + if (lfPathOnly()) { // indices contains currIndex as last() + itpSolver.push() + for (indexing in indices) { + if (indexing != indices.last()) { + val allVarsSame = + And( + vars.map { + Eq(PathUtils.unfold(it.ref, indexing), PathUtils.unfold(it.ref, indices.last())) + } + ) + itpSolver.add(a, Not(allVarsSame)) } + } - itpSolver.add(b, Not(unfoldedPropExpr(indices.last()))) + if (itpSolver.check().isUnsat) { + itpSolver.pop() + itpSolver.pop() + logger.write(Logger.Level.MAINSTEP, "Safety proven in IMC/BMC step\n") + return SafetyResult.safe(EmptyProof.getInstance(), BoundedStatistics(iteration)) + } + itpSolver.pop() + } - val status = itpSolver.check() + itpSolver.add(b, Not(unfoldedPropExpr(indices.last()))) - if (status.isSat) { - val trace = getTrace(itpSolver.model) - logger.write(Logger.Level.MAINSTEP, "CeX found in IMC/BMC step (length ${trace.length()})\n") - itpSolver.pop() - itpSolver.pop() - return SafetyResult.unsafe(trace, EmptyWitness.getInstance(), BoundedStatistics(iteration)) - } + val status = itpSolver.check() - var img = unfoldedInitExpr - while (itpSolver.check().isUnsat) { - val interpolant = itpSolver.getInterpolant(pattern) - val itpFormula = PathUtils.unfold(PathUtils.foldin(interpolant.eval(a), indices[1]), indices[0]) - itpSolver.pop() - - itpSolver.push() - itpSolver.add(a, itpFormula) - itpSolver.add(a, Not(img)) - val itpStatus = itpSolver.check() - if (itpStatus.isUnsat) { - logger.write(Logger.Level.MAINSTEP, "Safety proven in IMC step\n") - itpSolver.pop() - itpSolver.pop() - return SafetyResult.safe(EmptyWitness.getInstance(), BoundedStatistics(iteration)) - } - itpSolver.pop() - img = Or(img, itpFormula) - - itpSolver.push() - itpSolver.add(a, itpFormula) - itpSolver.add(a, exprs[0]) - itpSolver.add(b, exprs.subList(1, exprs.size)) - itpSolver.add(b, Not(unfoldedPropExpr(indices.last()))) - } + if (status.isSat) { + val trace = getTrace(itpSolver.model) + logger.write(Logger.Level.MAINSTEP, "CeX found in IMC/BMC step (length ${trace.length()})\n") + itpSolver.pop() + itpSolver.pop() + return SafetyResult.unsafe(trace, EmptyProof.getInstance(), BoundedStatistics(iteration)) + } + var img = unfoldedInitExpr + while (itpSolver.check().isUnsat) { + val interpolant = itpSolver.getInterpolant(pattern) + val itpFormula = + PathUtils.unfold(PathUtils.foldin(interpolant.eval(a), indices[1]), indices[0]) + itpSolver.pop() + + itpSolver.push() + itpSolver.add(a, itpFormula) + itpSolver.add(a, Not(img)) + val itpStatus = itpSolver.check() + if (itpStatus.isUnsat) { + logger.write(Logger.Level.MAINSTEP, "Safety proven in IMC step\n") itpSolver.pop() itpSolver.pop() - return null + return SafetyResult.safe(EmptyProof.getInstance(), BoundedStatistics(iteration)) + } + itpSolver.pop() + img = Or(img, itpFormula) + + itpSolver.push() + itpSolver.add(a, itpFormula) + itpSolver.add(a, exprs[0]) + itpSolver.add(b, exprs.subList(1, exprs.size)) + itpSolver.add(b, Not(unfoldedPropExpr(indices.last()))) } - - private fun getTrace(model: Valuation): Trace { - val stateList = LinkedList() - val actionList = LinkedList() - var lastValuation: Valuation? = null - for (i in indices) { - val valuation = PathUtils.extractValuation(model, i, vars) - stateList.add(valToState(valuation)) - if (lastValuation != null) { - actionList.add(biValToAction(lastValuation, valuation)) - } - lastValuation = valuation - } - return Trace.of(stateList, actionList) + itpSolver.pop() + itpSolver.pop() + return null + } + + private fun getTrace(model: Valuation): Trace { + val stateList = LinkedList() + val actionList = LinkedList() + var lastValuation: Valuation? = null + for (i in indices) { + val valuation = PathUtils.extractValuation(model, i, vars) + stateList.add(valToState(valuation)) + if (lastValuation != null) { + actionList.add(biValToAction(lastValuation, valuation)) + } + lastValuation = valuation } - -} \ No newline at end of file + return Trace.of(stateList, actionList) + } +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/Abstractor.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/Abstractor.java index a7eb473efd..d4601d334e 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/Abstractor.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/Abstractor.java @@ -15,31 +15,18 @@ */ package hu.bme.mit.theta.analysis.algorithm.cegar; -import hu.bme.mit.theta.analysis.Action; import hu.bme.mit.theta.analysis.Prec; -import hu.bme.mit.theta.analysis.State; -import hu.bme.mit.theta.analysis.algorithm.arg.ARG; +import hu.bme.mit.theta.analysis.algorithm.Proof; /** - * Common interface for the abstractor component. It can create an initial ARG and check an ARG with - * a given precision. + * Common interface for the abstractor component. It can create an initial witness and check a + * witness with a given precision. */ -public interface Abstractor { +public interface Abstractor

{ - /** - * Create initial ARG. - * - * @return - */ - ARG createArg(); - - /** - * Check ARG with given precision. - * - * @param arg - * @param prec - * @return - */ - AbstractorResult check(ARG arg, P prec); + /** Create initial witness */ + Pr createProof(); + /** Check witness with given precision */ + AbstractorResult check(Pr witness, P prec); } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/ArgAbstractor.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/ArgAbstractor.java new file mode 100644 index 0000000000..67890b8a3a --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/ArgAbstractor.java @@ -0,0 +1,28 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.analysis.algorithm.cegar; + +import hu.bme.mit.theta.analysis.Action; +import hu.bme.mit.theta.analysis.Prec; +import hu.bme.mit.theta.analysis.State; +import hu.bme.mit.theta.analysis.algorithm.arg.ARG; + +/** + * Common interface for the abstractor component. It can create an initial ARG and check an ARG with + * a given precision. + */ +public interface ArgAbstractor + extends Abstractor> {} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/ArgCegarChecker.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/ArgCegarChecker.java new file mode 100644 index 0000000000..8ac9d3a5a7 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/ArgCegarChecker.java @@ -0,0 +1,49 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.analysis.algorithm.cegar; + +import hu.bme.mit.theta.analysis.Action; +import hu.bme.mit.theta.analysis.Prec; +import hu.bme.mit.theta.analysis.State; +import hu.bme.mit.theta.analysis.Trace; +import hu.bme.mit.theta.analysis.algorithm.arg.ARG; +import hu.bme.mit.theta.analysis.utils.ArgVisualizer; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.common.logging.NullLogger; + +/** + * Counterexample-Guided Abstraction Refinement (CEGAR) loop implementation, that uses an Abstractor + * to explore the abstract state space and a Refiner to check counterexamples and refine them if + * needed. It also provides certain statistics about its execution. + */ +public final class ArgCegarChecker { + + private ArgCegarChecker() {} + + public static + CegarChecker, Trace> create( + final ArgAbstractor abstractor, final ArgRefiner refiner) { + return create(abstractor, refiner, NullLogger.getInstance()); + } + + public static + CegarChecker, Trace> create( + final ArgAbstractor abstractor, + final ArgRefiner refiner, + final Logger logger) { + return CegarChecker.create(abstractor, refiner, logger, ArgVisualizer.getDefault()); + } +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/ArgRefiner.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/ArgRefiner.java new file mode 100644 index 0000000000..afb07b11c7 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/ArgRefiner.java @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.analysis.algorithm.cegar; + +import hu.bme.mit.theta.analysis.Action; +import hu.bme.mit.theta.analysis.Prec; +import hu.bme.mit.theta.analysis.State; +import hu.bme.mit.theta.analysis.Trace; +import hu.bme.mit.theta.analysis.algorithm.arg.ARG; + +/** + * Common interface for refiners. It takes an ARG and a precision, checks if the counterexample in + * the ARG is feasible and if not, it refines the precision and may also prune the ARG. + */ +public interface ArgRefiner + extends Refiner, Trace> {} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/BasicAbstractor.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/BasicArgAbstractor.java similarity index 81% rename from subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/BasicAbstractor.java rename to subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/BasicArgAbstractor.java index 42eb1487e1..a92dbedfc2 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/BasicAbstractor.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/BasicArgAbstractor.java @@ -15,6 +15,9 @@ */ package hu.bme.mit.theta.analysis.algorithm.cegar; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + import hu.bme.mit.theta.analysis.Action; import hu.bme.mit.theta.analysis.Prec; import hu.bme.mit.theta.analysis.State; @@ -30,18 +33,13 @@ import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.common.logging.NullLogger; - import java.util.Collection; import java.util.Collections; import java.util.function.Function; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -/** - * Basic implementation for the abstractor, relying on an ArgBuilder. - */ -public class BasicAbstractor implements Abstractor { +/** Basic implementation for the abstractor, relying on an ArgBuilder. */ +public class BasicArgAbstractor + implements ArgAbstractor { protected final ArgBuilder argBuilder; protected final Function projection; @@ -49,8 +47,12 @@ public class BasicAbstractor protected final StopCriterion stopCriterion; protected final Logger logger; - protected BasicAbstractor(final ArgBuilder argBuilder, final Function projection, - final Waitlist> waitlist, final StopCriterion stopCriterion, final Logger logger) { + protected BasicArgAbstractor( + final ArgBuilder argBuilder, + final Function projection, + final Waitlist> waitlist, + final StopCriterion stopCriterion, + final Logger logger) { this.argBuilder = checkNotNull(argBuilder); this.projection = checkNotNull(projection); this.waitlist = checkNotNull(waitlist); @@ -64,7 +66,7 @@ public static Builder createArg() { + public ARG createProof() { return argBuilder.createArg(); } @@ -82,11 +84,16 @@ public AbstractorResult check(final ARG arg, final P prec) { assert arg.isInitialized(); - logger.write(Level.INFO, "| | Starting ARG: %d nodes, %d incomplete, %d unsafe%n", arg.getNodes().count(), - arg.getIncompleteNodes().count(), arg.getUnsafeNodes().count()); + logger.write( + Level.INFO, + "| | Starting ARG: %d nodes, %d incomplete, %d unsafe%n", + arg.getNodes().count(), + arg.getIncompleteNodes().count(), + arg.getUnsafeNodes().count()); logger.write(Level.SUBSTEP, "| | Building ARG..."); - final Partition, ?> reachedSet = Partition.of(n -> projection.apply(n.getState())); + final Partition, ?> reachedSet = + Partition.of(n -> projection.apply(n.getState())); waitlist.clear(); reachedSet.addAll(arg.getNodes()); @@ -109,8 +116,12 @@ public AbstractorResult check(final ARG arg, final P prec) { } logger.write(Level.SUBSTEP, "done%n"); - logger.write(Level.INFO, "| | Finished ARG: %d nodes, %d incomplete, %d unsafe%n", arg.getNodes().count(), - arg.getIncompleteNodes().count(), arg.getUnsafeNodes().count()); + logger.write( + Level.INFO, + "| | Finished ARG: %d nodes, %d incomplete, %d unsafe%n", + arg.getNodes().count(), + arg.getIncompleteNodes().count(), + arg.getUnsafeNodes().count()); waitlist.clear(); // Optimization @@ -174,9 +185,9 @@ public Builder logger(final Logger logger) { return this; } - public BasicAbstractor build() { - return new BasicAbstractor<>(argBuilder, projection, waitlist, stopCriterion, logger); + public BasicArgAbstractor build() { + return new BasicArgAbstractor<>( + argBuilder, projection, waitlist, stopCriterion, logger); } } - } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/CegarChecker.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/CegarChecker.java index f264484a5b..6596eb7cbb 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/CegarChecker.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/CegarChecker.java @@ -15,16 +15,16 @@ */ package hu.bme.mit.theta.analysis.algorithm.cegar; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.base.Stopwatch; -import hu.bme.mit.theta.analysis.Action; +import hu.bme.mit.theta.analysis.Cex; import hu.bme.mit.theta.analysis.Prec; -import hu.bme.mit.theta.analysis.State; -import hu.bme.mit.theta.analysis.Trace; -import hu.bme.mit.theta.analysis.algorithm.SafetyResult; -import hu.bme.mit.theta.analysis.algorithm.arg.ARG; +import hu.bme.mit.theta.analysis.algorithm.Proof; import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; +import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.runtimemonitor.MonitorCheckpoint; -import hu.bme.mit.theta.analysis.utils.ArgVisualizer; +import hu.bme.mit.theta.analysis.utils.ProofVisualizer; import hu.bme.mit.theta.common.Utils; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; @@ -33,50 +33,59 @@ import hu.bme.mit.theta.common.visualization.writer.WebDebuggerLogger; import java.util.concurrent.TimeUnit; -import static com.google.common.base.Preconditions.checkNotNull; - /** - * Counterexample-Guided Abstraction Refinement (CEGAR) loop implementation, - * that uses an Abstractor to explore the abstract state space and a Refiner to - * check counterexamples and refine them if needed. It also provides certain - * statistics about its execution. + * Counterexample-Guided Abstraction Refinement (CEGAR) loop implementation, that uses an Abstractor + * to explore the abstract state space and a Refiner to check counterexamples and refine them if + * needed. It also provides certain statistics about its execution. */ -public final class CegarChecker implements SafetyChecker, Trace, P> { +public final class CegarChecker

+ implements SafetyChecker { - private final Abstractor abstractor; - private final Refiner refiner; + private final Abstractor abstractor; + private final Refiner refiner; private final Logger logger; - private final ARG arg; // TODO I don't think putting the ARG up here from check below causes any issues, but keep it in mind, that it might - - private CegarChecker(final Abstractor abstractor, final Refiner refiner, final Logger logger) { + private final Pr proof; + private final ProofVisualizer proofVisualizer; + + private CegarChecker( + final Abstractor abstractor, + final Refiner refiner, + final Logger logger, + final ProofVisualizer proofVisualizer) { this.abstractor = checkNotNull(abstractor); this.refiner = checkNotNull(refiner); this.logger = checkNotNull(logger); - arg = abstractor.createArg(); + proof = abstractor.createProof(); + this.proofVisualizer = checkNotNull(proofVisualizer); } - public static CegarChecker create( - final Abstractor abstractor, final Refiner refiner) { - return new CegarChecker<>(abstractor, refiner, NullLogger.getInstance()); + public static

CegarChecker create( + final Abstractor abstractor, + final Refiner refiner, + final ProofVisualizer proofVisualizer) { + return create(abstractor, refiner, NullLogger.getInstance(), proofVisualizer); } - public static CegarChecker create( - final Abstractor abstractor, final Refiner refiner, final Logger logger) { - return new CegarChecker<>(abstractor, refiner, logger); + public static

CegarChecker create( + final Abstractor abstractor, + final Refiner refiner, + final Logger logger, + final ProofVisualizer proofVisualizer) { + return new CegarChecker<>(abstractor, refiner, logger, proofVisualizer); } - public ARG getArg() { - return arg; + public Pr getProof() { + return proof; } @Override - public SafetyResult, Trace> check(final P initPrec) { + public SafetyResult check(final P initPrec) { logger.write(Level.INFO, "Configuration: %s%n", this); final Stopwatch stopwatch = Stopwatch.createStarted(); long abstractorTime = 0; long refinerTime = 0; - RefinerResult refinerResult = null; - AbstractorResult abstractorResult = null; + RefinerResult refinerResult = null; + AbstractorResult abstractorResult; P prec = initPrec; int iteration = 0; WebDebuggerLogger wdl = WebDebuggerLogger.getInstance(); @@ -85,12 +94,14 @@ public SafetyResult, Trace> check(final P initPrec) { logger.write(Level.MAINSTEP, "Iteration %d%n", iteration); logger.write(Level.MAINSTEP, "| Checking abstraction...%n"); final long abstractorStartTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - abstractorResult = abstractor.check(arg, prec); + abstractorResult = abstractor.check(proof, prec); abstractorTime += stopwatch.elapsed(TimeUnit.MILLISECONDS) - abstractorStartTime; - logger.write(Level.MAINSTEP, "| Checking abstraction done, result: %s%n", abstractorResult); + logger.write( + Level.MAINSTEP, "| Checking abstraction done, result: %s%n", abstractorResult); if (WebDebuggerLogger.enabled()) { - String argGraph = JSONWriter.getInstance().writeString(ArgVisualizer.getDefault().visualize(arg)); + String argGraph = + JSONWriter.getInstance().writeString(proofVisualizer.visualize(proof)); String precString = prec.toString(); wdl.addIteration(iteration, argGraph, precString); } @@ -101,35 +112,44 @@ public SafetyResult, Trace> check(final P initPrec) { P lastPrec = prec; logger.write(Level.MAINSTEP, "| Refining abstraction...%n"); final long refinerStartTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - refinerResult = refiner.refine(arg, prec); + refinerResult = refiner.refine(proof, prec); refinerTime += stopwatch.elapsed(TimeUnit.MILLISECONDS) - refinerStartTime; - logger.write(Level.MAINSTEP, "Refining abstraction done, result: %s%n", refinerResult); + logger.write( + Level.MAINSTEP, "Refining abstraction done, result: %s%n", refinerResult); if (refinerResult.isSpurious()) { prec = refinerResult.asSpurious().getRefinedPrec(); } if (lastPrec.equals(prec)) { - logger.write(Level.MAINSTEP, "! Precision did NOT change in this iteration" + System.lineSeparator()); + logger.write( + Level.MAINSTEP, + "! Precision did NOT change in this iteration" + + System.lineSeparator()); } else { - logger.write(Level.MAINSTEP, "! Precision DID change in this iteration" + System.lineSeparator()); + logger.write( + Level.MAINSTEP, + "! Precision DID change in this iteration" + System.lineSeparator()); } - } } while (!abstractorResult.isSafe() && !refinerResult.isUnsafe()); stopwatch.stop(); - SafetyResult, Trace> cegarResult = null; - final CegarStatistics stats = new CegarStatistics(stopwatch.elapsed(TimeUnit.MILLISECONDS), abstractorTime, - refinerTime, iteration); + SafetyResult cegarResult = null; + final CegarStatistics stats = + new CegarStatistics( + stopwatch.elapsed(TimeUnit.MILLISECONDS), + abstractorTime, + refinerTime, + iteration); - assert abstractorResult.isSafe() || (refinerResult != null && refinerResult.isUnsafe()); + assert abstractorResult.isSafe() || refinerResult.isUnsafe(); if (abstractorResult.isSafe()) { - cegarResult = SafetyResult.safe(arg, stats); + cegarResult = SafetyResult.safe(proof, stats); } else if (refinerResult.isUnsafe()) { - cegarResult = SafetyResult.unsafe(refinerResult.asUnsafe().getCex(), arg, stats); + cegarResult = SafetyResult.unsafe(refinerResult.asUnsafe().getCex(), proof, stats); } assert cegarResult != null; @@ -140,6 +160,9 @@ public SafetyResult, Trace> check(final P initPrec) { @Override public String toString() { - return Utils.lispStringBuilder(getClass().getSimpleName()).add(abstractor).add(refiner).toString(); + return Utils.lispStringBuilder(getClass().getSimpleName()) + .add(abstractor) + .add(refiner) + .toString(); } } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/Refiner.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/Refiner.java index 0bbfe07f1d..819bd3c403 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/Refiner.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/Refiner.java @@ -15,24 +15,16 @@ */ package hu.bme.mit.theta.analysis.algorithm.cegar; -import hu.bme.mit.theta.analysis.Action; +import hu.bme.mit.theta.analysis.Cex; import hu.bme.mit.theta.analysis.Prec; -import hu.bme.mit.theta.analysis.State; -import hu.bme.mit.theta.analysis.algorithm.arg.ARG; +import hu.bme.mit.theta.analysis.algorithm.Proof; /** - * Common interface for refiners. It takes an ARG and a precision, checks if the counterexample in - * the ARG is feasible and if not, it refines the precision and may also prune the ARG. + * Common interface for refiners. It takes a witness and a precision, checks if the counterexample + * in the witness is feasible and if not, it refines the precision */ -public interface Refiner { +public interface Refiner

{ - /** - * Checks if the counterexample in the ARG is feasible. If not, refines the precision and prunes - * the ARG. - * - * @param arg - * @param prec - * @return - */ - RefinerResult refine(ARG arg, P prec); + /** Checks if the counterexample in the witness is feasible. If not, refines the precision */ + RefinerResult refine(Pr witness, P prec); } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/RefinerResult.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/RefinerResult.java index bbce8b5451..63011b7d08 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/RefinerResult.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/cegar/RefinerResult.java @@ -17,29 +17,24 @@ import static com.google.common.base.Preconditions.checkNotNull; -import hu.bme.mit.theta.analysis.Action; +import hu.bme.mit.theta.analysis.Cex; import hu.bme.mit.theta.analysis.Prec; -import hu.bme.mit.theta.analysis.State; -import hu.bme.mit.theta.analysis.Trace; import hu.bme.mit.theta.common.Utils; /** * Represents the result of the Refiner class that can be either spurious or unsafe. In the former * case it also contains the refined precision and in the latter case the feasible counterexample. */ -public abstract class RefinerResult { +public abstract class RefinerResult

{ - private RefinerResult() { - } + protected RefinerResult() {} /** * Create a new spurious result. * * @param refinedPrec Refined precision - * @return */ - public static Spurious spurious( - final P refinedPrec) { + public static

Spurious spurious(final P refinedPrec) { return new Spurious<>(refinedPrec); } @@ -47,10 +42,8 @@ public static Spurious Unsafe unsafe( - final Trace cex) { + public static

Unsafe unsafe(final C cex) { return new Unsafe<>(cex); } @@ -58,15 +51,12 @@ public static Unsafe asSpurious(); + public abstract Spurious asSpurious(); - public abstract Unsafe asUnsafe(); + public abstract Unsafe asUnsafe(); - /** - * Represents the spurious result with a refined precision. - */ - public static final class Spurious - extends RefinerResult { + /** Represents the spurious result with a refined precision. */ + public static final class Spurious

extends RefinerResult { private final P refinedPrec; @@ -89,14 +79,16 @@ public boolean isUnsafe() { } @Override - public Spurious asSpurious() { + public Spurious asSpurious() { return this; } @Override - public Unsafe asUnsafe() { + public Unsafe asUnsafe() { throw new ClassCastException( - "Cannot cast " + Spurious.class.getSimpleName() + " to " + "Cannot cast " + + Spurious.class.getSimpleName() + + " to " + Unsafe.class.getSimpleName()); } @@ -108,19 +100,16 @@ public String toString() { } } - /** - * Represents the unsafe result with a feasible counterexample. - */ - public static final class Unsafe extends - RefinerResult { + /** Represents the unsafe result with a feasible counterexample. */ + public static final class Unsafe

extends RefinerResult { - private final Trace cex; + private final C cex; - private Unsafe(final Trace cex) { + private Unsafe(final C cex) { this.cex = checkNotNull(cex); } - public Trace getCex() { + public C getCex() { return cex; } @@ -135,14 +124,16 @@ public boolean isUnsafe() { } @Override - public Spurious asSpurious() { + public Spurious asSpurious() { throw new ClassCastException( - "Cannot cast " + Unsafe.class.getSimpleName() + " to " + "Cannot cast " + + Unsafe.class.getSimpleName() + + " to " + Spurious.class.getSimpleName()); } @Override - public Unsafe asUnsafe() { + public Unsafe asUnsafe() { return this; } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/chc/HornChecker.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/chc/HornChecker.kt index 0c083b7cd0..a52da53d51 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/chc/HornChecker.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/chc/HornChecker.kt @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.chc import hu.bme.mit.theta.analysis.Cex +import hu.bme.mit.theta.analysis.algorithm.Proof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.SafetyResult -import hu.bme.mit.theta.analysis.algorithm.Witness import hu.bme.mit.theta.analysis.unit.UnitPrec import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.core.Relation @@ -30,58 +29,62 @@ import hu.bme.mit.theta.solver.ProofNode import hu.bme.mit.theta.solver.SolverFactory import hu.bme.mit.theta.solver.SolverStatus -data class Invariant(val lookup: Map>) : Witness +data class Invariant(val lookup: Map>) : Proof data class CexTree(val proofNode: ProofNode) : Cex { - override fun length(): Int = proofNode.depth() + override fun length(): Int = proofNode.depth() } -/** - * A checker for CHC-based verification. - */ +/** A checker for CHC-based verification. */ class HornChecker( - private val relations: List, - private val hornSolverFactory: SolverFactory, - private val logger: Logger, + private val relations: List, + private val hornSolverFactory: SolverFactory, + private val logger: Logger, ) : SafetyChecker { - override fun check(prec: UnitPrec?): SafetyResult { - val solver = hornSolverFactory.createHornSolver() - logger.write(Logger.Level.MAINSTEP, "Starting encoding\n") - solver.add(relations) - logger.write(Logger.Level.DETAIL, "Relations:\n\t${ + override fun check(prec: UnitPrec?): SafetyResult { + val solver = hornSolverFactory.createHornSolver() + logger.write(Logger.Level.MAINSTEP, "Starting encoding\n") + solver.add(relations) + logger.write( + Logger.Level.DETAIL, + "Relations:\n\t${ relations.joinToString("\n\t") { it.constDecl.toString() } - }\n") - logger.write(Logger.Level.DETAIL, "Rules:\n\t${ + }\n", + ) + logger.write( + Logger.Level.DETAIL, + "Rules:\n\t${ solver.assertions.joinToString("\n\t") { it.toString().replace(Regex("[\r\n\t ]+"), " ") } - }\n") - logger.write(Logger.Level.MAINSTEP, "Added constraints to solver\n") - solver.check() - logger.write(Logger.Level.MAINSTEP, "Check() finished (result: ${solver.status})\n") - return when (solver.status) { - SolverStatus.SAT -> { - logger.write(Logger.Level.MAINSTEP, "Proof (model) found\n") - val model = solver.model.toMap() - SafetyResult.safe( - Invariant(relations.associateWith { model[it.constDecl] as? Expr ?: True() })) - } + }\n", + ) + logger.write(Logger.Level.MAINSTEP, "Added constraints to solver\n") + solver.check() + logger.write(Logger.Level.MAINSTEP, "Check() finished (result: ${solver.status})\n") + return when (solver.status) { + SolverStatus.SAT -> { + logger.write(Logger.Level.MAINSTEP, "Proof (model) found\n") + val model = solver.model.toMap() + SafetyResult.safe( + Invariant(relations.associateWith { model[it.constDecl] as? Expr ?: True() }) + ) + } - SolverStatus.UNSAT -> { - logger.write(Logger.Level.MAINSTEP, "Counterexample found\n") - val proof = solver.proof - SafetyResult.unsafe(CexTree(proof), Invariant(emptyMap())) - } + SolverStatus.UNSAT -> { + logger.write(Logger.Level.MAINSTEP, "Counterexample found\n") + val proof = solver.proof + SafetyResult.unsafe(CexTree(proof), Invariant(emptyMap())) + } - else -> { - logger.write(Logger.Level.MAINSTEP, "No solution found.\n") - SafetyResult.unknown() - } - } + else -> { + logger.write(Logger.Level.MAINSTEP, "No solution found.\n") + SafetyResult.unknown() + } } - -} \ No newline at end of file + } +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/analysis/FiniteStateChecker.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/analysis/FiniteStateChecker.kt index 05c02303c3..b316300d9c 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/analysis/FiniteStateChecker.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/analysis/FiniteStateChecker.kt @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.mcm.analysis import hu.bme.mit.theta.analysis.EmptyCex import hu.bme.mit.theta.analysis.InitFunc import hu.bme.mit.theta.analysis.LTS import hu.bme.mit.theta.analysis.TransFunc -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness +import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.algorithm.mcm.interpreter.MemoryEventProvider @@ -35,52 +34,52 @@ import hu.bme.mit.theta.graphsolver.patterns.constraints.GraphConstraint import hu.bme.mit.theta.graphsolver.solvers.GraphSolver import java.util.* -/** - * WiP checker for finite-state systems - */ +/** WiP checker for finite-state systems */ class FiniteStateChecker( - private val mcm: Collection, - private val initFunc: InitFunc, - private val actionFunc: LTS, - private val transFunc: TransFunc, - private val memoryEventProvider: MemoryEventProvider, - private val graphPatternCompiler: GraphPatternCompiler, - private val graphPatternSolver: GraphSolver -) : SafetyChecker { + private val mcm: Collection, + private val initFunc: InitFunc, + private val actionFunc: LTS, + private val transFunc: TransFunc, + private val memoryEventProvider: MemoryEventProvider, + private val graphPatternCompiler: GraphPatternCompiler, + private val graphPatternSolver: GraphSolver, +) : SafetyChecker { - override fun check(prec: ExplPrec): SafetyResult { - val eventIds = LinkedList() - val rels = LinkedList>() - val lastIds = LinkedHashMap() - val initId = nextId(eventIds) - val initStates = LinkedList(initFunc.getInitStates(prec)) - initStates.forEach { lastIds[it] = initId } - while (initStates.isNotEmpty()) { - val state = initStates.pop() - val lastId = checkNotNull(lastIds[state]) - val actions = actionFunc.getEnabledActionsFor(state) - - val nextStates = actions.map { a -> - val memEvent = memoryEventProvider[a, prec] - transFunc.getSuccStates(state, a, prec).onEach { s -> - memEvent?.also { - val id = nextId(eventIds) - rels.add(Pair(memEvent.type().label, Tuple1.of(id))) - rels.add(Pair("po", Tuple2.of(lastId, id))) - lastIds[s] = id - } - } - }.flatten() - initStates.addAll(nextStates) - } -// PartialSolver(mcm, CandidateExecutionGraph(eventIds, rels)) - return SafetyResult.unsafe(EmptyCex.getInstance(), EmptyWitness.getInstance()) + override fun check(prec: ExplPrec): SafetyResult { + val eventIds = LinkedList() + val rels = LinkedList>() + val lastIds = LinkedHashMap() + val initId = nextId(eventIds) + val initStates = LinkedList(initFunc.getInitStates(prec)) + initStates.forEach { lastIds[it] = initId } + while (initStates.isNotEmpty()) { + val state = initStates.pop() + val lastId = checkNotNull(lastIds[state]) + val actions = actionFunc.getEnabledActionsFor(state) + val nextStates = + actions + .map { a -> + val memEvent = memoryEventProvider[a, prec] + transFunc.getSuccStates(state, a, prec).onEach { s -> + memEvent?.also { + val id = nextId(eventIds) + rels.add(Pair(memEvent.type().label, Tuple1.of(id))) + rels.add(Pair("po", Tuple2.of(lastId, id))) + lastIds[s] = id + } + } + } + .flatten() + initStates.addAll(nextStates) } + // PartialSolver(mcm, CandidateExecutionGraph(eventIds, rels)) + return SafetyResult.unsafe(EmptyCex.getInstance(), EmptyProof.getInstance()) + } - private fun nextId(list: MutableList): Int { - val ret = list.size - list.add(list.size) - return ret - } -} \ No newline at end of file + private fun nextId(list: MutableList): Int { + val ret = list.size + list.add(list.size) + return ret + } +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddChecker.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddChecker.java index df1feb4743..cd81d5a697 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddChecker.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddChecker.java @@ -15,28 +15,28 @@ */ package hu.bme.mit.theta.analysis.algorithm.mdd; -import hu.bme.mit.delta.collections.impl.RecursiveIntObjMapViews; -import hu.bme.mit.delta.java.mdd.*; +import static hu.bme.mit.theta.core.type.booltype.SmartBoolExprs.Not; + +import hu.bme.mit.delta.java.mdd.JavaMddFactory; +import hu.bme.mit.delta.java.mdd.MddGraph; +import hu.bme.mit.delta.java.mdd.MddHandle; +import hu.bme.mit.delta.java.mdd.MddVariableOrder; import hu.bme.mit.delta.mdd.MddInterpreter; import hu.bme.mit.delta.mdd.MddVariableDescriptor; -import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; +import hu.bme.mit.theta.analysis.algorithm.SafetyResult; +import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.AbstractNextStateDescriptor; +import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.impl.MddNodeInitializer; +import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.impl.MddNodeNextStateDescriptor; +import hu.bme.mit.theta.analysis.algorithm.mdd.expressionnode.ExprLatticeDefinition; +import hu.bme.mit.theta.analysis.algorithm.mdd.expressionnode.MddExpressionTemplate; import hu.bme.mit.theta.analysis.algorithm.mdd.fixedpoint.BfsProvider; import hu.bme.mit.theta.analysis.algorithm.mdd.fixedpoint.GeneralizedSaturationProvider; -import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.AbstractNextStateDescriptor; import hu.bme.mit.theta.analysis.algorithm.mdd.fixedpoint.SimpleSaturationProvider; import hu.bme.mit.theta.analysis.algorithm.mdd.fixedpoint.StateSpaceEnumerationProvider; -import hu.bme.mit.theta.common.logging.Logger.Level; -import hu.bme.mit.theta.solver.SolverPool; -import hu.bme.mit.theta.analysis.algorithm.mdd.expressionnode.ExprLatticeDefinition; -import hu.bme.mit.theta.analysis.algorithm.mdd.expressionnode.MddExpressionTemplate; -import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.impl.MddNodeInitializer; -import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.impl.MddNodeNextStateDescriptor; import hu.bme.mit.theta.analysis.expr.ExprAction; -import hu.bme.mit.theta.analysis.utils.MddNodeVisualizer; import hu.bme.mit.theta.common.logging.Logger; -import hu.bme.mit.theta.common.visualization.Graph; -import hu.bme.mit.theta.common.visualization.writer.GraphvizWriter; +import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.core.decl.Decl; import hu.bme.mit.theta.core.decl.VarDecl; import hu.bme.mit.theta.core.type.Expr; @@ -45,15 +45,11 @@ import hu.bme.mit.theta.core.utils.PathUtils; import hu.bme.mit.theta.core.utils.indexings.VarIndexing; import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory; -import hu.bme.mit.theta.solver.SolverFactory; - -import java.io.FileNotFoundException; +import hu.bme.mit.theta.solver.SolverPool; import java.util.List; import java.util.Set; -import static hu.bme.mit.theta.core.type.booltype.SmartBoolExprs.Not; - -public class MddChecker implements SafetyChecker { +public class MddChecker implements SafetyChecker { private final Expr initRel; private final VarIndexing initIndexing; @@ -64,16 +60,19 @@ public class MddChecker implements SafetyChecker initRel, - VarIndexing initIndexing, - A transRel, - Expr safetyProperty, - SolverPool solverPool, - Logger logger, - IterationStrategy iterationStrategy) { + private MddChecker( + Expr initRel, + VarIndexing initIndexing, + A transRel, + Expr safetyProperty, + SolverPool solverPool, + Logger logger, + IterationStrategy iterationStrategy) { this.initRel = initRel; this.initIndexing = initIndexing; this.transRel = transRel; @@ -83,40 +82,63 @@ private MddChecker(Expr initRel, this.iterationStrategy = iterationStrategy; } - public static MddChecker create(Expr initRel, - VarIndexing initIndexing, - A transRel, - Expr safetyProperty, - SolverPool solverPool, - Logger logger) { - return new MddChecker(initRel, initIndexing, transRel, safetyProperty, solverPool, logger, IterationStrategy.GSAT); + public static MddChecker create( + Expr initRel, + VarIndexing initIndexing, + A transRel, + Expr safetyProperty, + SolverPool solverPool, + Logger logger) { + return new MddChecker( + initRel, + initIndexing, + transRel, + safetyProperty, + solverPool, + logger, + IterationStrategy.GSAT); } - public static MddChecker create(Expr initRel, - VarIndexing initIndexing, - A transRel, - Expr safetyProperty, - SolverPool solverPool, - Logger logger, - IterationStrategy iterationStrategy) { - return new MddChecker(initRel, initIndexing, transRel, safetyProperty, solverPool, logger, iterationStrategy); + public static MddChecker create( + Expr initRel, + VarIndexing initIndexing, + A transRel, + Expr safetyProperty, + SolverPool solverPool, + Logger logger, + IterationStrategy iterationStrategy) { + return new MddChecker( + initRel, + initIndexing, + transRel, + safetyProperty, + solverPool, + logger, + iterationStrategy); } @Override - public SafetyResult check(Void input) { + public SafetyResult check(Void input) { - final MddGraph mddGraph = JavaMddFactory.getDefault().createMddGraph(ExprLatticeDefinition.forExpr()); + final MddGraph mddGraph = + JavaMddFactory.getDefault().createMddGraph(ExprLatticeDefinition.forExpr()); - final MddVariableOrder stateOrder = JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); - final MddVariableOrder transOrder = JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); + final MddVariableOrder stateOrder = + JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); + final MddVariableOrder transOrder = + JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); - final Set> vars = ExprUtils.getVars(List.of(initRel, transRel.toExpr(), safetyProperty)); + final Set> vars = + ExprUtils.getVars(List.of(initRel, transRel.toExpr(), safetyProperty)); for (var v : vars) { final var domainSize = v.getType() instanceof BoolType ? 2 : 0; - stateOrder.createOnTop(MddVariableDescriptor.create(v.getConstDecl(initIndexing.get(v)), domainSize)); + stateOrder.createOnTop( + MddVariableDescriptor.create(v.getConstDecl(initIndexing.get(v)), domainSize)); - transOrder.createOnTop(MddVariableDescriptor.create(v.getConstDecl(transRel.nextIndexing().get(v)), domainSize)); + transOrder.createOnTop( + MddVariableDescriptor.create( + v.getConstDecl(transRel.nextIndexing().get(v)), domainSize)); transOrder.createOnTop(MddVariableDescriptor.create(v.getConstDecl(0), domainSize)); } @@ -124,13 +146,20 @@ public SafetyResult check(Void input) { final var transSig = transOrder.getDefaultSetSignature(); final Expr initExpr = PathUtils.unfold(initRel, initIndexing); - final MddHandle initNode = stateSig.getTopVariableHandle().checkInNode(MddExpressionTemplate.of(initExpr, o -> (Decl) o, solverPool)); + final MddHandle initNode = + stateSig.getTopVariableHandle() + .checkInNode(MddExpressionTemplate.of(initExpr, o -> (Decl) o, solverPool)); logger.write(Level.INFO, "Created initial node"); - final Expr transExpr = PathUtils.unfold(transRel.toExpr(), VarIndexingFactory.indexing(0)); - final MddHandle transitionNode = transSig.getTopVariableHandle().checkInNode(MddExpressionTemplate.of(transExpr, o -> (Decl) o, solverPool)); - final AbstractNextStateDescriptor nextStates = MddNodeNextStateDescriptor.of(transitionNode); + final Expr transExpr = + PathUtils.unfold(transRel.toExpr(), VarIndexingFactory.indexing(0)); + final MddHandle transitionNode = + transSig.getTopVariableHandle() + .checkInNode( + MddExpressionTemplate.of(transExpr, o -> (Decl) o, solverPool)); + final AbstractNextStateDescriptor nextStates = + MddNodeNextStateDescriptor.of(transitionNode); logger.write(Level.INFO, "Created next-state node, starting fixed point calculation"); @@ -147,12 +176,20 @@ public SafetyResult check(Void input) { } default -> throw new IllegalStateException("Unexpected value: " + iterationStrategy); } - final MddHandle stateSpace = stateSpaceProvider.compute(MddNodeInitializer.of(initNode), nextStates, stateSig.getTopVariableHandle()); + final MddHandle stateSpace = + stateSpaceProvider.compute( + MddNodeInitializer.of(initNode), + nextStates, + stateSig.getTopVariableHandle()); logger.write(Level.INFO, "Enumerated state-space"); final Expr negatedPropExpr = PathUtils.unfold(Not(safetyProperty), initIndexing); - final MddHandle propNode = stateSig.getTopVariableHandle().checkInNode(MddExpressionTemplate.of(negatedPropExpr, o -> (Decl) o, solverPool)); + final MddHandle propNode = + stateSig.getTopVariableHandle() + .checkInNode( + MddExpressionTemplate.of( + negatedPropExpr, o -> (Decl) o, solverPool)); final MddHandle propViolating = (MddHandle) stateSpace.intersection(propNode); @@ -164,13 +201,21 @@ public SafetyResult check(Void input) { final Long stateSpaceSize = MddInterpreter.calculateNonzeroCount(stateSpace); logger.write(Level.DETAIL, "State space size: " + stateSpaceSize); - final MddAnalysisStatistics statistics = new MddAnalysisStatistics(violatingSize, stateSpaceSize, stateSpaceProvider.getHitCount(), stateSpaceProvider.getQueryCount(), stateSpaceProvider.getCacheSize()); + final MddAnalysisStatistics statistics = + new MddAnalysisStatistics( + violatingSize, + stateSpaceSize, + stateSpaceProvider.getHitCount(), + stateSpaceProvider.getQueryCount(), + stateSpaceProvider.getCacheSize()); - final SafetyResult result; + final SafetyResult result; if (violatingSize != 0) { - result = SafetyResult.unsafe(MddCex.of(propViolating), MddWitness.of(stateSpace), statistics); + result = + SafetyResult.unsafe( + MddCex.of(propViolating), MddProof.of(stateSpace), statistics); } else { - result = SafetyResult.safe(MddWitness.of(stateSpace), statistics); + result = SafetyResult.safe(MddProof.of(stateSpace), statistics); } logger.write(Level.RESULT, "%s%n", result); return result; diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddWitness.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddProof.java similarity index 81% rename from subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddWitness.java rename to subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddProof.java index 300dc6579f..805a138399 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddWitness.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddProof.java @@ -17,18 +17,18 @@ import hu.bme.mit.delta.java.mdd.MddHandle; import hu.bme.mit.delta.mdd.MddInterpreter; -import hu.bme.mit.theta.analysis.algorithm.Witness; +import hu.bme.mit.theta.analysis.algorithm.Proof; -public class MddWitness implements Witness { +public class MddProof implements Proof { private final MddHandle stateSpace; - private MddWitness(MddHandle stateSpace) { + private MddProof(MddHandle stateSpace) { this.stateSpace = stateSpace; } - public static MddWitness of(MddHandle stateSpace) { - return new MddWitness(stateSpace); + public static MddProof of(MddHandle stateSpace) { + return new MddProof(stateSpace); } public Long size() { diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/AasporRefiner.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/AasporRefiner.java index 8f2f499547..69b184e8b6 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/AasporRefiner.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/AasporRefiner.java @@ -16,59 +16,73 @@ package hu.bme.mit.theta.analysis.expr.refinement; import hu.bme.mit.theta.analysis.Prec; +import hu.bme.mit.theta.analysis.Trace; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode; -import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgRefiner; import hu.bme.mit.theta.analysis.algorithm.cegar.RefinerResult; import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.analysis.expr.ExprState; import hu.bme.mit.theta.core.decl.VarDecl; - import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -public final class AasporRefiner implements Refiner { +public final class AasporRefiner + implements ArgRefiner { - private final Refiner refiner; + private final ArgRefiner refiner; private final PruneStrategy pruneStrategy; private final Map, Set> ignoredVarRegistry; - private AasporRefiner(final Refiner refiner, - final PruneStrategy pruneStrategy, - final Map, Set> ignoredVarRegistry) { + private AasporRefiner( + final ArgRefiner refiner, + final PruneStrategy pruneStrategy, + final Map, Set> ignoredVarRegistry) { this.refiner = refiner; this.pruneStrategy = pruneStrategy; this.ignoredVarRegistry = ignoredVarRegistry; } - public static AasporRefiner create( - final Refiner refiner, final PruneStrategy pruneStrategy, - final Map, Set> ignoredVarRegistry) { + public static + AasporRefiner create( + final ArgRefiner refiner, + final PruneStrategy pruneStrategy, + final Map, Set> ignoredVarRegistry) { return new AasporRefiner<>(refiner, pruneStrategy, ignoredVarRegistry); } @Override - public RefinerResult refine(final ARG arg, final P prec) { - final RefinerResult result = refiner.refine(arg, prec); + public RefinerResult> refine(final ARG arg, final P prec) { + final RefinerResult> result = refiner.refine(arg, prec); if (result.isUnsafe() || pruneStrategy != PruneStrategy.LAZY) return result; final P newPrec = result.asSpurious().getRefinedPrec(); final Set> newlyAddedVars = new HashSet<>(newPrec.getUsedVars()); newlyAddedVars.removeAll(prec.getUsedVars()); - newlyAddedVars.forEach(newVar -> { - if (ignoredVarRegistry.containsKey(newVar)) { - Set> nodesToReExpand = ignoredVarRegistry.get(newVar).stream().flatMap(stateToPrune -> - arg.getNodes().filter(node -> node.getState().equals(stateToPrune)) // TODO one state can be in one ARG node? - ).collect(Collectors.toSet()); - nodesToReExpand.forEach(arg::markForReExpansion); - ignoredVarRegistry.remove(newVar); - } - }); + newlyAddedVars.forEach( + newVar -> { + if (ignoredVarRegistry.containsKey(newVar)) { + Set> nodesToReExpand = + ignoredVarRegistry.get(newVar).stream() + .flatMap( + stateToPrune -> + arg.getNodes() + .filter( + node -> + node.getState() + .equals( + stateToPrune)) // TODO one state can be in one ARG node? + ) + .collect(Collectors.toSet()); + nodesToReExpand.forEach(arg::markForReExpansion); + ignoredVarRegistry.remove(newVar); + } + }); return result; } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/MultiExprTraceRefiner.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/MultiExprTraceRefiner.java index 72c516adef..71b8799468 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/MultiExprTraceRefiner.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/MultiExprTraceRefiner.java @@ -15,26 +15,25 @@ */ package hu.bme.mit.theta.analysis.expr.refinement; +import static com.google.common.base.Preconditions.checkNotNull; + import hu.bme.mit.theta.analysis.Prec; import hu.bme.mit.theta.analysis.Trace; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode; import hu.bme.mit.theta.analysis.algorithm.arg.ArgTrace; -import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgRefiner; import hu.bme.mit.theta.analysis.algorithm.cegar.RefinerResult; import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.analysis.expr.ExprState; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; - import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkNotNull; - -public final class MultiExprTraceRefiner - implements Refiner { +public final class MultiExprTraceRefiner< + S extends ExprState, A extends ExprAction, P extends Prec, R extends Refutation> + implements ArgRefiner { private final ExprTraceChecker exprTraceChecker; private final PrecRefiner precRefiner; @@ -42,9 +41,11 @@ public final class MultiExprTraceRefiner nodePruner; private final Logger logger; - private MultiExprTraceRefiner(final ExprTraceChecker exprTraceChecker, - final PrecRefiner precRefiner, - final PruneStrategy pruneStrategy, final Logger logger) { + private MultiExprTraceRefiner( + final ExprTraceChecker exprTraceChecker, + final PrecRefiner precRefiner, + final PruneStrategy pruneStrategy, + final Logger logger) { this.exprTraceChecker = checkNotNull(exprTraceChecker); this.precRefiner = checkNotNull(precRefiner); this.pruneStrategy = checkNotNull(pruneStrategy); @@ -52,10 +53,12 @@ private MultiExprTraceRefiner(final ExprTraceChecker exprTraceChecker, this.logger = checkNotNull(logger); } - private MultiExprTraceRefiner(final ExprTraceChecker exprTraceChecker, - final PrecRefiner precRefiner, - final PruneStrategy pruneStrategy, final Logger logger, - final NodePruner nodePruner) { + private MultiExprTraceRefiner( + final ExprTraceChecker exprTraceChecker, + final PrecRefiner precRefiner, + final PruneStrategy pruneStrategy, + final Logger logger, + final NodePruner nodePruner) { this.exprTraceChecker = checkNotNull(exprTraceChecker); this.precRefiner = checkNotNull(precRefiner); this.pruneStrategy = checkNotNull(pruneStrategy); @@ -63,30 +66,38 @@ private MultiExprTraceRefiner(final ExprTraceChecker exprTraceChecker, this.logger = checkNotNull(logger); } - public static MultiExprTraceRefiner create( - final ExprTraceChecker exprTraceChecker, final PrecRefiner precRefiner, - final PruneStrategy pruneStrategy, final Logger logger) { + public static + MultiExprTraceRefiner create( + final ExprTraceChecker exprTraceChecker, + final PrecRefiner precRefiner, + final PruneStrategy pruneStrategy, + final Logger logger) { return new MultiExprTraceRefiner<>(exprTraceChecker, precRefiner, pruneStrategy, logger); } - public static MultiExprTraceRefiner create( - final ExprTraceChecker exprTraceChecker, final PrecRefiner precRefiner, - final PruneStrategy pruneStrategy, final Logger logger, final NodePruner nodePruner) { - return new MultiExprTraceRefiner<>(exprTraceChecker, precRefiner, pruneStrategy, logger, nodePruner); + public static + MultiExprTraceRefiner create( + final ExprTraceChecker exprTraceChecker, + final PrecRefiner precRefiner, + final PruneStrategy pruneStrategy, + final Logger logger, + final NodePruner nodePruner) { + return new MultiExprTraceRefiner<>( + exprTraceChecker, precRefiner, pruneStrategy, logger, nodePruner); } @Override - public RefinerResult refine(final ARG arg, final P prec) { + public RefinerResult> refine(final ARG arg, final P prec) { checkNotNull(arg); checkNotNull(prec); assert !arg.isSafe() : "ARG must be unsafe"; - final List> cexs = arg.getCexs().collect(Collectors.toList()); - final List> traces = arg.getCexs().map(ArgTrace::toTrace).collect(Collectors.toList()); + final List> cexs = arg.getCexs().toList(); + final List> traces = arg.getCexs().map(ArgTrace::toTrace).toList(); assert traces.size() == cexs.size(); logger.write(Level.INFO, "| | Number of traces: %d%n", traces.size()); - assert traces.size() > 0 : "No counterexample in ARG"; + assert !traces.isEmpty() : "No counterexample in ARG"; logger.write(Level.SUBSTEP, "| | Checking traces..."); final List> cexStatuses = new ArrayList<>(traces.size()); @@ -100,13 +111,18 @@ public RefinerResult refine(final ARG arg, final P prec) { if (cexStatuses.stream().anyMatch(ExprTraceStatus::isFeasible)) { logger.write(Level.SUBSTEP, "done, result: found feasible%n"); - return RefinerResult.unsafe(traces.get( - cexStatuses.indexOf(cexStatuses.stream().filter(ExprTraceStatus::isFeasible).findFirst().get()))); + return RefinerResult.unsafe( + traces.get( + cexStatuses.indexOf( + cexStatuses.stream() + .filter(ExprTraceStatus::isFeasible) + .findFirst() + .get()))); } else { assert cexStatuses.size() == cexs.size(); logger.write(Level.SUBSTEP, "done, result: all infeasible%n"); - final List refutations = cexStatuses.stream().map(s -> s.asInfeasible().getRefutation()) - .collect(Collectors.toList()); + final List refutations = + cexStatuses.stream().map(s -> s.asInfeasible().getRefutation()).toList(); assert refutations.size() == cexs.size(); final List> nodesToPrune = new ArrayList<>(traces.size()); @@ -120,7 +136,7 @@ public RefinerResult refine(final ARG arg, final P prec) { for (int i = 0; i < nodesToPrune.size(); ++i) { final ArgNode node = nodesToPrune.get(i); - if (node.properAncestors().anyMatch(a -> nodesToPrune.contains(a))) { + if (node.properAncestors().anyMatch(nodesToPrune::contains)) { skip.set(i, true); } } @@ -130,7 +146,8 @@ public RefinerResult refine(final ARG arg, final P prec) { P refinedPrec = prec; for (int i = 0; i < refutations.size(); ++i) { if (!skip.get(i)) { - refinedPrec = precRefiner.refine(refinedPrec, traces.get(i), refutations.get(i)); + refinedPrec = + precRefiner.refine(refinedPrec, traces.get(i), refutations.get(i)); } } @@ -153,7 +170,5 @@ public RefinerResult refine(final ARG arg, final P prec) { logger.write(Level.SUBSTEP, "done%n"); return RefinerResult.spurious(refinedPrec); } - } - -} \ No newline at end of file +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/SingleExprTraceRefiner.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/SingleExprTraceRefiner.java index ac06cf2a87..04befdbfcb 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/SingleExprTraceRefiner.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/SingleExprTraceRefiner.java @@ -15,38 +15,40 @@ */ package hu.bme.mit.theta.analysis.expr.refinement; +import static com.google.common.base.Preconditions.checkNotNull; + import hu.bme.mit.theta.analysis.Prec; import hu.bme.mit.theta.analysis.Trace; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode; import hu.bme.mit.theta.analysis.algorithm.arg.ArgTrace; -import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgRefiner; import hu.bme.mit.theta.analysis.algorithm.cegar.RefinerResult; import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.analysis.expr.ExprState; import hu.bme.mit.theta.common.Utils; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; - import java.util.Optional; -import static com.google.common.base.Preconditions.checkNotNull; - /** - * A Refiner implementation that can refine a single trace (of ExprStates and - * ExprActions) using an ExprTraceChecker and a PrecRefiner. + * A Refiner implementation that can refine a single trace (of ExprStates and ExprActions) using an + * ExprTraceChecker and a PrecRefiner. */ -public class SingleExprTraceRefiner - implements Refiner { +public class SingleExprTraceRefiner< + S extends ExprState, A extends ExprAction, P extends Prec, R extends Refutation> + implements ArgRefiner { protected final ExprTraceChecker exprTraceChecker; protected final PrecRefiner precRefiner; protected final PruneStrategy pruneStrategy; protected final NodePruner nodePruner; protected final Logger logger; - protected SingleExprTraceRefiner(final ExprTraceChecker exprTraceChecker, - final PrecRefiner precRefiner, - final PruneStrategy pruneStrategy, final Logger logger) { + protected SingleExprTraceRefiner( + final ExprTraceChecker exprTraceChecker, + final PrecRefiner precRefiner, + final PruneStrategy pruneStrategy, + final Logger logger) { this.exprTraceChecker = checkNotNull(exprTraceChecker); this.precRefiner = checkNotNull(precRefiner); this.pruneStrategy = checkNotNull(pruneStrategy); @@ -54,10 +56,12 @@ protected SingleExprTraceRefiner(final ExprTraceChecker exprTraceChecker, this.logger = checkNotNull(logger); } - protected SingleExprTraceRefiner(final ExprTraceChecker exprTraceChecker, - final PrecRefiner precRefiner, - final PruneStrategy pruneStrategy, final Logger logger, - final NodePruner nodePruner) { + protected SingleExprTraceRefiner( + final ExprTraceChecker exprTraceChecker, + final PrecRefiner precRefiner, + final PruneStrategy pruneStrategy, + final Logger logger, + final NodePruner nodePruner) { this.exprTraceChecker = checkNotNull(exprTraceChecker); this.precRefiner = checkNotNull(precRefiner); this.pruneStrategy = checkNotNull(pruneStrategy); @@ -65,20 +69,28 @@ protected SingleExprTraceRefiner(final ExprTraceChecker exprTraceChecker, this.logger = checkNotNull(logger); } - public static SingleExprTraceRefiner create( - final ExprTraceChecker exprTraceChecker, final PrecRefiner precRefiner, - final PruneStrategy pruneStrategy, final Logger logger) { + public static + SingleExprTraceRefiner create( + final ExprTraceChecker exprTraceChecker, + final PrecRefiner precRefiner, + final PruneStrategy pruneStrategy, + final Logger logger) { return new SingleExprTraceRefiner<>(exprTraceChecker, precRefiner, pruneStrategy, logger); } - public static SingleExprTraceRefiner create( - final ExprTraceChecker exprTraceChecker, final PrecRefiner precRefiner, - final PruneStrategy pruneStrategy, final Logger logger, final NodePruner nodePruner) { - return new SingleExprTraceRefiner<>(exprTraceChecker, precRefiner, pruneStrategy, logger, nodePruner); + public static + SingleExprTraceRefiner create( + final ExprTraceChecker exprTraceChecker, + final PrecRefiner precRefiner, + final PruneStrategy pruneStrategy, + final Logger logger, + final NodePruner nodePruner) { + return new SingleExprTraceRefiner<>( + exprTraceChecker, precRefiner, pruneStrategy, logger, nodePruner); } @Override - public RefinerResult refine(final ARG arg, final P prec) { + public RefinerResult> refine(final ARG arg, final P prec) { checkNotNull(arg); checkNotNull(prec); assert !arg.isSafe() : "ARG must be unsafe"; @@ -127,7 +139,9 @@ public RefinerResult refine(final ARG arg, final P prec) { @Override public String toString() { - return Utils.lispStringBuilder(getClass().getSimpleName()).add(exprTraceChecker).add(precRefiner).toString(); + return Utils.lispStringBuilder(getClass().getSimpleName()) + .add(exprTraceChecker) + .add(precRefiner) + .toString(); } - } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/utils/ArgVisualizer.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/utils/ArgVisualizer.java index 9992e5a3e7..89efd988a5 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/utils/ArgVisualizer.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/utils/ArgVisualizer.java @@ -18,25 +18,23 @@ import static hu.bme.mit.theta.common.visualization.Alignment.LEFT; import static hu.bme.mit.theta.common.visualization.Shape.RECTANGLE; -import java.awt.Color; - -import hu.bme.mit.theta.common.container.Containers; - -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - import hu.bme.mit.theta.analysis.Action; import hu.bme.mit.theta.analysis.State; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgEdge; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode; +import hu.bme.mit.theta.common.container.Containers; import hu.bme.mit.theta.common.visualization.EdgeAttributes; import hu.bme.mit.theta.common.visualization.Graph; import hu.bme.mit.theta.common.visualization.LineStyle; import hu.bme.mit.theta.common.visualization.NodeAttributes; +import java.awt.*; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; -public final class ArgVisualizer { +public final class ArgVisualizer + implements ProofVisualizer> { private static final LineStyle COVER_EDGE_STYLE = LineStyle.DASHED; private static final LineStyle SUCC_EDGE_STYLE = LineStyle.NORMAL; @@ -53,8 +51,8 @@ public final class ArgVisualizer { private static class LazyHolderDefault { - static final ArgVisualizer INSTANCE = new ArgVisualizer<>(s -> s.toString(), - a -> a.toString()); + static final ArgVisualizer INSTANCE = + new ArgVisualizer<>(Object::toString, Object::toString); } private static class LazyHolderStructureOnly { @@ -62,8 +60,8 @@ private static class LazyHolderStructureOnly { static final ArgVisualizer INSTANCE = new ArgVisualizer<>(s -> "", a -> ""); } - public ArgVisualizer(final Function stateToString, - final Function actionToString) { + public ArgVisualizer( + final Function stateToString, final Function actionToString) { this.stateToString = stateToString; this.actionToString = actionToString; } @@ -81,29 +79,43 @@ public static ArgVisualizer getStructureOnly() { return LazyHolderStructureOnly.INSTANCE; } + @Override public Graph visualize(final ARG arg) { final Graph graph = new Graph(ARG_ID, ARG_LABEL); final Set> traversed = Containers.createSet(); - for (final ArgNode initNode : arg.getInitNodes() - .collect(Collectors.toSet())) { + for (final ArgNode initNode : + arg.getInitNodes().collect(Collectors.toSet())) { traverse(graph, initNode, traversed); - final NodeAttributes nAttributes = NodeAttributes.builder().label("") - .fillColor(FILL_COLOR) - .lineColor(FILL_COLOR).lineStyle(SUCC_EDGE_STYLE).peripheries(1).build(); + final NodeAttributes nAttributes = + NodeAttributes.builder() + .label("") + .fillColor(FILL_COLOR) + .lineColor(FILL_COLOR) + .lineStyle(SUCC_EDGE_STYLE) + .peripheries(1) + .build(); graph.addNode(PHANTOM_INIT_ID + initNode.getId(), nAttributes); - final EdgeAttributes eAttributes = EdgeAttributes.builder().label("").color(LINE_COLOR) - .lineStyle(SUCC_EDGE_STYLE).build(); - graph.addEdge(PHANTOM_INIT_ID + initNode.getId(), NODE_ID_PREFIX + initNode.getId(), + final EdgeAttributes eAttributes = + EdgeAttributes.builder() + .label("") + .color(LINE_COLOR) + .lineStyle(SUCC_EDGE_STYLE) + .build(); + graph.addEdge( + PHANTOM_INIT_ID + initNode.getId(), + NODE_ID_PREFIX + initNode.getId(), eAttributes); } return graph; } - private void traverse(final Graph graph, final ArgNode node, - final Set> traversed) { + private void traverse( + final Graph graph, + final ArgNode node, + final Set> traversed) { if (traversed.contains(node)) { return; } else { @@ -113,21 +125,33 @@ private void traverse(final Graph graph, final ArgNode final LineStyle lineStyle = SUCC_EDGE_STYLE; final int peripheries = node.isTarget() ? 2 : 1; - final NodeAttributes nAttributes = NodeAttributes.builder() - .label(stateToString.apply(node.getState())) - .alignment(LEFT).shape(RECTANGLE).font(FONT).fillColor(FILL_COLOR).lineColor(LINE_COLOR) - .lineStyle(lineStyle).peripheries(peripheries).build(); + final NodeAttributes nAttributes = + NodeAttributes.builder() + .label(stateToString.apply(node.getState())) + .alignment(LEFT) + .shape(RECTANGLE) + .font(FONT) + .fillColor(FILL_COLOR) + .lineColor(LINE_COLOR) + .lineStyle(lineStyle) + .peripheries(peripheries) + .build(); graph.addNode(nodeId, nAttributes); - for (final ArgEdge edge : node.getOutEdges() - .collect(Collectors.toSet())) { + for (final ArgEdge edge : + node.getOutEdges().collect(Collectors.toSet())) { traverse(graph, edge.getTarget(), traversed); final String sourceId = NODE_ID_PREFIX + edge.getSource().getId(); final String targetId = NODE_ID_PREFIX + edge.getTarget().getId(); - final EdgeAttributes eAttributes = EdgeAttributes.builder() - .label(actionToString.apply(edge.getAction())) - .alignment(LEFT).font(FONT).color(LINE_COLOR).lineStyle(SUCC_EDGE_STYLE).build(); + final EdgeAttributes eAttributes = + EdgeAttributes.builder() + .label(actionToString.apply(edge.getAction())) + .alignment(LEFT) + .font(FONT) + .color(LINE_COLOR) + .lineStyle(SUCC_EDGE_STYLE) + .build(); graph.addEdge(sourceId, targetId, eAttributes); } @@ -135,10 +159,14 @@ private void traverse(final Graph graph, final ArgNode traverse(graph, node.getCoveringNode().get(), traversed); final String sourceId = NODE_ID_PREFIX + node.getId(); final String targetId = NODE_ID_PREFIX + node.getCoveringNode().get().getId(); - final EdgeAttributes eAttributes = EdgeAttributes.builder().label("").color(LINE_COLOR) - .lineStyle(COVER_EDGE_STYLE).weight(0).build(); + final EdgeAttributes eAttributes = + EdgeAttributes.builder() + .label("") + .color(LINE_COLOR) + .lineStyle(COVER_EDGE_STYLE) + .weight(0) + .build(); graph.addEdge(sourceId, targetId, eAttributes); } } - } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/utils/ProofVisualizer.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/utils/ProofVisualizer.java new file mode 100644 index 0000000000..954604871d --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/utils/ProofVisualizer.java @@ -0,0 +1,24 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.analysis.utils; + +import hu.bme.mit.theta.analysis.algorithm.Proof; +import hu.bme.mit.theta.common.visualization.Graph; + +public interface ProofVisualizer { + + Graph visualize(Pr proof); +} diff --git a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/config/StmtMultiConfigBuilder.kt b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/config/StmtMultiConfigBuilder.kt index 2e7f57a91a..e0d8173455 100644 --- a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/config/StmtMultiConfigBuilder.kt +++ b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/config/StmtMultiConfigBuilder.kt @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.multi.config import hu.bme.mit.theta.analysis.Prec import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder -import hu.bme.mit.theta.analysis.algorithm.cegar.BasicAbstractor -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgCegarChecker +import hu.bme.mit.theta.analysis.algorithm.cegar.BasicArgAbstractor import hu.bme.mit.theta.analysis.expr.ExprState import hu.bme.mit.theta.analysis.expr.StmtAction import hu.bme.mit.theta.analysis.expr.refinement.* @@ -39,117 +38,264 @@ import hu.bme.mit.theta.solver.SolverFactory import hu.bme.mit.theta.solver.UCSolver import java.util.function.Predicate -sealed class StmtMultiConfigBuilder( - private val product: MultiBuilderResultPOJO, ExprMultiState, StmtMultiAction, StmtMultiLts>, - val prop: Expr, - val target: Predicate>, - private val lRefToPrec: RefutationToPrec, - private val rRefToPrec: RefutationToPrec, - private val dRefToPrec: RefutationToPrec, - private val lInitPrec: LPrec, - private val rInitPrec: RPrec, - private val dInitPrec: DataPrec, - val solverFactory: SolverFactory, - val logger: Logger, - val pruneStrategy: PruneStrategy = PruneStrategy.FULL +sealed class StmtMultiConfigBuilder< + LState : ExprState, + RState : ExprState, + DataState : ExprState, + LControl : ExprState, + RControl : ExprState, + LAction : StmtAction, + RAction : StmtAction, + R : Refutation, + LPrec : Prec, + RPrec : Prec, + DataPrec : Prec, + LControlPrec : Prec, + RControlPrec : Prec, +>( + private val product: + MultiBuilderResultPOJO< + LState, + RState, + DataState, + LControl, + RControl, + LAction, + RAction, + LPrec, + RPrec, + DataPrec, + LControlPrec, + RControlPrec, + ExprMultiState, + ExprMultiState, + StmtMultiAction, + StmtMultiLts, + >, + val prop: Expr, + val target: Predicate>, + private val lRefToPrec: RefutationToPrec, + private val rRefToPrec: RefutationToPrec, + private val dRefToPrec: RefutationToPrec, + private val lInitPrec: LPrec, + private val rInitPrec: RPrec, + private val dInitPrec: DataPrec, + val solverFactory: SolverFactory, + val logger: Logger, + val pruneStrategy: PruneStrategy = PruneStrategy.FULL, ) { - abstract fun getTraceChecker(): ExprTraceChecker + abstract fun getTraceChecker(): ExprTraceChecker - fun build(): MultiConfig, StmtMultiAction> { - val argBuilder = ArgBuilder.create(product.lts, product.side.analysis, target) - val abstractor = BasicAbstractor.builder(argBuilder).build() - val traceChecker = getTraceChecker() - val precRefiner = - JoiningPrecRefiner.create, StmtMultiAction, MultiPrec, R>( - RefToMultiPrec(lRefToPrec, rRefToPrec, dRefToPrec) - ) - val refiner = SingleExprTraceRefiner.create(traceChecker, precRefiner, pruneStrategy, logger) - return MultiConfig(CegarChecker.create(abstractor, refiner), MultiPrec(lInitPrec, rInitPrec, dInitPrec)) - } + fun build(): + MultiConfig< + DataState, + LControl, + RControl, + LAction, + RAction, + LPrec, + RPrec, + DataPrec, + ExprMultiState, + StmtMultiAction, + > { + val argBuilder = ArgBuilder.create(product.lts, product.side.analysis, target) + val abstractor = BasicArgAbstractor.builder(argBuilder).build() + val traceChecker = getTraceChecker() + val precRefiner = + JoiningPrecRefiner.create< + ExprMultiState, + StmtMultiAction, + MultiPrec, + R, + >( + RefToMultiPrec(lRefToPrec, rRefToPrec, dRefToPrec) + ) + val refiner = SingleExprTraceRefiner.create(traceChecker, precRefiner, pruneStrategy, logger) + return MultiConfig( + ArgCegarChecker.create(abstractor, refiner), + MultiPrec(lInitPrec, rInitPrec, dInitPrec), + ) + } - class ItpStmtMultiConfigBuilder( - product: MultiBuilderResultPOJO, ExprMultiState, StmtMultiAction, StmtMultiLts>, - prop: Expr, - target: Predicate>, - lRefToPrec: RefutationToPrec, - rRefToPrec: RefutationToPrec, - dRefToPrec: RefutationToPrec, - lInitPrec: LPrec, - rInitPrec: RPrec, - dInitPrec: DataPrec, - solverFactory: SolverFactory, - logger: Logger, - pruneStrategy: PruneStrategy = PruneStrategy.FULL, - private val traceCheckerType: TraceCheckerType = TraceCheckerType.SEQ_ITP - ) : StmtMultiConfigBuilder( - product, - prop, - target, - lRefToPrec, - rRefToPrec, - dRefToPrec, - lInitPrec, - rInitPrec, - dInitPrec, - solverFactory, - logger, - pruneStrategy + class ItpStmtMultiConfigBuilder< + LState : ExprState, + RState : ExprState, + DataState : ExprState, + LControl : ExprState, + RControl : ExprState, + LAction : StmtAction, + RAction : StmtAction, + LPrec : Prec, + RPrec : Prec, + DataPrec : Prec, + LControlPrec : Prec, + RControlPrec : Prec, + >( + product: + MultiBuilderResultPOJO< + LState, + RState, + DataState, + LControl, + RControl, + LAction, + RAction, + LPrec, + RPrec, + DataPrec, + LControlPrec, + RControlPrec, + ExprMultiState, + ExprMultiState, + StmtMultiAction, + StmtMultiLts, + >, + prop: Expr, + target: Predicate>, + lRefToPrec: RefutationToPrec, + rRefToPrec: RefutationToPrec, + dRefToPrec: RefutationToPrec, + lInitPrec: LPrec, + rInitPrec: RPrec, + dInitPrec: DataPrec, + solverFactory: SolverFactory, + logger: Logger, + pruneStrategy: PruneStrategy = PruneStrategy.FULL, + private val traceCheckerType: TraceCheckerType = TraceCheckerType.SEQ_ITP, + ) : + StmtMultiConfigBuilder< + LState, + RState, + DataState, + LControl, + RControl, + LAction, + RAction, + ItpRefutation, + LPrec, + RPrec, + DataPrec, + LControlPrec, + RControlPrec, + >( + product, + prop, + target, + lRefToPrec, + rRefToPrec, + dRefToPrec, + lInitPrec, + rInitPrec, + dInitPrec, + solverFactory, + logger, + pruneStrategy, ) { - enum class TraceCheckerType( - val generator: (init: Expr, target: Expr, solver: ItpSolver) -> ExprTraceChecker - ) { - - SEQ_ITP(ExprTraceSeqItpChecker::create), FW_BIN(ExprTraceFwBinItpChecker::create), BW_BIN( - ExprTraceBwBinItpChecker::create - ) - } + enum class TraceCheckerType( + val generator: + (init: Expr, target: Expr, solver: ItpSolver) -> ExprTraceChecker< + ItpRefutation + > + ) { - override fun getTraceChecker(): ExprTraceChecker = traceCheckerType.generator( - BoolExprs.True(), prop, solverFactory.createItpSolver() - ) + SEQ_ITP(ExprTraceSeqItpChecker::create), + FW_BIN(ExprTraceFwBinItpChecker::create), + BW_BIN(ExprTraceBwBinItpChecker::create), } - class VarsStmtMultiConfigBuilder( - product: MultiBuilderResultPOJO, ExprMultiState, StmtMultiAction, StmtMultiLts>, - prop: Expr, - target: Predicate>, - lRefToPrec: RefutationToPrec, - rRefToPrec: RefutationToPrec, - dRefToPrec: RefutationToPrec, - lInitPrec: LPrec, - rInitPrec: RPrec, - dInitPrec: DataPrec, - solverFactory: SolverFactory, - logger: Logger, - pruneStrategy: PruneStrategy = PruneStrategy.FULL, - private val traceCheckerType: TraceCheckerType = TraceCheckerType.UNSAT_CORE - ) : StmtMultiConfigBuilder( - product, - prop, - target, - lRefToPrec, - rRefToPrec, - dRefToPrec, - lInitPrec, - rInitPrec, - dInitPrec, - solverFactory, - logger, - pruneStrategy - ) { + override fun getTraceChecker(): ExprTraceChecker = + traceCheckerType.generator(BoolExprs.True(), prop, solverFactory.createItpSolver()) + } - enum class TraceCheckerType( - val generator: (init: Expr, target: Expr, solver: UCSolver) -> ExprTraceChecker - ) { + class VarsStmtMultiConfigBuilder< + LState : ExprState, + RState : ExprState, + DataState : ExprState, + LControl : ExprState, + RControl : ExprState, + LAction : StmtAction, + RAction : StmtAction, + LPrec : Prec, + RPrec : Prec, + DataPrec : Prec, + LControlPrec : Prec, + RControlPrec : Prec, + >( + product: + MultiBuilderResultPOJO< + LState, + RState, + DataState, + LControl, + RControl, + LAction, + RAction, + LPrec, + RPrec, + DataPrec, + LControlPrec, + RControlPrec, + ExprMultiState, + ExprMultiState, + StmtMultiAction, + StmtMultiLts, + >, + prop: Expr, + target: Predicate>, + lRefToPrec: RefutationToPrec, + rRefToPrec: RefutationToPrec, + dRefToPrec: RefutationToPrec, + lInitPrec: LPrec, + rInitPrec: RPrec, + dInitPrec: DataPrec, + solverFactory: SolverFactory, + logger: Logger, + pruneStrategy: PruneStrategy = PruneStrategy.FULL, + private val traceCheckerType: TraceCheckerType = TraceCheckerType.UNSAT_CORE, + ) : + StmtMultiConfigBuilder< + LState, + RState, + DataState, + LControl, + RControl, + LAction, + RAction, + VarsRefutation, + LPrec, + RPrec, + DataPrec, + LControlPrec, + RControlPrec, + >( + product, + prop, + target, + lRefToPrec, + rRefToPrec, + dRefToPrec, + lInitPrec, + rInitPrec, + dInitPrec, + solverFactory, + logger, + pruneStrategy, + ) { - UNSAT_CORE(ExprTraceUnsatCoreChecker::create) - } + enum class TraceCheckerType( + val generator: + (init: Expr, target: Expr, solver: UCSolver) -> ExprTraceChecker< + VarsRefutation + > + ) { - override fun getTraceChecker(): ExprTraceChecker = traceCheckerType.generator( - BoolExprs.True(), prop, solverFactory.createUCSolver() - ) + UNSAT_CORE(ExprTraceUnsatCoreChecker::create) } + override fun getTraceChecker(): ExprTraceChecker = + traceCheckerType.generator(BoolExprs.True(), prop, solverFactory.createUCSolver()) + } } diff --git a/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddCheckerTest.java b/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddCheckerTest.java index 070daeb91f..0f9f524354 100644 --- a/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddCheckerTest.java +++ b/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/algorithm/mdd/MddCheckerTest.java @@ -15,6 +15,14 @@ */ package hu.bme.mit.theta.analysis.algorithm.mdd; +import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.*; +import static hu.bme.mit.theta.core.type.anytype.Exprs.Prime; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.common.logging.ConsoleLogger; @@ -29,31 +37,19 @@ import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory; import hu.bme.mit.theta.solver.SolverPool; import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory; +import java.util.Arrays; +import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; - -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.*; -import static hu.bme.mit.theta.core.type.anytype.Exprs.Prime; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - @RunWith(value = Parameterized.class) - public class MddCheckerTest { private static final VarDecl X = Decls.Var("x", IntType.getInstance()); private static final VarDecl Y = Decls.Var("y", IntType.getInstance()); private static final VarDecl Z = Decls.Var("z", IntType.getInstance()); - @Parameterized.Parameter(value = 0) public Expr initExpr; @@ -71,57 +67,86 @@ public class MddCheckerTest { @Parameterized.Parameters(name = "{index}: {0}, {1}, {2}, {3}, {4}") public static Collection data() { - return Arrays.asList(new Object[][]{ - - {Eq(X.getRef(), Int(0)), // x = 0 + return Arrays.asList( + new Object[][] { + { + Eq(X.getRef(), Int(0)), // x = 0 Eq(Prime(X.getRef()), X.getRef()), // x'=x Not(Eq(X.getRef(), Int(5))), // not x = 5 true, - 1L}, - - {Eq(X.getRef(), Int(0)), + 1L + }, + { + Eq(X.getRef(), Int(0)), Eq(Prime(X.getRef()), X.getRef()), Not(Eq(X.getRef(), Int(0))), false, - 1L}, - - {Eq(X.getRef(), Int(0)), // x = 0 - And(Eq(Prime(X.getRef()), Add(X.getRef(), Int(1))), Leq(Prime(X.getRef()), Int(10))), // x' = x + 1 & x' <= 10 + 1L + }, + { + Eq(X.getRef(), Int(0)), // x = 0 + And( + Eq(Prime(X.getRef()), Add(X.getRef(), Int(1))), + Leq(Prime(X.getRef()), Int(10))), // x' = x + 1 & x' <= 10 Not(Eq(X.getRef(), Int(5))), false, - 11L}, - - {Eq(X.getRef(), Int(0)), - And(Eq(Prime(X.getRef()), Add(X.getRef(), Int(1))), Leq(Prime(X.getRef()), Int(5))), + 11L + }, + { + Eq(X.getRef(), Int(0)), + And( + Eq(Prime(X.getRef()), Add(X.getRef(), Int(1))), + Leq(Prime(X.getRef()), Int(5))), Not(Eq(X.getRef(), Int(5))), false, - 6L}, - - {Eq(X.getRef(), Int(0)), - And(Eq(Prime(X.getRef()), Add(X.getRef(), Int(1))), Leq(Prime(X.getRef()), Int(4))), + 6L + }, + { + Eq(X.getRef(), Int(0)), + And( + Eq(Prime(X.getRef()), Add(X.getRef(), Int(1))), + Leq(Prime(X.getRef()), Int(4))), Not(Eq(X.getRef(), Int(5))), true, - 5L}, - - {And(Eq(X.getRef(), Int(0)), Eq(Y.getRef(), Int(0)), Eq(Z.getRef(), Int(0))), - And(And(Eq(Prime(X.getRef()), Add(Y.getRef(), Int(1))), Eq(Prime(Y.getRef()), Add(Z.getRef(), Int(1))), Eq(Prime(Z.getRef()), Add(Z.getRef(), Int(1)))), IntExprs.Lt(Prime(Z.getRef()), Int(10))), + 5L + }, + { + And(Eq(X.getRef(), Int(0)), Eq(Y.getRef(), Int(0)), Eq(Z.getRef(), Int(0))), + And( + And( + Eq(Prime(X.getRef()), Add(Y.getRef(), Int(1))), + Eq(Prime(Y.getRef()), Add(Z.getRef(), Int(1))), + Eq(Prime(Z.getRef()), Add(Z.getRef(), Int(1)))), + IntExprs.Lt(Prime(Z.getRef()), Int(10))), Not(Eq(X.getRef(), Int(5))), false, - 10L}, - - {And(Eq(X.getRef(), Int(0)), Eq(Y.getRef(), Int(0)), Eq(Z.getRef(), Int(0))), - And(And(Eq(Prime(X.getRef()), Add(Y.getRef(), Int(1))), Eq(Prime(Y.getRef()), Add(Z.getRef(), Int(1))), Eq(Prime(Z.getRef()), Add(Z.getRef(), Int(1)))), IntExprs.Lt(Prime(Z.getRef()), Int(6))), + 10L + }, + { + And(Eq(X.getRef(), Int(0)), Eq(Y.getRef(), Int(0)), Eq(Z.getRef(), Int(0))), + And( + And( + Eq(Prime(X.getRef()), Add(Y.getRef(), Int(1))), + Eq(Prime(Y.getRef()), Add(Z.getRef(), Int(1))), + Eq(Prime(Z.getRef()), Add(Z.getRef(), Int(1)))), + IntExprs.Lt(Prime(Z.getRef()), Int(6))), Not(Eq(X.getRef(), Int(5))), false, - 6L}, - - {And(Eq(X.getRef(), Int(0)), Eq(Y.getRef(), Int(0)), Eq(Z.getRef(), Int(0))), - And(And(Eq(Prime(X.getRef()), Add(Y.getRef(), Int(1))), Eq(Prime(Y.getRef()), Add(Z.getRef(), Int(1))), Eq(Prime(Z.getRef()), Add(Z.getRef(), Int(1)))), IntExprs.Lt(Prime(Z.getRef()), Int(5))), + 6L + }, + { + And(Eq(X.getRef(), Int(0)), Eq(Y.getRef(), Int(0)), Eq(Z.getRef(), Int(0))), + And( + And( + Eq(Prime(X.getRef()), Add(Y.getRef(), Int(1))), + Eq(Prime(Y.getRef()), Add(Z.getRef(), Int(1))), + Eq(Prime(Z.getRef()), Add(Z.getRef(), Int(1)))), + IntExprs.Lt(Prime(Z.getRef()), Int(5))), Not(Eq(X.getRef(), Int(5))), true, - 5L}, - - }); + 5L + }, + }); } @Test @@ -139,23 +164,32 @@ public void testGsat() throws Exception { testWithIterationStrategy(MddChecker.IterationStrategy.GSAT); } - public void testWithIterationStrategy(MddChecker.IterationStrategy iterationStrategy) throws Exception { + public void testWithIterationStrategy(MddChecker.IterationStrategy iterationStrategy) + throws Exception { final Logger logger = new ConsoleLogger(Logger.Level.SUBSTEP); - final SafetyResult status; + final SafetyResult status; try (var solverPool = new SolverPool(Z3LegacySolverFactory.getInstance())) { - final MddChecker checker = MddChecker.create(initExpr, VarIndexingFactory.indexing(0), new ExprAction() { - @Override - public Expr toExpr() { - return tranExpr; - } - - @Override - public VarIndexing nextIndexing() { - return VarIndexingFactory.indexing(1); - } - }, propExpr, solverPool, logger, iterationStrategy); + final MddChecker checker = + MddChecker.create( + initExpr, + VarIndexingFactory.indexing(0), + new ExprAction() { + @Override + public Expr toExpr() { + return tranExpr; + } + + @Override + public VarIndexing nextIndexing() { + return VarIndexingFactory.indexing(1); + } + }, + propExpr, + solverPool, + logger, + iterationStrategy); status = checker.check(null); } @@ -164,9 +198,6 @@ public VarIndexing nextIndexing() { } else { assertTrue(status.isUnsafe()); } - assertEquals(stateSpaceSize, status.getWitness().size()); - - + assertEquals(stateSpaceSize, status.getProof().size()); } - } diff --git a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/SafetyResultAdapter.kt b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/SafetyResultAdapter.kt index 6829d577c0..0cd6eb5b2f 100644 --- a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/SafetyResultAdapter.kt +++ b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/SafetyResultAdapter.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.grammar.gson import com.google.gson.Gson @@ -24,72 +23,75 @@ import com.google.gson.stream.JsonWriter import hu.bme.mit.theta.analysis.Action import hu.bme.mit.theta.analysis.State import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.arg.ARG import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.algorithm.Statistics +import hu.bme.mit.theta.analysis.algorithm.arg.ARG import java.lang.reflect.Type import java.util.* class SafetyResultAdapter( - val gsonSupplier: () -> Gson, - private val argTypeSupplier: () -> Type, - private val traceTypeSupplier: () -> Type, + val gsonSupplier: () -> Gson, + private val argTypeSupplier: () -> Type, + private val traceTypeSupplier: () -> Type, ) : TypeAdapter, Trace>>() { - private lateinit var gson: Gson - private lateinit var argType: Type - private lateinit var traceType: Type + private lateinit var gson: Gson + private lateinit var argType: Type + private lateinit var traceType: Type - override fun write(writer: JsonWriter, - value: SafetyResult, Trace>) { - initGson() - writer.beginObject() - writer.name("arg") - gson.toJson(gson.toJsonTree(value.witness), writer) - writer.name("stats") -// gson.toJson(gson.toJsonTree(value.stats), writer) - gson.toJson(gson.toJsonTree(Optional.empty()), writer) - if (value.isSafe) { - writer.name("safe").value(true) - } else { - val unsafe = value.asUnsafe() - writer.name("safe").value(false) - writer.name("trace") - gson.toJson(gson.toJsonTree(unsafe.cex), writer) - } - writer.endObject() + override fun write( + writer: JsonWriter, + value: SafetyResult, Trace>, + ) { + initGson() + writer.beginObject() + writer.name("arg") + gson.toJson(gson.toJsonTree(value.proof), writer) + writer.name("stats") + // gson.toJson(gson.toJsonTree(value.stats), writer) + gson.toJson(gson.toJsonTree(Optional.empty()), writer) + if (value.isSafe) { + writer.name("safe").value(true) + } else { + val unsafe = value.asUnsafe() + writer.name("safe").value(false) + writer.name("trace") + gson.toJson(gson.toJsonTree(unsafe.cex), writer) } + writer.endObject() + } - override fun read(reader: JsonReader): SafetyResult, Trace> { - initGson() - initTypes() - lateinit var arg: ARG - lateinit var stats: Optional - var safe: Boolean? = null - lateinit var trace: Trace - reader.beginObject() - while (reader.peek() != JsonToken.END_OBJECT) { - when (reader.nextName()) { - "arg" -> arg = gson.fromJson(reader, argType) - "stats" -> stats = gson.fromJson(reader, Optional::class.java) - "safe" -> safe = reader.nextBoolean() - "trace" -> trace = gson.fromJson(reader, traceType) - } - } - reader.endObject() - return if (stats.isEmpty) - if (safe == true) SafetyResult.safe(arg) else SafetyResult.unsafe(trace, arg) - else - if (safe == false) SafetyResult.safe(arg, stats.get()) else SafetyResult.unsafe(trace, - arg, stats.get()) + override fun read( + reader: JsonReader + ): SafetyResult, Trace> { + initGson() + initTypes() + lateinit var arg: ARG + lateinit var stats: Optional + var safe: Boolean? = null + lateinit var trace: Trace + reader.beginObject() + while (reader.peek() != JsonToken.END_OBJECT) { + when (reader.nextName()) { + "arg" -> arg = gson.fromJson(reader, argType) + "stats" -> stats = gson.fromJson(reader, Optional::class.java) + "safe" -> safe = reader.nextBoolean() + "trace" -> trace = gson.fromJson(reader, traceType) + } } + reader.endObject() + return if (stats.isEmpty) + if (safe == true) SafetyResult.safe(arg) else SafetyResult.unsafe(trace, arg) + else if (safe == false) SafetyResult.safe(arg, stats.get()) + else SafetyResult.unsafe(trace, arg, stats.get()) + } - private fun initGson() { - if (!this::gson.isInitialized) gson = gsonSupplier() - } + private fun initGson() { + if (!this::gson.isInitialized) gson = gsonSupplier() + } - private fun initTypes() { - if (!this::traceType.isInitialized) traceType = traceTypeSupplier() - if (!this::argType.isInitialized) argType = argTypeSupplier() - } -} \ No newline at end of file + private fun initTypes() { + if (!this::traceType.isInitialized) traceType = traceTypeSupplier() + if (!this::argType.isInitialized) argType = argTypeSupplier() + } +} diff --git a/subprojects/frontends/c-frontend/src/main/antlr/C.g4 b/subprojects/frontends/c-frontend/src/main/antlr/C.g4 index af6b7ccd1c..386d294c42 100644 --- a/subprojects/frontends/c-frontend/src/main/antlr/C.g4 +++ b/subprojects/frontends/c-frontend/src/main/antlr/C.g4 @@ -173,7 +173,7 @@ logicalOrExpression ; conditionalExpression - : logicalOrExpression ('?' expression ':' conditionalExpression)? + : logicalOrExpression ('?' ifTrue=expression ':' ifFalse=expression)? ; assignmentExpression diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/UnsupportedFrontendElementException.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/UnsupportedFrontendElementException.java new file mode 100644 index 0000000000..e3bef460e0 --- /dev/null +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/UnsupportedFrontendElementException.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hu.bme.mit.theta.frontend; + +public class UnsupportedFrontendElementException extends RuntimeException { + public UnsupportedFrontendElementException(String message) { + super(message); + } +} diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/ExpressionVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/ExpressionVisitor.java index 60a7ff7609..609518ea3e 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/ExpressionVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/ExpressionVisitor.java @@ -18,13 +18,7 @@ import hu.bme.mit.theta.c.frontend.dsl.gen.CBaseVisitor; import hu.bme.mit.theta.c.frontend.dsl.gen.CParser; -import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionAccessContext; -import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionBracesContext; -import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionBracketsContext; -import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionDecrementContext; -import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionIncrementContext; -import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionMemberAccessContext; -import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionPtrMemberAccessContext; +import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.*; import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; @@ -35,23 +29,16 @@ import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs; import hu.bme.mit.theta.core.type.abstracttype.DivExpr; import hu.bme.mit.theta.core.type.abstracttype.ModExpr; -import hu.bme.mit.theta.core.type.anytype.Dereference; -import hu.bme.mit.theta.core.type.anytype.Exprs; -import hu.bme.mit.theta.core.type.anytype.IteExpr; -import hu.bme.mit.theta.core.type.anytype.RefExpr; -import hu.bme.mit.theta.core.type.anytype.Reference; +import hu.bme.mit.theta.core.type.anytype.*; import hu.bme.mit.theta.core.type.booltype.BoolExprs; import hu.bme.mit.theta.core.type.booltype.BoolType; -import hu.bme.mit.theta.core.type.bvtype.BvAndExpr; -import hu.bme.mit.theta.core.type.bvtype.BvExprs; -import hu.bme.mit.theta.core.type.bvtype.BvOrExpr; -import hu.bme.mit.theta.core.type.bvtype.BvType; -import hu.bme.mit.theta.core.type.bvtype.BvXorExpr; +import hu.bme.mit.theta.core.type.bvtype.*; import hu.bme.mit.theta.core.type.fptype.FpLitExpr; import hu.bme.mit.theta.core.type.inttype.IntType; import hu.bme.mit.theta.core.utils.BvUtils; import hu.bme.mit.theta.core.utils.FpUtils; import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; import hu.bme.mit.theta.frontend.transformation.ArchitectureConfig; import hu.bme.mit.theta.frontend.transformation.grammar.function.FunctionVisitor; import hu.bme.mit.theta.frontend.transformation.grammar.preprocess.TypedefVisitor; @@ -59,7 +46,6 @@ import hu.bme.mit.theta.frontend.transformation.model.declaration.CDeclaration; import hu.bme.mit.theta.frontend.transformation.model.statements.CAssignment; import hu.bme.mit.theta.frontend.transformation.model.statements.CCall; -import hu.bme.mit.theta.frontend.transformation.model.statements.CCompound; import hu.bme.mit.theta.frontend.transformation.model.statements.CExpr; import hu.bme.mit.theta.frontend.transformation.model.statements.CStatement; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; @@ -70,23 +56,14 @@ import org.kframework.mpfr.BinaryMathContext; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Deque; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkState; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Add; import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Div; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Geq; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Ite; import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Mod; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Neq; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Sub; +import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.*; import static hu.bme.mit.theta.core.type.anytype.Exprs.Reference; import static hu.bme.mit.theta.core.type.fptype.FpExprs.FpType; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; @@ -136,38 +113,6 @@ public List getPreStatements() { return preStatements; } - @Override - public Expr visitConditionalExpression(CParser.ConditionalExpressionContext ctx) { - if (ctx.expression() != null) { - CStatement ifTrue = ctx.expression().accept(functionVisitor); - addPreStatements(ifTrue); - if (ifTrue instanceof CAssignment) { - preStatements.add(ifTrue); - } - Expr expr = ctx.logicalOrExpression().accept(this); - Expr lhs = ifTrue.getExpression(); - Expr rhs = ctx.conditionalExpression().accept(this); - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(List.of(CComplexType.getType(lhs, parseContext), CComplexType.getType(rhs, parseContext)), parseContext); - IteExpr ite = Ite( - AbstractExprs.Neq(CComplexType.getType(expr, parseContext).getNullValue(), expr), - smallestCommonType.castTo(lhs), - smallestCommonType.castTo(rhs) - ); - parseContext.getMetadata().create(ite, "cType", smallestCommonType); - return ite; - } else return ctx.logicalOrExpression().accept(this); - } - - private void addPreStatements(CStatement ifTrue) { - if (ifTrue instanceof CCompound) { - if (ifTrue.getPreStatements() != null) preStatements.add(ifTrue.getPreStatements()); - if (ifTrue.getPostStatements() != null) postStatements.add(ifTrue.getPostStatements()); - for (CStatement cStatement : ((CCompound) ifTrue).getcStatementList()) { - addPreStatements(cStatement); - } - } - } - @Override public Expr visitLogicalOrExpression(CParser.LogicalOrExpressionContext ctx) { if (ctx.logicalAndExpression().size() > 1) { @@ -310,7 +255,7 @@ public Expr visitRelationalExpression(CParser.RelationalExpressionContext ctx guard = AbstractExprs.Geq(leftExpr, rightExpr); break; default: - throw new IllegalStateException("Unexpected value: " + ctx.signs.get(i).getText()); + throw new UnsupportedFrontendElementException("Unexpected relational expression sign: " + ctx.signs.get(i).getText()); } // MaxEnumAnalyzer.instance.consume(guard); TODO: handle circular dependency CComplexType signedInt = CComplexType.getSignedInt(parseContext); @@ -415,7 +360,7 @@ public Expr visitMultiplicativeExpression(CParser.MultiplicativeExpressionCon } break; default: - throw new IllegalStateException("Unexpected value: " + ctx.signs.get(i).getText()); + throw new UnsupportedFrontendElementException("Unexpected multiplicative expression sign: " + ctx.signs.get(i).getText()); } parseContext.getMetadata().create(expr, "cType", smallestCommonType); expr = smallestCommonType.castTo(expr); @@ -665,9 +610,9 @@ public Expr visitPrimaryExpressionConstant(CParser.PrimaryExpressionConstantC BigFloat bigFloat; if (text.startsWith("0x")) { - throw new UnsupportedOperationException("Hexadecimal FP constants are not yet supported!"); + throw new UnsupportedFrontendElementException("Hexadecimal FP constants are not yet supported!"); } else if (text.startsWith("0b")) { - throw new UnsupportedOperationException("Binary FP constants are not yet supported!"); + throw new UnsupportedFrontendElementException("Binary FP constants are not yet supported!"); } else { bigFloat = new BigFloat(text, new BinaryMathContext(significand - 1, exponent)); } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/UnsupportedInitializer.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/UnsupportedInitializer.java index 121f0a9bba..b11b230c07 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/UnsupportedInitializer.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/UnsupportedInitializer.java @@ -20,6 +20,7 @@ import hu.bme.mit.theta.core.type.LitExpr; import hu.bme.mit.theta.core.type.NullaryExpr; import hu.bme.mit.theta.core.type.inttype.IntType; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; @@ -32,7 +33,7 @@ public IntType getType() { @Override public LitExpr eval(Valuation val) { - throw new UnsupportedOperationException("UnsupportedInitializer expressions are not supported."); + throw new UnsupportedFrontendElementException("UnsupportedInitializer expressions are not supported."); } @Override diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/function/FunctionVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/function/FunctionVisitor.java index eb98d8d707..4f9041fa08 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/function/FunctionVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/function/FunctionVisitor.java @@ -26,7 +26,9 @@ import hu.bme.mit.theta.core.stmt.AssumeStmt; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.LitExpr; +import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs; import hu.bme.mit.theta.core.type.anytype.Exprs; +import hu.bme.mit.theta.core.type.anytype.IteExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.frontend.ParseContext; @@ -40,26 +42,7 @@ import hu.bme.mit.theta.frontend.transformation.grammar.type.DeclarationVisitor; import hu.bme.mit.theta.frontend.transformation.grammar.type.TypeVisitor; import hu.bme.mit.theta.frontend.transformation.model.declaration.CDeclaration; -import hu.bme.mit.theta.frontend.transformation.model.statements.CAssignment; -import hu.bme.mit.theta.frontend.transformation.model.statements.CAssume; -import hu.bme.mit.theta.frontend.transformation.model.statements.CBreak; -import hu.bme.mit.theta.frontend.transformation.model.statements.CCase; -import hu.bme.mit.theta.frontend.transformation.model.statements.CCompound; -import hu.bme.mit.theta.frontend.transformation.model.statements.CContinue; -import hu.bme.mit.theta.frontend.transformation.model.statements.CDecls; -import hu.bme.mit.theta.frontend.transformation.model.statements.CDefault; -import hu.bme.mit.theta.frontend.transformation.model.statements.CDoWhile; -import hu.bme.mit.theta.frontend.transformation.model.statements.CExpr; -import hu.bme.mit.theta.frontend.transformation.model.statements.CFor; -import hu.bme.mit.theta.frontend.transformation.model.statements.CFunction; -import hu.bme.mit.theta.frontend.transformation.model.statements.CGoto; -import hu.bme.mit.theta.frontend.transformation.model.statements.CIf; -import hu.bme.mit.theta.frontend.transformation.model.statements.CInitializerList; -import hu.bme.mit.theta.frontend.transformation.model.statements.CProgram; -import hu.bme.mit.theta.frontend.transformation.model.statements.CRet; -import hu.bme.mit.theta.frontend.transformation.model.statements.CStatement; -import hu.bme.mit.theta.frontend.transformation.model.statements.CSwitch; -import hu.bme.mit.theta.frontend.transformation.model.statements.CWhile; +import hu.bme.mit.theta.frontend.transformation.model.statements.*; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CVoid; import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; @@ -67,20 +50,13 @@ import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.StringJoiner; +import java.util.*; +import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkState; import static hu.bme.mit.theta.core.decl.Decls.Var; import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Add; +import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Ite; import static hu.bme.mit.theta.core.utils.TypeUtils.cast; import static hu.bme.mit.theta.grammar.UtilsKt.textWithWS; @@ -456,14 +432,19 @@ public CStatement visitBodyDeclaration(CParser.BodyDeclarationContext ctx) { createVars(declaration); if (declaration.getType() instanceof Struct) { checkState(declaration.getInitExpr() instanceof CInitializerList, "Struct can only be initialized via an initializer list!"); + final var initializerList = (CInitializerList) declaration.getInitExpr(); List> varDecls = declaration.getVarDecls(); - for (int i = 0; i < varDecls.size(); i++) { - VarDecl varDecl = varDecls.get(i); - Tuple2, CStatement> initializer = ((CInitializerList) declaration.getInitExpr()).getStatements().get(i); - - CAssignment cAssignment = new CAssignment(varDecl.getRef(), initializer.get2(), "=", parseContext); + VarDecl varDecl = varDecls.get(0); + final var ptrType = CComplexType.getUnsignedLong(parseContext); + LitExpr currentValue = ptrType.getNullValue(); + LitExpr unitValue = ptrType.getUnitValue(); + for (Tuple2, CStatement> statement : initializerList.getStatements()) { + final var expr = statement.get2().getExpression(); + final var deref = Exprs.Dereference(cast(varDecl.getRef(), currentValue.getType()), cast(currentValue, currentValue.getType()), expr.getType()); + CAssignment cAssignment = new CAssignment(deref, statement.get2(), "=", parseContext); recordMetadata(ctx, cAssignment); compound.getcStatementList().add(cAssignment); + currentValue = Add(currentValue, unitValue).eval(ImmutableValuation.empty()); } } else { checkState(declaration.getVarDecls().size() == 1, "non-struct declarations shall only have one variable!"); @@ -484,6 +465,18 @@ public CStatement visitBodyDeclaration(CParser.BodyDeclarationContext ctx) { CAssignment cAssignment = new CAssignment(declaration.getVarDecls().get(0).getRef(), declaration.getInitExpr(), "=", parseContext); recordMetadata(ctx, cAssignment); compound.getcStatementList().add(cAssignment); + if (declaration.getInitExpr() instanceof CCompound compoundInitExpr) { + final var preCompound = new CCompound(parseContext); + final var postCompound = new CCompound(parseContext); + final var preStatements = collectPreStatements(compoundInitExpr); + preCompound.getcStatementList().addAll(preStatements); + final var postStatements = collectPostStatements(compoundInitExpr); + postCompound.getcStatementList().addAll(postStatements); + resetPreStatements(compoundInitExpr); + resetPostStatements(compoundInitExpr); + compound.setPreStatements(preCompound); + compound.setPostStatements(postCompound); + } } } } else { @@ -528,7 +521,16 @@ public CStatement visitAssignmentExpressionAssignmentExpression(CParser.Assignme CCompound preStatements = new CCompound(parseContext); CCompound postStatements = new CCompound(parseContext); Expr ret = ctx.unaryExpression().accept(expressionVisitor); - CAssignment cAssignment = new CAssignment(ret, ctx.assignmentExpression().accept(this), ctx.assignmentOperator().getText(), parseContext); + CStatement rhs = ctx.assignmentExpression().accept(this); + if (rhs instanceof CCompound compoundInitExpr) { + final var preStatementList = collectPreStatements(compoundInitExpr); + preStatements.getcStatementList().addAll(preStatementList); + final var postStatementList = collectPostStatements(compoundInitExpr); + postStatements.getcStatementList().addAll(postStatementList); + resetPreStatements(compoundInitExpr); + resetPostStatements(compoundInitExpr); + } + CAssignment cAssignment = new CAssignment(ret, rhs, ctx.assignmentOperator().getText(), parseContext); compound.getcStatementList().add(cAssignment); preStatements.getcStatementList().addAll(expressionVisitor.getPreStatements()); compound.setPreStatements(preStatements); @@ -541,18 +543,146 @@ public CStatement visitAssignmentExpressionAssignmentExpression(CParser.Assignme @Override public CStatement visitAssignmentExpressionConditionalExpression(CParser.AssignmentExpressionConditionalExpressionContext ctx) { - ExpressionVisitor expressionVisitor = new ExpressionVisitor(parseContext, this, variables, functions, typedefVisitor, typeVisitor, uniqueWarningLogger); + return ctx.conditionalExpression().accept(this); + } + + private void resetPreStatements(CStatement statement) { + if (statement instanceof CCompound compound) { + compound.setPreStatements(null); + for (CStatement cStatement : compound.getcStatementList()) { + resetPreStatements(cStatement); + } + } + } + + private void resetPostStatements(CStatement statement) { + if (statement instanceof CCompound compound) { + compound.setPostStatements(null); + for (CStatement cStatement : compound.getcStatementList()) { + resetPostStatements(cStatement); + } + } + } + + private List getStatementList(CStatement statement) { + if (statement instanceof CCompound compound) { + return compound.getcStatementList().stream().flatMap(i -> getStatementList(i).stream()).toList(); + } else if (statement != null) { + return List.of(statement); + } else { + return List.of(); + } + } + + /* + This collects the following: + - the current compound's pre-statement + - all pre-statements of the pre-statement (before the pre-statement) + - all the pre-statement of every cStatement + */ + private List collectPreStatements(CStatement cStatement) { + if (cStatement instanceof CCompound) { + return Stream.concat( + Stream.concat( + collectPreStatements(cStatement.getPreStatements()).stream(), + getStatementList(cStatement.getPreStatements()).stream()), + ((CCompound) cStatement).getcStatementList().stream().flatMap(cStatement1 -> collectPreStatements(cStatement1).stream()) + ).filter(i -> !(i instanceof CExpr)).toList(); + } else return List.of(); + } + + /* + This collects the following: + - all the post-statements of every cStatement + - the current compound's post-statement + - all post-statements of the post-statement (after the post-statement) + */ + private List collectPostStatements(CStatement cStatement) { + if (cStatement instanceof CCompound) { + return Stream.concat( + ((CCompound) cStatement).getcStatementList().stream().flatMap(cStatement1 -> collectPostStatements(cStatement1).stream()), + Stream.concat( + getStatementList(cStatement.getPostStatements()).stream(), + collectPostStatements(cStatement.getPostStatements()).stream()) + ).filter(i -> !(i instanceof CExpr)).toList(); + } else return List.of(); + } + + // This is in the function visitor, not in the expression visitor, because + // cond ? f1() : f2() + // will only call either f1 or f2 (it can be used for branching) + @Override + public CStatement visitConditionalExpression(CParser.ConditionalExpressionContext ctx) { CCompound compound = new CCompound(parseContext); CCompound preStatements = new CCompound(parseContext); CCompound postStatements = new CCompound(parseContext); - Expr ret = ctx.conditionalExpression().accept(expressionVisitor); - CExpr cexpr = new CExpr(ret, parseContext); + + ExpressionVisitor expressionVisitor = new ExpressionVisitor(parseContext, this, variables, functions, typedefVisitor, typeVisitor, uniqueWarningLogger); + + Expr iteExpr; + if (!ctx.expression().isEmpty()) { + CStatement ifTrue = ctx.ifTrue.accept(this); + CStatement ifFalse = ctx.ifFalse.accept(this); + + Expr expr = ctx.logicalOrExpression().accept(expressionVisitor); + Expr lhs = ifTrue.getExpression(); + Expr rhs = ifFalse.getExpression(); + + CCompound guardCompound = new CCompound(parseContext); + guardCompound.getcStatementList().add(new CExpr(expr, parseContext)); + guardCompound.setPostStatements(new CCompound(parseContext)); + guardCompound.setPreStatements(new CCompound(parseContext)); + + + CCompound ifTruePre = new CCompound(parseContext); + List ifTruePreList = collectPreStatements(ifTrue); + ifTruePre.getcStatementList().addAll(ifTruePreList); + ifTruePre.setPostStatements(new CCompound(parseContext)); + ifTruePre.setPreStatements(new CCompound(parseContext)); + CCompound ifFalsePre = new CCompound(parseContext); + List ifFalsePreList = collectPreStatements(ifFalse); + ifFalsePre.getcStatementList().addAll(ifFalsePreList); + ifFalsePre.setPostStatements(new CCompound(parseContext)); + ifFalsePre.setPreStatements(new CCompound(parseContext)); + + CCompound ifTruePost = new CCompound(parseContext); + List ifTruePostList = collectPostStatements(ifTrue); + ifTruePost.getcStatementList().addAll(ifTruePostList); + ifTruePost.setPostStatements(new CCompound(parseContext)); + ifTruePost.setPreStatements(new CCompound(parseContext)); + CCompound ifFalsePost = new CCompound(parseContext); + List ifFalsePostList = collectPostStatements(ifFalse); + ifFalsePost.getcStatementList().addAll(ifFalsePostList); + ifFalsePost.setPostStatements(new CCompound(parseContext)); + ifFalsePost.setPreStatements(new CCompound(parseContext)); + + + if (!ifTruePreList.isEmpty() || !ifFalsePreList.isEmpty()) { + preStatements.getcStatementList().add(new CIf(guardCompound, ifTruePre, ifFalsePre, parseContext)); + } + if (!ifTruePostList.isEmpty() || !ifFalsePostList.isEmpty()) { + postStatements.getcStatementList().add(new CIf(guardCompound, ifTruePost, ifFalsePost, parseContext)); + } + + CComplexType smallestCommonType = CComplexType.getSmallestCommonType(List.of(CComplexType.getType(lhs, parseContext), CComplexType.getType(rhs, parseContext)), parseContext); + IteExpr ite = Ite( + AbstractExprs.Neq(CComplexType.getType(expr, parseContext).getNullValue(), expr), + smallestCommonType.castTo(lhs), + smallestCommonType.castTo(rhs) + ); + parseContext.getMetadata().create(ite, "cType", smallestCommonType); + iteExpr = ite; + } else { + iteExpr = ctx.logicalOrExpression().accept(expressionVisitor); + } + + CExpr cexpr = new CExpr(iteExpr, parseContext); compound.getcStatementList().add(cexpr); - preStatements.getcStatementList().addAll(expressionVisitor.getPreStatements()); + preStatements.getcStatementList().addAll(0, expressionVisitor.getPreStatements()); compound.setPreStatements(preStatements); recordMetadata(ctx, compound); - postStatements.getcStatementList().addAll(expressionVisitor.getPostStatements()); compound.setPostStatements(postStatements); + postStatements.getcStatementList().addAll(expressionVisitor.getPostStatements()); recordMetadata(ctx, compound); return compound; } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/DeclarationVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/DeclarationVisitor.java index f752eb7ef8..84be79414d 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/DeclarationVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/DeclarationVisitor.java @@ -22,6 +22,7 @@ import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; import hu.bme.mit.theta.frontend.transformation.grammar.expression.UnsupportedInitializer; import hu.bme.mit.theta.frontend.transformation.grammar.function.FunctionVisitor; import hu.bme.mit.theta.frontend.transformation.grammar.preprocess.TypedefVisitor; @@ -140,7 +141,7 @@ public CDeclaration visitStructDeclaratorSimple(CParser.StructDeclaratorSimpleCo @Override public CDeclaration visitStructDeclaratorConstant(CParser.StructDeclaratorConstantContext ctx) { - throw new UnsupportedOperationException("Not yet supported!"); + throw new UnsupportedFrontendElementException("Not yet supported!"); } @Override @@ -191,17 +192,17 @@ public CDeclaration visitDirectDeclaratorArray1(CParser.DirectDeclaratorArray1Co @Override public CDeclaration visitDirectDeclaratorArray2(CParser.DirectDeclaratorArray2Context ctx) { - throw new UnsupportedOperationException("Not yet implemented!"); + throw new UnsupportedFrontendElementException("Not yet implemented!"); } @Override public CDeclaration visitDirectDeclaratorArray3(CParser.DirectDeclaratorArray3Context ctx) { - throw new UnsupportedOperationException("Not yet implemented!"); + throw new UnsupportedFrontendElementException("Not yet implemented!"); } @Override public CDeclaration visitDirectDeclaratorArray4(CParser.DirectDeclaratorArray4Context ctx) { - throw new UnsupportedOperationException("Not yet implemented!"); + throw new UnsupportedFrontendElementException("Not yet implemented!"); } @Override diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/TypeVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/TypeVisitor.java index 83acdef4b6..8fad36f961 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/TypeVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/TypeVisitor.java @@ -25,35 +25,19 @@ import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; import hu.bme.mit.theta.frontend.transformation.grammar.preprocess.TypedefVisitor; import hu.bme.mit.theta.frontend.transformation.model.declaration.CDeclaration; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; -import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; -import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory; -import hu.bme.mit.theta.frontend.transformation.model.types.simple.DeclaredName; import hu.bme.mit.theta.frontend.transformation.model.types.simple.Enum; -import hu.bme.mit.theta.frontend.transformation.model.types.simple.NamedType; -import hu.bme.mit.theta.frontend.transformation.model.types.simple.Struct; +import hu.bme.mit.theta.frontend.transformation.model.types.simple.*; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.Atomic; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.DeclaredName; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.Enum; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.Extern; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.NamedType; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.Signed; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.ThreadLocal; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.Typedef; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.Unsigned; -import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.Volatile; +import static hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory.*; public class TypeVisitor extends CBaseVisitor { private final DeclarationVisitor declarationVisitor; @@ -211,15 +195,15 @@ public CSimpleType visitStorageClassSpecifier(CParser.StorageClassSpecifierConte case "auto": case "register": case "_Thread_local": - throw new UnsupportedOperationException("Not yet implemented"); + throw new UnsupportedFrontendElementException("Not yet implemented (" + ctx.getText() + ")"); } - throw new UnsupportedOperationException( + throw new UnsupportedFrontendElementException( "Storage class specifier not expected: " + ctx.getText()); } @Override public CSimpleType visitTypeSpecifierAtomic(CParser.TypeSpecifierAtomicContext ctx) { - throw new UnsupportedOperationException("Not yet implemented"); + throw new UnsupportedFrontendElementException("Not yet implemented"); } @Override @@ -229,7 +213,7 @@ public CSimpleType visitTypeSpecifierCompound(CParser.TypeSpecifierCompoundConte @Override public CSimpleType visitTypeSpecifierFunctionPointer(CParser.TypeSpecifierFunctionPointerContext ctx) { - throw new UnsupportedOperationException("Function pointers not yet implemented"); + throw new UnsupportedFrontendElementException("Function pointers not yet implemented"); } @Override @@ -245,13 +229,17 @@ public CSimpleType visitCompoundDefinition(CParser.CompoundDefinitionContext ctx CParser.SpecifierQualifierListContext specifierQualifierListContext = structDeclarationContext.specifierQualifierList(); CSimpleType cSimpleType = specifierQualifierListContext.accept(this); if (structDeclarationContext.structDeclaratorList() == null) { - struct.addField(cSimpleType.getAssociatedName(), cSimpleType); + final var decl = new CDeclaration(cSimpleType); + struct.addField(decl); } else { for (CParser.StructDeclaratorContext structDeclaratorContext : structDeclarationContext.structDeclaratorList() .structDeclarator()) { CDeclaration declaration = structDeclaratorContext.accept( declarationVisitor); - struct.addField(declaration.getName(), cSimpleType); + if (declaration.getType() == null) { + declaration.setType(cSimpleType); + } + struct.addField(declaration); } } } @@ -298,7 +286,7 @@ public CSimpleType visitEnumUsage(CParser.EnumUsageContext ctx) { @Override public CSimpleType visitTypeSpecifierExtension(CParser.TypeSpecifierExtensionContext ctx) { - throw new UnsupportedOperationException("Not yet implemented"); + throw new UnsupportedFrontendElementException("Not yet implemented typeSpecifierExtension"); } @Override @@ -361,7 +349,7 @@ public CSimpleType visitTypeSpecifierTypedefName(CParser.TypeSpecifierTypedefNam @Override public CSimpleType visitTypeSpecifierTypeof(CParser.TypeSpecifierTypeofContext ctx) { - throw new UnsupportedOperationException("Not yet implemented"); + throw new UnsupportedFrontendElementException("Not yet implemented typeSpecifierTypeof"); } @Override @@ -370,13 +358,13 @@ public CSimpleType visitTypeQualifier(CParser.TypeQualifierContext ctx) { case "const": return null; case "restrict": - throw new UnsupportedOperationException("Not yet implemented!"); + throw new UnsupportedFrontendElementException("Not yet implemented 'restrict'!"); case "volatile": return Volatile(); case "_Atomic": return Atomic(); } - throw new UnsupportedOperationException( + throw new UnsupportedFrontendElementException( "Type qualifier " + ctx.getText() + " not expected!"); } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/declaration/CDeclaration.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/declaration/CDeclaration.java index 7c8ce9b585..7242590d40 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/declaration/CDeclaration.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/declaration/CDeclaration.java @@ -25,6 +25,8 @@ import java.util.ArrayList; import java.util.List; +import static com.google.common.base.Preconditions.checkNotNull; + public class CDeclaration { private CSimpleType type; @@ -37,7 +39,7 @@ public class CDeclaration { private CStatement initExpr; public CDeclaration(CSimpleType cSimpleType) { - this.name = null; + this.name = checkNotNull(cSimpleType).getAssociatedName(); this.type = cSimpleType; this.derefCounter = cSimpleType.getPointerLevel(); this.varDecls = new ArrayList<>(); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CAssignment.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CAssignment.java index 748b919252..8d285f2d04 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CAssignment.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CAssignment.java @@ -21,6 +21,7 @@ import hu.bme.mit.theta.core.type.bvtype.BvExprs; import hu.bme.mit.theta.core.type.bvtype.BvType; import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; import java.util.List; @@ -99,7 +100,7 @@ public Expr getrExpression() { ret = BvExprs.Or(List.of((Expr) type.castTo(lValue), (Expr) type.castTo(rExpression))); break; default: - throw new RuntimeException("Bad operator: " + operator); + throw new UnsupportedFrontendElementException("Unsupported operator: " + operator); } parseContext.getMetadata().create(ret, "cType", CComplexType.getType(lValue, parseContext)); ret = CComplexType.getType(lValue, parseContext).castTo(ret); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CInitializerList.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CInitializerList.java index 4d7670b096..d12151fe4b 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CInitializerList.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CInitializerList.java @@ -19,6 +19,7 @@ import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; import java.util.ArrayList; @@ -38,7 +39,7 @@ public CInitializerList(CComplexType type, ParseContext parseContext) { @Override public Expr getExpression() { - throw new UnsupportedOperationException("Cannot create expression of initializer list."); + throw new UnsupportedFrontendElementException("Cannot create expression of initializer list."); } @Override diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CStatement.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CStatement.java index 44f8087a79..93a2199239 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CStatement.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CStatement.java @@ -18,6 +18,7 @@ import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; /** * Every Program, Function and Statement is a subclass of this base class. @@ -60,7 +61,7 @@ public void setId(String id) { * @return The expression associated with the statement. */ public Expr getExpression() { - throw new RuntimeException("Cannot get expression!"); + throw new UnsupportedFrontendElementException("Cannot get expression!"); } public CStatement getPostStatements() { @@ -68,7 +69,7 @@ public CStatement getPostStatements() { } public void setPostStatements(CStatement postStatements) { - throw new UnsupportedOperationException("Only CCompounds shall currently have pre- and post statements!"); + throw new UnsupportedFrontendElementException("Only CCompounds shall currently have pre- and post statements!"); } public CStatement getPreStatements() { @@ -76,7 +77,7 @@ public CStatement getPreStatements() { } public void setPreStatements(CStatement preStatements) { - throw new UnsupportedOperationException("Only CCompounds shall currently have pre- and post statements!"); + throw new UnsupportedFrontendElementException("Only CCompounds shall currently have pre- and post statements!"); } public abstract R accept(CStatementVisitor visitor, P param); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CComplexType.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CComplexType.java index 32b8c4b0e4..1f54441d28 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CComplexType.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CComplexType.java @@ -26,11 +26,8 @@ import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.core.type.inttype.IntType; import hu.bme.mit.theta.frontend.ParseContext; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CArray; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CCompound; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CFunction; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CPointer; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CStruct; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.*; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInteger; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Fitsall; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.C128; @@ -62,12 +59,7 @@ import java.util.Map.Entry; import java.util.Optional; -import static hu.bme.mit.theta.frontend.transformation.ArchitectureConfig.getCastVisitor; -import static hu.bme.mit.theta.frontend.transformation.ArchitectureConfig.getLimitVisitor; -import static hu.bme.mit.theta.frontend.transformation.ArchitectureConfig.getNullValueVisitor; -import static hu.bme.mit.theta.frontend.transformation.ArchitectureConfig.getTypeVisitor; -import static hu.bme.mit.theta.frontend.transformation.ArchitectureConfig.getUnitValueVisitor; -import static hu.bme.mit.theta.frontend.transformation.ArchitectureConfig.getValueVisitor; +import static hu.bme.mit.theta.frontend.transformation.ArchitectureConfig.*; public abstract class CComplexType { private final CSimpleType origin; @@ -208,7 +200,7 @@ private static CComplexType getType(Type type, ParseContext parseContext) { if (longDoubleType.equals(type)) { return new CFloat(null, parseContext); } - throw new RuntimeException("No suitable size found for type: " + type); + throw new UnsupportedFrontendElementException("No suitable size found for type: " + type); } else if (type instanceof BvType) { for (Entry entry : parseContext.getArchitecture().standardTypeSizes.entrySet()) { String s = entry.getKey(); @@ -230,9 +222,9 @@ private static CComplexType getType(Type type, ParseContext parseContext) { } } } - throw new RuntimeException("No suitable width found for type: " + type); + throw new UnsupportedFrontendElementException("No suitable width found for type: " + type); } else { - throw new RuntimeException("Not yet implemented for type: " + type); + throw new UnsupportedFrontendElementException("Not yet implemented for type: " + type); } } @@ -307,7 +299,7 @@ public R accept(CComplexTypeVisitor visitor, T param) { public static class CComplexTypeVisitor { public R visit(CComplexType type, T param) { - throw new UnsupportedOperationException("Not (yet) implemented (" + type.getClass().getSimpleName() + " in " + this.getClass().getName() + ")"); + throw new UnsupportedFrontendElementException("Not (yet) implemented (" + type.getClass().getSimpleName() + " in " + this.getClass().getName() + ")"); } public R visit(CVoid type, T param) { diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CVoid.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CVoid.java index 4d9d121d1e..353ca2cc9a 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CVoid.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CVoid.java @@ -29,6 +29,11 @@ public R accept(CComplexTypeVisitor visitor, T param) { return visitor.visit(this, param); } + @Override + public CComplexType getSmallestCommonType(CComplexType type) { + return type; + } + @Override public String getTypeName() { return "void"; diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/Fitsall.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/Fitsall.java index 6550e48040..8cb56130a7 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/Fitsall.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/Fitsall.java @@ -41,7 +41,7 @@ public String getTypeName() { @Override public CInteger getSignedVersion() { - throw new RuntimeException("Bool does not have a signed version!"); + throw new RuntimeException("Fitsall does not have a signed version!"); } @Override diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/CastVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/CastVisitor.java index ab2fd3faa7..6187ff3391 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/CastVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/CastVisitor.java @@ -25,6 +25,7 @@ import hu.bme.mit.theta.core.type.fptype.FpRoundingMode; import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CVoid; import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CPointer; @@ -89,7 +90,7 @@ private Expr handleSignedConversion(CInteger type, Expr param } } } else { - throw new IllegalStateException("Compound types are not directly supported!"); + throw new UnsupportedFrontendElementException("Compound types are not directly supported!"); } } @@ -119,7 +120,7 @@ private Expr handleUnsignedConversion(CInteger type, Expr par } } } else { - throw new IllegalStateException("Compound types are not directly supported!"); + throw new UnsupportedFrontendElementException("Compound types are not directly supported!"); } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/NamedType.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/NamedType.java index 2cbc83220d..2ff2c5fdb0 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/NamedType.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/NamedType.java @@ -19,6 +19,7 @@ import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CVoid; import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CPointer; @@ -169,7 +170,7 @@ protected void patch(CSimpleType cSimpleType) { break; default: if (!cSimpleType.isTypedef()) { - throw new RuntimeException( + throw new UnsupportedFrontendElementException( "namedType should be short or long or type specifier, instead it is " + namedType); } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Struct.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Struct.java index eb3d8ad12f..a7b55db04d 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Struct.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Struct.java @@ -20,7 +20,9 @@ import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.transformation.model.declaration.CDeclaration; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CPointer; import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CStruct; import java.util.ArrayList; @@ -28,9 +30,11 @@ import java.util.List; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + public class Struct extends NamedType { - private final Map fields; + private final Map fields; private final String name; private final Logger uniqueWarningLogger; @@ -52,8 +56,8 @@ public static Struct getByName(String name) { currentlyBeingBuilt = false; } - public void addField(String name, CSimpleType type) { - fields.put(name, type); + public void addField(CDeclaration decl) { + fields.put(checkNotNull(decl.getName()), checkNotNull(decl)); } @Override @@ -64,9 +68,16 @@ public CComplexType getActualType() { } currentlyBeingBuilt = true; List> actualFields = new ArrayList<>(); - fields.forEach((s, cSimpleType) -> actualFields.add(Tuple2.of(s, cSimpleType.getActualType()))); + fields.forEach((s, cDeclaration) -> actualFields.add(Tuple2.of(s, cDeclaration.getActualType()))); currentlyBeingBuilt = false; - return new CStruct(this, actualFields, parseContext); + + CComplexType type = new CStruct(this, actualFields, parseContext); + + for (int i = 0; i < getPointerLevel(); i++) { + type = new CPointer(this, type, parseContext); + } + + return type; } @Override diff --git a/subprojects/sts/sts-analysis/src/main/java/hu/bme/mit/theta/sts/analysis/config/StsConfigBuilder.java b/subprojects/sts/sts-analysis/src/main/java/hu/bme/mit/theta/sts/analysis/config/StsConfigBuilder.java index 397a4a4ef8..7e946253e9 100644 --- a/subprojects/sts/sts-analysis/src/main/java/hu/bme/mit/theta/sts/analysis/config/StsConfigBuilder.java +++ b/subprojects/sts/sts-analysis/src/main/java/hu/bme/mit/theta/sts/analysis/config/StsConfigBuilder.java @@ -15,16 +15,18 @@ */ package hu.bme.mit.theta.sts.analysis.config; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; + import hu.bme.mit.theta.analysis.*; +import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators.ArgNodeComparator; -import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.BasicAbstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker; -import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgCegarChecker; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgRefiner; +import hu.bme.mit.theta.analysis.algorithm.cegar.BasicArgAbstractor; import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterions; import hu.bme.mit.theta.analysis.expl.ExplAnalysis; import hu.bme.mit.theta.analysis.expl.ExplPrec; @@ -66,19 +68,23 @@ import hu.bme.mit.theta.sts.analysis.initprec.StsEmptyInitPrec; import hu.bme.mit.theta.sts.analysis.initprec.StsInitPrec; import hu.bme.mit.theta.sts.analysis.initprec.StsPropInitPrec; - import java.util.function.Predicate; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; - public final class StsConfigBuilder { public enum Domain { - EXPL, PRED_BOOL, PRED_CART, PRED_SPLIT + EXPL, + PRED_BOOL, + PRED_CART, + PRED_SPLIT } public enum Refinement { - FW_BIN_ITP, BW_BIN_ITP, SEQ_ITP, MULTI_SEQ, UNSAT_CORE + FW_BIN_ITP, + BW_BIN_ITP, + SEQ_ITP, + MULTI_SEQ, + UNSAT_CORE } public enum Search { @@ -91,7 +97,6 @@ public enum Search { private Search(final ArgNodeComparator comparator) { this.comparator = comparator; } - } public enum PredSplit { @@ -109,14 +114,14 @@ private PredSplit(final ExprSplitter splitter) { } public enum InitPrec { - EMPTY(new StsEmptyInitPrec()), PROP(new StsPropInitPrec()); + EMPTY(new StsEmptyInitPrec()), + PROP(new StsPropInitPrec()); public final StsInitPrec builder; private InitPrec(final StsInitPrec builder) { this.builder = builder; } - } private Logger logger = NullLogger.getInstance(); @@ -128,8 +133,8 @@ private InitPrec(final StsInitPrec builder) { private InitPrec initPrec = InitPrec.EMPTY; private PruneStrategy pruneStrategy = PruneStrategy.LAZY; - public StsConfigBuilder(final Domain domain, final Refinement refinement, - final SolverFactory solverFactory) { + public StsConfigBuilder( + final Domain domain, final Refinement refinement, final SolverFactory solverFactory) { this.domain = domain; this.refinement = refinement; this.solverFactory = solverFactory; @@ -168,63 +173,80 @@ public StsConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { if (domain == Domain.EXPL) { final Solver analysisSolver = solverFactory.createSolver(); final Predicate target = new ExplStatePredicate(negProp, analysisSolver); - final Analysis analysis = ExplAnalysis.create( - analysisSolver, init); - final ArgBuilder argBuilder = ArgBuilder.create(lts, - analysis, target, - true); - final Abstractor abstractor = BasicAbstractor.builder( - argBuilder) - .waitlist(PriorityWaitlist.create(search.comparator)) - .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() - : StopCriterions.firstCex()) - .logger(logger).build(); - - Refiner refiner = null; + final Analysis analysis = + ExplAnalysis.create(analysisSolver, init); + final ArgBuilder argBuilder = + ArgBuilder.create(lts, analysis, target, true); + final ArgAbstractor abstractor = + BasicArgAbstractor.builder(argBuilder) + .waitlist(PriorityWaitlist.create(search.comparator)) + .stopCriterion( + refinement == Refinement.MULTI_SEQ + ? StopCriterions.fullExploration() + : StopCriterions.firstCex()) + .logger(logger) + .build(); + + ArgRefiner refiner = null; switch (refinement) { case FW_BIN_ITP: - refiner = SingleExprTraceRefiner.create( - ExprTraceFwBinItpChecker.create(init, negProp, - solverFactory.createItpSolver()), - JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = + SingleExprTraceRefiner.create( + ExprTraceFwBinItpChecker.create( + init, negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(new ItpRefToExplPrec()), + pruneStrategy, + logger); break; case BW_BIN_ITP: - refiner = SingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(init, negProp, - solverFactory.createItpSolver()), - JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = + SingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + init, negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(new ItpRefToExplPrec()), + pruneStrategy, + logger); break; case SEQ_ITP: - refiner = SingleExprTraceRefiner.create( - ExprTraceSeqItpChecker.create(init, negProp, - solverFactory.createItpSolver()), - JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = + SingleExprTraceRefiner.create( + ExprTraceSeqItpChecker.create( + init, negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(new ItpRefToExplPrec()), + pruneStrategy, + logger); break; case MULTI_SEQ: - refiner = MultiExprTraceRefiner.create( - ExprTraceSeqItpChecker.create(init, negProp, - solverFactory.createItpSolver()), - JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = + MultiExprTraceRefiner.create( + ExprTraceSeqItpChecker.create( + init, negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(new ItpRefToExplPrec()), + pruneStrategy, + logger); break; case UNSAT_CORE: - refiner = SingleExprTraceRefiner.create( - ExprTraceUnsatCoreChecker.create(init, negProp, - solverFactory.createUCSolver()), - JoiningPrecRefiner.create(new VarsRefToExplPrec()), pruneStrategy, logger); + refiner = + SingleExprTraceRefiner.create( + ExprTraceUnsatCoreChecker.create( + init, negProp, solverFactory.createUCSolver()), + JoiningPrecRefiner.create(new VarsRefToExplPrec()), + pruneStrategy, + logger); break; default: throw new UnsupportedOperationException( domain + " domain does not support " + refinement + " refinement."); } - final SafetyChecker, Trace, ExplPrec> checker = CegarChecker.create( - abstractor, refiner, - logger); + final SafetyChecker, Trace, ExplPrec> + checker = ArgCegarChecker.create(abstractor, refiner, logger); final ExplPrec prec = initPrec.builder.createExpl(sts); return StsConfig.create(checker, prec); - } else if (domain == Domain.PRED_BOOL || domain == Domain.PRED_CART + } else if (domain == Domain.PRED_BOOL + || domain == Domain.PRED_CART || domain == Domain.PRED_SPLIT) { final Solver analysisSolver = solverFactory.createSolver(); PredAbstractor predAbstractor = null; @@ -242,55 +264,65 @@ public StsConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { throw new UnsupportedOperationException(domain + " domain is not supported."); } final Predicate target = new ExprStatePredicate(negProp, analysisSolver); - final Analysis analysis = PredAnalysis.create( - analysisSolver, predAbstractor, - init); - final ArgBuilder argBuilder = ArgBuilder.create(lts, - analysis, target, - true); - final Abstractor abstractor = BasicAbstractor.builder( - argBuilder) - .waitlist(PriorityWaitlist.create(search.comparator)) - .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() - : StopCriterions.firstCex()) - .logger(logger).build(); + final Analysis analysis = + PredAnalysis.create(analysisSolver, predAbstractor, init); + final ArgBuilder argBuilder = + ArgBuilder.create(lts, analysis, target, true); + final ArgAbstractor abstractor = + BasicArgAbstractor.builder(argBuilder) + .waitlist(PriorityWaitlist.create(search.comparator)) + .stopCriterion( + refinement == Refinement.MULTI_SEQ + ? StopCriterions.fullExploration() + : StopCriterions.firstCex()) + .logger(logger) + .build(); ExprTraceChecker exprTraceChecker = null; switch (refinement) { case FW_BIN_ITP: - exprTraceChecker = ExprTraceFwBinItpChecker.create(init, negProp, - solverFactory.createItpSolver()); + exprTraceChecker = + ExprTraceFwBinItpChecker.create( + init, negProp, solverFactory.createItpSolver()); break; case BW_BIN_ITP: - exprTraceChecker = ExprTraceBwBinItpChecker.create(init, negProp, - solverFactory.createItpSolver()); + exprTraceChecker = + ExprTraceBwBinItpChecker.create( + init, negProp, solverFactory.createItpSolver()); break; case SEQ_ITP: - exprTraceChecker = ExprTraceSeqItpChecker.create(init, negProp, - solverFactory.createItpSolver()); + exprTraceChecker = + ExprTraceSeqItpChecker.create( + init, negProp, solverFactory.createItpSolver()); break; case MULTI_SEQ: - exprTraceChecker = ExprTraceSeqItpChecker.create(init, negProp, - solverFactory.createItpSolver()); + exprTraceChecker = + ExprTraceSeqItpChecker.create( + init, negProp, solverFactory.createItpSolver()); break; default: throw new UnsupportedOperationException( domain + " domain does not support " + refinement + " refinement."); } - Refiner refiner; + ArgRefiner refiner; if (refinement == Refinement.MULTI_SEQ) { - refiner = MultiExprTraceRefiner.create(exprTraceChecker, - JoiningPrecRefiner.create(new ItpRefToPredPrec(predSplit.splitter)), - pruneStrategy, logger); + refiner = + MultiExprTraceRefiner.create( + exprTraceChecker, + JoiningPrecRefiner.create(new ItpRefToPredPrec(predSplit.splitter)), + pruneStrategy, + logger); } else { - refiner = SingleExprTraceRefiner.create(exprTraceChecker, - JoiningPrecRefiner.create(new ItpRefToPredPrec(predSplit.splitter)), - pruneStrategy, logger); + refiner = + SingleExprTraceRefiner.create( + exprTraceChecker, + JoiningPrecRefiner.create(new ItpRefToPredPrec(predSplit.splitter)), + pruneStrategy, + logger); } - final SafetyChecker, Trace, PredPrec> checker = CegarChecker.create( - abstractor, refiner, - logger); + final SafetyChecker, Trace, PredPrec> + checker = ArgCegarChecker.create(abstractor, refiner, logger); final PredPrec prec = initPrec.builder.createPred(sts); return StsConfig.create(checker, prec); diff --git a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsExplTest.java b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsExplTest.java index db171f2718..f6609fdc97 100644 --- a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsExplTest.java +++ b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsExplTest.java @@ -15,18 +15,25 @@ */ package hu.bme.mit.theta.sts.analysis; +import static hu.bme.mit.theta.analysis.algorithm.arg.ArgUtils.isWellLabeled; +import static hu.bme.mit.theta.core.decl.Decls.Var; +import static hu.bme.mit.theta.core.type.anytype.Exprs.Prime; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.*; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.*; +import static org.junit.Assert.assertTrue; + import hu.bme.mit.theta.analysis.Analysis; import hu.bme.mit.theta.analysis.LTS; import hu.bme.mit.theta.analysis.State; import hu.bme.mit.theta.analysis.Trace; +import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators; -import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.BasicAbstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgCegarChecker; +import hu.bme.mit.theta.analysis.algorithm.cegar.BasicArgAbstractor; import hu.bme.mit.theta.analysis.expl.ExplAnalysis; import hu.bme.mit.theta.analysis.expl.ExplPrec; import hu.bme.mit.theta.analysis.expl.ExplState; @@ -34,12 +41,7 @@ import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.analysis.expr.ExprState; import hu.bme.mit.theta.analysis.expr.ExprStatePredicate; -import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceChecker; -import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceUnsatCoreChecker; -import hu.bme.mit.theta.analysis.expr.refinement.JoiningPrecRefiner; -import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy; -import hu.bme.mit.theta.analysis.expr.refinement.SingleExprTraceRefiner; -import hu.bme.mit.theta.analysis.expr.refinement.VarsRefutation; +import hu.bme.mit.theta.analysis.expr.refinement.*; import hu.bme.mit.theta.analysis.waitlist.PriorityWaitlist; import hu.bme.mit.theta.common.logging.ConsoleLogger; import hu.bme.mit.theta.common.logging.Logger; @@ -52,23 +54,9 @@ import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory; import hu.bme.mit.theta.sts.STS; import hu.bme.mit.theta.sts.STS.Builder; -import org.junit.Test; - import java.util.Collections; import java.util.function.Predicate; - -import static hu.bme.mit.theta.analysis.algorithm.arg.ArgUtils.isWellLabeled; -import static hu.bme.mit.theta.core.decl.Decls.Var; -import static hu.bme.mit.theta.core.type.anytype.Exprs.Prime; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Imply; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Add; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Eq; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Geq; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Lt; -import static org.junit.Assert.assertTrue; +import org.junit.Test; public class StsExplTest { @@ -88,8 +76,10 @@ public void test() { builder.addInit(Eq(x, Int(0))); builder.addInit(Eq(y, Int(0))); - builder.addTrans(And(Imply(Lt(x, Int(mod)), Eq(Prime(x), Add(x, Int(1)))), - Imply(Geq(x, Int(mod)), Eq(Prime(x), Int(0))))); + builder.addTrans( + And( + Imply(Lt(x, Int(mod)), Eq(Prime(x), Add(x, Int(1)))), + Imply(Geq(x, Int(mod)), Eq(Prime(x), Int(0))))); builder.addTrans(Eq(Prime(y), Int(0))); builder.setProp(Not(Eq(x, Int(mod)))); @@ -98,40 +88,45 @@ public void test() { final Solver abstractionSolver = Z3LegacySolverFactory.getInstance().createSolver(); final UCSolver refinementSolver = Z3LegacySolverFactory.getInstance().createUCSolver(); - final Analysis analysis = ExplAnalysis.create( - abstractionSolver, sts.getInit()); - final Predicate target = new ExprStatePredicate(Not(sts.getProp()), - abstractionSolver); + final Analysis analysis = + ExplAnalysis.create(abstractionSolver, sts.getInit()); + final Predicate target = + new ExprStatePredicate(Not(sts.getProp()), abstractionSolver); final ExplPrec prec = ExplPrec.of(Collections.singleton(vy)); final LTS lts = StsLts.create(sts); - final ArgBuilder argBuilder = ArgBuilder.create(lts, - analysis, target); + final ArgBuilder argBuilder = + ArgBuilder.create(lts, analysis, target); - final Abstractor abstractor = BasicAbstractor.builder( - argBuilder) - .waitlist(PriorityWaitlist.create(ArgNodeComparators.bfs())).logger(logger).build(); + final ArgAbstractor abstractor = + BasicArgAbstractor.builder(argBuilder) + .waitlist(PriorityWaitlist.create(ArgNodeComparators.bfs())) + .logger(logger) + .build(); - final ExprTraceChecker exprTraceChecker = ExprTraceUnsatCoreChecker.create( - sts.getInit(), - Not(sts.getProp()), refinementSolver); + final ExprTraceChecker exprTraceChecker = + ExprTraceUnsatCoreChecker.create( + sts.getInit(), Not(sts.getProp()), refinementSolver); - final SingleExprTraceRefiner refiner = SingleExprTraceRefiner - .create(exprTraceChecker, JoiningPrecRefiner.create(new VarsRefToExplPrec()), - PruneStrategy.LAZY, logger); + final SingleExprTraceRefiner refiner = + SingleExprTraceRefiner.create( + exprTraceChecker, + JoiningPrecRefiner.create(new VarsRefToExplPrec()), + PruneStrategy.LAZY, + logger); - final SafetyChecker, Trace, ExplPrec> checker = CegarChecker.create( - abstractor, refiner, logger); + final SafetyChecker, Trace, ExplPrec> + checker = ArgCegarChecker.create(abstractor, refiner, logger); - final SafetyResult, Trace> safetyStatus = checker.check(prec); + final SafetyResult, Trace> safetyStatus = + checker.check(prec); - final ARG arg = safetyStatus.getWitness(); + final ARG arg = safetyStatus.getProof(); assertTrue(isWellLabeled(arg, abstractionSolver)); // System.out.println(new // GraphvizWriter().writeString(ArgVisualizer.visualize(arg))); } - } diff --git a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsMddCheckerTest.java b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsMddCheckerTest.java index e9f88faaad..929a01e400 100644 --- a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsMddCheckerTest.java +++ b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsMddCheckerTest.java @@ -15,11 +15,13 @@ */ package hu.bme.mit.theta.sts.analysis; +import static org.junit.Assert.assertTrue; + import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.algorithm.mdd.MddCex; import hu.bme.mit.theta.analysis.algorithm.mdd.MddChecker; import hu.bme.mit.theta.analysis.algorithm.mdd.MddChecker.IterationStrategy; -import hu.bme.mit.theta.analysis.algorithm.mdd.MddWitness; +import hu.bme.mit.theta.analysis.algorithm.mdd.MddProof; import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.common.Utils; import hu.bme.mit.theta.common.logging.ConsoleLogger; @@ -35,16 +37,12 @@ import hu.bme.mit.theta.sts.aiger.AigerToSts; import hu.bme.mit.theta.sts.dsl.StsDslManager; import hu.bme.mit.theta.sts.dsl.StsSpec; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.io.FileInputStream; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; - -import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(value = Parameterized.class) public class StsMddCheckerTest { @@ -56,35 +54,23 @@ public class StsMddCheckerTest { @Parameterized.Parameters(name = "{index}: {0}, {1}") public static Collection data() { - return Arrays.asList(new Object[][]{ - {"src/test/resources/hw1_false.aag", false}, - - {"src/test/resources/hw2_true.aag", true}, - - {"src/test/resources/boolean1.system", false}, - - {"src/test/resources/boolean2.system", false}, - - {"src/test/resources/counter.system", true}, - - {"src/test/resources/counter_bad.system", false}, - - {"src/test/resources/counter_parametric.system", true}, - - {"src/test/resources/loop.system", true}, - - {"src/test/resources/loop_bad.system", false}, - - {"src/test/resources/multipleinitial.system", false}, - - {"src/test/resources/readerswriters.system", true}, - - {"src/test/resources/simple1.system", false}, - - {"src/test/resources/simple2.system", true}, - - {"src/test/resources/simple3.system", false}, - }); + return Arrays.asList( + new Object[][] { + {"src/test/resources/hw1_false.aag", false}, + {"src/test/resources/hw2_true.aag", true}, + {"src/test/resources/boolean1.system", false}, + {"src/test/resources/boolean2.system", false}, + {"src/test/resources/counter.system", true}, + {"src/test/resources/counter_bad.system", false}, + {"src/test/resources/counter_parametric.system", true}, + {"src/test/resources/loop.system", true}, + {"src/test/resources/loop_bad.system", false}, + {"src/test/resources/multipleinitial.system", false}, + {"src/test/resources/readerswriters.system", true}, + {"src/test/resources/simple1.system", false}, + {"src/test/resources/simple2.system", true}, + {"src/test/resources/simple3.system", false}, + }); } @Test @@ -100,19 +86,27 @@ public void test() throws Exception { sts = Utils.singleElementOf(spec.getAllSts()); } - final SafetyResult status; + final SafetyResult status; try (var solverPool = new SolverPool(Z3LegacySolverFactory.getInstance())) { - final MddChecker checker = MddChecker.create(sts.getInit(), VarIndexingFactory.indexing(0), new ExprAction() { - @Override - public Expr toExpr() { - return sts.getTrans(); - } - - @Override - public VarIndexing nextIndexing() { - return VarIndexingFactory.indexing(1); - } - }, sts.getProp(), solverPool, logger, IterationStrategy.GSAT); + final MddChecker checker = + MddChecker.create( + sts.getInit(), + VarIndexingFactory.indexing(0), + new ExprAction() { + @Override + public Expr toExpr() { + return sts.getTrans(); + } + + @Override + public VarIndexing nextIndexing() { + return VarIndexingFactory.indexing(1); + } + }, + sts.getProp(), + solverPool, + logger, + IterationStrategy.GSAT); status = checker.check(null); } @@ -122,5 +116,4 @@ public VarIndexing nextIndexing() { assertTrue(status.isUnsafe()); } } - } diff --git a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsPredTest.java b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsPredTest.java index 5eea722540..5022d36401 100644 --- a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsPredTest.java +++ b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsPredTest.java @@ -18,53 +18,40 @@ import static hu.bme.mit.theta.analysis.algorithm.arg.ArgUtils.isWellLabeled; import static hu.bme.mit.theta.core.decl.Decls.Var; import static hu.bme.mit.theta.core.type.anytype.Exprs.Prime; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Imply; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Add; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Eq; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Geq; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Lt; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.*; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.*; import static org.junit.Assert.assertTrue; -import java.util.function.Predicate; - -import hu.bme.mit.theta.analysis.Trace; -import hu.bme.mit.theta.analysis.algorithm.SafetyResult; -import hu.bme.mit.theta.analysis.expr.refinement.*; -import hu.bme.mit.theta.solver.ItpSolver; -import hu.bme.mit.theta.solver.Solver; -import org.junit.Before; -import org.junit.Test; - import hu.bme.mit.theta.analysis.Analysis; import hu.bme.mit.theta.analysis.LTS; import hu.bme.mit.theta.analysis.State; +import hu.bme.mit.theta.analysis.Trace; +import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; +import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder; -import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.BasicAbstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgCegarChecker; +import hu.bme.mit.theta.analysis.algorithm.cegar.BasicArgAbstractor; import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.analysis.expr.ExprState; import hu.bme.mit.theta.analysis.expr.ExprStatePredicate; -import hu.bme.mit.theta.analysis.pred.ExprSplitters; -import hu.bme.mit.theta.analysis.pred.ItpRefToPredPrec; -import hu.bme.mit.theta.analysis.pred.PredAbstractors; -import hu.bme.mit.theta.analysis.pred.PredAnalysis; -import hu.bme.mit.theta.analysis.pred.PredPrec; -import hu.bme.mit.theta.analysis.pred.PredState; +import hu.bme.mit.theta.analysis.expr.refinement.*; +import hu.bme.mit.theta.analysis.pred.*; import hu.bme.mit.theta.common.logging.ConsoleLogger; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.core.decl.VarDecl; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.inttype.IntType; +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory; import hu.bme.mit.theta.sts.STS; import hu.bme.mit.theta.sts.STS.Builder; +import java.util.function.Predicate; +import org.junit.Before; +import org.junit.Test; public class StsPredTest { @@ -81,8 +68,10 @@ public void setUp() { final Builder builder = STS.builder(); builder.addInit(Eq(x, Int(0))); - builder.addTrans(And(Imply(Lt(x, Int(mod)), Eq(Prime(x), Add(x, Int(1)))), - Imply(Geq(x, Int(mod)), Eq(Prime(x), Int(0))))); + builder.addTrans( + And( + Imply(Lt(x, Int(mod)), Eq(Prime(x), Add(x, Int(1)))), + Imply(Geq(x, Int(mod)), Eq(Prime(x), Int(0))))); builder.setProp(Not(Eq(x, Int(mod)))); sts = builder.build(); @@ -91,39 +80,43 @@ public void setUp() { @Test public void testPredPrec() { - final Analysis analysis = PredAnalysis.create( - abstractionSolver, - PredAbstractors.booleanSplitAbstractor(abstractionSolver), sts.getInit()); - final Predicate target = new ExprStatePredicate(Not(sts.getProp()), - abstractionSolver); + final Analysis analysis = + PredAnalysis.create( + abstractionSolver, + PredAbstractors.booleanSplitAbstractor(abstractionSolver), + sts.getInit()); + final Predicate target = + new ExprStatePredicate(Not(sts.getProp()), abstractionSolver); final PredPrec prec = PredPrec.of(); final LTS lts = StsLts.create(sts); - final ArgBuilder argBuilder = ArgBuilder.create(lts, - analysis, target); + final ArgBuilder argBuilder = + ArgBuilder.create(lts, analysis, target); - final Abstractor abstractor = BasicAbstractor.builder( - argBuilder).logger(logger) - .build(); + final ArgAbstractor abstractor = + BasicArgAbstractor.builder(argBuilder).logger(logger).build(); - final ExprTraceChecker exprTraceChecker = ExprTraceFwBinItpChecker.create( - sts.getInit(), - Not(sts.getProp()), refinementSolver); + final ExprTraceChecker exprTraceChecker = + ExprTraceFwBinItpChecker.create( + sts.getInit(), Not(sts.getProp()), refinementSolver); - final SingleExprTraceRefiner refiner = SingleExprTraceRefiner - .create(exprTraceChecker, + final SingleExprTraceRefiner refiner = + SingleExprTraceRefiner.create( + exprTraceChecker, JoiningPrecRefiner.create(new ItpRefToPredPrec(ExprSplitters.atoms())), - PruneStrategy.LAZY, logger); + PruneStrategy.LAZY, + logger); - final SafetyChecker, Trace, PredPrec> checker = CegarChecker.create( - abstractor, refiner, logger); + final SafetyChecker, Trace, PredPrec> + checker = ArgCegarChecker.create(abstractor, refiner, logger); - final SafetyResult, Trace> safetyStatus = checker.check(prec); + final SafetyResult, Trace> safetyStatus = + checker.check(prec); System.out.println(safetyStatus); - final ARG arg = safetyStatus.getWitness(); + final ARG arg = safetyStatus.getProof(); assertTrue(isWellLabeled(arg, abstractionSolver)); // System.out.println(new diff --git a/subprojects/sts/sts-cli/src/main/java/hu/bme/mit/theta/sts/cli/StsCli.java b/subprojects/sts/sts-cli/src/main/java/hu/bme/mit/theta/sts/cli/StsCli.java index ddb48470eb..884bfabdae 100644 --- a/subprojects/sts/sts-cli/src/main/java/hu/bme/mit/theta/sts/cli/StsCli.java +++ b/subprojects/sts/sts-cli/src/main/java/hu/bme/mit/theta/sts/cli/StsCli.java @@ -52,26 +52,14 @@ import hu.bme.mit.theta.sts.analysis.StsTraceConcretizer; import hu.bme.mit.theta.sts.analysis.config.StsConfig; import hu.bme.mit.theta.sts.analysis.config.StsConfigBuilder; -import hu.bme.mit.theta.sts.analysis.config.StsConfigBuilder.Domain; -import hu.bme.mit.theta.sts.analysis.config.StsConfigBuilder.InitPrec; -import hu.bme.mit.theta.sts.analysis.config.StsConfigBuilder.PredSplit; -import hu.bme.mit.theta.sts.analysis.config.StsConfigBuilder.Refinement; -import hu.bme.mit.theta.sts.analysis.config.StsConfigBuilder.Search; +import hu.bme.mit.theta.sts.analysis.config.StsConfigBuilder.*; import hu.bme.mit.theta.sts.dsl.StsDslManager; import hu.bme.mit.theta.sts.dsl.StsSpec; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.io.StringWriter; +import java.io.*; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -/** - * A command line interface for running a CEGAR configuration on an STS. - */ +/** A command line interface for running a CEGAR configuration on an STS. */ public class StsCli { private static final String JAR_NAME = "theta-sts-cli.jar"; @@ -85,41 +73,64 @@ enum Algorithm { IMC } - @Parameter(names = {"--domain"}, description = "Abstract domain") + @Parameter( + names = {"--domain"}, + description = "Abstract domain") Domain domain = Domain.PRED_CART; - @Parameter(names = {"--algorithm"}, description = "Algorithm") + @Parameter( + names = {"--algorithm"}, + description = "Algorithm") Algorithm algorithm = Algorithm.CEGAR; - @Parameter(names = {"--refinement"}, description = "Refinement strategy") + @Parameter( + names = {"--refinement"}, + description = "Refinement strategy") Refinement refinement = Refinement.SEQ_ITP; - @Parameter(names = {"--search"}, description = "Search strategy") + @Parameter( + names = {"--search"}, + description = "Search strategy") Search search = Search.BFS; - @Parameter(names = {"--predsplit"}, description = "Predicate splitting") + @Parameter( + names = {"--predsplit"}, + description = "Predicate splitting") PredSplit predSplit = PredSplit.WHOLE; - @Parameter(names = {"--model"}, description = "Path of the input STS model", required = true) + @Parameter( + names = {"--model"}, + description = "Path of the input STS model", + required = true) String model; - @Parameter(names = {"--initprec"}, description = "Initial precision") + @Parameter( + names = {"--initprec"}, + description = "Initial precision") InitPrec initPrec = InitPrec.EMPTY; - @Parameter(names = "--prunestrategy", description = "Strategy for pruning the ARG after refinement") + @Parameter( + names = "--prunestrategy", + description = "Strategy for pruning the ARG after refinement") PruneStrategy pruneStrategy = PruneStrategy.LAZY; - @Parameter(names = {"--loglevel"}, description = "Detailedness of logging") + @Parameter( + names = {"--loglevel"}, + description = "Detailedness of logging") Logger.Level logLevel = Level.SUBSTEP; - @Parameter(names = {"--benchmark"}, description = "Benchmark mode (only print metrics)") + @Parameter( + names = {"--benchmark"}, + description = "Benchmark mode (only print metrics)") Boolean benchmarkMode = false; @Parameter(names = "--cex", description = "Write concrete counterexample to a file") String cexfile = null; - @Parameter(names = { - "--header"}, description = "Print only a header (for benchmarks)", help = true) + @Parameter( + names = {"--header"}, + description = "Print only a header (for benchmarks)", + help = true) boolean headerOnly = false; @Parameter(names = "--stacktrace", description = "Print full stack trace in case of exception") @@ -169,11 +180,15 @@ private void run() { if (algorithm.equals(Algorithm.CEGAR)) { final StsConfig configuration = buildConfiguration(sts); status = check(configuration); - } else if (algorithm == Algorithm.BMC || algorithm == Algorithm.KINDUCTION || algorithm == Algorithm.IMC) { - final BoundedChecker checker = buildBoundedChecker(sts, Z3LegacySolverFactory.getInstance()); + } else if (algorithm == Algorithm.BMC + || algorithm == Algorithm.KINDUCTION + || algorithm == Algorithm.IMC) { + final BoundedChecker checker = + buildBoundedChecker(sts, Z3LegacySolverFactory.getInstance()); status = checker.check(null); } else { - throw new UnsupportedOperationException("Algorithm " + algorithm + " not supported"); + throw new UnsupportedOperationException( + "Algorithm " + algorithm + " not supported"); } sw.stop(); printResult(status, sts, sw.elapsed(TimeUnit.MILLISECONDS)); @@ -186,20 +201,35 @@ private void run() { } } - private SafetyResult, ? extends Trace> check(StsConfig configuration) throws Exception { + private SafetyResult, ? extends Trace> check( + StsConfig configuration) throws Exception { try { return configuration.check(); } catch (final Exception ex) { String message = ex.getMessage() == null ? "(no message)" : ex.getMessage(); throw new Exception( - "Error while running algorithm: " + ex.getClass().getSimpleName() + " " + message, + "Error while running algorithm: " + + ex.getClass().getSimpleName() + + " " + + message, ex); } } private void printHeader() { - Stream.of("Result", "TimeMs", "AlgoTimeMs", "AbsTimeMs", "RefTimeMs", "Iterations", - "ArgSize", "ArgDepth", "ArgMeanBranchFactor", "CexLen", "Vars", "Size") + Stream.of( + "Result", + "TimeMs", + "AlgoTimeMs", + "AbsTimeMs", + "RefTimeMs", + "Iterations", + "ArgSize", + "ArgDepth", + "ArgMeanBranchFactor", + "CexLen", + "Vars", + "Size") .forEach(writer::cell); writer.newRow(); } @@ -228,49 +258,64 @@ private STS loadModel() throws Exception { private StsConfig buildConfiguration(final STS sts) throws Exception { try { return new StsConfigBuilder(domain, refinement, Z3LegacySolverFactory.getInstance()) - .initPrec(initPrec).search(search) - .predSplit(predSplit).pruneStrategy(pruneStrategy).logger(logger).build(sts); + .initPrec(initPrec) + .search(search) + .predSplit(predSplit) + .pruneStrategy(pruneStrategy) + .logger(logger) + .build(sts); } catch (final Exception ex) { throw new Exception("Could not create configuration: " + ex.getMessage(), ex); } } - private BoundedChecker buildBoundedChecker(final STS sts, final SolverFactory abstractionSolverFactory) { + private BoundedChecker buildBoundedChecker( + final STS sts, final SolverFactory abstractionSolverFactory) { final MonolithicExpr monolithicExpr = StsToMonolithicExprKt.toMonolithicExpr(sts); final BoundedChecker checker; switch (algorithm) { - case BMC -> checker = BoundedCheckerBuilderKt.buildBMC( - monolithicExpr, - abstractionSolverFactory.createSolver(), - val -> StsToMonolithicExprKt.valToState(sts, val), - (val1, val2) -> StsToMonolithicExprKt.valToAction(sts, val1, val2), - logger - ); - case KINDUCTION -> checker = BoundedCheckerBuilderKt.buildKIND( - monolithicExpr, - abstractionSolverFactory.createSolver(), - abstractionSolverFactory.createSolver(), - val -> StsToMonolithicExprKt.valToState(sts, val), - (val1, val2) -> StsToMonolithicExprKt.valToAction(sts, val1, val2), - logger - ); - case IMC -> checker = BoundedCheckerBuilderKt.buildIMC( - monolithicExpr, - abstractionSolverFactory.createSolver(), - abstractionSolverFactory.createItpSolver(), - val -> StsToMonolithicExprKt.valToState(sts, val), - (val1, val2) -> StsToMonolithicExprKt.valToAction(sts, val1, val2), - logger - ); + case BMC -> + checker = + BoundedCheckerBuilderKt.buildBMC( + monolithicExpr, + abstractionSolverFactory.createSolver(), + val -> StsToMonolithicExprKt.valToState(sts, val), + (val1, val2) -> + StsToMonolithicExprKt.valToAction(sts, val1, val2), + logger); + case KINDUCTION -> + checker = + BoundedCheckerBuilderKt.buildKIND( + monolithicExpr, + abstractionSolverFactory.createSolver(), + abstractionSolverFactory.createSolver(), + val -> StsToMonolithicExprKt.valToState(sts, val), + (val1, val2) -> + StsToMonolithicExprKt.valToAction(sts, val1, val2), + logger); + case IMC -> + checker = + BoundedCheckerBuilderKt.buildIMC( + monolithicExpr, + abstractionSolverFactory.createSolver(), + abstractionSolverFactory.createItpSolver(), + val -> StsToMonolithicExprKt.valToState(sts, val), + (val1, val2) -> + StsToMonolithicExprKt.valToAction(sts, val1, val2), + logger); default -> - throw new UnsupportedOperationException("Algorithm " + algorithm + " not supported"); + throw new UnsupportedOperationException( + "Algorithm " + algorithm + " not supported"); } return checker; } - private void printResult(final SafetyResult> status, final STS sts, - final long totalTimeMs) { - final CegarStatistics stats = (CegarStatistics) status.getStats().orElse(new CegarStatistics(0, 0, 0, 0)); + private void printResult( + final SafetyResult> status, + final STS sts, + final long totalTimeMs) { + final CegarStatistics stats = + (CegarStatistics) status.getStats().orElse(new CegarStatistics(0, 0, 0, 0)); if (benchmarkMode) { writer.cell(status.isSafe()); writer.cell(totalTimeMs); @@ -278,7 +323,7 @@ private void printResult(final SafetyResult> status, fi writer.cell(stats.getAbstractorTimeMs()); writer.cell(stats.getRefinerTimeMs()); writer.cell(stats.getIterations()); - if (status.getWitness() instanceof ARG arg) { + if (status.getProof() instanceof ARG arg) { writer.cell(arg.size()); writer.cell(arg.getDepth()); writer.cell(arg.getMeanBranchingFactor()); @@ -304,7 +349,10 @@ private void printError(final Throwable ex) { writer.cell("[EX] " + ex.getClass().getSimpleName() + ": " + message); writer.newRow(); } else { - logger.write(Level.RESULT, "%s occurred, message: %s%n", ex.getClass().getSimpleName(), + logger.write( + Level.RESULT, + "%s occurred, message: %s%n", + ex.getClass().getSimpleName(), message); if (stacktrace) { final StringWriter errors = new StringWriter(); @@ -318,9 +366,10 @@ private void printError(final Throwable ex) { private void writeCex(final STS sts, final SafetyResult.Unsafe> status) throws FileNotFoundException { - @SuppressWarnings("unchecked") final Trace trace = (Trace) status.getCex(); - final Trace concrTrace = StsTraceConcretizer.concretize(sts, trace, - Z3LegacySolverFactory.getInstance()); + @SuppressWarnings("unchecked") + final Trace trace = (Trace) status.getCex(); + final Trace concrTrace = + StsTraceConcretizer.concretize(sts, trace, Z3LegacySolverFactory.getInstance()); final File file = new File(cexfile); PrintWriter printWriter = null; try { diff --git a/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/FrontendXcfaBuilder.kt b/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/FrontendXcfaBuilder.kt index 6dccaaa36c..d82a92fa4a 100644 --- a/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/FrontendXcfaBuilder.kt +++ b/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/FrontendXcfaBuilder.kt @@ -94,7 +94,9 @@ class FrontendXcfaBuilder(val parseContext: ParseContext, val checkOverflow: Boo continue } if (type is CStruct) { - error("Not handling init expression of struct array ${globalDeclaration.get1()}") + uniqueWarningLogger.write( + Logger.Level.INFO, "Not handling init expression of struct array ${globalDeclaration.get1()}" + ) } builder.addVar(XcfaGlobalVar(globalDeclaration.get2(), type.nullValue)) if (type is CArray) { @@ -190,7 +192,7 @@ class FrontendXcfaBuilder(val parseContext: ParseContext, val checkOverflow: Boo for (flatVariable in flatVariables) { builder.addVar(flatVariable) val type = CComplexType.getType(flatVariable.ref, parseContext) - if (type is CArray && builder.getParams().none { it.first == flatVariable }) { + if ((type is CArray || type is CStruct) && builder.getParams().none { it.first == flatVariable }) { initStmtList.add(StmtLabel( Stmts.Assign(cast(flatVariable, flatVariable.type), cast(type.getValue("$ptrCnt"), flatVariable.type)) diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAbstractor.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAbstractor.kt deleted file mode 100644 index d17f15fda1..0000000000 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAbstractor.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2024 Budapest University of Technology and Economics - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package hu.bme.mit.theta.xcfa.analysis - -import com.google.common.base.Preconditions -import hu.bme.mit.theta.analysis.Action -import hu.bme.mit.theta.analysis.Prec -import hu.bme.mit.theta.analysis.State -import hu.bme.mit.theta.analysis.algorithm.arg.ARG -import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder -import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode -import hu.bme.mit.theta.analysis.algorithm.cegar.AbstractorResult -import hu.bme.mit.theta.analysis.algorithm.cegar.BasicAbstractor -import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterion -import hu.bme.mit.theta.analysis.reachedset.Partition -import hu.bme.mit.theta.analysis.waitlist.Waitlist -import hu.bme.mit.theta.common.logging.Logger -import java.util.function.Function - -class XcfaAbstractor( - argBuilder: ArgBuilder, - projection: Function?, - waitlist: Waitlist>, - stopCriterion: StopCriterion, - logger: Logger, -) : BasicAbstractor(argBuilder, projection, waitlist, stopCriterion, logger) { - - override fun check(arg: ARG, prec: P): AbstractorResult { - logger.write(Logger.Level.DETAIL, "| | Precision: %s%n", prec) - - if (!arg.isInitialized) { - logger.write(Logger.Level.SUBSTEP, "| | (Re)initializing ARG...") - argBuilder.init(arg, prec) - logger.write(Logger.Level.SUBSTEP, "done%n") - } - - assert(arg.isInitialized) - - logger.write( - Logger.Level.INFO, "| | Starting ARG: %d nodes, %d incomplete, %d unsafe%n", arg.nodes.count(), - arg.incompleteNodes.count(), arg.unsafeNodes.count() - ) - logger.write(Logger.Level.SUBSTEP, "| | Building ARG...") - - val reachedSet: Partition, *> = Partition.of { n: ArgNode -> - projection.apply(n.state) - } - waitlist.clear() - - reachedSet.addAll(arg.nodes) - waitlist.addAll(arg.incompleteNodes) - - if (!stopCriterion.canStop(arg)) { - while (!waitlist.isEmpty) { - val node = waitlist.remove() - var newNodes: Collection>? = emptyList() - if ((node.state as XcfaState<*>).xcfa!!.isInlined) { - close(node, reachedSet[node]) - } else { - val expandProcedureCall = (node.state as XcfaState<*>) in (prec as XcfaPrec

).noPop - closePop(node, reachedSet[node], !expandProcedureCall) - } - if (!node.isSubsumed && !node.isTarget) { - newNodes = argBuilder.expand(node, prec) - reachedSet.addAll(newNodes) - waitlist.addAll(newNodes) - } - if (stopCriterion.canStop(arg, newNodes)) break - } - } - - logger.write(Logger.Level.SUBSTEP, "done%n") - logger.write( - Logger.Level.INFO, "| | Finished ARG: %d nodes, %d incomplete, %d unsafe%n", arg.nodes.count(), - arg.incompleteNodes.count(), arg.unsafeNodes.count() - ) - - waitlist.clear() // Optimization - - return if (arg.isSafe) { - Preconditions.checkState(arg.isComplete, "Returning incomplete ARG as safe") - AbstractorResult.safe() - } else { - AbstractorResult.unsafe() - } - } - - fun closePop(node: ArgNode, candidates: Collection>, popCovered: Boolean) { - if (!node.isLeaf) { - return - } - for (candidate in candidates) { - if (candidate.mayCover(node)) { - var onlyStackCovers = false - (node.state as XcfaState<*>).processes.forEach { (pid: Int, proc: XcfaProcessState) -> - if (proc != (candidate.state as XcfaState<*>).processes[pid]) { - if (popCovered) proc.popped = proc.locs.pop() - onlyStackCovers = true - } - } - if (!onlyStackCovers) { - node.cover(candidate) - } - return - } - } - } - - companion object { - - fun builder( - argBuilder: ArgBuilder): BasicAbstractor.Builder { - return Builder(argBuilder) - } - } - - class Builder(argBuilder: ArgBuilder) - : BasicAbstractor.Builder(argBuilder) { - - override fun build(): BasicAbstractor { - return XcfaAbstractor(argBuilder, projection, waitlist, stopCriterion, logger) - } - } -} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAnalysis.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAnalysis.kt index 64bd2e3d62..f923d0e6f0 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAnalysis.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAnalysis.kt @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis import hu.bme.mit.theta.analysis.* import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterion import hu.bme.mit.theta.analysis.expl.ExplInitFunc import hu.bme.mit.theta.analysis.expl.ExplPrec @@ -54,251 +53,364 @@ import java.util.* import java.util.function.Predicate open class XcfaAnalysis( - private val corePartialOrd: PartialOrd>>, - private val coreInitFunc: InitFunc>, XcfaPrec

>, - private var coreTransFunc: TransFunc>, XcfaAction, XcfaPrec

>, + private val corePartialOrd: PartialOrd>>, + private val coreInitFunc: InitFunc>, XcfaPrec

>, + private var coreTransFunc: TransFunc>, XcfaAction, XcfaPrec

>, ) : Analysis>, XcfaAction, XcfaPrec

> { - init { - ConeOfInfluence.coreTransFunc = transFunc as TransFunc>, XcfaAction, XcfaPrec> - coreTransFunc = ConeOfInfluence.transFunc as TransFunc>, XcfaAction, XcfaPrec

> - } + init { + ConeOfInfluence.coreTransFunc = + transFunc as TransFunc>, XcfaAction, XcfaPrec> + coreTransFunc = + ConeOfInfluence.transFunc as TransFunc>, XcfaAction, XcfaPrec

> + } + + override fun getPartialOrd(): PartialOrd>> = corePartialOrd + + override fun getInitFunc(): InitFunc>, XcfaPrec

> = coreInitFunc - override fun getPartialOrd(): PartialOrd>> = corePartialOrd - override fun getInitFunc(): InitFunc>, XcfaPrec

> = coreInitFunc - override fun getTransFunc(): TransFunc>, XcfaAction, XcfaPrec

> = coreTransFunc + override fun getTransFunc(): TransFunc>, XcfaAction, XcfaPrec

> = + coreTransFunc } /// Common private var tempCnt: Int = 0 -fun getCoreXcfaLts() = LTS>, XcfaAction> { s -> - s.processes.map { proc -> + +fun getCoreXcfaLts() = + LTS>, XcfaAction> { s -> + s.processes + .map { proc -> if (proc.value.locs.peek().final) { - listOf(XcfaAction(proc.key, - XcfaEdge(proc.value.locs.peek(), proc.value.locs.peek(), SequenceLabel(listOf( + listOf( + XcfaAction( + proc.key, + XcfaEdge( + proc.value.locs.peek(), + proc.value.locs.peek(), + SequenceLabel( + listOf( proc.value.paramStmts.peek().second, ReturnLabel(proc.value.returnStmts.peek()), - ))), nextCnt = s.sGlobal.nextCnt)) + ) + ), + ), + nextCnt = s.sGlobal.nextCnt, + ) + ) } else if (!proc.value.paramsInitialized) { - listOf(XcfaAction(proc.key, XcfaEdge(proc.value.locs.peek(), proc.value.locs.peek(), - proc.value.paramStmts.peek().first), nextCnt = s.sGlobal.nextCnt)) + listOf( + XcfaAction( + proc.key, + XcfaEdge( + proc.value.locs.peek(), + proc.value.locs.peek(), + proc.value.paramStmts.peek().first, + ), + nextCnt = s.sGlobal.nextCnt, + ) + ) } else { - proc.value.locs.peek().outgoingEdges.map { edge -> - val newLabel = edge.label.changeVars(proc.value.varLookup.peek()) - val flatLabels = newLabel.getFlatLabels() - if (flatLabels.any { it is InvokeLabel || it is StartLabel }) { - val newNewLabel = SequenceLabel(flatLabels.map { label -> - if (label is InvokeLabel) { - val procedure = s.xcfa?.procedures?.find { proc -> proc.name == label.name } - ?: error("No such method ${label.name}.") - val lookup: MutableMap, VarDecl<*>> = LinkedHashMap() - SequenceLabel(listOf(procedure.params.withIndex() - .filter { it.value.second != ParamDirection.OUT }.map { iVal -> - val originalVar = iVal.value.first - val tempVar = Var("tmp${tempCnt++}_" + originalVar.name, - originalVar.type) - lookup[originalVar] = tempVar + proc.value.locs.peek().outgoingEdges.map { edge -> + val newLabel = edge.label.changeVars(proc.value.varLookup.peek()) + val flatLabels = newLabel.getFlatLabels() + if (flatLabels.any { it is InvokeLabel || it is StartLabel }) { + val newNewLabel = + SequenceLabel( + flatLabels.map { label -> + if (label is InvokeLabel) { + val procedure = + s.xcfa?.procedures?.find { proc -> proc.name == label.name } + ?: error("No such method ${label.name}.") + val lookup: MutableMap, VarDecl<*>> = LinkedHashMap() + SequenceLabel( + listOf( + procedure.params + .withIndex() + .filter { it.value.second != ParamDirection.OUT } + .map { iVal -> + val originalVar = iVal.value.first + val tempVar = + Var("tmp${tempCnt++}_" + originalVar.name, originalVar.type) + lookup[originalVar] = tempVar + StmtLabel( + Stmts.Assign( + TypeUtils.cast(tempVar, tempVar.type), + TypeUtils.cast(label.params[iVal.index], tempVar.type), + ), + metadata = label.metadata, + ) + }, + listOf(label.copy(tempLookup = lookup)), + ) + .flatten() + ) + } else if (label is StartLabel) { + val procedure = + s.xcfa?.procedures?.find { proc -> proc.name == label.name } + ?: error("No such method ${label.name}.") + val lookup: MutableMap, VarDecl<*>> = LinkedHashMap() + SequenceLabel( + listOf( + procedure.params + .withIndex() + .filter { it.value.second != ParamDirection.OUT } + .mapNotNull { iVal -> + val originalVar = iVal.value.first + val tempVar = + Var("tmp${tempCnt++}_" + originalVar.name, originalVar.type) + lookup[originalVar] = tempVar + val trial = + Try.attempt { StmtLabel( - Stmts.Assign( - TypeUtils.cast(tempVar, tempVar.type), - TypeUtils.cast(label.params[iVal.index], tempVar.type)), - metadata = label.metadata) - }, listOf(label.copy(tempLookup = lookup))).flatten()) - } else if (label is StartLabel) { - val procedure = s.xcfa?.procedures?.find { proc -> proc.name == label.name } - ?: error("No such method ${label.name}.") - val lookup: MutableMap, VarDecl<*>> = LinkedHashMap() - SequenceLabel(listOf(procedure.params.withIndex() - .filter { it.value.second != ParamDirection.OUT }.mapNotNull { iVal -> - val originalVar = iVal.value.first - val tempVar = Var("tmp${tempCnt++}_" + originalVar.name, - originalVar.type) - lookup[originalVar] = tempVar - val trial = Try.attempt { - StmtLabel( - Stmts.Assign( - TypeUtils.cast(tempVar, tempVar.type), - TypeUtils.cast(label.params[iVal.index], tempVar.type)), - metadata = label.metadata) - } - if (trial.isSuccess) { - trial.asSuccess().value - } else { - null - } - }, listOf(label.copy(tempLookup = lookup))).flatten()) - } else label - }) - XcfaAction(proc.key, edge.withLabel(newNewLabel), nextCnt = s.sGlobal.nextCnt) - } else - XcfaAction(proc.key, edge.withLabel(newLabel), nextCnt = s.sGlobal.nextCnt) - } + Stmts.Assign( + TypeUtils.cast(tempVar, tempVar.type), + TypeUtils.cast(label.params[iVal.index], tempVar.type), + ), + metadata = label.metadata, + ) + } + if (trial.isSuccess) { + trial.asSuccess().value + } else { + null + } + }, + listOf(label.copy(tempLookup = lookup)), + ) + .flatten() + ) + } else label + } + ) + XcfaAction(proc.key, edge.withLabel(newNewLabel), nextCnt = s.sGlobal.nextCnt) + } else XcfaAction(proc.key, edge.withLabel(newLabel), nextCnt = s.sGlobal.nextCnt) + } } - }.flatten().toSet() -} + } + .flatten() + .toSet() + } fun getXcfaLts(): LTS>, XcfaAction> { - val lts = getCoreXcfaLts() - return LTS>, XcfaAction> { s -> - lts.getEnabledActionsFor(s).filter { !s.apply(it).first.bottom }.toSet() - } + val lts = getCoreXcfaLts() + return LTS>, XcfaAction> { s -> + lts.getEnabledActionsFor(s).filter { !s.apply(it).first.bottom }.toSet() + } } enum class ErrorDetection { - ERROR_LOCATION, - DATA_RACE, - OVERFLOW, - NO_ERROR + ERROR_LOCATION, + DATA_RACE, + OVERFLOW, + NO_ERROR, } fun getXcfaErrorPredicate( - errorDetection: ErrorDetection): Predicate>> = when (errorDetection) { + errorDetection: ErrorDetection +): Predicate>> = + when (errorDetection) { ErrorDetection.ERROR_LOCATION -> - Predicate>> { s -> s.processes.any { it.value.locs.peek().error } } + Predicate>> { s -> + s.processes.any { it.value.locs.peek().error } + } ErrorDetection.DATA_RACE -> { - Predicate>> { s -> - val xcfa = s.xcfa!! - for (process1 in s.processes) - for (process2 in s.processes) - if (process1.key != process2.key) - for (edge1 in process1.value.locs.peek().outgoingEdges) - for (edge2 in process2.value.locs.peek().outgoingEdges) { - val mutexes1 = s.mutexes.filterValues { it == process1.key }.keys - val mutexes2 = s.mutexes.filterValues { it == process2.key }.keys - val globalVars1 = edge1.getGlobalVarsWithNeededMutexes(xcfa, mutexes1) - val globalVars2 = edge2.getGlobalVarsWithNeededMutexes(xcfa, mutexes2) - for (v1 in globalVars1) - for (v2 in globalVars2) - if (v1.varDecl == v2.varDecl) - if (v1.access.isWritten || v2.access.isWritten) - if ((v1.mutexes intersect v2.mutexes).isEmpty()) - return@Predicate true - } - false - } + Predicate>> { s -> + val xcfa = s.xcfa!! + for (process1 in s.processes) for (process2 in s.processes) if ( + process1.key != process2.key + ) + for (edge1 in process1.value.locs.peek().outgoingEdges) for (edge2 in + process2.value.locs.peek().outgoingEdges) { + val mutexes1 = s.mutexes.filterValues { it == process1.key }.keys + val mutexes2 = s.mutexes.filterValues { it == process2.key }.keys + val globalVars1 = edge1.getGlobalVarsWithNeededMutexes(xcfa, mutexes1) + val globalVars2 = edge2.getGlobalVarsWithNeededMutexes(xcfa, mutexes2) + for (v1 in globalVars1) for (v2 in globalVars2) if (v1.varDecl == v2.varDecl) + if (v1.access.isWritten || v2.access.isWritten) + if ((v1.mutexes intersect v2.mutexes).isEmpty()) return@Predicate true + } + false + } } - ErrorDetection.NO_ERROR, ErrorDetection.OVERFLOW -> Predicate>> { false } -} + ErrorDetection.NO_ERROR, + ErrorDetection.OVERFLOW -> Predicate>> { false } + } fun getPartialOrder(partialOrd: PartialOrd>) = - PartialOrd>> { s1, s2 -> - s1.processes == s2.processes && s1.bottom == s2.bottom && s1.mutexes == s2.mutexes && partialOrd.isLeq( - s1.sGlobal, s2.sGlobal) - } + PartialOrd>> { s1, s2 -> + s1.processes == s2.processes && + s1.bottom == s2.bottom && + s1.mutexes == s2.mutexes && + partialOrd.isLeq(s1.sGlobal, s2.sGlobal) + } -private fun stackIsLeq(s1: XcfaState>, - s2: XcfaState>) = s2.processes.keys.all { pid -> +private fun stackIsLeq(s1: XcfaState>, s2: XcfaState>) = + s2.processes.keys.all { pid -> s1.processes[pid]?.let { ps1 -> - val ps2 = s2.processes.getValue(pid) - ps1.locs.peek() == ps2.locs.peek() && ps1.paramsInitialized && ps2.paramsInitialized + val ps2 = s2.processes.getValue(pid) + ps1.locs.peek() == ps2.locs.peek() && ps1.paramsInitialized && ps2.paramsInitialized } ?: false -} + } fun getStackPartialOrder(partialOrd: PartialOrd>) = - PartialOrd>> { s1, s2 -> - s1.processes.size == s2.processes.size && stackIsLeq(s1, - s2) && s1.bottom == s2.bottom && s1.mutexes == s2.mutexes - && partialOrd.isLeq(s1.withGeneralizedVars(), s2.withGeneralizedVars()) - } + PartialOrd>> { s1, s2 -> + s1.processes.size == s2.processes.size && + stackIsLeq(s1, s2) && + s1.bottom == s2.bottom && + s1.mutexes == s2.mutexes && + partialOrd.isLeq(s1.withGeneralizedVars(), s2.withGeneralizedVars()) + } private fun >, P : XcfaPrec> getXcfaArgBuilder( - analysis: Analysis, - lts: LTS>, XcfaAction>, - errorDetection: ErrorDetection) - : ArgBuilder = - ArgBuilder.create( - lts, - analysis, - getXcfaErrorPredicate(errorDetection) - ) + analysis: Analysis, + lts: LTS>, XcfaAction>, + errorDetection: ErrorDetection, +): ArgBuilder = + ArgBuilder.create(lts, analysis, getXcfaErrorPredicate(errorDetection)) fun >, P : XcfaPrec> getXcfaAbstractor( - analysis: Analysis, - waitlist: Waitlist<*>, - stopCriterion: StopCriterion<*, *>, - logger: Logger, - lts: LTS>, XcfaAction>, - errorDetection: ErrorDetection -): Abstractor>, XcfaAction, out XcfaPrec> = - XcfaAbstractor.builder(getXcfaArgBuilder(analysis, lts, errorDetection)) - .waitlist(waitlist as Waitlist>) // TODO: can we do this nicely? - .stopCriterion(stopCriterion as StopCriterion).logger(logger) - .projection { - if (it.xcfa!!.isInlined) it.processes - else it.processes.map { (_, p) -> p.locs.peek() } - } - .build() // TODO: can we do this nicely? + analysis: Analysis, + waitlist: Waitlist<*>, + stopCriterion: StopCriterion<*, *>, + logger: Logger, + lts: LTS>, XcfaAction>, + errorDetection: ErrorDetection, +): ArgAbstractor>, XcfaAction, out XcfaPrec> = + XcfaArgAbstractor.builder(getXcfaArgBuilder(analysis, lts, errorDetection)) + .waitlist(waitlist as Waitlist>) // TODO: can we do this nicely? + .stopCriterion(stopCriterion as StopCriterion) + .logger(logger) + .projection { + if (it.xcfa!!.isInlined) it.processes else it.processes.map { (_, p) -> p.locs.peek() } + } + .build() // TODO: can we do this nicely? /// EXPL -private fun getExplXcfaInitFunc(xcfa: XCFA, - solver: Solver): (XcfaPrec>) -> List>> { - val processInitState = xcfa.initProcedures.mapIndexed { i, it -> +private fun getExplXcfaInitFunc( + xcfa: XCFA, + solver: Solver, +): (XcfaPrec>) -> List>> { + val processInitState = + xcfa.initProcedures + .mapIndexed { i, it -> val initLocStack: LinkedList = LinkedList() initLocStack.add(it.first.initLoc) - Pair(i, XcfaProcessState(initLocStack, prefix = "T$i", - varLookup = LinkedList(listOf(createLookup(it.first, "T$i", ""))))) - }.toMap() - return { p -> - ExplInitFunc.create(solver, True()).getPtrInitFunc().getInitStates(p.p) - .map { XcfaState(xcfa, processInitState, it) } + Pair( + i, + XcfaProcessState( + initLocStack, + prefix = "T$i", + varLookup = LinkedList(listOf(createLookup(it.first, "T$i", ""))), + ), + ) + } + .toMap() + return { p -> + ExplInitFunc.create(solver, True()).getPtrInitFunc().getInitStates(p.p).map { + XcfaState(xcfa, processInitState, it) } + } } -private fun getExplXcfaTransFunc(solver: Solver, maxEnum: Int, isHavoc: Boolean): - (XcfaState>, XcfaAction, XcfaPrec>) -> List>> { - val explTransFunc = (ExplStmtTransFunc.create(solver, - maxEnum) as TransFunc).getPtrTransFunc(isHavoc) - return { s, a, p -> - val (newSt, newAct) = s.apply(a) - explTransFunc.getSuccStates(newSt.sGlobal, newAct, p.p.addVars( - listOf(s.processes.map { it.value.varLookup }.flatten(), - listOf(getTempLookup(a.label))).flatten())).map { newSt.withState(it) } - } +private fun getExplXcfaTransFunc( + solver: Solver, + maxEnum: Int, + isHavoc: Boolean, +): (XcfaState>, XcfaAction, XcfaPrec>) -> List< + XcfaState> + > { + val explTransFunc = + (ExplStmtTransFunc.create(solver, maxEnum) as TransFunc) + .getPtrTransFunc(isHavoc) + return { s, a, p -> + val (newSt, newAct) = s.apply(a) + explTransFunc + .getSuccStates( + newSt.sGlobal, + newAct, + p.p.addVars( + listOf(s.processes.map { it.value.varLookup }.flatten(), listOf(getTempLookup(a.label))) + .flatten() + ), + ) + .map { newSt.withState(it) } + } } -class ExplXcfaAnalysis(xcfa: XCFA, solver: Solver, maxEnum: Int, - partialOrd: PartialOrd>>, isHavoc: Boolean) : - XcfaAnalysis>( - corePartialOrd = partialOrd, - coreInitFunc = getExplXcfaInitFunc(xcfa, solver), - coreTransFunc = getExplXcfaTransFunc(solver, maxEnum, isHavoc) - ) +class ExplXcfaAnalysis( + xcfa: XCFA, + solver: Solver, + maxEnum: Int, + partialOrd: PartialOrd>>, + isHavoc: Boolean, +) : + XcfaAnalysis>( + corePartialOrd = partialOrd, + coreInitFunc = getExplXcfaInitFunc(xcfa, solver), + coreTransFunc = getExplXcfaTransFunc(solver, maxEnum, isHavoc), + ) /// PRED -private fun getPredXcfaInitFunc(xcfa: XCFA, - predAbstractor: PredAbstractor): (XcfaPrec>) -> List>> { - val processInitState = xcfa.initProcedures.mapIndexed { i, it -> +private fun getPredXcfaInitFunc( + xcfa: XCFA, + predAbstractor: PredAbstractor, +): (XcfaPrec>) -> List>> { + val processInitState = + xcfa.initProcedures + .mapIndexed { i, it -> val initLocStack: LinkedList = LinkedList() initLocStack.add(it.first.initLoc) - Pair(i, XcfaProcessState(initLocStack, prefix = "T$i", - varLookup = LinkedList(listOf(createLookup(it.first, "T$i", ""))))) - }.toMap() - return { p -> - PredInitFunc.create(predAbstractor, True()).getPtrInitFunc().getInitStates(p.p) - .map { XcfaState(xcfa, processInitState, it) } + Pair( + i, + XcfaProcessState( + initLocStack, + prefix = "T$i", + varLookup = LinkedList(listOf(createLookup(it.first, "T$i", ""))), + ), + ) + } + .toMap() + return { p -> + PredInitFunc.create(predAbstractor, True()).getPtrInitFunc().getInitStates(p.p).map { + XcfaState(xcfa, processInitState, it) } + } } -private fun getPredXcfaTransFunc(predAbstractor: PredAbstractors.PredAbstractor, isHavoc: Boolean): - (XcfaState>, XcfaAction, XcfaPrec>) -> List>> { - val predTransFunc = (PredTransFunc.create( - predAbstractor) as TransFunc).getPtrTransFunc(isHavoc) - return { s, a, p -> - val (newSt, newAct) = s.apply(a) - predTransFunc.getSuccStates(newSt.sGlobal, newAct, p.p.addVars( - s.processes.map { it.value.foldVarLookup() + getTempLookup(a.label) } - )).map { newSt.withState(it) } - } +private fun getPredXcfaTransFunc( + predAbstractor: PredAbstractors.PredAbstractor, + isHavoc: Boolean, +): (XcfaState>, XcfaAction, XcfaPrec>) -> List< + XcfaState> + > { + val predTransFunc = + (PredTransFunc.create(predAbstractor) as TransFunc) + .getPtrTransFunc(isHavoc) + return { s, a, p -> + val (newSt, newAct) = s.apply(a) + predTransFunc + .getSuccStates( + newSt.sGlobal, + newAct, + p.p.addVars(s.processes.map { it.value.foldVarLookup() + getTempLookup(a.label) }), + ) + .map { newSt.withState(it) } + } } -class PredXcfaAnalysis(xcfa: XCFA, solver: Solver, predAbstractor: PredAbstractor, - partialOrd: PartialOrd>>, isHavoc: Boolean) : - XcfaAnalysis>( - corePartialOrd = partialOrd, - coreInitFunc = getPredXcfaInitFunc(xcfa, predAbstractor), - coreTransFunc = getPredXcfaTransFunc(predAbstractor, isHavoc) - ) +class PredXcfaAnalysis( + xcfa: XCFA, + solver: Solver, + predAbstractor: PredAbstractor, + partialOrd: PartialOrd>>, + isHavoc: Boolean, +) : + XcfaAnalysis>( + corePartialOrd = partialOrd, + coreInitFunc = getPredXcfaInitFunc(xcfa, predAbstractor), + coreTransFunc = getPredXcfaTransFunc(predAbstractor, isHavoc), + ) diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaArgAbstractor.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaArgAbstractor.kt new file mode 100644 index 0000000000..bc2d2b816c --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaArgAbstractor.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.xcfa.analysis + +import com.google.common.base.Preconditions +import hu.bme.mit.theta.analysis.Action +import hu.bme.mit.theta.analysis.Prec +import hu.bme.mit.theta.analysis.State +import hu.bme.mit.theta.analysis.algorithm.arg.ARG +import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder +import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode +import hu.bme.mit.theta.analysis.algorithm.cegar.AbstractorResult +import hu.bme.mit.theta.analysis.algorithm.cegar.BasicArgAbstractor +import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterion +import hu.bme.mit.theta.analysis.reachedset.Partition +import hu.bme.mit.theta.analysis.waitlist.Waitlist +import hu.bme.mit.theta.common.logging.Logger +import java.util.function.Function + +class XcfaArgAbstractor( + argBuilder: ArgBuilder, + projection: Function?, + waitlist: Waitlist>, + stopCriterion: StopCriterion, + logger: Logger, +) : BasicArgAbstractor(argBuilder, projection, waitlist, stopCriterion, logger) { + + override fun check(arg: ARG, prec: P): AbstractorResult { + logger.write(Logger.Level.DETAIL, "| | Precision: %s%n", prec) + + if (!arg.isInitialized) { + logger.write(Logger.Level.SUBSTEP, "| | (Re)initializing ARG...") + argBuilder.init(arg, prec) + logger.write(Logger.Level.SUBSTEP, "done%n") + } + + assert(arg.isInitialized) + + logger.write( + Logger.Level.INFO, + "| | Starting ARG: %d nodes, %d incomplete, %d unsafe%n", + arg.nodes.count(), + arg.incompleteNodes.count(), + arg.unsafeNodes.count(), + ) + logger.write(Logger.Level.SUBSTEP, "| | Building ARG...") + + val reachedSet: Partition, *> = + Partition.of { n: ArgNode -> projection.apply(n.state) } + waitlist.clear() + + reachedSet.addAll(arg.nodes) + waitlist.addAll(arg.incompleteNodes) + + if (!stopCriterion.canStop(arg)) { + while (!waitlist.isEmpty) { + val node = waitlist.remove() + var newNodes: Collection>? = emptyList() + if ((node.state as XcfaState<*>).xcfa!!.isInlined) { + close(node, reachedSet[node]) + } else { + val expandProcedureCall = (node.state as XcfaState<*>) in (prec as XcfaPrec

).noPop + closePop(node, reachedSet[node], !expandProcedureCall) + } + if (!node.isSubsumed && !node.isTarget) { + newNodes = argBuilder.expand(node, prec) + reachedSet.addAll(newNodes) + waitlist.addAll(newNodes) + } + if (stopCriterion.canStop(arg, newNodes)) break + } + } + + logger.write(Logger.Level.SUBSTEP, "done%n") + logger.write( + Logger.Level.INFO, + "| | Finished ARG: %d nodes, %d incomplete, %d unsafe%n", + arg.nodes.count(), + arg.incompleteNodes.count(), + arg.unsafeNodes.count(), + ) + + waitlist.clear() // Optimization + + return if (arg.isSafe) { + Preconditions.checkState(arg.isComplete, "Returning incomplete ARG as safe") + AbstractorResult.safe() + } else { + AbstractorResult.unsafe() + } + } + + fun closePop(node: ArgNode, candidates: Collection>, popCovered: Boolean) { + if (!node.isLeaf) { + return + } + for (candidate in candidates) { + if (candidate.mayCover(node)) { + var onlyStackCovers = false + (node.state as XcfaState<*>).processes.forEach { (pid: Int, proc: XcfaProcessState) -> + if (proc != (candidate.state as XcfaState<*>).processes[pid]) { + if (popCovered) proc.popped = proc.locs.pop() + onlyStackCovers = true + } + } + if (!onlyStackCovers) { + node.cover(candidate) + } + return + } + } + } + + companion object { + + fun builder( + argBuilder: ArgBuilder + ): BasicArgAbstractor.Builder { + return Builder(argBuilder) + } + } + + class Builder(argBuilder: ArgBuilder) : + BasicArgAbstractor.Builder(argBuilder) { + + override fun build(): BasicArgAbstractor { + return XcfaArgAbstractor(argBuilder, projection, waitlist, stopCriterion, logger) + } + } +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaSingleExprTraceRefiner.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaSingleExprTraceRefiner.kt index ad0a385e5d..4ca545dd9b 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaSingleExprTraceRefiner.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaSingleExprTraceRefiner.kt @@ -29,143 +29,168 @@ import hu.bme.mit.theta.analysis.ptr.patch import hu.bme.mit.theta.common.logging.Logger import java.util.* - class XcfaSingleExprTraceRefiner : - SingleExprTraceRefiner { - - private constructor( - exprTraceChecker: ExprTraceChecker, - precRefiner: PrecRefiner, - pruneStrategy: PruneStrategy, - logger: Logger - ) : super(exprTraceChecker, precRefiner, pruneStrategy, logger) - - private constructor( - exprTraceChecker: ExprTraceChecker, - precRefiner: PrecRefiner, - pruneStrategy: PruneStrategy, - logger: Logger, - nodePruner: NodePruner - ) : super(exprTraceChecker, precRefiner, pruneStrategy, logger, nodePruner) - - private fun findPoppedState(trace: Trace): Pair>? { - trace.states.forEachIndexed { i, s -> - val state = s as XcfaState - state.processes.entries.find { (_, processState) -> processState.popped != null } - ?.let { (pid, processState) -> - val stackBeforePop = LinkedList(processState.locs) - stackBeforePop.push(processState.popped) - val processesBeforePop = state.processes.toMutableMap() - processesBeforePop[pid] = processState.copy(locs = stackBeforePop) - val stateBeforePop = state.copy(processes = processesBeforePop) - return Pair(i, stateBeforePop) - } + SingleExprTraceRefiner { + + private constructor( + exprTraceChecker: ExprTraceChecker, + precRefiner: PrecRefiner, + pruneStrategy: PruneStrategy, + logger: Logger, + ) : super(exprTraceChecker, precRefiner, pruneStrategy, logger) + + private constructor( + exprTraceChecker: ExprTraceChecker, + precRefiner: PrecRefiner, + pruneStrategy: PruneStrategy, + logger: Logger, + nodePruner: NodePruner, + ) : super(exprTraceChecker, precRefiner, pruneStrategy, logger, nodePruner) + + private fun findPoppedState(trace: Trace): Pair>? { + trace.states.forEachIndexed { i, s -> + val state = s as XcfaState + state.processes.entries + .find { (_, processState) -> processState.popped != null } + ?.let { (pid, processState) -> + val stackBeforePop = LinkedList(processState.locs) + stackBeforePop.push(processState.popped) + val processesBeforePop = state.processes.toMutableMap() + processesBeforePop[pid] = processState.copy(locs = stackBeforePop) + val stateBeforePop = state.copy(processes = processesBeforePop) + return Pair(i, stateBeforePop) } - return null } - - fun refineTemp(arg: ARG, prec: P?): RefinerResult { - Preconditions.checkNotNull(arg) - Preconditions.checkNotNull(prec) - assert(!arg.isSafe) { "ARG must be unsafe" } - val optionalNewCex = arg.cexs.findFirst() - val cexToConcretize = optionalNewCex.get() - val rawTrace = cexToConcretize.toTrace() - val (_, states, actions) = rawTrace.actions.foldIndexed( - Triple(Pair(emptyMap(), 0), listOf(rawTrace.getState(0)), - listOf())) { i: Int, (wTripleCnt: Pair, states: List, actions: List): Triple, List, List>, a: A -> - val (wTriple, cnt) = wTripleCnt - val newA = (a as XcfaAction).withLastWrites(wTriple, cnt) - val newState = (rawTrace.getState(i + 1) as XcfaState>).let { - it.withState(PtrState(it.sGlobal.innerState.patch(newA.nextWriteTriples()))) - } - Triple(Pair(newA.nextWriteTriples(), newA.cnts.values.maxOrNull() ?: newA.inCnt), states + (newState as S), - actions + (newA as A)) + return null + } + + fun refineTemp(arg: ARG, prec: P?): RefinerResult> { + Preconditions.checkNotNull(arg) + Preconditions.checkNotNull(prec) + assert(!arg.isSafe) { "ARG must be unsafe" } + val optionalNewCex = arg.cexs.findFirst() + val cexToConcretize = optionalNewCex.get() + val rawTrace = cexToConcretize.toTrace() + val (_, states, actions) = + rawTrace.actions.foldIndexed( + Triple(Pair(emptyMap(), 0), listOf(rawTrace.getState(0)), listOf()) + ) { + i: Int, + (wTripleCnt: Pair, states: List, actions: List): Triple< + Pair, + List, + List, + >, + a: A -> + val (wTriple, cnt) = wTripleCnt + val newA = (a as XcfaAction).withLastWrites(wTriple, cnt) + val newState = + (rawTrace.getState(i + 1) as XcfaState>).let { + it.withState(PtrState(it.sGlobal.innerState.patch(newA.nextWriteTriples()))) + } + Triple( + Pair(newA.nextWriteTriples(), newA.cnts.values.maxOrNull() ?: newA.inCnt), + states + (newState as S), + actions + (newA as A), + ) + } + val traceToConcretize = Trace.of(states, actions) + + logger.write(Logger.Level.INFO, "| | Trace length: %d%n", traceToConcretize.length()) + logger.write(Logger.Level.DETAIL, "| | Trace: %s%n", traceToConcretize) + logger.write(Logger.Level.SUBSTEP, "| | Checking trace...") + val cexStatus = exprTraceChecker.check(traceToConcretize) + logger.write(Logger.Level.SUBSTEP, "done, result: %s%n", cexStatus) + assert(cexStatus.isFeasible() || cexStatus.isInfeasible()) { "Unknown CEX status" } + return if (cexStatus.isFeasible()) { + RefinerResult.unsafe(traceToConcretize) + } else { + val refutation = cexStatus.asInfeasible().refutation + logger.write(Logger.Level.DETAIL, "| | | Refutation: %s%n", refutation) + val refinedPrec = precRefiner.refine(prec, traceToConcretize, refutation) + val pruneIndex = refutation.getPruneIndex() + assert(0 <= pruneIndex) { "Pruning index must be non-negative" } + assert(pruneIndex <= cexToConcretize.length()) { "Pruning index larger than cex length" } + when (pruneStrategy) { + PruneStrategy.LAZY -> { + logger.write(Logger.Level.SUBSTEP, "| | Pruning from index %d...", pruneIndex) + val nodeToPrune = cexToConcretize.node(pruneIndex) + nodePruner.prune(arg, nodeToPrune) } - val traceToConcretize = Trace.of(states, actions) - - logger.write(Logger.Level.INFO, "| | Trace length: %d%n", traceToConcretize.length()) - logger.write(Logger.Level.DETAIL, "| | Trace: %s%n", traceToConcretize) - logger.write(Logger.Level.SUBSTEP, "| | Checking trace...") - val cexStatus = exprTraceChecker.check(traceToConcretize) - logger.write(Logger.Level.SUBSTEP, "done, result: %s%n", cexStatus) - assert(cexStatus.isFeasible() || cexStatus.isInfeasible()) { "Unknown CEX status" } - return if (cexStatus.isFeasible()) { - RefinerResult.unsafe(traceToConcretize) - } else { - val refutation = cexStatus.asInfeasible().refutation - logger.write(Logger.Level.DETAIL, "| | | Refutation: %s%n", refutation) - val refinedPrec = precRefiner.refine(prec, traceToConcretize, refutation) - val pruneIndex = refutation.getPruneIndex() - assert(0 <= pruneIndex) { "Pruning index must be non-negative" } - assert(pruneIndex <= cexToConcretize.length()) { "Pruning index larger than cex length" } - when (pruneStrategy) { - PruneStrategy.LAZY -> { - logger.write(Logger.Level.SUBSTEP, "| | Pruning from index %d...", pruneIndex) - val nodeToPrune = cexToConcretize.node(pruneIndex) - nodePruner.prune(arg, nodeToPrune) - } - - PruneStrategy.FULL -> { - logger.write(Logger.Level.SUBSTEP, "| | Pruning whole ARG", pruneIndex) - arg.pruneAll() - } - - else -> throw java.lang.UnsupportedOperationException("Unsupported pruning strategy") - } - logger.write(Logger.Level.SUBSTEP, "done%n") - RefinerResult.spurious(refinedPrec) + + PruneStrategy.FULL -> { + logger.write(Logger.Level.SUBSTEP, "| | Pruning whole ARG", pruneIndex) + arg.pruneAll() } - } - override fun refine(arg: ARG, prec: P?): RefinerResult { - Preconditions.checkNotNull(arg) - Preconditions.checkNotNull

(prec) - assert(!arg.isSafe) { "ARG must be unsafe" } - - val optionalNewCex = arg.cexs.findFirst() - val cexToConcretize = optionalNewCex.get() - val traceToConcretize = cexToConcretize.toTrace() - - val refinerResult = refineTemp(arg, prec) //super.refine(arg, prec) - val checkForPop = !(traceToConcretize.states.first() as XcfaState<*>).xcfa!!.isInlined - - return if (checkForPop && refinerResult.isUnsafe) findPoppedState(traceToConcretize)?.let { (i, state) -> - when (pruneStrategy) { - PruneStrategy.LAZY -> { - logger.write(Logger.Level.SUBSTEP, "| | Pruning from index %d...", i) - val nodeToPrune = cexToConcretize.node(i) - nodePruner.prune(arg, nodeToPrune) - } - - PruneStrategy.FULL -> { - logger.write(Logger.Level.SUBSTEP, "| | Pruning whole ARG", i) - arg.pruneAll() - } - - else -> throw UnsupportedOperationException("Unsupported pruning strategy") - } - - val refinedPrec = (prec as XcfaPrec

).copy() - refinedPrec.noPop.add(state) - RefinerResult.spurious(refinedPrec as P?) - } ?: refinerResult else refinerResult + else -> throw java.lang.UnsupportedOperationException("Unsupported pruning strategy") + } + logger.write(Logger.Level.SUBSTEP, "done%n") + RefinerResult.spurious(refinedPrec) } - - companion object { - - fun create( - exprTraceChecker: ExprTraceChecker, precRefiner: PrecRefiner, - pruneStrategy: PruneStrategy, logger: Logger - ): XcfaSingleExprTraceRefiner { - return XcfaSingleExprTraceRefiner(exprTraceChecker, precRefiner, pruneStrategy, logger) + } + + override fun refine(arg: ARG, prec: P?): RefinerResult> { + Preconditions.checkNotNull(arg) + Preconditions.checkNotNull

(prec) + assert(!arg.isSafe) { "ARG must be unsafe" } + + val optionalNewCex = arg.cexs.findFirst() + val cexToConcretize = optionalNewCex.get() + val traceToConcretize = cexToConcretize.toTrace() + + val refinerResult = refineTemp(arg, prec) // super.refine(arg, prec) + val checkForPop = !(traceToConcretize.states.first() as XcfaState<*>).xcfa!!.isInlined + + return if (checkForPop && refinerResult.isUnsafe) + findPoppedState(traceToConcretize)?.let { (i, state) -> + when (pruneStrategy) { + PruneStrategy.LAZY -> { + logger.write(Logger.Level.SUBSTEP, "| | Pruning from index %d...", i) + val nodeToPrune = cexToConcretize.node(i) + nodePruner.prune(arg, nodeToPrune) + } + + PruneStrategy.FULL -> { + logger.write(Logger.Level.SUBSTEP, "| | Pruning whole ARG", i) + arg.pruneAll() + } + + else -> throw UnsupportedOperationException("Unsupported pruning strategy") } - fun create( - exprTraceChecker: ExprTraceChecker, precRefiner: PrecRefiner, - pruneStrategy: PruneStrategy, logger: Logger, nodePruner: NodePruner - ): XcfaSingleExprTraceRefiner { - return XcfaSingleExprTraceRefiner(exprTraceChecker, precRefiner, pruneStrategy, logger, nodePruner) - } + val refinedPrec = (prec as XcfaPrec

).copy() + refinedPrec.noPop.add(state) + RefinerResult.spurious(refinedPrec as P?) + } ?: refinerResult + else refinerResult + } + + companion object { + + fun create( + exprTraceChecker: ExprTraceChecker, + precRefiner: PrecRefiner, + pruneStrategy: PruneStrategy, + logger: Logger, + ): XcfaSingleExprTraceRefiner { + return XcfaSingleExprTraceRefiner(exprTraceChecker, precRefiner, pruneStrategy, logger) + } + + fun create( + exprTraceChecker: ExprTraceChecker, + precRefiner: PrecRefiner, + pruneStrategy: PruneStrategy, + logger: Logger, + nodePruner: NodePruner, + ): XcfaSingleExprTraceRefiner { + return XcfaSingleExprTraceRefiner( + exprTraceChecker, + precRefiner, + pruneStrategy, + logger, + nodePruner, + ) } + } } diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcChecker.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcChecker.kt index 6ffa9bc1fb..72f7a2520e 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcChecker.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcChecker.kt @@ -13,15 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis.oc -import hu.bme.mit.theta.analysis.EmptyCex import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness +import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.SafetyResult -import hu.bme.mit.theta.analysis.algorithm.arg.ARG import hu.bme.mit.theta.analysis.algorithm.oc.EventType import hu.bme.mit.theta.analysis.algorithm.oc.OcChecker import hu.bme.mit.theta.analysis.algorithm.oc.Relation @@ -52,335 +49,363 @@ import hu.bme.mit.theta.xcfa.passes.AssumeFalseRemovalPass import hu.bme.mit.theta.xcfa.passes.AtomicReadsOneWritePass import hu.bme.mit.theta.xcfa.passes.MutexToVarPass -private val Expr<*>.vars get() = ExprUtils.getVars(this) +private val Expr<*>.vars + get() = ExprUtils.getVars(this) -class XcfaOcChecker(xcfa: XCFA, decisionProcedure: OcDecisionProcedureType, private val logger: Logger) : - SafetyChecker>, XcfaAction>, XcfaPrec> { +class XcfaOcChecker( + xcfa: XCFA, + decisionProcedure: OcDecisionProcedureType, + private val logger: Logger, +) : SafetyChecker>, XcfaAction>, XcfaPrec> { - private val ocChecker: OcChecker = decisionProcedure.checker() - private val solver = ocChecker.solver + private val ocChecker: OcChecker = decisionProcedure.checker() + private val solver = ocChecker.solver - private val xcfa: XCFA = xcfa.optimizeFurther( - listOf(AssumeFalseRemovalPass(), MutexToVarPass(), AtomicReadsOneWritePass()) + private val xcfa: XCFA = + xcfa.optimizeFurther( + listOf(AssumeFalseRemovalPass(), MutexToVarPass(), AtomicReadsOneWritePass()) ) - private var indexing = VarIndexingFactory.indexing(0) - private val localVars = mutableMapOf, MutableMap>>() - - private val threads = mutableSetOf() - private val events = mutableMapOf, MutableMap>>() - private val violations = mutableListOf() // OR! - private val branchingConditions = mutableListOf>() - private val pos = mutableListOf() - private val rfs = mutableMapOf, MutableSet>() - - override fun check( - prec: XcfaPrec?): SafetyResult>, XcfaAction>> = let { - if (xcfa.initProcedures.size > 1) error("Multiple entry points are not supported by OC checker.") + private var indexing = VarIndexingFactory.indexing(0) + private val localVars = mutableMapOf, MutableMap>>() + + private val threads = mutableSetOf() + private val events = mutableMapOf, MutableMap>>() + private val violations = mutableListOf() // OR! + private val branchingConditions = mutableListOf>() + private val pos = mutableListOf() + private val rfs = mutableMapOf, MutableSet>() + + override fun check( + prec: XcfaPrec? + ): SafetyResult>, XcfaAction>> = + let { + if (xcfa.initProcedures.size > 1) + error("Multiple entry points are not supported by OC checker.") logger.write(Logger.Level.MAINSTEP, "Adding constraints...\n") xcfa.initProcedures.forEach { processThread(Thread(it.first)) } addCrossThreadRelations() - if (!addToSolver()) return@let SafetyResult.safe>, XcfaAction>>( - EmptyWitness.getInstance()) // no violations in the model + if (!addToSolver()) + return@let SafetyResult.safe>, XcfaAction>>( + EmptyProof.getInstance() + ) // no violations in the model logger.write(Logger.Level.MAINSTEP, "Start checking...\n") val status = ocChecker.check(events, pos, rfs) when { - status?.isUnsat == true -> SafetyResult.safe(EmptyWitness.getInstance()) - status?.isSat == true -> SafetyResult.unsafe( - XcfaOcTraceExtractor(xcfa, ocChecker, threads, events, violations, pos).trace, - EmptyWitness.getInstance() + status?.isUnsat == true -> SafetyResult.safe(EmptyProof.getInstance()) + status?.isSat == true -> + SafetyResult.unsafe( + XcfaOcTraceExtractor(xcfa, ocChecker, threads, events, violations, pos).trace, + EmptyProof.getInstance(), ) - else -> SafetyResult.unknown() + else -> SafetyResult.unknown() } - }.also { logger.write(Logger.Level.MAINSTEP, "OC checker result: $it\n") } - - private fun processThread(thread: Thread): List { - threads.add(thread) - val pid = thread.pid - var last = listOf() - var guard = setOf>() - lateinit var lastWrites: MutableMap, Set> - lateinit var edge: XcfaEdge - var inEdge = false - var atomicEntered: Boolean? = null - - val newEvent: (VarDecl<*>, EventType) -> List = { d, type -> - check(!inEdge || last.size == 1) - val decl = d.threadVar(pid) - val useLastClk = inEdge || atomicEntered == true - val e = - if (useLastClk) E(decl.getNewIndexed(), type, guard, pid, edge, last.first().clkId) - else E(decl.getNewIndexed(), type, guard, pid, edge) - last.forEach { po(it, e) } - inEdge = true - if (atomicEntered == false) atomicEntered = true - when (type) { - EventType.READ -> lastWrites[decl]?.forEach { rfs.add(RelationType.RF, it, e) } - EventType.WRITE -> lastWrites[decl] = setOf(e) - } - events[decl] = (events[decl] ?: mutableMapOf()).apply { - this[pid] = (this[pid] ?: mutableListOf()).apply { add(e) } - } - listOf(e) + } + .also { logger.write(Logger.Level.MAINSTEP, "OC checker result: $it\n") } + + private fun processThread(thread: Thread): List { + threads.add(thread) + val pid = thread.pid + var last = listOf() + var guard = setOf>() + lateinit var lastWrites: MutableMap, Set> + lateinit var edge: XcfaEdge + var inEdge = false + var atomicEntered: Boolean? = null + + val newEvent: (VarDecl<*>, EventType) -> List = { d, type -> + check(!inEdge || last.size == 1) + val decl = d.threadVar(pid) + val useLastClk = inEdge || atomicEntered == true + val e = + if (useLastClk) E(decl.getNewIndexed(), type, guard, pid, edge, last.first().clkId) + else E(decl.getNewIndexed(), type, guard, pid, edge) + last.forEach { po(it, e) } + inEdge = true + if (atomicEntered == false) atomicEntered = true + when (type) { + EventType.READ -> lastWrites[decl]?.forEach { rfs.add(RelationType.RF, it, e) } + EventType.WRITE -> lastWrites[decl] = setOf(e) + } + events[decl] = + (events[decl] ?: mutableMapOf()).apply { + this[pid] = (this[pid] ?: mutableListOf()).apply { add(e) } } + listOf(e) + } - val waitList = mutableSetOf() - val toVisit = mutableSetOf(SearchItem(thread.procedure.initLoc).apply { - guards.add(thread.guard) - thread.startEvent?.let { lastEvents.add(it) } - this.lastWrites.add(thread.lastWrites) - }) - val threads = mutableListOf() - - while (toVisit.isNotEmpty()) { - val current = toVisit.first() - toVisit.remove(current) - check(current.incoming == current.loc.incomingEdges.size) - check(current.incoming == current.guards.size || current.loc.initial) - // lastEvents intentionally skipped - check(current.incoming == current.lastWrites.size || current.loc.initial) - check(current.incoming == current.threadLookups.size) - check(current.incoming == current.atomics.size) - check(current.atomics.all { it == current.atomics.first() }) // bad pattern otherwise - - if (current.loc.error) { - val errorGuard = Or(current.lastEvents.map { it.guard.toAnd() }) - violations.add(Violation(current.loc, pid, errorGuard, current.lastEvents)) - continue - } - - if (current.loc.final) { - thread.finalEvents.addAll(current.lastEvents) + val waitList = mutableSetOf() + val toVisit = + mutableSetOf( + SearchItem(thread.procedure.initLoc).apply { + guards.add(thread.guard) + thread.startEvent?.let { lastEvents.add(it) } + this.lastWrites.add(thread.lastWrites) + } + ) + val threads = mutableListOf() + + while (toVisit.isNotEmpty()) { + val current = toVisit.first() + toVisit.remove(current) + check(current.incoming == current.loc.incomingEdges.size) + check(current.incoming == current.guards.size || current.loc.initial) + // lastEvents intentionally skipped + check(current.incoming == current.lastWrites.size || current.loc.initial) + check(current.incoming == current.threadLookups.size) + check(current.incoming == current.atomics.size) + check(current.atomics.all { it == current.atomics.first() }) // bad pattern otherwise + + if (current.loc.error) { + val errorGuard = Or(current.lastEvents.map { it.guard.toAnd() }) + violations.add(Violation(current.loc, pid, errorGuard, current.lastEvents)) + continue + } + + if (current.loc.final) { + thread.finalEvents.addAll(current.lastEvents) + } + + val mergedGuard = current.guards.toOrInSet() + val assumeConsts = mutableMapOf, MutableList>>() + + for (e in current.loc.outgoingEdges) { + edge = e + inEdge = false + last = current.lastEvents + // intersection of guards of incoming edges: + guard = mergedGuard + lastWrites = current.lastWrites.merge().toMutableMap() + val threadLookup = + current.threadLookups + .merge { s1, s2 -> + s1 + s2.filter { (guard2, _) -> s1.none { (guard1, _) -> guard1 == guard2 } } } + .toMutableMap() + var firstLabel = true + atomicEntered = current.atomics.firstOrNull() + + edge.getFlatLabels().forEach { label -> + if (label.references.isNotEmpty() || label.dereferences.isNotEmpty()) { + error("References not supported by OC checker.") + } + when (label) { + is StmtLabel -> { + when (val stmt = label.stmt) { + is AssignStmt<*> -> { + val consts = mutableMapOf, ConstDecl<*>>() + stmt.expr.vars.forEach { + last = newEvent(it, EventType.READ) + consts[it] = last.first().const + } + last = newEvent(stmt.varDecl, EventType.WRITE) + last.first().assignment = Eq(last.first().const.ref, stmt.expr.withConsts(consts)) + } - val mergedGuard = current.guards.toOrInSet() - val assumeConsts = mutableMapOf, MutableList>>() - - for (e in current.loc.outgoingEdges) { - edge = e - inEdge = false - last = current.lastEvents - // intersection of guards of incoming edges: - guard = mergedGuard - lastWrites = current.lastWrites.merge().toMutableMap() - val threadLookup = current.threadLookups.merge { s1, s2 -> - s1 + s2.filter { (guard2, _) -> s1.none { (guard1, _) -> guard1 == guard2 } } - }.toMutableMap() - var firstLabel = true - atomicEntered = current.atomics.firstOrNull() - - edge.getFlatLabels().forEach { label -> - if (label.references.isNotEmpty() || label.dereferences.isNotEmpty()) { - error("References not supported by OC checker.") - } - when (label) { - is StmtLabel -> { - when (val stmt = label.stmt) { - is AssignStmt<*> -> { - val consts = mutableMapOf, ConstDecl<*>>() - stmt.expr.vars.forEach { - last = newEvent(it, EventType.READ) - consts[it] = last.first().const - } - last = newEvent(stmt.varDecl, EventType.WRITE) - last.first().assignment = Eq(last.first().const.ref, stmt.expr.withConsts(consts)) - } - - is AssumeStmt -> { - val consts = stmt.cond.vars.associateWith { it.threadVar(pid).getNewIndexed(false) } - val condWithConsts = stmt.cond.withConsts(consts) - val asAssign = consts.size == 1 && consts.keys.first().threadVar(pid) !in lastWrites - if (edge.source.outgoingEdges.size > 1 || !asAssign) { - guard = guard + condWithConsts - if (firstLabel) { - consts.forEach { (v, c) -> - assumeConsts.getOrPut(v) { mutableListOf() }.add(c) - } - } - } - stmt.cond.vars.forEach { - last = newEvent(it, EventType.READ) - } - if (edge.source.outgoingEdges.size == 1 && asAssign) { - last.first().assignment = condWithConsts - } - } - - is HavocStmt<*> -> { - last = newEvent(stmt.varDecl, EventType.WRITE) - } - - else -> error("Unsupported statement type: $stmt") - } - } - - is StartLabel -> { - // TODO StartLabel params - if (label.name in thread.startHistory) { - error("Recursive thread start not supported by OC checker.") - } - val procedure = xcfa.procedures.find { it.name == label.name } - ?: error("Procedure not found: ${label.name}") - last = newEvent(label.pidVar, EventType.WRITE) - val pidVar = label.pidVar.threadVar(pid) - if (this.threads.any { it.pidVar == pidVar }) { - error("Using a pthread_t variable in multiple threads is not supported by OC checker.") - } - val newHistory = thread.startHistory + thread.procedure.name - val newThread = Thread(procedure, guard, pidVar, last.first(), newHistory, lastWrites) - last.first().assignment = Eq(last.first().const.ref, Int(newThread.pid)) - threadLookup[pidVar] = setOf(Pair(guard, newThread)) - processThread(newThread) - } - - is JoinLabel -> { - val incomingGuard = guard - val lastEvents = mutableListOf() - val joinGuards = mutableListOf>>() - threadLookup[label.pidVar.threadVar(pid)]?.forEach { (g, thread) -> - guard = incomingGuard + g + thread.finalEvents.map { it.guard }.toOrInSet() - val joinEvent = newEvent(label.pidVar, EventType.READ).first() - thread.finalEvents.forEach { final -> po(final, joinEvent) } - lastEvents.add(joinEvent) - joinGuards.add(guard) - } ?: error("Thread started in a different thread: not supported by OC checker.") - guard = joinGuards.toOrInSet() - last = lastEvents - } - - is FenceLabel -> { - if (label.labels.size > 1 || label.labels.firstOrNull()?.contains("ATOMIC") != true) { - error("Untransformed fence label: $label") - } - if (label.isAtomicBegin) atomicEntered = false - if (label.isAtomicEnd) atomicEntered = null - } - - is NopLabel -> {} - else -> error("Unsupported label type by OC checker: $label") + is AssumeStmt -> { + val consts = + stmt.cond.vars.associateWith { it.threadVar(pid).getNewIndexed(false) } + val condWithConsts = stmt.cond.withConsts(consts) + val asAssign = + consts.size == 1 && consts.keys.first().threadVar(pid) !in lastWrites + if (edge.source.outgoingEdges.size > 1 || !asAssign) { + guard = guard + condWithConsts + if (firstLabel) { + consts.forEach { (v, c) -> + assumeConsts.getOrPut(v) { mutableListOf() }.add(c) + } } - firstLabel = false + } + stmt.cond.vars.forEach { last = newEvent(it, EventType.READ) } + if (edge.source.outgoingEdges.size == 1 && asAssign) { + last.first().assignment = condWithConsts + } } - val searchItem = waitList.find { it.loc == edge.target } - ?: SearchItem(edge.target).apply { waitList.add(this) } - searchItem.guards.add(guard) - searchItem.lastEvents.addAll(last) - searchItem.lastWrites.add(lastWrites) - searchItem.threadLookups.add(threadLookup) - searchItem.atomics.add(atomicEntered) - searchItem.incoming++ - if (searchItem.incoming == searchItem.loc.incomingEdges.size) { - waitList.remove(searchItem) - toVisit.add(searchItem) + is HavocStmt<*> -> { + last = newEvent(stmt.varDecl, EventType.WRITE) } - } - if (current.loc.outgoingEdges.size > 1) { - for (e in current.loc.outgoingEdges) { - val first = e.getFlatLabels().first() - if (first !is StmtLabel || first.stmt !is AssumeStmt) { - error("Branching with non-assume labels not supported by OC checker.") - } - } - assumeConsts.forEach { (_, set) -> - for ((i1, v1) in set.withIndex()) - for ((i2, v2) in set.withIndex()) { - if (i1 == i2) break - branchingConditions.add(Eq(v1.ref, v2.ref)) - } - } + else -> error("Unsupported statement type: $stmt") + } } - } - if (waitList.isNotEmpty()) error("Loops and dangling edges not supported by OC checker.") - return threads - } + is StartLabel -> { + // TODO StartLabel params + if (label.name in thread.startHistory) { + error("Recursive thread start not supported by OC checker.") + } + val procedure = + xcfa.procedures.find { it.name == label.name } + ?: error("Procedure not found: ${label.name}") + last = newEvent(label.pidVar, EventType.WRITE) + val pidVar = label.pidVar.threadVar(pid) + if (this.threads.any { it.pidVar == pidVar }) { + error( + "Using a pthread_t variable in multiple threads is not supported by OC checker." + ) + } + val newHistory = thread.startHistory + thread.procedure.name + val newThread = Thread(procedure, guard, pidVar, last.first(), newHistory, lastWrites) + last.first().assignment = Eq(last.first().const.ref, Int(newThread.pid)) + threadLookup[pidVar] = setOf(Pair(guard, newThread)) + processThread(newThread) + } - private fun addCrossThreadRelations() { - for ((_, map) in events) - for ((pid1, list1) in map) - for ((pid2, list2) in map) - if (pid1 != pid2) - for (e1 in list1.filter { it.type == EventType.WRITE }) - for (e2 in list2.filter { it.type == EventType.READ }) - rfs.add(RelationType.RF, e1, e2) - } + is JoinLabel -> { + val incomingGuard = guard + val lastEvents = mutableListOf() + val joinGuards = mutableListOf>>() + threadLookup[label.pidVar.threadVar(pid)]?.forEach { (g, thread) -> + guard = incomingGuard + g + thread.finalEvents.map { it.guard }.toOrInSet() + val joinEvent = newEvent(label.pidVar, EventType.READ).first() + thread.finalEvents.forEach { final -> po(final, joinEvent) } + lastEvents.add(joinEvent) + joinGuards.add(guard) + } ?: error("Thread started in a different thread: not supported by OC checker.") + guard = joinGuards.toOrInSet() + last = lastEvents + } - private fun addToSolver(): Boolean { - if (violations.isEmpty()) return false + is FenceLabel -> { + if (label.labels.size > 1 || label.labels.firstOrNull()?.contains("ATOMIC") != true) { + error("Untransformed fence label: $label") + } + if (label.isAtomicBegin) atomicEntered = false + if (label.isAtomicEnd) atomicEntered = null + } - // Value assignment - events.values.flatMap { it.values.flatten() }.filter { it.assignment != null }.forEach { event -> - if (event.guard.isEmpty()) solver.add(event.assignment) - else solver.add(Imply(event.guardExpr, event.assignment)) + is NopLabel -> {} + else -> error("Unsupported label type by OC checker: $label") + } + firstLabel = false } - // Branching conditions - branchingConditions.forEach { solver.add(it) } - - // Property violation - solver.add(Or(violations.map { it.guard })) - - // RF - rfs.forEach { (_, list) -> - list.groupBy { it.to }.forEach { (event, rels) -> - rels.forEach { rel -> - solver.add( - Imply( - rel.declRef, - And(rel.from.guardExpr, rel.to.guardExpr, Eq(rel.from.const.ref, rel.to.const.ref)) - ) - ) // RF-Val - } - solver.add(Imply(event.guardExpr, Or(rels.map { it.declRef }))) // RF-Some - } + val searchItem = + waitList.find { it.loc == edge.target } + ?: SearchItem(edge.target).apply { waitList.add(this) } + searchItem.guards.add(guard) + searchItem.lastEvents.addAll(last) + searchItem.lastWrites.add(lastWrites) + searchItem.threadLookups.add(threadLookup) + searchItem.atomics.add(atomicEntered) + searchItem.incoming++ + if (searchItem.incoming == searchItem.loc.incomingEdges.size) { + waitList.remove(searchItem) + toVisit.add(searchItem) } - - return true - } - - // Utility functions - - private fun po(from: E?, to: E) { - from ?: return - pos.add(Relation(RelationType.PO, from, to)) + } + + if (current.loc.outgoingEdges.size > 1) { + for (e in current.loc.outgoingEdges) { + val first = e.getFlatLabels().first() + if (first !is StmtLabel || first.stmt !is AssumeStmt) { + error("Branching with non-assume labels not supported by OC checker.") + } + } + assumeConsts.forEach { (_, set) -> + for ((i1, v1) in set.withIndex()) for ((i2, v2) in set.withIndex()) { + if (i1 == i2) break + branchingConditions.add(Eq(v1.ref, v2.ref)) + } + } + } } - private fun List>>.merge(merge: (Set, Set) -> Set = { a, b -> a + b }) = - reduce(mapOf()) { acc, map -> - (acc.keys + map.keys).associateWith { k -> - val set1 = acc[k] ?: setOf() - val set2 = map[k] ?: setOf() - merge(set1, set2) - } + if (waitList.isNotEmpty()) error("Loops and dangling edges not supported by OC checker.") + return threads + } + + private fun addCrossThreadRelations() { + for ((_, map) in events) for ((pid1, list1) in map) for ((pid2, list2) in map) if (pid1 != pid2) + for (e1 in list1.filter { it.type == EventType.WRITE }) for (e2 in + list2.filter { it.type == EventType.READ }) rfs.add(RelationType.RF, e1, e2) + } + + private fun addToSolver(): Boolean { + if (violations.isEmpty()) return false + + // Value assignment + events.values + .flatMap { it.values.flatten() } + .filter { it.assignment != null } + .forEach { event -> + if (event.guard.isEmpty()) solver.add(event.assignment) + else solver.add(Imply(event.guardExpr, event.assignment)) + } + + // Branching conditions + branchingConditions.forEach { solver.add(it) } + + // Property violation + solver.add(Or(violations.map { it.guard })) + + // RF + rfs.forEach { (_, list) -> + list + .groupBy { it.to } + .forEach { (event, rels) -> + rels.forEach { rel -> + solver.add( + Imply( + rel.declRef, + And(rel.from.guardExpr, rel.to.guardExpr, Eq(rel.from.const.ref, rel.to.const.ref)), + ) + ) // RF-Val + } + solver.add(Imply(event.guardExpr, Or(rels.map { it.declRef }))) // RF-Some } + } - private inline fun Collection.reduce(default: T, operation: (T, T) -> T): T = - if (isEmpty()) default else reduce(operation) + return true + } + + // Utility functions + + private fun po(from: E?, to: E) { + from ?: return + pos.add(Relation(RelationType.PO, from, to)) + } + + private fun List>>.merge( + merge: (Set, Set) -> Set = { a, b -> a + b } + ) = + reduce(mapOf()) { acc, map -> + (acc.keys + map.keys).associateWith { k -> + val set1 = acc[k] ?: setOf() + val set2 = map[k] ?: setOf() + merge(set1, set2) + } + } - private fun MutableMap, MutableSet>.add(type: RelationType, from: E, to: E) = - getOrPut(from.const.varDecl) { mutableSetOf() }.add(Relation(type, from, to)) + private inline fun Collection.reduce(default: T, operation: (T, T) -> T): T = + if (isEmpty()) default else reduce(operation) - private fun Expr.withConsts(varToConst: Map, ConstDecl<*>>): Expr { - if (this is RefExpr) { - return varToConst[decl]?.ref?.let { cast(it, type) } ?: this - } - return map { it.withConsts(varToConst) } - } + private fun MutableMap, MutableSet>.add(type: RelationType, from: E, to: E) = + getOrPut(from.const.varDecl) { mutableSetOf() }.add(Relation(type, from, to)) - private fun VarDecl.threadVar(pid: Int): VarDecl = - if (xcfa.vars.none { it.wrappedVar == this && !it.threadLocal }) { // if not global var - cast(localVars.getOrPut(this) { mutableMapOf() }.getOrPut(pid) { - Decls.Var("t$pid::$name", type) - }, type) - } else this - - private fun VarDecl.getNewIndexed(increment: Boolean = true): IndexedConstDecl { - val constDecl = getConstDecl(indexing.get(this)) - if (increment) indexing = indexing.inc(this) - return constDecl + private fun Expr.withConsts(varToConst: Map, ConstDecl<*>>): Expr { + if (this is RefExpr) { + return varToConst[decl]?.ref?.let { cast(it, type) } ?: this } + return map { it.withConsts(varToConst) } + } + + private fun VarDecl.threadVar(pid: Int): VarDecl = + if (xcfa.vars.none { it.wrappedVar == this && !it.threadLocal }) { // if not global var + cast( + localVars + .getOrPut(this) { mutableMapOf() } + .getOrPut(pid) { Decls.Var("t$pid::$name", type) }, + type, + ) + } else this + + private fun VarDecl.getNewIndexed(increment: Boolean = true): IndexedConstDecl { + val constDecl = getConstDecl(indexing.get(this)) + if (increment) indexing = indexing.inc(this) + return constDecl + } } diff --git a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaExplAnalysisTest.kt b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaExplAnalysisTest.kt index b4aa758303..a04508f9cf 100644 --- a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaExplAnalysisTest.kt +++ b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaExplAnalysisTest.kt @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis -import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators import hu.bme.mit.theta.analysis.algorithm.SafetyResult -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker -import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner +import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgCegarChecker +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgRefiner import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterions import hu.bme.mit.theta.analysis.expl.* import hu.bme.mit.theta.analysis.expr.refinement.* @@ -39,255 +38,309 @@ import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory import hu.bme.mit.theta.xcfa.analysis.coi.ConeOfInfluence import hu.bme.mit.theta.xcfa.analysis.coi.XcfaCoiMultiThread import hu.bme.mit.theta.xcfa.analysis.por.* +import java.util.* +import kotlin.random.Random import org.junit.jupiter.api.Assertions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource -import java.util.* -import kotlin.random.Random - class XcfaExplAnalysisTest { + companion object { - companion object { - - private val seed = 1001 - - @JvmStatic - fun data(): Collection> { - return listOf( - arrayOf("/00assignment.c", SafetyResult<*, *>::isUnsafe), - arrayOf("/01function.c", SafetyResult<*, *>::isUnsafe), - arrayOf("/02functionparam.c", SafetyResult<*, *>::isSafe), - arrayOf("/03nondetfunction.c", SafetyResult<*, *>::isUnsafe), - arrayOf("/04multithread.c", SafetyResult<*, *>::isUnsafe), - ) - } - } - - @ParameterizedTest - @MethodSource("data") - fun testNoporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - println("Testing NOPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val analysis = ExplXcfaAnalysis( - xcfa, - Z3LegacySolverFactory.getInstance().createSolver(), - 1, - getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd()), false - ) - - val lts = getXcfaLts() - - val abstractor = getXcfaAbstractor(analysis, - PriorityWaitlist.create( - ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner>, ExplPrec, ItpRefutation>( - ItpRefToPtrPrec(ItpRefToExplPrec())) - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, refiner) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) - - - - Assertions.assertTrue(verdict(safetyResult)) - } - - @ParameterizedTest - @MethodSource("data") - fun testSporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - println("Testing SPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val analysis = ExplXcfaAnalysis( - xcfa, - Z3LegacySolverFactory.getInstance().createSolver(), - 1, - getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd()), false - ) - - val lts = XcfaSporLts(xcfa) - - val abstractor = getXcfaAbstractor(analysis, - PriorityWaitlist.create( - ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner>, ExplPrec, ItpRefutation>( - ItpRefToPtrPrec(ItpRefToExplPrec())) - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, refiner) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) - - - - Assertions.assertTrue(verdict(safetyResult)) - } - - @ParameterizedTest - @MethodSource("data") - fun testDporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - XcfaDporLts.random = Random(seed) - println("Testing DPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val analysis = ExplXcfaAnalysis( - xcfa, - Z3LegacySolverFactory.getInstance().createSolver(), - 1, - XcfaDporLts.getPartialOrder(getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd())), false - ) - - val lts = XcfaDporLts(xcfa) - - val abstractor = getXcfaAbstractor(analysis, - lts.waitlist, - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner>, ExplPrec, ItpRefutation>( - ItpRefToPtrPrec(ItpRefToExplPrec())) - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, - ConsoleLogger( - Logger.Level.DETAIL)) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, refiner) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) - - - - Assertions.assertTrue(verdict(safetyResult)) - } - - @ParameterizedTest - @MethodSource("data") - fun testAasporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - println("Testing AASPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val analysis = ExplXcfaAnalysis( - xcfa, - Z3LegacySolverFactory.getInstance().createSolver(), - 1, - getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd()), false - ) - - val lts = XcfaAasporLts(xcfa, mutableMapOf()) - - val abstractor = getXcfaAbstractor(analysis, - PriorityWaitlist.create( - ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner, ExplPrec, ItpRefutation>( - ItpRefToPtrPrec(ItpRefToExplPrec())) - val atomicNodePruner = AtomicNodePruner>, XcfaAction>() - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, NullLogger.getInstance(), - atomicNodePruner) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, AasporRefiner.create(refiner, PruneStrategy.FULL, mutableMapOf())) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) - - - - Assertions.assertTrue(verdict(safetyResult)) - } - - @ParameterizedTest - @MethodSource("data") - fun testAadporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - XcfaDporLts.random = Random(seed) - println("Testing AADPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val analysis = ExplXcfaAnalysis( - xcfa, - Z3LegacySolverFactory.getInstance().createSolver(), - 1, - XcfaDporLts.getPartialOrder(getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd())), false - ) - - val lts = XcfaAadporLts(xcfa) - - val abstractor = getXcfaAbstractor(analysis, - lts.waitlist, - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner(ItpRefToPtrPrec(ItpRefToExplPrec())) - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, refiner) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) - - + private val seed = 1001 - Assertions.assertTrue(verdict(safetyResult)) + @JvmStatic + fun data(): Collection> { + return listOf( + arrayOf("/00assignment.c", SafetyResult<*, *>::isUnsafe), + arrayOf("/01function.c", SafetyResult<*, *>::isUnsafe), + arrayOf("/02functionparam.c", SafetyResult<*, *>::isSafe), + arrayOf("/03nondetfunction.c", SafetyResult<*, *>::isUnsafe), + arrayOf("/04multithread.c", SafetyResult<*, *>::isUnsafe), + ) } -} \ No newline at end of file + } + + @ParameterizedTest + @MethodSource("data") + fun testNoporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + println("Testing NOPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val analysis = + ExplXcfaAnalysis( + xcfa, + Z3LegacySolverFactory.getInstance().createSolver(), + 1, + getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd()), + false, + ) + + val lts = getXcfaLts() + + val abstractor = + getXcfaAbstractor( + analysis, + PriorityWaitlist.create( + ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs()) + ), + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner>, ExplPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToExplPrec()) + ) + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + NullLogger.getInstance(), + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = ArgCegarChecker.create(abstractor, refiner) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } + + @ParameterizedTest + @MethodSource("data") + fun testSporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + println("Testing SPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val analysis = + ExplXcfaAnalysis( + xcfa, + Z3LegacySolverFactory.getInstance().createSolver(), + 1, + getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd()), + false, + ) + + val lts = XcfaSporLts(xcfa) + + val abstractor = + getXcfaAbstractor( + analysis, + PriorityWaitlist.create( + ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs()) + ), + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner>, ExplPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToExplPrec()) + ) + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + NullLogger.getInstance(), + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = ArgCegarChecker.create(abstractor, refiner) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } + + @ParameterizedTest + @MethodSource("data") + fun testDporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + XcfaDporLts.random = Random(seed) + println("Testing DPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val analysis = + ExplXcfaAnalysis( + xcfa, + Z3LegacySolverFactory.getInstance().createSolver(), + 1, + XcfaDporLts.getPartialOrder(getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd())), + false, + ) + + val lts = XcfaDporLts(xcfa) + + val abstractor = + getXcfaAbstractor( + analysis, + lts.waitlist, + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner>, ExplPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToExplPrec()) + ) + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + ConsoleLogger(Logger.Level.DETAIL), + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = ArgCegarChecker.create(abstractor, refiner) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } + + @ParameterizedTest + @MethodSource("data") + fun testAasporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + println("Testing AASPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val analysis = + ExplXcfaAnalysis( + xcfa, + Z3LegacySolverFactory.getInstance().createSolver(), + 1, + getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd()), + false, + ) + + val lts = XcfaAasporLts(xcfa, mutableMapOf()) + + val abstractor = + getXcfaAbstractor( + analysis, + PriorityWaitlist.create( + ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs()) + ), + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner, ExplPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToExplPrec()) + ) + val atomicNodePruner = AtomicNodePruner>, XcfaAction>() + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + NullLogger.getInstance(), + atomicNodePruner, + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = + ArgCegarChecker.create( + abstractor, + AasporRefiner.create(refiner, PruneStrategy.FULL, mutableMapOf()), + ) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } + + @ParameterizedTest + @MethodSource("data") + fun testAadporExpl(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + XcfaDporLts.random = Random(seed) + println("Testing AADPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val analysis = + ExplXcfaAnalysis( + xcfa, + Z3LegacySolverFactory.getInstance().createSolver(), + 1, + XcfaDporLts.getPartialOrder(getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd())), + false, + ) + + val lts = XcfaAadporLts(xcfa) + + val abstractor = + getXcfaAbstractor( + analysis, + lts.waitlist, + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner(ItpRefToPtrPrec(ItpRefToExplPrec())) + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + NullLogger.getInstance(), + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = ArgCegarChecker.create(abstractor, refiner) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } +} diff --git a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaPredAnalysisTest.kt b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaPredAnalysisTest.kt index e398b5ed07..a10e82f096 100644 --- a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaPredAnalysisTest.kt +++ b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaPredAnalysisTest.kt @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis -import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators import hu.bme.mit.theta.analysis.algorithm.SafetyResult -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker -import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner +import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgCegarChecker +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgRefiner import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterions import hu.bme.mit.theta.analysis.expr.refinement.* import hu.bme.mit.theta.analysis.pred.* @@ -39,262 +38,316 @@ import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory import hu.bme.mit.theta.xcfa.analysis.coi.ConeOfInfluence import hu.bme.mit.theta.xcfa.analysis.coi.XcfaCoiMultiThread import hu.bme.mit.theta.xcfa.analysis.por.* +import java.util.* +import kotlin.random.Random import org.junit.jupiter.api.Assertions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource -import java.util.* -import kotlin.random.Random - class XcfaPredAnalysisTest { + companion object { - companion object { - - private val seed = 1001; - - @JvmStatic - fun data(): Collection> { - return listOf( - arrayOf("/00assignment.c", SafetyResult<*, *>::isUnsafe), - arrayOf("/01function.c", SafetyResult<*, *>::isUnsafe), - arrayOf("/02functionparam.c", SafetyResult<*, *>::isSafe), - arrayOf("/03nondetfunction.c", SafetyResult<*, *>::isUnsafe), - arrayOf("/04multithread.c", SafetyResult<*, *>::isUnsafe), - ) - } - } - - @ParameterizedTest - @MethodSource("data") - fun testNoporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - println("Testing NOPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val solver = Z3LegacySolverFactory.getInstance().createSolver() - val analysis = PredXcfaAnalysis( - xcfa, - solver, - PredAbstractors.cartesianAbstractor(solver), - getPartialOrder(PredOrd.create(solver).getPtrPartialOrd()), - false - ) - - val lts = getXcfaLts() - - val abstractor = getXcfaAbstractor(analysis, - PriorityWaitlist.create( - ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner>, PredPrec, ItpRefutation>( - ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, refiner) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) - - - - Assertions.assertTrue(verdict(safetyResult)) - } - - @ParameterizedTest - @MethodSource("data") - fun testSporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - println("Testing SPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val solver = Z3LegacySolverFactory.getInstance().createSolver() - val analysis = PredXcfaAnalysis( - xcfa, - solver, - PredAbstractors.cartesianAbstractor(solver), - getPartialOrder(PredOrd.create(solver).getPtrPartialOrd()), false - ) - - val lts = XcfaSporLts(xcfa) - - val abstractor = getXcfaAbstractor(analysis, - PriorityWaitlist.create( - ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner>, PredPrec, ItpRefutation>( - ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, refiner) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) - - - - Assertions.assertTrue(verdict(safetyResult)) - } - - @ParameterizedTest - @MethodSource("data") - fun testDporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - XcfaDporLts.random = Random(seed) - println("Testing DPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val solver = Z3LegacySolverFactory.getInstance().createSolver() - val analysis = PredXcfaAnalysis( - xcfa, - solver, - PredAbstractors.cartesianAbstractor(solver), - XcfaDporLts.getPartialOrder(getPartialOrder(PredOrd.create(solver).getPtrPartialOrd())), false - ) - - val lts = XcfaDporLts(xcfa) - - val abstractor = getXcfaAbstractor(analysis, - lts.waitlist, - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner>, PredPrec, ItpRefutation>( - ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, - ConsoleLogger( - Logger.Level.DETAIL)) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, refiner) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) - - - - Assertions.assertTrue(verdict(safetyResult)) - } - - @ParameterizedTest - @MethodSource("data") - fun testAasporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - println("Testing AASPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val solver = Z3LegacySolverFactory.getInstance().createSolver() - val analysis = PredXcfaAnalysis( - xcfa, - solver, - PredAbstractors.cartesianAbstractor(solver), - getPartialOrder(PredOrd.create(solver).getPtrPartialOrd()), false - ) - - val lts = XcfaAasporLts(xcfa, mutableMapOf()) - - val abstractor = getXcfaAbstractor(analysis, - PriorityWaitlist.create( - ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner, PredPrec, ItpRefutation>( - ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) - val atomicNodePruner = AtomicNodePruner>, XcfaAction>() - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, NullLogger.getInstance(), - atomicNodePruner) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, AasporRefiner.create(refiner, PruneStrategy.FULL, mutableMapOf())) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) - - - - Assertions.assertTrue(verdict(safetyResult)) - } - - @ParameterizedTest - @MethodSource("data") - fun testAadporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { - XcfaDporLts.random = Random(seed) - println("Testing AADPOR on $filepath...") - val stream = javaClass.getResourceAsStream(filepath) - val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first - ConeOfInfluence = XcfaCoiMultiThread(xcfa) - - val solver = Z3LegacySolverFactory.getInstance().createSolver() - val analysis = PredXcfaAnalysis( - xcfa, - solver, - PredAbstractors.cartesianAbstractor(solver), - XcfaDporLts.getPartialOrder(getPartialOrder(PredOrd.create(solver).getPtrPartialOrd())), false - ) - - val lts = XcfaAadporLts(xcfa) - - val abstractor = getXcfaAbstractor(analysis, - lts.waitlist, - StopCriterions.firstCex>, XcfaAction>(), - ConsoleLogger(Logger.Level.DETAIL), - lts, - ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - - val precRefiner = XcfaPrecRefiner( - ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) - - val refiner = - XcfaSingleExprTraceRefiner.create( - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), - Z3LegacySolverFactory.getInstance().createItpSolver()), - precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> - - val cegarChecker = - CegarChecker.create(abstractor, refiner) - - val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) - - + private val seed = 1001 - Assertions.assertTrue(verdict(safetyResult)) + @JvmStatic + fun data(): Collection> { + return listOf( + arrayOf("/00assignment.c", SafetyResult<*, *>::isUnsafe), + arrayOf("/01function.c", SafetyResult<*, *>::isUnsafe), + arrayOf("/02functionparam.c", SafetyResult<*, *>::isSafe), + arrayOf("/03nondetfunction.c", SafetyResult<*, *>::isUnsafe), + arrayOf("/04multithread.c", SafetyResult<*, *>::isUnsafe), + ) } -} \ No newline at end of file + } + + @ParameterizedTest + @MethodSource("data") + fun testNoporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + println("Testing NOPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val solver = Z3LegacySolverFactory.getInstance().createSolver() + val analysis = + PredXcfaAnalysis( + xcfa, + solver, + PredAbstractors.cartesianAbstractor(solver), + getPartialOrder(PredOrd.create(solver).getPtrPartialOrd()), + false, + ) + + val lts = getXcfaLts() + + val abstractor = + getXcfaAbstractor( + analysis, + PriorityWaitlist.create( + ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs()) + ), + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner>, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole())) + ) + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + NullLogger.getInstance(), + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = ArgCegarChecker.create(abstractor, refiner) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } + + @ParameterizedTest + @MethodSource("data") + fun testSporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + println("Testing SPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val solver = Z3LegacySolverFactory.getInstance().createSolver() + val analysis = + PredXcfaAnalysis( + xcfa, + solver, + PredAbstractors.cartesianAbstractor(solver), + getPartialOrder(PredOrd.create(solver).getPtrPartialOrd()), + false, + ) + + val lts = XcfaSporLts(xcfa) + + val abstractor = + getXcfaAbstractor( + analysis, + PriorityWaitlist.create( + ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs()) + ), + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner>, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole())) + ) + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + NullLogger.getInstance(), + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = ArgCegarChecker.create(abstractor, refiner) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } + + @ParameterizedTest + @MethodSource("data") + fun testDporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + XcfaDporLts.random = Random(seed) + println("Testing DPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val solver = Z3LegacySolverFactory.getInstance().createSolver() + val analysis = + PredXcfaAnalysis( + xcfa, + solver, + PredAbstractors.cartesianAbstractor(solver), + XcfaDporLts.getPartialOrder(getPartialOrder(PredOrd.create(solver).getPtrPartialOrd())), + false, + ) + + val lts = XcfaDporLts(xcfa) + + val abstractor = + getXcfaAbstractor( + analysis, + lts.waitlist, + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner>, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole())) + ) + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + ConsoleLogger(Logger.Level.DETAIL), + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = ArgCegarChecker.create(abstractor, refiner) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } + + @ParameterizedTest + @MethodSource("data") + fun testAasporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + println("Testing AASPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val solver = Z3LegacySolverFactory.getInstance().createSolver() + val analysis = + PredXcfaAnalysis( + xcfa, + solver, + PredAbstractors.cartesianAbstractor(solver), + getPartialOrder(PredOrd.create(solver).getPtrPartialOrd()), + false, + ) + + val lts = XcfaAasporLts(xcfa, mutableMapOf()) + + val abstractor = + getXcfaAbstractor( + analysis, + PriorityWaitlist.create( + ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs()) + ), + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole())) + ) + val atomicNodePruner = AtomicNodePruner>, XcfaAction>() + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + NullLogger.getInstance(), + atomicNodePruner, + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = + ArgCegarChecker.create( + abstractor, + AasporRefiner.create(refiner, PruneStrategy.FULL, mutableMapOf()), + ) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } + + @ParameterizedTest + @MethodSource("data") + fun testAadporPred(filepath: String, verdict: (SafetyResult<*, *>) -> Boolean) { + XcfaDporLts.random = Random(seed) + println("Testing AADPOR on $filepath...") + val stream = javaClass.getResourceAsStream(filepath) + val xcfa = getXcfaFromC(stream!!, ParseContext(), false, false, NullLogger.getInstance()).first + ConeOfInfluence = XcfaCoiMultiThread(xcfa) + + val solver = Z3LegacySolverFactory.getInstance().createSolver() + val analysis = + PredXcfaAnalysis( + xcfa, + solver, + PredAbstractors.cartesianAbstractor(solver), + XcfaDporLts.getPartialOrder(getPartialOrder(PredOrd.create(solver).getPtrPartialOrd())), + false, + ) + + val lts = XcfaAadporLts(xcfa) + + val abstractor = + getXcfaAbstractor( + analysis, + lts.waitlist, + StopCriterions.firstCex>, XcfaAction>(), + ConsoleLogger(Logger.Level.DETAIL), + lts, + ErrorDetection.ERROR_LOCATION, + ) + as ArgAbstractor>, XcfaAction, XcfaPrec>> + + val precRefiner = + XcfaPrecRefiner( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole())) + ) + + val refiner = + XcfaSingleExprTraceRefiner.create( + ExprTraceBwBinItpChecker.create( + BoolExprs.True(), + BoolExprs.True(), + Z3LegacySolverFactory.getInstance().createItpSolver(), + ), + precRefiner, + PruneStrategy.FULL, + NullLogger.getInstance(), + ) as ArgRefiner>, XcfaAction, XcfaPrec>> + + val cegarChecker = ArgCegarChecker.create(abstractor, refiner) + + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) + + Assertions.assertTrue(verdict(safetyResult)) + } +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt index d54865638a..c7027914fc 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli import com.google.common.base.Stopwatch @@ -23,7 +22,7 @@ import hu.bme.mit.theta.analysis.Action import hu.bme.mit.theta.analysis.EmptyCex import hu.bme.mit.theta.analysis.State import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness +import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.algorithm.arg.ARG import hu.bme.mit.theta.analysis.algorithm.arg.debug.ARGWebDebugger @@ -68,318 +67,379 @@ import java.util.concurrent.TimeUnit import kotlin.random.Random fun runConfig( - config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger, - throwDontExit: Boolean + config: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, + throwDontExit: Boolean, ): SafetyResult<*, *> { - propagateInputOptions(config, logger, uniqueLogger) + propagateInputOptions(config, logger, uniqueLogger) - registerAllSolverManagers(config.backendConfig.solverHome, logger) + registerAllSolverManagers(config.backendConfig.solverHome, logger) - validateInputOptions(config, logger, uniqueLogger) + validateInputOptions(config, logger, uniqueLogger) - val (xcfa, mcm, parseContext) = frontend(config, logger, uniqueLogger) + val (xcfa, mcm, parseContext) = frontend(config, logger, uniqueLogger) - preVerificationLogging(xcfa, mcm, parseContext, config, logger, uniqueLogger) + preVerificationLogging(xcfa, mcm, parseContext, config, logger, uniqueLogger) - val result = backend(xcfa, mcm, parseContext, config, logger, uniqueLogger, throwDontExit) + val result = backend(xcfa, mcm, parseContext, config, logger, uniqueLogger, throwDontExit) - postVerificationLogging(result, mcm, parseContext, config, logger, uniqueLogger) + postVerificationLogging(result, mcm, parseContext, config, logger, uniqueLogger) - return result + return result } - private fun propagateInputOptions(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger) { - config.inputConfig.property = determineProperty(config, logger) - LbePass.level = config.frontendConfig.lbeLevel - StaticCoiPass.enabled = config.frontendConfig.staticCoi - if (config.backendConfig.backend == Backend.CEGAR) { - val cegarConfig = config.backendConfig.specConfig - cegarConfig as CegarConfig - val random = Random(cegarConfig.porRandomSeed) - XcfaSporLts.random = random - XcfaDporLts.random = random - } - if (config.debugConfig.argToFile) { - WebDebuggerLogger.enableWebDebuggerLogger() - WebDebuggerLogger.getInstance().setTitle(config.inputConfig.input?.name) - } - - LoopUnrollPass.UNROLL_LIMIT = config.frontendConfig.loopUnroll - LoopUnrollPass.FORCE_UNROLL_LIMIT = config.frontendConfig.forceUnroll - FetchExecuteWriteback.enabled = config.frontendConfig.enableFew - ARGWebDebugger.on = config.debugConfig.argdebug + config.inputConfig.property = determineProperty(config, logger) + LbePass.level = config.frontendConfig.lbeLevel + StaticCoiPass.enabled = config.frontendConfig.staticCoi + if (config.backendConfig.backend == Backend.CEGAR) { + val cegarConfig = config.backendConfig.specConfig + cegarConfig as CegarConfig + val random = Random(cegarConfig.porRandomSeed) + XcfaSporLts.random = random + XcfaDporLts.random = random + } + if (config.debugConfig.argToFile) { + WebDebuggerLogger.enableWebDebuggerLogger() + WebDebuggerLogger.getInstance().setTitle(config.inputConfig.input?.name) + } + + LoopUnrollPass.UNROLL_LIMIT = config.frontendConfig.loopUnroll + LoopUnrollPass.FORCE_UNROLL_LIMIT = config.frontendConfig.forceUnroll + FetchExecuteWriteback.enabled = config.frontendConfig.enableFew + ARGWebDebugger.on = config.debugConfig.argdebug } private fun validateInputOptions(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger) { - rule("NoCoiWhenDataRace") { - config.backendConfig.backend == Backend.CEGAR && - (config.backendConfig.specConfig as? CegarConfig)?.coi != ConeOfInfluenceMode.NO_COI && - config.inputConfig.property == ErrorDetection.DATA_RACE - } - rule("NoAaporWhenDataRace") { - (config.backendConfig.specConfig as? CegarConfig)?.porLevel?.isAbstractionAware == true && - config.inputConfig.property == ErrorDetection.DATA_RACE - } - rule("DPORWithoutDFS") { - (config.backendConfig.specConfig as? CegarConfig)?.porLevel?.isDynamic == true && - (config.backendConfig.specConfig as? CegarConfig)?.abstractorConfig?.search != Search.DFS - } - rule("SensibleLoopUnrollLimits") { - config.frontendConfig.loopUnroll != -1 && config.frontendConfig.loopUnroll < config.frontendConfig.forceUnroll - } - rule("NoPredSplitUntilFixed(https://github.com/ftsrg/theta/issues/267)") { - (config.backendConfig.specConfig as? CegarConfig)?.abstractorConfig?.domain == Domain.PRED_SPLIT - } + rule("NoCoiWhenDataRace") { + config.backendConfig.backend == Backend.CEGAR && + (config.backendConfig.specConfig as? CegarConfig)?.coi != ConeOfInfluenceMode.NO_COI && + config.inputConfig.property == ErrorDetection.DATA_RACE + } + rule("NoAaporWhenDataRace") { + (config.backendConfig.specConfig as? CegarConfig)?.porLevel?.isAbstractionAware == true && + config.inputConfig.property == ErrorDetection.DATA_RACE + } + rule("DPORWithoutDFS") { + (config.backendConfig.specConfig as? CegarConfig)?.porLevel?.isDynamic == true && + (config.backendConfig.specConfig as? CegarConfig)?.abstractorConfig?.search != Search.DFS + } + rule("SensibleLoopUnrollLimits") { + config.frontendConfig.loopUnroll != -1 && + config.frontendConfig.loopUnroll < config.frontendConfig.forceUnroll + } + rule("NoPredSplitUntilFixed(https://github.com/ftsrg/theta/issues/267)") { + (config.backendConfig.specConfig as? CegarConfig)?.abstractorConfig?.domain == Domain.PRED_SPLIT + } } -fun frontend(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger): Triple { - if (config.inputConfig.xcfaWCtx != null) { - val xcfa = config.inputConfig.xcfaWCtx!!.first - ConeOfInfluence = if (config.inputConfig.xcfaWCtx!!.third.multiThreading) { - XcfaCoiMultiThread(xcfa) - } else { - XcfaCoiSingleThread(xcfa) - } - return config.inputConfig.xcfaWCtx!! - } - - val stopwatch = Stopwatch.createStarted() - - val input = config.inputConfig.input!! - logger.write(Logger.Level.INFO, "Parsing the input $input as ${config.frontendConfig.inputType}\n") - - val parseContext = ParseContext() - - if (config.frontendConfig.inputType == InputType.C) { - val cConfig = config.frontendConfig.specConfig - cConfig as CFrontendConfig - parseContext.arithmetic = cConfig.arithmetic - parseContext.architecture = cConfig.architecture - } - - val xcfa = getXcfa(config, parseContext, logger, uniqueLogger) - - val mcm = if (config.inputConfig.catFile != null) { - CatDslManager.createMCM(config.inputConfig.catFile!!) +fun frontend( + config: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, +): Triple { + if (config.inputConfig.xcfaWCtx != null) { + val xcfa = config.inputConfig.xcfaWCtx!!.first + ConeOfInfluence = + if (config.inputConfig.xcfaWCtx!!.third.multiThreading) { + XcfaCoiMultiThread(xcfa) + } else { + XcfaCoiSingleThread(xcfa) + } + return config.inputConfig.xcfaWCtx!! + } + + val stopwatch = Stopwatch.createStarted() + + val input = config.inputConfig.input!! + logger.write( + Logger.Level.INFO, + "Parsing the input $input as ${config.frontendConfig.inputType}\n", + ) + + val parseContext = ParseContext() + + if (config.frontendConfig.inputType == InputType.C) { + val cConfig = config.frontendConfig.specConfig + cConfig as CFrontendConfig + parseContext.arithmetic = cConfig.arithmetic + parseContext.architecture = cConfig.architecture + } + + val xcfa = getXcfa(config, parseContext, logger, uniqueLogger) + + val mcm = + if (config.inputConfig.catFile != null) { + CatDslManager.createMCM(config.inputConfig.catFile!!) } else { - emptySet() - } - - ConeOfInfluence = if (parseContext.multiThreading) XcfaCoiMultiThread(xcfa) else XcfaCoiSingleThread(xcfa) - - if (parseContext.multiThreading && (config.backendConfig.specConfig as? CegarConfig)?.let { it.abstractorConfig.search == Search.ERR } == true) { - val cConfig = config.backendConfig.specConfig as CegarConfig - cConfig.abstractorConfig.search = Search.DFS - uniqueLogger.write(INFO, "Multithreaded program found, using DFS instead of ERR.") + emptySet() } - logger.write( - Logger.Level.INFO, "Frontend finished: ${xcfa.name} (in ${ + ConeOfInfluence = + if (parseContext.multiThreading) XcfaCoiMultiThread(xcfa) else XcfaCoiSingleThread(xcfa) + + if ( + parseContext.multiThreading && + (config.backendConfig.specConfig as? CegarConfig)?.let { + it.abstractorConfig.search == Search.ERR + } == true + ) { + val cConfig = config.backendConfig.specConfig as CegarConfig + cConfig.abstractorConfig.search = Search.DFS + uniqueLogger.write(INFO, "Multithreaded program found, using DFS instead of ERR.") + } + + logger.write( + Logger.Level.INFO, + "Frontend finished: ${xcfa.name} (in ${ stopwatch.elapsed(TimeUnit.MILLISECONDS) - } ms)\n" - ) + } ms)\n", + ) - logger.write(RESULT, "ParsingResult Success\n") - logger.write(RESULT, - "Alias graph size: ${xcfa.pointsToGraph.size} -> ${xcfa.pointsToGraph.values.map { it.size }.toList()}\n") + logger.write(RESULT, "ParsingResult Success\n") + logger.write( + RESULT, + "Alias graph size: ${xcfa.pointsToGraph.size} -> ${xcfa.pointsToGraph.values.map { it.size }.toList()}\n", + ) - return Triple(xcfa, mcm, parseContext) + return Triple(xcfa, mcm, parseContext) } private fun backend( - xcfa: XCFA, mcm: MCM, parseContext: ParseContext, config: XcfaConfig<*, *>, - logger: Logger, - uniqueLogger: Logger, - throwDontExit: Boolean + xcfa: XCFA, + mcm: MCM, + parseContext: ParseContext, + config: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, + throwDontExit: Boolean, ): SafetyResult<*, *> = - if (config.backendConfig.backend == Backend.NONE) { - SafetyResult.unknown() + if (config.backendConfig.backend == Backend.NONE) { + SafetyResult.unknown() + } else { + if ( + xcfa.procedures.all { + it.errorLoc.isEmpty && config.inputConfig.property == ErrorDetection.ERROR_LOCATION + } + ) { + val result = SafetyResult.safe(EmptyProof.getInstance()) + logger.write(Logger.Level.INFO, "Input is trivially safe\n") + + logger.write(RESULT, result.toString() + "\n") + result } else { - if (xcfa.procedures.all { it.errorLoc.isEmpty && config.inputConfig.property == ErrorDetection.ERROR_LOCATION }) { - val result = SafetyResult.safe(EmptyWitness.getInstance()) - logger.write(Logger.Level.INFO, "Input is trivially safe\n") - - logger.write(RESULT, result.toString() + "\n") - result - } else { - val stopwatch = Stopwatch.createStarted() - - logger.write( - Logger.Level.INFO, - "Starting verification of ${if (xcfa.name == "") "UnnamedXcfa" else xcfa.name} using ${config.backendConfig.backend}\n" - ) - - val checker = getChecker(xcfa, mcm, config, parseContext, logger, uniqueLogger) - val result = exitOnError(config.debugConfig.stacktrace, config.debugConfig.debug || throwDontExit) { - checker.check() - }.let { result -> - when { - result.isSafe && LoopUnrollPass.FORCE_UNROLL_USED -> { // cannot report safe if force unroll was used - logger.write(RESULT, "Incomplete loop unroll used: safe result is unreliable.\n") - SafetyResult.unknown() - } - - else -> result - } + val stopwatch = Stopwatch.createStarted() + + logger.write( + Logger.Level.INFO, + "Starting verification of ${if (xcfa.name == "") "UnnamedXcfa" else xcfa.name} using ${config.backendConfig.backend}\n", + ) + + val checker = getChecker(xcfa, mcm, config, parseContext, logger, uniqueLogger) + val result = + exitOnError(config.debugConfig.stacktrace, config.debugConfig.debug || throwDontExit) { + checker.check() + } + .let { result -> + when { + result.isSafe && + LoopUnrollPass.FORCE_UNROLL_USED -> { // cannot report safe if force unroll was used + logger.write(RESULT, "Incomplete loop unroll used: safe result is unreliable.\n") + SafetyResult.unknown() + } + + else -> result } + } - logger.write( - Logger.Level.INFO, "Backend finished (in ${ + logger.write( + Logger.Level.INFO, + "Backend finished (in ${ stopwatch.elapsed(TimeUnit.MILLISECONDS) - } ms)\n" - ) + } ms)\n", + ) - logger.write(RESULT, result.toString() + "\n") - result - } + logger.write(RESULT, result.toString() + "\n") + result } + } private fun preVerificationLogging( - xcfa: XCFA, mcm: MCM, parseContext: ParseContext, config: XcfaConfig<*, *>, - logger: Logger, uniqueLogger: Logger + xcfa: XCFA, + mcm: MCM, + parseContext: ParseContext, + config: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, ) { - if (config.outputConfig.enableOutput) { - try { - val resultFolder = config.outputConfig.resultFolder - resultFolder.mkdirs() - - logger.write( - Logger.Level.INFO, - "Writing pre-verification artifacts to directory ${resultFolder.absolutePath}\n" - ) - - if (!config.outputConfig.chcOutputConfig.disable) { - xcfa.procedures.forEach { - try { - val chcFile = File(resultFolder, "xcfa-${it.name}.smt2") - chcFile.writeText(it.toSMT2CHC()) - } catch (e: Exception) { - logger.write(INFO, "Could not write CHC file: " + e.stackTraceToString()) - } - } - } + if (config.outputConfig.enableOutput) { + try { + val resultFolder = config.outputConfig.resultFolder + resultFolder.mkdirs() + + logger.write( + Logger.Level.INFO, + "Writing pre-verification artifacts to directory ${resultFolder.absolutePath}\n", + ) + + if (!config.outputConfig.chcOutputConfig.disable) { + xcfa.procedures.forEach { + try { + val chcFile = File(resultFolder, "xcfa-${it.name}.smt2") + chcFile.writeText(it.toSMT2CHC()) + } catch (e: Exception) { + logger.write(INFO, "Could not write CHC file: " + e.stackTraceToString()) + } + } + } - if (!config.outputConfig.xcfaOutputConfig.disable) { - val xcfaDotFile = File(resultFolder, "xcfa.dot") - xcfaDotFile.writeText(xcfa.toDot()) + if (!config.outputConfig.xcfaOutputConfig.disable) { + val xcfaDotFile = File(resultFolder, "xcfa.dot") + xcfaDotFile.writeText(xcfa.toDot()) - val xcfaJsonFile = File(resultFolder, "xcfa.json") - val uglyJson = getGson(xcfa).toJson(xcfa) - val create = GsonBuilder().setPrettyPrinting().create() - xcfaJsonFile.writeText(create.toJson(JsonParser.parseString(uglyJson))) - } + val xcfaJsonFile = File(resultFolder, "xcfa.json") + val uglyJson = getGson(xcfa).toJson(xcfa) + val create = GsonBuilder().setPrettyPrinting().create() + xcfaJsonFile.writeText(create.toJson(JsonParser.parseString(uglyJson))) + } - if (!config.outputConfig.cOutputConfig.disable) { - try { - val xcfaCFile = File(resultFolder, "xcfa.c") - xcfaCFile.writeText( - xcfa.toC( - parseContext, config.outputConfig.cOutputConfig.useArr, - config.outputConfig.cOutputConfig.useExArr, config.outputConfig.cOutputConfig.useRange - ) - ) - } catch (e: Throwable) { - logger.write(Logger.Level.VERBOSE, "Could not emit C file\n") - } - } + if (!config.outputConfig.cOutputConfig.disable) { + try { + val xcfaCFile = File(resultFolder, "xcfa.c") + xcfaCFile.writeText( + xcfa.toC( + parseContext, + config.outputConfig.cOutputConfig.useArr, + config.outputConfig.cOutputConfig.useExArr, + config.outputConfig.cOutputConfig.useRange, + ) + ) } catch (e: Throwable) { - logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n") + logger.write(Logger.Level.VERBOSE, "Could not emit C file\n") } + } + } catch (e: Throwable) { + logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n") } + } } private fun postVerificationLogging( - safetyResult: SafetyResult<*, *>, mcm: MCM, - parseContext: ParseContext, config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger + safetyResult: SafetyResult<*, *>, + mcm: MCM, + parseContext: ParseContext, + config: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, ) { - if (config.outputConfig.enableOutput) { - try { - // we only want to log the files if the current configuration is not --in-process or portfolio - if (config.backendConfig.inProcess || config.backendConfig.backend == Backend.PORTFOLIO) { - return - } - - val resultFolder = config.outputConfig.resultFolder - resultFolder.mkdirs() - - logger.write( - Logger.Level.INFO, - "Writing post-verification artifacts to directory ${resultFolder.absolutePath}\n" + if (config.outputConfig.enableOutput) { + try { + // we only want to log the files if the current configuration is not --in-process or portfolio + if (config.backendConfig.inProcess || config.backendConfig.backend == Backend.PORTFOLIO) { + return + } + + val resultFolder = config.outputConfig.resultFolder + resultFolder.mkdirs() + + logger.write( + Logger.Level.INFO, + "Writing post-verification artifacts to directory ${resultFolder.absolutePath}\n", + ) + + // TODO eliminate the need for the instanceof check + if ( + !config.outputConfig.argConfig.disable && safetyResult.proof is ARG? + ) { + val argFile = File(resultFolder, "arg-${safetyResult.isSafe}.dot") + val g: Graph = + ArgVisualizer.getDefault().visualize(safetyResult.proof as ARG?) + argFile.writeText(GraphvizWriter.getInstance().writeString(g)) + } + + if (!config.outputConfig.witnessConfig.disable) { + if ( + safetyResult.isUnsafe && + safetyResult.asUnsafe().cex != null && + !config.outputConfig.witnessConfig.svcomp + ) { + val concrTrace: Trace, XcfaAction> = + XcfaTraceConcretizer.concretize( + safetyResult.asUnsafe().cex as Trace>, XcfaAction>?, + getSolver( + config.outputConfig.witnessConfig.concretizerSolver, + config.outputConfig.witnessConfig.validateConcretizerSolver, + ), + parseContext, ) - // TODO eliminate the need for the instanceof check - if (!config.outputConfig.argConfig.disable && safetyResult.witness is ARG?) { - val argFile = File(resultFolder, "arg-${safetyResult.isSafe}.dot") - val g: Graph = ArgVisualizer.getDefault().visualize(safetyResult.witness as ARG?) - argFile.writeText(GraphvizWriter.getInstance().writeString(g)) - } - - if (!config.outputConfig.witnessConfig.disable) { - if (safetyResult.isUnsafe && safetyResult.asUnsafe().cex != null) { - val concrTrace: Trace, XcfaAction> = XcfaTraceConcretizer.concretize( - safetyResult.asUnsafe().cex as Trace>, XcfaAction>?, - getSolver( - config.outputConfig.witnessConfig.concretizerSolver, - config.outputConfig.witnessConfig.validateConcretizerSolver - ), - parseContext - ) - - val traceFile = File(resultFolder, "trace.dot") - val traceG: Graph = TraceVisualizer.getDefault().visualize(concrTrace) - traceFile.writeText(GraphvizWriter.getInstance().writeString(traceG)) - - val sequenceFile = File(resultFolder, "trace.plantuml") - writeSequenceTrace(sequenceFile, - safetyResult.asUnsafe().cex as Trace, XcfaAction>) { (_, act) -> - act.label.getFlatLabels().map(XcfaLabel::toString) - } - - val optSequenceFile = File(resultFolder, "trace-optimized.plantuml") - writeSequenceTrace(optSequenceFile, concrTrace) { (_, act) -> - act.label.getFlatLabels().map(XcfaLabel::toString) - } - - val cSequenceFile = File(resultFolder, "trace-c.plantuml") - writeSequenceTrace(cSequenceFile, concrTrace) { (state, act) -> - val proc = state.processes[act.pid] - val loc = proc?.locs?.peek() - (loc?.metadata as? CMetaData)?.sourceText?.split("\n") ?: listOf("") - } - } - val witnessFile = File(resultFolder, "witness.graphml") - XcfaWitnessWriter().writeWitness( - safetyResult, config.inputConfig.input!!, - getSolver( - config.outputConfig.witnessConfig.concretizerSolver, - config.outputConfig.witnessConfig.validateConcretizerSolver - ), parseContext, witnessFile - ) - } - } catch (e: Throwable) { - logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n") + val traceFile = File(resultFolder, "trace.dot") + val traceG: Graph = TraceVisualizer.getDefault().visualize(concrTrace) + traceFile.writeText(GraphvizWriter.getInstance().writeString(traceG)) + + val sequenceFile = File(resultFolder, "trace.plantuml") + writeSequenceTrace( + sequenceFile, + safetyResult.asUnsafe().cex as Trace, XcfaAction>, + ) { (_, act) -> + act.label.getFlatLabels().map(XcfaLabel::toString) + } + + val optSequenceFile = File(resultFolder, "trace-optimized.plantuml") + writeSequenceTrace(optSequenceFile, concrTrace) { (_, act) -> + act.label.getFlatLabels().map(XcfaLabel::toString) + } + + val cSequenceFile = File(resultFolder, "trace-c.plantuml") + writeSequenceTrace(cSequenceFile, concrTrace) { (state, act) -> + val proc = state.processes[act.pid] + val loc = proc?.locs?.peek() + (loc?.metadata as? CMetaData)?.sourceText?.split("\n") ?: listOf("") + } } + val witnessFile = File(resultFolder, "witness.graphml") + XcfaWitnessWriter() + .writeWitness( + safetyResult, + config.inputConfig.input!!, + getSolver( + config.outputConfig.witnessConfig.concretizerSolver, + config.outputConfig.witnessConfig.validateConcretizerSolver, + ), + parseContext, + witnessFile, + ) + } + } catch (e: Throwable) { + logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n") } + } } -private fun writeSequenceTrace(sequenceFile: File, trace: Trace, XcfaAction>, - printer: (Pair, XcfaAction>) -> List) { - sequenceFile.writeText("@startuml\n") - var maxWidth = 0 - trace.actions.forEachIndexed { i, it -> - val stateBefore = trace.states[i] - sequenceFile.appendText("hnote over ${it.pid}\n") - val labelStrings = printer(Pair(stateBefore, it)) - if (maxWidth < (labelStrings.maxOfOrNull { it.length } ?: 0)) { - maxWidth = labelStrings.maxOfOrNull { it.length } ?: 0 - } - sequenceFile.appendText("${labelStrings.joinToString("\n")}\n") - sequenceFile.appendText("endhnote\n") +private fun writeSequenceTrace( + sequenceFile: File, + trace: Trace, XcfaAction>, + printer: (Pair, XcfaAction>) -> List, +) { + sequenceFile.writeText("@startuml\n") + var maxWidth = 0 + trace.actions.forEachIndexed { i, it -> + val stateBefore = trace.states[i] + sequenceFile.appendText("hnote over ${it.pid}\n") + val labelStrings = printer(Pair(stateBefore, it)) + if (maxWidth < (labelStrings.maxOfOrNull { it.length } ?: 0)) { + maxWidth = labelStrings.maxOfOrNull { it.length } ?: 0 } - trace.actions.map { it.pid }.distinct().reduce { acc, current -> - sequenceFile.appendText("$acc --> $current: \"${" ".repeat(maxWidth)}\"\n") - current + sequenceFile.appendText("${labelStrings.joinToString("\n")}\n") + sequenceFile.appendText("endhnote\n") + } + trace.actions + .map { it.pid } + .distinct() + .reduce { acc, current -> + sequenceFile.appendText("$acc --> $current: \"${" ".repeat(maxWidth)}\"\n") + current } - sequenceFile.appendText("@enduml\n") -} \ No newline at end of file + sequenceFile.appendText("@enduml\n") +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/XcfaCli.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/XcfaCli.kt index f5257effa4..54e2c0d406 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/XcfaCli.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/XcfaCli.kt @@ -41,6 +41,9 @@ class XcfaCli(private val args: Array) { @Parameter(names = ["--help", "-h"], help = true) private var help = false + @Parameter(names = ["--svcomp"]) + private var svcomp = false + @Parameter var remainingFlags: MutableList = ArrayList() @@ -55,6 +58,18 @@ class XcfaCli(private val args: Array) { } else { config = XcfaConfig() } + if (svcomp) { + remainingFlags.addAll( + listOf( + "--enable-output", + "--disable-xcfa-serialization", + "--disable-arg-generation", + "--disable-chc-serialization", + "--disable-c-serialization", + "--only-svcomp-witness" + ) + ) + } while (remainingFlags.isNotEmpty()) { val nextArgs = remainingFlags.toTypedArray() remainingFlags.clear() diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToBoundedChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToBoundedChecker.kt index 18e0bd82ca..5825b0d922 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToBoundedChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToBoundedChecker.kt @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.checkers import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness +import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.bounded.BoundedChecker import hu.bme.mit.theta.analysis.ptr.PtrState @@ -30,35 +29,41 @@ import hu.bme.mit.theta.xcfa.cli.params.XcfaConfig import hu.bme.mit.theta.xcfa.cli.utils.getSolver import hu.bme.mit.theta.xcfa.model.XCFA -fun getBoundedChecker(xcfa: XCFA, mcm: MCM, - config: XcfaConfig<*, *>, - logger: Logger): SafetyChecker>, XcfaAction>, XcfaPrec<*>> { - - val boundedConfig = config.backendConfig.specConfig as BoundedConfig +fun getBoundedChecker( + xcfa: XCFA, + mcm: MCM, + config: XcfaConfig<*, *>, + logger: Logger, +): SafetyChecker>, XcfaAction>, XcfaPrec<*>> { - return BoundedChecker( - monolithicExpr = xcfa.toMonolithicExpr(), - bmcSolver = tryGetSolver(boundedConfig.bmcConfig.bmcSolver, - boundedConfig.bmcConfig.validateBMCSolver)?.createSolver(), - bmcEnabled = { !boundedConfig.bmcConfig.disable }, - lfPathOnly = { !boundedConfig.bmcConfig.nonLfPath }, - itpSolver = tryGetSolver(boundedConfig.itpConfig.itpSolver, - boundedConfig.itpConfig.validateItpSolver)?.createItpSolver(), - imcEnabled = { !boundedConfig.itpConfig.disable }, - indSolver = tryGetSolver(boundedConfig.indConfig.indSolver, - boundedConfig.indConfig.validateIndSolver)?.createSolver(), - kindEnabled = { !boundedConfig.indConfig.disable }, - valToState = { xcfa.valToState(it) }, - biValToAction = { val1, val2 -> xcfa.valToAction(val1, val2) }, - logger = logger - ) as SafetyChecker>, XcfaAction>, XcfaPrec<*>> + val boundedConfig = config.backendConfig.specConfig as BoundedConfig + return BoundedChecker( + monolithicExpr = xcfa.toMonolithicExpr(), + bmcSolver = + tryGetSolver(boundedConfig.bmcConfig.bmcSolver, boundedConfig.bmcConfig.validateBMCSolver) + ?.createSolver(), + bmcEnabled = { !boundedConfig.bmcConfig.disable }, + lfPathOnly = { !boundedConfig.bmcConfig.nonLfPath }, + itpSolver = + tryGetSolver(boundedConfig.itpConfig.itpSolver, boundedConfig.itpConfig.validateItpSolver) + ?.createItpSolver(), + imcEnabled = { !boundedConfig.itpConfig.disable }, + indSolver = + tryGetSolver(boundedConfig.indConfig.indSolver, boundedConfig.indConfig.validateIndSolver) + ?.createSolver(), + kindEnabled = { !boundedConfig.indConfig.disable }, + valToState = { xcfa.valToState(it) }, + biValToAction = { val1, val2 -> xcfa.valToAction(val1, val2) }, + logger = logger, + ) + as SafetyChecker>, XcfaAction>, XcfaPrec<*>> } private fun tryGetSolver(name: String, validate: Boolean): SolverFactory? { - try { - return getSolver(name, validate) - } catch (e: Throwable) { - return null - } -} \ No newline at end of file + try { + return getSolver(name, validate) + } catch (e: Throwable) { + return null + } +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToCegarChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToCegarChecker.kt index 8c39486021..25a81fdb2e 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToCegarChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToCegarChecker.kt @@ -13,19 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.checkers import hu.bme.mit.theta.analysis.PartialOrd import hu.bme.mit.theta.analysis.Prec import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.algorithm.arg.ARG -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker -import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner +import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgCegarChecker +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgRefiner import hu.bme.mit.theta.analysis.expr.ExprAction import hu.bme.mit.theta.analysis.expr.ExprState import hu.bme.mit.theta.analysis.expr.refinement.* @@ -43,95 +42,139 @@ import hu.bme.mit.theta.xcfa.cli.params.* import hu.bme.mit.theta.xcfa.cli.utils.getSolver import hu.bme.mit.theta.xcfa.model.XCFA -fun getCegarChecker(xcfa: XCFA, mcm: MCM, - config: XcfaConfig<*, *>, - logger: Logger): SafetyChecker, XcfaAction>, Trace>, XcfaAction>, XcfaPrec<*>> { - val cegarConfig = config.backendConfig.specConfig as CegarConfig - val abstractionSolverFactory: SolverFactory = getSolver(cegarConfig.abstractorConfig.abstractionSolver, - cegarConfig.abstractorConfig.validateAbstractionSolver) - val refinementSolverFactory: SolverFactory = getSolver(cegarConfig.refinerConfig.refinementSolver, - cegarConfig.refinerConfig.validateRefinementSolver) +fun getCegarChecker( + xcfa: XCFA, + mcm: MCM, + config: XcfaConfig<*, *>, + logger: Logger, +): SafetyChecker< + ARG, XcfaAction>, + Trace>, XcfaAction>, + XcfaPrec<*>, +> { + val cegarConfig = config.backendConfig.specConfig as CegarConfig + val abstractionSolverFactory: SolverFactory = + getSolver( + cegarConfig.abstractorConfig.abstractionSolver, + cegarConfig.abstractorConfig.validateAbstractionSolver, + ) + val refinementSolverFactory: SolverFactory = + getSolver( + cegarConfig.refinerConfig.refinementSolver, + cegarConfig.refinerConfig.validateRefinementSolver, + ) - val ignoredVarRegistry = mutableMapOf, MutableSet>() + val ignoredVarRegistry = mutableMapOf, MutableSet>() - val lts = cegarConfig.coi.getLts(xcfa, ignoredVarRegistry, cegarConfig.porLevel) - val waitlist = if (cegarConfig.porLevel.isDynamic) { - (cegarConfig.coi.porLts as XcfaDporLts).waitlist + val lts = cegarConfig.coi.getLts(xcfa, ignoredVarRegistry, cegarConfig.porLevel) + val waitlist = + if (cegarConfig.porLevel.isDynamic) { + (cegarConfig.coi.porLts as XcfaDporLts).waitlist } else { - PriorityWaitlist.create>, XcfaAction>>( - cegarConfig.abstractorConfig.search.getComp(xcfa)) + PriorityWaitlist.create>, XcfaAction>>( + cegarConfig.abstractorConfig.search.getComp(xcfa) + ) } - val abstractionSolverInstance = abstractionSolverFactory.createSolver() - val globalStatePartialOrd: PartialOrd> = cegarConfig.abstractorConfig.domain.partialOrd( - abstractionSolverInstance) as PartialOrd> - val corePartialOrd: PartialOrd>> = - if (xcfa.isInlined) getPartialOrder(globalStatePartialOrd) - else getStackPartialOrder(globalStatePartialOrd) - val abstractor: Abstractor = cegarConfig.abstractorConfig.domain.abstractor( - xcfa, - abstractionSolverInstance, - cegarConfig.abstractorConfig.maxEnum, - waitlist, - cegarConfig.refinerConfig.refinement.stopCriterion, - logger, - lts, - config.inputConfig.property, - if (cegarConfig.porLevel.isDynamic) { - XcfaDporLts.getPartialOrder(corePartialOrd) - } else { - corePartialOrd - }, - cegarConfig.abstractorConfig.havocMemory - ) as Abstractor - - val ref: ExprTraceChecker = - cegarConfig.refinerConfig.refinement.refiner(refinementSolverFactory, cegarConfig.cexMonitor) - as ExprTraceChecker - val precRefiner: PrecRefiner = - cegarConfig.abstractorConfig.domain.itpPrecRefiner(cegarConfig.refinerConfig.exprSplitter.exprSplitter) - as PrecRefiner - val atomicNodePruner: NodePruner = cegarConfig.abstractorConfig.domain.nodePruner as NodePruner - val refiner: Refiner = - if (cegarConfig.refinerConfig.refinement == Refinement.MULTI_SEQ) - if (cegarConfig.porLevel == POR.AASPOR) - MultiExprTraceRefiner.create(ref, precRefiner, cegarConfig.refinerConfig.pruneStrategy, logger, - atomicNodePruner) - else - MultiExprTraceRefiner.create(ref, precRefiner, cegarConfig.refinerConfig.pruneStrategy, logger) - else - if (cegarConfig.porLevel == POR.AASPOR) - XcfaSingleExprTraceRefiner.create(ref, precRefiner, cegarConfig.refinerConfig.pruneStrategy, logger, - atomicNodePruner) - else - XcfaSingleExprTraceRefiner.create(ref, precRefiner, cegarConfig.refinerConfig.pruneStrategy, logger) + val abstractionSolverInstance = abstractionSolverFactory.createSolver() + val globalStatePartialOrd: PartialOrd> = + cegarConfig.abstractorConfig.domain.partialOrd(abstractionSolverInstance) + as PartialOrd> + val corePartialOrd: PartialOrd>> = + if (xcfa.isInlined) getPartialOrder(globalStatePartialOrd) + else getStackPartialOrder(globalStatePartialOrd) + val abstractor: ArgAbstractor = + cegarConfig.abstractorConfig.domain.abstractor( + xcfa, + abstractionSolverInstance, + cegarConfig.abstractorConfig.maxEnum, + waitlist, + cegarConfig.refinerConfig.refinement.stopCriterion, + logger, + lts, + config.inputConfig.property, + if (cegarConfig.porLevel.isDynamic) { + XcfaDporLts.getPartialOrder(corePartialOrd) + } else { + corePartialOrd + }, + cegarConfig.abstractorConfig.havocMemory, + ) as ArgAbstractor - val cegarChecker = if (cegarConfig.porLevel == POR.AASPOR) - CegarChecker.create( - abstractor, - AasporRefiner.create(refiner, cegarConfig.refinerConfig.pruneStrategy, ignoredVarRegistry), - logger + val ref: ExprTraceChecker = + cegarConfig.refinerConfig.refinement.refiner(refinementSolverFactory, cegarConfig.cexMonitor) + as ExprTraceChecker + val precRefiner: PrecRefiner = + cegarConfig.abstractorConfig.domain.itpPrecRefiner( + cegarConfig.refinerConfig.exprSplitter.exprSplitter + ) as PrecRefiner + val atomicNodePruner: NodePruner = + cegarConfig.abstractorConfig.domain.nodePruner as NodePruner + val refiner: ArgRefiner = + if (cegarConfig.refinerConfig.refinement == Refinement.MULTI_SEQ) + if (cegarConfig.porLevel == POR.AASPOR) + MultiExprTraceRefiner.create( + ref, + precRefiner, + cegarConfig.refinerConfig.pruneStrategy, + logger, + atomicNodePruner, + ) + else + MultiExprTraceRefiner.create( + ref, + precRefiner, + cegarConfig.refinerConfig.pruneStrategy, + logger, ) + else if (cegarConfig.porLevel == POR.AASPOR) + XcfaSingleExprTraceRefiner.create( + ref, + precRefiner, + cegarConfig.refinerConfig.pruneStrategy, + logger, + atomicNodePruner, + ) else - CegarChecker.create(abstractor, refiner, logger) + XcfaSingleExprTraceRefiner.create( + ref, + precRefiner, + cegarConfig.refinerConfig.pruneStrategy, + logger, + ) - // initialize monitors - MonitorCheckpoint.reset() - if (cegarConfig.cexMonitor == CexMonitorOptions.CHECK) { - val cm = CexMonitor(logger, cegarChecker.arg) - MonitorCheckpoint.register(cm, "CegarChecker.unsafeARG") - } + val cegarChecker = + if (cegarConfig.porLevel == POR.AASPOR) + ArgCegarChecker.create( + abstractor, + AasporRefiner.create(refiner, cegarConfig.refinerConfig.pruneStrategy, ignoredVarRegistry), + logger, + ) + else ArgCegarChecker.create(abstractor, refiner, logger) + + // initialize monitors + MonitorCheckpoint.reset() + if (cegarConfig.cexMonitor == CexMonitorOptions.CHECK) { + val cm = CexMonitor(logger, cegarChecker.proof) + MonitorCheckpoint.register(cm, "CegarChecker.unsafeARG") + } - return object : - SafetyChecker, XcfaAction>, Trace>, XcfaAction>, XcfaPrec<*>> { - override fun check( - prec: XcfaPrec<*>?): SafetyResult, XcfaAction>, Trace>, XcfaAction>> { - return cegarChecker.check( - prec) as SafetyResult, XcfaAction>, Trace>, XcfaAction>> - } + return object : + SafetyChecker< + ARG, XcfaAction>, + Trace>, XcfaAction>, + XcfaPrec<*>, + > { + override fun check( + prec: XcfaPrec<*>? + ): SafetyResult, XcfaAction>, Trace>, XcfaAction>> { + return cegarChecker.check(prec) + as SafetyResult, XcfaAction>, Trace>, XcfaAction>> + } - override fun check(): SafetyResult, XcfaAction>, Trace>, XcfaAction>> { - return check(cegarConfig.abstractorConfig.domain.initPrec(xcfa, cegarConfig.initPrec)) - } + override fun check(): + SafetyResult, XcfaAction>, Trace>, XcfaAction>> { + return check(cegarConfig.abstractorConfig.domain.initPrec(xcfa, cegarConfig.initPrec)) } -} \ No newline at end of file + } +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToHornChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToHornChecker.kt index dd63b5eb48..a944126ace 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToHornChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToHornChecker.kt @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.checkers import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness +import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.algorithm.chc.HornChecker @@ -36,32 +35,37 @@ import hu.bme.mit.theta.xcfa.model.XCFA import hu.bme.mit.theta.xcfa2chc.toCHC import org.abego.treelayout.internal.util.Contract.checkState -fun getHornChecker(xcfa: XCFA, mcm: MCM, config: XcfaConfig<*, *>, logger: Logger): - SafetyChecker>, XcfaAction>, XcfaPrec<*>> { +fun getHornChecker( + xcfa: XCFA, + mcm: MCM, + config: XcfaConfig<*, *>, + logger: Logger, +): SafetyChecker>, XcfaAction>, XcfaPrec<*>> { - checkState(xcfa.isInlined, "Only inlined XCFAs work right now") - checkState(xcfa.initProcedures.size == 1, "Only one-procedure XCFAs work right now") + checkState(xcfa.isInlined, "Only inlined XCFAs work right now") + checkState(xcfa.initProcedures.size == 1, "Only one-procedure XCFAs work right now") - val hornConfig = config.backendConfig.specConfig as HornConfig + val hornConfig = config.backendConfig.specConfig as HornConfig - val checker = HornChecker( - relations = xcfa.initProcedures[0].first.toCHC(), - hornSolverFactory = getSolver(hornConfig.solver, hornConfig.validateSolver), - logger = logger, + val checker = + HornChecker( + relations = xcfa.initProcedures[0].first.toCHC(), + hornSolverFactory = getSolver(hornConfig.solver, hornConfig.validateSolver), + logger = logger, ) - return SafetyChecker>, XcfaAction>, XcfaPrec<*>> { - val result = checker.check(null) + return SafetyChecker>, XcfaAction>, XcfaPrec<*>> { + val result = checker.check(null) - if (result.isSafe) { - SafetyResult.safe(EmptyWitness.getInstance()) - } else if (result.isUnsafe) { - val proof = result.asUnsafe().cex - val state = XcfaState>(xcfa, mapOf(), PtrState(PredState.of(proof.proofNode.expr))) - SafetyResult.unsafe(Trace.of(listOf(state), listOf()), EmptyWitness.getInstance()) - } else { - SafetyResult.unknown() - } + if (result.isSafe) { + SafetyResult.safe(EmptyProof.getInstance()) + } else if (result.isUnsafe) { + val proof = result.asUnsafe().cex + val state = + XcfaState>(xcfa, mapOf(), PtrState(PredState.of(proof.proofNode.expr))) + SafetyResult.unsafe(Trace.of(listOf(state), listOf()), EmptyProof.getInstance()) + } else { + SafetyResult.unknown() } - -} \ No newline at end of file + } +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToOcChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToOcChecker.kt index df14f6ee8f..d8e3fc76ff 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToOcChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToOcChecker.kt @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.checkers import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness +import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.ptr.PtrState @@ -31,14 +30,21 @@ import hu.bme.mit.theta.xcfa.cli.params.OcConfig import hu.bme.mit.theta.xcfa.cli.params.XcfaConfig import hu.bme.mit.theta.xcfa.model.XCFA -fun getOcChecker(xcfa: XCFA, mcm: MCM, - config: XcfaConfig<*, *>, - logger: Logger): SafetyChecker>, XcfaAction>, XcfaPrec<*>> { - val ocChecker = XcfaOcChecker(xcfa, (config.backendConfig.specConfig as OcConfig).decisionProcedure, logger) - return object : SafetyChecker>, XcfaAction>, XcfaPrec<*>> { - override fun check( - prec: XcfaPrec<*>?): SafetyResult>, XcfaAction>> = check() +fun getOcChecker( + xcfa: XCFA, + mcm: MCM, + config: XcfaConfig<*, *>, + logger: Logger, +): SafetyChecker>, XcfaAction>, XcfaPrec<*>> { + val ocChecker = + XcfaOcChecker(xcfa, (config.backendConfig.specConfig as OcConfig).decisionProcedure, logger) + return object : + SafetyChecker>, XcfaAction>, XcfaPrec<*>> { + override fun check( + prec: XcfaPrec<*>? + ): SafetyResult>, XcfaAction>> = check() - override fun check(): SafetyResult>, XcfaAction>> = ocChecker.check() - } -} \ No newline at end of file + override fun check(): SafetyResult>, XcfaAction>> = + ocChecker.check() + } +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/InProcessChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/InProcessChecker.kt index a20d9a5aaf..193a062499 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/InProcessChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/InProcessChecker.kt @@ -13,17 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.checkers import com.zaxxer.nuprocess.NuAbstractProcessHandler import com.zaxxer.nuprocess.NuProcess import com.zaxxer.nuprocess.NuProcessBuilder import hu.bme.mit.theta.analysis.EmptyCex -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness +import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.SafetyResult -import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.xcfa.analysis.XcfaPrec @@ -40,47 +38,48 @@ import java.util.concurrent.TimeUnit import kotlin.io.path.createTempDirectory class InProcessChecker( - val xcfa: XCFA, - val config: XcfaConfig, - val parseContext: ParseContext, - val logger: Logger, -) : SafetyChecker> { - - override fun check( - prec: XcfaPrec<*>?): SafetyResult { - return check() - } - - override fun check(): SafetyResult { - val tempDir = createTempDirectory(config.outputConfig.resultFolder.toPath()) - - val xcfaJson = CachingFileSerializer.serialize("xcfa.json", xcfa) { getGson(xcfa).toJson(xcfa) } - val parseContextJson = CachingFileSerializer.serialize("parseContext.json", parseContext) { - getGson(xcfa).toJson(parseContext) - } - - val processConfig = config.copy( - inputConfig = config.inputConfig.copy( - input = xcfaJson, - parseCtx = parseContextJson, - ), - frontendConfig = config.frontendConfig.copy( - inputType = InputType.JSON - ), - backendConfig = config.backendConfig.copy(inProcess = false, timeoutMs = 0), - outputConfig = config.outputConfig.copy( - resultFolder = tempDir.toFile(), - cOutputConfig = COutputConfig(disable = true), - xcfaOutputConfig = XcfaOutputConfig(disable = true), - argConfig = config.outputConfig.argConfig.copy(disable = false), // we need the arg to be produced - ) - ) - - val configJson = CachingFileSerializer.serialize("config.json", processConfig) { - getGson(xcfa).toJson(processConfig) - } - - val pb = NuProcessBuilder(listOf( + val xcfa: XCFA, + val config: XcfaConfig, + val parseContext: ParseContext, + val logger: Logger, +) : SafetyChecker> { + + override fun check(prec: XcfaPrec<*>?): SafetyResult { + return check() + } + + override fun check(): SafetyResult { + val tempDir = createTempDirectory(config.outputConfig.resultFolder.toPath()) + + val xcfaJson = CachingFileSerializer.serialize("xcfa.json", xcfa) { getGson(xcfa).toJson(xcfa) } + val parseContextJson = + CachingFileSerializer.serialize("parseContext.json", parseContext) { + getGson(xcfa).toJson(parseContext) + } + + val processConfig = + config.copy( + inputConfig = config.inputConfig.copy(input = xcfaJson, parseCtx = parseContextJson), + frontendConfig = config.frontendConfig.copy(inputType = InputType.JSON), + backendConfig = config.backendConfig.copy(inProcess = false, timeoutMs = 0), + outputConfig = + config.outputConfig.copy( + resultFolder = tempDir.toFile(), + cOutputConfig = COutputConfig(disable = true), + xcfaOutputConfig = XcfaOutputConfig(disable = true), + argConfig = + config.outputConfig.argConfig.copy(disable = false), // we need the arg to be produced + ), + ) + + val configJson = + CachingFileSerializer.serialize("config.json", processConfig) { + getGson(xcfa).toJson(processConfig) + } + + val pb = + NuProcessBuilder( + listOf( ProcessHandle.current().info().command().orElse("java"), "-Xss120m", "-Xmx14210m", @@ -88,91 +87,94 @@ class InProcessChecker( File(XcfaCli::class.java.protectionDomain.codeSource.location.toURI()).absolutePath, XcfaCli::class.qualifiedName, "-c", - configJson.absolutePath - ).filterNotNull()) - val processHandler = ProcessHandler() - pb.setProcessListener(processHandler) - val process: NuProcess = pb.start() - pb.environment().putAll(System.getenv()) - - val retCode = process.waitFor(config.backendConfig.timeoutMs, TimeUnit.MILLISECONDS) - val booleanSafetyResult = - if (retCode == Int.MIN_VALUE) { - if (processHandler.safetyResult == null) { - process.destroy(true) - throw ErrorCodeException(ExitCodes.TIMEOUT.code) - } else { - logger.write(Logger.Level.RESULT, - "Config timed out but started writing result, trying to wait an additional 10%...") - val retCode = process.waitFor(config.backendConfig.timeoutMs / 10, TimeUnit.MILLISECONDS) - if (retCode != 0) { - throw ErrorCodeException(retCode) - } else { - processHandler.safetyResult - } - } - } else if (retCode != 0) { - throw ErrorCodeException(retCode) - } else { - processHandler.safetyResult - } - - tempDir.toFile().listFiles()?.forEach { - it.copyTo(config.outputConfig.resultFolder.resolve(it.name), overwrite = true) + configJson.absolutePath, + ) + .filterNotNull() + ) + val processHandler = ProcessHandler() + pb.setProcessListener(processHandler) + val process: NuProcess = pb.start() + pb.environment().putAll(System.getenv()) + + val retCode = process.waitFor(config.backendConfig.timeoutMs, TimeUnit.MILLISECONDS) + val booleanSafetyResult = + if (retCode == Int.MIN_VALUE) { + if (processHandler.safetyResult == null) { + process.destroy(true) + throw ErrorCodeException(ExitCodes.TIMEOUT.code) + } else { + logger.write( + Logger.Level.RESULT, + "Config timed out but started writing result, trying to wait an additional 10%...", + ) + val retCode = process.waitFor(config.backendConfig.timeoutMs / 10, TimeUnit.MILLISECONDS) + if (retCode != 0) { + throw ErrorCodeException(retCode) + } else { + processHandler.safetyResult + } } - tempDir.toFile().deleteRecursively() - - return booleanSafetyResult as SafetyResult + } else if (retCode != 0) { + throw ErrorCodeException(retCode) + } else { + processHandler.safetyResult + } + + tempDir.toFile().listFiles()?.forEach { + it.copyTo(config.outputConfig.resultFolder.resolve(it.name), overwrite = true) } + tempDir.toFile().deleteRecursively() - private class ProcessHandler : NuAbstractProcessHandler() { - - private val stdout = LinkedList() - private var stdoutRemainder = "" - private val stderr = LinkedList() - private var stderrRemainder = "" - var safetyResult: SafetyResult<*, *>? = null - private set - - override fun onStdout(buffer: ByteBuffer, closed: Boolean) { - if (!closed) { - val bytes = ByteArray(buffer.remaining()) - buffer[bytes] - val str = bytes.decodeToString() - - stdoutRemainder += str - if (stdoutRemainder.contains("SafetyResult Safe")) { - safetyResult = SafetyResult.safe(EmptyWitness.getInstance()) - } - if (stdoutRemainder.contains("SafetyResult Unsafe")) { - safetyResult = SafetyResult.unsafe(EmptyCex.getInstance(), EmptyWitness.getInstance()) - } - - val newLines = stdoutRemainder.split("\n") // if ends with \n, last element will be "" - newLines.subList(0, newLines.size - 1).forEach { - stdout.add(it) - println("server: $it") - } - stdoutRemainder = newLines[newLines.size - 1] - } + return booleanSafetyResult as SafetyResult + } + + private class ProcessHandler : NuAbstractProcessHandler() { + + private val stdout = LinkedList() + private var stdoutRemainder = "" + private val stderr = LinkedList() + private var stderrRemainder = "" + var safetyResult: SafetyResult<*, *>? = null + private set + + override fun onStdout(buffer: ByteBuffer, closed: Boolean) { + if (!closed) { + val bytes = ByteArray(buffer.remaining()) + buffer[bytes] + val str = bytes.decodeToString() + + stdoutRemainder += str + if (stdoutRemainder.contains("SafetyResult Safe")) { + safetyResult = SafetyResult.safe(EmptyProof.getInstance()) + } + if (stdoutRemainder.contains("SafetyResult Unsafe")) { + safetyResult = SafetyResult.unsafe(EmptyCex.getInstance(), EmptyProof.getInstance()) } - override fun onStderr(buffer: ByteBuffer, closed: Boolean) { - if (!closed) { - val bytes = ByteArray(buffer.remaining()) - buffer[bytes] - val str = bytes.decodeToString() - - stderrRemainder += str - - val newLines = stderrRemainder.split("\n") // if ends with \n, last element will be "" - newLines.subList(0, newLines.size - 1).forEach { - stderr.add(it) - err.println("server: $it") - } - stderrRemainder = newLines[newLines.size - 1] - } + val newLines = stdoutRemainder.split("\n") // if ends with \n, last element will be "" + newLines.subList(0, newLines.size - 1).forEach { + stdout.add(it) + println("server: $it") } + stdoutRemainder = newLines[newLines.size - 1] + } } -} \ No newline at end of file + override fun onStderr(buffer: ByteBuffer, closed: Boolean) { + if (!closed) { + val bytes = ByteArray(buffer.remaining()) + buffer[bytes] + val str = bytes.decodeToString() + + stderrRemainder += str + + val newLines = stderrRemainder.split("\n") // if ends with \n, last element will be "" + newLines.subList(0, newLines.size - 1).forEach { + stderr.add(it) + err.println("server: $it") + } + stderrRemainder = newLines[newLines.size - 1] + } + } + } +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ExitCodes.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ExitCodes.kt index 563a7abaa3..1ecf5e665a 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ExitCodes.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ExitCodes.kt @@ -18,6 +18,7 @@ package hu.bme.mit.theta.xcfa.cli.params import com.microsoft.z3.Z3Exception import hu.bme.mit.theta.common.exception.NotSolvableException +import hu.bme.mit.theta.frontend.UnsupportedFrontendElementException import hu.bme.mit.theta.solver.UnknownSolverStatusException import hu.bme.mit.theta.solver.javasmt.JavaSMTSolverException import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException @@ -32,6 +33,7 @@ enum class ExitCodes(val code: Int) { SERVER_ERROR(202), PORTFOLIO_ERROR(203), + UNSUPPORTED_ELEMENT(209), FRONTEND_FAILED(210), INVALID_PARAM(211), @@ -59,6 +61,9 @@ fun exitOnError(stacktrace: Boolean, throwDontExit: Boolean, body: () -> T): } catch (e: ErrorCodeException) { e.printStackTrace() exitProcess(throwDontExit, e, e.code) + } catch (e: UnsupportedFrontendElementException) { + e.printCauseAndTrace(stacktrace) + exitProcess(throwDontExit, e, ExitCodes.UNSUPPORTED_ELEMENT.code) } catch (e: SmtLibSolverException) { e.printCauseAndTrace(stacktrace) exitProcess(throwDontExit, e, ExitCodes.SOLVER_ERROR.code) diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ParamValues.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ParamValues.kt index 359d953a57..35e6f8f7d6 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ParamValues.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ParamValues.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.params import com.google.gson.reflect.TypeToken @@ -23,7 +22,7 @@ import hu.bme.mit.theta.analysis.Prec import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators.ArgNodeComparator -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterion import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterions import hu.bme.mit.theta.analysis.expl.ExplPrec @@ -55,12 +54,12 @@ import hu.bme.mit.theta.xcfa.model.XCFA import java.lang.reflect.Type enum class InputType { - C, - LLVM, - JSON, - DSL, - CHC, - LITMUS, + C, + LLVM, + JSON, + DSL, + CHC, + LITMUS, } enum class Backend { @@ -75,231 +74,307 @@ enum class Backend { } enum class POR( - val getLts: (XCFA, MutableMap, MutableSet>) -> LTS>, XcfaAction>, - val isDynamic: Boolean, - val isAbstractionAware: Boolean + val getLts: + (XCFA, MutableMap, MutableSet>) -> LTS< + XcfaState>, + XcfaAction, + >, + val isDynamic: Boolean, + val isAbstractionAware: Boolean, ) { - NOPOR({ _, _ -> getXcfaLts() }, false, false), - SPOR({ xcfa, _ -> XcfaSporLts(xcfa) }, false, false), - AASPOR({ xcfa, registry -> XcfaAasporLts(xcfa, registry) }, false, true), - DPOR({ xcfa, _ -> XcfaDporLts(xcfa) }, true, false), - AADPOR({ xcfa, _ -> XcfaAadporLts(xcfa) }, true, true) + NOPOR({ _, _ -> getXcfaLts() }, false, false), + SPOR({ xcfa, _ -> XcfaSporLts(xcfa) }, false, false), + AASPOR({ xcfa, registry -> XcfaAasporLts(xcfa, registry) }, false, true), + DPOR({ xcfa, _ -> XcfaDporLts(xcfa) }, true, false), + AADPOR({ xcfa, _ -> XcfaAadporLts(xcfa) }, true, true), } enum class Strategy { - DIRECT, - SERVER, - SERVER_DEBUG, - PORTFOLIO + DIRECT, + SERVER, + SERVER_DEBUG, + PORTFOLIO, } // TODO partial orders nicely enum class Domain( - val abstractor: ( - xcfa: XCFA, - solver: Solver, - maxEnum: Int, - waitlist: Waitlist>, XcfaAction>>, - stopCriterion: StopCriterion>, XcfaAction>, - logger: Logger, - lts: LTS>, XcfaAction>, - errorDetectionType: ErrorDetection, - partialOrd: PartialOrd>>, - isHavoc: Boolean - ) -> Abstractor, - val itpPrecRefiner: (exprSplitter: ExprSplitter) -> PrecRefiner, - val initPrec: (XCFA, InitPrec) -> XcfaPrec>, - val partialOrd: (Solver) -> PartialOrd>, - val nodePruner: NodePruner, - val stateType: Type + val abstractor: + ( + xcfa: XCFA, + solver: Solver, + maxEnum: Int, + waitlist: Waitlist>, XcfaAction>>, + stopCriterion: StopCriterion>, XcfaAction>, + logger: Logger, + lts: LTS>, XcfaAction>, + errorDetectionType: ErrorDetection, + partialOrd: PartialOrd>>, + isHavoc: Boolean, + ) -> ArgAbstractor, + val itpPrecRefiner: + (exprSplitter: ExprSplitter) -> PrecRefiner< + out ExprState, + out ExprAction, + out Prec, + out Refutation, + >, + val initPrec: (XCFA, InitPrec) -> XcfaPrec>, + val partialOrd: (Solver) -> PartialOrd>, + val nodePruner: NodePruner, + val stateType: Type, ) { - EXPL( - abstractor = { a, b, c, d, e, f, g, h, i, j -> - getXcfaAbstractor(ExplXcfaAnalysis(a, b, c, i as PartialOrd>>, j), d, - e, f, g, h) - }, - itpPrecRefiner = { - XcfaPrecRefiner, ExplPrec, ItpRefutation>(ItpRefToPtrPrec(ItpRefToExplPrec())) - }, - initPrec = { x, ip -> ip.explPrec(x) }, - partialOrd = { PartialOrd { s1, s2 -> s1.isLeq(s2) }.getPtrPartialOrd() }, - nodePruner = AtomicNodePruner>, XcfaAction>(), - stateType = TypeToken.get(ExplState::class.java).type - ), - PRED_BOOL( - abstractor = { a, b, c, d, e, f, g, h, i, j -> - getXcfaAbstractor(PredXcfaAnalysis(a, b, PredAbstractors.booleanAbstractor(b), - i as PartialOrd>>, j), d, e, f, g, h) - }, - itpPrecRefiner = { a -> - XcfaPrecRefiner, PredPrec, ItpRefutation>(ItpRefToPtrPrec(ItpRefToPredPrec(a))) - }, - initPrec = { x, ip -> ip.predPrec(x) }, - partialOrd = { solver -> PredOrd.create(solver).getPtrPartialOrd() }, - nodePruner = AtomicNodePruner>, XcfaAction>(), - stateType = TypeToken.get(PredState::class.java).type - ), - PRED_CART( - abstractor = { a, b, c, d, e, f, g, h, i, j -> - getXcfaAbstractor(PredXcfaAnalysis(a, b, PredAbstractors.cartesianAbstractor(b), - i as PartialOrd>>, j), d, e, f, g, h) - }, - itpPrecRefiner = { a -> - XcfaPrecRefiner, PredPrec, ItpRefutation>(ItpRefToPtrPrec(ItpRefToPredPrec(a))) - }, - initPrec = { x, ip -> ip.predPrec(x) }, - partialOrd = { solver -> PredOrd.create(solver).getPtrPartialOrd() }, - nodePruner = AtomicNodePruner>, XcfaAction>(), - stateType = TypeToken.get(PredState::class.java).type - ), - PRED_SPLIT( - abstractor = { a, b, c, d, e, f, g, h, i, j -> - getXcfaAbstractor(PredXcfaAnalysis(a, b, PredAbstractors.booleanSplitAbstractor(b), - i as PartialOrd>>, j), d, e, f, g, h) - }, - itpPrecRefiner = { a -> - XcfaPrecRefiner, PredPrec, ItpRefutation>(ItpRefToPtrPrec(ItpRefToPredPrec(a))) - }, - initPrec = { x, ip -> ip.predPrec(x) }, - partialOrd = { solver -> PredOrd.create(solver).getPtrPartialOrd() }, - nodePruner = AtomicNodePruner>, XcfaAction>(), - stateType = TypeToken.get(PredState::class.java).type - ), + EXPL( + abstractor = { a, b, c, d, e, f, g, h, i, j -> + getXcfaAbstractor( + ExplXcfaAnalysis(a, b, c, i as PartialOrd>>, j), + d, + e, + f, + g, + h, + ) + }, + itpPrecRefiner = { + XcfaPrecRefiner, ExplPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToExplPrec()) + ) + }, + initPrec = { x, ip -> ip.explPrec(x) }, + partialOrd = { PartialOrd { s1, s2 -> s1.isLeq(s2) }.getPtrPartialOrd() }, + nodePruner = AtomicNodePruner>, XcfaAction>(), + stateType = TypeToken.get(ExplState::class.java).type, + ), + PRED_BOOL( + abstractor = { a, b, c, d, e, f, g, h, i, j -> + getXcfaAbstractor( + PredXcfaAnalysis( + a, + b, + PredAbstractors.booleanAbstractor(b), + i as PartialOrd>>, + j, + ), + d, + e, + f, + g, + h, + ) + }, + itpPrecRefiner = { a -> + XcfaPrecRefiner, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(a)) + ) + }, + initPrec = { x, ip -> ip.predPrec(x) }, + partialOrd = { solver -> PredOrd.create(solver).getPtrPartialOrd() }, + nodePruner = AtomicNodePruner>, XcfaAction>(), + stateType = TypeToken.get(PredState::class.java).type, + ), + PRED_CART( + abstractor = { a, b, c, d, e, f, g, h, i, j -> + getXcfaAbstractor( + PredXcfaAnalysis( + a, + b, + PredAbstractors.cartesianAbstractor(b), + i as PartialOrd>>, + j, + ), + d, + e, + f, + g, + h, + ) + }, + itpPrecRefiner = { a -> + XcfaPrecRefiner, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(a)) + ) + }, + initPrec = { x, ip -> ip.predPrec(x) }, + partialOrd = { solver -> PredOrd.create(solver).getPtrPartialOrd() }, + nodePruner = AtomicNodePruner>, XcfaAction>(), + stateType = TypeToken.get(PredState::class.java).type, + ), + PRED_SPLIT( + abstractor = { a, b, c, d, e, f, g, h, i, j -> + getXcfaAbstractor( + PredXcfaAnalysis( + a, + b, + PredAbstractors.booleanSplitAbstractor(b), + i as PartialOrd>>, + j, + ), + d, + e, + f, + g, + h, + ) + }, + itpPrecRefiner = { a -> + XcfaPrecRefiner, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(a)) + ) + }, + initPrec = { x, ip -> ip.predPrec(x) }, + partialOrd = { solver -> PredOrd.create(solver).getPtrPartialOrd() }, + nodePruner = AtomicNodePruner>, XcfaAction>(), + stateType = TypeToken.get(PredState::class.java).type, + ), } enum class Refinement( - val refiner: (solverFactory: SolverFactory, monitorOption: CexMonitorOptions) -> ExprTraceChecker, - val stopCriterion: StopCriterion>, XcfaAction>, + val refiner: + (solverFactory: SolverFactory, monitorOption: CexMonitorOptions) -> ExprTraceChecker< + out Refutation + >, + val stopCriterion: StopCriterion>, XcfaAction>, ) { - FW_BIN_ITP( - refiner = { s, _ -> - ExprTraceFwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), s.createItpSolver()) - }, - stopCriterion = StopCriterions.firstCex(), - ), - BW_BIN_ITP( - refiner = { s, _ -> - ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), s.createItpSolver()) - }, - stopCriterion = StopCriterions.firstCex() - ), - SEQ_ITP( - refiner = { s, _ -> - ExprTraceSeqItpChecker.create(BoolExprs.True(), BoolExprs.True(), s.createItpSolver()) - }, - stopCriterion = StopCriterions.firstCex() - ), - MULTI_SEQ( - refiner = { s, m -> - if (m == CexMonitorOptions.CHECK) error("CexMonitor is not implemented for MULTI_SEQ") - ExprTraceSeqItpChecker.create(BoolExprs.True(), BoolExprs.True(), s.createItpSolver()) - }, - stopCriterion = StopCriterions.fullExploration() - ), - UNSAT_CORE( - refiner = { s, _ -> - ExprTraceUnsatCoreChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - }, - stopCriterion = StopCriterions.firstCex() - ), - UCB( - refiner = { s, _ -> - ExprTraceUCBChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - }, - stopCriterion = StopCriterions.firstCex() - ), - - NWT_SP( - refiner = { s, _ -> - ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - .withoutIT().withSP().withoutLV() - }, - stopCriterion = StopCriterions.firstCex() - ), - NWT_WP( - refiner = { s, _ -> - ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - .withoutIT().withWP().withoutLV() - }, - stopCriterion = StopCriterions.firstCex() - ), - NWT_SP_LV( - refiner = { s, _ -> - ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - .withoutIT().withSP().withLV() - }, - stopCriterion = StopCriterions.firstCex() - ), - NWT_WP_LV( - refiner = { s, _ -> - ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - .withoutIT().withWP().withLV() - }, - stopCriterion = StopCriterions.firstCex() - ), - NWT_IT_WP( - refiner = { s, _ -> - ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - .withIT().withWP().withoutLV() - }, - stopCriterion = StopCriterions.firstCex() - ), - NWT_IT_SP( - refiner = { s, _ -> - ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - .withIT().withSP().withoutLV() - }, - stopCriterion = StopCriterions.firstCex() - ), - NWT_IT_WP_LV( - refiner = { s, _ -> - ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - .withIT().withWP().withLV() - }, - stopCriterion = StopCriterions.firstCex() - ), - NWT_IT_SP_LV( - refiner = { s, _ -> - ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) - .withIT().withSP().withLV() - }, - stopCriterion = StopCriterions.firstCex() - ) + FW_BIN_ITP( + refiner = { s, _ -> + ExprTraceFwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), s.createItpSolver()) + }, + stopCriterion = StopCriterions.firstCex(), + ), + BW_BIN_ITP( + refiner = { s, _ -> + ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), s.createItpSolver()) + }, + stopCriterion = StopCriterions.firstCex(), + ), + SEQ_ITP( + refiner = { s, _ -> + ExprTraceSeqItpChecker.create(BoolExprs.True(), BoolExprs.True(), s.createItpSolver()) + }, + stopCriterion = StopCriterions.firstCex(), + ), + MULTI_SEQ( + refiner = { s, m -> + if (m == CexMonitorOptions.CHECK) error("CexMonitor is not implemented for MULTI_SEQ") + ExprTraceSeqItpChecker.create(BoolExprs.True(), BoolExprs.True(), s.createItpSolver()) + }, + stopCriterion = StopCriterions.fullExploration(), + ), + UNSAT_CORE( + refiner = { s, _ -> + ExprTraceUnsatCoreChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + }, + stopCriterion = StopCriterions.firstCex(), + ), + UCB( + refiner = { s, _ -> + ExprTraceUCBChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + }, + stopCriterion = StopCriterions.firstCex(), + ), + NWT_SP( + refiner = { s, _ -> + ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + .withoutIT() + .withSP() + .withoutLV() + }, + stopCriterion = StopCriterions.firstCex(), + ), + NWT_WP( + refiner = { s, _ -> + ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + .withoutIT() + .withWP() + .withoutLV() + }, + stopCriterion = StopCriterions.firstCex(), + ), + NWT_SP_LV( + refiner = { s, _ -> + ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + .withoutIT() + .withSP() + .withLV() + }, + stopCriterion = StopCriterions.firstCex(), + ), + NWT_WP_LV( + refiner = { s, _ -> + ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + .withoutIT() + .withWP() + .withLV() + }, + stopCriterion = StopCriterions.firstCex(), + ), + NWT_IT_WP( + refiner = { s, _ -> + ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + .withIT() + .withWP() + .withoutLV() + }, + stopCriterion = StopCriterions.firstCex(), + ), + NWT_IT_SP( + refiner = { s, _ -> + ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + .withIT() + .withSP() + .withoutLV() + }, + stopCriterion = StopCriterions.firstCex(), + ), + NWT_IT_WP_LV( + refiner = { s, _ -> + ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + .withIT() + .withWP() + .withLV() + }, + stopCriterion = StopCriterions.firstCex(), + ), + NWT_IT_SP_LV( + refiner = { s, _ -> + ExprTraceNewtonChecker.create(BoolExprs.True(), BoolExprs.True(), s.createUCSolver()) + .withIT() + .withSP() + .withLV() + }, + stopCriterion = StopCriterions.firstCex(), + ), } - enum class ExprSplitterOptions(val exprSplitter: ExprSplitter) { - WHOLE(ExprSplitters.whole()), - CONJUNCTS(ExprSplitters.conjuncts()), - ATOMS(ExprSplitters.atoms()); + WHOLE(ExprSplitters.whole()), + CONJUNCTS(ExprSplitters.conjuncts()), + ATOMS(ExprSplitters.atoms()), } enum class Search { - BFS { + BFS { - override fun getComp(cfa: XCFA): ArgNodeComparator { - return ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), - ArgNodeComparators.bfs()) - } - }, - DFS { + override fun getComp(cfa: XCFA): ArgNodeComparator { + return ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs()) + } + }, + DFS { - override fun getComp(cfa: XCFA): ArgNodeComparator { - return ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), - ArgNodeComparators.dfs()) - } - }, - ERR { + override fun getComp(cfa: XCFA): ArgNodeComparator { + return ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.dfs()) + } + }, + ERR { - override fun getComp(cfa: XCFA): ArgNodeComparator { - return XcfaDistToErrComparator(cfa) - } - }; + override fun getComp(cfa: XCFA): ArgNodeComparator { + return XcfaDistToErrComparator(cfa) + } + }; - abstract fun getComp(cfa: XCFA): ArgNodeComparator + abstract fun getComp(cfa: XCFA): ArgNodeComparator } enum class TracegenAbstraction{ @@ -308,62 +383,63 @@ enum class TracegenAbstraction{ } enum class InitPrec( - val explPrec: (xcfa: XCFA) -> XcfaPrec>, - val predPrec: (xcfa: XCFA) -> XcfaPrec>, + val explPrec: (xcfa: XCFA) -> XcfaPrec>, + val predPrec: (xcfa: XCFA) -> XcfaPrec>, ) { - EMPTY( - explPrec = { XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet())) }, - predPrec = { XcfaPrec(PtrPrec(PredPrec.of(), emptySet())) } - ), - ALLVARS( - explPrec = { xcfa -> XcfaPrec(PtrPrec(ExplPrec.of(xcfa.collectVars()), emptySet())) }, - predPrec = { error("ALLVARS is not interpreted for the predicate domain.") } - ), - ALLGLOBALS( - explPrec = { xcfa -> XcfaPrec(PtrPrec(ExplPrec.of(xcfa.vars.map { it.wrappedVar }), emptySet())) }, - predPrec = { error("ALLGLOBALS is not interpreted for the predicate domain.") } - ), - ALLASSUMES( - explPrec = { xcfa -> - XcfaPrec( - PtrPrec(ExplPrec.of(xcfa.collectAssumes().flatMap(ExprUtils::getVars)), emptySet())) - }, - predPrec = { xcfa -> XcfaPrec(PtrPrec(PredPrec.of(xcfa.collectAssumes()), emptySet())) }, - ), - + EMPTY( + explPrec = { XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet())) }, + predPrec = { XcfaPrec(PtrPrec(PredPrec.of(), emptySet())) }, + ), + ALLVARS( + explPrec = { xcfa -> XcfaPrec(PtrPrec(ExplPrec.of(xcfa.collectVars()), emptySet())) }, + predPrec = { error("ALLVARS is not interpreted for the predicate domain.") }, + ), + ALLGLOBALS( + explPrec = { xcfa -> + XcfaPrec(PtrPrec(ExplPrec.of(xcfa.vars.map { it.wrappedVar }), emptySet())) + }, + predPrec = { error("ALLGLOBALS is not interpreted for the predicate domain.") }, + ), + ALLASSUMES( + explPrec = { xcfa -> + XcfaPrec(PtrPrec(ExplPrec.of(xcfa.collectAssumes().flatMap(ExprUtils::getVars)), emptySet())) + }, + predPrec = { xcfa -> XcfaPrec(PtrPrec(PredPrec.of(xcfa.collectAssumes()), emptySet())) }, + ), } enum class ConeOfInfluenceMode( - val getLts: (XCFA, MutableMap, MutableSet>, POR) -> LTS>, XcfaAction> + val getLts: + (XCFA, MutableMap, MutableSet>, POR) -> LTS< + XcfaState>, + XcfaAction, + > ) { - NO_COI({ xcfa, ivr, por -> - por.getLts(xcfa, ivr).also { NO_COI.porLts = it } - }), - COI({ xcfa, ivr, por -> - ConeOfInfluence.coreLts = por.getLts(xcfa, ivr).also { COI.porLts = it } - ConeOfInfluence.lts - }), - POR_COI({ xcfa, ivr, por -> - ConeOfInfluence.coreLts = getXcfaLts() - if (por.isAbstractionAware) XcfaAasporCoiLts(xcfa, ivr, ConeOfInfluence.lts) - else XcfaSporCoiLts(xcfa, ConeOfInfluence.lts) - }), - POR_COI_POR({ xcfa, ivr, por -> - ConeOfInfluence.coreLts = por.getLts(xcfa, ivr).also { POR_COI_POR.porLts = it } - if (por.isAbstractionAware) XcfaAasporCoiLts(xcfa, ivr, ConeOfInfluence.lts) - else XcfaSporCoiLts(xcfa, ConeOfInfluence.lts) - }) - ; + NO_COI({ xcfa, ivr, por -> por.getLts(xcfa, ivr).also { NO_COI.porLts = it } }), + COI({ xcfa, ivr, por -> + ConeOfInfluence.coreLts = por.getLts(xcfa, ivr).also { COI.porLts = it } + ConeOfInfluence.lts + }), + POR_COI({ xcfa, ivr, por -> + ConeOfInfluence.coreLts = getXcfaLts() + if (por.isAbstractionAware) XcfaAasporCoiLts(xcfa, ivr, ConeOfInfluence.lts) + else XcfaSporCoiLts(xcfa, ConeOfInfluence.lts) + }), + POR_COI_POR({ xcfa, ivr, por -> + ConeOfInfluence.coreLts = por.getLts(xcfa, ivr).also { POR_COI_POR.porLts = it } + if (por.isAbstractionAware) XcfaAasporCoiLts(xcfa, ivr, ConeOfInfluence.lts) + else XcfaSporCoiLts(xcfa, ConeOfInfluence.lts) + }); - var porLts: LTS>, XcfaAction>? = null + var porLts: LTS>, XcfaAction>? = null } // TODO CexMonitor: disable for multi_seq // TODO add new monitor to xsts cli enum class CexMonitorOptions { - CHECK, - DISABLE + CHECK, + DISABLE, } diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt index 2785ce67f3..5459559ee3 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt @@ -355,11 +355,12 @@ data class OutputConfig( ) : Config { override fun getObjects(): Set { - return super.getObjects() union cOutputConfig.getObjects() union xcfaOutputConfig.getObjects() union witnessConfig.getObjects() union argConfig.getObjects() + return super.getObjects() union cOutputConfig.getObjects() union xcfaOutputConfig.getObjects() union chcOutputConfig.getObjects() union witnessConfig.getObjects() union argConfig.getObjects() } override fun update(): Boolean = - listOf(cOutputConfig, xcfaOutputConfig, witnessConfig, argConfig).map { it.update() }.any { it } + listOf(cOutputConfig, xcfaOutputConfig, chcOutputConfig, witnessConfig, argConfig).map { it.update() } + .any { it } } data class XcfaOutputConfig( @@ -390,6 +391,9 @@ data class WitnessConfig( @Parameter(names = ["--disable-witness-generation"]) var disable: Boolean = false, + @Parameter(names = ["--only-svcomp-witness"]) + var svcomp: Boolean = false, + @Parameter(names = ["--cex-solver"], description = "Concretizer solver name") var concretizerSolver: String = "Z3", diff --git a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliProofTest.kt b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliProofTest.kt new file mode 100644 index 0000000000..3d29c73fb0 --- /dev/null +++ b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliProofTest.kt @@ -0,0 +1,136 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.xcfa.cli + +import hu.bme.mit.theta.xcfa.cli.XcfaCli.Companion.main +import java.util.stream.Stream +import kotlin.io.path.absolutePathString +import kotlin.io.path.createTempDirectory +import kotlin.io.path.exists +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +data class WitnessEdge( + val startlineRange: Pair?, + val endlineRange: Pair?, + val startoffsetRange: Pair?, + val endoffsetRange: Pair?, + val assumption: Regex?, +) + +class XcfaCliProofTest { + companion object { + + @JvmStatic + fun cFiles(): Stream { + return Stream.of( + Arguments.of( + "/c/litmustest/singlethread/witness_test.c", + null, + listOf( + WitnessEdge( + startlineRange = Pair(5, 5), + endlineRange = Pair(5, 5), + startoffsetRange = Pair(100, 130), + endoffsetRange = Pair(100, 130), + assumption = Regex("i *== *-1"), + ) + ), + ), + Arguments.of( + "/c/litmustest/singlethread/witness_test.c", + "--backend BOUNDED", + listOf( + WitnessEdge( + startlineRange = Pair(5, 5), + endlineRange = Pair(5, 5), + startoffsetRange = Pair(100, 130), + endoffsetRange = Pair(100, 130), + assumption = Regex("i *== *-1"), + ) + ), + ), + ) + } + } + + @ParameterizedTest + @MethodSource("cFiles") + fun testCWitness(filePath: String, extraArgs: String?, expectedWitnessEdges: List) { + val temp = createTempDirectory() + val params = + arrayOf( + "--enable-output", + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), + "--stacktrace", + "--output-directory", + temp.absolutePathString(), + "--debug", + ) + main(params) + Assertions.assertTrue(temp.resolve("witness.graphml").exists()) + val witnessContents = temp.resolve("witness.graphml").toFile().readText() + val edges = mutableListOf>() + val edgeMatcher = Regex("(?s)(.*)") + val data = mutableMapOf() + for (dataMatch in dataMatcher.findAll(match.value)) { + val (key, value) = dataMatch.destructured + data.put(key, value) + } + edges.add(data) + println( + "Found edge containing data: ${data.entries.map { "${it.key}: ${it.value}" }.joinToString(", ")}" + ) + } + for (expectedWitnessEdge in expectedWitnessEdges) { + Assertions.assertFalse( + edges.none { edge -> + val startline = + expectedWitnessEdge.startlineRange + ?.let { edge["startline"]?.let { v -> Pair(it, Integer.parseInt(v)) } } + ?.let { it.first.first <= it.second && it.second <= it.first.second } ?: false + val endline = + expectedWitnessEdge.endlineRange + ?.let { edge["endline"]?.let { v -> Pair(it, Integer.parseInt(v)) } } + ?.let { it.first.first <= it.second && it.second <= it.first.second } ?: false + val startoffset = + expectedWitnessEdge.startoffsetRange + ?.let { edge["startoffset"]?.let { v -> Pair(it, Integer.parseInt(v)) } } + ?.let { it.first.first <= it.second && it.second <= it.first.second } ?: false + val endoffset = + expectedWitnessEdge.endoffsetRange + ?.let { edge["endoffset"]?.let { v -> Pair(it, Integer.parseInt(v)) } } + ?.let { it.first.first <= it.second && it.second <= it.first.second } ?: false + val assumption = + expectedWitnessEdge.assumption + ?.let { edge["assumption"]?.let { v -> Pair(it, v) } } + ?.let { it.first.matches(it.second) } ?: false + startline && endline && startoffset && endoffset && assumption + }, + "Expected witness edge not found: $expectedWitnessEdge", + ) + } + temp.toFile().deleteRecursively() + } +} diff --git a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliWitnessTest.kt b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliWitnessTest.kt deleted file mode 100644 index bc5657f5ba..0000000000 --- a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliWitnessTest.kt +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2024 Budapest University of Technology and Economics - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package hu.bme.mit.theta.xcfa.cli - -import hu.bme.mit.theta.xcfa.cli.XcfaCli.Companion.main -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.MethodSource -import java.util.stream.Stream -import kotlin.io.path.absolutePathString -import kotlin.io.path.createTempDirectory -import kotlin.io.path.exists - -data class WitnessEdge( - val startlineRange: Pair?, - val endlineRange: Pair?, - val startoffsetRange: Pair?, - val endoffsetRange: Pair?, - val assumption: Regex?, -) - -class XcfaCliWitnessTest { - companion object { - - @JvmStatic - fun cFiles(): Stream { - return Stream.of( - Arguments.of("/c/litmustest/singlethread/witness_test.c", null, listOf( - WitnessEdge( - startlineRange = Pair(5, 5), - endlineRange = Pair(5, 5), - startoffsetRange = Pair(100, 130), - endoffsetRange = Pair(100, 130), - assumption = Regex("i *== *-1"), - ), - )), - Arguments.of("/c/litmustest/singlethread/witness_test.c", "--backend BOUNDED", listOf( - WitnessEdge( - startlineRange = Pair(5, 5), - endlineRange = Pair(5, 5), - startoffsetRange = Pair(100, 130), - endoffsetRange = Pair(100, 130), - assumption = Regex("i *== *-1"), - ), - )), - ) - } - - } - - @ParameterizedTest - @MethodSource("cFiles") - fun testCWitness(filePath: String, extraArgs: String?, expectedWitnessEdges: List) { - val temp = createTempDirectory() - val params = arrayOf( - "--enable-output", - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), - "--stacktrace", - "--output-directory", temp.absolutePathString(), - "--debug", - ) - main(params) - Assertions.assertTrue(temp.resolve("witness.graphml").exists()) - val witnessContents = temp.resolve("witness.graphml").toFile().readText() - val edges = mutableListOf>() - val edgeMatcher = Regex("(?s)(.*)") - val data = mutableMapOf() - for (dataMatch in dataMatcher.findAll(match.value)) { - val (key, value) = dataMatch.destructured - data.put(key, value) - } - edges.add(data) - println("Found edge containing data: ${data.entries.map { "${it.key}: ${it.value}" }.joinToString(", ")}") - } - for (expectedWitnessEdge in expectedWitnessEdges) { - Assertions.assertFalse( - edges.none { edge -> - val startline = expectedWitnessEdge.startlineRange?.let { - edge["startline"]?.let { v -> - Pair(it, Integer.parseInt(v)) - } - }?.let { it.first.first <= it.second && it.second <= it.first.second } ?: false - val endline = expectedWitnessEdge.endlineRange?.let { - edge["endline"]?.let { v -> - Pair(it, Integer.parseInt(v)) - } - }?.let { it.first.first <= it.second && it.second <= it.first.second } ?: false - val startoffset = expectedWitnessEdge.startoffsetRange?.let { - edge["startoffset"]?.let { v -> - Pair(it, Integer.parseInt(v)) - } - }?.let { it.first.first <= it.second && it.second <= it.first.second } ?: false - val endoffset = expectedWitnessEdge.endoffsetRange?.let { - edge["endoffset"]?.let { v -> - Pair(it, Integer.parseInt(v)) - } - }?.let { it.first.first <= it.second && it.second <= it.first.second } ?: false - val assumption = expectedWitnessEdge.assumption?.let { - edge["assumption"]?.let { v -> - Pair(it, v) - } - }?.let { it.first.matches(it.second) } ?: false - startline && endline && startoffset && endoffset && assumption - }, - "Expected witness edge not found: $expectedWitnessEdge" - ) - } - temp.toFile().deleteRecursively() - } - - -} diff --git a/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/config/XstsConfigBuilder.java b/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/config/XstsConfigBuilder.java index 35b38b2a27..016d828fcb 100644 --- a/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/config/XstsConfigBuilder.java +++ b/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/config/XstsConfigBuilder.java @@ -13,18 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xsts.analysis.config; +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; + import hu.bme.mit.theta.analysis.*; +import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder; import hu.bme.mit.theta.analysis.algorithm.arg.ArgNodeComparators; -import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.BasicAbstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker; -import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgCegarChecker; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgRefiner; +import hu.bme.mit.theta.analysis.algorithm.cegar.BasicArgAbstractor; import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterion; import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterions; import hu.bme.mit.theta.analysis.expl.*; @@ -60,16 +62,12 @@ import hu.bme.mit.theta.xsts.analysis.initprec.*; import hu.bme.mit.theta.xsts.analysis.util.XstsCombineExtractUtilsKt; import hu.bme.mit.theta.xsts.analysis.util.XstsControlInitFuncKt; - import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; -import static com.google.common.base.Preconditions.checkState; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; - public class XstsConfigBuilder { //////////// CEGAR configuration //////////// @@ -94,20 +92,26 @@ public enum Domain { public enum Refinement { FW_BIN_ITP { @Override - public ExprTraceChecker getItpExprTraceChecker(Expr init, Expr target, ItpSolver solver) { + public ExprTraceChecker getItpExprTraceChecker( + Expr init, Expr target, ItpSolver solver) { return ExprTraceFwBinItpChecker.create(init, target, solver); } - }, BW_BIN_ITP { + }, + BW_BIN_ITP { @Override - public ExprTraceChecker getItpExprTraceChecker(Expr init, Expr target, ItpSolver solver) { + public ExprTraceChecker getItpExprTraceChecker( + Expr init, Expr target, ItpSolver solver) { return ExprTraceBwBinItpChecker.create(init, target, solver); } - }, SEQ_ITP { + }, + SEQ_ITP { @Override - public ExprTraceChecker getItpExprTraceChecker(Expr init, Expr target, ItpSolver solver) { + public ExprTraceChecker getItpExprTraceChecker( + Expr init, Expr target, ItpSolver solver) { return ExprTraceSeqItpChecker.create(init, target, solver); } - }, UNSAT_CORE, + }, + UNSAT_CORE, MULTI_SEQ { @Override public StopCriterion getStopCriterion() { @@ -115,19 +119,20 @@ public StopCriterion getStopCriterion() { } @Override - public ExprTraceChecker getItpExprTraceChecker(Expr init, Expr target, ItpSolver solver) { + public ExprTraceChecker getItpExprTraceChecker( + Expr init, Expr target, ItpSolver solver) { return ExprTraceSeqItpChecker.create(init, target, solver); } @Override - public Refiner, XstsAction, P> - createRefiner( - ExprTraceChecker traceChecker, - RefutationToPrec refToPrec, - PruneStrategy pruneStrategy, - Logger logger - ) { - return MultiExprTraceRefiner.create(traceChecker, JoiningPrecRefiner.create(refToPrec), pruneStrategy, logger); + public + ArgRefiner, XstsAction, P> createRefiner( + ExprTraceChecker traceChecker, + RefutationToPrec refToPrec, + PruneStrategy pruneStrategy, + Logger logger) { + return MultiExprTraceRefiner.create( + traceChecker, JoiningPrecRefiner.create(refToPrec), pruneStrategy, logger); } }; @@ -136,23 +141,22 @@ public StopCriterion getStopCriterion() { } public ExprTraceChecker getItpExprTraceChecker( - final Expr init, - final Expr target, - final ItpSolver solver - ) { - throw new UnsupportedOperationException(String.format("%s domain can't provide trace checker of ItpRefutation", this.getClass().getSimpleName())); + final Expr init, final Expr target, final ItpSolver solver) { + throw new UnsupportedOperationException( + String.format( + "%s domain can't provide trace checker of ItpRefutation", + this.getClass().getSimpleName())); + } + + public + ArgRefiner, XstsAction, P> createRefiner( + ExprTraceChecker traceChecker, + RefutationToPrec refToPrec, + PruneStrategy pruneStrategy, + Logger logger) { + return SingleExprTraceRefiner.create( + traceChecker, JoiningPrecRefiner.create(refToPrec), pruneStrategy, logger); } - - public Refiner, XstsAction, P> - createRefiner( - ExprTraceChecker traceChecker, - RefutationToPrec refToPrec, - PruneStrategy pruneStrategy, - Logger logger - ) { - return SingleExprTraceRefiner.create(traceChecker, JoiningPrecRefiner.create(refToPrec), pruneStrategy, logger); - } - } public enum Search { @@ -165,7 +169,6 @@ public enum Search { Search(final ArgNodeComparators.ArgNodeComparator comparator) { this.comparator = comparator; } - } public enum PredSplit { @@ -196,7 +199,6 @@ public enum InitPrec { InitPrec(final XstsInitPrec builder) { this.builder = builder; } - } public enum AutoExpl { @@ -214,7 +216,8 @@ public enum AutoExpl { } public enum OptimizeStmts { - ON, OFF + ON, + OFF } private Logger logger = NullLogger.getInstance(); @@ -230,8 +233,11 @@ public enum OptimizeStmts { private OptimizeStmts optimizeStmts = OptimizeStmts.ON; private AutoExpl autoExpl = AutoExpl.NEWOPERANDS; - public XstsConfigBuilder(final Domain domain, final Refinement refinement, - final SolverFactory abstractionSolverFactory, final SolverFactory refinementSolverFactory) { + public XstsConfigBuilder( + final Domain domain, + final Refinement refinement, + final SolverFactory abstractionSolverFactory, + final SolverFactory refinementSolverFactory) { this.domain = domain; this.refinement = refinement; this.abstractionSolverFactory = abstractionSolverFactory; @@ -282,12 +288,15 @@ public XstsConfigBuilder autoExpl(final AutoExpl autoExpl) { if (domain == Domain.EXPL) { return (new ExplStrategy(xsts)).buildConfig(); } - if (domain == Domain.PRED_BOOL || domain == Domain.PRED_CART + if (domain == Domain.PRED_BOOL + || domain == Domain.PRED_CART || domain == Domain.PRED_SPLIT) { return (new PredStrategy(xsts)).buildConfig(); } - if (domain == Domain.EXPL_PRED_BOOL || domain == Domain.EXPL_PRED_CART - || domain == Domain.EXPL_PRED_SPLIT || domain == Domain.EXPL_PRED_COMBINED) { + if (domain == Domain.EXPL_PRED_BOOL + || domain == Domain.EXPL_PRED_CART + || domain == Domain.EXPL_PRED_SPLIT + || domain == Domain.EXPL_PRED_COMBINED) { return (new ProdStrategy(xsts)).buildConfig(); } throw new UnsupportedOperationException(domain + " domain is not supported."); @@ -295,15 +304,26 @@ public XstsConfigBuilder autoExpl(final AutoExpl autoExpl) { public abstract class BuilderStrategy { - protected static final String UNSUPPORTED_CONFIG_VALUE = "Builder strategy %s does not support configuration value %s as %s"; + protected static final String UNSUPPORTED_CONFIG_VALUE = + "Builder strategy %s does not support configuration value %s as %s"; protected final XSTS xsts; protected final Solver abstractionSolver; protected final Expr negProp; @SuppressWarnings("java:S1699") protected BuilderStrategy(XSTS xsts) { - checkState(getSupportedDomains().contains(domain), UNSUPPORTED_CONFIG_VALUE, getClass().getSimpleName(), domain, "domain"); - checkState(getSupportedRefinements().contains(refinement), UNSUPPORTED_CONFIG_VALUE, getClass().getSimpleName(), refinement, "refinement"); + checkState( + getSupportedDomains().contains(domain), + UNSUPPORTED_CONFIG_VALUE, + getClass().getSimpleName(), + domain, + "domain"); + checkState( + getSupportedRefinements().contains(refinement), + UNSUPPORTED_CONFIG_VALUE, + getClass().getSimpleName(), + refinement, + "refinement"); this.xsts = xsts; abstractionSolver = abstractionSolverFactory.createSolver(); negProp = Not(xsts.getProp()); @@ -336,7 +356,7 @@ public XstsAnalysis getAnalysis() { public abstract RefutationToPrec getItpRefToPrec(); - public Refiner, XstsAction, P> getRefiner() { + public ArgRefiner, XstsAction, P> getRefiner() { return refinement.createRefiner( refinement.getItpExprTraceChecker( xsts.getInitFormula(), @@ -353,22 +373,22 @@ XstsConfig, XstsAction, P> buildConfig() { final LTS, XstsAction> lts = getLts(); final Predicate> target = getPredicate(); final Analysis, XstsAction, P> analysis = getAnalysis(); - final ArgBuilder, XstsAction, P> argBuilder = ArgBuilder.create( - lts, analysis, target, - true); - final Abstractor, XstsAction, P> abstractor = BasicAbstractor.builder( - argBuilder) - .waitlist(PriorityWaitlist.create(search.comparator)) - .stopCriterion(refinement.getStopCriterion()) - .logger(logger).build(); - final Refiner, XstsAction, P> refiner = getRefiner(); - final SafetyChecker, XstsAction>, Trace, XstsAction>, P> checker = CegarChecker.create( - abstractor, refiner, - logger); + final ArgBuilder, XstsAction, P> argBuilder = + ArgBuilder.create(lts, analysis, target, true); + final ArgAbstractor, XstsAction, P> abstractor = + BasicArgAbstractor.builder(argBuilder) + .waitlist(PriorityWaitlist.create(search.comparator)) + .stopCriterion(refinement.getStopCriterion()) + .logger(logger) + .build(); + final ArgRefiner, XstsAction, P> refiner = getRefiner(); + final SafetyChecker, XstsAction>, Trace, XstsAction>, P> + checker = ArgCegarChecker.create(abstractor, refiner, logger); return XstsConfig.create(checker, getInitPrec()); } - public MultiAnalysisSide, S, XstsState, XstsAction, P, UnitPrec> getMultiSide() { + public MultiAnalysisSide, S, XstsState, XstsAction, P, UnitPrec> + getMultiSide() { return new MultiAnalysisSide<>( getAnalysis(), XstsControlInitFuncKt.xstsControlInitFunc(), @@ -377,7 +397,6 @@ public MultiAnalysisSide, S, XstsState, XstsAction, P, U XstsCombineExtractUtilsKt::xstsExtractDataState, XstsCombineExtractUtilsKt::xstsExtractControlPrec); } - } public class ExplStrategy extends BuilderStrategy { @@ -394,8 +413,12 @@ Set getSupportedRefinements() { public ExplStrategy(XSTS xsts) { super(xsts); - checkState(domain == Domain.EXPL, - UNSUPPORTED_CONFIG_VALUE, this.getClass().getSimpleName(), domain, "domain"); + checkState( + domain == Domain.EXPL, + UNSUPPORTED_CONFIG_VALUE, + this.getClass().getSimpleName(), + domain, + "domain"); } @Override @@ -419,12 +442,16 @@ public RefutationToPrec getItpRefToPrec() { } @Override - public Refiner, XstsAction, ExplPrec> getRefiner() { + public ArgRefiner, XstsAction, ExplPrec> getRefiner() { if (refinement == Refinement.UNSAT_CORE) { return SingleExprTraceRefiner.create( - ExprTraceUnsatCoreChecker.create(xsts.getInitFormula(), negProp, + ExprTraceUnsatCoreChecker.create( + xsts.getInitFormula(), + negProp, refinementSolverFactory.createUCSolver()), - JoiningPrecRefiner.create(new VarsRefToExplPrec()), pruneStrategy, logger); + JoiningPrecRefiner.create(new VarsRefToExplPrec()), + pruneStrategy, + logger); } return super.getRefiner(); } @@ -433,7 +460,6 @@ public Refiner, XstsAction, ExplPrec> getRefiner() { public ExplPrec getInitPrec() { return initPrec.builder.createExpl(xsts); } - } public class PredStrategy extends BuilderStrategy { @@ -444,13 +470,24 @@ Set getSupportedDomains() { @Override Set getSupportedRefinements() { - return new HashSet<>(List.of(Refinement.FW_BIN_ITP, Refinement.BW_BIN_ITP, Refinement.SEQ_ITP, Refinement.MULTI_SEQ)); + return new HashSet<>( + List.of( + Refinement.FW_BIN_ITP, + Refinement.BW_BIN_ITP, + Refinement.SEQ_ITP, + Refinement.MULTI_SEQ)); } public PredStrategy(XSTS xsts) { super(xsts); - checkState(domain == Domain.PRED_BOOL || domain == Domain.PRED_SPLIT || domain == Domain.PRED_CART, - UNSUPPORTED_CONFIG_VALUE, this.getClass().getSimpleName(), domain, "domain"); + checkState( + domain == Domain.PRED_BOOL + || domain == Domain.PRED_SPLIT + || domain == Domain.PRED_CART, + UNSUPPORTED_CONFIG_VALUE, + this.getClass().getSimpleName(), + domain, + "domain"); } @Override @@ -482,55 +519,80 @@ public PredPrec getInitPrec() { } } - public class ProdStrategy extends BuilderStrategy, Prod2Prec> { + public class ProdStrategy + extends BuilderStrategy< + Prod2State, Prod2Prec> { @Override Set getSupportedDomains() { - return new HashSet<>(List.of(Domain.EXPL_PRED_BOOL, Domain.EXPL_PRED_CART, Domain.EXPL_PRED_SPLIT, Domain.EXPL_PRED_COMBINED)); + return new HashSet<>( + List.of( + Domain.EXPL_PRED_BOOL, + Domain.EXPL_PRED_CART, + Domain.EXPL_PRED_SPLIT, + Domain.EXPL_PRED_COMBINED)); } @Override Set getSupportedRefinements() { - return new HashSet<>(List.of(Refinement.FW_BIN_ITP, Refinement.BW_BIN_ITP, Refinement.SEQ_ITP, Refinement.MULTI_SEQ)); + return new HashSet<>( + List.of( + Refinement.FW_BIN_ITP, + Refinement.BW_BIN_ITP, + Refinement.SEQ_ITP, + Refinement.MULTI_SEQ)); } public ProdStrategy(XSTS xsts) { super(xsts); - checkState(domain == Domain.EXPL_PRED_BOOL || domain == Domain.EXPL_PRED_SPLIT - || domain == Domain.EXPL_PRED_CART || domain == Domain.EXPL_PRED_COMBINED, - UNSUPPORTED_CONFIG_VALUE, this.getClass().getSimpleName(), domain, "domain"); - checkState(refinement != Refinement.UNSAT_CORE, UNSUPPORTED_CONFIG_VALUE, getClass().getSimpleName(), refinement, "refinement"); + checkState( + domain == Domain.EXPL_PRED_BOOL + || domain == Domain.EXPL_PRED_SPLIT + || domain == Domain.EXPL_PRED_CART + || domain == Domain.EXPL_PRED_COMBINED, + UNSUPPORTED_CONFIG_VALUE, + this.getClass().getSimpleName(), + domain, + "domain"); + checkState( + refinement != Refinement.UNSAT_CORE, + UNSUPPORTED_CONFIG_VALUE, + getClass().getSimpleName(), + refinement, + "refinement"); } @Override StmtOptimizer> getLtsOptimizer() { - return Prod2ExplPredStmtOptimizer.create( - ExplStmtOptimizer.getInstance() - ); + return Prod2ExplPredStmtOptimizer.create(ExplStmtOptimizer.getInstance()); } @Override public Predicate>> getPredicate() { - return new XstsStatePredicate<>( - new ExprStatePredicate(negProp, abstractionSolver)); + return new XstsStatePredicate<>(new ExprStatePredicate(negProp, abstractionSolver)); } @Override - public Analysis, StmtAction, Prod2Prec> getDataAnalysis() { - if (domain == Domain.EXPL_PRED_BOOL || domain == Domain.EXPL_PRED_CART + public Analysis, StmtAction, Prod2Prec> + getDataAnalysis() { + if (domain == Domain.EXPL_PRED_BOOL + || domain == Domain.EXPL_PRED_CART || domain == Domain.EXPL_PRED_SPLIT) { - final PredAbstractors.PredAbstractor predAbstractor = domain.predAbstractorFunction.apply(abstractionSolver); + final PredAbstractors.PredAbstractor predAbstractor = + domain.predAbstractorFunction.apply(abstractionSolver); return Prod2Analysis.create( ExplStmtAnalysis.create(abstractionSolver, xsts.getInitFormula(), maxEnum), - PredAnalysis.create(abstractionSolver, predAbstractor, xsts.getInitFormula()), + PredAnalysis.create( + abstractionSolver, predAbstractor, xsts.getInitFormula()), Prod2ExplPredPreStrengtheningOperator.create(), Prod2ExplPredStrengtheningOperator.create(abstractionSolver)); } else { - final Prod2ExplPredAbstractors.Prod2ExplPredAbstractor prodAbstractor = Prod2ExplPredAbstractors.booleanAbstractor( - abstractionSolver); + final Prod2ExplPredAbstractors.Prod2ExplPredAbstractor prodAbstractor = + Prod2ExplPredAbstractors.booleanAbstractor(abstractionSolver); return Prod2ExplPredAnalysis.create( ExplAnalysis.create(abstractionSolver, xsts.getInitFormula()), - PredAnalysis.create(abstractionSolver, + PredAnalysis.create( + abstractionSolver, PredAbstractors.booleanAbstractor(abstractionSolver), xsts.getInitFormula()), Prod2ExplPredStrengtheningOperator.create(abstractionSolver), @@ -548,7 +610,5 @@ public RefutationToPrec, ItpRefutation> getItpRefT public Prod2Prec getInitPrec() { return initPrec.builder.createProd2ExplPred(xsts); } - } - -} \ No newline at end of file +} diff --git a/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/mdd/XstsMddChecker.java b/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/mdd/XstsMddChecker.java index ae9262e03f..b8ae5f098e 100644 --- a/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/mdd/XstsMddChecker.java +++ b/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/mdd/XstsMddChecker.java @@ -15,30 +15,32 @@ */ package hu.bme.mit.theta.xsts.analysis.mdd; +import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; +import static hu.bme.mit.theta.core.type.booltype.SmartBoolExprs.Not; + import com.google.common.base.Preconditions; -import hu.bme.mit.delta.collections.impl.RecursiveIntObjMapViews; -import hu.bme.mit.delta.java.mdd.*; +import hu.bme.mit.delta.java.mdd.JavaMddFactory; +import hu.bme.mit.delta.java.mdd.MddGraph; +import hu.bme.mit.delta.java.mdd.MddHandle; +import hu.bme.mit.delta.java.mdd.MddVariableOrder; import hu.bme.mit.delta.mdd.MddInterpreter; import hu.bme.mit.delta.mdd.MddVariableDescriptor; -import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; +import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.algorithm.mdd.MddAnalysisStatistics; import hu.bme.mit.theta.analysis.algorithm.mdd.MddCex; import hu.bme.mit.theta.analysis.algorithm.mdd.MddChecker.IterationStrategy; -import hu.bme.mit.theta.analysis.algorithm.mdd.MddWitness; -import hu.bme.mit.theta.analysis.algorithm.mdd.fixedpoint.*; +import hu.bme.mit.theta.analysis.algorithm.mdd.MddProof; import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.AbstractNextStateDescriptor; +import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.impl.MddNodeInitializer; +import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.impl.MddNodeNextStateDescriptor; import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.impl.OrNextStateDescriptor; -import hu.bme.mit.theta.common.logging.Logger; -import hu.bme.mit.theta.common.logging.Logger.Level; -import hu.bme.mit.theta.solver.SolverPool; import hu.bme.mit.theta.analysis.algorithm.mdd.expressionnode.ExprLatticeDefinition; import hu.bme.mit.theta.analysis.algorithm.mdd.expressionnode.MddExpressionTemplate; -import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.impl.MddNodeInitializer; -import hu.bme.mit.theta.analysis.algorithm.mdd.ansd.impl.MddNodeNextStateDescriptor; -import hu.bme.mit.theta.analysis.utils.MddNodeVisualizer; -import hu.bme.mit.theta.common.visualization.Graph; -import hu.bme.mit.theta.common.visualization.writer.GraphvizWriter; +import hu.bme.mit.theta.analysis.algorithm.mdd.fixedpoint.*; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.core.decl.Decl; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -48,17 +50,12 @@ import hu.bme.mit.theta.core.utils.PathUtils; import hu.bme.mit.theta.core.utils.StmtUtils; import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory; +import hu.bme.mit.theta.solver.SolverPool; import hu.bme.mit.theta.xsts.XSTS; - -import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; -import static hu.bme.mit.theta.core.type.booltype.SmartBoolExprs.Not; - -public class XstsMddChecker implements SafetyChecker { +public class XstsMddChecker implements SafetyChecker { private final SolverPool solverPool; private final XSTS xsts; @@ -66,7 +63,8 @@ public class XstsMddChecker implements SafetyChecker { private final Logger logger; private IterationStrategy iterationStrategy; - private XstsMddChecker(XSTS xsts, SolverPool solverPool, Logger logger, IterationStrategy iterationStrategy) { + private XstsMddChecker( + XSTS xsts, SolverPool solverPool, Logger logger, IterationStrategy iterationStrategy) { this.xsts = xsts; this.solverPool = solverPool; this.logger = logger; @@ -77,33 +75,64 @@ public static XstsMddChecker create(XSTS xsts, SolverPool solverPool, Logger log return new XstsMddChecker(xsts, solverPool, logger, IterationStrategy.GSAT); } - public static XstsMddChecker create(XSTS xsts, SolverPool solverPool, Logger logger, IterationStrategy iterationStrategy) { + public static XstsMddChecker create( + XSTS xsts, SolverPool solverPool, Logger logger, IterationStrategy iterationStrategy) { return new XstsMddChecker(xsts, solverPool, logger, iterationStrategy); } @Override - public SafetyResult check(Void input) { - - final MddGraph mddGraph = JavaMddFactory.getDefault().createMddGraph(ExprLatticeDefinition.forExpr()); - - final MddVariableOrder stateOrder = JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); - final MddVariableOrder transOrder = JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); - final MddVariableOrder initOrder = JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); - - final NonDetStmt envTran = NonDetStmt.of(xsts.getEnv().getStmts().stream().flatMap(e -> xsts.getTran().getStmts().stream().map(t -> (Stmt) SequenceStmt.of(List.of(e, t)))).toList()); + public SafetyResult check(Void input) { + + final MddGraph mddGraph = + JavaMddFactory.getDefault().createMddGraph(ExprLatticeDefinition.forExpr()); + + final MddVariableOrder stateOrder = + JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); + final MddVariableOrder transOrder = + JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); + final MddVariableOrder initOrder = + JavaMddFactory.getDefault().createMddVariableOrder(mddGraph); + + final NonDetStmt envTran = + NonDetStmt.of( + xsts.getEnv().getStmts().stream() + .flatMap( + e -> + xsts.getTran().getStmts().stream() + .map( + t -> + (Stmt) + SequenceStmt.of( + List.of( + e, + t)))) + .toList()); final var envTranToExprResult = StmtUtils.toExpr(envTran, VarIndexingFactory.indexing(0)); - final var initToExprResult = StmtUtils.toExpr(xsts.getInit(), VarIndexingFactory.indexing(0)); + final var initToExprResult = + StmtUtils.toExpr(xsts.getInit(), VarIndexingFactory.indexing(0)); for (var v : xsts.getVars()) { final var domainSize = /*v.getType() instanceof BoolType ? 2 :*/ 0; stateOrder.createOnTop(MddVariableDescriptor.create(v.getConstDecl(0), domainSize)); - transOrder.createOnTop(MddVariableDescriptor.create(v.getConstDecl(envTranToExprResult.getIndexing().get(v) == 0 ? 1 : envTranToExprResult.getIndexing().get(v)), domainSize)); + transOrder.createOnTop( + MddVariableDescriptor.create( + v.getConstDecl( + envTranToExprResult.getIndexing().get(v) == 0 + ? 1 + : envTranToExprResult.getIndexing().get(v)), + domainSize)); transOrder.createOnTop(MddVariableDescriptor.create(v.getConstDecl(0), domainSize)); // TODO if indexes are identical, inject v'=v - initOrder.createOnTop(MddVariableDescriptor.create(v.getConstDecl(initToExprResult.getIndexing().get(v) == 0 ? 1 : initToExprResult.getIndexing().get(v)), domainSize)); + initOrder.createOnTop( + MddVariableDescriptor.create( + v.getConstDecl( + initToExprResult.getIndexing().get(v) == 0 + ? 1 + : initToExprResult.getIndexing().get(v)), + domainSize)); initOrder.createOnTop(MddVariableDescriptor.create(v.getConstDecl(0), domainSize)); } @@ -112,21 +141,30 @@ public SafetyResult check(Void input) { final var initSig = initOrder.getDefaultSetSignature(); final Expr initExpr = PathUtils.unfold(xsts.getInitFormula(), 0); - final MddHandle initNode = stateSig.getTopVariableHandle().checkInNode(MddExpressionTemplate.of(initExpr, o -> (Decl) o, solverPool)); + final MddHandle initNode = + stateSig.getTopVariableHandle() + .checkInNode(MddExpressionTemplate.of(initExpr, o -> (Decl) o, solverPool)); Preconditions.checkState(initToExprResult.getExprs().size() == 1); - final var initUnfold = PathUtils.unfold(initToExprResult.getExprs().stream().findFirst().get(), 0); + final var initUnfold = + PathUtils.unfold(initToExprResult.getExprs().stream().findFirst().get(), 0); final var initIdentityExprs = new ArrayList>(); for (var v : xsts.getVars()) { if (initToExprResult.getIndexing().get(v) == 0) initIdentityExprs.add(Eq(v.getConstDecl(0).getRef(), v.getConstDecl(1).getRef())); } final var initExprWithIdentity = And(initUnfold, And(initIdentityExprs)); - final MddHandle initTranNode = initSig.getTopVariableHandle().checkInNode(MddExpressionTemplate.of(initExprWithIdentity, o -> (Decl) o, solverPool)); - final AbstractNextStateDescriptor initNextState = MddNodeNextStateDescriptor.of(initTranNode); + final MddHandle initTranNode = + initSig.getTopVariableHandle() + .checkInNode( + MddExpressionTemplate.of( + initExprWithIdentity, o -> (Decl) o, solverPool)); + final AbstractNextStateDescriptor initNextState = + MddNodeNextStateDescriptor.of(initTranNode); final var rel = new LegacyRelationalProductProvider(stateSig.getVariableOrder()); - final var initResult = rel.compute(initNode, initNextState, stateSig.getTopVariableHandle()); + final var initResult = + rel.compute(initNode, initNextState, stateSig.getTopVariableHandle()); logger.write(Level.INFO, "Created initial node"); @@ -138,13 +176,21 @@ public SafetyResult check(Void input) { final var identityExprs = new ArrayList>(); for (var v : xsts.getVars()) { if (stmtToExpr.getIndexing().get(v) < envTranToExprResult.getIndexing().get(v)) - identityExprs.add(Eq(v.getConstDecl(stmtToExpr.getIndexing().get(v)).getRef(), v.getConstDecl(envTranToExprResult.getIndexing().get(v)).getRef())); + identityExprs.add( + Eq( + v.getConstDecl(stmtToExpr.getIndexing().get(v)).getRef(), + v.getConstDecl(envTranToExprResult.getIndexing().get(v)) + .getRef())); if (envTranToExprResult.getIndexing().get(v) == 0) identityExprs.add(Eq(v.getConstDecl(0).getRef(), v.getConstDecl(1).getRef())); } if (!identityExprs.isEmpty()) stmtUnfold = And(stmtUnfold, And(identityExprs)); - MddHandle transitionNode = transSig.getTopVariableHandle().checkInNode(MddExpressionTemplate.of(stmtUnfold, o -> (Decl) o, solverPool)); + MddHandle transitionNode = + transSig.getTopVariableHandle() + .checkInNode( + MddExpressionTemplate.of( + stmtUnfold, o -> (Decl) o, solverPool)); descriptors.add(MddNodeNextStateDescriptor.of(transitionNode)); } final AbstractNextStateDescriptor nextStates = OrNextStateDescriptor.create(descriptors); @@ -164,12 +210,20 @@ public SafetyResult check(Void input) { } default -> throw new IllegalStateException("Unexpected value: " + iterationStrategy); } - final MddHandle stateSpace = stateSpaceProvider.compute(MddNodeInitializer.of(initResult), nextStates, stateSig.getTopVariableHandle()); + final MddHandle stateSpace = + stateSpaceProvider.compute( + MddNodeInitializer.of(initResult), + nextStates, + stateSig.getTopVariableHandle()); logger.write(Level.INFO, "Enumerated state-space"); final Expr negatedPropExpr = PathUtils.unfold(Not(xsts.getProp()), 0); - final MddHandle propNode = stateSig.getTopVariableHandle().checkInNode(MddExpressionTemplate.of(negatedPropExpr, o -> (Decl) o, solverPool)); + final MddHandle propNode = + stateSig.getTopVariableHandle() + .checkInNode( + MddExpressionTemplate.of( + negatedPropExpr, o -> (Decl) o, solverPool)); final MddHandle propViolating = (MddHandle) stateSpace.intersection(propNode); @@ -181,16 +235,23 @@ public SafetyResult check(Void input) { final Long stateSpaceSize = MddInterpreter.calculateNonzeroCount(stateSpace); logger.write(Level.DETAIL, "State space size: " + stateSpaceSize); - final MddAnalysisStatistics statistics = new MddAnalysisStatistics(violatingSize, stateSpaceSize, stateSpaceProvider.getHitCount(), stateSpaceProvider.getQueryCount(), stateSpaceProvider.getCacheSize()); + final MddAnalysisStatistics statistics = + new MddAnalysisStatistics( + violatingSize, + stateSpaceSize, + stateSpaceProvider.getHitCount(), + stateSpaceProvider.getQueryCount(), + stateSpaceProvider.getCacheSize()); - final SafetyResult result; + final SafetyResult result; if (violatingSize != 0) { - result = SafetyResult.unsafe(MddCex.of(propViolating), MddWitness.of(stateSpace), statistics); + result = + SafetyResult.unsafe( + MddCex.of(propViolating), MddProof.of(stateSpace), statistics); } else { - result = SafetyResult.safe(MddWitness.of(stateSpace), statistics); + result = SafetyResult.safe(MddProof.of(stateSpace), statistics); } logger.write(Level.RESULT, "%s%n", result); return result; - } } diff --git a/subprojects/xsts/xsts-analysis/src/test/java/hu/bme/mit/theta/xsts/analysis/XstsMddCheckerTest.java b/subprojects/xsts/xsts-analysis/src/test/java/hu/bme/mit/theta/xsts/analysis/XstsMddCheckerTest.java index bda3053ed6..4208cdfd6a 100644 --- a/subprojects/xsts/xsts-analysis/src/test/java/hu/bme/mit/theta/xsts/analysis/XstsMddCheckerTest.java +++ b/subprojects/xsts/xsts-analysis/src/test/java/hu/bme/mit/theta/xsts/analysis/XstsMddCheckerTest.java @@ -15,10 +15,12 @@ */ package hu.bme.mit.theta.xsts.analysis; +import static org.junit.Assert.assertTrue; + import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.algorithm.mdd.MddCex; import hu.bme.mit.theta.analysis.algorithm.mdd.MddChecker.IterationStrategy; -import hu.bme.mit.theta.analysis.algorithm.mdd.MddWitness; +import hu.bme.mit.theta.analysis.algorithm.mdd.MddProof; import hu.bme.mit.theta.common.logging.ConsoleLogger; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.solver.SolverPool; @@ -26,99 +28,159 @@ import hu.bme.mit.theta.xsts.XSTS; import hu.bme.mit.theta.xsts.analysis.mdd.XstsMddChecker; import hu.bme.mit.theta.xsts.dsl.XstsDslManager; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.io.FileInputStream; -import java.io.IOException; import java.io.InputStream; import java.io.SequenceInputStream; import java.util.Arrays; import java.util.Collection; -import static org.junit.Assert.assertTrue; - public class XstsMddCheckerTest { public static Collection data() { - return Arrays.asList(new Object[][]{ - -// { "src/test/resources/model/trafficlight.xsts", "src/test/resources/property/green_and_red.prop", true}, - - {"src/test/resources/model/trafficlight_v2.xsts", "src/test/resources/property/green_and_red.prop", true}, - - {"src/test/resources/model/counter5.xsts", "src/test/resources/property/x_between_0_and_5.prop", true}, - - {"src/test/resources/model/counter5.xsts", "src/test/resources/property/x_eq_5.prop", false}, - - {"src/test/resources/model/x_and_y.xsts", "src/test/resources/property/x_geq_y.prop", true}, - - {"src/test/resources/model/x_powers.xsts", "src/test/resources/property/x_even.prop", true}, - -// { "src/test/resources/model/cross_with.xsts", "src/test/resources/property/cross.prop", false}, - -// { "src/test/resources/model/cross_without.xsts", "src/test/resources/property/cross.prop", false}, - - {"src/test/resources/model/choices.xsts", "src/test/resources/property/choices.prop", false}, - -// { "src/test/resources/model/literals.xsts", "src/test/resources/property/literals.prop", true}, - -// { "src/test/resources/model/cross3.xsts", "src/test/resources/property/cross.prop", false}, - - {"src/test/resources/model/sequential.xsts", "src/test/resources/property/sequential.prop", true}, - - {"src/test/resources/model/sequential.xsts", "src/test/resources/property/sequential2.prop", false}, - - {"src/test/resources/model/on_off_statemachine.xsts", "src/test/resources/property/on_off_statemachine.prop", false}, - - {"src/test/resources/model/on_off_statemachine.xsts", "src/test/resources/property/on_off_statemachine2.prop", true}, - - {"src/test/resources/model/on_off_statemachine.xsts", "src/test/resources/property/on_off_statemachine3.prop", false}, - -// {"src/test/resources/model/counter50.xsts", "src/test/resources/property/x_eq_5.prop", false}, -// -// {"src/test/resources/model/counter50.xsts", "src/test/resources/property/x_eq_50.prop", false}, -// -// {"src/test/resources/model/counter50.xsts", "src/test/resources/property/x_eq_51.prop", true}, - - {"src/test/resources/model/count_up_down.xsts", "src/test/resources/property/count_up_down.prop", false}, - - {"src/test/resources/model/count_up_down.xsts", "src/test/resources/property/count_up_down2.prop", true}, - -// {"src/test/resources/model/bhmr2007.xsts", "src/test/resources/property/bhmr2007.prop", true}, -// -// {"src/test/resources/model/css2003.xsts", "src/test/resources/property/css2003.prop", true}, -// -// { "src/test/resources/model/array_counter.xsts", "src/test/resources/property/array_10.prop", false}, -// -// { "src/test/resources/model/array_constant.xsts", "src/test/resources/property/array_constant.prop", true}, -// -// { "src/test/resources/model/localvars.xsts", "src/test/resources/property/localvars.prop", true}, -// -// { "src/test/resources/model/localvars2.xsts", "src/test/resources/property/localvars2.prop", true}, -// -// { "src/test/resources/model/loopxy.xsts", "src/test/resources/property/loopxy.prop", true}, -// -// { "src/test/resources/model/arraywrite_sugar.xsts", "src/test/resources/property/arraywrite_sugar.prop", false}, -// -// { "src/test/resources/model/if1.xsts", "src/test/resources/property/if1.prop", true}, -// -// { "src/test/resources/model/if2.xsts", "src/test/resources/property/if2.prop", false} - }); + return Arrays.asList( + new Object[][] { + + // { "src/test/resources/model/trafficlight.xsts", + // "src/test/resources/property/green_and_red.prop", true}, + + { + "src/test/resources/model/trafficlight_v2.xsts", + "src/test/resources/property/green_and_red.prop", + true + }, + { + "src/test/resources/model/counter5.xsts", + "src/test/resources/property/x_between_0_and_5.prop", + true + }, + { + "src/test/resources/model/counter5.xsts", + "src/test/resources/property/x_eq_5.prop", + false + }, + { + "src/test/resources/model/x_and_y.xsts", + "src/test/resources/property/x_geq_y.prop", + true + }, + { + "src/test/resources/model/x_powers.xsts", + "src/test/resources/property/x_even.prop", + true + }, + + // { "src/test/resources/model/cross_with.xsts", + // "src/test/resources/property/cross.prop", false}, + + // { "src/test/resources/model/cross_without.xsts", + // "src/test/resources/property/cross.prop", false}, + + { + "src/test/resources/model/choices.xsts", + "src/test/resources/property/choices.prop", + false + }, + + // { "src/test/resources/model/literals.xsts", + // "src/test/resources/property/literals.prop", true}, + + // { "src/test/resources/model/cross3.xsts", + // "src/test/resources/property/cross.prop", false}, + + { + "src/test/resources/model/sequential.xsts", + "src/test/resources/property/sequential.prop", + true + }, + { + "src/test/resources/model/sequential.xsts", + "src/test/resources/property/sequential2.prop", + false + }, + { + "src/test/resources/model/on_off_statemachine.xsts", + "src/test/resources/property/on_off_statemachine.prop", + false + }, + { + "src/test/resources/model/on_off_statemachine.xsts", + "src/test/resources/property/on_off_statemachine2.prop", + true + }, + { + "src/test/resources/model/on_off_statemachine.xsts", + "src/test/resources/property/on_off_statemachine3.prop", + false + }, + + // {"src/test/resources/model/counter50.xsts", + // "src/test/resources/property/x_eq_5.prop", false}, + // + // {"src/test/resources/model/counter50.xsts", + // "src/test/resources/property/x_eq_50.prop", false}, + // + // {"src/test/resources/model/counter50.xsts", + // "src/test/resources/property/x_eq_51.prop", true}, + + { + "src/test/resources/model/count_up_down.xsts", + "src/test/resources/property/count_up_down.prop", + false + }, + { + "src/test/resources/model/count_up_down.xsts", + "src/test/resources/property/count_up_down2.prop", + true + }, + + // {"src/test/resources/model/bhmr2007.xsts", + // "src/test/resources/property/bhmr2007.prop", true}, + // + // {"src/test/resources/model/css2003.xsts", + // "src/test/resources/property/css2003.prop", true}, + // + // { "src/test/resources/model/array_counter.xsts", + // "src/test/resources/property/array_10.prop", false}, + // + // { "src/test/resources/model/array_constant.xsts", + // "src/test/resources/property/array_constant.prop", true}, + // + // { "src/test/resources/model/localvars.xsts", + // "src/test/resources/property/localvars.prop", true}, + // + // { "src/test/resources/model/localvars2.xsts", + // "src/test/resources/property/localvars2.prop", true}, + // + // { "src/test/resources/model/loopxy.xsts", + // "src/test/resources/property/loopxy.prop", true}, + // + // { "src/test/resources/model/arraywrite_sugar.xsts", + // "src/test/resources/property/arraywrite_sugar.prop", false}, + // + // { "src/test/resources/model/if1.xsts", + // "src/test/resources/property/if1.prop", true}, + // + // { "src/test/resources/model/if2.xsts", + // "src/test/resources/property/if2.prop", false} + }); } - public static void runTestWithIterationStrategy(String filePath, String propPath, boolean safe, IterationStrategy iterationStrategy) throws Exception { + public static void runTestWithIterationStrategy( + String filePath, String propPath, boolean safe, IterationStrategy iterationStrategy) + throws Exception { final Logger logger = new ConsoleLogger(Logger.Level.SUBSTEP); XSTS xsts; - try (InputStream inputStream = new SequenceInputStream(new FileInputStream(filePath), new FileInputStream(propPath))) { + try (InputStream inputStream = + new SequenceInputStream( + new FileInputStream(filePath), new FileInputStream(propPath))) { xsts = XstsDslManager.createXsts(inputStream); } - final SafetyResult status; + final SafetyResult status; try (var solverPool = new SolverPool(Z3LegacySolverFactory.getInstance())) { - final XstsMddChecker checker = XstsMddChecker.create(xsts, solverPool, logger, iterationStrategy); + final XstsMddChecker checker = + XstsMddChecker.create(xsts, solverPool, logger, iterationStrategy); status = checker.check(null); } @@ -128,5 +190,4 @@ public static void runTestWithIterationStrategy(String filePath, String propPath assertTrue(status.isUnsafe()); } } - } diff --git a/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliBounded.kt b/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliBounded.kt index b0582bf87f..7df23a5d53 100644 --- a/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliBounded.kt +++ b/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliBounded.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xsts.cli import com.github.ajalt.clikt.parameters.options.default @@ -21,7 +20,7 @@ import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.enum import com.google.common.base.Stopwatch import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness +import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.algorithm.bounded.* import hu.bme.mit.theta.analysis.expl.ExplState @@ -40,84 +39,109 @@ import kotlin.system.exitProcess typealias S = XstsState -class XstsCliBounded : XstsCliBaseCommand( +class XstsCliBounded : + XstsCliBaseCommand( name = "BOUNDED", - help = "Bounded model checking algorithms (BMC, IMC, KINDUCTION). Use --variant to select the algorithm (by default BMC is selected)." -) { - - enum class Variant { - BMC { + help = + "Bounded model checking algorithms (BMC, IMC, KINDUCTION). Use --variant to select the algorithm (by default BMC is selected).", + ) { - override fun buildChecker( - monolithicExpr: MonolithicExpr, solverFactory: SolverFactory, - valToState: (Valuation) -> S, biValToAction: (Valuation, Valuation) -> XstsAction, - logger: Logger - ) = buildBMC(monolithicExpr, solverFactory.createSolver(), valToState, biValToAction, logger) - }, - KINDUCTION { + enum class Variant { + BMC { - override fun buildChecker( - monolithicExpr: MonolithicExpr, solverFactory: SolverFactory, - valToState: (Valuation) -> S, biValToAction: (Valuation, Valuation) -> XstsAction, - logger: Logger - ) = buildKIND( - monolithicExpr, solverFactory.createSolver(), solverFactory.createSolver(), valToState, biValToAction, - logger - ) - }, - IMC { + override fun buildChecker( + monolithicExpr: MonolithicExpr, + solverFactory: SolverFactory, + valToState: (Valuation) -> S, + biValToAction: (Valuation, Valuation) -> XstsAction, + logger: Logger, + ) = buildBMC(monolithicExpr, solverFactory.createSolver(), valToState, biValToAction, logger) + }, + KINDUCTION { - override fun buildChecker( - monolithicExpr: MonolithicExpr, solverFactory: SolverFactory, - valToState: (Valuation) -> S, biValToAction: (Valuation, Valuation) -> XstsAction, - logger: Logger - ) = buildIMC( - monolithicExpr, solverFactory.createSolver(), solverFactory.createItpSolver(), valToState, - biValToAction, logger - ) - }; + override fun buildChecker( + monolithicExpr: MonolithicExpr, + solverFactory: SolverFactory, + valToState: (Valuation) -> S, + biValToAction: (Valuation, Valuation) -> XstsAction, + logger: Logger, + ) = + buildKIND( + monolithicExpr, + solverFactory.createSolver(), + solverFactory.createSolver(), + valToState, + biValToAction, + logger, + ) + }, + IMC { - abstract fun buildChecker( - monolithicExpr: MonolithicExpr, solverFactory: SolverFactory, valToState: (Valuation) -> S, - biValToAction: (Valuation, Valuation) -> XstsAction, logger: Logger - ): BoundedChecker - } + override fun buildChecker( + monolithicExpr: MonolithicExpr, + solverFactory: SolverFactory, + valToState: (Valuation) -> S, + biValToAction: (Valuation, Valuation) -> XstsAction, + logger: Logger, + ) = + buildIMC( + monolithicExpr, + solverFactory.createSolver(), + solverFactory.createItpSolver(), + valToState, + biValToAction, + logger, + ) + }; - private val variant by option().enum().default(Variant.BMC) + abstract fun buildChecker( + monolithicExpr: MonolithicExpr, + solverFactory: SolverFactory, + valToState: (Valuation) -> S, + biValToAction: (Valuation, Valuation) -> XstsAction, + logger: Logger, + ): BoundedChecker + } - private fun printResult(status: SafetyResult>, xsts: XSTS, totalTimeMs: Long) { - if (!outputOptions.benchmarkMode) return - printCommonResult(status, xsts, totalTimeMs) - val stats = status.stats.orElse(BoundedStatistics(0)) as BoundedStatistics - listOf( - stats.iterations, - ).forEach(writer::cell) - writer.newRow() - } + private val variant by option().enum().default(Variant.BMC) - override fun run() { - try { - doRun() - } catch (e: Exception) { - printError(e) - exitProcess(1) - } - } + private fun printResult( + status: SafetyResult>, + xsts: XSTS, + totalTimeMs: Long, + ) { + if (!outputOptions.benchmarkMode) return + printCommonResult(status, xsts, totalTimeMs) + val stats = status.stats.orElse(BoundedStatistics(0)) as BoundedStatistics + listOf(stats.iterations).forEach(writer::cell) + writer.newRow() + } - private fun doRun() { - registerSolverManagers() - val solverFactory = SolverManager.resolveSolverFactory(solver) - val xsts = inputOptions.loadXsts() - val sw = Stopwatch.createStarted() - val checker = variant.buildChecker( - xsts.toMonolithicExpr(), solverFactory, xsts::valToState, - xsts::valToAction, - logger - ) - val result = checker.check() - sw.stop() - printResult(result, xsts, sw.elapsed(TimeUnit.MILLISECONDS)) - writeCex(result, xsts) + override fun run() { + try { + doRun() + } catch (e: Exception) { + printError(e) + exitProcess(1) } + } -} \ No newline at end of file + private fun doRun() { + registerSolverManagers() + val solverFactory = SolverManager.resolveSolverFactory(solver) + val xsts = inputOptions.loadXsts() + val sw = Stopwatch.createStarted() + val checker = + variant.buildChecker( + xsts.toMonolithicExpr(), + solverFactory, + xsts::valToState, + xsts::valToAction, + logger, + ) + val result = checker.check() + sw.stop() + printResult(result, xsts, sw.elapsed(TimeUnit.MILLISECONDS)) + writeCex(result, xsts) + } +} diff --git a/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliCegar.kt b/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliCegar.kt index ee455f86dc..ade34d7127 100644 --- a/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliCegar.kt +++ b/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliCegar.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xsts.cli import com.github.ajalt.clikt.parameters.options.default @@ -38,73 +37,90 @@ import hu.bme.mit.theta.xsts.analysis.config.XstsConfigBuilder.* import java.util.concurrent.TimeUnit import kotlin.system.exitProcess -class XstsCliCegar : XstsCliBaseCommand( +class XstsCliCegar : + XstsCliBaseCommand( name = "CEGAR", - help = "Model checking using the CEGAR (CounterExample Guided Abstraction Refinement) algorithm" -) { + help = "Model checking using the CEGAR (CounterExample Guided Abstraction Refinement) algorithm", + ) { - private val domain: Domain by option(help = "Abstraction domain to use").enum().default(Domain.PRED_CART) - private val refinement: Refinement by option(help = "Refinement strategy to use").enum() - .default(Refinement.SEQ_ITP) - private val search: Search by option(help = "Search strategy to use").enum().default(Search.BFS) - private val refinementSolver: String? by option(help = "Use a different solver for abstraction") - private val abstractionSolver: String? by option(help = "Use a different solver for refinement") - private val predsplit: PredSplit by option().enum().default(PredSplit.WHOLE) - private val maxenum: Int by option().int().default(0) - private val autoexpl: AutoExpl by option().enum().default(AutoExpl.NEWOPERANDS) - private val initprec: InitPrec by option().enum().default(InitPrec.EMPTY) - private val prunestrategy: PruneStrategy by option().enum().default(PruneStrategy.LAZY) - private val optimizestmts: OptimizeStmts by option().enum().default(OptimizeStmts.ON) + private val domain: Domain by + option(help = "Abstraction domain to use").enum().default(Domain.PRED_CART) + private val refinement: Refinement by + option(help = "Refinement strategy to use").enum().default(Refinement.SEQ_ITP) + private val search: Search by + option(help = "Search strategy to use").enum().default(Search.BFS) + private val refinementSolver: String? by option(help = "Use a different solver for abstraction") + private val abstractionSolver: String? by option(help = "Use a different solver for refinement") + private val predsplit: PredSplit by option().enum().default(PredSplit.WHOLE) + private val maxenum: Int by option().int().default(0) + private val autoexpl: AutoExpl by option().enum().default(AutoExpl.NEWOPERANDS) + private val initprec: InitPrec by option().enum().default(InitPrec.EMPTY) + private val prunestrategy: PruneStrategy by + option().enum().default(PruneStrategy.LAZY) + private val optimizestmts: OptimizeStmts by + option().enum().default(OptimizeStmts.ON) - private fun printResult(status: SafetyResult?, out Trace<*, *>?>, xsts: XSTS, totalTimeMs: Long) { - if (!outputOptions.benchmarkMode) return - printCommonResult(status, xsts, totalTimeMs) - val stats = status.stats.orElse(CegarStatistics(0, 0, 0, 0)) as CegarStatistics - listOf( - stats.algorithmTimeMs, - stats.abstractorTimeMs, - stats.refinerTimeMs, - stats.iterations, - status.witness!!.size(), - status.witness!!.depth, - status.witness!!.meanBranchingFactor, - ).forEach(writer::cell) - writer.newRow() - } + private fun printResult( + status: SafetyResult?, out Trace<*, *>?>, + xsts: XSTS, + totalTimeMs: Long, + ) { + if (!outputOptions.benchmarkMode) return + printCommonResult(status, xsts, totalTimeMs) + val stats = status.stats.orElse(CegarStatistics(0, 0, 0, 0)) as CegarStatistics + listOf( + stats.algorithmTimeMs, + stats.abstractorTimeMs, + stats.refinerTimeMs, + stats.iterations, + status.proof!!.size(), + status.proof!!.depth, + status.proof!!.meanBranchingFactor, + ) + .forEach(writer::cell) + writer.newRow() + } - private fun writeVisualStatus( - status: SafetyResult?, out Trace?> - ) { - if (outputOptions.visualize == null) return - val graph = if (status.isSafe) ArgVisualizer.getDefault().visualize(status.asSafe().witness) - else TraceVisualizer.getDefault().visualize(status.asUnsafe().cex) - GraphvizWriter.getInstance().writeFile(graph, outputOptions.visualize!!) - } - - override fun run() { - try { - doRun() - } catch (e: Exception) { - printError(e) - exitProcess(1) - } - } + private fun writeVisualStatus( + status: SafetyResult?, out Trace?> + ) { + if (outputOptions.visualize == null) return + val graph = + if (status.isSafe) ArgVisualizer.getDefault().visualize(status.asSafe().proof) + else TraceVisualizer.getDefault().visualize(status.asUnsafe().cex) + GraphvizWriter.getInstance().writeFile(graph, outputOptions.visualize!!) + } - private fun doRun() { - registerSolverManagers() - val abstractionSolverFactory = SolverManager.resolveSolverFactory(abstractionSolver ?: solver) - val refinementSolverFactory = SolverManager.resolveSolverFactory(refinementSolver ?: solver) - val xsts = inputOptions.loadXsts() - val config = - XstsConfigBuilder(domain, refinement, abstractionSolverFactory, refinementSolverFactory).maxEnum(maxenum) - .autoExpl(autoexpl).initPrec(initprec).pruneStrategy(prunestrategy).search(search).predSplit(predsplit) - .optimizeStmts(optimizestmts).logger(logger).build(xsts) - val sw = Stopwatch.createStarted() - val result = config.check() - sw.stop() - printResult(result, xsts, sw.elapsed(TimeUnit.MILLISECONDS)) - writeCex(result, xsts) - writeVisualStatus(result) + override fun run() { + try { + doRun() + } catch (e: Exception) { + printError(e) + exitProcess(1) } + } -} \ No newline at end of file + private fun doRun() { + registerSolverManagers() + val abstractionSolverFactory = SolverManager.resolveSolverFactory(abstractionSolver ?: solver) + val refinementSolverFactory = SolverManager.resolveSolverFactory(refinementSolver ?: solver) + val xsts = inputOptions.loadXsts() + val config = + XstsConfigBuilder(domain, refinement, abstractionSolverFactory, refinementSolverFactory) + .maxEnum(maxenum) + .autoExpl(autoexpl) + .initPrec(initprec) + .pruneStrategy(prunestrategy) + .search(search) + .predSplit(predsplit) + .optimizeStmts(optimizestmts) + .logger(logger) + .build(xsts) + val sw = Stopwatch.createStarted() + val result = config.check() + sw.stop() + printResult(result, xsts, sw.elapsed(TimeUnit.MILLISECONDS)) + writeCex(result, xsts) + writeVisualStatus(result) + } +} diff --git a/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliMdd.kt b/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliMdd.kt index cc74818770..f64094d0a8 100644 --- a/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliMdd.kt +++ b/subprojects/xsts/xsts-cli/src/main/kotlin/hu/bme/mit/theta/xsts/cli/XstsCliMdd.kt @@ -13,80 +13,70 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xsts.cli import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.option -import com.github.ajalt.clikt.parameters.options.required import com.github.ajalt.clikt.parameters.types.enum import com.google.common.base.Stopwatch -import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.EmptyWitness import hu.bme.mit.theta.analysis.algorithm.SafetyResult -import hu.bme.mit.theta.analysis.algorithm.bounded.* -import hu.bme.mit.theta.analysis.algorithm.cegar.CegarStatistics import hu.bme.mit.theta.analysis.algorithm.mdd.MddAnalysisStatistics import hu.bme.mit.theta.analysis.algorithm.mdd.MddCex import hu.bme.mit.theta.analysis.algorithm.mdd.MddChecker -import hu.bme.mit.theta.analysis.algorithm.mdd.MddWitness -import hu.bme.mit.theta.common.logging.Logger -import hu.bme.mit.theta.core.model.Valuation -import hu.bme.mit.theta.solver.SolverFactory +import hu.bme.mit.theta.analysis.algorithm.mdd.MddProof import hu.bme.mit.theta.solver.SolverManager import hu.bme.mit.theta.solver.SolverPool import hu.bme.mit.theta.xsts.XSTS -import hu.bme.mit.theta.xsts.analysis.XstsAction -import hu.bme.mit.theta.xsts.analysis.hu.bme.mit.theta.xsts.analysis.toMonolithicExpr -import hu.bme.mit.theta.xsts.analysis.hu.bme.mit.theta.xsts.analysis.valToAction -import hu.bme.mit.theta.xsts.analysis.hu.bme.mit.theta.xsts.analysis.valToState import hu.bme.mit.theta.xsts.analysis.mdd.XstsMddChecker import java.util.concurrent.TimeUnit import kotlin.system.exitProcess -class XstsCliMdd : XstsCliBaseCommand( +class XstsCliMdd : + XstsCliBaseCommand( name = "MDD", - help = "Model checking of XSTS using MDDs (Multi-value Decision Diagrams)" -) { - - private val iterationStrategy: MddChecker.IterationStrategy by option( - help = "The state space enumeration algorithm to use" - ).enum().default(MddChecker.IterationStrategy.GSAT) + help = "Model checking of XSTS using MDDs (Multi-value Decision Diagrams)", + ) { - private fun printResult(status: SafetyResult, xsts: XSTS, totalTimeMs: Long) { - if (!outputOptions.benchmarkMode) return - printCommonResult(status, xsts, totalTimeMs) - val stats = status.stats.orElse(MddAnalysisStatistics(0, 0, 0, 0, 0)) as MddAnalysisStatistics - listOf( - stats.violatingSize, - stats.stateSpaceSize, - stats.hitCount, - stats.queryCount, - stats.cacheSize, - ).forEach(writer::cell) - writer.newRow() - } + private val iterationStrategy: MddChecker.IterationStrategy by + option(help = "The state space enumeration algorithm to use") + .enum() + .default(MddChecker.IterationStrategy.GSAT) - override fun run() { - try { - doRun() - } catch (e: Exception) { - printError(e) - exitProcess(1) - } - } + private fun printResult(status: SafetyResult, xsts: XSTS, totalTimeMs: Long) { + if (!outputOptions.benchmarkMode) return + printCommonResult(status, xsts, totalTimeMs) + val stats = status.stats.orElse(MddAnalysisStatistics(0, 0, 0, 0, 0)) as MddAnalysisStatistics + listOf( + stats.violatingSize, + stats.stateSpaceSize, + stats.hitCount, + stats.queryCount, + stats.cacheSize, + ) + .forEach(writer::cell) + writer.newRow() + } - private fun doRun() { - registerSolverManagers() - val solverFactory = SolverManager.resolveSolverFactory(solver) - val xsts = inputOptions.loadXsts() - val sw = Stopwatch.createStarted() - val result = SolverPool(solverFactory).use { - val checker = XstsMddChecker.create(xsts, it, logger, iterationStrategy) - checker.check(null) - } - sw.stop() - printResult(result, xsts, sw.elapsed(TimeUnit.MILLISECONDS)) + override fun run() { + try { + doRun() + } catch (e: Exception) { + printError(e) + exitProcess(1) } + } -} \ No newline at end of file + private fun doRun() { + registerSolverManagers() + val solverFactory = SolverManager.resolveSolverFactory(solver) + val xsts = inputOptions.loadXsts() + val sw = Stopwatch.createStarted() + val result = + SolverPool(solverFactory).use { + val checker = XstsMddChecker.create(xsts, it, logger, iterationStrategy) + checker.check(null) + } + sw.stop() + printResult(result, xsts, sw.elapsed(TimeUnit.MILLISECONDS)) + } +} diff --git a/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/LazyXtaCheckerTest.java b/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/LazyXtaCheckerTest.java index be1fc4afe0..eefc740ce6 100644 --- a/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/LazyXtaCheckerTest.java +++ b/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/LazyXtaCheckerTest.java @@ -15,13 +15,17 @@ */ package hu.bme.mit.theta.xta.analysis; +import static hu.bme.mit.theta.analysis.algorithm.arg.SearchStrategy.BFS; +import static hu.bme.mit.theta.xta.analysis.lazy.ClockStrategy.LU; +import static org.junit.Assert.assertTrue; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import hu.bme.mit.theta.analysis.Trace; +import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; import hu.bme.mit.theta.analysis.algorithm.SafetyResult; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgChecker; -import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; import hu.bme.mit.theta.analysis.unit.UnitPrec; import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory; import hu.bme.mit.theta.xta.XtaSystem; @@ -29,6 +33,10 @@ import hu.bme.mit.theta.xta.analysis.lazy.DataStrategy; import hu.bme.mit.theta.xta.analysis.lazy.LazyXtaCheckerFactory; import hu.bme.mit.theta.xta.dsl.XtaDslManager; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,15 +44,6 @@ import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; - -import static hu.bme.mit.theta.analysis.algorithm.arg.SearchStrategy.BFS; -import static hu.bme.mit.theta.xta.analysis.lazy.ClockStrategy.LU; -import static org.junit.Assert.assertTrue; - @RunWith(Parameterized.class) public final class LazyXtaCheckerTest { @@ -55,13 +54,17 @@ public final class LazyXtaCheckerTest { private static final String MODEL_ENGINE = "/engine-classic.xta"; private static final String MODEL_BROADCAST = "/broadcast.xta"; - private static final Collection MODELS = ImmutableList.of(MODEL_CSMA, MODEL_FDDI, - MODEL_FISCHER, - MODEL_LYNCH, MODEL_ENGINE, MODEL_BROADCAST); + private static final Collection MODELS = + ImmutableList.of( + MODEL_CSMA, + MODEL_FDDI, + MODEL_FISCHER, + MODEL_LYNCH, + MODEL_ENGINE, + MODEL_BROADCAST); - private static final Collection MODELS_WITH_UNKNOWN_SOLVER_STATUS = ImmutableSet.of( - MODEL_FDDI, - MODEL_ENGINE, MODEL_BROADCAST); + private static final Collection MODELS_WITH_UNKNOWN_SOLVER_STATUS = + ImmutableSet.of(MODEL_FDDI, MODEL_ENGINE, MODEL_BROADCAST); @Parameter(0) public String filepath; @@ -72,7 +75,11 @@ public final class LazyXtaCheckerTest { @Parameter(2) public ClockStrategy clockStrategy; - private SafetyChecker, XtaAction>, ? extends Trace, XtaAction>, UnitPrec> checker; + private SafetyChecker< + ? extends ARG, XtaAction>, + ? extends Trace, XtaAction>, + UnitPrec> + checker; @Parameters(name = "model: {0}, discrete: {1}, clock: {2}") public static Collection data() { @@ -80,9 +87,9 @@ public static Collection data() { for (final String model : MODELS) { for (final DataStrategy dataStrategy : DataStrategy.values()) { for (final ClockStrategy clockStrategy : ClockStrategy.values()) { - if (!MODELS_WITH_UNKNOWN_SOLVER_STATUS.contains(model) || (clockStrategy - != LU)) { - result.add(new Object[]{model, dataStrategy, clockStrategy}); + if (!MODELS_WITH_UNKNOWN_SOLVER_STATUS.contains(model) + || (clockStrategy != LU)) { + result.add(new Object[] {model, dataStrategy, clockStrategy}); } } } @@ -100,14 +107,15 @@ public void initialize() throws IOException { @Test public void test() { // Act - final SafetyResult, XtaAction>, ? extends Trace, XtaAction>> status = checker.check( - UnitPrec.getInstance()); + final SafetyResult< + ? extends ARG, XtaAction>, + ? extends Trace, XtaAction>> + status = checker.check(UnitPrec.getInstance()); // Assert - final ArgChecker argChecker = ArgChecker.create( - Z3LegacySolverFactory.getInstance().createSolver()); - final boolean argCheckResult = argChecker.isWellLabeled(status.getWitness()); + final ArgChecker argChecker = + ArgChecker.create(Z3LegacySolverFactory.getInstance().createSolver()); + final boolean argCheckResult = argChecker.isWellLabeled(status.getProof()); assertTrue(argCheckResult); } - } diff --git a/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/XtaAnalysisTest.java b/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/XtaAnalysisTest.java index 592abd7ad5..aabfaa25e3 100644 --- a/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/XtaAnalysisTest.java +++ b/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/XtaAnalysisTest.java @@ -15,24 +15,12 @@ */ package hu.bme.mit.theta.xta.analysis; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Collection; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; - import hu.bme.mit.theta.analysis.Analysis; import hu.bme.mit.theta.analysis.LTS; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder; -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.BasicAbstractor; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor; +import hu.bme.mit.theta.analysis.algorithm.cegar.BasicArgAbstractor; import hu.bme.mit.theta.analysis.unit.UnitAnalysis; import hu.bme.mit.theta.analysis.unit.UnitPrec; import hu.bme.mit.theta.analysis.unit.UnitState; @@ -40,27 +28,31 @@ import hu.bme.mit.theta.common.visualization.writer.GraphvizWriter; import hu.bme.mit.theta.xta.XtaSystem; import hu.bme.mit.theta.xta.dsl.XtaDslManager; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public final class XtaAnalysisTest { @Parameters(name = "{0}") public static Collection data() { - return Arrays.asList(new Object[][]{ - - {"/critical-2-25-50.xta"}, - - {"/csma-2.xta"}, - - {"/fddi-2.xta"}, - - {"/fischer-2-32-64.xta"}, - - {"/lynch-2-16.xta"}, - - {"/broadcast.xta"}, - - }); + return Arrays.asList( + new Object[][] { + {"/critical-2-25-50.xta"}, + {"/csma-2.xta"}, + {"/fddi-2.xta"}, + {"/fischer-2-32-64.xta"}, + {"/lynch-2-16.xta"}, + {"/broadcast.xta"}, + }); } @Parameter(0) @@ -72,22 +64,19 @@ public void test() throws FileNotFoundException, IOException { final XtaSystem system = XtaDslManager.createSystem(inputStream); final LTS, XtaAction> lts = XtaLts.create(system); - final Analysis, XtaAction, UnitPrec> analysis = XtaAnalysis.create( - system, - UnitAnalysis.getInstance()); - final ArgBuilder, XtaAction, UnitPrec> argBuilder = ArgBuilder.create( - lts, analysis, - s -> false); + final Analysis, XtaAction, UnitPrec> analysis = + XtaAnalysis.create(system, UnitAnalysis.getInstance()); + final ArgBuilder, XtaAction, UnitPrec> argBuilder = + ArgBuilder.create(lts, analysis, s -> false); - final Abstractor, XtaAction, UnitPrec> abstractor = BasicAbstractor.builder( - argBuilder) - .projection(s -> s.getLocs()).build(); + final ArgAbstractor, XtaAction, UnitPrec> abstractor = + BasicArgAbstractor.builder(argBuilder).projection(s -> s.getLocs()).build(); - final ARG, XtaAction> arg = abstractor.createArg(); + final ARG, XtaAction> arg = abstractor.createProof(); abstractor.check(arg, UnitPrec.getInstance()); System.out.println( - GraphvizWriter.getInstance().writeString(ArgVisualizer.getDefault().visualize(arg))); + GraphvizWriter.getInstance() + .writeString(ArgVisualizer.getDefault().visualize(arg))); } - } diff --git a/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/XtaZoneAnalysisTest.java b/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/XtaZoneAnalysisTest.java index 437049b128..1c1e6fa56e 100644 --- a/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/XtaZoneAnalysisTest.java +++ b/subprojects/xta/xta-analysis/src/test/java/hu/bme/mit/theta/xta/analysis/XtaZoneAnalysisTest.java @@ -15,25 +15,12 @@ */ package hu.bme.mit.theta.xta.analysis; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.stream.Collectors; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; - import hu.bme.mit.theta.analysis.Analysis; import hu.bme.mit.theta.analysis.LTS; import hu.bme.mit.theta.analysis.algorithm.arg.ARG; import hu.bme.mit.theta.analysis.algorithm.arg.ArgBuilder; -import hu.bme.mit.theta.analysis.algorithm.cegar.Abstractor; -import hu.bme.mit.theta.analysis.algorithm.cegar.BasicAbstractor; +import hu.bme.mit.theta.analysis.algorithm.cegar.ArgAbstractor; +import hu.bme.mit.theta.analysis.algorithm.cegar.BasicArgAbstractor; import hu.bme.mit.theta.analysis.expl.ExplState; import hu.bme.mit.theta.analysis.impl.PrecMappingAnalysis; import hu.bme.mit.theta.analysis.prod2.Prod2Analysis; @@ -46,25 +33,31 @@ import hu.bme.mit.theta.xta.analysis.expl.XtaExplAnalysis; import hu.bme.mit.theta.xta.analysis.zone.XtaZoneAnalysis; import hu.bme.mit.theta.xta.dsl.XtaDslManager; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Collectors; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public final class XtaZoneAnalysisTest { @Parameters(name = "{0}") public static Collection data() { - return Arrays.asList(new Object[][]{ - - {"/csma-2.xta"}, - - {"/fddi-2.xta"}, - - {"/fischer-2-32-64.xta"}, - - {"/lynch-2-16.xta"}, - - {"/broadcast.xta"}, - - }); + return Arrays.asList( + new Object[][] { + {"/csma-2.xta"}, + {"/fddi-2.xta"}, + {"/fischer-2-32-64.xta"}, + {"/lynch-2-16.xta"}, + {"/broadcast.xta"}, + }); } @Parameter(0) @@ -76,30 +69,32 @@ public void test() throws FileNotFoundException, IOException { final XtaSystem system = XtaDslManager.createSystem(inputStream); final LTS, XtaAction> lts = XtaLts.create(system); - final Analysis explAnalysis = XtaExplAnalysis.create( - system); + final Analysis explAnalysis = + XtaExplAnalysis.create(system); final Analysis zoneAnalysis = XtaZoneAnalysis.getInstance(); - final Analysis, XtaAction, Prod2Prec> prodAnalysis = Prod2Analysis - .create(explAnalysis, zoneAnalysis); - final Analysis, XtaAction, ZonePrec> mappedAnalysis = PrecMappingAnalysis - .create(prodAnalysis, z -> Prod2Prec.of(UnitPrec.getInstance(), z)); - final Analysis>, XtaAction, ZonePrec> analysis = XtaAnalysis - .create(system, mappedAnalysis); + final Analysis, XtaAction, Prod2Prec> + prodAnalysis = Prod2Analysis.create(explAnalysis, zoneAnalysis); + final Analysis, XtaAction, ZonePrec> mappedAnalysis = + PrecMappingAnalysis.create( + prodAnalysis, z -> Prod2Prec.of(UnitPrec.getInstance(), z)); + final Analysis>, XtaAction, ZonePrec> analysis = + XtaAnalysis.create(system, mappedAnalysis); final ZonePrec prec = ZonePrec.of(system.getClockVars()); - final ArgBuilder>, XtaAction, ZonePrec> argBuilder = ArgBuilder - .create(lts, analysis, s -> false); + final ArgBuilder>, XtaAction, ZonePrec> + argBuilder = ArgBuilder.create(lts, analysis, s -> false); - final Abstractor>, XtaAction, ZonePrec> abstractor = BasicAbstractor - .builder(argBuilder).projection(s -> s.getLocs()).build(); + final ArgAbstractor>, XtaAction, ZonePrec> + abstractor = + BasicArgAbstractor.builder(argBuilder).projection(s -> s.getLocs()).build(); - final ARG>, XtaAction> arg = abstractor.createArg(); + final ARG>, XtaAction> arg = + abstractor.createProof(); abstractor.check(arg, prec); System.out.println(arg.getNodes().collect(Collectors.toSet())); System.out.println(arg.getNodes().count()); } - } diff --git a/subprojects/xta/xta-cli/src/main/java/hu/bme/mit/theta/xta/cli/XtaCli.java b/subprojects/xta/xta-cli/src/main/java/hu/bme/mit/theta/xta/cli/XtaCli.java index 4bf1694ff4..7f5185f6fb 100644 --- a/subprojects/xta/xta-cli/src/main/java/hu/bme/mit/theta/xta/cli/XtaCli.java +++ b/subprojects/xta/xta-cli/src/main/java/hu/bme/mit/theta/xta/cli/XtaCli.java @@ -15,12 +15,9 @@ */ package hu.bme.mit.theta.xta.cli; -import java.io.*; - import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; - import hu.bme.mit.theta.analysis.Action; import hu.bme.mit.theta.analysis.State; import hu.bme.mit.theta.analysis.Trace; @@ -42,6 +39,7 @@ import hu.bme.mit.theta.xta.analysis.lazy.LazyXtaCheckerFactory; import hu.bme.mit.theta.xta.analysis.lazy.LazyXtaStatistics; import hu.bme.mit.theta.xta.dsl.XtaDslManager; +import java.io.*; public final class XtaCli { @@ -49,29 +47,44 @@ public final class XtaCli { private final String[] args; private final TableWriter writer; - @Parameter(names = {"--model", "-m"}, description = "Path of the input model", required = true) + @Parameter( + names = {"--model", "-m"}, + description = "Path of the input model", + required = true) String model; - @Parameter(names = {"--discrete", - "-d"}, description = "Refinement strategy for discrete variables", required = false) + @Parameter( + names = {"--discrete", "-d"}, + description = "Refinement strategy for discrete variables", + required = false) DataStrategy dataStrategy = DataStrategy.NONE; - @Parameter(names = {"--clock", - "-c"}, description = "Refinement strategy for clock variables", required = true) + @Parameter( + names = {"--clock", "-c"}, + description = "Refinement strategy for clock variables", + required = true) ClockStrategy clockStrategy; - @Parameter(names = {"--search", "-s"}, description = "Search strategy", required = true) + @Parameter( + names = {"--search", "-s"}, + description = "Search strategy", + required = true) SearchStrategy searchStrategy; - @Parameter(names = {"--benchmark", "-b"}, description = "Benchmark mode (only print metrics)") + @Parameter( + names = {"--benchmark", "-b"}, + description = "Benchmark mode (only print metrics)") Boolean benchmarkMode = false; - @Parameter(names = {"--visualize", - "-v"}, description = "Write proof or counterexample to file in dot format") + @Parameter( + names = {"--visualize", "-v"}, + description = "Write proof or counterexample to file in dot format") String dotfile = null; - @Parameter(names = {"--header", - "-h"}, description = "Print only a header (for benchmarks)", help = true) + @Parameter( + names = {"--header", "-h"}, + description = "Print only a header (for benchmarks)", + help = true) boolean headerOnly = false; @Parameter(names = "--stacktrace", description = "Print full stack trace in case of exception") @@ -112,10 +125,12 @@ private void run() { try { final XtaSystem system = loadModel(); - final SafetyChecker checker = LazyXtaCheckerFactory.create(system, - dataStrategy, - clockStrategy, searchStrategy); - final SafetyResult, ? extends Trace> result = check(checker); + final SafetyChecker checker = + LazyXtaCheckerFactory.create( + system, dataStrategy, clockStrategy, searchStrategy); + final SafetyResult< + ? extends ARG, ? extends Trace> + result = check(checker); printResult(result); if (dotfile != null) { writeVisualStatus(result, dotfile); @@ -126,13 +141,20 @@ private void run() { } } - private SafetyResult, ? extends Trace> check(SafetyChecker checker) throws Exception { + private SafetyResult, ? extends Trace> + check(SafetyChecker checker) throws Exception { try { - return (SafetyResult, ? extends Trace>) checker.check(UnitPrec.getInstance()); + return (SafetyResult< + ? extends ARG, + ? extends Trace>) + checker.check(UnitPrec.getInstance()); } catch (final Exception ex) { String message = ex.getMessage() == null ? "(no message)" : ex.getMessage(); throw new Exception( - "Error while running algorithm: " + ex.getClass().getSimpleName() + " " + message, + "Error while running algorithm: " + + ex.getClass().getSimpleName() + + " " + + message, ex); } } @@ -147,7 +169,8 @@ private XtaSystem loadModel() throws Exception { } } - private void printResult(final SafetyResult, ? extends Trace> result) { + private void printResult( + final SafetyResult, ? extends Trace> result) { final LazyXtaStatistics stats = (LazyXtaStatistics) result.getStats().get(); if (benchmarkMode) { stats.writeData(writer); @@ -173,12 +196,16 @@ private void printError(final Throwable ex) { } } - private void writeVisualStatus(final SafetyResult, ? extends Trace> status, final String filename) + private void writeVisualStatus( + final SafetyResult< + ? extends ARG, ? extends Trace> + status, + final String filename) throws FileNotFoundException { final Graph graph = - status.isSafe() ? ArgVisualizer.getDefault().visualize(status.asSafe().getWitness()) + status.isSafe() + ? ArgVisualizer.getDefault().visualize(status.asSafe().getProof()) : TraceVisualizer.getDefault().visualize(status.asUnsafe().getCex()); GraphvizWriter.getInstance().writeFile(graph, filename); } - }