From 088f395b1da7384dd9bf3d305e8a978e08ee880c Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Wed, 2 Aug 2023 15:31:28 +0200 Subject: [PATCH 01/25] prototype non-linear CHC solver --- .gitignore | 1 + CMakeLists.txt | 1 + benchmarks/LIA-Lin_adcl.txt | 499 +++++++++++++++++++++++++++++ benchmarks/LIA-Lin_z3.txt | 499 +++++++++++++++++++++++++++++ benchmarks/LIA_adcl.txt | 456 ++++++++++++++++++++++++++ benchmarks/LIA_z3.txt | 456 ++++++++++++++++++++++++++ run_benchmark.sh | 73 +++++ run_tests.sh | 2 +- src/CMakeLists.txt | 1 + src/analysis/preprocessing.cpp | 16 +- src/analysis/reachability.cpp | 250 ++++++++++++--- src/analysis/reachability.hpp | 32 +- src/its/itsproblem.cpp | 146 +++++++++ src/its/itsproblem.hpp | 15 + src/lib/expr/expr.cpp | 36 +++ src/lib/expr/expr.hpp | 2 + src/main.cpp | 8 +- src/nonlinear/CMakeLists.txt | 10 + src/nonlinear/clause.cpp | 242 ++++++++++++++ src/nonlinear/clause.hpp | 56 ++++ src/nonlinear/linearsolver.hpp | 40 +++ src/nonlinear/nonlinear.cpp | 67 ++++ src/nonlinear/nonlinear.hpp | 14 + src/parser/chc/CHCParseVisitor.cpp | 76 +---- src/parser/chc/CHCParseVisitor.h | 18 -- test_data_lialin.txt | 175 ++++++++++ 26 files changed, 3066 insertions(+), 125 deletions(-) create mode 100644 benchmarks/LIA-Lin_adcl.txt create mode 100644 benchmarks/LIA-Lin_z3.txt create mode 100644 benchmarks/LIA_adcl.txt create mode 100644 benchmarks/LIA_z3.txt create mode 100755 run_benchmark.sh create mode 100644 src/nonlinear/CMakeLists.txt create mode 100644 src/nonlinear/clause.cpp create mode 100644 src/nonlinear/clause.hpp create mode 100644 src/nonlinear/linearsolver.hpp create mode 100644 src/nonlinear/nonlinear.cpp create mode 100644 src/nonlinear/nonlinear.hpp create mode 100644 test_data_lialin.txt diff --git a/.gitignore b/.gitignore index 01f57638c..8637306c9 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ build build-static bundle CMakeFiles +CMakeCache.txt compile_commands.json loat-static LoAT.* diff --git a/CMakeLists.txt b/CMakeLists.txt index df8dcad14..549f414fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ endif() add_compile_options(-Wall -Wextra -pedantic -Wno-unused-parameter) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -g") +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) message(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) message(STATUS "Compiler flags:" ${CMAKE_CXX_COMPILE_FLAGS}) diff --git a/benchmarks/LIA-Lin_adcl.txt b/benchmarks/LIA-Lin_adcl.txt new file mode 100644 index 000000000..6eeef3c1b --- /dev/null +++ b/benchmarks/LIA-Lin_adcl.txt @@ -0,0 +1,499 @@ +000 timeout +001 unknown +002 unknown +003 unknown +004 unknown +005 unknown +006 unknown +007 unknown +008 timeout +009 unknown +010 unknown +011 unknown +012 timeout +013 timeout +014 unknown +015 unknown +016 timeout +017 unknown +018 unknown +019 unknown +020 unknown +021 unknown +022 unknown +023 unknown +024 unknown +025 timeout +026 unknown +027 timeout +028 unknown +029 unknown +030 unsat +031 unsat +032 unsat +033 unsat +034 unsat +035 unsat +036 timeout +037 unsat +038 unsat +039 unsat +040 timeout +041 unknown +042 unknown +043 unsat +044 unsat +045 unsat +046 timeout +047 unsat +048 unknown +049 unknown +050 unsat +051 unsat +052 unsat +053 unsat +054 unknown +055 unknown +056 timeout +057 unsat +058 unknown +059 unsat +060 unsat +061 unsat +062 unsat +063 unsat +064 unsat +065 unsat +066 unsat +067 unsat +068 timeout +069 unsat +070 timeout +071 unsat +072 unsat +073 timeout +074 timeout +075 unsat +076 unsat +077 unknown +078 unknown +079 unknown +080 timeout +081 unknown +082 unknown +083 unsat +084 unsat +085 unknown +086 unsat +087 timeout +088 timeout +089 unsat +090 unknown +091 unknown +092 unsat +093 unknown +094 unknown +095 unknown +096 timeout +097 unknown +098 unknown +099 unknown +100 unknown +101 unknown +102 timeout +103 unknown +104 unknown +105 unknown +106 timeout +107 unknown +108 unknown +109 unknown +110 unknown +111 unsat +112 unknown +113 timeout +114 timeout +115 unknown +116 unsat +117 unsat +118 unknown +119 unsat +120 unknown +121 unknown +122 timeout +123 unsat +124 timeout +125 unknown +126 unknown +127 unknown +128 unknown +129 timeout +130 unsat +131 timeout +132 timeout +133 unsat +134 unknown +135 timeout +136 timeout +137 timeout +138 timeout +139 timeout +140 timeout +141 timeout +142 timeout +143 timeout +144 timeout +145 timeout +146 timeout +147 timeout +148 timeout +149 timeout +150 unknown +151 timeout +152 timeout +153 timeout +154 unsat +155 timeout +156 timeout +157 timeout +158 timeout +159 timeout +160 timeout +161 timeout +162 timeout +163 timeout +164 timeout +165 timeout +166 timeout +167 timeout +168 timeout +169 timeout +170 timeout +171 timeout +172 timeout +173 timeout +174 unknown +175 unsat +176 timeout +177 unknown +178 unknown +179 timeout +180 timeout +181 timeout +182 timeout +183 unknown +184 unknown +185 unsat +186 timeout +187 unsat +188 timeout +189 timeout +190 timeout +191 timeout +192 timeout +193 timeout +194 timeout +195 timeout +196 timeout +197 unsat +198 timeout +199 timeout +200 timeout +201 timeout +202 timeout +203 timeout +204 timeout +205 timeout +206 timeout +207 timeout +208 timeout +209 timeout +210 timeout +211 timeout +212 timeout +213 timeout +214 timeout +215 timeout +216 timeout +217 timeout +218 timeout +219 timeout +220 timeout +221 timeout +222 timeout +223 timeout +224 timeout +225 timeout +226 timeout +227 timeout +228 timeout +229 timeout +230 timeout +231 timeout +232 timeout +233 timeout +234 timeout +235 timeout +236 timeout +237 timeout +238 timeout +239 timeout +240 timeout +241 timeout +242 timeout +243 timeout +244 timeout +245 timeout +246 timeout +247 timeout +248 timeout +249 timeout +250 timeout +251 timeout +252 timeout +253 timeout +254 timeout +255 timeout +256 timeout +257 timeout +258 timeout +259 timeout +260 timeout +261 unknown +262 timeout +263 unknown +264 unknown +265 unknown +266 unknown +267 unknown +268 timeout +269 unknown +270 unknown +271 timeout +272 timeout +273 unknown +274 timeout +275 unknown +276 timeout +277 unknown +278 timeout +279 timeout +280 unknown +281 timeout +282 unknown +283 unknown +284 unknown +285 unknown +286 unknown +287 unknown +288 timeout +289 unknown +290 unknown +291 unknown +292 unknown +293 unknown +294 unknown +295 unknown +296 unknown +297 unsat +298 unknown +299 unsat +300 unknown +301 unknown +302 timeout +303 unknown +304 unknown +305 unknown +306 unknown +307 unknown +308 unknown +309 unknown +310 unknown +311 unknown +312 unknown +313 unsat +314 unknown +315 unknown +316 unsat +317 unknown +318 unknown +319 unsat +320 unknown +321 unsat +322 unsat +323 unknown +324 timeout +325 unknown +326 unsat +327 unsat +328 unsat +329 unsat +330 unsat +331 unsat +332 unsat +333 unknown +334 unsat +335 unsat +336 unsat +337 unsat +338 timeout +339 timeout +340 timeout +341 timeout +342 timeout +343 timeout +344 timeout +345 unsat +346 unsat +347 unsat +348 unsat +349 unsat +350 timeout +351 unsat +352 timeout +353 timeout +354 timeout +355 timeout +356 timeout +357 timeout +358 unsat +359 timeout +360 timeout +361 timeout +362 unsat +363 timeout +364 timeout +365 timeout +366 timeout +367 timeout +368 timeout +369 timeout +370 timeout +371 timeout +372 timeout +373 timeout +374 timeout +375 timeout +376 timeout +377 timeout +378 timeout +379 timeout +380 timeout +381 unsat +382 timeout +383 timeout +384 timeout +385 timeout +386 unsat +387 timeout +388 timeout +389 timeout +390 timeout +391 timeout +392 timeout +393 timeout +394 timeout +395 timeout +396 timeout +397 timeout +398 timeout +399 timeout +400 timeout +401 unsat +402 unsat +403 timeout +404 timeout +405 unsat +406 timeout +407 timeout +408 timeout +409 unknown +410 unknown +411 unknown +412 unknown +413 unknown +414 unknown +415 unknown +416 unknown +417 unknown +418 unknown +419 unknown +420 unknown +421 unknown +422 unknown +423 unknown +424 unknown +425 unknown +426 unknown +427 unknown +428 unknown +429 unknown +430 unknown +431 timeout +432 timeout +433 timeout +434 unknown +435 unknown +436 unknown +437 unknown +438 unknown +439 unknown +440 unknown +441 unknown +442 timeout +443 unknown +444 unknown +445 unknown +446 timeout +447 timeout +448 timeout +449 timeout +450 timeout +451 timeout +452 timeout +453 timeout +454 timeout +455 timeout +456 timeout +457 timeout +458 timeout +459 timeout +460 timeout +461 unknown +462 timeout +463 timeout +464 timeout +465 timeout +466 timeout +467 timeout +468 timeout +469 timeout +470 timeout +471 timeout +472 timeout +473 timeout +474 timeout +475 unsat +476 timeout +477 timeout +478 timeout +479 timeout +480 timeout +481 timeout +482 timeout +483 timeout +484 timeout +485 timeout +486 timeout +487 timeout +488 timeout +489 timeout +490 timeout +491 timeout +492 timeout +493 timeout +494 timeout +495 timeout +496 timeout +497 timeout +498 timeout diff --git a/benchmarks/LIA-Lin_z3.txt b/benchmarks/LIA-Lin_z3.txt new file mode 100644 index 000000000..23d4a9bdd --- /dev/null +++ b/benchmarks/LIA-Lin_z3.txt @@ -0,0 +1,499 @@ +000 timeout +001 timeout +002 timeout +003 timeout +004 timeout +005 timeout +006 timeout +007 sat +008 timeout +009 timeout +010 sat +011 sat +012 timeout +013 timeout +014 timeout +015 timeout +016 timeout +017 timeout +018 timeout +019 timeout +020 timeout +021 sat +022 timeout +023 sat +024 timeout +025 timeout +026 timeout +027 timeout +028 sat +029 timeout +030 unsat +031 unsat +032 unsat +033 unsat +034 unsat +035 unsat +036 unsat +037 unsat +038 unsat +039 unsat +040 timeout +041 timeout +042 timeout +043 timeout +044 timeout +045 timeout +046 timeout +047 timeout +048 timeout +049 timeout +050 timeout +051 timeout +052 timeout +053 timeout +054 timeout +055 timeout +056 unsat +057 timeout +058 timeout +059 timeout +060 unsat +061 unsat +062 unsat +063 unsat +064 unsat +065 timeout +066 unsat +067 unsat +068 timeout +069 timeout +070 timeout +071 unsat +072 unsat +073 sat +074 timeout +075 timeout +076 timeout +077 timeout +078 timeout +079 sat +080 sat +081 sat +082 timeout +083 unsat +084 unsat +085 sat +086 unsat +087 sat +088 sat +089 unsat +090 timeout +091 timeout +092 unsat +093 sat +094 sat +095 sat +096 unsat +097 timeout +098 timeout +099 timeout +100 timeout +101 timeout +102 timeout +103 timeout +104 timeout +105 timeout +106 timeout +107 timeout +108 timeout +109 timeout +110 sat +111 unsat +112 sat +113 sat +114 sat +115 sat +116 unsat +117 unsat +118 sat +119 unsat +120 sat +121 sat +122 sat +123 unsat +124 sat +125 timeout +126 timeout +127 timeout +128 timeout +129 sat +130 unsat +131 sat +132 sat +133 unsat +134 timeout +135 timeout +136 timeout +137 timeout +138 timeout +139 timeout +140 timeout +141 timeout +142 timeout +143 timeout +144 timeout +145 timeout +146 timeout +147 timeout +148 timeout +149 timeout +150 timeout +151 timeout +152 timeout +153 timeout +154 timeout +155 timeout +156 timeout +157 timeout +158 timeout +159 timeout +160 timeout +161 timeout +162 timeout +163 timeout +164 timeout +165 timeout +166 timeout +167 timeout +168 timeout +169 timeout +170 sat +171 sat +172 sat +173 sat +174 sat +175 unsat +176 sat +177 sat +178 sat +179 sat +180 sat +181 sat +182 sat +183 sat +184 sat +185 unsat +186 sat +187 unsat +188 sat +189 unsat +190 sat +191 sat +192 sat +193 sat +194 sat +195 sat +196 sat +197 unsat +198 sat +199 timeout +200 unsat +201 unsat +202 timeout +203 sat +204 sat +205 sat +206 timeout +207 timeout +208 timeout +209 timeout +210 timeout +211 timeout +212 timeout +213 timeout +214 timeout +215 timeout +216 timeout +217 timeout +218 sat +219 timeout +220 timeout +221 timeout +222 timeout +223 sat +224 sat +225 timeout +226 timeout +227 timeout +228 sat +229 timeout +230 sat +231 timeout +232 sat +233 timeout +234 timeout +235 timeout +236 timeout +237 timeout +238 timeout +239 timeout +240 timeout +241 timeout +242 timeout +243 timeout +244 timeout +245 timeout +246 timeout +247 timeout +248 timeout +249 timeout +250 timeout +251 timeout +252 timeout +253 timeout +254 timeout +255 timeout +256 timeout +257 timeout +258 timeout +259 timeout +260 sat +261 timeout +262 unknown +263 timeout +264 timeout +265 sat +266 timeout +267 timeout +268 timeout +269 timeout +270 timeout +271 timeout +272 timeout +273 timeout +274 timeout +275 timeout +276 timeout +277 timeout +278 timeout +279 timeout +280 timeout +281 timeout +282 timeout +283 timeout +284 timeout +285 timeout +286 timeout +287 timeout +288 sat +289 timeout +290 sat +291 sat +292 timeout +293 sat +294 sat +295 sat +296 sat +297 unsat +298 timeout +299 unsat +300 sat +301 sat +302 timeout +303 timeout +304 timeout +305 timeout +306 sat +307 sat +308 sat +309 sat +310 sat +311 sat +312 timeout +313 unsat +314 sat +315 sat +316 unsat +317 sat +318 sat +319 unsat +320 sat +321 unsat +322 unsat +323 sat +324 sat +325 sat +326 unsat +327 unsat +328 unsat +329 unsat +330 unsat +331 unsat +332 unsat +333 sat +334 unsat +335 unsat +336 unsat +337 unsat +338 sat +339 sat +340 sat +341 sat +342 sat +343 unsat +344 sat +345 unsat +346 unsat +347 unsat +348 unsat +349 unsat +350 sat +351 unsat +352 unsat +353 unsat +354 unsat +355 timeout +356 timeout +357 timeout +358 timeout +359 timeout +360 timeout +361 timeout +362 timeout +363 timeout +364 timeout +365 timeout +366 timeout +367 timeout +368 timeout +369 timeout +370 timeout +371 timeout +372 timeout +373 timeout +374 timeout +375 timeout +376 timeout +377 timeout +378 timeout +379 sat +380 timeout +381 timeout +382 timeout +383 timeout +384 timeout +385 timeout +386 timeout +387 timeout +388 timeout +389 timeout +390 timeout +391 timeout +392 timeout +393 timeout +394 timeout +395 unsat +396 timeout +397 timeout +398 sat +399 timeout +400 timeout +401 timeout +402 timeout +403 sat +404 timeout +405 timeout +406 timeout +407 timeout +408 timeout +409 sat +410 sat +411 sat +412 sat +413 sat +414 sat +415 sat +416 sat +417 sat +418 sat +419 sat +420 sat +421 sat +422 sat +423 sat +424 sat +425 sat +426 sat +427 sat +428 sat +429 timeout +430 timeout +431 timeout +432 unsat +433 timeout +434 sat +435 sat +436 sat +437 sat +438 sat +439 sat +440 sat +441 sat +442 unsat +443 sat +444 sat +445 sat +446 timeout +447 timeout +448 timeout +449 timeout +450 timeout +451 timeout +452 timeout +453 timeout +454 timeout +455 timeout +456 timeout +457 unsat +458 timeout +459 timeout +460 timeout +461 timeout +462 timeout +463 timeout +464 timeout +465 timeout +466 timeout +467 timeout +468 timeout +469 timeout +470 timeout +471 timeout +472 timeout +473 timeout +474 unsat +475 timeout +476 timeout +477 timeout +478 timeout +479 timeout +480 timeout +481 sat +482 timeout +483 timeout +484 timeout +485 timeout +486 unsat +487 sat +488 timeout +489 timeout +490 timeout +491 timeout +492 timeout +493 timeout +494 timeout +495 sat +496 sat +497 timeout +498 timeout diff --git a/benchmarks/LIA_adcl.txt b/benchmarks/LIA_adcl.txt new file mode 100644 index 000000000..22e922bc4 --- /dev/null +++ b/benchmarks/LIA_adcl.txt @@ -0,0 +1,456 @@ +000 timeout +001 timeout +002 timeout +003 timeout +004 timeout +005 timeout +006 timeout +007 timeout +008 timeout +009 timeout +010 timeout +011 timeout +012 timeout +013 timeout +014 timeout +015 timeout +016 timeout +017 timeout +018 timeout +019 timeout +020 timeout +021 timeout +022 timeout +023 timeout +024 timeout +025 timeout +026 timeout +027 timeout +028 timeout +029 timeout +030 timeout +031 timeout +032 timeout +033 timeout +034 timeout +035 timeout +036 timeout +037 timeout +038 timeout +039 timeout +040 timeout +041 timeout +042 timeout +043 timeout +044 timeout +045 timeout +046 timeout +047 timeout +048 timeout +049 timeout +050 timeout +051 timeout +052 timeout +053 timeout +054 timeout +055 timeout +056 timeout +057 timeout +058 timeout +059 timeout +060 unsat +061 timeout +062 unsat +063 unknown +064 timeout +065 timeout +066 timeout +067 unsat +068 unknown +069 unknown +070 unknown +071 unknown +072 unsat +073 unknown +074 unknown +075 unsat +076 unknown +077 unknown +078 unknown +079 unknown +080 unknown +081 unknown +082 timeout +083 unknown +084 unknown +085 unknown +086 unknown +087 timeout +088 unknown +089 unknown +090 unknown +091 unknown +092 unknown +093 unknown +094 unsat +095 unknown +096 unsat +097 unsat +098 unknown +099 unknown +100 unknown +101 unknown +102 unknown +103 unsat +104 unsat +105 unsat +106 unsat +107 timeout +108 unknown +109 unsat +110 unsat +111 unsat +112 timeout +113 unsat +114 unsat +115 unknown +116 timeout +117 unknown +118 timeout +119 timeout +120 timeout +121 unknown +122 timeout +123 unknown +124 timeout +125 unknown +126 timeout +127 unknown +128 timeout +129 timeout +130 unknown +131 timeout +132 unknown +133 unknown +134 unknown +135 unknown +136 unknown +137 unknown +138 unknown +139 unknown +140 unknown +141 unknown +142 unknown +143 unknown +144 unknown +145 timeout +146 unknown +147 unknown +148 unknown +149 unknown +150 unknown +151 timeout +152 unknown +153 timeout +154 timeout +155 timeout +156 timeout +157 unknown +158 timeout +159 unknown +160 timeout +161 timeout +162 timeout +163 unknown +164 timeout +165 unknown +166 unknown +167 timeout +168 unknown +169 timeout +170 timeout +171 timeout +172 unknown +173 unknown +174 timeout +175 timeout +176 unknown +177 timeout +178 unknown +179 timeout +180 unknown +181 timeout +182 unknown +183 timeout +184 unknown +185 timeout +186 timeout +187 unknown +188 timeout +189 timeout +190 unknown +191 unknown +192 timeout +193 timeout +194 timeout +195 timeout +196 timeout +197 unknown +198 unsat +199 unsat +200 timeout +201 unknown +202 unknown +203 unsat +204 unknown +205 unknown +206 timeout +207 timeout +208 timeout +209 timeout +210 timeout +211 unsat +212 timeout +213 timeout +214 timeout +215 unknown +216 unknown +217 unknown +218 timeout +219 timeout +220 timeout +221 unknown +222 unknown +223 unknown +224 unknown +225 timeout +226 timeout +227 timeout +228 unknown +229 timeout +230 unknown +231 unknown +232 unknown +233 timeout +234 unknown +235 unknown +236 unknown +237 unknown +238 timeout +239 unknown +240 unknown +241 unknown +242 unknown +243 unknown +244 unknown +245 unknown +246 unknown +247 unknown +248 unknown +249 unknown +250 unknown +251 unknown +252 unknown +253 unknown +254 unknown +255 unknown +256 unknown +257 unknown +258 unknown +259 unknown +260 unknown +261 unknown +262 unknown +263 unknown +264 unknown +265 unknown +266 timeout +267 unknown +268 unknown +269 unknown +270 unknown +271 unknown +272 unknown +273 unknown +274 unknown +275 unknown +276 unknown +277 unknown +278 unknown +279 unknown +280 unknown +281 unknown +282 unknown +283 unknown +284 timeout +285 unknown +286 unknown +287 unknown +288 unknown +289 unknown +290 unknown +291 unknown +292 unknown +293 unknown +294 unknown +295 unknown +296 unknown +297 unknown +298 unknown +299 unknown +300 unknown +301 unknown +302 unknown +303 unknown +304 unknown +305 unknown +306 unknown +307 unknown +308 unknown +309 unknown +310 unknown +311 unknown +312 unknown +313 unknown +314 unknown +315 unknown +316 unknown +317 unknown +318 unknown +319 unknown +320 unknown +321 unknown +322 unknown +323 unknown +324 unknown +325 unknown +326 unknown +327 unknown +328 unknown +329 unknown +330 unknown +331 unknown +332 unknown +333 unknown +334 unknown +335 unknown +336 unsat +337 timeout +338 unsat +339 unsat +340 timeout +341 timeout +342 unknown +343 timeout +344 timeout +345 unknown +346 timeout +347 timeout +348 timeout +349 unsat +350 timeout +351 timeout +352 timeout +353 unknown +354 unknown +355 timeout +356 timeout +357 unknown +358 unknown +359 timeout +360 timeout +361 unknown +362 unknown +363 timeout +364 timeout +365 timeout +366 timeout +367 timeout +368 timeout +369 timeout +370 timeout +371 timeout +372 timeout +373 timeout +374 timeout +375 timeout +376 timeout +377 unknown +378 timeout +379 timeout +380 timeout +381 timeout +382 timeout +383 timeout +384 timeout +385 timeout +386 timeout +387 timeout +388 timeout +389 timeout +390 timeout +391 timeout +392 timeout +393 timeout +394 timeout +395 timeout +396 timeout +397 timeout +398 timeout +399 unknown +400 timeout +401 timeout +402 unknown +403 unknown +404 unknown +405 unknown +406 unknown +407 timeout +408 timeout +409 timeout +410 timeout +411 timeout +412 timeout +413 timeout +414 timeout +415 timeout +416 timeout +417 timeout +418 timeout +419 timeout +420 timeout +421 timeout +422 timeout +423 timeout +424 timeout +425 timeout +426 timeout +427 timeout +428 timeout +429 timeout +430 timeout +431 timeout +432 timeout +433 timeout +434 timeout +435 timeout +436 unknown +437 timeout +438 unknown +439 timeout +440 timeout +441 timeout +442 timeout +443 timeout +444 timeout +445 timeout +446 timeout +447 timeout +448 timeout +449 timeout +450 unknown +451 timeout +452 timeout +453 timeout +454 timeout +455 timeout diff --git a/benchmarks/LIA_z3.txt b/benchmarks/LIA_z3.txt new file mode 100644 index 000000000..908a18419 --- /dev/null +++ b/benchmarks/LIA_z3.txt @@ -0,0 +1,456 @@ +000 sat +001 sat +002 sat +003 sat +004 sat +005 sat +006 sat +007 sat +008 sat +009 sat +010 sat +011 sat +012 sat +013 sat +014 timeout +015 sat +016 timeout +017 sat +018 timeout +019 sat +020 timeout +021 sat +022 sat +023 sat +024 timeout +025 timeout +026 timeout +027 timeout +028 timeout +029 timeout +030 timeout +031 timeout +032 timeout +033 timeout +034 timeout +035 timeout +036 timeout +037 timeout +038 timeout +039 timeout +040 timeout +041 timeout +042 timeout +043 sat +044 timeout +045 timeout +046 timeout +047 timeout +048 sat +049 unsat +050 timeout +051 sat +052 timeout +053 timeout +054 timeout +055 timeout +056 timeout +057 timeout +058 timeout +059 sat +060 unsat +061 sat +062 unsat +063 unsat +064 sat +065 sat +066 sat +067 unsat +068 sat +069 unsat +070 sat +071 sat +072 unsat +073 unsat +074 sat +075 unsat +076 unsat +077 unsat +078 sat +079 sat +080 unsat +081 unsat +082 sat +083 sat +084 sat +085 sat +086 unsat +087 timeout +088 sat +089 sat +090 unsat +091 sat +092 timeout +093 unsat +094 timeout +095 sat +096 timeout +097 timeout +098 unsat +099 sat +100 sat +101 timeout +102 timeout +103 unsat +104 unsat +105 unsat +106 unsat +107 sat +108 sat +109 unsat +110 unsat +111 unsat +112 unsat +113 unsat +114 unsat +115 sat +116 sat +117 sat +118 sat +119 timeout +120 sat +121 sat +122 timeout +123 timeout +124 sat +125 sat +126 timeout +127 sat +128 sat +129 sat +130 timeout +131 timeout +132 timeout +133 unsat +134 unsat +135 sat +136 unsat +137 unsat +138 sat +139 unsat +140 sat +141 unsat +142 unsat +143 sat +144 sat +145 unsat +146 sat +147 unsat +148 unsat +149 sat +150 unsat +151 sat +152 unsat +153 sat +154 sat +155 sat +156 sat +157 unsat +158 unsat +159 sat +160 sat +161 sat +162 sat +163 sat +164 unsat +165 unsat +166 sat +167 sat +168 unsat +169 timeout +170 timeout +171 timeout +172 sat +173 timeout +174 timeout +175 timeout +176 timeout +177 timeout +178 sat +179 timeout +180 sat +181 timeout +182 sat +183 timeout +184 sat +185 unknown +186 sat +187 timeout +188 timeout +189 timeout +190 sat +191 timeout +192 sat +193 sat +194 timeout +195 timeout +196 timeout +197 sat +198 unsat +199 unsat +200 sat +201 sat +202 sat +203 unsat +204 sat +205 sat +206 sat +207 sat +208 sat +209 timeout +210 sat +211 unsat +212 timeout +213 sat +214 timeout +215 timeout +216 sat +217 timeout +218 timeout +219 sat +220 timeout +221 sat +222 sat +223 sat +224 sat +225 timeout +226 timeout +227 sat +228 sat +229 sat +230 sat +231 sat +232 sat +233 sat +234 sat +235 sat +236 sat +237 sat +238 sat +239 timeout +240 timeout +241 timeout +242 timeout +243 timeout +244 sat +245 unsat +246 sat +247 unsat +248 sat +249 unsat +250 sat +251 sat +252 sat +253 unsat +254 sat +255 sat +256 unsat +257 sat +258 sat +259 unsat +260 sat +261 sat +262 sat +263 unsat +264 unsat +265 timeout +266 unsat +267 timeout +268 unsat +269 sat +270 sat +271 unsat +272 sat +273 unsat +274 timeout +275 unsat +276 unsat +277 unsat +278 sat +279 unsat +280 unsat +281 timeout +282 timeout +283 unsat +284 timeout +285 timeout +286 unsat +287 timeout +288 sat +289 unsat +290 timeout +291 sat +292 timeout +293 sat +294 timeout +295 unsat +296 timeout +297 unsat +298 timeout +299 sat +300 timeout +301 unsat +302 timeout +303 timeout +304 timeout +305 unsat +306 timeout +307 unsat +308 timeout +309 timeout +310 timeout +311 timeout +312 sat +313 timeout +314 unsat +315 timeout +316 timeout +317 unsat +318 sat +319 unsat +320 sat +321 timeout +322 timeout +323 sat +324 sat +325 timeout +326 unsat +327 timeout +328 unsat +329 sat +330 timeout +331 timeout +332 timeout +333 unsat +334 sat +335 sat +336 unsat +337 sat +338 unsat +339 unsat +340 sat +341 sat +342 unsat +343 unsat +344 unsat +345 unsat +346 sat +347 sat +348 sat +349 unsat +350 unsat +351 unsat +352 sat +353 unsat +354 unsat +355 sat +356 unsat +357 timeout +358 sat +359 sat +360 sat +361 timeout +362 sat +363 sat +364 unsat +365 sat +366 sat +367 sat +368 sat +369 sat +370 sat +371 sat +372 sat +373 sat +374 sat +375 sat +376 sat +377 sat +378 sat +379 sat +380 sat +381 sat +382 sat +383 sat +384 sat +385 sat +386 sat +387 unsat +388 sat +389 unsat +390 sat +391 sat +392 sat +393 unsat +394 sat +395 sat +396 sat +397 sat +398 sat +399 unsat +400 unsat +401 sat +402 timeout +403 timeout +404 timeout +405 timeout +406 timeout +407 sat +408 sat +409 unsat +410 sat +411 unsat +412 sat +413 sat +414 unsat +415 sat +416 sat +417 sat +418 sat +419 unsat +420 sat +421 sat +422 unsat +423 unsat +424 sat +425 sat +426 unsat +427 unsat +428 sat +429 unsat +430 unsat +431 unsat +432 sat +433 sat +434 unsat +435 sat +436 unsat +437 unsat +438 timeout +439 sat +440 sat +441 sat +442 unsat +443 sat +444 unsat +445 sat +446 sat +447 sat +448 unsat +449 sat +450 timeout +451 unsat +452 unsat +453 sat +454 sat +455 sat diff --git a/run_benchmark.sh b/run_benchmark.sh new file mode 100755 index 000000000..b3c356b50 --- /dev/null +++ b/run_benchmark.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +set -e + +# Switch working directory to the folder, where this script is laying around. +# That way all paths are guaranteed to be relative to the script locaiton. +pushd $(dirname ${BASH_SOURCE[0]}) + +cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo +cmake --build build + +# debug: 009 + +# pushd build +# make -j4 +# popd + + +# gdb --args \ +# ./build/loat-static \ +# --mode reachability \ +# --format horn \ +# --proof-level 0 \ +# "../chc-comp22-benchmarks/LIA/chc-LIA_060.smt2" +# # # # # "../chc-comp22-benchmarks/LIA/chc-LIA_999.smt2" +# # # # # --log \ + +# popd +# exit + +benchmark="LIA" +# benchmark="LIA" +# compare_with="z3" +compare_with="z3" + +while IFS= read -r line +do + if [[ $line =~ ^#.*$ ]] || [ "$line" = "" ] ; then + # skip comments and empty lines + continue + else + read idx prev_result <<< "$line" + file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" + + # if [[ "$prev_result" != "timeout" ]]; then + + set +e + result=$(timeout 5 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") + # result=$(timeout 10 z3 "$file") + exit_status=$? + set -e + + if [ $exit_status -eq 0 ]; then + result=$(echo "$result" | head -n 1) + # result="$result" + elif [ $exit_status -eq 124 ]; then + result="timeout" + else + result="error" + fi + + if [[ "$result" == "$prev_result" ]]; then + printf "$idx $result \n" + else + printf "$idx $prev_result --> $result \n" + fi + + #fi + fi +done < "./benchmarks/${benchmark}_${compare_with}.txt" + +# "undo" pushd +popd diff --git a/run_tests.sh b/run_tests.sh index ad6005932..a1e5575f9 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -28,7 +28,7 @@ while read line; do exit 0 fi else - if [ "$line" = "LIA-Lin" ]; then + if [[ "$line" = "LIA-Lin" ]] || [[ $line = "LIA" ]]; then # line contains the folder of the following benchmarks path=$line else diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d9822229..fc4d99b87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(util) add_subdirectory(redundance) add_subdirectory(abmc) add_subdirectory(bmc) +add_subdirectory(nonlinear) target_sources(${EXECUTABLE} PRIVATE diff --git a/src/analysis/preprocessing.cpp b/src/analysis/preprocessing.cpp index eb3d1097d..a88e94b33 100644 --- a/src/analysis/preprocessing.cpp +++ b/src/analysis/preprocessing.cpp @@ -25,7 +25,16 @@ using namespace std; +// FIXME: for trivial ITS with no (reachable) initial locations or no (reachable) sinks, +// we should return SAT, but due to this preprocessing step we will throw an error instead. ResultViaSideEffects remove_irrelevant_clauses(ITSProblem &its, bool forward) { + if (its.getInitialTransitions().size() == 0) { + throw std::logic_error("remove_irrelevant_clauses: ITS has no initial transitions"); + } + if (its.getSinkTransitions().size() == 0) { + throw std::logic_error("remove_irrelevant_clauses: ITS has no sink transitions"); + } + std::set keep; std::stack todo; for (const auto x: forward ? its.getInitialTransitions() : its.getSinkTransitions()) { @@ -149,7 +158,12 @@ ResultViaSideEffects Preprocess::preprocess(ITSProblem &its) { if (Config::Analysis::log) { std::cout << "starting preprocesing..." << std::endl; } - if (Config::Analysis::reachability()) { + if (Config::Analysis::reachability() && its.nonLinearCHCs.size() == 0) { + // In this preprocessing step we remove transitions, that are not "forward reachable" + // from a initial location or "backward reachable" from a sink. Note, that this step + // is not valid though, if the ITS contains non-linear CHCs. For example a sink might + // only be reachable via a non-linear CHCs, but because non-linear CHCs are not acounted + // for in the dependency graph, we would remove the sink because it appears to be unreachable. if (Config::Analysis::log) { std::cout << "removing irrelevant clauses..." << std::endl; } diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index a1a03a37b..b1a82df12 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -106,9 +106,11 @@ ITSProof* ProvedUnsat::operator->() { ProofFailed::ProofFailed(const std::string &msg): std::runtime_error(msg) {} -Reachability::Reachability(ITSProblem &chcs): +Reachability::Reachability(ITSProblem &chcs, bool incremental_mode): chcs(chcs), - drop(!Config::Analysis::safety()) + drop(!Config::Analysis::safety()), + analysis_result(LinearSolver::Result::Pending), + incremental_mode(incremental_mode) { switch (Config::Analysis::smtSolver) { case Config::Analysis::Z3: @@ -119,6 +121,11 @@ Reachability::Reachability(ITSProblem &chcs): break; } solver->enableModels(); + proof.majorProofStep("Initial ITS", ITSProof(), chcs); + if (Config::Analysis::log) { + std::cout << "Initial ITS" << std::endl; + ITSExport::printForProof(chcs, std::cout); + } } Step::Step(const TransIdx transition, const BoolExpr &sat, const Subs &var_renaming, const Rule &resolvent): @@ -225,14 +232,18 @@ void Reachability::update_cpx() { Rule Reachability::compute_resolvent(const TransIdx idx, const BoolExpr &implicant) const { static Rule dummy(top(), Subs()); - if (!Config::Analysis::complexity()) { + + // Resolvent only has to be computed in incremental mode (i.e. when the problem includes non linear CHCs) or + // for complexity analysis. Otherwise we can skip it. + if (Config::Analysis::complexity() || incremental_mode) { + auto resolvent = idx->withGuard(implicant); + if (!trace.empty()) { + resolvent = Chaining::chain(trace.back().resolvent, resolvent).first; + } + return *Preprocess::preprocessRule(resolvent); + } else { return dummy; } - auto resolvent = idx->withGuard(implicant); - if (!trace.empty()) { - resolvent = Chaining::chain(trace.back().resolvent, resolvent).first; - } - return *Preprocess::preprocessRule(resolvent); } bool Reachability::store_step(const TransIdx idx, const Rule &implicant, bool force) { @@ -341,7 +352,7 @@ void Reachability::luby_next() { void Reachability::unsat() { const auto res = Config::Analysis::safety() ? "unknown" : (Config::Analysis::reachability() ? "unsat" : "NO"); - std::cout << res << std::endl << std::endl; + analysis_result = analysis_result_from(res); if (!Config::Analysis::log && Proof::disabled()) { return; } @@ -366,7 +377,7 @@ void Reachability::unsat() { void Reachability::sat() { const auto res = Config::Analysis::safety() ? "sat" : "unknown"; - std::cout << res << std::endl << std::endl; + analysis_result = analysis_result_from(res); if (!Config::Analysis::log && Proof::disabled()) { return; } @@ -696,13 +707,26 @@ bool Reachability::try_to_finish() { return false; } +LinearSolver::Result Reachability::analysis_result_from(std::string res) const { + if (res == "sat") { + return LinearSolver::Result::Sat; + } else if (res == "unsat") { + return LinearSolver::Result::Unsat; + } else if (res == "unknown") { + return LinearSolver::Result::Unknown; + } else if (res == "pending") { + return LinearSolver::Result::Pending; + } else { + throw std::logic_error("undefined analysis result"); + } +} + +LinearSolver::Result Reachability::get_analysis_result() const { + return analysis_result; +} + void Reachability::analyze() { - static std::default_random_engine rnd {}; - proof.majorProofStep("Initial ITS", ITSProof(), chcs); - if (Config::Analysis::log) { - std::cout << "Initial ITS" << std::endl; - ITSExport::printForProof(chcs, std::cout); - } + // TODO: are other preprocessing steps also unsound when in non-linear context? const auto res {Preprocess::preprocess(chcs)}; if (res) { proof.concat(res.getProof()); @@ -712,15 +736,89 @@ void Reachability::analyze() { } } init(); - if (try_to_finish()) { - return; + + if (!try_to_finish()) { + blocked_clauses[0].clear(); + derive_new_facts(); + } + + switch (get_analysis_result()) { + case LinearSolver::Result::Sat: + std::cout << "sat"; + break; + case LinearSolver::Result::Unsat: + std::cout << "unsat"; + break; + case LinearSolver::Result::Unknown: + std::cout << "unknown"; + break; + case LinearSolver::Result::Pending: + throw std::logic_error("exited main loop although analysis result is pending"); } - blocked_clauses[0].clear(); - do { + + std::cout << std::endl << std::endl << std::endl; +} + +/** + * The resolvent of the entire current trace represents a fact, + * that can be converted into a `Clause`. If the trace is empty, + * this function returns nullopt. + */ +const std::optional Reachability::trace_as_fact() { + if (!incremental_mode) { + // In non-incremental mode the trace resolvent is not really computed and only a dummy expression. + // See `compute_resolvent` above. So `trace_as_fact` will silently return nonsense. + throw std::logic_error("Calling `trace_as_fact` in non-incremental mode doesn't make sense."); + } + + if (trace.empty()) { + return {}; + } else { + const Step step = trace.back(); + + std::vector guard_conj = { step.resolvent.getGuard() }; + std::vector args_renamed; + const auto subs = step.resolvent.getUpdate(); + + for (const Var &var : chcs.getProgVars()) { + auto it = subs.find(var); + + if (it == subs.end()) { + args_renamed.push_back(var); + } else { + const auto optional_var = expr::toVar(expr::second(*it)); + + if (optional_var.has_value()) { + args_renamed.push_back(optional_var.value()); + } else { + const auto new_var = expr::next(var); + args_renamed.push_back(new_var); + guard_conj.push_back(expr::mkEq(expr::toExpr(new_var), subs.get(var))); + } + } + } + + const auto guard = BExpression::buildAnd(guard_conj); + const auto rhs = FunApp(chcs.getRhsLoc(step.clause_idx), args_renamed); + return Clause({}, rhs, guard); + } +} + +const std::list Reachability::derive_new_facts() { + static std::default_random_engine rnd {}; + + std::list derived_facts; + + while (true) { size_t next_restart = luby_unit * luby.second; std::unique_ptr state; if (Config::Analysis::log) std::cout << "trace: " << trace << std::endl; if (!trace.empty()) { + // true by default, because if no case in the following for-loop applies, then we still want to add a new fact, + // because we have a non-empty unseen trace. + bool should_add_fact = true; + + // QUESTION: why not exit loop after frist matching case? for (auto backlink = has_looping_suffix(trace.size() - 1); backlink && luby_loop_count < next_restart; backlink = has_looping_suffix(*backlink - 1)) { @@ -733,6 +831,8 @@ void Reachability::analyze() { backtrack(); proof.headline("Covered"); print_state(); + // if we run into `covered` we don't add a new fact, because backtrack to previously seen facts. + should_add_fact = false; } else if (state->succeeded()) { if (simple_loop) { block(step); @@ -740,31 +840,51 @@ void Reachability::analyze() { proof.majorProofStep("Accelerate", (*state->succeeded())->getProof(), chcs); print_state(); if ((drop || simple_loop) && try_to_finish()) { - return; + return derived_facts; } + + // if we just applied acceleration successfully, then the trace contains a new (more general) fact. + should_add_fact = true; } else if (state->dropped()) { if (simple_loop && !Config::Analysis::safety()) { block(step); } proof.majorProofStep("Accelerate and Drop", state->dropped()->get_proof(), chcs); print_state(); + // QUESTION: why shouldn't we add a fact in this case again? + should_add_fact = false; } else if (state->unsat()) { proof.majorProofStep("Nonterm", **state->unsat(), chcs); proof.headline("Step with " + std::to_string(trace.back().clause_idx->getId())); print_state(); unsat(); - return; + return derived_facts; + } + } + + ////////////////////////////////////// TODO: refactor this /////////////////////////////////////////////////////////// + if (incremental_mode && should_add_fact) { + // Using a crude (additional) redundancy criterion for facts here. We identify facts by the trace that lead to them. + // This is only sufficient because equivalent facts can have multiple traces. We don't need to memoize the entire + // trace structure. It's enough to store clause_idx/implicant for each trace step. + std::vector> trace_id; + for (const auto &step: trace) { + trace_id.push_back(std::make_pair( + step.clause_idx, + step.implicant + )); + } + + if (!seen_traces.contains(trace_id)) { + derived_facts.push_back(trace_as_fact().value()); + seen_traces.insert(trace_id); } } + } if (luby_loop_count == next_restart || (state && state->restart()) || !check_consistency()) { if (Config::Analysis::log) std::cout << "restarting after " << luby_loop_count << " loops" << std::endl; - // restart - while (!trace.empty()) { - pop(); - } - luby_next(); - proof.headline("Restart"); + restart(); } const auto try_set = trace.empty() ? chcs.getInitialTransitions() : chcs.getSuccessors(trace.back().clause_idx); std::vector to_try(try_set.begin(), try_set.end()); @@ -783,27 +903,79 @@ void Reachability::analyze() { } } if (trace.empty()) { - break; + // TODO: is this the only place where we break out of the main loop with SAT? + proof.headline("Accept"); + if (Config::Analysis::complexity()) { + proof.result(cpx.toString()); + proof.print(); + } else { + sat(); + } + + return derived_facts; } else if (all_failed) { backtrack(); proof.headline("Backtrack"); print_state(); } else if (try_to_finish()) { // check whether a query is applicable after every step and, importantly, before acceleration (which might approximate) - return; + return derived_facts; } - } while (true); - proof.headline("Accept"); - if (Config::Analysis::complexity()) { - proof.result(cpx.toString()); - proof.print(); - } else { - sat(); } - std::cout << std::endl; +} + +void Reachability::restart() { + while (!trace.empty()) { + pop(); + } + luby_next(); + proof.headline("Restart"); + + analysis_result = LinearSolver::Result::Pending; +} + +void Reachability::add_clauses(const std::list &clauses) { + bool any_linear_clauses = false; + for (const auto &chc : clauses) { + chcs.addClause(chc); + if (chc.isLinear()) { + any_linear_clauses = true; + } + } + + if (any_linear_clauses) { + chcs.refineDependencyGraph(); + // TODO: are other preprocessing steps also unsound when in non-linear context? + const auto res {Preprocess::preprocess(chcs)}; + if (res) { + proof.concat(res.getProof()); + if (Config::Analysis::log) { + std::cout << "Simplified ITS" << std::endl; + ITSExport::printForProof(chcs, std::cout); + } + } + init(); + + // When we add a new linear clause we have to restart the solver, + // i.e. reset the trace, otherwise we might block a potential reachability path. + restart(); + } +} + +const std::list Reachability::get_initial_facts() const { + std::list facts; + for (const auto trans_idx : chcs.getInitialTransitions()) { + const Clause fact = chcs.clauseFrom(trans_idx); + facts.push_back(fact); + } + return facts; +} + +const std::list Reachability::get_non_linear_chcs() const { + return chcs.nonLinearCHCs; } void Reachability::analyze(ITSProblem &its) { - Reachability(its).analyze(); + Reachability(its, false).analyze(); } } diff --git a/src/analysis/reachability.hpp b/src/analysis/reachability.hpp index 0d9542348..681c34817 100644 --- a/src/analysis/reachability.hpp +++ b/src/analysis/reachability.hpp @@ -149,7 +149,7 @@ class ProofFailed : public std::runtime_error { ProofFailed(const std::string &msg); }; -class Reachability { +class Reachability : public ILinearSolver { ITSProblem &chcs; @@ -293,12 +293,38 @@ class Reachability { bool try_to_finish(); - Reachability(ITSProblem &its); - void analyze(); + void analyze_incremental(); + + bool main_loop_body(); + + void restart(); + + LinearSolver::Result analysis_result; + + LinearSolver::Result analysis_result_from(std::string res) const; + + const bool incremental_mode; + + std::set>> seen_traces; + + const std::optional trace_as_fact(); + public: + Reachability(ITSProblem &chcs, bool incremental_mode); + + LinearSolver::Result get_analysis_result() const override; + + void add_clauses(const std::list &chc) override; + + const std::list derive_new_facts() override; + + const std::list get_initial_facts() const override; + + const std::list get_non_linear_chcs() const override; + static void analyze(ITSProblem &its); }; diff --git a/src/its/itsproblem.cpp b/src/its/itsproblem.cpp index d2dd50912..6b76f38c7 100644 --- a/src/its/itsproblem.cpp +++ b/src/its/itsproblem.cpp @@ -17,6 +17,7 @@ #include "itsproblem.hpp" #include "export.hpp" +#include "expr.hpp" #include "smtfactory.hpp" #include "chain.hpp" @@ -258,3 +259,148 @@ std::set ITSProblem::refineDependencyGraph() { size_t ITSProblem::size() const { return graph.size(); } + +const std::vector ITSProblem::getProgVars() const { + std::vector prog_vars; + prog_vars.reserve(numProgVars.size() + boolProgVars.size()); + for (const auto &var: numProgVars) { + prog_vars.push_back(var); + } + for (const auto &var: boolProgVars) { + prog_vars.push_back(var); + } + return prog_vars; +} + +/** + * Converts an ITS rule (identified by a TransIdx) back to CHC representation. + * This does not restore the original representation after parsing perfectly, + * since number and order of predicate arguments is lost. + */ +const Clause ITSProblem::clauseFrom(TransIdx rule) const { + const auto prog_vars = getProgVars(); + + const auto guard = rule->getGuard(); + + const LocationIdx lhs_loc = getLhsLoc(rule); + const LocationIdx rhs_loc = getRhsLoc(rule); + + const auto rhs = FunApp(rhs_loc, prog_vars).renameWith(rule->getUpdate()); + + if (lhs_loc == getInitialLocation()) { + // rule is a linear CHC with no LHS predicates, ie a "fact" + return Clause({}, rhs, guard); + } else { + // rule is a linear CHC with exactly one LHS predicates, ie a "rule" + return Clause({ FunApp(lhs_loc, prog_vars) }, rhs, guard); + } +} + +/** + * TODO docs + */ +const FunApp normalizePredicate(unsigned int_var_count, unsigned bool_var_count, const FunApp &pred) { + std::vector int_args; + int_args.reserve(int_var_count); + std::vector bool_args; + bool_args.reserve(bool_var_count); + + for (const Var &arg: pred.args) { + if (std::holds_alternative(arg)) { + int_args.push_back(arg); + } else { + bool_args.push_back(arg); + } + } + + while (int_args.size() < int_var_count) { + int_args.push_back(NumVar::next()); + } + + while (bool_args.size() < bool_var_count) { + bool_args.push_back(BoolVar::next()); + } + + int_args.insert(int_args.end(), bool_args.begin(), bool_args.end()); + return FunApp(pred.loc, int_args); +} + +/** + * Adds a clause to the ITS problem. If the clause is linear, it's converted to an ITS rule. + * If the clause is non-linear it's separately stored in `nonLinearCHCs`. + */ +void ITSProblem::addClause(const Clause &c) { + if (c.isLinear()) { + // If the clause is linear, extract the single LHS predicate. Or in case there are zero + // LHS predicates, construct a dummy predicate with the initial ITS location as the + // predicate symbol. + const FunApp lhs = c.lhs.size() == 1 ? *c.lhs.begin() : FunApp(getInitialLocation(), {}); + + Subs ren; + // replace the arguments of the body predicate with the corresponding program variables + unsigned bool_arg {0}; + unsigned int_arg {0}; + for (unsigned i = 0; i < lhs.args.size(); ++i) { + if (std::holds_alternative(lhs.args[i])) { + ren.put( + std::get(lhs.args[i]), + numProgVars[int_arg] + ); + ++int_arg; + } else { + ren.put( + std::get(lhs.args[i]), + std::get(expr::toExpr(boolProgVars[bool_arg])) + ); + ++bool_arg; + } + } + VarSet cVars; + for (const auto &var: c.rhs.args) { + cVars.insert(var); + } + c.guard->collectVars(cVars); + // replace all other variables from the clause with temporary variables + for (const auto &x: cVars) { + if (!ren.contains(x)) { + ren.put(x, expr::toExpr(expr::next(x))); + } + } + bool_arg = 0; + int_arg = 0; + Subs up; + for (unsigned i = 0; i < c.rhs.args.size(); ++i) { + if (std::holds_alternative(c.rhs.args[i])) { + up.put(numProgVars[int_arg], ren.get(std::get(c.rhs.args[i]))); + ++int_arg; + } else if (std::holds_alternative(c.rhs.args[i])) { + up.put(boolProgVars[bool_arg], ren.get(std::get(c.rhs.args[i]))); + ++bool_arg; + } else { + throw std::logic_error("unsupported theory in CHCParseVisitor"); + } + } + for (unsigned i = int_arg; i < numProgVars.size(); ++i) { + up.put(numProgVars[i], NumVar::next()); + } + for (unsigned i = bool_arg; i < boolProgVars.size(); ++i) { + up.put(boolProgVars[i], std::get(expr::toExpr(BoolVar::next()))); + } + up.put(NumVar::loc_var, c.rhs.loc); + const BoolExpr guard = c.guard->subs(ren)->simplify() & Rel::buildEq(NumVar::loc_var, lhs.loc); + addRule(Rule(guard, up), lhs.loc); + } else { + std::set lhs_normalized; + + for (const FunApp &pred: c.lhs) { + const auto pred_normalized = normalizePredicate(numProgVars.size(), boolProgVars.size(), pred); + lhs_normalized.insert(pred_normalized); + } + + nonLinearCHCs.push_back(Clause( + lhs_normalized, + normalizePredicate(numProgVars.size(), boolProgVars.size(), c.rhs), + c.guard + )); + } +} diff --git a/src/its/itsproblem.hpp b/src/its/itsproblem.hpp index 1cf097f7d..aaf93bc74 100644 --- a/src/its/itsproblem.hpp +++ b/src/its/itsproblem.hpp @@ -18,6 +18,7 @@ #pragma once #include "rule.hpp" +#include "linearsolver.hpp" #include "dependencygraph.hpp" #include "types.hpp" @@ -106,6 +107,20 @@ class ITSProblem { size_t size() const; + // TODO: these attributes should probably not be "so" public. They should only be initialized + // once after parsing and should not be mutated during analysis. But because the ITS instance is constructed + // incrementally, we can't pass these values into the constructor and having public getters+setters + // is no different from making the attributes public directly. + std::vector numProgVars; + std::vector boolProgVars; + const std::vector getProgVars() const; + + std::list nonLinearCHCs; + + void addClause(const Clause &chc); + + const Clause clauseFrom(TransIdx trans_idx) const; + protected: DG graph; diff --git a/src/lib/expr/expr.cpp b/src/lib/expr/expr.cpp index 25c22217d..ae300bc95 100644 --- a/src/lib/expr/expr.cpp +++ b/src/lib/expr/expr.cpp @@ -14,6 +14,42 @@ bool isProgVar(const Var &var) { return !isTempVar(var); } +/** + * If the given expression is a simple (non-negated) variable, + * it is returned. For compound expressions, we return nullopt. + */ +const std::optional toVar(const ThExpr &expr) { + return std::visit(Overload{ + [](const Expr &num_expr) -> std::optional { + if (num_expr.isVar()) { + return Var(num_expr.toVar()); + } else { + return {}; + } + }, + [](const BoolExpr &bool_expr) -> std::optional { + if (bool_expr->isTheoryLit()) { + const auto theory_lit = bool_expr->getTheoryLit(); + + return std::visit(Overload{ + [](const BoolLit &lit) -> std::optional { + if (lit.isNegated()) { + return {}; + } else { + return Var(lit.getBoolVar()); + } + }, + [](const Rel &rel) -> std::optional { + return {}; + } + }, *theory_lit); + } else { + return {}; + } + } + }, expr); +} + Var next(const Var &var) { return std::visit(Overload{ [](const NumVar&) { diff --git a/src/lib/expr/expr.hpp b/src/lib/expr/expr.hpp index 04fd139ea..7a2fc3ac4 100644 --- a/src/lib/expr/expr.hpp +++ b/src/lib/expr/expr.hpp @@ -10,6 +10,8 @@ bool isTempVar(const Var &var); bool isProgVar(const Var &var); +const std::optional toVar(const ThExpr &expr); + Var next(const Var &var); ThExpr toExpr(const Var &var); diff --git a/src/main.cpp b/src/main.cpp index 1e602b91e..a10ba19c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ #include "abmc.hpp" #include "yices.hpp" #include "recurrence.hpp" +#include "nonlinear.hpp" #include #include @@ -193,7 +194,12 @@ int main(int argc, char *argv[]) { yices::init(); switch (Config::Analysis::engine) { case Config::Analysis::ADCL: - reachability::Reachability::analyze(*its); + if (its->nonLinearCHCs.size() == 0) { + reachability::Reachability::analyze(*its); + } else { + auto linear_solver = reachability::Reachability(*its, true); + NonLinearSolver::analyze(linear_solver); + } break; case Config::Analysis::BMC: BMC::analyze(*its); diff --git a/src/nonlinear/CMakeLists.txt b/src/nonlinear/CMakeLists.txt new file mode 100644 index 000000000..1350584e7 --- /dev/null +++ b/src/nonlinear/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources(${EXECUTABLE} + PRIVATE + nonlinear.hpp + nonlinear.cpp + clause.hpp + clause.cpp + linearsolver.hpp +) + +target_include_directories(${EXECUTABLE} PRIVATE ".") diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp new file mode 100644 index 000000000..5e662f756 --- /dev/null +++ b/src/nonlinear/clause.cpp @@ -0,0 +1,242 @@ +#include "clause.hpp" +#include "boolexpr.hpp" +#include "expr.hpp" +#include "theory.hpp" +#include +#include + +/** + * TODO docs + */ +const Var varAt(const Var &var, const Subs &subs) { + auto it = subs.find(var); + + if (it == subs.end()) { + return var; + } else { + const auto optional_var = expr::toVar(expr::second(*it)); + + if (optional_var.has_value()) { + return optional_var.value(); + } else { + throw std::logic_error("Renaming contains non-variable expression"); + } + } +} + +/** + * Tries to compute a unifier for the two given predicates. Because predicate + * arguments are always simple variables (instead of expressions), the unifier + * is simply a variable renaming. If the predicates don't have the same + * predicate symbol or different arity or the argument types don't match, + * unification is not possible. For example: + * + * F(x,x) and G(y,z) are not unifiable + * F(x) and F(y,z) are not unifiable + * F(w,x) and F(y,z) returns { w -> y, x -> z } + * + * In practice we normalize all predicates to have the same arity with same argument + * types in the same order. So only differing predicate symbols should be a reason + * for two predicates to not be unifiable. But for "local" correctness we also check + * arity and argument types. + */ +const std::optional computeUnifier(const FunApp &pred1, const FunApp &pred2) { + if (pred1.loc == pred2.loc && pred1.args.size() == pred2.args.size()) { + Subs subs; + + size_t size = pred1.args.size(); + for (size_t i = 0; i < size; ++i) { + const auto var1 = pred1.args[i]; + const auto var2 = pred2.args[i]; + + // QUESTION: I suspect some C++ template magic can make this prettier + bool both_nums = std::holds_alternative(var1) && + std::holds_alternative(var2); + bool both_bools = std::holds_alternative(var1) && + std::holds_alternative(var2); + + if (both_nums) { + subs.put(std::make_pair( + std::get(var1), + std::get(expr::toExpr(var2)) + )); + } else if (both_bools) { + subs.put(std::make_pair( + std::get(var1), + std::get(expr::toExpr(var2)) + )); + } else { + // argument types don't match ==> not unifiable + return {}; + } + } + + return subs; + } else { + return {}; + } +} + +/** + * Apply `renaming` to all arguments of the predicate. + * Will throw an error if the renaming maps to compound expressions + * instead of variables. + */ +const FunApp FunApp::renameWith(const Subs &renaming) const { + std::vector args_renamed; + + for (const Var &var : args) { + const auto target_var = varAt(var, renaming); + args_renamed.push_back(target_var); + } + + return FunApp(loc, args_renamed); +} + +/** + * Collect set of variables used in predicate arguments. + */ +const VarSet FunApp::vars() const { + VarSet vs; + for (const auto &arg: args) { + vs.insert(arg); + } + return vs; +} + +/** + * Apply `renaming` to all variables in the clause, i.e variables in the guard and the + * arguments of all predicates on both LHS and RHS. Will throw an error if the renaming + * maps to compound expressions + */ +const Clause Clause::renameWith(const Subs &renaming) const { + std::set lhs_renamed; + for (const FunApp &pred : lhs) { + lhs_renamed.insert(pred.renameWith(renaming)); + } + + const FunApp rhs_renamed = rhs.renameWith(renaming); + const auto guard_renamed = guard->subs(renaming); + return Clause(lhs_renamed, rhs_renamed, guard_renamed); +} + +/** + * Collect set of variables used in predicate arguments and guard. + */ +const VarSet Clause::vars() const { + VarSet vs; + for (const auto &pred: lhs) { + vs.insertAll(pred.vars()); + } + vs.insertAll(rhs.vars()); + guard->collectVars(vs); + return vs; +} + +/** + * Compute resolution of `this` and `chc` using the RHS of `this` and `pred`, + * which is assumed to be on the LHS of `chc`. So the caller is responsible for + * choosing the literal to do resolution with. If `pred` is not on the LHS of + * `chc` we throw an error. + * + * For example, with + * + * this : F(x) ==> G(x) + * chc : G(y) /\ G(z) /\ y < z ==> H(y,z) + * pred : G(z) + * + * the returned resolvent should be + * + * G(y) /\ F(z) /\ y < z ==> H(y,z) + * + */ +const std::optional Clause::resolutionWith(const Clause &chc, const FunApp &pred) const { + if (!chc.lhs.contains(pred)) { + throw std::logic_error("Given `pred` is not on the LHS of `chc`"); + } + + std::set chc_lhs_without_pred = chc.lhs; + chc_lhs_without_pred.erase(pred); + + // Make sure variables in `this` and `chc` are disjoint. + Subs renaming; + const VarSet this_vars {this->vars()}; + for (const auto &var: chc.vars()) { + if (this_vars.find(var) != this_vars.end()) { + renaming.put(var, expr::toExpr(expr::next(var))); + } + } + const Clause this_with_disjoint_vars = this->renameWith(renaming); + + const auto unifier = computeUnifier(this_with_disjoint_vars.rhs, pred); + + // If the predicates are not unifiable, we don't throw an error but return + // nullopt. That way the caller can filter out unifiable predicates using this + // function. We could throw an error here too, but that implies that we expect + // the caller to check unifiablility first and implementing that check would + // be redundant. On the other hand, choosing a literal from the LHS of `chc` + // is trivial, so we require the caller to do that correctly. + if (!unifier.has_value()) { + return {}; + } + + const Clause this_unified = this->renameWith(unifier.value()); + + // LHS of resolvent is the union of the renamed LHS of `this` ... + std::set resolvent_lhs = this_unified.lhs; + // ... and the LHS of `chc` where `pred` is removed. + resolvent_lhs.insert(chc_lhs_without_pred.begin(), chc_lhs_without_pred.end()); + + return Clause( + resolvent_lhs, + chc.rhs, + this_unified.guard & chc.guard + ); +} + +/** + * Returns true iff the clause has at most one LHS predicate. + */ +bool Clause::isLinear() const { + return lhs.size() <= 1; +} + +bool operator<(const FunApp &fun1, const FunApp &fun2) { + if (fun1.loc < fun2.loc) { + return true; + } else if (fun2.loc < fun1.loc) { + return false; + } else { + return fun1.args < fun2.args; + } +} + +std::ostream &operator<<(std::ostream &s, const FunApp &fun) { + s << "F" << fun.loc; + + if (fun.args.size() > 0) { + auto iter = fun.args.begin(); + + s << "(" << *iter; + iter++; + + while (iter < fun.args.end()) { + s << "," << *iter; + iter++; + } + + s << ")"; + } + + return s; +} + +std::ostream &operator<<(std::ostream &s, const Clause &chc) { + for (const FunApp &fun_app : chc.lhs) { + s << fun_app << " /\\ "; + } + + s << chc.guard << " ==> " << chc.rhs; + + return s; +} diff --git a/src/nonlinear/clause.hpp b/src/nonlinear/clause.hpp new file mode 100644 index 000000000..69480cab1 --- /dev/null +++ b/src/nonlinear/clause.hpp @@ -0,0 +1,56 @@ +#include "rule.hpp" +#include "theory.hpp" +#include "types.hpp" +#include + +class FunApp { + +public: + const LocationIdx loc; + const std::vector args; + + FunApp(const LocationIdx loc, const std::vector args): loc(loc), args(args) {} + + const FunApp renameWith(const Subs &renaming) const; + + const VarSet vars() const; +}; + +class Clause { + +public: + const std::set lhs; + const FunApp rhs; + const BoolExpr guard; + + /** + * Constructor for a Constrained Horn Clause (CHC). For example: + * + * F(x1) /\ G(x2,x3) /\ (x1 < x2 /\ x2 < x3) ==> H(x1,x2,x3) + * + * ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ + * lhs guard rhs + * + * @param lhs - a set of predicates on the left-hand-side (LHS) of the implication. + * @param rhs - a single predicate on the right-hand-side (RHS) of the implication. + * @param guard - a boolean expression describing the clause constraint. + */ + Clause(const std::set &lhs, const FunApp &rhs, const BoolExpr &guard) + : lhs(lhs), rhs(rhs), guard(guard) {} + + const Clause renameWith(const Subs &renaming) const; + + const std::optional resolutionWith(const Clause &chc, const FunApp &pred) const; + + bool isLinear() const; + + const VarSet vars() const; + +}; + +// implement comparison operator for FunApp so we can store them in std::set +bool operator<(const FunApp &fun1, const FunApp &fun2); + +std::ostream& operator<<(std::ostream &s, const FunApp &fun_app); + +std::ostream& operator<<(std::ostream &s, const Clause &chc); diff --git a/src/nonlinear/linearsolver.hpp b/src/nonlinear/linearsolver.hpp new file mode 100644 index 000000000..b62225032 --- /dev/null +++ b/src/nonlinear/linearsolver.hpp @@ -0,0 +1,40 @@ +#pragma once +#include "clause.hpp" + +namespace LinearSolver { + enum Result { Unsat, Sat, Unknown, Pending }; +} + +/** + * Any linear CHC solver that implements this interface (e.g. ADCL) is compatible with the + * non-linear CHC solver. The linear solver must also act as a data structure + * for the CHC problem. That is, it must store all available linear and non-linear + * clauses. That way the non-linear solver is not only independent of the linear solver + * but also the problem representation (e.g. ITS). + */ +class ILinearSolver { + +public: + + // virtual destructor + virtual ~ILinearSolver() {}; + + /** + * Let linear solver derive all (ideally non-redundant) facts it can derive with + * the current linear clause set. + */ + virtual const std::list derive_new_facts() = 0; + + virtual LinearSolver::Result get_analysis_result() const = 0; + + /** + * Adds a new clauses to the linear solver in batch. The added clauses can be + * linear or non-linear. + */ + virtual void add_clauses(const std::list &clauses) = 0; + + virtual const std::list get_initial_facts() const = 0; + + virtual const std::list get_non_linear_chcs() const = 0; + +}; diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp new file mode 100644 index 000000000..14de380aa --- /dev/null +++ b/src/nonlinear/nonlinear.cpp @@ -0,0 +1,67 @@ +#include "nonlinear.hpp" +#include "booltheory.hpp" +#include "linearsolver.hpp" +#include + +void NonLinearSolver::analyze(ILinearSolver &linear_solver) { + std::stack> facts(linear_solver.get_initial_facts()); + + while (true) { + // std::cout << "================" << std::endl; + std::list resolvents; + + // Do resolution with of all `facts` with all non-linear clauses with the goal to derive + // new linear clauses that the linear solver can handle. + while (!facts.empty()) { + const Clause fact = facts.top(); + facts.pop(); + + // Note, we don't add resolvents to the ITS during this loop but instead collect on a stack, + // and then add the contained clauses to the ITS afterwards in a dedicated loop. + // Otherwise, the following for-loop is "dynamically extended" because we also loop + // over the newly added clauses. + for (const Clause &non_linear_chc: linear_solver.get_non_linear_chcs()) { + for (const auto &pred: non_linear_chc.lhs) { + const auto optional_resolvent = fact.resolutionWith(non_linear_chc, pred); + + if (optional_resolvent.has_value()) { + const auto resolvent = optional_resolvent.value(); + // TODO: check for redundancy + resolvents.push_back(resolvent); + } + } + } + } + + linear_solver.add_clauses(resolvents); + + const auto new_facts = linear_solver.derive_new_facts(); + for (const auto &fact : new_facts) { + // std::cout << fact << std::endl; + facts.push(fact); + } + + // std::cout << "facts: " << new_facts.size() << std::endl; + + const auto result = linear_solver.get_analysis_result(); + if (result == LinearSolver::Result::Unsat) { + break; + } else if ((result == LinearSolver::Result::Sat || result == LinearSolver::Result::Unknown) && facts.empty()) { + break; + } + } + + switch (linear_solver.get_analysis_result()) { + case LinearSolver::Result::Sat: + std::cout << "sat" << std::endl; + break; + case LinearSolver::Result::Unsat: + std::cout << "unsat" << std::endl; + break; + case LinearSolver::Result::Unknown: + std::cout << "unknown" << std::endl; + break; + case LinearSolver::Result::Pending: + throw std::logic_error("exited main loop, although linear solver is still pending"); + } +} diff --git a/src/nonlinear/nonlinear.hpp b/src/nonlinear/nonlinear.hpp new file mode 100644 index 000000000..c9ea1ad5b --- /dev/null +++ b/src/nonlinear/nonlinear.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "linearsolver.hpp" + +class NonLinearSolver { + +private: + + // NonLinearSolver(); + +public: + + static void analyze(ILinearSolver &linear_solver); + +}; diff --git a/src/parser/chc/CHCParseVisitor.cpp b/src/parser/chc/CHCParseVisitor.cpp index 8969d7016..bf5c57c19 100644 --- a/src/parser/chc/CHCParseVisitor.cpp +++ b/src/parser/chc/CHCParseVisitor.cpp @@ -20,7 +20,7 @@ using lit_type = Res; using assert_type = Clause; using query_type = Clause; using symbol_type = std::string; -using tail_type = std::pair; +using tail_type = std::pair, BoolExpr>; using head_type = FunApp; using var_or_atom_type = std::variant; using boolop_type = BoolOp; @@ -55,71 +55,23 @@ antlrcpp::Any CHCParseVisitor::visitMain(CHCParser::MainContext *ctx) { for (const auto &c: ctx->chc_query()) { clauses.push_back(any_cast(visit(c))); } + std::vector vars; - std::vector bvars; for (unsigned i = 0; i < max_int_arity; ++i) { vars.emplace_back(NumVar::nextProgVar()); } + its->numProgVars = vars; + + std::vector bvars; for (unsigned i = 0; i < max_bool_arity; ++i) { bvars.emplace_back(BoolVar::nextProgVar()); } + its->boolProgVars = bvars; + for (const Clause &c: clauses) { - Subs ren; - // replace the arguments of the body predicate with the corresponding program variables - unsigned bool_arg {0}; - unsigned int_arg {0}; - for (unsigned i = 0; i < c.lhs.args.size(); ++i) { - if (std::holds_alternative(c.lhs.args[i])) { - ren.put(std::get(c.lhs.args[i]), vars[int_arg]); - ++int_arg; - } else { - ren.put(std::get(c.lhs.args[i]), BExpression::buildTheoryLit(BoolLit(bvars[bool_arg]))); - ++bool_arg; - } - } - VarSet cVars; - for (const auto &var: c.rhs.args) { - cVars.insert(var); - } - c.guard->collectVars(cVars); - // replace all other variables from the clause with temporary variables - for (const auto &x: cVars) { - if (!ren.contains(x)) { - if (std::holds_alternative(x)) { - const auto &var = std::get(x); - ren.put(var, NumVar::next()); - } else if (std::holds_alternative(x)) { - const auto &var = std::get(x); - ren.put(var, BExpression::buildTheoryLit(BoolLit(BoolVar::next()))); - } else { - throw std::logic_error("unsupported theory in CHCParseVisitor"); - } - } - } - bool_arg = 0; - int_arg = 0; - Subs up; - for (unsigned i = 0; i < c.rhs.args.size(); ++i) { - if (std::holds_alternative(c.rhs.args[i])) { - up.put(vars[int_arg], ren.get(std::get(c.rhs.args[i]))); - ++int_arg; - } else if (std::holds_alternative(c.rhs.args[i])) { - up.put(bvars[bool_arg], ren.get(std::get(c.rhs.args[i]))); - ++bool_arg; - } else { - throw std::logic_error("unsupported theory in CHCParseVisitor"); - } - } - for (unsigned i = int_arg; i < max_int_arity; ++i) { - up.put(vars[i], NumVar::next()); - } - for (unsigned i = bool_arg; i < max_bool_arity; ++i) { - up.put(bvars[i], BExpression::buildTheoryLit(BoolLit(BoolVar::next()))); - } - up.put(NumVar::loc_var, c.rhs.loc); - const BoolExpr guard = c.guard->subs(ren)->simplify() & Rel::buildEq(NumVar::loc_var, c.lhs.loc); - its->addRule(Rule(guard, up), c.lhs.loc); + its->addClause(c); } + return its; } @@ -183,18 +135,18 @@ antlrcpp::Any CHCParseVisitor::visitChc_tail(CHCParser::Chc_tailContext *ctx) { guards.push_back(r.t); guards.insert(guards.end(), r.refinement.begin(), r.refinement.end()); } - std::optional lhs; + + std::set predicates; for (const auto &c: ctx->var_or_atom()) { const auto v = any_cast(visit(c)); if (std::holds_alternative(v)) { guards.push_back(BExpression::buildTheoryLit(BoolLit(std::get(v)))); - } else if (lhs) { - throw std::invalid_argument("non-linear clause " + ctx->getText()); } else { - lhs = std::get(v); + predicates.insert(std::get(v)); } } - return std::pair(lhs.value_or(FunApp(its->getInitialLocation(), {})), BExpression::buildAnd(guards)); + + return std::pair(predicates, BExpression::buildAnd(guards)); } antlrcpp::Any CHCParseVisitor::visitChc_query(CHCParser::Chc_queryContext *ctx) { diff --git a/src/parser/chc/CHCParseVisitor.h b/src/parser/chc/CHCParseVisitor.h index cfc0f12e0..bf65b23c4 100644 --- a/src/parser/chc/CHCParseVisitor.h +++ b/src/parser/chc/CHCParseVisitor.h @@ -50,24 +50,6 @@ enum Sort { Int, Bool }; -struct FunApp { - - LocationIdx loc; - std::vector args; - - FunApp(const LocationIdx loc, const std::vector args): loc(loc), args(args) {} - -}; - -struct Clause { - const FunApp lhs; - const FunApp rhs; - const BoolExpr guard; - - Clause(const FunApp &lhs, const FunApp &rhs, const BoolExpr &guard): lhs(lhs), rhs(rhs), guard(guard) {} - -}; - /** * This class provides an empty implementation of CHCVisitor, which can be * extended to create a visitor which only needs to handle a subset of the available methods. diff --git a/test_data_lialin.txt b/test_data_lialin.txt new file mode 100644 index 000000000..62bb8f19f --- /dev/null +++ b/test_data_lialin.txt @@ -0,0 +1,175 @@ +032 unsat +034 unsat +038 unsat +043 unsat +044 unsat +045 unsat +047 unsat +050 unsat +052 unsat +053 unsat +057 unsat +059 unsat +083 unsat +084 unsat +089 unsat +119 unsat +130 unsat +133 unsat +154 unsat +297 unsat +299 unsat +316 unsat +185 unsat +187 unsat +175 unsat +402 unsat +321 unsat +319 unsat +331 unsat +329 unsat +330 unsat +328 unsat +322 unsat +336 unsat +332 unsat +335 unsat +327 unsat +401 unsat +334 unsat +358 unsat +362 unsat +386 unsat +381 unsat +348 unsat +197 unsat +037 unsat +111 unsat +036 unknown +242 unknown +179 unknown +199 unknown +172 unknown +170 unknown +323 unknown +214 unknown +182 unknown +006 unknown +015 unknown +018 unknown +020 unknown +022 unknown +026 unknown +029 unknown +090 unknown +091 unknown +103 unknown +104 unknown +105 unknown +107 unknown +108 unknown +125 unknown +126 unknown +127 unknown +461 unknown +000 unknown +001 unknown +002 unknown +003 unknown +004 unknown +005 unknown +010 unknown +014 unknown +021 unknown +023 unknown +079 unknown +081 unknown +085 unknown +094 unknown +095 unknown +097 unknown +112 unknown +115 unknown +118 unknown +120 unknown +128 unknown +134 unknown +290 unknown +291 unknown +293 unknown +294 unknown +295 unknown +296 unknown +298 unknown +300 unknown +301 unknown +303 unknown +304 unknown +305 unknown +306 unknown +307 unknown +308 unknown +309 unknown +310 unknown +311 unknown +409 unknown +410 unknown +411 unknown +412 unknown +413 unknown +414 unknown +415 unknown +416 unknown +417 unknown +418 unknown +419 unknown +420 unknown +421 unknown +422 unknown +423 unknown +424 unknown +425 unknown +426 unknown +427 unknown +428 unknown +429 unknown +430 unknown +431 unknown +434 unknown +435 unknown +436 unknown +437 unknown +438 unknown +439 unknown +440 unknown +441 unknown +443 unknown +444 unknown +445 unknown +178 unknown +183 unknown +177 unknown +184 unknown +176 unknown +186 unknown +173 unknown +320 unknown +016 unknown +150 unknown +009 unknown +333 unknown +325 unknown +180 unknown +181 unknown +240 unknown +171 unknown +208 unknown +215 unknown +257 unknown +240 unknown +207 unknown +241 unknown +210 unknown +231 unknown +248 unknown +243 unknown From c049b4350d543301b9b282c2a3e574af64d8195a Mon Sep 17 00:00:00 2001 From: Florian Frohn Date: Thu, 28 Sep 2023 13:16:14 +0200 Subject: [PATCH 02/25] initialization --- src/lib/expr/subs.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/expr/subs.hpp b/src/lib/expr/subs.hpp index ea5e1c33d..ba0bc1662 100644 --- a/src/lib/expr/subs.hpp +++ b/src/lib/expr/subs.hpp @@ -428,7 +428,7 @@ class Subs { public: size_t hash() const { - size_t res; + size_t res {0}; hashImpl(res); return res; } From 1153ed5d089db06d7d2e4f66863d379ee4bfff8e Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 16 Oct 2023 14:29:19 +0200 Subject: [PATCH 03/25] work on non-linear solver 1) fix bug in clause resolution 2) Check that resolvent constraints are SAT. Otherwise don't pass them to the linear solver 3) Don't just do resolution with all combinations of facts and non-linear clauses but also keep doing resolution with the resulting clauses until we derived all possible linear clauses. This is more work per main loop iteration and seems to lead to more timeouts in the LIA benchmark so far. --- benchmarks/LIA_adcl.txt | 210 +++++++++++++++++----------------- src/analysis/reachability.cpp | 1 - src/its/itsproblem.cpp | 2 +- src/nonlinear/clause.cpp | 6 +- src/nonlinear/nonlinear.cpp | 49 ++++---- 5 files changed, 139 insertions(+), 129 deletions(-) diff --git a/benchmarks/LIA_adcl.txt b/benchmarks/LIA_adcl.txt index 22e922bc4..9ff077118 100644 --- a/benchmarks/LIA_adcl.txt +++ b/benchmarks/LIA_adcl.txt @@ -59,46 +59,46 @@ 058 timeout 059 timeout 060 unsat -061 timeout -062 unsat +061 unknown +062 unknown 063 unknown -064 timeout +064 unknown 065 timeout -066 timeout +066 unknown 067 unsat -068 unknown +068 timeout 069 unknown -070 unknown +070 timeout 071 unknown 072 unsat -073 unknown +073 timeout 074 unknown 075 unsat -076 unknown +076 timeout 077 unknown 078 unknown 079 unknown -080 unknown +080 timeout 081 unknown 082 timeout 083 unknown -084 unknown -085 unknown +084 timeout +085 timeout 086 unknown 087 timeout -088 unknown +088 timeout 089 unknown 090 unknown 091 unknown 092 unknown 093 unknown -094 unsat +094 timeout 095 unknown 096 unsat 097 unsat 098 unknown 099 unknown -100 unknown +100 timeout 101 unknown 102 unknown 103 unsat @@ -113,93 +113,93 @@ 112 timeout 113 unsat 114 unsat -115 unknown +115 timeout 116 timeout -117 unknown +117 timeout 118 timeout 119 timeout 120 timeout -121 unknown +121 timeout 122 timeout -123 unknown +123 timeout 124 timeout -125 unknown +125 timeout 126 timeout -127 unknown +127 timeout 128 timeout 129 timeout -130 unknown +130 timeout 131 timeout -132 unknown -133 unknown -134 unknown -135 unknown -136 unknown +132 timeout +133 timeout +134 timeout +135 timeout +136 timeout 137 unknown -138 unknown -139 unknown +138 timeout +139 timeout 140 unknown 141 unknown -142 unknown -143 unknown +142 unsat +143 timeout 144 unknown 145 timeout -146 unknown -147 unknown -148 unknown -149 unknown -150 unknown +146 timeout +147 timeout +148 timeout +149 timeout +150 timeout 151 timeout -152 unknown +152 timeout 153 timeout 154 timeout 155 timeout 156 timeout -157 unknown +157 timeout 158 timeout -159 unknown +159 timeout 160 timeout 161 timeout 162 timeout -163 unknown +163 timeout 164 timeout -165 unknown -166 unknown +165 timeout +166 timeout 167 timeout -168 unknown +168 timeout 169 timeout 170 timeout 171 timeout -172 unknown -173 unknown +172 timeout +173 timeout 174 timeout 175 timeout -176 unknown +176 timeout 177 timeout -178 unknown +178 timeout 179 timeout -180 unknown +180 timeout 181 timeout -182 unknown +182 timeout 183 timeout -184 unknown +184 timeout 185 timeout 186 timeout -187 unknown +187 timeout 188 timeout 189 timeout -190 unknown -191 unknown -192 timeout -193 timeout -194 timeout -195 timeout -196 timeout +190 timeout +191 timeout +192 unknown +193 unknown +194 unknown +195 unknown +196 unknown 197 unknown 198 unsat 199 unsat 200 timeout -201 unknown +201 timeout 202 unknown 203 unsat 204 unknown @@ -214,35 +214,35 @@ 213 timeout 214 timeout 215 unknown -216 unknown +216 timeout 217 unknown 218 timeout 219 timeout 220 timeout -221 unknown -222 unknown -223 unknown -224 unknown +221 timeout +222 timeout +223 timeout +224 timeout 225 timeout 226 timeout 227 timeout 228 unknown 229 timeout 230 unknown -231 unknown +231 timeout 232 unknown 233 timeout 234 unknown 235 unknown 236 unknown -237 unknown +237 timeout 238 timeout 239 unknown 240 unknown 241 unknown 242 unknown 243 unknown -244 unknown +244 timeout 245 unknown 246 unknown 247 unknown @@ -265,80 +265,80 @@ 264 unknown 265 unknown 266 timeout -267 unknown +267 timeout 268 unknown 269 unknown 270 unknown 271 unknown 272 unknown 273 unknown -274 unknown -275 unknown +274 timeout +275 timeout 276 unknown 277 unknown 278 unknown 279 unknown 280 unknown 281 unknown -282 unknown +282 timeout 283 unknown 284 timeout -285 unknown -286 unknown -287 unknown -288 unknown +285 timeout +286 timeout +287 timeout +288 timeout 289 unknown -290 unknown -291 unknown -292 unknown -293 unknown -294 unknown -295 unknown -296 unknown -297 unknown +290 timeout +291 timeout +292 timeout +293 timeout +294 timeout +295 timeout +296 timeout +297 timeout 298 unknown -299 unknown +299 timeout 300 unknown 301 unknown 302 unknown -303 unknown -304 unknown +303 timeout +304 timeout 305 unknown -306 unknown +306 timeout 307 unknown -308 unknown +308 timeout 309 unknown -310 unknown -311 unknown +310 timeout +311 timeout 312 unknown -313 unknown +313 timeout 314 unknown 315 unknown 316 unknown 317 unknown 318 unknown 319 unknown -320 unknown +320 timeout 321 unknown -322 unknown -323 unknown -324 unknown -325 unknown +322 timeout +323 timeout +324 timeout +325 timeout 326 unknown -327 unknown +327 timeout 328 unknown -329 unknown -330 unknown -331 unknown -332 unknown +329 timeout +330 timeout +331 timeout +332 timeout 333 unknown 334 unknown -335 unknown +335 timeout 336 unsat 337 timeout 338 unsat 339 unsat -340 timeout +340 unknown 341 timeout 342 unknown 343 timeout @@ -348,16 +348,16 @@ 347 timeout 348 timeout 349 unsat -350 timeout +350 unsat 351 timeout 352 timeout 353 unknown 354 unknown -355 timeout +355 unknown 356 timeout 357 unknown 358 unknown -359 timeout +359 unknown 360 timeout 361 unknown 362 unknown diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index b1a82df12..455297309 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -862,7 +862,6 @@ const std::list Reachability::derive_new_facts() { } } - ////////////////////////////////////// TODO: refactor this /////////////////////////////////////////////////////////// if (incremental_mode && should_add_fact) { // Using a crude (additional) redundancy criterion for facts here. We identify facts by the trace that lead to them. // This is only sufficient because equivalent facts can have multiple traces. We don't need to memoize the entire diff --git a/src/its/itsproblem.cpp b/src/its/itsproblem.cpp index 6b76f38c7..656bb32b2 100644 --- a/src/its/itsproblem.cpp +++ b/src/its/itsproblem.cpp @@ -400,7 +400,7 @@ void ITSProblem::addClause(const Clause &c) { nonLinearCHCs.push_back(Clause( lhs_normalized, normalizePredicate(numProgVars.size(), boolProgVars.size(), c.rhs), - c.guard + c.guard->simplify() )); } } diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index 5e662f756..8304608e5 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -180,17 +180,19 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA return {}; } - const Clause this_unified = this->renameWith(unifier.value()); + const Clause this_unified = this_with_disjoint_vars.renameWith(unifier.value()); // LHS of resolvent is the union of the renamed LHS of `this` ... std::set resolvent_lhs = this_unified.lhs; // ... and the LHS of `chc` where `pred` is removed. resolvent_lhs.insert(chc_lhs_without_pred.begin(), chc_lhs_without_pred.end()); + const auto new_guard = this_unified.guard & chc.guard; + return Clause( resolvent_lhs, chc.rhs, - this_unified.guard & chc.guard + new_guard->simplify() ); } diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 14de380aa..397df962b 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -1,33 +1,46 @@ #include "nonlinear.hpp" #include "booltheory.hpp" +#include "linearizingsolver.hpp" #include "linearsolver.hpp" +#include "smt.hpp" #include void NonLinearSolver::analyze(ILinearSolver &linear_solver) { - std::stack> facts(linear_solver.get_initial_facts()); + std::unique_ptr> smt(new LinearizingSolver(4294967295u)); + smt->enableModels(); + + // For the first loop iteration, the list of facts is composed of the original facts + // given in the CHC problem. In subsequent iterations, the facts are whatever + // the linear solver managed to derive. + std::list facts(linear_solver.get_initial_facts()); while (true) { - // std::cout << "================" << std::endl; std::list resolvents; + std::list non_linear_chcs = linear_solver.get_non_linear_chcs(); - // Do resolution with of all `facts` with all non-linear clauses with the goal to derive - // new linear clauses that the linear solver can handle. - while (!facts.empty()) { - const Clause fact = facts.top(); - facts.pop(); - - // Note, we don't add resolvents to the ITS during this loop but instead collect on a stack, - // and then add the contained clauses to the ITS afterwards in a dedicated loop. - // Otherwise, the following for-loop is "dynamically extended" because we also loop - // over the newly added clauses. - for (const Clause &non_linear_chc: linear_solver.get_non_linear_chcs()) { + // Do resolution with all combinations of facts and non-linear clauses to + // get every possible linear clause that we can derive in this iteration. + for (const Clause &non_linear_chc: non_linear_chcs) { + for (const auto &fact: facts) { for (const auto &pred: non_linear_chc.lhs) { const auto optional_resolvent = fact.resolutionWith(non_linear_chc, pred); if (optional_resolvent.has_value()) { const auto resolvent = optional_resolvent.value(); // TODO: check for redundancy - resolvents.push_back(resolvent); + + // TODO: Is SMT a singleton? Does this reset influence the linear solver? + // Filter out resolvents, where guard is UNSAT + smt->resetSolver(); + smt->add(resolvent.guard); + if (smt->check() != Unsat) { + resolvents.push_back(resolvent); + if (!resolvent.isLinear()) { + // Note that we append items to `non_linear_chcs` while iterating over it. + // That means we also iterate over all added items. + non_linear_chcs.push_back(resolvent); + } + } } } } @@ -35,13 +48,9 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { linear_solver.add_clauses(resolvents); + facts.clear(); const auto new_facts = linear_solver.derive_new_facts(); - for (const auto &fact : new_facts) { - // std::cout << fact << std::endl; - facts.push(fact); - } - - // std::cout << "facts: " << new_facts.size() << std::endl; + facts.insert(facts.end(), new_facts.begin(), new_facts.end()); const auto result = linear_solver.get_analysis_result(); if (result == LinearSolver::Result::Unsat) { From de92b29fe0b4bd759b47cb92b84821c1601b6c46 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Thu, 19 Oct 2023 12:11:12 +0200 Subject: [PATCH 04/25] non-linear solver: fix several issues 1) Previously we distinguished learned- from original clauses just by their integer ID, since all learned clauses had a larger ID then the last original clause. Now that we add original clauses incrementally, this doesn't work anymore. Thus, we store the set of IDs of learned clauses in a dedicated set. 2) Don't call `init()` in `add_clauses()`. The program variables don't change even in incremental mode, since they are fixed from the start. However, `init()` is never called if we don't add linear clauses in the first round of resolution in the non-linear solver. Also we need to call `init()` after preprocessing because it leads to variable renaming. Thus we now do preprocessing+init in the constructor of `Reachability`. 3) In the following situtation we got UNKNOWN instaed of UNSAT: One round of resolution in the non-linear solver produced a set of linear clauses. Then processing simplified this to trivially UNSAT clauses, such as true ==> false Then we call `derive_new_clauses()` but the main loop exits with UNKNOWN because we cant do a Step with `true ==> false` on the trace. Thus, we need to call `try_to_finish()` at the very start of `derive_new_clauses()`. --- benchmarks/LIA_adcl.txt | 142 +++++++++++++++++----------------- src/analysis/reachability.cpp | 47 ++++++----- src/analysis/reachability.hpp | 2 + src/config.cpp | 3 +- src/its/itsproblem.cpp | 38 ++++++++- src/nonlinear/nonlinear.cpp | 14 ++-- 6 files changed, 142 insertions(+), 104 deletions(-) diff --git a/benchmarks/LIA_adcl.txt b/benchmarks/LIA_adcl.txt index 9ff077118..574d4a021 100644 --- a/benchmarks/LIA_adcl.txt +++ b/benchmarks/LIA_adcl.txt @@ -41,13 +41,13 @@ 040 timeout 041 timeout 042 timeout -043 timeout +043 unknown 044 timeout 045 timeout 046 timeout 047 timeout 048 timeout -049 timeout +049 unknown 050 timeout 051 timeout 052 timeout @@ -59,9 +59,9 @@ 058 timeout 059 timeout 060 unsat -061 unknown -062 unknown -063 unknown +061 timeout +062 unsat +063 unsat 064 unknown 065 timeout 066 unknown @@ -101,18 +101,18 @@ 100 timeout 101 unknown 102 unknown -103 unsat -104 unsat +103 unknown +104 unknown 105 unsat -106 unsat +106 timeout 107 timeout 108 unknown -109 unsat -110 unsat -111 unsat +109 timeout +110 timeout +111 timeout 112 timeout -113 unsat -114 unsat +113 timeout +114 timeout 115 timeout 116 timeout 117 timeout @@ -130,19 +130,19 @@ 129 timeout 130 timeout 131 timeout -132 timeout +132 unknown 133 timeout 134 timeout 135 timeout 136 timeout -137 unknown +137 unsat 138 timeout 139 timeout -140 unknown -141 unknown +140 timeout +141 unsat 142 unsat 143 timeout -144 unknown +144 timeout 145 timeout 146 timeout 147 timeout @@ -190,11 +190,11 @@ 189 timeout 190 timeout 191 timeout -192 unknown -193 unknown -194 unknown -195 unknown -196 unknown +192 timeout +193 timeout +194 timeout +195 timeout +196 timeout 197 unknown 198 unsat 199 unsat @@ -204,7 +204,7 @@ 203 unsat 204 unknown 205 unknown -206 timeout +206 unknown 207 timeout 208 timeout 209 timeout @@ -215,7 +215,7 @@ 214 timeout 215 unknown 216 timeout -217 unknown +217 timeout 218 timeout 219 timeout 220 timeout @@ -225,30 +225,30 @@ 224 timeout 225 timeout 226 timeout -227 timeout -228 unknown -229 timeout -230 unknown +227 unknown +228 timeout +229 unknown +230 timeout 231 timeout 232 unknown 233 timeout -234 unknown -235 unknown -236 unknown -237 timeout +234 timeout +235 unsat +236 unsat +237 unknown 238 timeout -239 unknown +239 unsat 240 unknown -241 unknown -242 unknown -243 unknown -244 timeout +241 unsat +242 unsat +243 unsat +244 unsat 245 unknown 246 unknown 247 unknown 248 unknown 249 unknown -250 unknown +250 error 251 unknown 252 unknown 253 unknown @@ -259,35 +259,35 @@ 258 unknown 259 unknown 260 unknown -261 unknown -262 unknown -263 unknown -264 unknown -265 unknown +261 error +262 error +263 error +264 timeout +265 error 266 timeout 267 timeout 268 unknown 269 unknown 270 unknown -271 unknown +271 timeout 272 unknown 273 unknown 274 timeout 275 timeout 276 unknown -277 unknown +277 timeout 278 unknown 279 unknown 280 unknown 281 unknown 282 timeout -283 unknown +283 timeout 284 timeout 285 timeout 286 timeout 287 timeout 288 timeout -289 unknown +289 timeout 290 timeout 291 timeout 292 timeout @@ -296,43 +296,43 @@ 295 timeout 296 timeout 297 timeout -298 unknown +298 timeout 299 timeout -300 unknown -301 unknown +300 timeout +301 timeout 302 unknown 303 timeout 304 timeout -305 unknown +305 timeout 306 timeout -307 unknown +307 timeout 308 timeout -309 unknown +309 timeout 310 timeout 311 timeout -312 unknown +312 timeout 313 timeout -314 unknown -315 unknown -316 unknown -317 unknown +314 timeout +315 error +316 timeout +317 timeout 318 unknown -319 unknown +319 timeout 320 timeout -321 unknown +321 timeout 322 timeout 323 timeout 324 timeout 325 timeout -326 unknown +326 timeout 327 timeout 328 unknown 329 timeout 330 timeout 331 timeout 332 timeout -333 unknown -334 unknown +333 timeout +334 timeout 335 timeout 336 unsat 337 timeout @@ -340,19 +340,19 @@ 339 unsat 340 unknown 341 timeout -342 unknown +342 timeout 343 timeout 344 timeout 345 unknown 346 timeout 347 timeout 348 timeout -349 unsat +349 error 350 unsat 351 timeout 352 timeout -353 unknown -354 unknown +353 timeout +354 timeout 355 unknown 356 timeout 357 unknown @@ -360,7 +360,7 @@ 359 unknown 360 timeout 361 unknown -362 unknown +362 timeout 363 timeout 364 timeout 365 timeout @@ -404,7 +404,7 @@ 403 unknown 404 unknown 405 unknown -406 unknown +406 timeout 407 timeout 408 timeout 409 timeout @@ -453,4 +453,4 @@ 452 timeout 453 timeout 454 timeout -455 timeout +455 timeout \ No newline at end of file diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index 455297309..1aef2f55f 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -126,6 +126,18 @@ Reachability::Reachability(ITSProblem &chcs, bool incremental_mode): std::cout << "Initial ITS" << std::endl; ITSExport::printForProof(chcs, std::cout); } + + chcs.refineDependencyGraph(); + // TODO: are other preprocessing steps also unsound when in non-linear context? + const auto res {Preprocess::preprocess(chcs)}; + if (res) { + proof.concat(res.getProof()); + if (Config::Analysis::log) { + std::cout << "Simplified ITS" << std::endl; + ITSExport::printForProof(chcs, std::cout); + } + } + init(); } Step::Step(const TransIdx transition, const BoolExpr &sat, const Subs &var_renaming, const Rule &resolvent): @@ -330,6 +342,7 @@ void Reachability::print_state() { proof.storeSubProof(state_p); } +// TODO: do we need this function? ITS also has the function `getProgVars` now. void Reachability::init() { srand(42); for (const auto &x: chcs.getVars()) { @@ -337,11 +350,9 @@ void Reachability::init() { prog_vars.insert(x); } } - for (const auto &r: chcs.getAllTransitions()) { - last_orig_clause = std::max(last_orig_clause, r.getId()); - } } + void Reachability::luby_next() { const auto [u,v] {luby}; luby = (u & -u) == v ? std::pair(u+1, 1) : std::pair(u, 2 * v); @@ -426,7 +437,9 @@ std::optional Reachability::resolve(const TransIdx idx) { } else { block->second.clear(); } - if (Config::Analysis::log) std::cout << "could not find a model for " << idx << std::endl; + if (Config::Analysis::log) { + std::cout << "could not find a model for " << idx << std::endl; + } } } return {}; @@ -467,15 +480,16 @@ TransIdx Reachability::add_learned_clause(const Rule &accel, const unsigned back const auto fst = trace.at(backlink).clause_idx; const auto last = trace.back().clause_idx; const auto loop_idx = chcs.addLearnedRule(accel, fst, last); + learned_clause_ids.insert(loop_idx->getId()); return loop_idx; } bool Reachability::is_learned_clause(const TransIdx idx) const { - return idx->getId() > last_orig_clause; + return learned_clause_ids.contains(idx->getId()); } bool Reachability::is_orig_clause(const TransIdx idx) const { - return idx->getId() <= last_orig_clause; + return !is_learned_clause(idx); } Result Reachability::instantiate(const NumVar &n, const Rule &rule) const { @@ -726,17 +740,6 @@ LinearSolver::Result Reachability::get_analysis_result() const { } void Reachability::analyze() { - // TODO: are other preprocessing steps also unsound when in non-linear context? - const auto res {Preprocess::preprocess(chcs)}; - if (res) { - proof.concat(res.getProof()); - if (Config::Analysis::log) { - std::cout << "Simplified ITS" << std::endl; - ITSExport::printForProof(chcs, std::cout); - } - } - init(); - if (!try_to_finish()) { blocked_clauses[0].clear(); derive_new_facts(); @@ -776,6 +779,7 @@ const std::optional Reachability::trace_as_fact() { } else { const Step step = trace.back(); + // TODO: this has quite some duplication with `ITS::clauseFrom` std::vector guard_conj = { step.resolvent.getGuard() }; std::vector args_renamed; const auto subs = step.resolvent.getUpdate(); @@ -805,10 +809,14 @@ const std::optional Reachability::trace_as_fact() { } const std::list Reachability::derive_new_facts() { - static std::default_random_engine rnd {}; + static std::default_random_engine rnd {}; std::list derived_facts; + if (try_to_finish()) { + return derived_facts; + } + while (true) { size_t next_restart = luby_unit * luby.second; std::unique_ptr state; @@ -862,7 +870,7 @@ const std::list Reachability::derive_new_facts() { } } - if (incremental_mode && should_add_fact) { + if (incremental_mode && should_add_fact) { // TODO: when trace not empty // Using a crude (additional) redundancy criterion for facts here. We identify facts by the trace that lead to them. // This is only sufficient because equivalent facts can have multiple traces. We don't need to memoize the entire // trace structure. It's enough to store clause_idx/implicant for each trace step. @@ -952,7 +960,6 @@ void Reachability::add_clauses(const std::list &clauses) { ITSExport::printForProof(chcs, std::cout); } } - init(); // When we add a new linear clause we have to restart the solver, // i.e. reset the trace, otherwise we might block a potential reachability path. diff --git a/src/analysis/reachability.hpp b/src/analysis/reachability.hpp index 681c34817..4fe6b9107 100644 --- a/src/analysis/reachability.hpp +++ b/src/analysis/reachability.hpp @@ -311,6 +311,8 @@ class Reachability : public ILinearSolver { const std::optional trace_as_fact(); + std::set learned_clause_ids; + public: Reachability(ITSProblem &chcs, bool incremental_mode); diff --git a/src/config.cpp b/src/config.cpp index a5c461fc8..30add4b87 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -34,7 +34,8 @@ namespace Config { namespace Output { // Whether to enable colors in the proof output bool Colors {true}; - bool PrintDependencyGraph {false}; + // bool PrintDependencyGraph {false}; + bool PrintDependencyGraph {true}; } namespace Input { diff --git a/src/its/itsproblem.cpp b/src/its/itsproblem.cpp index 656bb32b2..68b48e62e 100644 --- a/src/its/itsproblem.cpp +++ b/src/its/itsproblem.cpp @@ -272,20 +272,50 @@ const std::vector ITSProblem::getProgVars() const { return prog_vars; } + /** - * Converts an ITS rule (identified by a TransIdx) back to CHC representation. + * Converts an ITS rule (identified by a TransIdx) back to Clause representation. * This does not restore the original representation after parsing perfectly, * since number and order of predicate arguments is lost. */ const Clause ITSProblem::clauseFrom(TransIdx rule) const { const auto prog_vars = getProgVars(); - const auto guard = rule->getGuard(); - const LocationIdx lhs_loc = getLhsLoc(rule); const LocationIdx rhs_loc = getRhsLoc(rule); - const auto rhs = FunApp(rhs_loc, prog_vars).renameWith(rule->getUpdate()); + const auto update = rule->getUpdate(); + + // `update` might map vars to non-var compound expressions or literals. + // The `Clause` representation only allows variable arguments though for the + // RHS predicate. So we extract those expressions and put them back into the + // clause guard. This is a bit ad-hoc and also un-does work of the linear + // solver and the preprocessing, so it might be worth optimizing later. + std::vector guard_conj = { rule->getGuard() }; + std::vector rhs_args; + for (const Var &var : getProgVars()) { + auto it = update.find(var); + + if (it == update.end()) { + // If `var` is not contained in `update` it's implicitly mapped to itself, + // i.e. `update` does not change it's value. Thus, we add `var` directly + // into `rhs_args`. + rhs_args.push_back(var); + } else { + const auto optional_var = expr::toVar(expr::second(*it)); + + if (optional_var.has_value()) { + rhs_args.push_back(optional_var.value()); + } else { + const auto new_var = expr::next(var); + rhs_args.push_back(new_var); + guard_conj.push_back(expr::mkEq(expr::toExpr(new_var), update.get(var))); + } + } + } + + const auto guard = BExpression::buildAnd(guard_conj); + const auto rhs = FunApp(rhs_loc, rhs_args); if (lhs_loc == getInitialLocation()) { // rule is a linear CHC with no LHS predicates, ie a "fact" diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 397df962b..38f3f3a17 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -3,11 +3,11 @@ #include "linearizingsolver.hpp" #include "linearsolver.hpp" #include "smt.hpp" +#include "smtfactory.hpp" #include void NonLinearSolver::analyze(ILinearSolver &linear_solver) { - std::unique_ptr> smt(new LinearizingSolver(4294967295u)); - smt->enableModels(); + LinearizingSolver smt(4294967295u); // For the first loop iteration, the list of facts is composed of the original facts // given in the CHC problem. In subsequent iterations, the facts are whatever @@ -27,19 +27,17 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { if (optional_resolvent.has_value()) { const auto resolvent = optional_resolvent.value(); + // TODO: check for redundancy - // TODO: Is SMT a singleton? Does this reset influence the linear solver? - // Filter out resolvents, where guard is UNSAT - smt->resetSolver(); - smt->add(resolvent.guard); - if (smt->check() != Unsat) { + // maybe later dont reject resolvent on `Unknown`? + if (SmtFactory::check(resolvent.guard) == Sat) { resolvents.push_back(resolvent); if (!resolvent.isLinear()) { // Note that we append items to `non_linear_chcs` while iterating over it. // That means we also iterate over all added items. non_linear_chcs.push_back(resolvent); - } + } } } } From e056f02e3ad742035a0ace6be9ee42b2fb1b5b4f Mon Sep 17 00:00:00 2001 From: Florian Frohn Date: Thu, 5 Oct 2023 13:54:22 +0200 Subject: [PATCH 05/25] fix for replaceNode --- src/its/dependencygraph.hpp | 39 ++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/its/dependencygraph.hpp b/src/its/dependencygraph.hpp index 974ccae56..f895461a7 100644 --- a/src/its/dependencygraph.hpp +++ b/src/its/dependencygraph.hpp @@ -85,21 +85,38 @@ class DependencyGraph { void replaceNode(Node to_replace, Node replacement) { nodes.insert(replacement); nodes.erase(to_replace); + // all predecessors of to_replace get replacement as successor for (const auto &pred: predecessors.at(to_replace)) { - successors.at(pred).erase(to_replace); - successors.at(pred).insert(replacement); + auto it {successors.find(pred)}; + it->second.erase(to_replace); + it->second.insert(replacement); } - for (const auto &pred: successors.at(to_replace)) { - if (pred == replacement) { - predecessors.at(to_replace).erase(to_replace); - predecessors.at(to_replace).insert(replacement); - } else { - predecessors.at(pred).erase(to_replace); - predecessors.at(pred).insert(replacement); + for (const auto &succ: successors.at(to_replace)) { + auto it {predecessors.find(succ)}; + // all former successors of to_replace get replacement as predecessor + if (it != predecessors.end()) { + it->second.erase(to_replace); + it->second.insert(replacement); + } + // to_replace has a self-loop -- redirect it to replacement + // we'll copy the predecessors of to_replace to replacement later + if (succ == replacement) { + auto it {predecessors.find(to_replace)}; + it->second.erase(to_replace); + it->second.insert(replacement); } } - predecessors.emplace(replacement, predecessors.at(to_replace)); - successors.emplace(replacement, successors.at(to_replace)); + auto [it, new_node] {predecessors.emplace(replacement, predecessors.at(to_replace))}; + if (!new_node) { + // replacement is an old node -- add all predecessors of to_replace + const auto &to_insert {predecessors.at(to_replace)}; + it->second.insert(to_insert.begin(), to_insert.end()); + } + it = successors.emplace(replacement, successors.at(to_replace)).first; + if (!new_node) { + const auto &to_insert {successors.at(to_replace)}; + it->second.insert(to_insert.begin(), to_insert.end()); + } predecessors.erase(to_replace); successors.erase(to_replace); } From ddbdd50e6ddd6ba8675a02ecf79ca93413971f1b Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Thu, 19 Oct 2023 16:47:59 +0200 Subject: [PATCH 06/25] LIA benchmark results after replaceNode fix --- benchmarks/LIA_adcl.txt | 17 +++++++-------- run_benchmark.sh | 46 ++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/benchmarks/LIA_adcl.txt b/benchmarks/LIA_adcl.txt index 574d4a021..01eb9f810 100644 --- a/benchmarks/LIA_adcl.txt +++ b/benchmarks/LIA_adcl.txt @@ -62,7 +62,7 @@ 061 timeout 062 unsat 063 unsat -064 unknown +064 timeout 065 timeout 066 unknown 067 unsat @@ -248,7 +248,7 @@ 247 unknown 248 unknown 249 unknown -250 error +250 unknown 251 unknown 252 unknown 253 unknown @@ -259,11 +259,11 @@ 258 unknown 259 unknown 260 unknown -261 error -262 error -263 error +261 unknown +262 unknown +263 unknown 264 timeout -265 error +265 unknown 266 timeout 267 timeout 268 unknown @@ -313,7 +313,7 @@ 312 timeout 313 timeout 314 timeout -315 error +315 unknown 316 timeout 317 timeout 318 unknown @@ -347,7 +347,7 @@ 346 timeout 347 timeout 348 timeout -349 error +349 unsat 350 unsat 351 timeout 352 timeout @@ -453,4 +453,3 @@ 452 timeout 453 timeout 454 timeout -455 timeout \ No newline at end of file diff --git a/run_benchmark.sh b/run_benchmark.sh index b3c356b50..e6df8f1b2 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -7,31 +7,27 @@ set -e pushd $(dirname ${BASH_SOURCE[0]}) cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -cmake --build build - -# debug: 009 - -# pushd build -# make -j4 -# popd - +# cmake --build build +pushd build +make -j4 +popd -# gdb --args \ -# ./build/loat-static \ -# --mode reachability \ -# --format horn \ -# --proof-level 0 \ -# "../chc-comp22-benchmarks/LIA/chc-LIA_060.smt2" -# # # # # "../chc-comp22-benchmarks/LIA/chc-LIA_999.smt2" -# # # # # --log \ +# # debug: 009 +# # gdb --args \ +# ./build/loat-static \ +# --mode reachability \ +# --format horn \ +# --proof-level 0 \ +# --log \ +# "../chc-comp22-benchmarks/LIA/chc-LIA_265.smt2" # popd # exit +########################################################################## + benchmark="LIA" -# benchmark="LIA" -# compare_with="z3" -compare_with="z3" +compare_with="adcl" while IFS= read -r line do @@ -42,11 +38,11 @@ do read idx prev_result <<< "$line" file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" - # if [[ "$prev_result" != "timeout" ]]; then - + # if true; then + if [[ "$prev_result" != "timeout" ]]; then set +e result=$(timeout 5 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") - # result=$(timeout 10 z3 "$file") + # result=$(timeout 5 z3 "$file") exit_status=$? set -e @@ -64,8 +60,12 @@ do else printf "$idx $prev_result --> $result \n" fi + else + # if we skip an instance nevertheless include it in the output + # so the log can easily be copied and saved as a whole + printf "$idx $prev_result \n" + fi - #fi fi done < "./benchmarks/${benchmark}_${compare_with}.txt" From f2b8163d0b0c3dd3d3ab0394aa50a53d8d1558a7 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Sun, 29 Oct 2023 18:53:13 +0100 Subject: [PATCH 07/25] non-linear: disable linear path chaining The preprocessing step of chaining linear paths is problematic in incremental mode, because we remove the chained rules/facts, which cuts-off paths that might be reachable via a non-linear rule. For example, consider the CHC problem: fib(1,1) fib(2,1) fib(a,c) /\ fib(b,d) /\ b=a+1 ==> f(b+1,c+d) fib(5,5) ==> false The fact `fib(5,5)` is reachable so the problem should be UNSAT. However, when we compute the resolvents of the non-linear clause with all facts we get: fib(0,c) ==> fib(2,c+1) (R1) fib(1,c) ==> fib(3,c+1) (R2) fib(2,d) ==> fib(3,d+1) (R3) fib(3,y) ==> fib(4,d+1) (R4) If we now chain linear paths we get: [fib(1,1), R2] = fib(3,2) [fib(2,1), R3] = fib(3,2) [fib(3,2), R4] = fib(4,3) At this point we remove all facts except `fib(4,3)` because in the linear context we can assume that all paths from these facts lead to `fib(4,3)` anyway. However in the non-linear context we need to keep `fib(3,2)` to derive `fib(5,5)`. After disabling linear path chaining we can prove UNSAT of 13 new problem instances in the LIA benchmark that previously gave UNKNOWN. However, there is also a regression. For the following instances we now get a timeout although we previously got UNSAT: 137,141,336 QUESTION: We could add the chained rules to the ITS without removing the rules that are part of the chain. However, that's potentially a lot of rules and is this really cheaper re-generating these rules during search? Maybe we can just add the maximal paths instead of every prefix of every path. --- benchmarks/LIA_adcl.txt | 170 ++++++++++++++++----------------- src/analysis/preprocessing.cpp | 60 +++++++++--- src/analysis/reachability.cpp | 9 +- src/config.cpp | 1 - src/nonlinear/nonlinear.cpp | 25 ++++- 5 files changed, 161 insertions(+), 104 deletions(-) diff --git a/benchmarks/LIA_adcl.txt b/benchmarks/LIA_adcl.txt index 01eb9f810..60bc6d09c 100644 --- a/benchmarks/LIA_adcl.txt +++ b/benchmarks/LIA_adcl.txt @@ -41,13 +41,13 @@ 040 timeout 041 timeout 042 timeout -043 unknown +043 timeout 044 timeout 045 timeout 046 timeout 047 timeout 048 timeout -049 unknown +049 timeout 050 timeout 051 timeout 052 timeout @@ -64,49 +64,49 @@ 063 unsat 064 timeout 065 timeout -066 unknown +066 unsat 067 unsat 068 timeout -069 unknown +069 timeout 070 timeout 071 unknown 072 unsat 073 timeout -074 unknown +074 timeout 075 unsat 076 timeout -077 unknown -078 unknown -079 unknown +077 timeout +078 timeout +079 timeout 080 timeout -081 unknown +081 timeout 082 timeout -083 unknown +083 timeout 084 timeout 085 timeout -086 unknown +086 timeout 087 timeout 088 timeout -089 unknown -090 unknown -091 unknown -092 unknown -093 unknown +089 timeout +090 timeout +091 timeout +092 unsat +093 timeout 094 timeout -095 unknown +095 timeout 096 unsat 097 unsat -098 unknown -099 unknown +098 timeout +099 timeout 100 timeout -101 unknown -102 unknown -103 unknown -104 unknown +101 timeout +102 timeout +103 unsat +104 unsat 105 unsat 106 timeout 107 timeout -108 unknown +108 unsat 109 timeout 110 timeout 111 timeout @@ -130,16 +130,16 @@ 129 timeout 130 timeout 131 timeout -132 unknown +132 timeout 133 timeout 134 timeout 135 timeout 136 timeout -137 unsat +137 timeout 138 timeout 139 timeout 140 timeout -141 unsat +141 timeout 142 unsat 143 timeout 144 timeout @@ -195,16 +195,16 @@ 194 timeout 195 timeout 196 timeout -197 unknown +197 timeout 198 unsat 199 unsat 200 timeout 201 timeout 202 unknown 203 unsat -204 unknown -205 unknown -206 unknown +204 timeout +205 timeout +206 timeout 207 timeout 208 timeout 209 timeout @@ -213,7 +213,7 @@ 212 timeout 213 timeout 214 timeout -215 unknown +215 timeout 216 timeout 217 timeout 218 timeout @@ -225,61 +225,61 @@ 224 timeout 225 timeout 226 timeout -227 unknown +227 timeout 228 timeout -229 unknown +229 timeout 230 timeout 231 timeout -232 unknown +232 timeout 233 timeout 234 timeout 235 unsat 236 unsat -237 unknown +237 timeout 238 timeout 239 unsat -240 unknown +240 unsat 241 unsat 242 unsat 243 unsat 244 unsat -245 unknown -246 unknown -247 unknown -248 unknown -249 unknown -250 unknown -251 unknown -252 unknown -253 unknown -254 unknown -255 unknown -256 unknown -257 unknown +245 timeout +246 timeout +247 timeout +248 timeout +249 unsat +250 timeout +251 timeout +252 timeout +253 timeout +254 timeout +255 timeout +256 timeout +257 timeout 258 unknown -259 unknown -260 unknown +259 timeout +260 timeout 261 unknown 262 unknown -263 unknown +263 timeout 264 timeout -265 unknown +265 timeout 266 timeout 267 timeout -268 unknown -269 unknown -270 unknown +268 timeout +269 timeout +270 timeout 271 timeout 272 unknown -273 unknown +273 timeout 274 timeout 275 timeout -276 unknown +276 timeout 277 timeout -278 unknown -279 unknown -280 unknown -281 unknown +278 timeout +279 timeout +280 timeout +281 timeout 282 timeout 283 timeout 284 timeout @@ -300,7 +300,7 @@ 299 timeout 300 timeout 301 timeout -302 unknown +302 timeout 303 timeout 304 timeout 305 timeout @@ -313,10 +313,10 @@ 312 timeout 313 timeout 314 timeout -315 unknown +315 timeout 316 timeout 317 timeout -318 unknown +318 timeout 319 timeout 320 timeout 321 timeout @@ -326,7 +326,7 @@ 325 timeout 326 timeout 327 timeout -328 unknown +328 timeout 329 timeout 330 timeout 331 timeout @@ -334,16 +334,16 @@ 333 timeout 334 timeout 335 timeout -336 unsat +336 timeout 337 timeout 338 unsat 339 unsat -340 unknown +340 timeout 341 timeout -342 timeout +342 error 343 timeout 344 timeout -345 unknown +345 timeout 346 timeout 347 timeout 348 timeout @@ -353,13 +353,13 @@ 352 timeout 353 timeout 354 timeout -355 unknown +355 unsat 356 timeout -357 unknown -358 unknown -359 unknown +357 timeout +358 timeout +359 unsat 360 timeout -361 unknown +361 timeout 362 timeout 363 timeout 364 timeout @@ -375,7 +375,7 @@ 374 timeout 375 timeout 376 timeout -377 unknown +377 timeout 378 timeout 379 timeout 380 timeout @@ -397,13 +397,13 @@ 396 timeout 397 timeout 398 timeout -399 unknown +399 timeout 400 timeout 401 timeout -402 unknown -403 unknown -404 unknown -405 unknown +402 timeout +403 unsat +404 timeout +405 timeout 406 timeout 407 timeout 408 timeout @@ -434,9 +434,9 @@ 433 timeout 434 timeout 435 timeout -436 unknown +436 unsat 437 timeout -438 unknown +438 unsat 439 timeout 440 timeout 441 timeout @@ -448,7 +448,7 @@ 447 timeout 448 timeout 449 timeout -450 unknown +450 timeout 451 timeout 452 timeout 453 timeout diff --git a/src/analysis/preprocessing.cpp b/src/analysis/preprocessing.cpp index a88e94b33..449659c5d 100644 --- a/src/analysis/preprocessing.cpp +++ b/src/analysis/preprocessing.cpp @@ -175,17 +175,55 @@ ResultViaSideEffects Preprocess::preprocess(ITSProblem &its) { res.succeed(); res.majorProofStep("Removed Irrelevant Clauses", sub_res.getProof(), its); } - } - if (Config::Analysis::log) { - std::cout << "chaining linear paths..." << std::endl; - } - sub_res = chainLinearPaths(its); - if (Config::Analysis::log) { - std::cout << "finished chaining linear paths" << std::endl; - } - if (sub_res) { - res.succeed(); - res.majorProofStep("Chained Linear Paths", sub_res.getProof(), its); + + /* + Chaining linear paths is also problematic in incremental mode, because + we remove the chained rules/facts, which cuts-off paths that might be + reachable via a non-linear rule. For example, consider the CHC problem: + + fib(1,1) + fib(2,1) + fib(a,c) /\ fib(b,d) /\ b=a+1 ==> f(b+1,c+d) + fib(5,5) ==> false + + The fact `fib(5,5)` is reachable so the problem should be UNSAT. + However, when we compute the resolvents of the non-linear clause with + all facts we get: + + fib(0,c) ==> fib(2,c+1) (R1) + fib(1,c) ==> fib(3,c+1) (R2) + fib(2,d) ==> fib(3,d+1) (R3) + fib(3,y) ==> fib(4,d+1) (R4) + + If we now chain linear paths we get: + + [fib(1,1), R2] = fib(3,2) + [fib(2,1), R3] = fib(3,2) + + [fib(3,2), R4] = fib(4,3) + + At this point we remove all facts except `fib(4,3)` because in the + linear context we can assume that all paths from these facts lead + to `fib(4,3)` anyway. However in the non-linear context we need to + keep `fib(3,2)` to derive `fib(5,5)`. + + QUESTION: We could add the chained rules to the ITS without removing + the rules that are part of the chain. However, that's potentially a + lot of rules and is this really cheaper re-generating these rules + during search? Maybe we can just add the maximal paths instead of + every prefix of every path. + */ + if (Config::Analysis::log) { + std::cout << "chaining linear paths..." << std::endl; + } + sub_res = chainLinearPaths(its); + if (Config::Analysis::log) { + std::cout << "finished chaining linear paths" << std::endl; + } + if (sub_res) { + res.succeed(); + res.majorProofStep("Chained Linear Paths", sub_res.getProof(), its); + } } if (Config::Analysis::log) { std::cout << "preprocessing rules..." << std::endl; diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index 1aef2f55f..6c2f4d40f 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -740,10 +740,8 @@ LinearSolver::Result Reachability::get_analysis_result() const { } void Reachability::analyze() { - if (!try_to_finish()) { - blocked_clauses[0].clear(); - derive_new_facts(); - } + blocked_clauses[0].clear(); + derive_new_facts(); switch (get_analysis_result()) { case LinearSolver::Result::Sat: @@ -961,6 +959,9 @@ void Reachability::add_clauses(const std::list &clauses) { } } + // TODO: shouldn't we clear all components? + blocked_clauses[0].clear(); + // When we add a new linear clause we have to restart the solver, // i.e. reset the trace, otherwise we might block a potential reachability path. restart(); diff --git a/src/config.cpp b/src/config.cpp index 30add4b87..13f69470c 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -34,7 +34,6 @@ namespace Config { namespace Output { // Whether to enable colors in the proof output bool Colors {true}; - // bool PrintDependencyGraph {false}; bool PrintDependencyGraph {true}; } diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 38f3f3a17..abb754221 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -1,5 +1,6 @@ #include "nonlinear.hpp" #include "booltheory.hpp" +#include "config.hpp" #include "linearizingsolver.hpp" #include "linearsolver.hpp" #include "smt.hpp" @@ -27,29 +28,47 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { if (optional_resolvent.has_value()) { const auto resolvent = optional_resolvent.value(); - // TODO: check for redundancy - // maybe later dont reject resolvent on `Unknown`? if (SmtFactory::check(resolvent.guard) == Sat) { resolvents.push_back(resolvent); if (!resolvent.isLinear()) { + // std::cout << "new non-linear: " << resolvent << std::endl; // Note that we append items to `non_linear_chcs` while iterating over it. // That means we also iterate over all added items. non_linear_chcs.push_back(resolvent); - } + } + + if (Config::Analysis::log) { + if (resolvent.isLinear()) { + std::cout << "new linear: " << resolvent << std::endl; + } else { + std::cout << "new non-linear: " << resolvent << std::endl; + } + } } } } } } + // TODO: saw this constraint: (-2+i3 == 0 /\ 2-i3 == 0). Could be simplified more. + linear_solver.add_clauses(resolvents); facts.clear(); const auto new_facts = linear_solver.derive_new_facts(); facts.insert(facts.end(), new_facts.begin(), new_facts.end()); + if (Config::Analysis::log) { + for (const auto &fact: new_facts) { + std::cout << "new fact: " << fact << std::endl; + // NOTE: seeing facts that are redundant up to renaming: + // new fact: (-1+i1 == 0 /\ -1+it15 < 0 /\ -3+it59 == 0 /\ -2+it60-it15 == 0) ==> F2(it59,it60) + // new fact: (-1+i1 == 0 /\ -2+it35 < 0 /\ -1+it66-it35 == 0 /\ -3+it65 == 0) ==> F2(it65,it66) + } + } + const auto result = linear_solver.get_analysis_result(); if (result == LinearSolver::Result::Unsat) { break; From 0b5e5593e4bb075a0ee2e1847f913e81a65a5b8b Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 30 Oct 2023 00:32:18 +0100 Subject: [PATCH 08/25] non-linear: fix resolution issue When computing the resolvent of two clauses we compute a unifier of the resolved predicates. Previously, we simply mapped the variables directly: F(a, b) v v F(x, y) However, apparently a predicate may not have disjoint arguments. That means, the arguments can encode implicit equalities, which are lost during unification: F(a, a) v v F(x, y) Taking this into account seems to fix several LIA benchmark instances where we gave UNSAT although Z3 gaves SAT: 066,108,235,236,244,355,359 --- benchmarks/LIA_adcl.txt | 28 +++++++-------- src/analysis/reachability.cpp | 1 + src/config.cpp | 2 +- src/its/itsproblem.cpp | 2 +- src/nonlinear/clause.cpp | 65 ++++++++++++++++++++--------------- src/nonlinear/nonlinear.cpp | 5 +++ 6 files changed, 60 insertions(+), 43 deletions(-) diff --git a/benchmarks/LIA_adcl.txt b/benchmarks/LIA_adcl.txt index 60bc6d09c..d335d10e4 100644 --- a/benchmarks/LIA_adcl.txt +++ b/benchmarks/LIA_adcl.txt @@ -64,7 +64,7 @@ 063 unsat 064 timeout 065 timeout -066 unsat +066 timeout 067 unsat 068 timeout 069 timeout @@ -90,7 +90,7 @@ 089 timeout 090 timeout 091 timeout -092 unsat +092 timeout 093 timeout 094 timeout 095 timeout @@ -106,7 +106,7 @@ 105 unsat 106 timeout 107 timeout -108 unsat +108 timeout 109 timeout 110 timeout 111 timeout @@ -233,16 +233,16 @@ 232 timeout 233 timeout 234 timeout -235 unsat -236 unsat +235 timeout +236 timeout 237 timeout 238 timeout -239 unsat -240 unsat -241 unsat -242 unsat -243 unsat -244 unsat +239 timeout +240 unknown +241 unknown +242 timeout +243 timeout +244 timeout 245 timeout 246 timeout 247 timeout @@ -353,11 +353,11 @@ 352 timeout 353 timeout 354 timeout -355 unsat +355 timeout 356 timeout 357 timeout 358 timeout -359 unsat +359 timeout 360 timeout 361 timeout 362 timeout @@ -436,7 +436,7 @@ 435 timeout 436 unsat 437 timeout -438 unsat +438 timeout 439 timeout 440 timeout 441 timeout diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index 6c2f4d40f..b8f6f85fa 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -1,4 +1,5 @@ #include "reachability.hpp" +#include "config.hpp" #include "preprocessing.hpp" #include "rulepreprocessing.hpp" #include "loopacceleration.hpp" diff --git a/src/config.cpp b/src/config.cpp index 13f69470c..a5c461fc8 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -34,7 +34,7 @@ namespace Config { namespace Output { // Whether to enable colors in the proof output bool Colors {true}; - bool PrintDependencyGraph {true}; + bool PrintDependencyGraph {false}; } namespace Input { diff --git a/src/its/itsproblem.cpp b/src/its/itsproblem.cpp index 68b48e62e..467ae99ab 100644 --- a/src/its/itsproblem.cpp +++ b/src/its/itsproblem.cpp @@ -293,7 +293,7 @@ const Clause ITSProblem::clauseFrom(TransIdx rule) const { // solver and the preprocessing, so it might be worth optimizing later. std::vector guard_conj = { rule->getGuard() }; std::vector rhs_args; - for (const Var &var : getProgVars()) { + for (const Var &var: prog_vars) { auto it = update.find(var); if (it == update.end()) { diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index 8304608e5..6904b1504 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -34,6 +34,7 @@ const Var varAt(const Var &var, const Subs &subs) { * F(x,x) and G(y,z) are not unifiable * F(x) and F(y,z) are not unifiable * F(w,x) and F(y,z) returns { w -> y, x -> z } + * F(x,x) and F(y,z) returns { x -> y, z -> y } * * In practice we normalize all predicates to have the same arity with same argument * types in the same order. So only differing predicate symbols should be a reason @@ -46,28 +47,37 @@ const std::optional computeUnifier(const FunApp &pred1, const FunApp &pred size_t size = pred1.args.size(); for (size_t i = 0; i < size; ++i) { - const auto var1 = pred1.args[i]; - const auto var2 = pred2.args[i]; - - // QUESTION: I suspect some C++ template magic can make this prettier - bool both_nums = std::holds_alternative(var1) && - std::holds_alternative(var2); - bool both_bools = std::holds_alternative(var1) && - std::holds_alternative(var2); - - if (both_nums) { - subs.put(std::make_pair( - std::get(var1), - std::get(expr::toExpr(var2)) - )); - } else if (both_bools) { - subs.put(std::make_pair( - std::get(var1), - std::get(expr::toExpr(var2)) - )); + const Var var1 = pred1.args[i]; + const Var var2 = pred2.args[i]; + + auto it = subs.find(var1); + if (it != subs.end()) { + // If `var1` is already contained in `subs`, then `pred1` must + // have `var1` multiple times as an argument. This is an implict + // equality that must also hold in `pred2` so we map `var2` to + // whatever `var1` already maps to. + subs.put(var2, expr::second(*it)); } else { - // argument types don't match ==> not unifiable - return {}; + // QUESTION: I suspect some C++ template magic can make this prettier + bool both_nums = std::holds_alternative(var1) && + std::holds_alternative(var2); + bool both_bools = std::holds_alternative(var1) && + std::holds_alternative(var2); + + if (both_nums) { + subs.put(std::make_pair( + std::get(var1), + std::get(expr::toExpr(var2)) + )); + } else if (both_bools) { + subs.put(std::make_pair( + std::get(var1), + std::get(expr::toExpr(var2)) + )); + } else { + // argument types don't match ==> not unifiable + return {}; + } } } @@ -155,9 +165,6 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA throw std::logic_error("Given `pred` is not on the LHS of `chc`"); } - std::set chc_lhs_without_pred = chc.lhs; - chc_lhs_without_pred.erase(pred); - // Make sure variables in `this` and `chc` are disjoint. Subs renaming; const VarSet this_vars {this->vars()}; @@ -181,17 +188,21 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA } const Clause this_unified = this_with_disjoint_vars.renameWith(unifier.value()); + std::set chc_lhs_without_pred = chc.lhs; + chc_lhs_without_pred.erase(pred); + const Clause chc_unified = Clause(chc_lhs_without_pred, chc.rhs, chc.guard) + .renameWith(unifier.value()); // LHS of resolvent is the union of the renamed LHS of `this` ... std::set resolvent_lhs = this_unified.lhs; // ... and the LHS of `chc` where `pred` is removed. - resolvent_lhs.insert(chc_lhs_without_pred.begin(), chc_lhs_without_pred.end()); + resolvent_lhs.insert(chc_unified.lhs.begin(), chc_unified.lhs.end()); - const auto new_guard = this_unified.guard & chc.guard; + const auto new_guard = this_unified.guard & chc_unified.guard; return Clause( resolvent_lhs, - chc.rhs, + chc_unified.rhs, new_guard->simplify() ); } diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index abb754221..54c1130aa 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -16,6 +16,11 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { std::list facts(linear_solver.get_initial_facts()); while (true) { + + if (Config::Analysis::log) { + std::cout << "============= non-linear solver main loop =============" << std::endl; + } + std::list resolvents; std::list non_linear_chcs = linear_solver.get_non_linear_chcs(); From aaecfae5e486d2a46a21fa66d4d991b28b32b21c Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Thu, 2 Nov 2023 12:03:32 +0100 Subject: [PATCH 09/25] non-linear: same file for benchmark results of Z3/ADCL put benchmark results for Z3 and ADCL in the same file so they can be compared during a test run more easily. --- benchmarks/LIA-Lin.txt | 500 ++++++++++++++++++++++++++++++++++++ benchmarks/LIA-Lin_adcl.txt | 499 ----------------------------------- benchmarks/LIA-Lin_z3.txt | 499 ----------------------------------- benchmarks/LIA.txt | 457 ++++++++++++++++++++++++++++++++ benchmarks/LIA_adcl.txt | 455 -------------------------------- benchmarks/LIA_z3.txt | 456 -------------------------------- run_benchmark.sh | 40 +-- 7 files changed, 981 insertions(+), 1925 deletions(-) create mode 100644 benchmarks/LIA-Lin.txt delete mode 100644 benchmarks/LIA-Lin_adcl.txt delete mode 100644 benchmarks/LIA-Lin_z3.txt create mode 100644 benchmarks/LIA.txt delete mode 100644 benchmarks/LIA_adcl.txt delete mode 100644 benchmarks/LIA_z3.txt diff --git a/benchmarks/LIA-Lin.txt b/benchmarks/LIA-Lin.txt new file mode 100644 index 000000000..aab9558fb --- /dev/null +++ b/benchmarks/LIA-Lin.txt @@ -0,0 +1,500 @@ +### Z3 ADCL +000 timeout timeout +001 timeout unknown +002 timeout unknown +003 timeout unknown +004 timeout unknown +005 timeout unknown +006 timeout unknown +007 sat unknown +008 timeout timeout +009 timeout unknown +010 sat unknown +011 sat unknown +012 timeout timeout +013 timeout timeout +014 timeout unknown +015 timeout unknown +016 timeout timeout +017 timeout unknown +018 timeout unknown +019 timeout unknown +020 timeout unknown +021 sat unknown +022 timeout unknown +023 sat unknown +024 timeout unknown +025 timeout timeout +026 timeout unknown +027 timeout timeout +028 sat unknown +029 timeout unknown +030 unsat unsat +031 unsat unsat +032 unsat unsat +033 unsat unsat +034 unsat unsat +035 unsat unsat +036 unsat timeout +037 unsat unsat +038 unsat unsat +039 unsat unsat +040 timeout timeout +041 timeout unknown +042 timeout unknown +043 timeout unsat +044 timeout unsat +045 timeout unsat +046 timeout timeout +047 timeout unsat +048 timeout unknown +049 timeout unknown +050 timeout unsat +051 timeout unsat +052 timeout unsat +053 timeout unsat +054 timeout unknown +055 timeout unknown +056 unsat timeout +057 timeout unsat +058 timeout unknown +059 timeout unsat +060 unsat unsat +061 unsat unsat +062 unsat unsat +063 unsat unsat +064 unsat unsat +065 timeout unsat +066 unsat unsat +067 unsat unsat +068 timeout timeout +069 timeout unsat +070 timeout timeout +071 unsat unsat +072 unsat unsat +073 sat timeout +074 timeout timeout +075 timeout unsat +076 timeout unsat +077 timeout unknown +078 timeout unknown +079 sat unknown +080 sat timeout +081 sat unknown +082 timeout unknown +083 unsat unsat +084 unsat unsat +085 sat unknown +086 unsat unsat +087 sat timeout +088 sat timeout +089 unsat unsat +090 timeout unknown +091 timeout unknown +092 unsat unsat +093 sat unknown +094 sat unknown +095 sat unknown +096 unsat timeout +097 timeout unknown +098 timeout unknown +099 timeout unknown +100 timeout unknown +101 timeout unknown +102 timeout timeout +103 timeout unknown +104 timeout unknown +105 timeout unknown +106 timeout timeout +107 timeout unknown +108 timeout unknown +109 timeout unknown +110 sat unknown +111 unsat unsat +112 sat unknown +113 sat timeout +114 sat timeout +115 sat unknown +116 unsat unsat +117 unsat unsat +118 sat unknown +119 unsat unsat +120 sat unknown +121 sat unknown +122 sat timeout +123 unsat unsat +124 sat timeout +125 timeout unknown +126 timeout unknown +127 timeout unknown +128 timeout unknown +129 sat timeout +130 unsat unsat +131 sat timeout +132 sat timeout +133 unsat unsat +134 timeout unknown +135 timeout timeout +136 timeout timeout +137 timeout timeout +138 timeout timeout +139 timeout timeout +140 timeout timeout +141 timeout timeout +142 timeout timeout +143 timeout timeout +144 timeout timeout +145 timeout timeout +146 timeout timeout +147 timeout timeout +148 timeout timeout +149 timeout timeout +150 timeout unknown +151 timeout timeout +152 timeout timeout +153 timeout timeout +154 timeout unsat +155 timeout timeout +156 timeout timeout +157 timeout timeout +158 timeout timeout +159 timeout timeout +160 timeout timeout +161 timeout timeout +162 timeout timeout +163 timeout timeout +164 timeout timeout +165 timeout timeout +166 timeout timeout +167 timeout timeout +168 timeout timeout +169 timeout timeout +170 sat timeout +171 sat timeout +172 sat timeout +173 sat timeout +174 sat unknown +175 unsat unsat +176 sat timeout +177 sat unknown +178 sat unknown +179 sat timeout +180 sat timeout +181 sat timeout +182 sat timeout +183 sat unknown +184 sat unknown +185 unsat unsat +186 sat timeout +187 unsat unsat +188 sat timeout +189 unsat timeout +190 sat timeout +191 sat timeout +192 sat timeout +193 sat timeout +194 sat timeout +195 sat timeout +196 sat timeout +197 unsat unsat +198 sat timeout +199 timeout timeout +200 unsat timeout +201 unsat timeout +202 timeout timeout +203 sat timeout +204 sat timeout +205 sat timeout +206 timeout timeout +207 timeout timeout +208 timeout timeout +209 timeout timeout +210 timeout timeout +211 timeout timeout +212 timeout timeout +213 timeout timeout +214 timeout timeout +215 timeout timeout +216 timeout timeout +217 timeout timeout +218 sat timeout +219 timeout timeout +220 timeout timeout +221 timeout timeout +222 timeout timeout +223 sat timeout +224 sat timeout +225 timeout timeout +226 timeout timeout +227 timeout timeout +228 sat timeout +229 timeout timeout +230 sat timeout +231 timeout timeout +232 sat timeout +233 timeout timeout +234 timeout timeout +235 timeout timeout +236 timeout timeout +237 timeout timeout +238 timeout timeout +239 timeout timeout +240 timeout timeout +241 timeout timeout +242 timeout timeout +243 timeout timeout +244 timeout timeout +245 timeout timeout +246 timeout timeout +247 timeout timeout +248 timeout timeout +249 timeout timeout +250 timeout timeout +251 timeout timeout +252 timeout timeout +253 timeout timeout +254 timeout timeout +255 timeout timeout +256 timeout timeout +257 timeout timeout +258 timeout timeout +259 timeout timeout +260 sat timeout +261 timeout unknown +262 unknown timeout +263 timeout unknown +264 timeout unknown +265 sat unknown +266 timeout unknown +267 timeout unknown +268 timeout timeout +269 timeout unknown +270 timeout unknown +271 timeout timeout +272 timeout timeout +273 timeout unknown +274 timeout timeout +275 timeout unknown +276 timeout timeout +277 timeout unknown +278 timeout timeout +279 timeout timeout +280 timeout unknown +281 timeout timeout +282 timeout unknown +283 timeout unknown +284 timeout unknown +285 timeout unknown +286 timeout unknown +287 timeout unknown +288 sat timeout +289 timeout unknown +290 sat unknown +291 sat unknown +292 timeout unknown +293 sat unknown +294 sat unknown +295 sat unknown +296 sat unknown +297 unsat unsat +298 timeout unknown +299 unsat unsat +300 sat unknown +301 sat unknown +302 timeout timeout +303 timeout unknown +304 timeout unknown +305 timeout unknown +306 sat unknown +307 sat unknown +308 sat unknown +309 sat unknown +310 sat unknown +311 sat unknown +312 timeout unknown +313 unsat unsat +314 sat unknown +315 sat unknown +316 unsat unsat +317 sat unknown +318 sat unknown +319 unsat unsat +320 sat unknown +321 unsat unsat +322 unsat unsat +323 sat unknown +324 sat timeout +325 sat unknown +326 unsat unsat +327 unsat unsat +328 unsat unsat +329 unsat unsat +330 unsat unsat +331 unsat unsat +332 unsat unsat +333 sat unknown +334 unsat unsat +335 unsat unsat +336 unsat unsat +337 unsat unsat +338 sat timeout +339 sat timeout +340 sat timeout +341 sat timeout +342 sat timeout +343 unsat timeout +344 sat timeout +345 unsat unsat +346 unsat unsat +347 unsat unsat +348 unsat unsat +349 unsat unsat +350 sat timeout +351 unsat unsat +352 unsat timeout +353 unsat timeout +354 unsat timeout +355 timeout timeout +356 timeout timeout +357 timeout timeout +358 timeout unsat +359 timeout timeout +360 timeout timeout +361 timeout timeout +362 timeout unsat +363 timeout timeout +364 timeout timeout +365 timeout timeout +366 timeout timeout +367 timeout timeout +368 timeout timeout +369 timeout timeout +370 timeout timeout +371 timeout timeout +372 timeout timeout +373 timeout timeout +374 timeout timeout +375 timeout timeout +376 timeout timeout +377 timeout timeout +378 timeout timeout +379 sat timeout +380 timeout timeout +381 timeout unsat +382 timeout timeout +383 timeout timeout +384 timeout timeout +385 timeout timeout +386 timeout unsat +387 timeout timeout +388 timeout timeout +389 timeout timeout +390 timeout timeout +391 timeout timeout +392 timeout timeout +393 timeout timeout +394 timeout timeout +395 unsat timeout +396 timeout timeout +397 timeout timeout +398 sat timeout +399 timeout timeout +400 timeout timeout +401 timeout unsat +402 timeout unsat +403 sat timeout +404 timeout timeout +405 timeout unsat +406 timeout timeout +407 timeout timeout +408 timeout timeout +409 sat unknown +410 sat unknown +411 sat unknown +412 sat unknown +413 sat unknown +414 sat unknown +415 sat unknown +416 sat unknown +417 sat unknown +418 sat unknown +419 sat unknown +420 sat unknown +421 sat unknown +422 sat unknown +423 sat unknown +424 sat unknown +425 sat unknown +426 sat unknown +427 sat unknown +428 sat unknown +429 timeout unknown +430 timeout unknown +431 timeout timeout +432 unsat timeout +433 timeout timeout +434 sat unknown +435 sat unknown +436 sat unknown +437 sat unknown +438 sat unknown +439 sat unknown +440 sat unknown +441 sat unknown +442 unsat timeout +443 sat unknown +444 sat unknown +445 sat unknown +446 timeout timeout +447 timeout timeout +448 timeout timeout +449 timeout timeout +450 timeout timeout +451 timeout timeout +452 timeout timeout +453 timeout timeout +454 timeout timeout +455 timeout timeout +456 timeout timeout +457 unsat timeout +458 timeout timeout +459 timeout timeout +460 timeout timeout +461 timeout unknown +462 timeout timeout +463 timeout timeout +464 timeout timeout +465 timeout timeout +466 timeout timeout +467 timeout timeout +468 timeout timeout +469 timeout timeout +470 timeout timeout +471 timeout timeout +472 timeout timeout +473 timeout timeout +474 unsat timeout +475 timeout unsat +476 timeout timeout +477 timeout timeout +478 timeout timeout +479 timeout timeout +480 timeout timeout +481 sat timeout +482 timeout timeout +483 timeout timeout +484 timeout timeout +485 timeout timeout +486 unsat timeout +487 sat timeout +488 timeout timeout +489 timeout timeout +490 timeout timeout +491 timeout timeout +492 timeout timeout +493 timeout timeout +494 timeout timeout +495 sat timeout +496 sat timeout +497 timeout timeout +498 timeout timeout diff --git a/benchmarks/LIA-Lin_adcl.txt b/benchmarks/LIA-Lin_adcl.txt deleted file mode 100644 index 6eeef3c1b..000000000 --- a/benchmarks/LIA-Lin_adcl.txt +++ /dev/null @@ -1,499 +0,0 @@ -000 timeout -001 unknown -002 unknown -003 unknown -004 unknown -005 unknown -006 unknown -007 unknown -008 timeout -009 unknown -010 unknown -011 unknown -012 timeout -013 timeout -014 unknown -015 unknown -016 timeout -017 unknown -018 unknown -019 unknown -020 unknown -021 unknown -022 unknown -023 unknown -024 unknown -025 timeout -026 unknown -027 timeout -028 unknown -029 unknown -030 unsat -031 unsat -032 unsat -033 unsat -034 unsat -035 unsat -036 timeout -037 unsat -038 unsat -039 unsat -040 timeout -041 unknown -042 unknown -043 unsat -044 unsat -045 unsat -046 timeout -047 unsat -048 unknown -049 unknown -050 unsat -051 unsat -052 unsat -053 unsat -054 unknown -055 unknown -056 timeout -057 unsat -058 unknown -059 unsat -060 unsat -061 unsat -062 unsat -063 unsat -064 unsat -065 unsat -066 unsat -067 unsat -068 timeout -069 unsat -070 timeout -071 unsat -072 unsat -073 timeout -074 timeout -075 unsat -076 unsat -077 unknown -078 unknown -079 unknown -080 timeout -081 unknown -082 unknown -083 unsat -084 unsat -085 unknown -086 unsat -087 timeout -088 timeout -089 unsat -090 unknown -091 unknown -092 unsat -093 unknown -094 unknown -095 unknown -096 timeout -097 unknown -098 unknown -099 unknown -100 unknown -101 unknown -102 timeout -103 unknown -104 unknown -105 unknown -106 timeout -107 unknown -108 unknown -109 unknown -110 unknown -111 unsat -112 unknown -113 timeout -114 timeout -115 unknown -116 unsat -117 unsat -118 unknown -119 unsat -120 unknown -121 unknown -122 timeout -123 unsat -124 timeout -125 unknown -126 unknown -127 unknown -128 unknown -129 timeout -130 unsat -131 timeout -132 timeout -133 unsat -134 unknown -135 timeout -136 timeout -137 timeout -138 timeout -139 timeout -140 timeout -141 timeout -142 timeout -143 timeout -144 timeout -145 timeout -146 timeout -147 timeout -148 timeout -149 timeout -150 unknown -151 timeout -152 timeout -153 timeout -154 unsat -155 timeout -156 timeout -157 timeout -158 timeout -159 timeout -160 timeout -161 timeout -162 timeout -163 timeout -164 timeout -165 timeout -166 timeout -167 timeout -168 timeout -169 timeout -170 timeout -171 timeout -172 timeout -173 timeout -174 unknown -175 unsat -176 timeout -177 unknown -178 unknown -179 timeout -180 timeout -181 timeout -182 timeout -183 unknown -184 unknown -185 unsat -186 timeout -187 unsat -188 timeout -189 timeout -190 timeout -191 timeout -192 timeout -193 timeout -194 timeout -195 timeout -196 timeout -197 unsat -198 timeout -199 timeout -200 timeout -201 timeout -202 timeout -203 timeout -204 timeout -205 timeout -206 timeout -207 timeout -208 timeout -209 timeout -210 timeout -211 timeout -212 timeout -213 timeout -214 timeout -215 timeout -216 timeout -217 timeout -218 timeout -219 timeout -220 timeout -221 timeout -222 timeout -223 timeout -224 timeout -225 timeout -226 timeout -227 timeout -228 timeout -229 timeout -230 timeout -231 timeout -232 timeout -233 timeout -234 timeout -235 timeout -236 timeout -237 timeout -238 timeout -239 timeout -240 timeout -241 timeout -242 timeout -243 timeout -244 timeout -245 timeout -246 timeout -247 timeout -248 timeout -249 timeout -250 timeout -251 timeout -252 timeout -253 timeout -254 timeout -255 timeout -256 timeout -257 timeout -258 timeout -259 timeout -260 timeout -261 unknown -262 timeout -263 unknown -264 unknown -265 unknown -266 unknown -267 unknown -268 timeout -269 unknown -270 unknown -271 timeout -272 timeout -273 unknown -274 timeout -275 unknown -276 timeout -277 unknown -278 timeout -279 timeout -280 unknown -281 timeout -282 unknown -283 unknown -284 unknown -285 unknown -286 unknown -287 unknown -288 timeout -289 unknown -290 unknown -291 unknown -292 unknown -293 unknown -294 unknown -295 unknown -296 unknown -297 unsat -298 unknown -299 unsat -300 unknown -301 unknown -302 timeout -303 unknown -304 unknown -305 unknown -306 unknown -307 unknown -308 unknown -309 unknown -310 unknown -311 unknown -312 unknown -313 unsat -314 unknown -315 unknown -316 unsat -317 unknown -318 unknown -319 unsat -320 unknown -321 unsat -322 unsat -323 unknown -324 timeout -325 unknown -326 unsat -327 unsat -328 unsat -329 unsat -330 unsat -331 unsat -332 unsat -333 unknown -334 unsat -335 unsat -336 unsat -337 unsat -338 timeout -339 timeout -340 timeout -341 timeout -342 timeout -343 timeout -344 timeout -345 unsat -346 unsat -347 unsat -348 unsat -349 unsat -350 timeout -351 unsat -352 timeout -353 timeout -354 timeout -355 timeout -356 timeout -357 timeout -358 unsat -359 timeout -360 timeout -361 timeout -362 unsat -363 timeout -364 timeout -365 timeout -366 timeout -367 timeout -368 timeout -369 timeout -370 timeout -371 timeout -372 timeout -373 timeout -374 timeout -375 timeout -376 timeout -377 timeout -378 timeout -379 timeout -380 timeout -381 unsat -382 timeout -383 timeout -384 timeout -385 timeout -386 unsat -387 timeout -388 timeout -389 timeout -390 timeout -391 timeout -392 timeout -393 timeout -394 timeout -395 timeout -396 timeout -397 timeout -398 timeout -399 timeout -400 timeout -401 unsat -402 unsat -403 timeout -404 timeout -405 unsat -406 timeout -407 timeout -408 timeout -409 unknown -410 unknown -411 unknown -412 unknown -413 unknown -414 unknown -415 unknown -416 unknown -417 unknown -418 unknown -419 unknown -420 unknown -421 unknown -422 unknown -423 unknown -424 unknown -425 unknown -426 unknown -427 unknown -428 unknown -429 unknown -430 unknown -431 timeout -432 timeout -433 timeout -434 unknown -435 unknown -436 unknown -437 unknown -438 unknown -439 unknown -440 unknown -441 unknown -442 timeout -443 unknown -444 unknown -445 unknown -446 timeout -447 timeout -448 timeout -449 timeout -450 timeout -451 timeout -452 timeout -453 timeout -454 timeout -455 timeout -456 timeout -457 timeout -458 timeout -459 timeout -460 timeout -461 unknown -462 timeout -463 timeout -464 timeout -465 timeout -466 timeout -467 timeout -468 timeout -469 timeout -470 timeout -471 timeout -472 timeout -473 timeout -474 timeout -475 unsat -476 timeout -477 timeout -478 timeout -479 timeout -480 timeout -481 timeout -482 timeout -483 timeout -484 timeout -485 timeout -486 timeout -487 timeout -488 timeout -489 timeout -490 timeout -491 timeout -492 timeout -493 timeout -494 timeout -495 timeout -496 timeout -497 timeout -498 timeout diff --git a/benchmarks/LIA-Lin_z3.txt b/benchmarks/LIA-Lin_z3.txt deleted file mode 100644 index 23d4a9bdd..000000000 --- a/benchmarks/LIA-Lin_z3.txt +++ /dev/null @@ -1,499 +0,0 @@ -000 timeout -001 timeout -002 timeout -003 timeout -004 timeout -005 timeout -006 timeout -007 sat -008 timeout -009 timeout -010 sat -011 sat -012 timeout -013 timeout -014 timeout -015 timeout -016 timeout -017 timeout -018 timeout -019 timeout -020 timeout -021 sat -022 timeout -023 sat -024 timeout -025 timeout -026 timeout -027 timeout -028 sat -029 timeout -030 unsat -031 unsat -032 unsat -033 unsat -034 unsat -035 unsat -036 unsat -037 unsat -038 unsat -039 unsat -040 timeout -041 timeout -042 timeout -043 timeout -044 timeout -045 timeout -046 timeout -047 timeout -048 timeout -049 timeout -050 timeout -051 timeout -052 timeout -053 timeout -054 timeout -055 timeout -056 unsat -057 timeout -058 timeout -059 timeout -060 unsat -061 unsat -062 unsat -063 unsat -064 unsat -065 timeout -066 unsat -067 unsat -068 timeout -069 timeout -070 timeout -071 unsat -072 unsat -073 sat -074 timeout -075 timeout -076 timeout -077 timeout -078 timeout -079 sat -080 sat -081 sat -082 timeout -083 unsat -084 unsat -085 sat -086 unsat -087 sat -088 sat -089 unsat -090 timeout -091 timeout -092 unsat -093 sat -094 sat -095 sat -096 unsat -097 timeout -098 timeout -099 timeout -100 timeout -101 timeout -102 timeout -103 timeout -104 timeout -105 timeout -106 timeout -107 timeout -108 timeout -109 timeout -110 sat -111 unsat -112 sat -113 sat -114 sat -115 sat -116 unsat -117 unsat -118 sat -119 unsat -120 sat -121 sat -122 sat -123 unsat -124 sat -125 timeout -126 timeout -127 timeout -128 timeout -129 sat -130 unsat -131 sat -132 sat -133 unsat -134 timeout -135 timeout -136 timeout -137 timeout -138 timeout -139 timeout -140 timeout -141 timeout -142 timeout -143 timeout -144 timeout -145 timeout -146 timeout -147 timeout -148 timeout -149 timeout -150 timeout -151 timeout -152 timeout -153 timeout -154 timeout -155 timeout -156 timeout -157 timeout -158 timeout -159 timeout -160 timeout -161 timeout -162 timeout -163 timeout -164 timeout -165 timeout -166 timeout -167 timeout -168 timeout -169 timeout -170 sat -171 sat -172 sat -173 sat -174 sat -175 unsat -176 sat -177 sat -178 sat -179 sat -180 sat -181 sat -182 sat -183 sat -184 sat -185 unsat -186 sat -187 unsat -188 sat -189 unsat -190 sat -191 sat -192 sat -193 sat -194 sat -195 sat -196 sat -197 unsat -198 sat -199 timeout -200 unsat -201 unsat -202 timeout -203 sat -204 sat -205 sat -206 timeout -207 timeout -208 timeout -209 timeout -210 timeout -211 timeout -212 timeout -213 timeout -214 timeout -215 timeout -216 timeout -217 timeout -218 sat -219 timeout -220 timeout -221 timeout -222 timeout -223 sat -224 sat -225 timeout -226 timeout -227 timeout -228 sat -229 timeout -230 sat -231 timeout -232 sat -233 timeout -234 timeout -235 timeout -236 timeout -237 timeout -238 timeout -239 timeout -240 timeout -241 timeout -242 timeout -243 timeout -244 timeout -245 timeout -246 timeout -247 timeout -248 timeout -249 timeout -250 timeout -251 timeout -252 timeout -253 timeout -254 timeout -255 timeout -256 timeout -257 timeout -258 timeout -259 timeout -260 sat -261 timeout -262 unknown -263 timeout -264 timeout -265 sat -266 timeout -267 timeout -268 timeout -269 timeout -270 timeout -271 timeout -272 timeout -273 timeout -274 timeout -275 timeout -276 timeout -277 timeout -278 timeout -279 timeout -280 timeout -281 timeout -282 timeout -283 timeout -284 timeout -285 timeout -286 timeout -287 timeout -288 sat -289 timeout -290 sat -291 sat -292 timeout -293 sat -294 sat -295 sat -296 sat -297 unsat -298 timeout -299 unsat -300 sat -301 sat -302 timeout -303 timeout -304 timeout -305 timeout -306 sat -307 sat -308 sat -309 sat -310 sat -311 sat -312 timeout -313 unsat -314 sat -315 sat -316 unsat -317 sat -318 sat -319 unsat -320 sat -321 unsat -322 unsat -323 sat -324 sat -325 sat -326 unsat -327 unsat -328 unsat -329 unsat -330 unsat -331 unsat -332 unsat -333 sat -334 unsat -335 unsat -336 unsat -337 unsat -338 sat -339 sat -340 sat -341 sat -342 sat -343 unsat -344 sat -345 unsat -346 unsat -347 unsat -348 unsat -349 unsat -350 sat -351 unsat -352 unsat -353 unsat -354 unsat -355 timeout -356 timeout -357 timeout -358 timeout -359 timeout -360 timeout -361 timeout -362 timeout -363 timeout -364 timeout -365 timeout -366 timeout -367 timeout -368 timeout -369 timeout -370 timeout -371 timeout -372 timeout -373 timeout -374 timeout -375 timeout -376 timeout -377 timeout -378 timeout -379 sat -380 timeout -381 timeout -382 timeout -383 timeout -384 timeout -385 timeout -386 timeout -387 timeout -388 timeout -389 timeout -390 timeout -391 timeout -392 timeout -393 timeout -394 timeout -395 unsat -396 timeout -397 timeout -398 sat -399 timeout -400 timeout -401 timeout -402 timeout -403 sat -404 timeout -405 timeout -406 timeout -407 timeout -408 timeout -409 sat -410 sat -411 sat -412 sat -413 sat -414 sat -415 sat -416 sat -417 sat -418 sat -419 sat -420 sat -421 sat -422 sat -423 sat -424 sat -425 sat -426 sat -427 sat -428 sat -429 timeout -430 timeout -431 timeout -432 unsat -433 timeout -434 sat -435 sat -436 sat -437 sat -438 sat -439 sat -440 sat -441 sat -442 unsat -443 sat -444 sat -445 sat -446 timeout -447 timeout -448 timeout -449 timeout -450 timeout -451 timeout -452 timeout -453 timeout -454 timeout -455 timeout -456 timeout -457 unsat -458 timeout -459 timeout -460 timeout -461 timeout -462 timeout -463 timeout -464 timeout -465 timeout -466 timeout -467 timeout -468 timeout -469 timeout -470 timeout -471 timeout -472 timeout -473 timeout -474 unsat -475 timeout -476 timeout -477 timeout -478 timeout -479 timeout -480 timeout -481 sat -482 timeout -483 timeout -484 timeout -485 timeout -486 unsat -487 sat -488 timeout -489 timeout -490 timeout -491 timeout -492 timeout -493 timeout -494 timeout -495 sat -496 sat -497 timeout -498 timeout diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt new file mode 100644 index 000000000..70da9894c --- /dev/null +++ b/benchmarks/LIA.txt @@ -0,0 +1,457 @@ +# Z3 ADCL +# +000 sat timeout +001 sat timeout +002 sat timeout +003 sat timeout +004 sat timeout +005 sat timeout +006 sat timeout +007 sat timeout +008 sat timeout +009 sat timeout +010 sat timeout +011 sat timeout +012 sat timeout +013 sat timeout +014 timeout timeout +015 sat timeout +016 timeout timeout +017 sat timeout +018 timeout timeout +019 sat timeout +020 timeout timeout +021 sat timeout +022 sat timeout +023 sat timeout +024 timeout timeout +025 timeout timeout +026 timeout timeout +027 timeout timeout +028 timeout timeout +029 timeout timeout +030 timeout timeout +031 timeout timeout +032 timeout timeout +033 timeout timeout +034 timeout timeout +035 timeout timeout +036 timeout timeout +037 timeout timeout +038 timeout timeout +039 timeout timeout +040 timeout timeout +041 timeout timeout +042 timeout timeout +043 sat timeout +044 timeout timeout +045 timeout timeout +046 timeout timeout +047 timeout timeout +048 sat timeout +049 unsat timeout +050 timeout timeout +051 sat timeout +052 timeout timeout +053 timeout timeout +054 timeout timeout +055 timeout timeout +056 timeout timeout +057 timeout timeout +058 timeout timeout +059 sat timeout +060 unsat unsat +061 sat timeout +062 unsat unsat +063 unsat unsat +064 sat timeout +065 sat timeout +066 sat timeout +067 unsat unsat +068 sat timeout +069 unsat timeout +070 sat timeout +071 sat unknown +072 unsat unsat +073 unsat timeout +074 sat timeout +075 unsat unsat +076 unsat timeout +077 unsat timeout +078 sat timeout +079 sat timeout +080 unsat timeout +081 unsat timeout +082 sat timeout +083 sat timeout +084 sat timeout +085 sat timeout +086 unsat timeout +087 timeout timeout +088 sat timeout +089 sat timeout +090 unsat timeout +091 sat timeout +092 timeout timeout +093 unsat timeout +094 timeout timeout +095 sat timeout +096 timeout unsat +097 timeout unsat +098 unsat timeout +099 sat timeout +100 sat timeout +101 timeout timeout +102 timeout timeout +103 unsat unsat +104 unsat unsat +105 unsat unsat +106 unsat timeout +107 sat timeout +108 sat timeout +109 unsat timeout +110 unsat timeout +111 unsat timeout +112 unsat timeout +113 unsat timeout +114 unsat timeout +115 sat timeout +116 sat timeout +117 sat timeout +118 sat timeout +119 timeout timeout +120 sat timeout +121 sat timeout +122 timeout timeout +123 timeout timeout +124 sat timeout +125 sat timeout +126 timeout timeout +127 sat timeout +128 sat timeout +129 sat timeout +130 timeout timeout +131 timeout timeout +132 timeout timeout +133 unsat timeout +134 unsat timeout +135 sat timeout +136 unsat timeout +137 unsat timeout +138 sat timeout +139 unsat timeout +140 sat timeout +141 unsat timeout +142 unsat unsat +143 sat timeout +144 sat timeout +145 unsat timeout +146 sat timeout +147 unsat timeout +148 unsat timeout +149 sat timeout +150 unsat timeout +151 sat timeout +152 unsat timeout +153 sat timeout +154 sat timeout +155 sat timeout +156 sat timeout +157 unsat timeout +158 unsat timeout +159 sat timeout +160 sat timeout +161 sat timeout +162 sat timeout +163 sat timeout +164 unsat timeout +165 unsat timeout +166 sat timeout +167 sat timeout +168 unsat timeout +169 timeout timeout +170 timeout timeout +171 timeout timeout +172 sat timeout +173 timeout timeout +174 timeout timeout +175 timeout timeout +176 timeout timeout +177 timeout timeout +178 sat timeout +179 timeout timeout +180 sat timeout +181 timeout timeout +182 sat timeout +183 timeout timeout +184 sat timeout +185 unknown timeout +186 sat timeout +187 timeout timeout +188 timeout timeout +189 timeout timeout +190 sat timeout +191 timeout timeout +192 sat timeout +193 sat timeout +194 timeout timeout +195 timeout timeout +196 timeout timeout +197 sat timeout +198 unsat unsat +199 unsat unsat +200 sat timeout +201 sat timeout +202 sat unknown +203 unsat unsat +204 sat timeout +205 sat timeout +206 sat timeout +207 sat timeout +208 sat timeout +209 timeout timeout +210 sat timeout +211 unsat unsat +212 timeout timeout +213 sat timeout +214 timeout timeout +215 timeout timeout +216 sat timeout +217 timeout timeout +218 timeout timeout +219 sat timeout +220 timeout timeout +221 sat timeout +222 sat timeout +223 sat timeout +224 sat timeout +225 timeout timeout +226 timeout timeout +227 sat timeout +228 sat timeout +229 sat timeout +230 sat timeout +231 sat timeout +232 sat timeout +233 sat timeout +234 sat timeout +235 sat timeout +236 sat timeout +237 sat timeout +238 sat timeout +239 timeout timeout +240 timeout unknown +241 timeout unknown +242 timeout timeout +243 timeout timeout +244 sat timeout +245 unsat timeout +246 sat timeout +247 unsat timeout +248 sat timeout +249 unsat unsat +250 sat timeout +251 sat timeout +252 sat timeout +253 unsat timeout +254 sat timeout +255 sat timeout +256 unsat timeout +257 sat timeout +258 sat unknown +259 unsat timeout +260 sat timeout +261 sat unknown +262 sat unknown +263 unsat timeout +264 unsat timeout +265 timeout timeout +266 unsat timeout +267 timeout timeout +268 unsat timeout +269 sat timeout +270 sat timeout +271 unsat timeout +272 sat unknown +273 unsat timeout +274 timeout timeout +275 unsat timeout +276 unsat timeout +277 unsat timeout +278 sat timeout +279 unsat timeout +280 unsat timeout +281 timeout timeout +282 timeout timeout +283 unsat timeout +284 timeout timeout +285 timeout timeout +286 unsat timeout +287 timeout timeout +288 sat timeout +289 unsat timeout +290 timeout timeout +291 sat timeout +292 timeout timeout +293 sat timeout +294 timeout timeout +295 unsat timeout +296 timeout timeout +297 unsat timeout +298 timeout timeout +299 sat timeout +300 timeout timeout +301 unsat timeout +302 timeout timeout +303 timeout timeout +304 timeout timeout +305 unsat timeout +306 timeout timeout +307 unsat timeout +308 timeout timeout +309 timeout timeout +310 timeout timeout +311 timeout timeout +312 sat timeout +313 timeout timeout +314 unsat timeout +315 timeout timeout +316 timeout timeout +317 unsat timeout +318 sat timeout +319 unsat timeout +320 sat timeout +321 timeout timeout +322 timeout timeout +323 sat timeout +324 sat timeout +325 timeout timeout +326 unsat timeout +327 timeout timeout +328 unsat timeout +329 sat timeout +330 timeout timeout +331 timeout timeout +332 timeout timeout +333 unsat timeout +334 sat timeout +335 sat timeout +336 unsat timeout +337 sat timeout +338 unsat unsat +339 unsat unsat +340 sat timeout +341 sat timeout +342 unsat error +343 unsat timeout +344 unsat timeout +345 unsat timeout +346 sat timeout +347 sat timeout +348 sat timeout +349 unsat unsat +350 unsat unsat +351 unsat timeout +352 sat timeout +353 unsat timeout +354 unsat timeout +355 sat timeout +356 unsat timeout +357 timeout timeout +358 sat timeout +359 sat timeout +360 sat timeout +361 timeout timeout +362 sat timeout +363 sat timeout +364 unsat timeout +365 sat timeout +366 sat timeout +367 sat timeout +368 sat timeout +369 sat timeout +370 sat timeout +371 sat timeout +372 sat timeout +373 sat timeout +374 sat timeout +375 sat timeout +376 sat timeout +377 sat timeout +378 sat timeout +379 sat timeout +380 sat timeout +381 sat timeout +382 sat timeout +383 sat timeout +384 sat timeout +385 sat timeout +386 sat timeout +387 unsat timeout +388 sat timeout +389 unsat timeout +390 sat timeout +391 sat timeout +392 sat timeout +393 unsat timeout +394 sat timeout +395 sat timeout +396 sat timeout +397 sat timeout +398 sat timeout +399 unsat timeout +400 unsat timeout +401 sat timeout +402 timeout timeout +403 timeout unsat +404 timeout timeout +405 timeout timeout +406 timeout timeout +407 sat timeout +408 sat timeout +409 unsat timeout +410 sat timeout +411 unsat timeout +412 sat timeout +413 sat timeout +414 unsat timeout +415 sat timeout +416 sat timeout +417 sat timeout +418 sat timeout +419 unsat timeout +420 sat timeout +421 sat timeout +422 unsat timeout +423 unsat timeout +424 sat timeout +425 sat timeout +426 unsat timeout +427 unsat timeout +428 sat timeout +429 unsat timeout +430 unsat timeout +431 unsat timeout +432 sat timeout +433 sat timeout +434 unsat timeout +435 sat timeout +436 unsat unsat +437 unsat timeout +438 timeout timeout +439 sat timeout +440 sat timeout +441 sat timeout +442 unsat timeout +443 sat timeout +444 unsat timeout +445 sat timeout +446 sat timeout +447 sat timeout +448 unsat timeout +449 sat timeout +450 timeout timeout +451 unsat timeout +452 unsat timeout +453 sat timeout +454 sat timeout diff --git a/benchmarks/LIA_adcl.txt b/benchmarks/LIA_adcl.txt deleted file mode 100644 index d335d10e4..000000000 --- a/benchmarks/LIA_adcl.txt +++ /dev/null @@ -1,455 +0,0 @@ -000 timeout -001 timeout -002 timeout -003 timeout -004 timeout -005 timeout -006 timeout -007 timeout -008 timeout -009 timeout -010 timeout -011 timeout -012 timeout -013 timeout -014 timeout -015 timeout -016 timeout -017 timeout -018 timeout -019 timeout -020 timeout -021 timeout -022 timeout -023 timeout -024 timeout -025 timeout -026 timeout -027 timeout -028 timeout -029 timeout -030 timeout -031 timeout -032 timeout -033 timeout -034 timeout -035 timeout -036 timeout -037 timeout -038 timeout -039 timeout -040 timeout -041 timeout -042 timeout -043 timeout -044 timeout -045 timeout -046 timeout -047 timeout -048 timeout -049 timeout -050 timeout -051 timeout -052 timeout -053 timeout -054 timeout -055 timeout -056 timeout -057 timeout -058 timeout -059 timeout -060 unsat -061 timeout -062 unsat -063 unsat -064 timeout -065 timeout -066 timeout -067 unsat -068 timeout -069 timeout -070 timeout -071 unknown -072 unsat -073 timeout -074 timeout -075 unsat -076 timeout -077 timeout -078 timeout -079 timeout -080 timeout -081 timeout -082 timeout -083 timeout -084 timeout -085 timeout -086 timeout -087 timeout -088 timeout -089 timeout -090 timeout -091 timeout -092 timeout -093 timeout -094 timeout -095 timeout -096 unsat -097 unsat -098 timeout -099 timeout -100 timeout -101 timeout -102 timeout -103 unsat -104 unsat -105 unsat -106 timeout -107 timeout -108 timeout -109 timeout -110 timeout -111 timeout -112 timeout -113 timeout -114 timeout -115 timeout -116 timeout -117 timeout -118 timeout -119 timeout -120 timeout -121 timeout -122 timeout -123 timeout -124 timeout -125 timeout -126 timeout -127 timeout -128 timeout -129 timeout -130 timeout -131 timeout -132 timeout -133 timeout -134 timeout -135 timeout -136 timeout -137 timeout -138 timeout -139 timeout -140 timeout -141 timeout -142 unsat -143 timeout -144 timeout -145 timeout -146 timeout -147 timeout -148 timeout -149 timeout -150 timeout -151 timeout -152 timeout -153 timeout -154 timeout -155 timeout -156 timeout -157 timeout -158 timeout -159 timeout -160 timeout -161 timeout -162 timeout -163 timeout -164 timeout -165 timeout -166 timeout -167 timeout -168 timeout -169 timeout -170 timeout -171 timeout -172 timeout -173 timeout -174 timeout -175 timeout -176 timeout -177 timeout -178 timeout -179 timeout -180 timeout -181 timeout -182 timeout -183 timeout -184 timeout -185 timeout -186 timeout -187 timeout -188 timeout -189 timeout -190 timeout -191 timeout -192 timeout -193 timeout -194 timeout -195 timeout -196 timeout -197 timeout -198 unsat -199 unsat -200 timeout -201 timeout -202 unknown -203 unsat -204 timeout -205 timeout -206 timeout -207 timeout -208 timeout -209 timeout -210 timeout -211 unsat -212 timeout -213 timeout -214 timeout -215 timeout -216 timeout -217 timeout -218 timeout -219 timeout -220 timeout -221 timeout -222 timeout -223 timeout -224 timeout -225 timeout -226 timeout -227 timeout -228 timeout -229 timeout -230 timeout -231 timeout -232 timeout -233 timeout -234 timeout -235 timeout -236 timeout -237 timeout -238 timeout -239 timeout -240 unknown -241 unknown -242 timeout -243 timeout -244 timeout -245 timeout -246 timeout -247 timeout -248 timeout -249 unsat -250 timeout -251 timeout -252 timeout -253 timeout -254 timeout -255 timeout -256 timeout -257 timeout -258 unknown -259 timeout -260 timeout -261 unknown -262 unknown -263 timeout -264 timeout -265 timeout -266 timeout -267 timeout -268 timeout -269 timeout -270 timeout -271 timeout -272 unknown -273 timeout -274 timeout -275 timeout -276 timeout -277 timeout -278 timeout -279 timeout -280 timeout -281 timeout -282 timeout -283 timeout -284 timeout -285 timeout -286 timeout -287 timeout -288 timeout -289 timeout -290 timeout -291 timeout -292 timeout -293 timeout -294 timeout -295 timeout -296 timeout -297 timeout -298 timeout -299 timeout -300 timeout -301 timeout -302 timeout -303 timeout -304 timeout -305 timeout -306 timeout -307 timeout -308 timeout -309 timeout -310 timeout -311 timeout -312 timeout -313 timeout -314 timeout -315 timeout -316 timeout -317 timeout -318 timeout -319 timeout -320 timeout -321 timeout -322 timeout -323 timeout -324 timeout -325 timeout -326 timeout -327 timeout -328 timeout -329 timeout -330 timeout -331 timeout -332 timeout -333 timeout -334 timeout -335 timeout -336 timeout -337 timeout -338 unsat -339 unsat -340 timeout -341 timeout -342 error -343 timeout -344 timeout -345 timeout -346 timeout -347 timeout -348 timeout -349 unsat -350 unsat -351 timeout -352 timeout -353 timeout -354 timeout -355 timeout -356 timeout -357 timeout -358 timeout -359 timeout -360 timeout -361 timeout -362 timeout -363 timeout -364 timeout -365 timeout -366 timeout -367 timeout -368 timeout -369 timeout -370 timeout -371 timeout -372 timeout -373 timeout -374 timeout -375 timeout -376 timeout -377 timeout -378 timeout -379 timeout -380 timeout -381 timeout -382 timeout -383 timeout -384 timeout -385 timeout -386 timeout -387 timeout -388 timeout -389 timeout -390 timeout -391 timeout -392 timeout -393 timeout -394 timeout -395 timeout -396 timeout -397 timeout -398 timeout -399 timeout -400 timeout -401 timeout -402 timeout -403 unsat -404 timeout -405 timeout -406 timeout -407 timeout -408 timeout -409 timeout -410 timeout -411 timeout -412 timeout -413 timeout -414 timeout -415 timeout -416 timeout -417 timeout -418 timeout -419 timeout -420 timeout -421 timeout -422 timeout -423 timeout -424 timeout -425 timeout -426 timeout -427 timeout -428 timeout -429 timeout -430 timeout -431 timeout -432 timeout -433 timeout -434 timeout -435 timeout -436 unsat -437 timeout -438 timeout -439 timeout -440 timeout -441 timeout -442 timeout -443 timeout -444 timeout -445 timeout -446 timeout -447 timeout -448 timeout -449 timeout -450 timeout -451 timeout -452 timeout -453 timeout -454 timeout diff --git a/benchmarks/LIA_z3.txt b/benchmarks/LIA_z3.txt deleted file mode 100644 index 908a18419..000000000 --- a/benchmarks/LIA_z3.txt +++ /dev/null @@ -1,456 +0,0 @@ -000 sat -001 sat -002 sat -003 sat -004 sat -005 sat -006 sat -007 sat -008 sat -009 sat -010 sat -011 sat -012 sat -013 sat -014 timeout -015 sat -016 timeout -017 sat -018 timeout -019 sat -020 timeout -021 sat -022 sat -023 sat -024 timeout -025 timeout -026 timeout -027 timeout -028 timeout -029 timeout -030 timeout -031 timeout -032 timeout -033 timeout -034 timeout -035 timeout -036 timeout -037 timeout -038 timeout -039 timeout -040 timeout -041 timeout -042 timeout -043 sat -044 timeout -045 timeout -046 timeout -047 timeout -048 sat -049 unsat -050 timeout -051 sat -052 timeout -053 timeout -054 timeout -055 timeout -056 timeout -057 timeout -058 timeout -059 sat -060 unsat -061 sat -062 unsat -063 unsat -064 sat -065 sat -066 sat -067 unsat -068 sat -069 unsat -070 sat -071 sat -072 unsat -073 unsat -074 sat -075 unsat -076 unsat -077 unsat -078 sat -079 sat -080 unsat -081 unsat -082 sat -083 sat -084 sat -085 sat -086 unsat -087 timeout -088 sat -089 sat -090 unsat -091 sat -092 timeout -093 unsat -094 timeout -095 sat -096 timeout -097 timeout -098 unsat -099 sat -100 sat -101 timeout -102 timeout -103 unsat -104 unsat -105 unsat -106 unsat -107 sat -108 sat -109 unsat -110 unsat -111 unsat -112 unsat -113 unsat -114 unsat -115 sat -116 sat -117 sat -118 sat -119 timeout -120 sat -121 sat -122 timeout -123 timeout -124 sat -125 sat -126 timeout -127 sat -128 sat -129 sat -130 timeout -131 timeout -132 timeout -133 unsat -134 unsat -135 sat -136 unsat -137 unsat -138 sat -139 unsat -140 sat -141 unsat -142 unsat -143 sat -144 sat -145 unsat -146 sat -147 unsat -148 unsat -149 sat -150 unsat -151 sat -152 unsat -153 sat -154 sat -155 sat -156 sat -157 unsat -158 unsat -159 sat -160 sat -161 sat -162 sat -163 sat -164 unsat -165 unsat -166 sat -167 sat -168 unsat -169 timeout -170 timeout -171 timeout -172 sat -173 timeout -174 timeout -175 timeout -176 timeout -177 timeout -178 sat -179 timeout -180 sat -181 timeout -182 sat -183 timeout -184 sat -185 unknown -186 sat -187 timeout -188 timeout -189 timeout -190 sat -191 timeout -192 sat -193 sat -194 timeout -195 timeout -196 timeout -197 sat -198 unsat -199 unsat -200 sat -201 sat -202 sat -203 unsat -204 sat -205 sat -206 sat -207 sat -208 sat -209 timeout -210 sat -211 unsat -212 timeout -213 sat -214 timeout -215 timeout -216 sat -217 timeout -218 timeout -219 sat -220 timeout -221 sat -222 sat -223 sat -224 sat -225 timeout -226 timeout -227 sat -228 sat -229 sat -230 sat -231 sat -232 sat -233 sat -234 sat -235 sat -236 sat -237 sat -238 sat -239 timeout -240 timeout -241 timeout -242 timeout -243 timeout -244 sat -245 unsat -246 sat -247 unsat -248 sat -249 unsat -250 sat -251 sat -252 sat -253 unsat -254 sat -255 sat -256 unsat -257 sat -258 sat -259 unsat -260 sat -261 sat -262 sat -263 unsat -264 unsat -265 timeout -266 unsat -267 timeout -268 unsat -269 sat -270 sat -271 unsat -272 sat -273 unsat -274 timeout -275 unsat -276 unsat -277 unsat -278 sat -279 unsat -280 unsat -281 timeout -282 timeout -283 unsat -284 timeout -285 timeout -286 unsat -287 timeout -288 sat -289 unsat -290 timeout -291 sat -292 timeout -293 sat -294 timeout -295 unsat -296 timeout -297 unsat -298 timeout -299 sat -300 timeout -301 unsat -302 timeout -303 timeout -304 timeout -305 unsat -306 timeout -307 unsat -308 timeout -309 timeout -310 timeout -311 timeout -312 sat -313 timeout -314 unsat -315 timeout -316 timeout -317 unsat -318 sat -319 unsat -320 sat -321 timeout -322 timeout -323 sat -324 sat -325 timeout -326 unsat -327 timeout -328 unsat -329 sat -330 timeout -331 timeout -332 timeout -333 unsat -334 sat -335 sat -336 unsat -337 sat -338 unsat -339 unsat -340 sat -341 sat -342 unsat -343 unsat -344 unsat -345 unsat -346 sat -347 sat -348 sat -349 unsat -350 unsat -351 unsat -352 sat -353 unsat -354 unsat -355 sat -356 unsat -357 timeout -358 sat -359 sat -360 sat -361 timeout -362 sat -363 sat -364 unsat -365 sat -366 sat -367 sat -368 sat -369 sat -370 sat -371 sat -372 sat -373 sat -374 sat -375 sat -376 sat -377 sat -378 sat -379 sat -380 sat -381 sat -382 sat -383 sat -384 sat -385 sat -386 sat -387 unsat -388 sat -389 unsat -390 sat -391 sat -392 sat -393 unsat -394 sat -395 sat -396 sat -397 sat -398 sat -399 unsat -400 unsat -401 sat -402 timeout -403 timeout -404 timeout -405 timeout -406 timeout -407 sat -408 sat -409 unsat -410 sat -411 unsat -412 sat -413 sat -414 unsat -415 sat -416 sat -417 sat -418 sat -419 unsat -420 sat -421 sat -422 unsat -423 unsat -424 sat -425 sat -426 unsat -427 unsat -428 sat -429 unsat -430 unsat -431 unsat -432 sat -433 sat -434 unsat -435 sat -436 unsat -437 unsat -438 timeout -439 sat -440 sat -441 sat -442 unsat -443 sat -444 unsat -445 sat -446 sat -447 sat -448 unsat -449 sat -450 timeout -451 unsat -452 unsat -453 sat -454 sat -455 sat diff --git a/run_benchmark.sh b/run_benchmark.sh index e6df8f1b2..10cc8bea2 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -12,14 +12,24 @@ pushd build make -j4 popd -# # debug: 009 + +# 074 +# 106 +# 109 +# 110 +# 111 +# 113 +# 114 + +# TODO: debug 342 (seg fault) + # # gdb --args \ # ./build/loat-static \ # --mode reachability \ # --format horn \ # --proof-level 0 \ # --log \ -# "../chc-comp22-benchmarks/LIA/chc-LIA_265.smt2" +# "../chc-comp22-benchmarks/LIA/chc-LIA_074.smt2" # popd # exit @@ -27,19 +37,20 @@ popd ########################################################################## benchmark="LIA" -compare_with="adcl" + +printf "### %-7s %-7s %-7s \n" "Z3" "ADCL" "now" while IFS= read -r line do - if [[ $line =~ ^#.*$ ]] || [ "$line" = "" ] ; then + if [[ $line =~ ^#.*$ ]] || [[ "$line" = "" ]]; then # skip comments and empty lines continue else - read idx prev_result <<< "$line" + read idx z3_result adcl_result <<< "$line" file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" # if true; then - if [[ "$prev_result" != "timeout" ]]; then + if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then set +e result=$(timeout 5 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") # result=$(timeout 5 z3 "$file") @@ -55,19 +66,16 @@ do result="error" fi - if [[ "$result" == "$prev_result" ]]; then - printf "$idx $result \n" - else - printf "$idx $prev_result --> $result \n" - fi - else - # if we skip an instance nevertheless include it in the output - # so the log can easily be copied and saved as a whole - printf "$idx $prev_result \n" + printf "$idx %-7s %-7s %-7s \n" $z3_result $adcl_result $result + + # else + # # if we skip an instance nevertheless include it in the output + # # so the log can easily be copied and saved as a whole + # printf "$idx $prev_result \n" fi fi -done < "./benchmarks/${benchmark}_${compare_with}.txt" +done < "./benchmarks/${benchmark}.txt" # "undo" pushd popd From e5c2c0eeec2d0a135ad60229ddf0c215f64a7824 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Thu, 2 Nov 2023 12:06:04 +0100 Subject: [PATCH 10/25] non-linear: update benchmark results Actually we get UNSAT on quite some more instances where we got a timeout before. Seems also related to disabling the linear path chaining. --- benchmarks/LIA.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index 70da9894c..b1c4c74dd 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -106,15 +106,15 @@ 103 unsat unsat 104 unsat unsat 105 unsat unsat -106 unsat timeout +106 unsat unsat 107 sat timeout 108 sat timeout -109 unsat timeout -110 unsat timeout -111 unsat timeout +109 unsat unsat +110 unsat unsat +111 unsat unsat 112 unsat timeout -113 unsat timeout -114 unsat timeout +113 unsat unsat +114 unsat unsat 115 sat timeout 116 sat timeout 117 sat timeout From 206597e7a79649dc654825dace4543a314ab5bb6 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 6 Nov 2023 17:14:19 +0100 Subject: [PATCH 11/25] non-linear: reduce redundant resolvents 1) Previously, the non-linear solver generated redundant resolvents. For example, for the CHC problem F(1) F(x) /\ F(y) /\ F(z) ==> G(x,y,z) we get the resolvent F(z) ==> G(1,1,z) twice by either resolving F(1) with F(x) first and then F(y) or with F(y) first and then F(x). We now avoid that by always picking the resolved predicates in order, namely: F(x) < F(y) < F(z) This should avoid redundant resolvents as long as the given facts and non-linear CHCs are not already redundant. 2) Also storing clauses in sets now (instead of lists) to automatically filter out (syntactically) redundant clauses. For that also making sure to remove the location variable from constraints when converting ITS rules to Clauses because they otherwise introduce a variable that is unique for every resolvent even if the same two clauses are resolved twice in a row. Thus, we loose easily detectable syntactic redundance. --- benchmarks/LIA.txt | 887 ++++++++++++++++----------------- run_benchmark.sh | 19 +- src/analysis/reachability.cpp | 56 +-- src/analysis/reachability.hpp | 8 +- src/its/itsproblem.cpp | 78 ++- src/its/itsproblem.hpp | 4 +- src/lib/expr/boolexpr.hpp | 2 + src/nonlinear/clause.cpp | 38 ++ src/nonlinear/clause.hpp | 5 +- src/nonlinear/linearsolver.hpp | 8 +- src/nonlinear/nonlinear.cpp | 155 ++++-- src/nonlinear/nonlinear.hpp | 6 +- 12 files changed, 704 insertions(+), 562 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index b1c4c74dd..3699d81b3 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -1,457 +1,456 @@ -# Z3 ADCL -# -000 sat timeout -001 sat timeout -002 sat timeout -003 sat timeout -004 sat timeout -005 sat timeout -006 sat timeout -007 sat timeout -008 sat timeout -009 sat timeout -010 sat timeout -011 sat timeout -012 sat timeout -013 sat timeout -014 timeout timeout -015 sat timeout -016 timeout timeout -017 sat timeout -018 timeout timeout -019 sat timeout -020 timeout timeout -021 sat timeout -022 sat timeout -023 sat timeout -024 timeout timeout -025 timeout timeout -026 timeout timeout -027 timeout timeout -028 timeout timeout -029 timeout timeout -030 timeout timeout -031 timeout timeout -032 timeout timeout -033 timeout timeout -034 timeout timeout -035 timeout timeout -036 timeout timeout -037 timeout timeout -038 timeout timeout -039 timeout timeout -040 timeout timeout -041 timeout timeout -042 timeout timeout -043 sat timeout -044 timeout timeout -045 timeout timeout -046 timeout timeout -047 timeout timeout -048 sat timeout -049 unsat timeout -050 timeout timeout -051 sat timeout -052 timeout timeout -053 timeout timeout -054 timeout timeout -055 timeout timeout -056 timeout timeout -057 timeout timeout -058 timeout timeout -059 sat timeout -060 unsat unsat -061 sat timeout -062 unsat unsat -063 unsat unsat -064 sat timeout -065 sat timeout -066 sat timeout -067 unsat unsat -068 sat timeout -069 unsat timeout -070 sat timeout -071 sat unknown -072 unsat unsat -073 unsat timeout -074 sat timeout -075 unsat unsat -076 unsat timeout -077 unsat timeout -078 sat timeout -079 sat timeout -080 unsat timeout -081 unsat timeout -082 sat timeout -083 sat timeout -084 sat timeout -085 sat timeout -086 unsat timeout -087 timeout timeout -088 sat timeout -089 sat timeout -090 unsat timeout -091 sat timeout +### Z3 ADCL +000 sat timeout +001 sat timeout +002 sat timeout +003 sat timeout +004 sat timeout +005 sat timeout +006 sat timeout +007 sat timeout +008 sat timeout +009 sat timeout +010 sat timeout +011 sat timeout +012 sat timeout +013 sat timeout +014 timeout timeout +015 sat timeout +016 timeout timeout +017 sat timeout +018 timeout timeout +019 sat timeout +020 timeout unknown +021 sat unknown +022 sat timeout +023 sat timeout +024 timeout timeout +025 timeout timeout +026 timeout timeout +027 timeout timeout +028 timeout timeout +029 timeout timeout +030 timeout timeout +031 timeout timeout +032 timeout timeout +033 timeout timeout +034 timeout unknown +035 timeout timeout +036 timeout timeout +037 timeout unknown +038 timeout timeout +039 timeout unknown +040 timeout timeout +041 timeout unknown +042 timeout unknown +043 sat timeout +044 timeout unknown +045 timeout unknown +046 timeout unknown +047 timeout timeout +048 sat timeout +049 unsat timeout +050 timeout timeout +051 sat unknown +052 timeout timeout +053 timeout timeout +054 timeout timeout +055 timeout timeout +056 timeout timeout +057 timeout timeout +058 timeout unknown +059 sat timeout +060 unsat unsat +061 sat timeout +062 unsat unsat +063 unsat unsat +064 sat timeout +065 sat unknown +066 sat unknown +067 unsat unsat +068 sat timeout +069 unsat unsat +070 sat timeout +071 sat unknown +072 unsat unsat +073 unsat timeout +074 sat timeout +075 unsat unsat +076 unsat timeout +077 unsat timeout +078 sat timeout +079 sat timeout +080 unsat timeout +081 unsat timeout +082 sat timeout +083 sat timeout +084 sat timeout +085 sat timeout +086 unsat timeout +087 timeout timeout +088 sat timeout +089 sat timeout +090 unsat timeout +091 sat timeout 092 timeout timeout -093 unsat timeout -094 timeout timeout -095 sat timeout -096 timeout unsat -097 timeout unsat -098 unsat timeout -099 sat timeout -100 sat timeout -101 timeout timeout -102 timeout timeout -103 unsat unsat -104 unsat unsat -105 unsat unsat +093 unsat timeout +094 timeout timeout +095 sat timeout +096 timeout unsat +097 timeout unsat +098 unsat timeout +099 sat timeout +100 sat timeout +101 timeout timeout +102 timeout timeout +103 unsat unsat +104 unsat unsat +105 unsat unsat 106 unsat unsat -107 sat timeout +107 sat timeout 108 sat timeout -109 unsat unsat -110 unsat unsat -111 unsat unsat -112 unsat timeout -113 unsat unsat -114 unsat unsat -115 sat timeout -116 sat timeout -117 sat timeout -118 sat timeout -119 timeout timeout -120 sat timeout -121 sat timeout -122 timeout timeout -123 timeout timeout -124 sat timeout -125 sat timeout -126 timeout timeout -127 sat timeout -128 sat timeout -129 sat timeout -130 timeout timeout -131 timeout timeout -132 timeout timeout -133 unsat timeout -134 unsat timeout -135 sat timeout -136 unsat timeout -137 unsat timeout -138 sat timeout -139 unsat timeout -140 sat timeout -141 unsat timeout -142 unsat unsat -143 sat timeout -144 sat timeout -145 unsat timeout -146 sat timeout -147 unsat timeout -148 unsat timeout -149 sat timeout -150 unsat timeout -151 sat timeout -152 unsat timeout -153 sat timeout -154 sat timeout -155 sat timeout -156 sat timeout -157 unsat timeout -158 unsat timeout -159 sat timeout -160 sat timeout -161 sat timeout -162 sat timeout -163 sat timeout -164 unsat timeout -165 unsat timeout -166 sat timeout -167 sat timeout -168 unsat timeout -169 timeout timeout -170 timeout timeout -171 timeout timeout -172 sat timeout -173 timeout timeout -174 timeout timeout -175 timeout timeout -176 timeout timeout -177 timeout timeout -178 sat timeout -179 timeout timeout -180 sat timeout -181 timeout timeout -182 sat timeout -183 timeout timeout -184 sat timeout -185 unknown timeout -186 sat timeout -187 timeout timeout -188 timeout timeout -189 timeout timeout -190 sat timeout -191 timeout timeout -192 sat timeout -193 sat timeout -194 timeout timeout -195 timeout timeout -196 timeout timeout -197 sat timeout -198 unsat unsat -199 unsat unsat -200 sat timeout -201 sat timeout -202 sat unknown -203 unsat unsat -204 sat timeout -205 sat timeout -206 sat timeout -207 sat timeout -208 sat timeout -209 timeout timeout -210 sat timeout -211 unsat unsat -212 timeout timeout -213 sat timeout -214 timeout timeout -215 timeout timeout -216 sat timeout -217 timeout timeout -218 timeout timeout -219 sat timeout -220 timeout timeout -221 sat timeout -222 sat timeout -223 sat timeout -224 sat timeout -225 timeout timeout -226 timeout timeout -227 sat timeout -228 sat timeout -229 sat timeout -230 sat timeout -231 sat timeout -232 sat timeout -233 sat timeout -234 sat timeout +109 unsat unsat +110 unsat unsat +111 unsat unsat +112 unsat timeout +113 unsat unsat +114 unsat unsat +115 sat timeout +116 sat timeout +117 sat timeout +118 sat timeout +119 timeout unsat +120 sat timeout +121 sat timeout +122 timeout timeout +123 timeout timeout +124 sat timeout +125 sat timeout +126 timeout unsat +127 sat timeout +128 sat timeout +129 sat timeout +130 timeout unsat +131 timeout timeout +132 timeout timeout +133 unsat unknown +134 unsat timeout +135 sat timeout +136 unsat timeout +137 unsat timeout +138 sat timeout +139 unsat unknown +140 sat timeout +141 unsat timeout +142 unsat unsat +143 sat timeout +144 sat timeout +145 unsat timeout +146 sat timeout +147 unsat timeout +148 unsat unknown +149 sat timeout +150 unsat timeout +151 sat timeout +152 unsat unknown +153 sat timeout +154 sat timeout +155 sat timeout +156 sat timeout +157 unsat timeout +158 unsat timeout +159 sat unknown +160 sat timeout +161 sat timeout +162 sat timeout +163 sat timeout +164 unsat timeout +165 unsat timeout +166 sat timeout +167 sat timeout +168 unsat timeout +169 timeout timeout +170 timeout timeout +171 timeout timeout +172 sat timeout +173 timeout timeout +174 timeout timeout +175 timeout timeout +176 timeout timeout +177 timeout timeout +178 sat timeout +179 timeout timeout +180 sat timeout +181 timeout timeout +182 sat timeout +183 timeout timeout +184 sat timeout +185 unknown timeout +186 sat timeout +187 timeout timeout +188 timeout timeout +189 timeout timeout +190 sat timeout +191 timeout timeout +192 sat timeout +193 sat timeout +194 timeout timeout +195 timeout timeout +196 timeout timeout +197 sat timeout +198 unsat unsat +199 unsat unsat +200 sat timeout +201 sat unknown +202 sat unknown +203 unsat unsat +204 sat timeout +205 sat timeout +206 sat timeout +207 sat timeout +208 sat timeout +209 timeout unknown +210 sat timeout +211 unsat unsat +212 timeout unknown +213 sat timeout +214 timeout timeout +215 timeout timeout +216 sat unknown +217 timeout timeout +218 timeout timeout +219 sat timeout +220 timeout timeout +221 sat timeout +222 sat timeout +223 sat unknown +224 sat unknown +225 timeout timeout +226 timeout timeout +227 sat timeout +228 sat timeout +229 sat timeout +230 sat unknown +231 sat timeout +232 sat unknown +233 sat timeout +234 sat timeout 235 sat timeout 236 sat timeout -237 sat timeout -238 sat timeout -239 timeout timeout +237 sat unknown +238 sat timeout +239 timeout unknown 240 timeout unknown 241 timeout unknown 242 timeout timeout 243 timeout timeout 244 sat timeout -245 unsat timeout -246 sat timeout -247 unsat timeout -248 sat timeout -249 unsat unsat -250 sat timeout -251 sat timeout -252 sat timeout -253 unsat timeout -254 sat timeout -255 sat timeout -256 unsat timeout -257 sat timeout -258 sat unknown -259 unsat timeout -260 sat timeout -261 sat unknown -262 sat unknown -263 unsat timeout -264 unsat timeout -265 timeout timeout -266 unsat timeout -267 timeout timeout -268 unsat timeout -269 sat timeout -270 sat timeout -271 unsat timeout -272 sat unknown -273 unsat timeout -274 timeout timeout -275 unsat timeout -276 unsat timeout -277 unsat timeout -278 sat timeout -279 unsat timeout -280 unsat timeout -281 timeout timeout -282 timeout timeout -283 unsat timeout -284 timeout timeout -285 timeout timeout -286 unsat timeout -287 timeout timeout -288 sat timeout -289 unsat timeout -290 timeout timeout -291 sat timeout -292 timeout timeout -293 sat timeout -294 timeout timeout -295 unsat timeout -296 timeout timeout -297 unsat timeout -298 timeout timeout -299 sat timeout -300 timeout timeout -301 unsat timeout -302 timeout timeout -303 timeout timeout -304 timeout timeout -305 unsat timeout -306 timeout timeout -307 unsat timeout -308 timeout timeout -309 timeout timeout -310 timeout timeout -311 timeout timeout -312 sat timeout -313 timeout timeout -314 unsat timeout -315 timeout timeout -316 timeout timeout -317 unsat timeout -318 sat timeout -319 unsat timeout -320 sat timeout -321 timeout timeout -322 timeout timeout -323 sat timeout -324 sat timeout -325 timeout timeout -326 unsat timeout -327 timeout timeout -328 unsat timeout -329 sat timeout -330 timeout timeout -331 timeout timeout -332 timeout timeout -333 unsat timeout -334 sat timeout -335 sat timeout -336 unsat timeout -337 sat timeout -338 unsat unsat -339 unsat unsat -340 sat timeout -341 sat timeout -342 unsat error -343 unsat timeout -344 unsat timeout -345 unsat timeout -346 sat timeout -347 sat timeout -348 sat timeout -349 unsat unsat -350 unsat unsat -351 unsat timeout -352 sat timeout -353 unsat timeout -354 unsat timeout +245 unsat timeout +246 sat timeout +247 unsat timeout +248 sat timeout +249 unsat unsat +250 sat timeout +251 sat timeout +252 sat timeout +253 unsat timeout +254 sat timeout +255 sat timeout +256 unsat timeout +257 sat timeout +258 sat unknown +259 unsat timeout +260 sat timeout +261 sat unknown +262 sat unknown +263 unsat timeout +264 unsat timeout +265 timeout timeout +266 unsat timeout +267 timeout timeout +268 unsat timeout +269 sat timeout +270 sat timeout +271 unsat timeout +272 sat unknown +273 unsat timeout +274 timeout timeout +275 unsat timeout +276 unsat timeout +277 unsat timeout +278 sat timeout +279 unsat timeout +280 unsat timeout +281 timeout timeout +282 timeout timeout +283 unsat timeout +284 timeout timeout +285 timeout timeout +286 unsat timeout +287 timeout timeout +288 sat timeout +289 unsat timeout +290 timeout timeout +291 sat timeout +292 timeout timeout +293 sat timeout +294 timeout timeout +295 unsat timeout +296 timeout timeout +297 unsat timeout +298 timeout timeout +299 sat timeout +300 timeout timeout +301 unsat timeout +302 timeout timeout +303 timeout timeout +304 timeout timeout +305 unsat timeout +306 timeout timeout +307 unsat timeout +308 timeout timeout +309 timeout timeout +310 timeout timeout +311 timeout timeout +312 sat timeout +313 timeout timeout +314 unsat timeout +315 timeout timeout +316 timeout timeout +317 unsat timeout +318 sat timeout +319 unsat timeout +320 sat timeout +321 timeout timeout +322 timeout timeout +323 sat timeout +324 sat timeout +325 timeout timeout +326 unsat timeout +327 timeout timeout +328 unsat timeout +329 sat timeout +330 timeout timeout +331 timeout timeout +332 timeout timeout +333 unsat timeout +334 sat timeout +335 sat timeout +336 unsat unsat +337 sat timeout +338 unsat unsat +339 unsat unsat +340 sat timeout +341 sat timeout +342 unsat unsat +343 unsat timeout +344 unsat timeout +345 unsat unknown +346 sat timeout +347 sat timeout +348 sat timeout +349 unsat unsat +350 unsat unsat +351 unsat timeout +352 sat timeout +353 unsat timeout +354 unsat timeout 355 sat timeout -356 unsat timeout -357 timeout timeout -358 sat timeout -359 sat timeout -360 sat timeout -361 timeout timeout -362 sat timeout -363 sat timeout -364 unsat timeout -365 sat timeout -366 sat timeout -367 sat timeout -368 sat timeout -369 sat timeout -370 sat timeout -371 sat timeout -372 sat timeout -373 sat timeout -374 sat timeout -375 sat timeout -376 sat timeout -377 sat timeout -378 sat timeout -379 sat timeout -380 sat timeout -381 sat timeout -382 sat timeout -383 sat timeout -384 sat timeout -385 sat timeout -386 sat timeout -387 unsat timeout -388 sat timeout -389 unsat timeout -390 sat timeout -391 sat timeout -392 sat timeout -393 unsat timeout -394 sat timeout -395 sat timeout -396 sat timeout -397 sat timeout -398 sat timeout -399 unsat timeout -400 unsat timeout -401 sat timeout -402 timeout timeout -403 timeout unsat -404 timeout timeout -405 timeout timeout -406 timeout timeout -407 sat timeout -408 sat timeout -409 unsat timeout -410 sat timeout -411 unsat timeout -412 sat timeout -413 sat timeout -414 unsat timeout -415 sat timeout -416 sat timeout -417 sat timeout -418 sat timeout -419 unsat timeout -420 sat timeout -421 sat timeout -422 unsat timeout -423 unsat timeout -424 sat timeout -425 sat timeout -426 unsat timeout -427 unsat timeout -428 sat timeout -429 unsat timeout -430 unsat timeout -431 unsat timeout -432 sat timeout -433 sat timeout -434 unsat timeout -435 sat timeout -436 unsat unsat -437 unsat timeout +356 unsat timeout +357 timeout timeout +358 sat timeout +359 sat timeout +360 sat timeout +361 timeout timeout +362 sat timeout +363 sat timeout +364 unsat timeout +365 sat timeout +366 sat timeout +367 sat timeout +368 sat timeout +369 sat timeout +370 sat timeout +371 sat timeout +372 sat timeout +373 sat timeout +374 sat timeout +375 sat timeout +376 sat timeout +377 sat unknown +378 sat timeout +379 sat timeout +380 sat timeout +381 sat timeout +382 sat timeout +383 sat timeout +384 sat timeout +385 sat timeout +386 sat timeout +387 unsat timeout +388 sat timeout +389 unsat timeout +390 sat timeout +391 sat timeout +392 sat timeout +393 unsat timeout +394 sat timeout +395 sat timeout +396 sat timeout +397 sat timeout +398 sat timeout +399 unsat timeout +400 unsat timeout +401 sat timeout +402 timeout timeout +403 timeout timeout +404 timeout timeout +405 timeout timeout +406 timeout timeout +407 sat timeout +408 sat timeout +409 unsat timeout +410 sat timeout +411 unsat timeout +412 sat timeout +413 sat timeout +414 unsat timeout +415 sat timeout +416 sat timeout +417 sat timeout +418 sat timeout +419 unsat timeout +420 sat timeout +421 sat timeout +422 unsat timeout +423 unsat timeout +424 sat timeout +425 sat timeout +426 unsat timeout +427 unsat timeout +428 sat timeout +429 unsat timeout +430 unsat timeout +431 unsat timeout +432 sat timeout +433 sat timeout +434 unsat timeout +435 sat timeout +436 unsat unsat +437 unsat timeout 438 timeout timeout -439 sat timeout -440 sat timeout -441 sat timeout -442 unsat timeout -443 sat timeout -444 unsat timeout -445 sat timeout -446 sat timeout -447 sat timeout -448 unsat timeout -449 sat timeout -450 timeout timeout -451 unsat timeout -452 unsat timeout -453 sat timeout +439 sat timeout +440 sat timeout +441 sat timeout +442 unsat timeout +443 sat timeout +444 unsat timeout +445 sat timeout +446 sat timeout +447 sat timeout +448 unsat timeout +449 sat timeout +450 timeout timeout +451 unsat timeout +452 unsat timeout +453 sat timeout 454 sat timeout diff --git a/run_benchmark.sh b/run_benchmark.sh index 10cc8bea2..4054261f8 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -12,24 +12,14 @@ pushd build make -j4 popd - -# 074 -# 106 -# 109 -# 110 -# 111 -# 113 -# 114 - -# TODO: debug 342 (seg fault) +# TODO: debug 335 (seg fault) # # gdb --args \ # ./build/loat-static \ # --mode reachability \ # --format horn \ # --proof-level 0 \ -# --log \ -# "../chc-comp22-benchmarks/LIA/chc-LIA_074.smt2" +# "../chc-comp22-benchmarks/LIA/chc-LIA_113.smt2" # popd # exit @@ -49,8 +39,9 @@ do read idx z3_result adcl_result <<< "$line" file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" - # if true; then - if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then + if true; then + # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then + # if [[ "$adcl_result" == "unsat" ]]; then set +e result=$(timeout 5 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") # result=$(timeout 5 z3 "$file") diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index b8f6f85fa..8b12ac1f7 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -777,40 +777,18 @@ const std::optional Reachability::trace_as_fact() { return {}; } else { const Step step = trace.back(); - - // TODO: this has quite some duplication with `ITS::clauseFrom` - std::vector guard_conj = { step.resolvent.getGuard() }; - std::vector args_renamed; - const auto subs = step.resolvent.getUpdate(); - - for (const Var &var : chcs.getProgVars()) { - auto it = subs.find(var); - - if (it == subs.end()) { - args_renamed.push_back(var); - } else { - const auto optional_var = expr::toVar(expr::second(*it)); - - if (optional_var.has_value()) { - args_renamed.push_back(optional_var.value()); - } else { - const auto new_var = expr::next(var); - args_renamed.push_back(new_var); - guard_conj.push_back(expr::mkEq(expr::toExpr(new_var), subs.get(var))); - } - } - } - - const auto guard = BExpression::buildAnd(guard_conj); - const auto rhs = FunApp(chcs.getRhsLoc(step.clause_idx), args_renamed); - return Clause({}, rhs, guard); + return chcs.clauseFrom( + chcs.getInitialLocation(), + chcs.getRhsLoc(step.clause_idx), + step.resolvent + ); } } -const std::list Reachability::derive_new_facts() { +const std::set Reachability::derive_new_facts() { static std::default_random_engine rnd {}; - std::list derived_facts; + std::set derived_facts; if (try_to_finish()) { return derived_facts; @@ -881,8 +859,13 @@ const std::list Reachability::derive_new_facts() { )); } - if (!seen_traces.contains(trace_id)) { - derived_facts.push_back(trace_as_fact().value()); + // Forwarding initial facts is redundant, because they are already obtained by calling `get_initial_facts`. + // TODO: Remove `get_initial_facts` and force non linear solver to get inital facts from first round of + // calling `derive_new_facts`. + bool is_initial_fact = trace.size() == 1 && chcs.isInitialTransition(trace[0].clause_idx); + + if (!seen_traces.contains(trace_id) && !is_initial_fact) { + derived_facts.insert(trace_as_fact().value()); seen_traces.insert(trace_id); } } @@ -939,7 +922,7 @@ void Reachability::restart() { analysis_result = LinearSolver::Result::Pending; } -void Reachability::add_clauses(const std::list &clauses) { +void Reachability::add_clauses(const std::set &clauses) { bool any_linear_clauses = false; for (const auto &chc : clauses) { chcs.addClause(chc); @@ -969,16 +952,15 @@ void Reachability::add_clauses(const std::list &clauses) { } } -const std::list Reachability::get_initial_facts() const { - std::list facts; +const std::set Reachability::get_initial_facts() const { + std::set facts; for (const auto trans_idx : chcs.getInitialTransitions()) { - const Clause fact = chcs.clauseFrom(trans_idx); - facts.push_back(fact); + facts.insert(chcs.clauseFrom(trans_idx)); } return facts; } -const std::list Reachability::get_non_linear_chcs() const { +const std::set Reachability::get_non_linear_chcs() const { return chcs.nonLinearCHCs; } diff --git a/src/analysis/reachability.hpp b/src/analysis/reachability.hpp index 4fe6b9107..b75d8ded2 100644 --- a/src/analysis/reachability.hpp +++ b/src/analysis/reachability.hpp @@ -319,13 +319,13 @@ class Reachability : public ILinearSolver { LinearSolver::Result get_analysis_result() const override; - void add_clauses(const std::list &chc) override; + void add_clauses(const std::set &chc) override; - const std::list derive_new_facts() override; + const std::set derive_new_facts() override; - const std::list get_initial_facts() const override; + const std::set get_initial_facts() const override; - const std::list get_non_linear_chcs() const override; + const std::set get_non_linear_chcs() const override; static void analyze(ITSProblem &its); diff --git a/src/its/itsproblem.cpp b/src/its/itsproblem.cpp index 467ae99ab..8eea3119b 100644 --- a/src/its/itsproblem.cpp +++ b/src/its/itsproblem.cpp @@ -18,6 +18,7 @@ #include "itsproblem.hpp" #include "export.hpp" #include "expr.hpp" +#include "inttheory.hpp" #include "smtfactory.hpp" #include "chain.hpp" @@ -272,26 +273,58 @@ const std::vector ITSProblem::getProgVars() const { return prog_vars; } - -/** - * Converts an ITS rule (identified by a TransIdx) back to Clause representation. - * This does not restore the original representation after parsing perfectly, - * since number and order of predicate arguments is lost. +/** + * Note that `clauseFrom(TransIdx rule)` can only be used for rules that are stored + * in the ITS. Not to turn an entire trace into a clause, because `ITSProblem::getLhsLoc` + * and `ITSProblem::getRhsLoc` are only defined for rules that are actually in the ITS. + * Thus, here is a generalized version of `clauseFrom` that accepts the lhs/rhs location + * explicitly. */ -const Clause ITSProblem::clauseFrom(TransIdx rule) const { +const Clause ITSProblem::clauseFrom(const LocationIdx lhs_loc, const LocationIdx rhs_loc, const Rule& rule) const { const auto prog_vars = getProgVars(); - - const LocationIdx lhs_loc = getLhsLoc(rule); - const LocationIdx rhs_loc = getRhsLoc(rule); - - const auto update = rule->getUpdate(); + const auto update = rule.getUpdate(); + + // First we eliminate the location variable from the guard. In the ITS context, + // the location variable encoded the lhs predicate. But in clauses the lhs + // predicate is carried explicitly. Thus, the location variable turns into + // a superfluous temporary variable in the constraint, which causes subsequent + // calls of Clause::resolutionWith to give different results even on same + // arguments. For example, computing the resolvent of + // + // (fact_F) i1=0 ==> F(y) + // (rule_F) i1=1 /\ F(x) ==> F(x) + // + // where `i1` is the location variable, gives: + // + // F(x) /\ i1=0 /\ i2=0 ==> F(x) + // + // because `i1` appeared in both clauses and had to be renamed in `fact_F` to + // make the variables in the clauses disjoint. If we do the same computation + // again we get: + // + // F(x) /\ i1=0 /\ i3=0 ==> F(x) + // + // with `i3` instead of `i2`, because new variables are created using a global + // counter and `i2` already exists. Thus, the resolvents are not syntactially + // equal, which leads to otherwise trivially detectable redundancy. + // + // To eliminate the location variable, we substitute `i1` with it's value to + // make the literate trivially true, which simplifies away when we later call + // `simplify()` on the guard, i.e. we substitute + // + // (i1=0 ==> F(y))[i1/0] + // = ( 0=0 ==> F(y)) + // = (true ==> F(y)) + // + const auto eliminate_loc_var = Subs::build(NumVar::loc_var, lhs_loc); + const auto guard_without_loc_var = rule.getGuard()->subs(eliminate_loc_var); // `update` might map vars to non-var compound expressions or literals. // The `Clause` representation only allows variable arguments though for the // RHS predicate. So we extract those expressions and put them back into the // clause guard. This is a bit ad-hoc and also un-does work of the linear // solver and the preprocessing, so it might be worth optimizing later. - std::vector guard_conj = { rule->getGuard() }; + std::vector guard_conj = { guard_without_loc_var }; std::vector rhs_args; for (const Var &var: prog_vars) { auto it = update.find(var); @@ -314,18 +347,29 @@ const Clause ITSProblem::clauseFrom(TransIdx rule) const { } } - const auto guard = BExpression::buildAnd(guard_conj); - const auto rhs = FunApp(rhs_loc, rhs_args); + const auto final_guard = BExpression::buildAnd(guard_conj)->simplify(); + const auto rhs = FunApp(rhs_loc, rhs_args); if (lhs_loc == getInitialLocation()) { // rule is a linear CHC with no LHS predicates, ie a "fact" - return Clause({}, rhs, guard); + return Clause({}, rhs, final_guard); } else { // rule is a linear CHC with exactly one LHS predicates, ie a "rule" - return Clause({ FunApp(lhs_loc, prog_vars) }, rhs, guard); + return Clause({ FunApp(lhs_loc, prog_vars) }, rhs, final_guard); } } +/** + * Converts an ITS rule (identified by a TransIdx) back to Clause representation. + * This does not restore the original representation after parsing perfectly, + * since number and order of predicate arguments is lost. + */ +const Clause ITSProblem::clauseFrom(TransIdx rule) const { + const LocationIdx lhs_loc = getLhsLoc(rule); + const LocationIdx rhs_loc = getRhsLoc(rule); + return clauseFrom(lhs_loc, rhs_loc, *rule); +} + /** * TODO docs */ @@ -427,7 +471,7 @@ void ITSProblem::addClause(const Clause &c) { lhs_normalized.insert(pred_normalized); } - nonLinearCHCs.push_back(Clause( + nonLinearCHCs.insert(Clause( lhs_normalized, normalizePredicate(numProgVars.size(), boolProgVars.size(), c.rhs), c.guard->simplify() diff --git a/src/its/itsproblem.hpp b/src/its/itsproblem.hpp index aaf93bc74..e6ea00459 100644 --- a/src/its/itsproblem.hpp +++ b/src/its/itsproblem.hpp @@ -115,12 +115,14 @@ class ITSProblem { std::vector boolProgVars; const std::vector getProgVars() const; - std::list nonLinearCHCs; + std::set nonLinearCHCs; void addClause(const Clause &chc); const Clause clauseFrom(TransIdx trans_idx) const; + const Clause clauseFrom(const LocationIdx lhs_loc, const LocationIdx rhs_loc, const Rule& rule) const; + protected: DG graph; diff --git a/src/lib/expr/boolexpr.hpp b/src/lib/expr/boolexpr.hpp index 71ab6c13a..613dec1ab 100644 --- a/src/lib/expr/boolexpr.hpp +++ b/src/lib/expr/boolexpr.hpp @@ -435,6 +435,7 @@ class BoolExpression: public std::enable_shared_from_this> }); } + // TODO: extend for program variables Subs impliedEqualities() const { Subs res; std::vector todo; @@ -442,6 +443,7 @@ class BoolExpression: public std::enable_shared_from_this> std::optional elim; const auto vars {c->vars().template get()}; for (const auto &x: vars) { + // TODO: here if (x.isTempVar()) { if (elim) { return std::optional{}; diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index 6904b1504..af40b8631 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -3,6 +3,7 @@ #include "expr.hpp" #include "theory.hpp" #include +#include #include /** @@ -207,6 +208,25 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA ); } +/** + * Partition clauses into linear- and non-linear. The first tuple component holds the linear + * clauses while second component holds the non-linear clauses. + */ +const std::tuple, std::set> partitionByDegree(const std::set chcs) { + std::set linear; + std::set non_linear; + + for (const Clause& chc: chcs) { + if (chc.isLinear()) { + linear.insert(chc); + } else { + non_linear.insert(chc); + } + } + + return std::make_tuple(linear, non_linear); +} + /** * Returns true iff the clause has at most one LHS predicate. */ @@ -224,6 +244,24 @@ bool operator<(const FunApp &fun1, const FunApp &fun2) { } } +bool operator<(const Clause &c1, const Clause &c2) { + if (c1.lhs < c2.lhs) { + return true; + } else if (c2.lhs > c1.lhs) { + return false; + } else if (c1.rhs < c2.rhs) { + return true; + } else if (c2.rhs < c1.rhs) { + return false; + } else if (c1.guard < c2.guard) { + return true; + } else if (c2.guard < c1.guard) { + return false; + } else { + return false; + } +} + std::ostream &operator<<(std::ostream &s, const FunApp &fun) { s << "F" << fun.loc; diff --git a/src/nonlinear/clause.hpp b/src/nonlinear/clause.hpp index 69480cab1..e666d56f7 100644 --- a/src/nonlinear/clause.hpp +++ b/src/nonlinear/clause.hpp @@ -48,7 +48,10 @@ class Clause { }; -// implement comparison operator for FunApp so we can store them in std::set +const std::tuple, std::set> partitionByDegree(const std::set chcs); + +// implement comparison operators so they can be stored in std::set +bool operator<(const Clause &c1, const Clause &c2); bool operator<(const FunApp &fun1, const FunApp &fun2); std::ostream& operator<<(std::ostream &s, const FunApp &fun_app); diff --git a/src/nonlinear/linearsolver.hpp b/src/nonlinear/linearsolver.hpp index b62225032..b07412c78 100644 --- a/src/nonlinear/linearsolver.hpp +++ b/src/nonlinear/linearsolver.hpp @@ -23,7 +23,7 @@ class ILinearSolver { * Let linear solver derive all (ideally non-redundant) facts it can derive with * the current linear clause set. */ - virtual const std::list derive_new_facts() = 0; + virtual const std::set derive_new_facts() = 0; virtual LinearSolver::Result get_analysis_result() const = 0; @@ -31,10 +31,10 @@ class ILinearSolver { * Adds a new clauses to the linear solver in batch. The added clauses can be * linear or non-linear. */ - virtual void add_clauses(const std::list &clauses) = 0; + virtual void add_clauses(const std::set &clauses) = 0; - virtual const std::list get_initial_facts() const = 0; + virtual const std::set get_initial_facts() const = 0; - virtual const std::list get_non_linear_chcs() const = 0; + virtual const std::set get_non_linear_chcs() const = 0; }; diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 54c1130aa..9bc49a4c7 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -1,6 +1,7 @@ #include "nonlinear.hpp" #include "booltheory.hpp" #include "config.hpp" +#include "expr.hpp" #include "linearizingsolver.hpp" #include "linearsolver.hpp" #include "smt.hpp" @@ -13,64 +14,50 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { // For the first loop iteration, the list of facts is composed of the original facts // given in the CHC problem. In subsequent iterations, the facts are whatever // the linear solver managed to derive. - std::list facts(linear_solver.get_initial_facts()); + std::set facts(linear_solver.get_initial_facts()); while (true) { - if (Config::Analysis::log) { std::cout << "============= non-linear solver main loop =============" << std::endl; } - std::list resolvents; - std::list non_linear_chcs = linear_solver.get_non_linear_chcs(); + const std::set non_linear_chcs(linear_solver.get_non_linear_chcs()); // Do resolution with all combinations of facts and non-linear clauses to // get every possible linear clause that we can derive in this iteration. - for (const Clause &non_linear_chc: non_linear_chcs) { - for (const auto &fact: facts) { - for (const auto &pred: non_linear_chc.lhs) { - const auto optional_resolvent = fact.resolutionWith(non_linear_chc, pred); - - if (optional_resolvent.has_value()) { - const auto resolvent = optional_resolvent.value(); - // TODO: check for redundancy - // maybe later dont reject resolvent on `Unknown`? - if (SmtFactory::check(resolvent.guard) == Sat) { - resolvents.push_back(resolvent); - if (!resolvent.isLinear()) { - // std::cout << "new non-linear: " << resolvent << std::endl; - // Note that we append items to `non_linear_chcs` while iterating over it. - // That means we also iterate over all added items. - non_linear_chcs.push_back(resolvent); - } - - if (Config::Analysis::log) { - if (resolvent.isLinear()) { - std::cout << "new linear: " << resolvent << std::endl; - } else { - std::cout << "new non-linear: " << resolvent << std::endl; - } - } - } - } - } - } + std::list resolvents; + for (const auto& non_linear_chc: linear_solver.get_non_linear_chcs()) { + const auto res(all_resolvents(non_linear_chc, facts)); + resolvents.insert( + resolvents.end(), + res.begin(), + res.end() + ); + } + + const std::set resolvents_distinct(resolvents.begin(), resolvents.end()); + + if (Config::Analysis::log) { + std::cout + << (resolvents.size() - resolvents_distinct.size()) + << " of " + << resolvents.size() + << " resolvents are syntactially redundant" + << std::endl; } - // TODO: saw this constraint: (-2+i3 == 0 /\ 2-i3 == 0). Could be simplified more. + linear_solver.add_clauses(resolvents_distinct); - linear_solver.add_clauses(resolvents); + // std::cout << "facts : " << facts.size() << std::endl; + // std::cout << "non linear : " << non_linear_chcs.size() << std::endl; facts.clear(); const auto new_facts = linear_solver.derive_new_facts(); - facts.insert(facts.end(), new_facts.begin(), new_facts.end()); + facts.insert(new_facts.begin(), new_facts.end()); if (Config::Analysis::log) { for (const auto &fact: new_facts) { std::cout << "new fact: " << fact << std::endl; - // NOTE: seeing facts that are redundant up to renaming: - // new fact: (-1+i1 == 0 /\ -1+it15 < 0 /\ -3+it59 == 0 /\ -2+it60-it15 == 0) ==> F2(it59,it60) - // new fact: (-1+i1 == 0 /\ -2+it35 < 0 /\ -1+it66-it35 == 0 /\ -3+it65 == 0) ==> F2(it65,it66) } } @@ -96,3 +83,93 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { throw std::logic_error("exited main loop, although linear solver is still pending"); } } + + +/** + * Compute all resolvents of `chc` with `facts`, while trying to avoid redundant derivations. + * For example, let `F(1)` be the only fact and `chc` be `F(x) /\ F(y) /\ F(z) ==> G(x,y,z)` + * then a redundant derivation would be: + * + * F(x) /\ F(y) /\ F(z) ==> G(x,y,z) + * + * / \ + * + * F(x) /\ F(y) ==> G(x,y,1) F(x) /\ F(z) ==> G(x,1,z) + * + * | | + * + * F(x) ==> G(x,1,1) F(x) ==> G(x,1,1) + * + * The returned clause list should not contain any redundant clauses as long as the given + * `facts` are not redundant. The returned list should also not contain `chc` itself and + * no facts (i.e. clauses with no LHS predicates). + */ +const std::list all_resolvents(const Clause& chc, const std::set& facts) { + return all_resolvents_aux(chc, chc.lhs.begin(), facts); +} +const std::list all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts) { + // === Running example === + // `chc` : F(x) /\ F(y) /\ F(z) ==> G(x,y,z) + // `preds` : ^---- iterator pointing on first predicate of `chc` + // `facts` : [F(1), F(2)] + + if (preds == chc.lhs.end() || chc.isLinear()) { + return {}; + } else { + const auto current_pred = *preds; // == F(x) + + // All resolvents that DONT eliminate F(x) + const auto res_with_head = all_resolvents_aux(chc, std::next(preds), facts); + // F(x) /\ F(y) ==> G(x,y,1) + // F(x) /\ F(y) ==> G(x,y,2) + // + // F(x) /\ F(z) ==> G(x,1,z) + // F(x) ==> G(x,1,1) + // F(x) ==> G(x,1,2) + // + // F(x) /\ F(z) ==> G(x,2,z) + // F(x) ==> G(x,2,1) + // F(x) ==> G(x,2,2) + + // All resolvents that DO eliminate F(x) + std::list res_without_head; + for (const auto& fact: facts) { + const auto optional_resolvent = fact.resolutionWith(chc, current_pred); + + if (optional_resolvent.has_value()) { + const auto resolvent = optional_resolvent.value(); + // for fact = F(1) : F(y) /\ F(z) ==> G(1,y,z) + // + // for fact = F(2) : F(y) /\ F(z) ==> G(2,y,z) + + if (SmtFactory::check(resolvent.guard) == Sat) { + const auto res(all_resolvents_aux(resolvent, resolvent.lhs.begin(), facts)); + // for fact = F(1) : F(y) ==> G(1,y,1) + // F(y) ==> G(1,y,2) + // F(z) ==> G(1,1,z) + // F(z) ==> G(1,2,z) + // + // for fact = F(2) : F(y) ==> G(2,y,1) + // F(y) ==> G(2,y,2) + // F(z) ==> G(2,1,z) + // F(z) ==> G(2,2,z) + + res_without_head.push_back(resolvent); + res_without_head.insert( + res_without_head.begin(), + res.begin(), + res.end() + ); + } + } + } + + // join all collected resolvents and return: + res_without_head.insert( + res_without_head.end(), + res_with_head.begin(), + res_with_head.end() + ); + return res_without_head; + } +} diff --git a/src/nonlinear/nonlinear.hpp b/src/nonlinear/nonlinear.hpp index c9ea1ad5b..4ca7a915d 100644 --- a/src/nonlinear/nonlinear.hpp +++ b/src/nonlinear/nonlinear.hpp @@ -5,10 +5,14 @@ class NonLinearSolver { private: - // NonLinearSolver(); + // NonLinearSolver(); public: static void analyze(ILinearSolver &linear_solver); }; + +const std::list all_resolvents(const Clause& chc, const std::set& facts); + +const std::list all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts); From 1693dbc2a562cc6991d0074a5fec7fe5b1b772c2 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Sun, 12 Nov 2023 20:34:52 +0100 Subject: [PATCH 12/25] propagate program variable equalities Previously, when simplifying boolean expressions, only temporary boolean variables were propagated. For example in (b1 \/ b2) /\ !b2 ==> F(b1) the temporary variable `b2` can be propagated, which simplifies the clause to b1 ==> F(b1) But program variables were not propagated. For example (b1 \/ b2) /\ !b2 ==> F(b1, b2) would not propagate `b2` since it's a program variable. When propagating program variables we have to make sure to not eliminate them completely like temporary variables. Otherwise we loose information. Instead the expected result is: b1 /\ !b2 ==> F(b1, b2) This simplification can speed up ADCL because potentially fewer conjunctive variants of the clause have to generated (observed in LIA instance 69). --- benchmarks/LIA.txt | 22 +++++++++++----------- run_benchmark.sh | 5 ++++- src/analysis/guardtoolbox.cpp | 31 +++++++++++++++++++++++++++++++ src/lib/expr/boolexpr.hpp | 30 +++++++++++++++++++----------- 4 files changed, 65 insertions(+), 23 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index 3699d81b3..b9d200e2b 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -15,9 +15,9 @@ 013 sat timeout 014 timeout timeout 015 sat timeout -016 timeout timeout +016 timeout unknown 017 sat timeout -018 timeout timeout +018 timeout unknown 019 sat timeout 020 timeout unknown 021 sat unknown @@ -33,19 +33,19 @@ 031 timeout timeout 032 timeout timeout 033 timeout timeout -034 timeout unknown +034 timeout error 035 timeout timeout 036 timeout timeout -037 timeout unknown +037 timeout timeout 038 timeout timeout -039 timeout unknown +039 timeout timeout 040 timeout timeout 041 timeout unknown 042 timeout unknown 043 sat timeout 044 timeout unknown 045 timeout unknown -046 timeout unknown +046 timeout timeout 047 timeout timeout 048 sat timeout 049 unsat timeout @@ -114,7 +114,7 @@ 112 unsat timeout 113 unsat unsat 114 unsat unsat -115 sat timeout +115 sat unknown 116 sat timeout 117 sat timeout 118 sat timeout @@ -238,7 +238,7 @@ 236 sat timeout 237 sat unknown 238 sat timeout -239 timeout unknown +239 timeout timeout 240 timeout unknown 241 timeout unknown 242 timeout timeout @@ -255,7 +255,7 @@ 253 unsat timeout 254 sat timeout 255 sat timeout -256 unsat timeout +256 unsat unsat 257 sat timeout 258 sat unknown 259 unsat timeout @@ -398,7 +398,7 @@ 396 sat timeout 397 sat timeout 398 sat timeout -399 unsat timeout +399 unsat unsat 400 unsat timeout 401 sat timeout 402 timeout timeout @@ -453,4 +453,4 @@ 451 unsat timeout 452 unsat timeout 453 sat timeout -454 sat timeout +454 sat timeout diff --git a/run_benchmark.sh b/run_benchmark.sh index 4054261f8..c01aa8d90 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -13,13 +13,15 @@ make -j4 popd # TODO: debug 335 (seg fault) +# TODO: debug 034 (seg fault) # # gdb --args \ # ./build/loat-static \ # --mode reachability \ # --format horn \ # --proof-level 0 \ -# "../chc-comp22-benchmarks/LIA/chc-LIA_113.smt2" +# --log \ +# "../chc-comp22-benchmarks/LIA/chc-LIA_069.smt2" # popd # exit @@ -41,6 +43,7 @@ do if true; then # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then + # if [[ "$z3_result" == "unsat" ]]; then # if [[ "$adcl_result" == "unsat" ]]; then set +e result=$(timeout 5 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") diff --git a/src/analysis/guardtoolbox.cpp b/src/analysis/guardtoolbox.cpp index dc03a35fe..a186bad29 100644 --- a/src/analysis/guardtoolbox.cpp +++ b/src/analysis/guardtoolbox.cpp @@ -16,6 +16,7 @@ */ #include "guardtoolbox.hpp" +#include "expr.hpp" #include "rule.hpp" #include "rel.hpp" @@ -87,14 +88,44 @@ Result GuardToolbox::propagateBooleanEqualities(const Rule &rule) { Result res(rule); Proof subproof; Subs equiv; + + // If the value of program variables is implied by the guard, + // we remove them during propagating, which looses information. + // Thus we collect implied program variable equalities and re-add + // them to the guard at the end. + std::vector program_var_equalities; do { equiv = res->getGuard()->impliedEqualities(); + + // collect implied program variable equalities: + for (const auto &var: equiv.domain()) { + if (expr::isProgVar(var)) { + if (res->getGuard()->countOccuranceOf(var) == 1) { + // when the program variable occurs only once in the expression, + // there is no need to substitute it. This also prevents an + // infinite loop, because when we re-add the program variables to the + // guard using `buildAnd`, we trigger `propagateBooleanEqualities` + // again. + equiv.erase(var); + } else { + program_var_equalities.push_back( + expr::mkEq(expr::toExpr(var), equiv.get(var))->simplify() + ); + } + } + } + if (!equiv.empty()) { res = res->subs(equiv); subproof.append(stringstream() << "propagated equivalences: " << equiv << std::endl); } } while (!equiv.empty()); if (res) { + // re-add program variable equalities to guard: + program_var_equalities.push_back(res->getGuard()); + const auto new_guard = BExpression::buildAnd(program_var_equalities); + res = res->withGuard(new_guard); + res.ruleTransformationProof(rule, "Propagated Equivalences", *res); res.storeSubProof(subproof); } diff --git a/src/lib/expr/boolexpr.hpp b/src/lib/expr/boolexpr.hpp index 613dec1ab..3ee2e5b11 100644 --- a/src/lib/expr/boolexpr.hpp +++ b/src/lib/expr/boolexpr.hpp @@ -12,6 +12,7 @@ #include #include #include +#include @@ -356,6 +357,19 @@ class BoolExpression: public std::enable_shared_from_this> }); } + unsigned countOccuranceOf(const Var& var) const { + unsigned count = 0; + iter([&var,&count](const Lit& lit) { + if (std::holds_alternative(lit) && std::holds_alternative(var)) { + const BoolLit bool_lit = std::get(lit); + if (bool_lit.getBoolVar() == std::get(var)) { + count++; + } + } + }); + return count; + } + template void collectVars(std::set &vars) const { VS res; @@ -435,7 +449,6 @@ class BoolExpression: public std::enable_shared_from_this> }); } - // TODO: extend for program variables Subs impliedEqualities() const { Subs res; std::vector todo; @@ -443,13 +456,10 @@ class BoolExpression: public std::enable_shared_from_this> std::optional elim; const auto vars {c->vars().template get()}; for (const auto &x: vars) { - // TODO: here - if (x.isTempVar()) { - if (elim) { - return std::optional{}; - } else { - elim = x; - } + if (elim) { + return std::optional{}; + } else { + elim = x; } } return elim; @@ -516,9 +526,7 @@ class BoolExpression: public std::enable_shared_from_this> if (std::holds_alternative(lit)) { const auto &bool_lit {std::get(lit)}; const auto var {bool_lit.getBoolVar()}; - if (var.isTempVar()) { - res.put(var, bool_lit.isNegated() ? bot() : top()); - } + res.put(var, bool_lit.isNegated() ? bot() : top()); } } } From df291eaf3716629bc5f71cd2ce759511d7694bd5 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 20 Nov 2023 19:10:27 +0100 Subject: [PATCH 13/25] non-linear: introduce "constraint tiers" Introduce constraint tiers: Linear, Polynomial, Exponential. Here "linear" does NOT refer the plurality of left-hand-side predicates but the clause constraint. For example: - linear constraint : F(x) /\ 2*x=4 ==> F(x) - polynomial constraint : F(x) /\ x^2=4 ==> F(x) - exponential constraint : F(x) /\ 2^x=4 ==> F(x) The distinction is for optimization. Because the constraint tiers are successively harder to deal with for SMT solvers, we can first try to solve a CHC problem by only considering "easy" linear constraints first and only consider harder polynomial and exponential constraints if necessary. --- benchmarks/LIA-Lin.txt | 998 ++++++++++++++++----------------- benchmarks/LIA.txt | 62 +- run_benchmark.sh | 22 +- src/analysis/guardtoolbox.cpp | 30 +- src/analysis/preprocessing.cpp | 6 - src/analysis/reachability.cpp | 61 +- src/analysis/reachability.hpp | 7 +- src/lib/acceleration/rule.cpp | 4 + src/lib/acceleration/rule.hpp | 2 + src/lib/expr/boolexpr.hpp | 13 - src/lib/smt/z3.hpp | 4 + src/nonlinear/linearsolver.hpp | 37 +- src/nonlinear/nonlinear.cpp | 130 +++-- src/nonlinear/nonlinear.hpp | 2 +- 14 files changed, 734 insertions(+), 644 deletions(-) diff --git a/benchmarks/LIA-Lin.txt b/benchmarks/LIA-Lin.txt index aab9558fb..e4d39e7f8 100644 --- a/benchmarks/LIA-Lin.txt +++ b/benchmarks/LIA-Lin.txt @@ -1,500 +1,500 @@ ### Z3 ADCL -000 timeout timeout -001 timeout unknown -002 timeout unknown -003 timeout unknown -004 timeout unknown -005 timeout unknown -006 timeout unknown -007 sat unknown -008 timeout timeout -009 timeout unknown -010 sat unknown -011 sat unknown -012 timeout timeout -013 timeout timeout -014 timeout unknown -015 timeout unknown -016 timeout timeout -017 timeout unknown -018 timeout unknown -019 timeout unknown -020 timeout unknown -021 sat unknown -022 timeout unknown -023 sat unknown -024 timeout unknown -025 timeout timeout -026 timeout unknown -027 timeout timeout -028 sat unknown -029 timeout unknown -030 unsat unsat -031 unsat unsat -032 unsat unsat -033 unsat unsat -034 unsat unsat -035 unsat unsat -036 unsat timeout -037 unsat unsat -038 unsat unsat -039 unsat unsat -040 timeout timeout -041 timeout unknown -042 timeout unknown -043 timeout unsat -044 timeout unsat -045 timeout unsat -046 timeout timeout -047 timeout unsat -048 timeout unknown -049 timeout unknown -050 timeout unsat -051 timeout unsat -052 timeout unsat -053 timeout unsat -054 timeout unknown -055 timeout unknown -056 unsat timeout -057 timeout unsat -058 timeout unknown -059 timeout unsat -060 unsat unsat -061 unsat unsat -062 unsat unsat -063 unsat unsat -064 unsat unsat -065 timeout unsat -066 unsat unsat -067 unsat unsat -068 timeout timeout -069 timeout unsat -070 timeout timeout -071 unsat unsat -072 unsat unsat -073 sat timeout -074 timeout timeout -075 timeout unsat -076 timeout unsat -077 timeout unknown -078 timeout unknown -079 sat unknown -080 sat timeout -081 sat unknown -082 timeout unknown -083 unsat unsat -084 unsat unsat -085 sat unknown -086 unsat unsat -087 sat timeout -088 sat timeout -089 unsat unsat -090 timeout unknown -091 timeout unknown -092 unsat unsat -093 sat unknown -094 sat unknown -095 sat unknown -096 unsat timeout -097 timeout unknown -098 timeout unknown -099 timeout unknown -100 timeout unknown -101 timeout unknown -102 timeout timeout -103 timeout unknown -104 timeout unknown -105 timeout unknown -106 timeout timeout -107 timeout unknown -108 timeout unknown -109 timeout unknown -110 sat unknown -111 unsat unsat -112 sat unknown -113 sat timeout -114 sat timeout -115 sat unknown -116 unsat unsat -117 unsat unsat -118 sat unknown -119 unsat unsat -120 sat unknown -121 sat unknown -122 sat timeout -123 unsat unsat -124 sat timeout -125 timeout unknown -126 timeout unknown -127 timeout unknown -128 timeout unknown -129 sat timeout -130 unsat unsat -131 sat timeout -132 sat timeout -133 unsat unsat -134 timeout unknown -135 timeout timeout -136 timeout timeout -137 timeout timeout -138 timeout timeout -139 timeout timeout -140 timeout timeout -141 timeout timeout -142 timeout timeout -143 timeout timeout -144 timeout timeout -145 timeout timeout -146 timeout timeout -147 timeout timeout -148 timeout timeout -149 timeout timeout -150 timeout unknown -151 timeout timeout -152 timeout timeout -153 timeout timeout -154 timeout unsat -155 timeout timeout -156 timeout timeout -157 timeout timeout -158 timeout timeout -159 timeout timeout -160 timeout timeout -161 timeout timeout -162 timeout timeout -163 timeout timeout -164 timeout timeout -165 timeout timeout -166 timeout timeout -167 timeout timeout -168 timeout timeout -169 timeout timeout -170 sat timeout -171 sat timeout -172 sat timeout -173 sat timeout -174 sat unknown -175 unsat unsat -176 sat timeout -177 sat unknown -178 sat unknown -179 sat timeout -180 sat timeout -181 sat timeout -182 sat timeout -183 sat unknown -184 sat unknown -185 unsat unsat -186 sat timeout -187 unsat unsat -188 sat timeout -189 unsat timeout -190 sat timeout -191 sat timeout -192 sat timeout -193 sat timeout -194 sat timeout -195 sat timeout -196 sat timeout -197 unsat unsat -198 sat timeout -199 timeout timeout -200 unsat timeout -201 unsat timeout -202 timeout timeout -203 sat timeout -204 sat timeout -205 sat timeout -206 timeout timeout -207 timeout timeout -208 timeout timeout -209 timeout timeout -210 timeout timeout -211 timeout timeout -212 timeout timeout -213 timeout timeout -214 timeout timeout -215 timeout timeout -216 timeout timeout -217 timeout timeout -218 sat timeout -219 timeout timeout -220 timeout timeout -221 timeout timeout -222 timeout timeout -223 sat timeout -224 sat timeout -225 timeout timeout -226 timeout timeout -227 timeout timeout -228 sat timeout -229 timeout timeout -230 sat timeout -231 timeout timeout -232 sat timeout -233 timeout timeout -234 timeout timeout -235 timeout timeout -236 timeout timeout -237 timeout timeout -238 timeout timeout -239 timeout timeout -240 timeout timeout -241 timeout timeout -242 timeout timeout -243 timeout timeout -244 timeout timeout -245 timeout timeout -246 timeout timeout -247 timeout timeout -248 timeout timeout -249 timeout timeout -250 timeout timeout -251 timeout timeout -252 timeout timeout -253 timeout timeout -254 timeout timeout -255 timeout timeout -256 timeout timeout -257 timeout timeout -258 timeout timeout -259 timeout timeout -260 sat timeout -261 timeout unknown -262 unknown timeout -263 timeout unknown -264 timeout unknown -265 sat unknown -266 timeout unknown -267 timeout unknown -268 timeout timeout -269 timeout unknown -270 timeout unknown -271 timeout timeout -272 timeout timeout -273 timeout unknown -274 timeout timeout -275 timeout unknown -276 timeout timeout -277 timeout unknown -278 timeout timeout -279 timeout timeout -280 timeout unknown -281 timeout timeout -282 timeout unknown -283 timeout unknown -284 timeout unknown -285 timeout unknown -286 timeout unknown -287 timeout unknown -288 sat timeout -289 timeout unknown -290 sat unknown -291 sat unknown -292 timeout unknown -293 sat unknown -294 sat unknown -295 sat unknown -296 sat unknown -297 unsat unsat -298 timeout unknown -299 unsat unsat -300 sat unknown -301 sat unknown -302 timeout timeout -303 timeout unknown -304 timeout unknown -305 timeout unknown -306 sat unknown -307 sat unknown -308 sat unknown -309 sat unknown -310 sat unknown -311 sat unknown -312 timeout unknown -313 unsat unsat -314 sat unknown -315 sat unknown -316 unsat unsat -317 sat unknown -318 sat unknown -319 unsat unsat -320 sat unknown -321 unsat unsat -322 unsat unsat -323 sat unknown -324 sat timeout -325 sat unknown -326 unsat unsat -327 unsat unsat -328 unsat unsat -329 unsat unsat -330 unsat unsat -331 unsat unsat -332 unsat unsat -333 sat unknown -334 unsat unsat -335 unsat unsat -336 unsat unsat -337 unsat unsat -338 sat timeout -339 sat timeout -340 sat timeout -341 sat timeout -342 sat timeout -343 unsat timeout -344 sat timeout -345 unsat unsat -346 unsat unsat -347 unsat unsat -348 unsat unsat -349 unsat unsat -350 sat timeout -351 unsat unsat -352 unsat timeout -353 unsat timeout -354 unsat timeout -355 timeout timeout -356 timeout timeout -357 timeout timeout -358 timeout unsat -359 timeout timeout -360 timeout timeout -361 timeout timeout -362 timeout unsat -363 timeout timeout -364 timeout timeout -365 timeout timeout -366 timeout timeout -367 timeout timeout -368 timeout timeout -369 timeout timeout -370 timeout timeout -371 timeout timeout -372 timeout timeout -373 timeout timeout -374 timeout timeout -375 timeout timeout -376 timeout timeout -377 timeout timeout -378 timeout timeout -379 sat timeout -380 timeout timeout -381 timeout unsat -382 timeout timeout -383 timeout timeout -384 timeout timeout -385 timeout timeout -386 timeout unsat -387 timeout timeout -388 timeout timeout -389 timeout timeout -390 timeout timeout -391 timeout timeout -392 timeout timeout -393 timeout timeout -394 timeout timeout -395 unsat timeout -396 timeout timeout -397 timeout timeout -398 sat timeout -399 timeout timeout -400 timeout timeout -401 timeout unsat -402 timeout unsat -403 sat timeout -404 timeout timeout -405 timeout unsat -406 timeout timeout -407 timeout timeout -408 timeout timeout -409 sat unknown -410 sat unknown -411 sat unknown -412 sat unknown -413 sat unknown -414 sat unknown -415 sat unknown -416 sat unknown -417 sat unknown -418 sat unknown -419 sat unknown -420 sat unknown -421 sat unknown -422 sat unknown -423 sat unknown -424 sat unknown -425 sat unknown -426 sat unknown -427 sat unknown -428 sat unknown -429 timeout unknown -430 timeout unknown -431 timeout timeout -432 unsat timeout -433 timeout timeout -434 sat unknown -435 sat unknown -436 sat unknown -437 sat unknown -438 sat unknown -439 sat unknown -440 sat unknown -441 sat unknown -442 unsat timeout -443 sat unknown -444 sat unknown -445 sat unknown -446 timeout timeout -447 timeout timeout -448 timeout timeout -449 timeout timeout -450 timeout timeout -451 timeout timeout -452 timeout timeout -453 timeout timeout -454 timeout timeout -455 timeout timeout -456 timeout timeout -457 unsat timeout -458 timeout timeout -459 timeout timeout -460 timeout timeout -461 timeout unknown -462 timeout timeout -463 timeout timeout -464 timeout timeout -465 timeout timeout -466 timeout timeout -467 timeout timeout -468 timeout timeout -469 timeout timeout -470 timeout timeout -471 timeout timeout -472 timeout timeout -473 timeout timeout -474 unsat timeout -475 timeout unsat -476 timeout timeout -477 timeout timeout -478 timeout timeout -479 timeout timeout -480 timeout timeout -481 sat timeout -482 timeout timeout -483 timeout timeout -484 timeout timeout -485 timeout timeout -486 unsat timeout -487 sat timeout -488 timeout timeout -489 timeout timeout -490 timeout timeout -491 timeout timeout -492 timeout timeout -493 timeout timeout -494 timeout timeout -495 sat timeout -496 sat timeout -497 timeout timeout -498 timeout timeout +000 timeout timeout +001 timeout unknown +002 timeout unknown +003 timeout unknown +004 timeout unknown +005 timeout unknown +006 timeout unknown +007 sat unknown +008 timeout timeout +009 timeout unknown +010 sat unknown +011 sat unknown +012 timeout timeout +013 timeout timeout +014 timeout timeout +015 timeout unknown +016 timeout unknown +017 timeout unknown +018 timeout unknown +019 timeout unknown +020 timeout unknown +021 sat unknown +022 timeout unknown +023 sat unknown +024 timeout unknown +025 timeout timeout +026 timeout unknown +027 timeout unknown +028 sat unknown +029 timeout unknown +030 unsat unsat +031 unsat unsat +032 unsat unsat +033 unsat unsat +034 unsat unsat +035 unsat unsat +036 unsat unknown +037 unsat unsat +038 unsat unsat +039 unsat unsat +040 timeout timeout +041 timeout timeout +042 timeout timeout +043 timeout unsat +044 timeout unsat +045 timeout unsat +046 timeout unknown +047 timeout unsat +048 timeout timeout +049 timeout unknown +050 timeout unsat +051 timeout unsat +052 timeout unsat +053 timeout unsat +054 timeout unknown +055 timeout unknown +056 unsat unknown +057 timeout unsat +058 timeout unknown +059 timeout unsat +060 unsat unsat +061 unsat unsat +062 unsat unsat +063 unsat unsat +064 unsat unsat +065 timeout unsat +066 unsat unsat +067 unsat unsat +068 timeout unknown +069 timeout unsat +070 timeout timeout +071 unsat unsat +072 unsat unsat +073 sat timeout +074 timeout timeout +075 timeout unsat +076 timeout unsat +077 timeout unknown +078 timeout unknown +079 sat unknown +080 sat timeout +081 sat unknown +082 timeout unknown +083 unsat unsat +084 unsat unsat +085 sat unknown +086 unsat unsat +087 sat timeout +088 sat unknown +089 unsat unsat +090 timeout unknown +091 timeout unknown +092 unsat unsat +093 sat unknown +094 sat unknown +095 sat unknown +096 unsat timeout +097 timeout unknown +098 timeout unknown +099 timeout timeout +100 timeout unknown +101 timeout unknown +102 timeout timeout +103 timeout unknown +104 timeout unknown +105 timeout unknown +106 timeout timeout +107 timeout unknown +108 timeout unknown +109 timeout unknown +110 sat unknown +111 unsat unsat +112 sat unknown +113 sat timeout +114 sat timeout +115 sat unknown +116 unsat unsat +117 unsat unsat +118 sat unknown +119 unsat unsat +120 sat unknown +121 sat unknown +122 sat timeout +123 unsat unsat +124 sat timeout +125 timeout unknown +126 timeout unknown +127 timeout unknown +128 timeout unknown +129 sat timeout +130 unsat unsat +131 sat timeout +132 sat timeout +133 unsat unsat +134 timeout unknown +135 timeout timeout +136 timeout timeout +137 timeout timeout +138 timeout timeout +139 timeout timeout +140 timeout timeout +141 timeout timeout +142 timeout timeout +143 timeout timeout +144 timeout timeout +145 timeout timeout +146 timeout timeout +147 timeout timeout +148 timeout timeout +149 timeout timeout +150 timeout unknown +151 timeout timeout +152 timeout timeout +153 timeout timeout +154 timeout unsat +155 timeout timeout +156 timeout timeout +157 timeout timeout +158 timeout timeout +159 timeout timeout +160 timeout timeout +161 timeout timeout +162 timeout timeout +163 timeout timeout +164 timeout timeout +165 timeout timeout +166 timeout timeout +167 timeout timeout +168 timeout timeout +169 timeout timeout +170 sat timeout +171 sat timeout +172 sat timeout +173 sat timeout +174 sat unknown +175 unsat unsat +176 sat unknown +177 sat unknown +178 sat unknown +179 sat timeout +180 sat timeout +181 sat timeout +182 sat timeout +183 sat unknown +184 sat unknown +185 unsat unsat +186 sat unknown +187 unsat unsat +188 sat timeout +189 unsat timeout +190 sat timeout +191 sat timeout +192 sat timeout +193 sat timeout +194 sat timeout +195 sat timeout +196 sat timeout +197 unsat unsat +198 sat timeout +199 timeout timeout +200 unsat timeout +201 unsat unsat +202 timeout timeout +203 sat timeout +204 sat timeout +205 sat timeout +206 timeout timeout +207 timeout timeout +208 timeout timeout +209 timeout timeout +210 timeout timeout +211 timeout timeout +212 timeout timeout +213 timeout timeout +214 timeout timeout +215 timeout timeout +216 timeout timeout +217 timeout timeout +218 sat timeout +219 timeout timeout +220 timeout timeout +221 timeout timeout +222 timeout timeout +223 sat timeout +224 sat timeout +225 timeout timeout +226 timeout timeout +227 timeout timeout +228 sat timeout +229 timeout timeout +230 sat timeout +231 timeout timeout +232 sat timeout +233 timeout timeout +234 timeout timeout +235 timeout timeout +236 timeout timeout +237 timeout timeout +238 timeout timeout +239 timeout timeout +240 timeout timeout +241 timeout timeout +242 timeout timeout +243 timeout timeout +244 timeout timeout +245 timeout timeout +246 timeout timeout +247 timeout timeout +248 timeout timeout +249 timeout timeout +250 timeout timeout +251 timeout timeout +252 timeout timeout +253 timeout timeout +254 timeout timeout +255 timeout timeout +256 timeout timeout +257 timeout timeout +258 timeout timeout +259 timeout timeout +260 sat timeout +261 timeout unknown +262 unknown timeout +263 timeout unknown +264 timeout unknown +265 sat unknown +266 timeout unknown +267 timeout unknown +268 timeout unknown +269 timeout unknown +270 timeout unknown +271 timeout timeout +272 timeout timeout +273 timeout unknown +274 timeout unknown +275 timeout unknown +276 timeout timeout +277 timeout timeout +278 timeout timeout +279 timeout timeout +280 timeout unknown +281 timeout timeout +282 timeout timeout +283 timeout unknown +284 timeout timeout +285 timeout unknown +286 timeout unknown +287 timeout unknown +288 sat timeout +289 timeout unknown +290 sat unknown +291 sat unknown +292 timeout unknown +293 sat unknown +294 sat unknown +295 sat unknown +296 sat unknown +297 unsat unsat +298 timeout unknown +299 unsat unsat +300 sat unknown +301 sat unknown +302 timeout timeout +303 timeout unknown +304 timeout unknown +305 timeout unknown +306 sat unknown +307 sat unknown +308 sat unknown +309 sat unknown +310 sat unknown +311 sat unknown +312 timeout unknown +313 unsat unsat +314 sat unknown +315 sat unknown +316 unsat unsat +317 sat unknown +318 sat unknown +319 unsat unsat +320 sat unknown +321 unsat unsat +322 unsat unsat +323 sat unknown +324 sat timeout +325 sat unknown +326 unsat unsat +327 unsat unsat +328 unsat unsat +329 unsat unsat +330 unsat unsat +331 unsat unsat +332 unsat unsat +333 sat unknown +334 unsat unsat +335 unsat unsat +336 unsat unsat +337 unsat unsat +338 sat timeout +339 sat timeout +340 sat timeout +341 sat timeout +342 sat timeout +343 unsat timeout +344 sat timeout +345 unsat unsat +346 unsat unsat +347 unsat unsat +348 unsat unsat +349 unsat unsat +350 sat timeout +351 unsat unsat +352 unsat timeout +353 unsat unsat +354 unsat timeout +355 timeout unknown +356 timeout timeout +357 timeout timeout +358 timeout unsat +359 timeout timeout +360 timeout timeout +361 timeout timeout +362 timeout unsat +363 timeout timeout +364 timeout timeout +365 timeout timeout +366 timeout timeout +367 timeout timeout +368 timeout timeout +369 timeout timeout +370 timeout timeout +371 timeout timeout +372 timeout timeout +373 timeout timeout +374 timeout timeout +375 timeout timeout +376 timeout timeout +377 timeout timeout +378 timeout timeout +379 sat timeout +380 timeout timeout +381 timeout unsat +382 timeout timeout +383 timeout timeout +384 timeout timeout +385 timeout timeout +386 timeout unsat +387 timeout timeout +388 timeout timeout +389 timeout timeout +390 timeout timeout +391 timeout timeout +392 timeout timeout +393 timeout timeout +394 timeout timeout +395 unsat timeout +396 timeout timeout +397 timeout timeout +398 sat timeout +399 timeout timeout +400 timeout timeout +401 timeout unsat +402 timeout unsat +403 sat timeout +404 timeout timeout +405 timeout unsat +406 timeout timeout +407 timeout timeout +408 timeout timeout +409 sat unknown +410 sat unknown +411 sat unknown +412 sat unknown +413 sat unknown +414 sat unknown +415 sat unknown +416 sat unknown +417 sat unknown +418 sat unknown +419 sat unknown +420 sat unknown +421 sat unknown +422 sat unknown +423 sat unknown +424 sat unknown +425 sat unknown +426 sat unknown +427 sat unknown +428 sat unknown +429 timeout unknown +430 timeout unknown +431 timeout timeout +432 unsat timeout +433 timeout timeout +434 sat unknown +435 sat unknown +436 sat unknown +437 sat unknown +438 sat unknown +439 sat unknown +440 sat unknown +441 sat unknown +442 unsat timeout +443 sat unknown +444 sat unknown +445 sat unknown +446 timeout timeout +447 timeout timeout +448 timeout timeout +449 timeout timeout +450 timeout timeout +451 timeout timeout +452 timeout timeout +453 timeout timeout +454 timeout timeout +455 timeout timeout +456 timeout timeout +457 unsat timeout +458 timeout timeout +459 timeout timeout +460 timeout timeout +461 timeout unknown +462 timeout timeout +463 timeout timeout +464 timeout timeout +465 timeout timeout +466 timeout timeout +467 timeout timeout +468 timeout timeout +469 timeout timeout +470 timeout timeout +471 timeout timeout +472 timeout timeout +473 timeout timeout +474 unsat timeout +475 timeout unsat +476 timeout timeout +477 timeout timeout +478 timeout timeout +479 timeout timeout +480 timeout timeout +481 sat timeout +482 timeout timeout +483 timeout timeout +484 timeout timeout +485 timeout timeout +486 unsat timeout +487 sat timeout +488 timeout timeout +489 timeout timeout +490 timeout timeout +491 timeout timeout +492 timeout timeout +493 timeout timeout +494 timeout timeout +495 sat timeout +496 sat timeout +497 timeout timeout +498 timeout timeout \ No newline at end of file diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index b9d200e2b..ed6786ad3 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -19,7 +19,7 @@ 017 sat timeout 018 timeout unknown 019 sat timeout -020 timeout unknown +020 timeout timeout 021 sat unknown 022 sat timeout 023 sat timeout @@ -29,11 +29,11 @@ 027 timeout timeout 028 timeout timeout 029 timeout timeout -030 timeout timeout +030 timeout unknown 031 timeout timeout 032 timeout timeout 033 timeout timeout -034 timeout error +034 timeout timeout 035 timeout timeout 036 timeout timeout 037 timeout timeout @@ -44,10 +44,10 @@ 042 timeout unknown 043 sat timeout 044 timeout unknown -045 timeout unknown -046 timeout timeout +045 timeout timeout +046 timeout unknown 047 timeout timeout -048 sat timeout +048 sat unknown 049 unsat timeout 050 timeout timeout 051 sat unknown @@ -57,7 +57,7 @@ 055 timeout timeout 056 timeout timeout 057 timeout timeout -058 timeout unknown +058 timeout timeout 059 sat timeout 060 unsat unsat 061 sat timeout @@ -65,11 +65,11 @@ 063 unsat unsat 064 sat timeout 065 sat unknown -066 sat unknown +066 sat timeout 067 unsat unsat 068 sat timeout -069 unsat unsat -070 sat timeout +069 unsat timeout +070 sat error 071 sat unknown 072 unsat unsat 073 unsat timeout @@ -86,7 +86,7 @@ 084 sat timeout 085 sat timeout 086 unsat timeout -087 timeout timeout +087 timeout unsat 088 sat timeout 089 sat timeout 090 unsat timeout @@ -94,7 +94,7 @@ 092 timeout timeout 093 unsat timeout 094 timeout timeout -095 sat timeout +095 sat error 096 timeout unsat 097 timeout unsat 098 unsat timeout @@ -122,7 +122,7 @@ 120 sat timeout 121 sat timeout 122 timeout timeout -123 timeout timeout +123 timeout unknown 124 sat timeout 125 sat timeout 126 timeout unsat @@ -133,32 +133,32 @@ 131 timeout timeout 132 timeout timeout 133 unsat unknown -134 unsat timeout +134 unsat unknown 135 sat timeout -136 unsat timeout +136 unsat unknown 137 unsat timeout -138 sat timeout +138 sat unknown 139 unsat unknown 140 sat timeout 141 unsat timeout 142 unsat unsat -143 sat timeout +143 sat unknown 144 sat timeout 145 unsat timeout -146 sat timeout -147 unsat timeout +146 sat unknown +147 unsat unknown 148 unsat unknown -149 sat timeout -150 unsat timeout +149 sat unknown +150 unsat unknown 151 sat timeout 152 unsat unknown -153 sat timeout +153 sat unknown 154 sat timeout 155 sat timeout 156 sat timeout 157 unsat timeout 158 unsat timeout -159 sat unknown +159 sat timeout 160 sat timeout 161 sat timeout 162 sat timeout @@ -208,22 +208,22 @@ 206 sat timeout 207 sat timeout 208 sat timeout -209 timeout unknown +209 timeout timeout 210 sat timeout 211 unsat unsat -212 timeout unknown +212 timeout timeout 213 sat timeout 214 timeout timeout -215 timeout timeout -216 sat unknown +215 timeout unknown +216 sat timeout 217 timeout timeout 218 timeout timeout 219 sat timeout 220 timeout timeout 221 sat timeout 222 sat timeout -223 sat unknown -224 sat unknown +223 sat timeout +224 sat timeout 225 timeout timeout 226 timeout timeout 227 sat timeout @@ -341,7 +341,7 @@ 339 unsat unsat 340 sat timeout 341 sat timeout -342 unsat unsat +342 unsat timeout 343 unsat timeout 344 unsat timeout 345 unsat unknown @@ -398,7 +398,7 @@ 396 sat timeout 397 sat timeout 398 sat timeout -399 unsat unsat +399 unsat timeout 400 unsat timeout 401 sat timeout 402 timeout timeout diff --git a/run_benchmark.sh b/run_benchmark.sh index c01aa8d90..e65a7410f 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -7,24 +7,23 @@ set -e pushd $(dirname ${BASH_SOURCE[0]}) cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo +# cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug # cmake --build build pushd build make -j4 popd -# TODO: debug 335 (seg fault) -# TODO: debug 034 (seg fault) +# ERROR: 070, 095 -# # gdb --args \ -# ./build/loat-static \ -# --mode reachability \ -# --format horn \ -# --proof-level 0 \ -# --log \ -# "../chc-comp22-benchmarks/LIA/chc-LIA_069.smt2" +gdb --args \ +./build/loat-static \ + --mode reachability \ + --format horn \ + --proof-level 0 \ + "../chc-comp22-benchmarks/LIA/chc-LIA_070.smt2" -# popd -# exit +popd +exit ########################################################################## @@ -44,6 +43,7 @@ do if true; then # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then # if [[ "$z3_result" == "unsat" ]]; then + # if [[ "$adcl_result" == "unknown" ]]; then # if [[ "$adcl_result" == "unsat" ]]; then set +e result=$(timeout 5 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") diff --git a/src/analysis/guardtoolbox.cpp b/src/analysis/guardtoolbox.cpp index a186bad29..ba7f2fa2d 100644 --- a/src/analysis/guardtoolbox.cpp +++ b/src/analysis/guardtoolbox.cpp @@ -100,18 +100,18 @@ Result GuardToolbox::propagateBooleanEqualities(const Rule &rule) { // collect implied program variable equalities: for (const auto &var: equiv.domain()) { if (expr::isProgVar(var)) { - if (res->getGuard()->countOccuranceOf(var) == 1) { - // when the program variable occurs only once in the expression, - // there is no need to substitute it. This also prevents an - // infinite loop, because when we re-add the program variables to the - // guard using `buildAnd`, we trigger `propagateBooleanEqualities` - // again. - equiv.erase(var); - } else { - program_var_equalities.push_back( - expr::mkEq(expr::toExpr(var), equiv.get(var))->simplify() - ); - } + // if (res->getGuard()->countOccuranceOf(var) == 1) { + // // when the program variable occurs only once in the expression, + // // there is no need to substitute it. This also prevents an + // // infinite loop, because when we re-add the program variables to the + // // guard using `buildAnd`, we trigger `propagateBooleanEqualities` + // // again. + // equiv.erase(var); + // } else { + program_var_equalities.push_back( + expr::mkEq(expr::toExpr(var), equiv.get(var))->simplify() + ); + // } } } @@ -124,8 +124,12 @@ Result GuardToolbox::propagateBooleanEqualities(const Rule &rule) { // re-add program variable equalities to guard: program_var_equalities.push_back(res->getGuard()); const auto new_guard = BExpression::buildAnd(program_var_equalities); - res = res->withGuard(new_guard); + if (rule.getGuard() == new_guard) { + return Result(rule); + } + + res = res->withGuard(new_guard); res.ruleTransformationProof(rule, "Propagated Equivalences", *res); res.storeSubProof(subproof); } diff --git a/src/analysis/preprocessing.cpp b/src/analysis/preprocessing.cpp index 449659c5d..ff98fbe65 100644 --- a/src/analysis/preprocessing.cpp +++ b/src/analysis/preprocessing.cpp @@ -206,12 +206,6 @@ ResultViaSideEffects Preprocess::preprocess(ITSProblem &its) { linear context we can assume that all paths from these facts lead to `fib(4,3)` anyway. However in the non-linear context we need to keep `fib(3,2)` to derive `fib(5,5)`. - - QUESTION: We could add the chained rules to the ITS without removing - the rules that are part of the chain. However, that's potentially a - lot of rules and is this really cheaper re-generating these rules - during search? Maybe we can just add the maximal paths instead of - every prefix of every path. */ if (Config::Analysis::log) { std::cout << "chaining linear paths..." << std::endl; diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index 8b12ac1f7..830160561 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -1,5 +1,6 @@ #include "reachability.hpp" #include "config.hpp" +#include "linearsolver.hpp" #include "preprocessing.hpp" #include "rulepreprocessing.hpp" #include "loopacceleration.hpp" @@ -128,8 +129,6 @@ Reachability::Reachability(ITSProblem &chcs, bool incremental_mode): ITSExport::printForProof(chcs, std::cout); } - chcs.refineDependencyGraph(); - // TODO: are other preprocessing steps also unsound when in non-linear context? const auto res {Preprocess::preprocess(chcs)}; if (res) { proof.concat(res.getProof()); @@ -566,6 +565,11 @@ std::unique_ptr Reachability::learn_clause(const Rule &rule, cons if (accel_res.accel) { // acceleration succeeded, simplify the result auto simplified = Preprocess::preprocessRule(accel_res.accel->rule); + + // if (!simplified->isLinear()) { + // return std::make_unique(1); + // } + if (simplified->getUpdate() != simp->getUpdate()) { // accelerated rule differs from the original one, update the result if (Config::Analysis::complexity()) { @@ -618,7 +622,7 @@ void Reachability::drop_until(const int new_size) { } } -std::unique_ptr Reachability::handle_loop(const unsigned backlink) { +std::unique_ptr Reachability::handle_loop(const unsigned backlink, LinearSolver::ConstraintTier max_constr_tier) { const auto lang {build_language(backlink)}; auto closure {lang}; redundancy->transitive_closure(closure); @@ -660,6 +664,16 @@ std::unique_ptr Reachability::handle_loop(const unsigned backlink const auto accel_state {*state->succeeded()}; const auto learned_clauses {**accel_state}; bool do_drop {drop || (backlink == trace.size() - 1 && learned_clauses.prefix == 0 && learned_clauses.period == 1)}; + + // To respect `max_constraint_tier` we don't replace the recursive suffix with a + // learned clause if at least one of the learned clauses has a higher constraint tier + // then the maximum dictated by `max_constraint_tier` right now. + for (const auto idx: learned_clauses.res) { + if (constraint_tier_of(idx) > max_constr_tier) { + do_drop = false; + } + } + if (do_drop) { drop_until(backlink); } @@ -741,8 +755,17 @@ LinearSolver::Result Reachability::get_analysis_result() const { } void Reachability::analyze() { - blocked_clauses[0].clear(); - derive_new_facts(); + // First only derive "easy" to handle rules with linear guard. If that's not enough + // also try rules with polynomial guard. Otherwise also consider exponential + // (arbitrary) guards. + for (const auto tier: constraint_tiers) { + blocked_clauses[0].clear(); + derive_new_facts(tier); + + if (get_analysis_result() == LinearSolver::Result::Unsat) { + break; + } + } switch (get_analysis_result()) { case LinearSolver::Result::Sat: @@ -785,7 +808,17 @@ const std::optional Reachability::trace_as_fact() { } } -const std::set Reachability::derive_new_facts() { +LinearSolver::ConstraintTier Reachability::constraint_tier_of(TransIdx rule) const { + if (rule->isLinear()) { + return LinearSolver::ConstraintTier::Linear; + } else if (rule->isPoly()) { + return LinearSolver::ConstraintTier::Polynomial; + } else { + return LinearSolver::ConstraintTier::Exponential; + } +} + +const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTier max_constr_tier) { static std::default_random_engine rnd {}; std::set derived_facts; @@ -809,7 +842,7 @@ const std::set Reachability::derive_new_facts() { backlink = has_looping_suffix(*backlink - 1)) { auto step {trace[*backlink]}; auto simple_loop {*backlink == trace.size() - 1}; - state = handle_loop(*backlink); + state = handle_loop(*backlink, max_constr_tier); if (state->restart()) { break; } else if (state->covered()) { @@ -862,6 +895,7 @@ const std::set Reachability::derive_new_facts() { // Forwarding initial facts is redundant, because they are already obtained by calling `get_initial_facts`. // TODO: Remove `get_initial_facts` and force non linear solver to get inital facts from first round of // calling `derive_new_facts`. + // NOTE: tried the approach above but it definitely lead to more timeouts. bool is_initial_fact = trace.size() == 1 && chcs.isInitialTransition(trace[0].clause_idx); if (!seen_traces.contains(trace_id) && !is_initial_fact) { @@ -875,7 +909,18 @@ const std::set Reachability::derive_new_facts() { if (Config::Analysis::log) std::cout << "restarting after " << luby_loop_count << " loops" << std::endl; restart(); } - const auto try_set = trace.empty() ? chcs.getInitialTransitions() : chcs.getSuccessors(trace.back().clause_idx); + + std::set try_set; + for (const auto idx: trace.empty() ? chcs.getInitialTransitions() : chcs.getSuccessors(trace.back().clause_idx)) { + // Only consider rules for "Step" whose guard has at most the the constraint tier + // configured for the current run. For example if `max_constr_tier` is `Polynomial`, + // we only consider rules with `Linear` or `Polynomial` guard but ignore rules with + // `Exponential` guard. + if (constraint_tier_of(idx) <= max_constr_tier) { + try_set.insert(idx); + } + } + std::vector to_try(try_set.begin(), try_set.end()); std::shuffle(to_try.begin(), to_try.end(), rnd); bool all_failed {true}; diff --git a/src/analysis/reachability.hpp b/src/analysis/reachability.hpp index b75d8ded2..08b62a9ac 100644 --- a/src/analysis/reachability.hpp +++ b/src/analysis/reachability.hpp @@ -3,6 +3,7 @@ #include "itsproblem.hpp" #include "linearizingsolver.hpp" #include "itsproblem.hpp" +#include "linearsolver.hpp" #include "result.hpp" #include "redundanceviaautomata.hpp" #include "complexity.hpp" @@ -250,7 +251,7 @@ class Reachability : public ILinearSolver { /** * does everything that needs to be done if the trace has a looping suffix */ - std::unique_ptr handle_loop(const unsigned backlink); + std::unique_ptr handle_loop(const unsigned backlink, LinearSolver::ConstraintTier max_constr_tier); /** * @return the start position of the looping suffix of the trace, if any, or -1 @@ -313,6 +314,8 @@ class Reachability : public ILinearSolver { std::set learned_clause_ids; + LinearSolver::ConstraintTier constraint_tier_of(TransIdx rule) const; + public: Reachability(ITSProblem &chcs, bool incremental_mode); @@ -321,7 +324,7 @@ class Reachability : public ILinearSolver { void add_clauses(const std::set &chc) override; - const std::set derive_new_facts() override; + const std::set derive_new_facts(LinearSolver::ConstraintTier max_constr_type) override; const std::set get_initial_facts() const override; diff --git a/src/lib/acceleration/rule.cpp b/src/lib/acceleration/rule.cpp index 14b39c291..1c69474d2 100644 --- a/src/lib/acceleration/rule.cpp +++ b/src/lib/acceleration/rule.cpp @@ -71,6 +71,10 @@ bool Rule::isPoly() const { return guard->isPoly() && update.isPoly(); } +bool Rule::isLinear() const { + return guard->isLinear() && update.isLinear(); +} + std::strong_ordering Rule::operator<=>(const Rule &that) const { if (id == that.id) { return std::strong_ordering::equivalent; diff --git a/src/lib/acceleration/rule.hpp b/src/lib/acceleration/rule.hpp index dc3e78fb9..10d941b7e 100644 --- a/src/lib/acceleration/rule.hpp +++ b/src/lib/acceleration/rule.hpp @@ -64,6 +64,8 @@ class Rule { bool isPoly() const; + bool isLinear() const; + std::strong_ordering operator<=>(const Rule &that) const; unsigned getId() const; diff --git a/src/lib/expr/boolexpr.hpp b/src/lib/expr/boolexpr.hpp index 3ee2e5b11..c1885e8a0 100644 --- a/src/lib/expr/boolexpr.hpp +++ b/src/lib/expr/boolexpr.hpp @@ -357,19 +357,6 @@ class BoolExpression: public std::enable_shared_from_this> }); } - unsigned countOccuranceOf(const Var& var) const { - unsigned count = 0; - iter([&var,&count](const Lit& lit) { - if (std::holds_alternative(lit) && std::holds_alternative(var)) { - const BoolLit bool_lit = std::get(lit); - if (bool_lit.getBoolVar() == std::get(var)) { - count++; - } - } - }); - return count; - } - template void collectVars(std::set &vars) const { VS res; diff --git a/src/lib/smt/z3.hpp b/src/lib/smt/z3.hpp index 2c767ff7a..de2d68c0c 100644 --- a/src/lib/smt/z3.hpp +++ b/src/lib/smt/z3.hpp @@ -31,6 +31,10 @@ class Z3 : public Smt { } SmtResult check() override { + // std::cerr << "==============================" << std::endl; + // // // std::cerr << solver << std::endl; + // std::cerr << "==============================" << std::endl; + switch (solver.check()) { case z3::sat: return Sat; case z3::unsat: return Unsat; diff --git a/src/nonlinear/linearsolver.hpp b/src/nonlinear/linearsolver.hpp index b07412c78..4aeec9ff6 100644 --- a/src/nonlinear/linearsolver.hpp +++ b/src/nonlinear/linearsolver.hpp @@ -3,6 +3,7 @@ namespace LinearSolver { enum Result { Unsat, Sat, Unknown, Pending }; + enum ConstraintTier { Linear, Polynomial, Exponential }; } /** @@ -21,9 +22,30 @@ class ILinearSolver { /** * Let linear solver derive all (ideally non-redundant) facts it can derive with - * the current linear clause set. - */ - virtual const std::set derive_new_facts() = 0; + * the current linear clause set. + * + * The argument `max_constr_tier` can be set to `Linear`, `Polynomial` or `Exponential`. + * Here "linear" does NOT refer the the plurality of left-hand-side predicates but the + * clause constraint. For example: + * + * - linear constraint : F(x) /\ 2*x=4 ==> F(x) + * - polynomial constraint : F(x) /\ x^2=4 ==> F(x) + * - exponential constraint : F(x) /\ 2^x=4 ==> F(x) + * + * If the argument is set to `Linear` the solver should only derive facts with linear + * constraint. If the argument is set to `Polynomial`, only polynomial facts should be + * derived. This includes linear facts, since linear constraints are also polynomial. + * Polynomial constraints are also exponential, so the argument `Exponential` instructs + * the solver to derive arbitrary constraints. + * + * The distinction is for optimization. Because the constraint tiers are successively + * harder to deal with for SMT solvers, we can first try to solve a CHC instance by only + * considering "easy" linear constraints and only consider harder polynomial and exponential + * constraints if necessary. + */ + virtual const std::set derive_new_facts( + LinearSolver::ConstraintTier max_constr_tier + ) = 0; virtual LinearSolver::Result get_analysis_result() const = 0; @@ -37,4 +59,13 @@ class ILinearSolver { virtual const std::set get_non_linear_chcs() const = 0; + /** + * Fixed list of all constaint tiers in order of solving difficulty. + */ + const std::vector constraint_tiers = { + LinearSolver::ConstraintTier::Linear, + LinearSolver::ConstraintTier::Polynomial, + LinearSolver::ConstraintTier::Exponential + }; + }; diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 9bc49a4c7..5841ca83e 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -16,55 +16,65 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { // the linear solver managed to derive. std::set facts(linear_solver.get_initial_facts()); - while (true) { - if (Config::Analysis::log) { - std::cout << "============= non-linear solver main loop =============" << std::endl; - } + for (const auto max_constraint_tier: linear_solver.constraint_tiers) { + while (true) { + if (Config::Analysis::log) { + std::cout << "============= non-linear solver main loop =============" << std::endl; + } - const std::set non_linear_chcs(linear_solver.get_non_linear_chcs()); - - // Do resolution with all combinations of facts and non-linear clauses to - // get every possible linear clause that we can derive in this iteration. - std::list resolvents; - for (const auto& non_linear_chc: linear_solver.get_non_linear_chcs()) { - const auto res(all_resolvents(non_linear_chc, facts)); - resolvents.insert( - resolvents.end(), - res.begin(), - res.end() - ); - } + const std::set non_linear_chcs(linear_solver.get_non_linear_chcs()); + + // Do resolution with all combinations of facts and non-linear clauses to + // get every possible linear clause that we can derive in this iteration. + std::list resolvents; + for (const auto& non_linear_chc: linear_solver.get_non_linear_chcs()) { + const auto res(all_resolvents(non_linear_chc, facts)); + resolvents.insert( + resolvents.end(), + res.begin(), + res.end() + ); + } - const std::set resolvents_distinct(resolvents.begin(), resolvents.end()); + // for (const auto& res: resolvents) { + // std::cout << "Res: " << res << std::endl; + // } - if (Config::Analysis::log) { - std::cout - << (resolvents.size() - resolvents_distinct.size()) - << " of " - << resolvents.size() - << " resolvents are syntactially redundant" - << std::endl; - } + const std::set resolvents_distinct(resolvents.begin(), resolvents.end()); + + if (Config::Analysis::log) { + std::cout + << (resolvents.size() - resolvents_distinct.size()) + << " of " + << resolvents.size() + << " resolvents are syntactially redundant" + << std::endl; + } - linear_solver.add_clauses(resolvents_distinct); + linear_solver.add_clauses(resolvents_distinct); - // std::cout << "facts : " << facts.size() << std::endl; - // std::cout << "non linear : " << non_linear_chcs.size() << std::endl; + // std::cout << "facts : " << facts.size() << std::endl; + // std::cout << "non linear : " << non_linear_chcs.size() << std::endl; - facts.clear(); - const auto new_facts = linear_solver.derive_new_facts(); - facts.insert(new_facts.begin(), new_facts.end()); + facts.clear(); + const auto new_facts = linear_solver.derive_new_facts(max_constraint_tier); + facts.insert(new_facts.begin(), new_facts.end()); - if (Config::Analysis::log) { - for (const auto &fact: new_facts) { - std::cout << "new fact: " << fact << std::endl; + if (Config::Analysis::log) { + for (const auto &fact: new_facts) { + std::cout << "new fact: " << fact << std::endl; + } + } + + const auto result = linear_solver.get_analysis_result(); + if (result == LinearSolver::Result::Unsat) { + break; + } else if ((result == LinearSolver::Result::Sat || result == LinearSolver::Result::Unknown) && facts.empty()) { + break; } } - const auto result = linear_solver.get_analysis_result(); - if (result == LinearSolver::Result::Unsat) { - break; - } else if ((result == LinearSolver::Result::Sat || result == LinearSolver::Result::Unknown) && facts.empty()) { + if (linear_solver.get_analysis_result() == LinearSolver::Result::Unsat) { break; } } @@ -105,21 +115,23 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { * no facts (i.e. clauses with no LHS predicates). */ const std::list all_resolvents(const Clause& chc, const std::set& facts) { - return all_resolvents_aux(chc, chc.lhs.begin(), facts); + std::list resolvents; + all_resolvents_aux(chc, chc.lhs.begin(), facts, resolvents); + return resolvents; } -const std::list all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts) { +void all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts, std::list& resolvents) { // === Running example === // `chc` : F(x) /\ F(y) /\ F(z) ==> G(x,y,z) // `preds` : ^---- iterator pointing on first predicate of `chc` // `facts` : [F(1), F(2)] if (preds == chc.lhs.end() || chc.isLinear()) { - return {}; + return; } else { const auto current_pred = *preds; // == F(x) // All resolvents that DONT eliminate F(x) - const auto res_with_head = all_resolvents_aux(chc, std::next(preds), facts); + all_resolvents_aux(chc, std::next(preds), facts, resolvents); // F(x) /\ F(y) ==> G(x,y,1) // F(x) /\ F(y) ==> G(x,y,2) // @@ -132,7 +144,7 @@ const std::list all_resolvents_aux(const Clause& chc, const std::set G(x,2,2) // All resolvents that DO eliminate F(x) - std::list res_without_head; + // std::list res_without_head; for (const auto& fact: facts) { const auto optional_resolvent = fact.resolutionWith(chc, current_pred); @@ -142,8 +154,12 @@ const std::list all_resolvents_aux(const Clause& chc, const std::set G(2,y,z) + if (SmtFactory::check(resolvent.guard) == Sat) { - const auto res(all_resolvents_aux(resolvent, resolvent.lhs.begin(), facts)); + resolvents.push_back(resolvent); + + // const auto res(all_resolvents_aux(resolvent, resolvent.lhs.begin(), facts)); + all_resolvents_aux(resolvent, resolvent.lhs.begin(), facts, resolvents); // for fact = F(1) : F(y) ==> G(1,y,1) // F(y) ==> G(1,y,2) // F(z) ==> G(1,1,z) @@ -154,22 +170,22 @@ const std::list all_resolvents_aux(const Clause& chc, const std::set G(2,1,z) // F(z) ==> G(2,2,z) - res_without_head.push_back(resolvent); - res_without_head.insert( - res_without_head.begin(), - res.begin(), - res.end() - ); + // res_without_head.push_back(resolvent); + // res_without_head.insert( + // res_without_head.begin(), + // res.begin(), + // res.end() + // ); } } } // join all collected resolvents and return: - res_without_head.insert( - res_without_head.end(), - res_with_head.begin(), - res_with_head.end() - ); - return res_without_head; + // res_without_head.insert( + // res_without_head.end(), + // res_with_head.begin(), + // res_with_head.end() + // ); + // return res_without_head; } } diff --git a/src/nonlinear/nonlinear.hpp b/src/nonlinear/nonlinear.hpp index 4ca7a915d..2c0bc7be6 100644 --- a/src/nonlinear/nonlinear.hpp +++ b/src/nonlinear/nonlinear.hpp @@ -15,4 +15,4 @@ class NonLinearSolver { const std::list all_resolvents(const Clause& chc, const std::set& facts); -const std::list all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts); +void all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts, std::list& resolvents); From c5db2a43d5aa6994fa47be6927bdbe02bea6da65 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Thu, 23 Nov 2023 18:58:57 +0100 Subject: [PATCH 14/25] non-linear: benchmark with 10 second timeout Run LIA benchmark with 10 second timeout instead of 5 seconds. --- benchmarks/LIA.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index ed6786ad3..3f95b2dd3 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -68,7 +68,7 @@ 066 sat timeout 067 unsat unsat 068 sat timeout -069 unsat timeout +069 unsat unsat 070 sat error 071 sat unknown 072 unsat unsat @@ -246,7 +246,7 @@ 244 sat timeout 245 unsat timeout 246 sat timeout -247 unsat timeout +247 unsat unsat 248 sat timeout 249 unsat unsat 250 sat timeout @@ -272,7 +272,7 @@ 270 sat timeout 271 unsat timeout 272 sat unknown -273 unsat timeout +273 unsat unsat 274 timeout timeout 275 unsat timeout 276 unsat timeout @@ -398,7 +398,7 @@ 396 sat timeout 397 sat timeout 398 sat timeout -399 unsat timeout +399 unsat unsat 400 unsat timeout 401 sat timeout 402 timeout timeout From 7ca8e6fd01d68ae7254f8497003a07f3d215108a Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 27 Nov 2023 19:49:09 +0100 Subject: [PATCH 15/25] non-linear: detect redundant (up to renaming) facts In many cases the linear solver produces facts that syntactically redundant up to renaming. This seems to happen quite a lot. Consider the CHC problem: (c1) fib(0, 1) (c2) fib(1, 1) (c3) fib(x1, y1) /\ fib(x2, y2) /\ x1+1 = x2 ==> fib(x2+1, y1+y2) Resolution of c1 with the *first* predicate of c3 gives: res(c1,c3) == fib(x2, y2) /\ 1+1 = x2 ==> fib(x2+1, 1+y2) == fib(1, y) ==> fib(2, y+1) Resolution of c2 with the *second* predicate of c3 gives: res(c2,c3) == fib(x1,y1) /\ x1+1 = 1 ==> fib(1+1,1+y2) == fib(0, y) ==> fib(2,y+1) Then the linear solver derives the fact `fib(2,2)` with two non-redundant traces: [c2, res(c1, c3)] = [f(1,1), fib(1, y) ==> fib(2, y+1)] = fib(2,2) [c1, res(c2, c3)] = [f(0,1), fib(0, y) ==> fib(2, y+1)] = fib(2,2) The LIA benchmark has a lot of Fibonacci related CHC instances. See for example: 081,093,090,086,098,353,354,077,345,073,080,076,343,344 And in all of these, the above issue seems to occur. Filtering out the redundant facts doesn't seem to help much though. --- src/its/itsproblem.cpp | 4 +- src/nonlinear/clause.cpp | 17 ++++-- src/nonlinear/clause.hpp | 2 + src/nonlinear/nonlinear.cpp | 110 ++++++++++++++++++++++++++++++------ src/nonlinear/nonlinear.hpp | 4 +- 5 files changed, 112 insertions(+), 25 deletions(-) diff --git a/src/its/itsproblem.cpp b/src/its/itsproblem.cpp index 8eea3119b..cda02eb7d 100644 --- a/src/its/itsproblem.cpp +++ b/src/its/itsproblem.cpp @@ -331,8 +331,8 @@ const Clause ITSProblem::clauseFrom(const LocationIdx lhs_loc, const LocationIdx if (it == update.end()) { // If `var` is not contained in `update` it's implicitly mapped to itself, - // i.e. `update` does not change it's value. Thus, we add `var` directly - // into `rhs_args`. + // i.e. `update` does not change the value of `var`. Thus, we add `var` + // directly into `rhs_args`. rhs_args.push_back(var); } else { const auto optional_var = expr::toVar(expr::second(*it)); diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index af40b8631..c9b1f88b8 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -43,13 +43,19 @@ const Var varAt(const Var &var, const Subs &subs) { * arity and argument types. */ const std::optional computeUnifier(const FunApp &pred1, const FunApp &pred2) { - if (pred1.loc == pred2.loc && pred1.args.size() == pred2.args.size()) { + if (pred1.loc == pred2.loc) { + return computeUnifier(pred1.args, pred2.args); + } else { + return {}; + } +} +const std::optional computeUnifier(const std::vector &args1, const std::vector &args2) { + if (args1.size() == args2.size()) { Subs subs; - size_t size = pred1.args.size(); - for (size_t i = 0; i < size; ++i) { - const Var var1 = pred1.args[i]; - const Var var2 = pred2.args[i]; + for (size_t i = 0; i < args1.size(); ++i) { + const Var var1 = args1[i]; + const Var var2 = args2[i]; auto it = subs.find(var1); if (it != subs.end()) { @@ -88,6 +94,7 @@ const std::optional computeUnifier(const FunApp &pred1, const FunApp &pred } } + /** * Apply `renaming` to all arguments of the predicate. * Will throw an error if the renaming maps to compound expressions diff --git a/src/nonlinear/clause.hpp b/src/nonlinear/clause.hpp index e666d56f7..a5f18722c 100644 --- a/src/nonlinear/clause.hpp +++ b/src/nonlinear/clause.hpp @@ -50,6 +50,8 @@ class Clause { const std::tuple, std::set> partitionByDegree(const std::set chcs); +const std::optional computeUnifier(const std::vector &args1, const std::vector &args2); + // implement comparison operators so they can be stored in std::set bool operator<(const Clause &c1, const Clause &c2); bool operator<(const FunApp &fun1, const FunApp &fun2); diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 5841ca83e..488062412 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -8,13 +8,76 @@ #include "smtfactory.hpp" #include +/** + * All predicates have the same arguments up to renaming. By unifying the RHS + * of a set of CHCs we should be able to detect CHCs that are syntactially + * equivalent up to renaming. + */ +const std::vector unify_all(const std::set& chcs) { + if (chcs.empty()) { + return {}; + } else { + std::vector res; + + auto it = chcs.begin(); + // Sample a clause from `chcs` and extract the argument vector from the + // RHS predicate: + const auto first_rhs_args = it->rhs.args; + + res.push_back(*it); + + it++; + while (it != chcs.end()) { + const auto next = *it; + const auto unifier = computeUnifier(next.rhs.args, first_rhs_args); + if (unifier.has_value()) { + res.push_back(next.renameWith(unifier.value())); + } else { + throw std::logic_error("should be unifiable by construction"); + } + it++; + } + + return res; + } +} + +/* + +c1) fib(0, 1) +c2) fib(2, 3) +c3) fib(x1, y1) /\ fib(x2, y2) /\ x1+1 = x2 ==> fib(x2+1, y1+y2) + + +res(c1,c3) = fib(x2, y2) /\ 0+1 = x2 ==> fib(x2+1, 1+y2) + = fib(1, y) ==> fib(2, y+1) + +res(c2,c3) = fib(x1, y1) /\ x1+1 = 2 ==> fib(2+1, y1+3) + = fib(1, y) ==> fib(3, y+3) + +fib(2,4) +fib(2,4) + + +fib(2,4) +fib(2,7) + +trace : + + [c2, res(c1, c3)] = [f(1,1), fib(1, y) ==> fib(2, y+1)] = fib(2,2) + + [c1, res(c2, c3)] = [f(0,1), fib(0, y) ==> fib(2, y+1)] = fib(2,2) + +*/ + + void NonLinearSolver::analyze(ILinearSolver &linear_solver) { LinearizingSolver smt(4294967295u); // For the first loop iteration, the list of facts is composed of the original facts // given in the CHC problem. In subsequent iterations, the facts are whatever // the linear solver managed to derive. - std::set facts(linear_solver.get_initial_facts()); + std::set facts(linear_solver.get_initial_facts()); for (const auto max_constraint_tier: linear_solver.constraint_tiers) { while (true) { @@ -36,12 +99,8 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { ); } - // for (const auto& res: resolvents) { - // std::cout << "Res: " << res << std::endl; - // } - + // Filter-out syntactially redundant resolvents const std::set resolvents_distinct(resolvents.begin(), resolvents.end()); - if (Config::Analysis::log) { std::cout << (resolvents.size() - resolvents_distinct.size()) @@ -50,18 +109,34 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { << " resolvents are syntactially redundant" << std::endl; } + const std::vector resolvents_normalized(unify_all(resolvents_distinct)); + const std::set resolvents_distinct2(resolvents_normalized.begin(), resolvents_normalized.end()); + if (Config::Analysis::log) { + std::cout + << (resolvents_normalized.size() - resolvents_distinct2.size()) + << " of " + << resolvents_normalized.size() + << " additional resolvents are redundant up to renaming" + << std::endl; + } - linear_solver.add_clauses(resolvents_distinct); - - // std::cout << "facts : " << facts.size() << std::endl; - // std::cout << "non linear : " << non_linear_chcs.size() << std::endl; + linear_solver.add_clauses(resolvents_distinct2); facts.clear(); const auto new_facts = linear_solver.derive_new_facts(max_constraint_tier); - facts.insert(new_facts.begin(), new_facts.end()); + const std::vector normalized_new_facts = unify_all(new_facts); + facts.insert(normalized_new_facts.begin(), normalized_new_facts.end()); + if (Config::Analysis::log) { + std::cout + << (normalized_new_facts.size() - facts.size()) + << " of " + << normalized_new_facts.size() + << " derived facts are redundant up to renaming" + << std::endl; + } if (Config::Analysis::log) { - for (const auto &fact: new_facts) { + for (const auto &fact: facts) { std::cout << "new fact: " << fact << std::endl; } } @@ -114,12 +189,12 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { * `facts` are not redundant. The returned list should also not contain `chc` itself and * no facts (i.e. clauses with no LHS predicates). */ -const std::list all_resolvents(const Clause& chc, const std::set& facts) { - std::list resolvents; +const std::vector all_resolvents(const Clause& chc, const std::set& facts) { + std::vector resolvents; all_resolvents_aux(chc, chc.lhs.begin(), facts, resolvents); return resolvents; } -void all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts, std::list& resolvents) { +void all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts, std::vector& resolvents) { // === Running example === // `chc` : F(x) /\ F(y) /\ F(z) ==> G(x,y,z) // `preds` : ^---- iterator pointing on first predicate of `chc` @@ -154,9 +229,12 @@ void all_resolvents_aux(const Clause& chc, const std::set::iterator pred // // for fact = F(2) : F(y) /\ F(z) ==> G(2,y,z) - if (SmtFactory::check(resolvent.guard) == Sat) { resolvents.push_back(resolvent); + // if (Config::Analysis::log) { + // std::cout << "new resolvent: " << resolvent << std::endl; + // } + // const auto res(all_resolvents_aux(resolvent, resolvent.lhs.begin(), facts)); all_resolvents_aux(resolvent, resolvent.lhs.begin(), facts, resolvents); diff --git a/src/nonlinear/nonlinear.hpp b/src/nonlinear/nonlinear.hpp index 2c0bc7be6..27a34481d 100644 --- a/src/nonlinear/nonlinear.hpp +++ b/src/nonlinear/nonlinear.hpp @@ -13,6 +13,6 @@ class NonLinearSolver { }; -const std::list all_resolvents(const Clause& chc, const std::set& facts); +const std::vector all_resolvents(const Clause& chc, const std::set& facts); -void all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts, std::list& resolvents); +void all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts, std::vector& resolvents); From 2a11ae844f0efc6c783ed6501c70393ab5907344 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 27 Nov 2023 20:23:20 +0100 Subject: [PATCH 16/25] non-linear: don't call refineDependencyGraph For some reason, callling `refineDependencyGraph` after passing new clauses to the linear solver, doesn't terminate or takes a very long on some instances. Calling it also seems to be unecessary since we call it again as part of the preprocessing step. This change gives `unsat` on 8 new instances. --- benchmarks/LIA.txt | 58 +++++++++++++++++------------------ run_benchmark.sh | 39 +++++++++++++---------- src/analysis/reachability.cpp | 1 - src/nonlinear/clause.cpp | 2 +- 4 files changed, 53 insertions(+), 47 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index 3f95b2dd3..59fbbdc6a 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -15,9 +15,9 @@ 013 sat timeout 014 timeout timeout 015 sat timeout -016 timeout unknown +016 timeout timeout 017 sat timeout -018 timeout unknown +018 timeout timeout 019 sat timeout 020 timeout timeout 021 sat unknown @@ -29,7 +29,7 @@ 027 timeout timeout 028 timeout timeout 029 timeout timeout -030 timeout unknown +030 timeout timeout 031 timeout timeout 032 timeout timeout 033 timeout timeout @@ -40,12 +40,12 @@ 038 timeout timeout 039 timeout timeout 040 timeout timeout -041 timeout unknown -042 timeout unknown +041 timeout timeout +042 timeout timeout 043 sat timeout -044 timeout unknown +044 timeout timeout 045 timeout timeout -046 timeout unknown +046 timeout timeout 047 timeout timeout 048 sat unknown 049 unsat timeout @@ -72,7 +72,7 @@ 070 sat error 071 sat unknown 072 unsat unsat -073 unsat timeout +073 unsat unsat 074 sat timeout 075 unsat unsat 076 unsat timeout @@ -91,7 +91,7 @@ 089 sat timeout 090 unsat timeout 091 sat timeout -092 timeout timeout +092 timeout unknown 093 unsat timeout 094 timeout timeout 095 sat error @@ -121,8 +121,8 @@ 119 timeout unsat 120 sat timeout 121 sat timeout -122 timeout timeout -123 timeout unknown +122 timeout unknown +123 timeout timeout 124 sat timeout 125 sat timeout 126 timeout unsat @@ -131,27 +131,27 @@ 129 sat timeout 130 timeout unsat 131 timeout timeout -132 timeout timeout -133 unsat unknown -134 unsat unknown +132 timeout unknown +133 unsat timeout +134 unsat timeout 135 sat timeout -136 unsat unknown -137 unsat timeout +136 unsat timeout +137 unsat unsat 138 sat unknown -139 unsat unknown +139 unsat timeout 140 sat timeout -141 unsat timeout +141 unsat unsat 142 unsat unsat 143 sat unknown 144 sat timeout -145 unsat timeout +145 unsat unsat 146 sat unknown -147 unsat unknown -148 unsat unknown +147 unsat timeout +148 unsat timeout 149 sat unknown -150 unsat unknown +150 unsat timeout 151 sat timeout -152 unsat unknown +152 unsat timeout 153 sat unknown 154 sat timeout 155 sat timeout @@ -252,7 +252,7 @@ 250 sat timeout 251 sat timeout 252 sat timeout -253 unsat timeout +253 unsat unsat 254 sat timeout 255 sat timeout 256 unsat unsat @@ -276,7 +276,7 @@ 274 timeout timeout 275 unsat timeout 276 unsat timeout -277 unsat timeout +277 unsat unsat 278 sat timeout 279 unsat timeout 280 unsat timeout @@ -341,10 +341,10 @@ 339 unsat unsat 340 sat timeout 341 sat timeout -342 unsat timeout -343 unsat timeout +342 unsat unsat +343 unsat unsat 344 unsat timeout -345 unsat unknown +345 unsat timeout 346 sat timeout 347 sat timeout 348 sat timeout @@ -453,4 +453,4 @@ 451 unsat timeout 452 unsat timeout 453 sat timeout -454 sat timeout +454 sat timeout \ No newline at end of file diff --git a/run_benchmark.sh b/run_benchmark.sh index e65a7410f..1162b8768 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -13,17 +13,23 @@ pushd build make -j4 popd -# ERROR: 070, 095 +# all fibonacci: 081,093,090,086,098,353,354,077,345,073,080,076,343,344 -gdb --args \ -./build/loat-static \ - --mode reachability \ - --format horn \ - --proof-level 0 \ - "../chc-comp22-benchmarks/LIA/chc-LIA_070.smt2" +# unsat: 141,145,137 -popd -exit +# too long: 136,150 + +# TODO: 147,134,342,157,148,152,168,139,133,165,158 + +# # # gdb --args \ +# time ./build/loat-static \ +# --mode reachability \ +# --format horn \ +# --proof-level 0 \ +# "../chc-comp22-benchmarks/LIA/chc-LIA_399.smt2" + +# popd +# exit ########################################################################## @@ -40,13 +46,14 @@ do read idx z3_result adcl_result <<< "$line" file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" - if true; then - # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then + # if true; then + if [[ "$z3_result" != "sat" ]]; then + # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]] && [[ "$adcl_result" == "timeout" ]]; then # if [[ "$z3_result" == "unsat" ]]; then # if [[ "$adcl_result" == "unknown" ]]; then # if [[ "$adcl_result" == "unsat" ]]; then set +e - result=$(timeout 5 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") + result=$(timeout 20 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") # result=$(timeout 5 z3 "$file") exit_status=$? set -e @@ -62,10 +69,10 @@ do printf "$idx %-7s %-7s %-7s \n" $z3_result $adcl_result $result - # else - # # if we skip an instance nevertheless include it in the output - # # so the log can easily be copied and saved as a whole - # printf "$idx $prev_result \n" + else + # if we skip an instance nevertheless include it in the output + # so the log can easily be copied and saved as a whole + printf "$idx %-7s %-7s %-7s \n" $z3_result $adcl_result $adcl_result fi fi diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index 830160561..f186dda95 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -977,7 +977,6 @@ void Reachability::add_clauses(const std::set &clauses) { } if (any_linear_clauses) { - chcs.refineDependencyGraph(); // TODO: are other preprocessing steps also unsound when in non-linear context? const auto res {Preprocess::preprocess(chcs)}; if (res) { diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index c9b1f88b8..0bc148608 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -254,7 +254,7 @@ bool operator<(const FunApp &fun1, const FunApp &fun2) { bool operator<(const Clause &c1, const Clause &c2) { if (c1.lhs < c2.lhs) { return true; - } else if (c2.lhs > c1.lhs) { + } else if (c2.lhs < c1.lhs) { return false; } else if (c1.rhs < c2.rhs) { return true; From 66a46c16a1042717c4838b7732ba352a189ff035 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 4 Dec 2023 18:14:34 +0100 Subject: [PATCH 17/25] move syntactic redundancy check into linear solver The commit titled: non-linear: detect redundant (up to renaming) facts introduced a syntactic redundancy check in the non-linear solver. This allows the non-linear solver to filter out syntactially redundant facts derived by the linear solver. Thus, we do fewer resolution steps in the non-linear solver and produce fewer rules that are passed to the linear solver. However, the linear solver still spends a lot of time deriving these redundant facts. By moving the redundancy check into the linear solver, it can backtrack immediatly when encountering a redundant fact. With that we get `unsat` on 7 new instances. --- benchmarks/LIA.txt | 14 ++++---- run_benchmark.sh | 19 +++++++---- src/analysis/reachability.cpp | 19 +++++++---- src/its/itsproblem.cpp | 4 +-- src/nonlinear/clause.cpp | 55 ++++++++++++++++++++++++++++++- src/nonlinear/clause.hpp | 2 ++ src/nonlinear/nonlinear.cpp | 61 +++-------------------------------- 7 files changed, 94 insertions(+), 80 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index 59fbbdc6a..e223eb37c 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -48,7 +48,7 @@ 046 timeout timeout 047 timeout timeout 048 sat unknown -049 unsat timeout +049 unsat unsat 050 timeout timeout 051 sat unknown 052 timeout timeout @@ -76,11 +76,11 @@ 074 sat timeout 075 unsat unsat 076 unsat timeout -077 unsat timeout +077 unsat unsat 078 sat timeout 079 sat timeout 080 unsat timeout -081 unsat timeout +081 unsat unsat 082 sat timeout 083 sat timeout 084 sat timeout @@ -244,7 +244,7 @@ 242 timeout timeout 243 timeout timeout 244 sat timeout -245 unsat timeout +245 unsat unsat 246 sat timeout 247 unsat unsat 248 sat timeout @@ -258,7 +258,7 @@ 256 unsat unsat 257 sat timeout 258 sat unknown -259 unsat timeout +259 unsat unsat 260 sat timeout 261 sat unknown 262 sat unknown @@ -344,13 +344,13 @@ 342 unsat unsat 343 unsat unsat 344 unsat timeout -345 unsat timeout +345 unsat unsat 346 sat timeout 347 sat timeout 348 sat timeout 349 unsat unsat 350 unsat unsat -351 unsat timeout +351 unsat unsat 352 sat timeout 353 unsat timeout 354 unsat timeout diff --git a/run_benchmark.sh b/run_benchmark.sh index 1162b8768..1bf0d4c40 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -17,16 +17,21 @@ popd # unsat: 141,145,137 +# unsat (1m10s): 147 +# unsat (17m) : 134 --> (1m6s) + # too long: 136,150 +# with hack: unsat 342 + # TODO: 147,134,342,157,148,152,168,139,133,165,158 -# # # gdb --args \ +# # gdb --args \ # time ./build/loat-static \ # --mode reachability \ # --format horn \ # --proof-level 0 \ -# "../chc-comp22-benchmarks/LIA/chc-LIA_399.smt2" +# "../chc-comp22-benchmarks/LIA/chc-LIA_141.smt2" # popd # exit @@ -47,7 +52,7 @@ do file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" # if true; then - if [[ "$z3_result" != "sat" ]]; then + if [[ "$z3_result" == "unsat" ]] && [[ "$adcl_result" == "timeout" ]]; then # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]] && [[ "$adcl_result" == "timeout" ]]; then # if [[ "$z3_result" == "unsat" ]]; then # if [[ "$adcl_result" == "unknown" ]]; then @@ -69,10 +74,10 @@ do printf "$idx %-7s %-7s %-7s \n" $z3_result $adcl_result $result - else - # if we skip an instance nevertheless include it in the output - # so the log can easily be copied and saved as a whole - printf "$idx %-7s %-7s %-7s \n" $z3_result $adcl_result $adcl_result + # else + # # if we skip an instance nevertheless include it in the output + # # so the log can easily be copied and saved as a whole + # printf "$idx %-7s %-7s %-7s \n" $z3_result $adcl_result $adcl_result fi fi diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index f186dda95..e8d6ba277 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -880,7 +880,7 @@ const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTi } } - if (incremental_mode && should_add_fact) { // TODO: when trace not empty + if (should_add_fact) { // TODO: when trace not empty // Using a crude (additional) redundancy criterion for facts here. We identify facts by the trace that lead to them. // This is only sufficient because equivalent facts can have multiple traces. We don't need to memoize the entire // trace structure. It's enough to store clause_idx/implicant for each trace step. @@ -929,11 +929,18 @@ const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTi const auto implicant {resolve(idx)}; solver->pop(); if (implicant && store_step(idx, *implicant, Config::Analysis::safety())) { - proof.headline("Step with " + std::to_string(idx->getId())); - print_state(); - update_cpx(); - all_failed = false; - break; + // Additional redundnacy check: if resolvent of trace (after step) is + // syntactially equivalent (up to renaming) to an already derived fact, + // then backtrack and try a different step. + if (derived_facts.contains(trace_as_fact().value())) { + backtrack(); + } else { + proof.headline("Step with " + std::to_string(idx->getId())); + print_state(); + update_cpx(); + all_failed = false; + break; + } } } if (trace.empty()) { diff --git a/src/its/itsproblem.cpp b/src/its/itsproblem.cpp index cda02eb7d..958087dd2 100644 --- a/src/its/itsproblem.cpp +++ b/src/its/itsproblem.cpp @@ -352,10 +352,10 @@ const Clause ITSProblem::clauseFrom(const LocationIdx lhs_loc, const LocationIdx if (lhs_loc == getInitialLocation()) { // rule is a linear CHC with no LHS predicates, ie a "fact" - return Clause({}, rhs, final_guard); + return Clause({}, rhs, final_guard).normalize(); } else { // rule is a linear CHC with exactly one LHS predicates, ie a "rule" - return Clause({ FunApp(lhs_loc, prog_vars) }, rhs, final_guard); + return Clause({ FunApp(lhs_loc, prog_vars) }, rhs, final_guard).normalize(); } } diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index 0bc148608..8ea2767e5 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -212,7 +212,7 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA resolvent_lhs, chc_unified.rhs, new_guard->simplify() - ); + ).normalize(); } /** @@ -234,6 +234,59 @@ const std::tuple, std::set> partitionByDegree(const std return std::make_tuple(linear, non_linear); } +/** + * Normalize the variable indices of the RHS predicate arguments. For example + * + * i44 > i9 /\ b23 ==> F(i44,b23,i9) + * + * is renamed to + * + * i0 > i2 /\ b1 ==> F(i0,b1,i2) + * + * So the variables indices on the right-hand-side should always be 0,1,2,3,etc. + * unless a variable occurs multiple times in the arguments of the original RHS + * predicate. For example: + * + * i44 > i9 /\ b23 ==> F(i44,b23,i9,i44) + * + * is renamed to + * + * i0 > i2 /\ b1 ==> F(i0,b1,i2,i0) + * + * to preserve the implict equality. + * + * Useful to detect if clauses are syntactially equivalent up-to-renaming. + */ +const Clause Clause::normalize() const { + // construct a vector of variables with the same length as `this->rhs.args` + // and the same variable types in each position, except that the variable + // indices are simply: 0,1,2,3,etc. + std::vector target_args; + for (unsigned i=0; i < size(this->rhs.args); i++) { + const auto var = std::visit(Overload{ + [i](const NumVar&) { + return Var(NumVar(i)); + }, + [i](const BoolVar&) { + return Var(BoolVar(i)); + } + }, this->rhs.args[i]); + + target_args.push_back(var); + } + + const auto unifier = computeUnifier(this->rhs.args, target_args); + + if (unifier.has_value()) { + return this->renameWith(unifier.value()); + } else { + // Variable vectors should always be unifiable unless the vectors have differnt length, + // but the vectors should have the same length by construction so this error should not + // occur. + throw std::logic_error("failed to unify variable vectors"); + } +} + /** * Returns true iff the clause has at most one LHS predicate. */ diff --git a/src/nonlinear/clause.hpp b/src/nonlinear/clause.hpp index a5f18722c..6fc14e5d2 100644 --- a/src/nonlinear/clause.hpp +++ b/src/nonlinear/clause.hpp @@ -42,6 +42,8 @@ class Clause { const std::optional resolutionWith(const Clause &chc, const FunApp &pred) const; + const Clause normalize() const; + bool isLinear() const; const VarSet vars() const; diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 488062412..88c8a2caf 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -8,40 +8,6 @@ #include "smtfactory.hpp" #include -/** - * All predicates have the same arguments up to renaming. By unifying the RHS - * of a set of CHCs we should be able to detect CHCs that are syntactially - * equivalent up to renaming. - */ -const std::vector unify_all(const std::set& chcs) { - if (chcs.empty()) { - return {}; - } else { - std::vector res; - - auto it = chcs.begin(); - // Sample a clause from `chcs` and extract the argument vector from the - // RHS predicate: - const auto first_rhs_args = it->rhs.args; - - res.push_back(*it); - - it++; - while (it != chcs.end()) { - const auto next = *it; - const auto unifier = computeUnifier(next.rhs.args, first_rhs_args); - if (unifier.has_value()) { - res.push_back(next.renameWith(unifier.value())); - } else { - throw std::logic_error("should be unifiable by construction"); - } - it++; - } - - return res; - } -} - /* c1) fib(0, 1) @@ -109,32 +75,13 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { << " resolvents are syntactially redundant" << std::endl; } - const std::vector resolvents_normalized(unify_all(resolvents_distinct)); - const std::set resolvents_distinct2(resolvents_normalized.begin(), resolvents_normalized.end()); - if (Config::Analysis::log) { - std::cout - << (resolvents_normalized.size() - resolvents_distinct2.size()) - << " of " - << resolvents_normalized.size() - << " additional resolvents are redundant up to renaming" - << std::endl; - } - linear_solver.add_clauses(resolvents_distinct2); + linear_solver.add_clauses(resolvents_distinct); facts.clear(); - const auto new_facts = linear_solver.derive_new_facts(max_constraint_tier); - const std::vector normalized_new_facts = unify_all(new_facts); - facts.insert(normalized_new_facts.begin(), normalized_new_facts.end()); - if (Config::Analysis::log) { - std::cout - << (normalized_new_facts.size() - facts.size()) - << " of " - << normalized_new_facts.size() - << " derived facts are redundant up to renaming" - << std::endl; - } - + facts = linear_solver.derive_new_facts(max_constraint_tier); + // const auto new_facts = linear_solver.derive_new_facts(max_constraint_tier); + // facts.insert(new_facts.begin(), new_facts.end()); if (Config::Analysis::log) { for (const auto &fact: facts) { std::cout << "new fact: " << fact << std::endl; From a9e9bdc06dc9b1236566427b2f8b9c20ea14eb17 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Sun, 14 Jan 2024 02:28:39 +0100 Subject: [PATCH 18/25] non-linear: chc comp23 results --- benchmarks/comp23-LIA-nonlin.txt | 427 +++++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 benchmarks/comp23-LIA-nonlin.txt diff --git a/benchmarks/comp23-LIA-nonlin.txt b/benchmarks/comp23-LIA-nonlin.txt new file mode 100644 index 000000000..21b6f294c --- /dev/null +++ b/benchmarks/comp23-LIA-nonlin.txt @@ -0,0 +1,427 @@ +### Z3 ADCL +000 sat timeout +001 sat timeout +002 sat timeout +003 sat timeout +004 unsat timeout +005 sat timeout +006 sat timeout +007 sat timeout +008 sat timeout +009 sat timeout +010 sat timeout +011 sat timeout +012 timeout timeout +013 timeout timeout +014 timeout timeout +015 timeout timeout +016 timeout timeout +017 timeout timeout +018 sat timeout +019 timeout timeout +020 sat timeout +021 timeout timeout +022 timeout timeout +023 timeout timeout +024 timeout timeout +025 timeout timeout +026 timeout timeout +027 timeout timeout +028 timeout timeout +029 timeout timeout +030 timeout timeout +031 timeout timeout +032 timeout timeout +033 timeout timeout +034 timeout timeout +035 timeout timeout +036 timeout timeout +037 timeout timeout +038 timeout timeout +039 timeout timeout +040 timeout timeout +041 timeout timeout +042 timeout timeout +043 timeout timeout +044 timeout timeout +045 timeout timeout +046 timeout timeout +047 timeout timeout +048 unsat unsat +049 unsat unsat +050 sat timeout +051 sat timeout +052 unsat unsat +053 sat timeout +054 sat timeout +055 sat timeout +056 unsat unsat +057 unsat timeout +058 sat timeout +059 sat timeout +060 sat timeout +061 sat timeout +062 sat timeout +063 timeout timeout +064 sat timeout +065 timeout timeout +066 timeout timeout +067 timeout timeout +068 sat timeout +069 unsat timeout +070 unsat unsat +071 unsat unsat +072 unsat unsat +073 sat timeout +074 sat timeout +075 sat timeout +076 sat timeout +077 sat timeout +078 timeout timeout +079 sat timeout +080 sat timeout +081 timeout timeout +082 sat timeout +083 timeout timeout +084 timeout timeout +085 timeout timeout +086 timeout timeout +087 timeout timeout +088 sat timeout +089 timeout timeout +090 timeout timeout +091 timeout timeout +092 timeout timeout +093 timeout timeout +094 sat timeout +095 sat timeout +096 sat timeout +097 sat timeout +098 sat timeout +099 unsat timeout +100 sat timeout +101 unsat unsat +102 unsat timeout +103 unsat timeout +104 unsat timeout +105 sat timeout +106 unsat timeout +107 sat timeout +108 unsat timeout +109 unsat timeout +110 sat timeout +111 unsat timeout +112 sat timeout +113 sat timeout +114 timeout timeout +115 timeout timeout +116 sat timeout +117 unsat timeout +118 timeout timeout +119 unsat timeout +120 timeout timeout +121 unsat timeout +122 unsat timeout +123 unsat timeout +124 unsat timeout +125 sat timeout +126 unsat timeout +127 sat timeout +128 sat timeout +129 unsat timeout +130 timeout timeout +131 sat timeout +132 timeout timeout +133 timeout timeout +134 timeout timeout +135 timeout timeout +136 timeout timeout +137 sat timeout +138 sat timeout +139 sat timeout +140 timeout timeout +141 timeout timeout +142 timeout timeout +143 sat timeout +144 timeout timeout +145 timeout timeout +146 sat timeout +147 timeout timeout +148 timeout timeout +149 timeout timeout +150 timeout timeout +151 sat timeout +152 sat timeout +153 sat timeout +154 sat timeout +155 timeout timeout +156 sat timeout +157 timeout timeout +158 unsat unsat +159 timeout timeout +160 sat timeout +161 sat timeout +162 sat timeout +163 timeout timeout +164 timeout timeout +165 timeout timeout +166 timeout timeout +167 timeout timeout +168 timeout timeout +169 sat timeout +170 sat timeout +171 unsat unsat +172 sat timeout +173 sat timeout +174 sat timeout +175 timeout timeout +176 sat timeout +177 sat timeout +178 timeout timeout +179 timeout timeout +180 timeout timeout +181 sat timeout +182 timeout timeout +183 timeout timeout +184 timeout timeout +185 sat timeout +186 sat timeout +187 sat timeout +188 unsat timeout +189 unsat timeout +190 unsat unsat +191 unsat timeout +192 unsat unsat +193 unsat unsat +194 unsat timeout +195 unsat unsat +196 unsat timeout +197 sat timeout +198 sat timeout +199 unsat unsat +200 sat timeout +201 unsat unsat +202 unsat timeout +203 unsat unsat +204 unsat unsat +205 unsat unsat +206 unsat unsat +207 unsat unsat +208 sat timeout +209 sat timeout +210 sat timeout +211 sat timeout +212 sat timeout +213 unsat unsat +214 unsat timeout +215 unsat timeout +216 sat timeout +217 unsat timeout +218 unsat unsat +219 sat timeout +220 sat timeout +221 sat timeout +222 sat timeout +223 sat timeout +224 unsat timeout +225 sat timeout +226 sat timeout +227 sat timeout +228 sat timeout +229 unsat timeout +230 sat timeout +231 unsat timeout +232 sat timeout +233 sat timeout +234 unsat timeout +235 unsat timeout +236 unsat timeout +237 unsat timeout +238 unsat timeout +239 sat timeout +240 sat timeout +241 sat timeout +242 sat timeout +243 sat timeout +244 sat timeout +245 sat timeout +246 sat timeout +247 unsat timeout +248 unsat timeout +249 unsat timeout +250 unsat timeout +251 unsat timeout +252 sat timeout +253 unsat timeout +254 unsat timeout +255 unsat timeout +256 timeout timeout +257 sat timeout +258 sat timeout +259 timeout timeout +260 unsat timeout +261 unsat timeout +262 unsat timeout +263 timeout timeout +264 unsat timeout +265 timeout timeout +266 timeout timeout +267 timeout timeout +268 unsat timeout +269 unsat timeout +270 timeout timeout +271 timeout timeout +272 timeout timeout +273 sat timeout +274 unsat timeout +275 unsat timeout +276 timeout timeout +277 unsat timeout +278 timeout timeout +279 unsat timeout +280 timeout timeout +281 sat timeout +282 unsat timeout +283 unsat timeout +284 timeout timeout +285 timeout timeout +286 timeout timeout +287 timeout timeout +288 unsat timeout +289 sat timeout +290 sat timeout +291 timeout timeout +292 timeout timeout +293 timeout timeout +294 timeout timeout +295 timeout timeout +296 timeout timeout +297 timeout timeout +298 timeout timeout +299 timeout timeout +300 unsat timeout +301 timeout timeout +302 timeout timeout +303 timeout timeout +304 timeout timeout +305 unsat timeout +306 unsat timeout +307 timeout timeout +308 unsat timeout +309 timeout timeout +310 timeout timeout +311 unsat timeout +312 sat timeout +313 timeout timeout +314 timeout timeout +315 unsat timeout +316 timeout timeout +317 unsat timeout +318 unsat timeout +319 unsat timeout +320 timeout timeout +321 sat timeout +322 unsat timeout +323 unsat timeout +324 unsat timeout +325 unsat timeout +326 unsat timeout +327 sat timeout +328 timeout timeout +329 timeout timeout +330 unsat timeout +331 timeout timeout +332 timeout timeout +333 unsat timeout +334 timeout timeout +335 timeout timeout +336 unsat timeout +337 timeout timeout +338 unsat timeout +339 sat timeout +340 timeout timeout +341 timeout timeout +342 timeout timeout +343 sat timeout +344 timeout timeout +345 timeout timeout +346 unsat timeout +347 timeout timeout +348 timeout timeout +349 timeout timeout +350 timeout timeout +351 unsat timeout +352 unsat timeout +353 unsat timeout +354 unsat timeout +355 unsat timeout +356 timeout timeout +357 sat timeout +358 timeout timeout +359 unsat timeout +360 unsat timeout +361 timeout timeout +362 unsat timeout +363 unsat timeout +364 unsat timeout +365 sat timeout +366 sat timeout +367 unsat timeout +368 unsat unsat +369 sat timeout +370 sat timeout +371 unsat unsat +372 sat timeout +373 sat timeout +374 unsat timeout +375 sat timeout +376 timeout timeout +377 sat timeout +378 sat timeout +379 unsat timeout +380 sat timeout +381 unsat timeout +382 sat timeout +383 unsat timeout +384 sat timeout +385 sat timeout +386 sat timeout +387 sat timeout +388 sat timeout +389 sat timeout +390 sat timeout +391 unsat timeout +392 unsat timeout +393 sat timeout +394 sat timeout +395 sat timeout +396 sat timeout +397 sat timeout +398 sat timeout +399 sat timeout +400 sat timeout +401 sat timeout +402 sat timeout +403 sat timeout +404 sat timeout +405 sat timeout +406 sat timeout +407 unsat timeout +408 sat timeout +409 sat timeout +410 sat timeout +411 sat timeout +412 sat timeout +413 sat timeout +414 sat timeout +415 sat timeout +416 timeout timeout +417 timeout timeout +418 sat timeout +419 timeout timeout +420 sat timeout +421 sat timeout +422 timeout timeout +423 timeout timeout +424 timeout timeout +425 timeout timeout From 2491756e8cb2430a00548cb3833e2f995d51d1a0 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Sun, 14 Jan 2024 02:28:56 +0100 Subject: [PATCH 19/25] non-linear: eliminate unilaterally resolvable CHCs If a predicate only occurs on the RHS of a single CHC, then there is only one choice how to eliminate the predicate on the LHS of other CHCs. Thus, we can pre-compute all resolvents where one of parent clauses has a unique RHS. Previously the CHC parser would directly output an ITS instance. But it's hard to reason about the side effect of deleting/modifying rules in the ITS. It's more convenient to preprocess the CHCs first and then construct the ITS instance from it. Thus, the CHC parser now outputs a set of clauses. With this change we get: - chc comp 2022: +16 UNSAT - chc comp 2023: +9 UNSAT --- benchmarks/LIA.txt | 38 ++-- benchmarks/comp23-LIA-nonlin.txt | 52 ++--- run_benchmark.sh | 44 ++-- src/abmc/abmc.cpp | 2 +- src/analysis/preprocessing.cpp | 16 +- src/analysis/preprocessing.hpp | 2 +- src/analysis/reachability.cpp | 49 ++--- src/analysis/reachability.hpp | 4 - src/bmc/bmc.cpp | 2 +- src/its/itsproblem.cpp | 225 ++++++++++++--------- src/its/itsproblem.hpp | 11 +- src/main.cpp | 11 +- src/nonlinear/clause.cpp | 174 +++++++++++++--- src/nonlinear/clause.hpp | 33 ++- src/nonlinear/linearsolver.hpp | 31 +-- src/nonlinear/nonlinear.cpp | 315 +++++++++++++++++++++++++++-- src/nonlinear/nonlinear.hpp | 8 +- src/parser/chc/CHCParseVisitor.cpp | 102 +++++----- src/parser/chc/CHCParseVisitor.h | 12 +- src/parser/chc/chcparser.cpp | 4 +- src/parser/chc/chcparser.hpp | 2 +- 21 files changed, 802 insertions(+), 335 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index e223eb37c..285af5594 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -1,4 +1,4 @@ -### Z3 ADCL +### Z3(117) ADCL(69) 000 sat timeout 001 sat timeout 002 sat timeout @@ -132,13 +132,13 @@ 130 timeout unsat 131 timeout timeout 132 timeout unknown -133 unsat timeout -134 unsat timeout +133 unsat unsat +134 unsat unsat 135 sat timeout -136 unsat timeout +136 unsat unsat 137 unsat unsat 138 sat unknown -139 unsat timeout +139 unsat unsat 140 sat timeout 141 unsat unsat 142 unsat unsat @@ -146,28 +146,28 @@ 144 sat timeout 145 unsat unsat 146 sat unknown -147 unsat timeout -148 unsat timeout +147 unsat unsat +148 unsat unsat 149 sat unknown -150 unsat timeout +150 unsat unsat 151 sat timeout -152 unsat timeout +152 unsat unsat 153 sat unknown 154 sat timeout 155 sat timeout 156 sat timeout -157 unsat timeout -158 unsat timeout +157 unsat unsat +158 unsat unsat 159 sat timeout 160 sat timeout 161 sat timeout 162 sat timeout 163 sat timeout -164 unsat timeout -165 unsat timeout +164 unsat unsat +165 unsat unsat 166 sat timeout 167 sat timeout -168 unsat timeout +168 unsat unsat 169 timeout timeout 170 timeout timeout 171 timeout timeout @@ -262,7 +262,7 @@ 260 sat timeout 261 sat unknown 262 sat unknown -263 unsat timeout +263 unsat unsat 264 unsat timeout 265 timeout timeout 266 unsat timeout @@ -270,7 +270,7 @@ 268 unsat timeout 269 sat timeout 270 sat timeout -271 unsat timeout +271 unsat unsat 272 sat unknown 273 unsat unsat 274 timeout timeout @@ -426,7 +426,7 @@ 424 sat timeout 425 sat timeout 426 unsat timeout -427 unsat timeout +427 unsat unsat 428 sat timeout 429 unsat timeout 430 unsat timeout @@ -452,5 +452,5 @@ 450 timeout timeout 451 unsat timeout 452 unsat timeout -453 sat timeout -454 sat timeout \ No newline at end of file +453 sat timeout +454 sat timeout diff --git a/benchmarks/comp23-LIA-nonlin.txt b/benchmarks/comp23-LIA-nonlin.txt index 21b6f294c..128a0582f 100644 --- a/benchmarks/comp23-LIA-nonlin.txt +++ b/benchmarks/comp23-LIA-nonlin.txt @@ -1,4 +1,4 @@ -### Z3 ADCL +### Z3(119) ADCL(40) 000 sat timeout 001 sat timeout 002 sat timeout @@ -98,34 +98,34 @@ 096 sat timeout 097 sat timeout 098 sat timeout -099 unsat timeout +099 unsat unsat 100 sat timeout 101 unsat unsat -102 unsat timeout -103 unsat timeout -104 unsat timeout +102 unsat unsat +103 unsat unsat +104 unsat unsat 105 sat timeout -106 unsat timeout +106 unsat unsat 107 sat timeout -108 unsat timeout -109 unsat timeout +108 unsat unsat +109 unsat unsat 110 sat timeout -111 unsat timeout +111 unsat unsat 112 sat timeout 113 sat timeout 114 timeout timeout 115 timeout timeout 116 sat timeout -117 unsat timeout +117 unsat unsat 118 timeout timeout -119 unsat timeout +119 unsat unsat 120 timeout timeout -121 unsat timeout +121 unsat unsat 122 unsat timeout 123 unsat timeout 124 unsat timeout 125 sat timeout -126 unsat timeout +126 unsat unsat 127 sat timeout 128 sat timeout 129 unsat timeout @@ -191,9 +191,9 @@ 189 unsat timeout 190 unsat unsat 191 unsat timeout -192 unsat unsat +192 unsat unsat 193 unsat unsat -194 unsat timeout +194 unsat unsat 195 unsat unsat 196 unsat timeout 197 sat timeout @@ -201,19 +201,19 @@ 199 unsat unsat 200 sat timeout 201 unsat unsat -202 unsat timeout +202 unsat unsat 203 unsat unsat -204 unsat unsat -205 unsat unsat -206 unsat unsat -207 unsat unsat +204 unsat timeout +205 unsat timeout +206 unsat timeout +207 unsat timeout 208 sat timeout 209 sat timeout 210 sat timeout 211 sat timeout 212 sat timeout 213 unsat unsat -214 unsat timeout +214 unsat timeout 215 unsat timeout 216 sat timeout 217 unsat timeout @@ -228,7 +228,7 @@ 226 sat timeout 227 sat timeout 228 sat timeout -229 unsat timeout +229 unsat unsat 230 sat timeout 231 unsat timeout 232 sat timeout @@ -237,7 +237,7 @@ 235 unsat timeout 236 unsat timeout 237 unsat timeout -238 unsat timeout +238 unsat unsat 239 sat timeout 240 sat timeout 241 sat timeout @@ -250,11 +250,11 @@ 248 unsat timeout 249 unsat timeout 250 unsat timeout -251 unsat timeout +251 unsat unsat 252 sat timeout 253 unsat timeout 254 unsat timeout -255 unsat timeout +255 unsat unsat 256 timeout timeout 257 sat timeout 258 sat timeout @@ -391,7 +391,7 @@ 389 sat timeout 390 sat timeout 391 unsat timeout -392 unsat timeout +392 unsat unsat 393 sat timeout 394 sat timeout 395 sat timeout diff --git a/run_benchmark.sh b/run_benchmark.sh index 1bf0d4c40..541d9a860 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -7,34 +7,26 @@ set -e pushd $(dirname ${BASH_SOURCE[0]}) cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -# cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -# cmake --build build pushd build make -j4 popd -# all fibonacci: 081,093,090,086,098,353,354,077,345,073,080,076,343,344 +# DEBUG: non-deterministic unsat/unknown for 133,139 -# unsat: 141,145,137 +# gdb --args \ +time ./build/loat-static \ + --mode reachability \ + --format horn \ + --proof-level 0 \ + --log \ + "../chc-comp22-benchmarks/LIA/chc-LIA_130.smt2" + # "../chc-comp23-benchmarks/test.smt2" + # "../chc-comp23-benchmarks/LIA-nonlin/chc-LIA_004.smt2" -# unsat (1m10s): 147 -# unsat (17m) : 134 --> (1m6s) - -# too long: 136,150 - -# with hack: unsat 342 - -# TODO: 147,134,342,157,148,152,168,139,133,165,158 - -# # gdb --args \ -# time ./build/loat-static \ -# --mode reachability \ -# --format horn \ -# --proof-level 0 \ -# "../chc-comp22-benchmarks/LIA/chc-LIA_141.smt2" +popd +exit -# popd -# exit +# CHECK: 073 () / 119 () / 126 () / 130 (1m13s) / 351 (2m50s) ########################################################################## @@ -49,14 +41,15 @@ do continue else read idx z3_result adcl_result <<< "$line" + # file="../chc-comp23-benchmarks/${benchmark}-nonlin/chc-${benchmark}_${idx}.smt2" file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" # if true; then - if [[ "$z3_result" == "unsat" ]] && [[ "$adcl_result" == "timeout" ]]; then - # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]] && [[ "$adcl_result" == "timeout" ]]; then - # if [[ "$z3_result" == "unsat" ]]; then + # if [[ "$z3_result" == "unsat" ]] && [[ "$adcl_result" == "timeout" ]]; then + # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then + # if [[ "$z3_result" != "sat" ]]; then # if [[ "$adcl_result" == "unknown" ]]; then - # if [[ "$adcl_result" == "unsat" ]]; then + if [[ "$adcl_result" == "unsat" ]]; then set +e result=$(timeout 20 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") # result=$(timeout 5 z3 "$file") @@ -81,6 +74,7 @@ do fi fi +# done < "./benchmarks/comp23-${benchmark}-nonlin.txt" done < "./benchmarks/${benchmark}.txt" # "undo" pushd diff --git a/src/abmc/abmc.cpp b/src/abmc/abmc.cpp index 31e4c5a9b..0f2df39b9 100644 --- a/src/abmc/abmc.cpp +++ b/src/abmc/abmc.cpp @@ -327,7 +327,7 @@ void ABMC::analyze() { its.print(std::cout); } proof.majorProofStep("Initial ITS", ITSProof(), its); - const auto res {Preprocess::preprocess(its)}; + const auto res {Preprocess::preprocess(its, false)}; if (res) { proof.concat(res.getProof()); if (Config::Analysis::log) { diff --git a/src/analysis/preprocessing.cpp b/src/analysis/preprocessing.cpp index ff98fbe65..ca169b77a 100644 --- a/src/analysis/preprocessing.cpp +++ b/src/analysis/preprocessing.cpp @@ -152,18 +152,18 @@ ResultViaSideEffects refine_dependency_graph(ITSProblem &its) { return res; } -ResultViaSideEffects Preprocess::preprocess(ITSProblem &its) { +ResultViaSideEffects Preprocess::preprocess(ITSProblem &its, bool incremental_mode) { ResultViaSideEffects res; ResultViaSideEffects sub_res; if (Config::Analysis::log) { std::cout << "starting preprocesing..." << std::endl; } - if (Config::Analysis::reachability() && its.nonLinearCHCs.size() == 0) { - // In this preprocessing step we remove transitions, that are not "forward reachable" - // from a initial location or "backward reachable" from a sink. Note, that this step - // is not valid though, if the ITS contains non-linear CHCs. For example a sink might - // only be reachable via a non-linear CHCs, but because non-linear CHCs are not acounted - // for in the dependency graph, we would remove the sink because it appears to be unreachable. + if (Config::Analysis::reachability() && !incremental_mode) { + // In this preprocessing step we remove transitions that are not "forward reachable" + // from an initial location or "backward reachable" from a sink. Note, that this step + // is not valid if the ITS contains non-linear CHCs. For example, a sink appears to + // be unreachable if it is only reachable via a non-linear CHC (which are not accounted + // for in the dependency graph). if (Config::Analysis::log) { std::cout << "removing irrelevant clauses..." << std::endl; } @@ -177,7 +177,7 @@ ResultViaSideEffects Preprocess::preprocess(ITSProblem &its) { } /* - Chaining linear paths is also problematic in incremental mode, because + Chaining linear paths is also problematic in incremental mode because we remove the chained rules/facts, which cuts-off paths that might be reachable via a non-linear rule. For example, consider the CHC problem: diff --git a/src/analysis/preprocessing.hpp b/src/analysis/preprocessing.hpp index d4cdfcc8e..7f29fbcd6 100644 --- a/src/analysis/preprocessing.hpp +++ b/src/analysis/preprocessing.hpp @@ -22,6 +22,6 @@ namespace Preprocess { - ResultViaSideEffects preprocess(ITSProblem &its); + ResultViaSideEffects preprocess(ITSProblem &its, bool incremental_mode); } diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index e8d6ba277..62442a15d 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -129,7 +129,7 @@ Reachability::Reachability(ITSProblem &chcs, bool incremental_mode): ITSExport::printForProof(chcs, std::cout); } - const auto res {Preprocess::preprocess(chcs)}; + const auto res {Preprocess::preprocess(chcs, incremental_mode)}; if (res) { proof.concat(res.getProof()); if (Config::Analysis::log) { @@ -243,19 +243,19 @@ void Reachability::update_cpx() { } Rule Reachability::compute_resolvent(const TransIdx idx, const BoolExpr &implicant) const { - static Rule dummy(top(), Subs()); + // static Rule dummy(top(), Subs()); // Resolvent only has to be computed in incremental mode (i.e. when the problem includes non linear CHCs) or // for complexity analysis. Otherwise we can skip it. - if (Config::Analysis::complexity() || incremental_mode) { + // if (Config::Analysis::complexity() || incremental_mode) { auto resolvent = idx->withGuard(implicant); if (!trace.empty()) { resolvent = Chaining::chain(trace.back().resolvent, resolvent).first; } return *Preprocess::preprocessRule(resolvent); - } else { - return dummy; - } + // } else { + // return dummy; + // } } bool Reachability::store_step(const TransIdx idx, const Rule &implicant, bool force) { @@ -758,7 +758,7 @@ void Reachability::analyze() { // First only derive "easy" to handle rules with linear guard. If that's not enough // also try rules with polynomial guard. Otherwise also consider exponential // (arbitrary) guards. - for (const auto tier: constraint_tiers) { + for (const auto tier: LinearSolver::constraint_tiers) { blocked_clauses[0].clear(); derive_new_facts(tier); @@ -790,11 +790,11 @@ void Reachability::analyze() { * this function returns nullopt. */ const std::optional Reachability::trace_as_fact() { - if (!incremental_mode) { - // In non-incremental mode the trace resolvent is not really computed and only a dummy expression. - // See `compute_resolvent` above. So `trace_as_fact` will silently return nonsense. - throw std::logic_error("Calling `trace_as_fact` in non-incremental mode doesn't make sense."); - } + // if (!incremental_mode) { + // // In non-incremental mode the trace resolvent is not really computed and only a dummy expression. + // // See `compute_resolvent` above. So `trace_as_fact` will silently return nonsense. + // throw std::logic_error("Calling `trace_as_fact` in non-incremental mode doesn't make sense."); + // } if (trace.empty()) { return {}; @@ -975,17 +975,18 @@ void Reachability::restart() { } void Reachability::add_clauses(const std::set &clauses) { - bool any_linear_clauses = false; - for (const auto &chc : clauses) { - chcs.addClause(chc); + for (const auto &chc: clauses) { if (chc.isLinear()) { - any_linear_clauses = true; + chcs.addClause(chc); + } else { + throw std::logic_error("Reachability::add_clauses: tried to add non-linear CHC"); } } - if (any_linear_clauses) { + // only start preprocessing again if at least one new clause was added. + if (!clauses.empty()) { // TODO: are other preprocessing steps also unsound when in non-linear context? - const auto res {Preprocess::preprocess(chcs)}; + const auto res {Preprocess::preprocess(chcs, incremental_mode)}; if (res) { proof.concat(res.getProof()); if (Config::Analysis::log) { @@ -1003,18 +1004,6 @@ void Reachability::add_clauses(const std::set &clauses) { } } -const std::set Reachability::get_initial_facts() const { - std::set facts; - for (const auto trans_idx : chcs.getInitialTransitions()) { - facts.insert(chcs.clauseFrom(trans_idx)); - } - return facts; -} - -const std::set Reachability::get_non_linear_chcs() const { - return chcs.nonLinearCHCs; -} - void Reachability::analyze(ITSProblem &its) { Reachability(its, false).analyze(); } diff --git a/src/analysis/reachability.hpp b/src/analysis/reachability.hpp index 08b62a9ac..36abb4452 100644 --- a/src/analysis/reachability.hpp +++ b/src/analysis/reachability.hpp @@ -326,10 +326,6 @@ class Reachability : public ILinearSolver { const std::set derive_new_facts(LinearSolver::ConstraintTier max_constr_type) override; - const std::set get_initial_facts() const override; - - const std::set get_non_linear_chcs() const override; - static void analyze(ITSProblem &its); }; diff --git a/src/bmc/bmc.cpp b/src/bmc/bmc.cpp index 179d4be4c..44d512523 100644 --- a/src/bmc/bmc.cpp +++ b/src/bmc/bmc.cpp @@ -38,7 +38,7 @@ void BMC::analyze() { its.print(std::cout); } proof.majorProofStep("Initial ITS", ITSProof(), its); - const auto res {Preprocess::preprocess(its)}; + const auto res {Preprocess::preprocess(its, false)}; if (res) { proof.concat(res.getProof()); if (Config::Analysis::log) { diff --git a/src/its/itsproblem.cpp b/src/its/itsproblem.cpp index 958087dd2..89410df93 100644 --- a/src/its/itsproblem.cpp +++ b/src/its/itsproblem.cpp @@ -21,6 +21,7 @@ #include "inttheory.hpp" #include "smtfactory.hpp" #include "chain.hpp" +#include using namespace std; @@ -348,14 +349,19 @@ const Clause ITSProblem::clauseFrom(const LocationIdx lhs_loc, const LocationIdx } const auto final_guard = BExpression::buildAnd(guard_conj)->simplify(); - const auto rhs = FunApp(rhs_loc, rhs_args); + const auto rhs_pred_name = getLocationNames().at(rhs_loc); + + const std::optional rhs = rhs_loc == getSink() + ? std::optional() + : FunApp(rhs_pred_name, rhs_args); if (lhs_loc == getInitialLocation()) { // rule is a linear CHC with no LHS predicates, ie a "fact" return Clause({}, rhs, final_guard).normalize(); } else { // rule is a linear CHC with exactly one LHS predicates, ie a "rule" - return Clause({ FunApp(lhs_loc, prog_vars) }, rhs, final_guard).normalize(); + const auto lhs_pred_name = getLocationNames().at(lhs_loc); + return Clause({ FunApp(lhs_pred_name, prog_vars) }, rhs, final_guard).normalize(); } } @@ -371,110 +377,147 @@ const Clause ITSProblem::clauseFrom(TransIdx rule) const { } /** - * TODO docs + * Adds a linear clause to the ITS problem by converting it to an ITS rule. + * Throws an error if the given clause is non-linear. */ -const FunApp normalizePredicate(unsigned int_var_count, unsigned bool_var_count, const FunApp &pred) { - std::vector int_args; - int_args.reserve(int_var_count); - std::vector bool_args; - bool_args.reserve(bool_var_count); - - for (const Var &arg: pred.args) { - if (std::holds_alternative(arg)) { - int_args.push_back(arg); +void ITSProblem::addClause(const Clause &c) { + if (!c.isLinear()) { + throw std::logic_error("Tried to add non-linear clause to ITS"); + } + + // If the clause is linear, extract the single LHS predicate. Or in case there are zero + // LHS predicates, construct a dummy predicate with the initial ITS location as the + // predicate symbol. + const auto initial_loc_name = getLocationNames().at(getInitialLocation()); + const FunApp lhs = c.lhs.size() == 1 ? *c.lhs.begin() : FunApp(initial_loc_name, {}); + + const std::vector rhs_args = c.rhs.has_value() + ? c.rhs.value().args + : std::vector(); + + Subs ren; + // replace the arguments of the body predicate with the corresponding program variables + unsigned bool_arg {0}; + unsigned int_arg {0}; + for (unsigned i = 0; i < lhs.args.size(); ++i) { + if (std::holds_alternative(lhs.args[i])) { + ren.put( + std::get(lhs.args[i]), + numProgVars[int_arg] + ); + ++int_arg; } else { - bool_args.push_back(arg); + ren.put( + std::get(lhs.args[i]), + std::get(expr::toExpr(boolProgVars[bool_arg])) + ); + ++bool_arg; } } - - while (int_args.size() < int_var_count) { - int_args.push_back(NumVar::next()); + VarSet cVars; + for (const auto &var: rhs_args) { + cVars.insert(var); + } + c.guard->collectVars(cVars); + // replace all other variables from the clause with temporary variables + for (const auto &x: cVars) { + if (!ren.contains(x)) { + ren.put(x, expr::toExpr(expr::next(x))); + } + } + bool_arg = 0; + int_arg = 0; + Subs up; + for (unsigned i = 0; i < rhs_args.size(); ++i) { + if (std::holds_alternative(rhs_args[i])) { + up.put(numProgVars[int_arg], ren.get(std::get(rhs_args[i]))); + ++int_arg; + } else if (std::holds_alternative(rhs_args[i])) { + up.put(boolProgVars[bool_arg], ren.get(std::get(rhs_args[i]))); + ++bool_arg; + } else { + throw std::logic_error("unsupported theory in CHCParseVisitor"); + } + } + for (unsigned i = int_arg; i < numProgVars.size(); ++i) { + up.put(numProgVars[i], NumVar::next()); + } + for (unsigned i = bool_arg; i < boolProgVars.size(); ++i) { + up.put(boolProgVars[i], std::get(expr::toExpr(BoolVar::next()))); } - while (bool_args.size() < bool_var_count) { - bool_args.push_back(BoolVar::next()); + const auto lhs_loc = getLocationIdx(lhs.name); + const auto rhs_pred_name = c.rhs.has_value() ? c.rhs.value().name : "LoAT_sink"; + const auto rhs_loc = c.rhs.has_value() ? getLocationIdx(c.rhs.value().name) : getSink(); + + if (!rhs_loc.has_value()) { + throw std::logic_error("tried to add clause with unknown RHS predicate called " + rhs_pred_name); + } + if (!lhs_loc.has_value()) { + throw std::logic_error("tried to add clause with unknown LHS predicate called " + lhs.name); } - int_args.insert(int_args.end(), bool_args.begin(), bool_args.end()); - return FunApp(pred.loc, int_args); + up.put(NumVar::loc_var, rhs_loc.value()); + const BoolExpr guard = c.guard->subs(ren)->simplify() & Rel::buildEq(NumVar::loc_var, lhs_loc.value()); + addRule(Rule(guard, up), lhs_loc.value()); } /** - * Adds a clause to the ITS problem. If the clause is linear, it's converted to an ITS rule. - * If the clause is non-linear it's separately stored in `nonLinearCHCs`. + * Construct an ITS instance from a set of clauses. Non-linear clauses are ignored in the sense that + * they don't contribute ITS rules. However, predicates that only occur in non-linear rules are added + * as locations, because if we later add new rules to the ITS via `addClause` that are derived from + * non-linear clauses, we expect all locations to already be known. */ -void ITSProblem::addClause(const Clause &c) { - if (c.isLinear()) { - // If the clause is linear, extract the single LHS predicate. Or in case there are zero - // LHS predicates, construct a dummy predicate with the initial ITS location as the - // predicate symbol. - const FunApp lhs = c.lhs.size() == 1 ? *c.lhs.begin() : FunApp(getInitialLocation(), {}); - - Subs ren; - // replace the arguments of the body predicate with the corresponding program variables - unsigned bool_arg {0}; - unsigned int_arg {0}; - for (unsigned i = 0; i < lhs.args.size(); ++i) { - if (std::holds_alternative(lhs.args[i])) { - ren.put( - std::get(lhs.args[i]), - numProgVars[int_arg] - ); - ++int_arg; - } else { - ren.put( - std::get(lhs.args[i]), - std::get(expr::toExpr(boolProgVars[bool_arg])) - ); - ++bool_arg; +ITSPtr ITSProblem::fromClauses(const std::set& chcs) { + std::vector chc_vec(chcs.begin(), chcs.end()); + return fromClauses(chc_vec); +} +ITSPtr ITSProblem::fromClauses(const std::vector& chc_problem) { + ITSPtr its = std::make_shared(); + + auto [max_int_arity, max_bool_arity] = maxArity(chc_problem); + + its->setInitialLocation(its->addNamedLocation("LoAT_init")); + // Collect all predicate that appear in `chc_problem`, add them as + // locations to the ITS : + std::map locations; + for (const auto &chc: chc_problem) { + if (chc.rhs.has_value()) { + const auto& rhs = chc.rhs.value(); + + bool location_already_added = its->getLocationIdx(rhs.name).has_value(); + if (!location_already_added) { + locations[rhs.name] = its->addNamedLocation(rhs.name); } } - VarSet cVars; - for (const auto &var: c.rhs.args) { - cVars.insert(var); - } - c.guard->collectVars(cVars); - // replace all other variables from the clause with temporary variables - for (const auto &x: cVars) { - if (!ren.contains(x)) { - ren.put(x, expr::toExpr(expr::next(x))); - } - } - bool_arg = 0; - int_arg = 0; - Subs up; - for (unsigned i = 0; i < c.rhs.args.size(); ++i) { - if (std::holds_alternative(c.rhs.args[i])) { - up.put(numProgVars[int_arg], ren.get(std::get(c.rhs.args[i]))); - ++int_arg; - } else if (std::holds_alternative(c.rhs.args[i])) { - up.put(boolProgVars[bool_arg], ren.get(std::get(c.rhs.args[i]))); - ++bool_arg; - } else { - throw std::logic_error("unsupported theory in CHCParseVisitor"); + + for (const auto &pred: chc.lhs) { + bool location_already_added = its->getLocationIdx(pred.name).has_value(); + if (!location_already_added) { + locations[pred.name] = its->addNamedLocation(pred.name); } } - for (unsigned i = int_arg; i < numProgVars.size(); ++i) { - up.put(numProgVars[i], NumVar::next()); - } - for (unsigned i = bool_arg; i < boolProgVars.size(); ++i) { - up.put(boolProgVars[i], std::get(expr::toExpr(BoolVar::next()))); + } + + // Construct "normalized" vectors of bool/int program variables, + // because in ITS rules must all have the same argument vector. + std::vector int_vars; + for (unsigned i = 0; i < max_int_arity; ++i) { + int_vars.emplace_back(NumVar::nextProgVar()); + } + its->numProgVars = int_vars; + + std::vector bool_vars; + for (unsigned i = 0; i < max_bool_arity; ++i) { + bool_vars.emplace_back(BoolVar::nextProgVar()); + } + its->boolProgVars = bool_vars; + + for (const auto &chc: chc_problem) { + if (chc.isLinear()) { + its->addClause(chc); } - up.put(NumVar::loc_var, c.rhs.loc); - const BoolExpr guard = c.guard->subs(ren)->simplify() & Rel::buildEq(NumVar::loc_var, lhs.loc); - addRule(Rule(guard, up), lhs.loc); - } else { - std::set lhs_normalized; - - for (const FunApp &pred: c.lhs) { - const auto pred_normalized = normalizePredicate(numProgVars.size(), boolProgVars.size(), pred); - lhs_normalized.insert(pred_normalized); - } - - nonLinearCHCs.insert(Clause( - lhs_normalized, - normalizePredicate(numProgVars.size(), boolProgVars.size(), c.rhs), - c.guard->simplify() - )); - } + } + + return its; } diff --git a/src/its/itsproblem.hpp b/src/its/itsproblem.hpp index e6ea00459..d9e82a896 100644 --- a/src/its/itsproblem.hpp +++ b/src/its/itsproblem.hpp @@ -111,11 +111,9 @@ class ITSProblem { // once after parsing and should not be mutated during analysis. But because the ITS instance is constructed // incrementally, we can't pass these values into the constructor and having public getters+setters // is no different from making the attributes public directly. - std::vector numProgVars; - std::vector boolProgVars; - const std::vector getProgVars() const; - - std::set nonLinearCHCs; + std::vector numProgVars; // TODO: clean up + std::vector boolProgVars; // TODO: clean up + const std::vector getProgVars() const; // TODO: clean up void addClause(const Clause &chc); @@ -123,6 +121,9 @@ class ITSProblem { const Clause clauseFrom(const LocationIdx lhs_loc, const LocationIdx rhs_loc, const Rule& rule) const; + static std::shared_ptr fromClauses(const std::set& chc_problem); + static std::shared_ptr fromClauses(const std::vector& chc_problem); + protected: DG graph; diff --git a/src/main.cpp b/src/main.cpp index a10ba19c2..6fa7370ac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ #include "main.hpp" #include "itsparser.hpp" +#include "itsproblem.hpp" #include "parser.hpp" #include "chcparser.hpp" #include "cintparser.hpp" @@ -173,6 +174,8 @@ int main(int argc, char *argv[]) { } ITSPtr its; + std::vector clauses; + switch (Config::Input::format) { case Config::Input::Koat: its = parser::ITSParser::loadFromFile(filename); @@ -181,7 +184,7 @@ int main(int argc, char *argv[]) { its = sexpressionparser::Parser::loadFromFile(filename); break; case Config::Input::Horn: - its = hornParser::HornParser::loadFromFile(filename); + clauses = hornParser::HornParser::loadFromFile(filename); break; case Config::Input::C: its = cintParser::CIntParser::loadFromFile(filename); @@ -194,11 +197,11 @@ int main(int argc, char *argv[]) { yices::init(); switch (Config::Analysis::engine) { case Config::Analysis::ADCL: - if (its->nonLinearCHCs.size() == 0) { + if (allLinear(clauses)) { + its = ITSProblem::fromClauses(clauses); reachability::Reachability::analyze(*its); } else { - auto linear_solver = reachability::Reachability(*its, true); - NonLinearSolver::analyze(linear_solver); + NonLinearSolver::analyze(clauses); } break; case Config::Analysis::BMC: diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index 8ea2767e5..ab2929b6f 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -39,11 +39,11 @@ const Var varAt(const Var &var, const Subs &subs) { * * In practice we normalize all predicates to have the same arity with same argument * types in the same order. So only differing predicate symbols should be a reason - * for two predicates to not be unifiable. But for "local" correctness we also check - * arity and argument types. + * for two predicates to not be unifiable. But this is a non-local assumption (that might + * change) so for "local correctness" we also check arity and argument types. */ const std::optional computeUnifier(const FunApp &pred1, const FunApp &pred2) { - if (pred1.loc == pred2.loc) { + if (pred1.name == pred2.name) { return computeUnifier(pred1.args, pred2.args); } else { return {}; @@ -108,7 +108,7 @@ const FunApp FunApp::renameWith(const Subs &renaming) const { args_renamed.push_back(target_var); } - return FunApp(loc, args_renamed); + return FunApp(name, args_renamed); } /** @@ -122,6 +122,32 @@ const VarSet FunApp::vars() const { return vs; } +/** + * Count number arguments with type integer valued arguments. + */ +unsigned long FunApp::intArity() const { + unsigned arity {0}; + for (const auto &arg: args) { + if (std::holds_alternative(arg)) { + arity++; + } + } + return arity; +} + +/** + * Count number arguments with type integer valued arguments. + */ +unsigned long FunApp::boolArity() const { + unsigned arity {0}; + for (const auto &arg: args) { + if (std::holds_alternative(arg)) { + arity++; + } + } + return arity; +} + /** * Apply `renaming` to all variables in the clause, i.e variables in the guard and the * arguments of all predicates on both LHS and RHS. Will throw an error if the renaming @@ -133,9 +159,17 @@ const Clause Clause::renameWith(const Subs &renaming) const { lhs_renamed.insert(pred.renameWith(renaming)); } - const FunApp rhs_renamed = rhs.renameWith(renaming); const auto guard_renamed = guard->subs(renaming); - return Clause(lhs_renamed, rhs_renamed, guard_renamed); + + if (rhs.has_value()) { + return Clause( + lhs_renamed, + rhs.value().renameWith(renaming), + guard_renamed + ); + } else { + return Clause(lhs_renamed, {}, guard_renamed); + } } /** @@ -146,7 +180,9 @@ const VarSet Clause::vars() const { for (const auto &pred: lhs) { vs.insertAll(pred.vars()); } - vs.insertAll(rhs.vars()); + if (rhs.has_value()) { + vs.insertAll(rhs.value().vars()); + } guard->collectVars(vs); return vs; } @@ -173,6 +209,11 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA throw std::logic_error("Given `pred` is not on the LHS of `chc`"); } + // No resolvent if `this` is a query, ie has no RHS predicate. + if (this->isQuery()) { + return {}; + } + // Make sure variables in `this` and `chc` are disjoint. Subs renaming; const VarSet this_vars {this->vars()}; @@ -183,7 +224,7 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA } const Clause this_with_disjoint_vars = this->renameWith(renaming); - const auto unifier = computeUnifier(this_with_disjoint_vars.rhs, pred); + const auto unifier = computeUnifier(this_with_disjoint_vars.rhs.value(), pred); // If the predicates are not unifiable, we don't throw an error but return // nullopt. That way the caller can filter out unifiable predicates using this @@ -219,23 +260,26 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA * Partition clauses into linear- and non-linear. The first tuple component holds the linear * clauses while second component holds the non-linear clauses. */ -const std::tuple, std::set> partitionByDegree(const std::set chcs) { - std::set linear; - std::set non_linear; +const std::tuple, std::set> partitionByDegree(const std::set& chcs) { + std::set linear_chcs; + std::set non_linear_chcs; for (const Clause& chc: chcs) { if (chc.isLinear()) { - linear.insert(chc); + linear_chcs.insert(chc); } else { - non_linear.insert(chc); + non_linear_chcs.insert(chc); } } - return std::make_tuple(linear, non_linear); + return std::make_tuple(linear_chcs, non_linear_chcs); } /** - * Normalize the variable indices of the RHS predicate arguments. For example + * Normalize variable names to detect syntactic equivalence of clauses up-to-renaming. + * + * For that the RHS predicate arguments are renamed to always have the indices 0,1,2,3,... + * For example * * i44 > i9 /\ b23 ==> F(i44,b23,i9) * @@ -243,9 +287,8 @@ const std::tuple, std::set> partitionByDegree(const std * * i0 > i2 /\ b1 ==> F(i0,b1,i2) * - * So the variables indices on the right-hand-side should always be 0,1,2,3,etc. - * unless a variable occurs multiple times in the arguments of the original RHS - * predicate. For example: + * One exception is when a variable occurs multiple times in the arguments of the + * original RHS predicate. For example: * * i44 > i9 /\ b23 ==> F(i44,b23,i9,i44) * @@ -253,16 +296,20 @@ const std::tuple, std::set> partitionByDegree(const std * * i0 > i2 /\ b1 ==> F(i0,b1,i2,i0) * - * to preserve the implict equality. - * - * Useful to detect if clauses are syntactially equivalent up-to-renaming. + * to preserve the implict equality. But this is not a problem to detect syntatic + * equivalence. */ const Clause Clause::normalize() const { + if (!this->rhs.has_value()) { + return *this; + } + const auto rhs = this->rhs.value(); + // construct a vector of variables with the same length as `this->rhs.args` // and the same variable types in each position, except that the variable // indices are simply: 0,1,2,3,etc. std::vector target_args; - for (unsigned i=0; i < size(this->rhs.args); i++) { + for (unsigned i=0; i < size(rhs.args); i++) { const auto var = std::visit(Overload{ [i](const NumVar&) { return Var(NumVar(i)); @@ -270,12 +317,12 @@ const Clause Clause::normalize() const { [i](const BoolVar&) { return Var(BoolVar(i)); } - }, this->rhs.args[i]); + }, rhs.args[i]); target_args.push_back(var); } - const auto unifier = computeUnifier(this->rhs.args, target_args); + const auto unifier = computeUnifier(rhs.args, target_args); if (unifier.has_value()) { return this->renameWith(unifier.value()); @@ -294,10 +341,75 @@ bool Clause::isLinear() const { return lhs.size() <= 1; } +/** + * Returns true iff the clause has no LHS predicates. + */ +bool Clause::isFact() const { + return lhs.size() == 0; +} + +/** + * Returns true iff the clause has no RHS predicate. + */ +bool Clause::isQuery() const { + return !rhs.has_value(); +} + +/** + * Return first predicate with given `name` if it occurs on the LHS of the clause. + * Returns nullopt if there is no predicate with given `name`. + */ +std::optional Clause::getLHSPredicate(const std::basic_string name) const { + for (const auto& pred: lhs) { + if (pred.name == name) { + return pred; + } + } + + return {}; +} + +/** + * Find maximum number of int/bool-valued variables that appear in the arguments of any + * any predicate in any clause of `chc_problem`. Returns counts as pair: + * + * [ max_int_arity, max_bool_arity ] = maxArity(chc_problem); + * + */ +const std::pair maxArity(const std::vector& chc_problem) { + unsigned long max_int_arity {0}; + unsigned long max_bool_arity {0}; + + for (const auto &chc: chc_problem) { + if (chc.rhs.has_value()) { + const auto& rhs = chc.rhs.value(); + max_int_arity = std::max(max_int_arity, rhs.intArity()); + max_bool_arity = std::max(max_bool_arity, rhs.boolArity()); + } + + for (const auto &pred: chc.lhs) { + max_int_arity = std::max(max_int_arity, pred.intArity()); + max_bool_arity = std::max(max_bool_arity, pred.boolArity()); + } + } + + return std::pair(max_int_arity, max_bool_arity); +} + +bool allLinear(const std::vector& chcs) { + for (const auto& chc: chcs) { + if (!chc.isLinear()) { + return false; + } + } + + return true; +} + bool operator<(const FunApp &fun1, const FunApp &fun2) { - if (fun1.loc < fun2.loc) { + if (fun1.name < fun2.name) { return true; - } else if (fun2.loc < fun1.loc) { + } else if (fun2.name < fun1.name) { return false; } else { return fun1.args < fun2.args; @@ -323,7 +435,7 @@ bool operator<(const Clause &c1, const Clause &c2) { } std::ostream &operator<<(std::ostream &s, const FunApp &fun) { - s << "F" << fun.loc; + s << fun.name; if (fun.args.size() > 0) { auto iter = fun.args.begin(); @@ -347,7 +459,13 @@ std::ostream &operator<<(std::ostream &s, const Clause &chc) { s << fun_app << " /\\ "; } - s << chc.guard << " ==> " << chc.rhs; + s << chc.guard << " ==> "; + + if (chc.rhs.has_value()) { + s << chc.rhs.value(); + } else { + s << "false"; + } return s; } diff --git a/src/nonlinear/clause.hpp b/src/nonlinear/clause.hpp index 6fc14e5d2..b49c2a498 100644 --- a/src/nonlinear/clause.hpp +++ b/src/nonlinear/clause.hpp @@ -6,21 +6,28 @@ class FunApp { public: - const LocationIdx loc; + const std::basic_string name; const std::vector args; - FunApp(const LocationIdx loc, const std::vector args): loc(loc), args(args) {} + FunApp( + const std::basic_string name, + const std::vector args + ): name(name), args(args) {} const FunApp renameWith(const Subs &renaming) const; const VarSet vars() const; + + unsigned long intArity() const; + + unsigned long boolArity() const; }; class Clause { public: const std::set lhs; - const FunApp rhs; + const std::optional rhs; const BoolExpr guard; /** @@ -32,11 +39,14 @@ class Clause { * lhs guard rhs * * @param lhs - a set of predicates on the left-hand-side (LHS) of the implication. - * @param rhs - a single predicate on the right-hand-side (RHS) of the implication. + * @param rhs - an optional predicate on the right-hand-side (RHS) of the implication. * @param guard - a boolean expression describing the clause constraint. */ - Clause(const std::set &lhs, const FunApp &rhs, const BoolExpr &guard) - : lhs(lhs), rhs(rhs), guard(guard) {} + Clause( + const std::set lhs, + const std::optional rhs, + const BoolExpr guard + ) : lhs(lhs), rhs(rhs), guard(guard) {} const Clause renameWith(const Subs &renaming) const; @@ -45,15 +55,24 @@ class Clause { const Clause normalize() const; bool isLinear() const; + + bool isFact() const; + + bool isQuery() const; const VarSet vars() const; + std::optional getLHSPredicate(const std::basic_string name) const; }; -const std::tuple, std::set> partitionByDegree(const std::set chcs); +const std::tuple, std::set> partitionByDegree(const std::set& chcs); const std::optional computeUnifier(const std::vector &args1, const std::vector &args2); +const std::pair maxArity(const std::vector& chc_problem); + +bool allLinear(const std::vector& chcs); + // implement comparison operators so they can be stored in std::set bool operator<(const Clause &c1, const Clause &c2); bool operator<(const FunApp &fun1, const FunApp &fun2); diff --git a/src/nonlinear/linearsolver.hpp b/src/nonlinear/linearsolver.hpp index 4aeec9ff6..c699834cb 100644 --- a/src/nonlinear/linearsolver.hpp +++ b/src/nonlinear/linearsolver.hpp @@ -4,8 +4,18 @@ namespace LinearSolver { enum Result { Unsat, Sat, Unknown, Pending }; enum ConstraintTier { Linear, Polynomial, Exponential }; + + /** + * Fixed list of all constaint tiers in order of solving difficulty. + */ + const std::vector constraint_tiers = { + LinearSolver::ConstraintTier::Linear, + LinearSolver::ConstraintTier::Polynomial, + LinearSolver::ConstraintTier::Exponential + }; } + /** * Any linear CHC solver that implements this interface (e.g. ADCL) is compatible with the * non-linear CHC solver. The linear solver must also act as a data structure @@ -47,25 +57,16 @@ class ILinearSolver { LinearSolver::ConstraintTier max_constr_tier ) = 0; - virtual LinearSolver::Result get_analysis_result() const = 0; - /** - * Adds a new clauses to the linear solver in batch. The added clauses can be - * linear or non-linear. + * Return current status of the solver. Status Unknown or Sat should be reset to + * Pending, when new clauses are added via `add_clauses`. The status Unsat should + * be considered final. */ - virtual void add_clauses(const std::set &clauses) = 0; - - virtual const std::set get_initial_facts() const = 0; - - virtual const std::set get_non_linear_chcs() const = 0; + virtual LinearSolver::Result get_analysis_result() const = 0; /** - * Fixed list of all constaint tiers in order of solving difficulty. + * Adds new clauses to the solver in batch. The added clauses must be linear. */ - const std::vector constraint_tiers = { - LinearSolver::ConstraintTier::Linear, - LinearSolver::ConstraintTier::Polynomial, - LinearSolver::ConstraintTier::Exponential - }; + virtual void add_clauses(const std::set &clauses) = 0; }; diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 88c8a2caf..cb2910865 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -2,6 +2,8 @@ #include "booltheory.hpp" #include "config.hpp" #include "expr.hpp" +#include "itsproblem.hpp" +#include "reachability.hpp" #include "linearizingsolver.hpp" #include "linearsolver.hpp" #include "smt.hpp" @@ -36,27 +38,36 @@ trace : */ +void NonLinearSolver::analyze(const std::vector& initial_chcs) { + const std::set chcs_preprocessed = presolve(normalize_all_preds(initial_chcs)); -void NonLinearSolver::analyze(ILinearSolver &linear_solver) { - LinearizingSolver smt(4294967295u); + // Setup ITS and linear solver: + ITSPtr its = ITSProblem::fromClauses(chcs_preprocessed); + // TODO: why cant we give `linear_solver` the type `ILinearSolver` ? + auto linear_solver = reachability::Reachability(*its, true); + + auto [linear_chcs, non_linear_chcs] = partitionByDegree(chcs_preprocessed); // For the first loop iteration, the list of facts is composed of the original facts // given in the CHC problem. In subsequent iterations, the facts are whatever // the linear solver managed to derive. - std::set facts(linear_solver.get_initial_facts()); + std::set facts; + for (const auto &chc: linear_chcs) { + if (chc.isFact()) { + facts.insert(chc); + } + } - for (const auto max_constraint_tier: linear_solver.constraint_tiers) { + for (const auto max_constraint_tier: LinearSolver::constraint_tiers) { while (true) { if (Config::Analysis::log) { std::cout << "============= non-linear solver main loop =============" << std::endl; } - const std::set non_linear_chcs(linear_solver.get_non_linear_chcs()); - // Do resolution with all combinations of facts and non-linear clauses to // get every possible linear clause that we can derive in this iteration. std::list resolvents; - for (const auto& non_linear_chc: linear_solver.get_non_linear_chcs()) { + for (const auto& non_linear_chc: non_linear_chcs) { const auto res(all_resolvents(non_linear_chc, facts)); resolvents.insert( resolvents.end(), @@ -65,7 +76,7 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { ); } - // Filter-out syntactially redundant resolvents + // Filter-out syntactially redundant resolvents: const std::set resolvents_distinct(resolvents.begin(), resolvents.end()); if (Config::Analysis::log) { std::cout @@ -74,20 +85,25 @@ void NonLinearSolver::analyze(ILinearSolver &linear_solver) { << resolvents.size() << " resolvents are syntactially redundant" << std::endl; - } + } - linear_solver.add_clauses(resolvents_distinct); + // Add linear resolvents to linear solver and store non-linear resolvents for the next resolutions round: + const auto [linear_resolvents, non_linear_resolvents] = partitionByDegree(resolvents_distinct); + linear_solver.add_clauses(linear_resolvents); + non_linear_chcs.insert(non_linear_resolvents.begin(), non_linear_resolvents.end()); + // Hand over to linear solver and let it derive new facts: facts.clear(); facts = linear_solver.derive_new_facts(max_constraint_tier); - // const auto new_facts = linear_solver.derive_new_facts(max_constraint_tier); - // facts.insert(new_facts.begin(), new_facts.end()); if (Config::Analysis::log) { for (const auto &fact: facts) { std::cout << "new fact: " << fact << std::endl; } } + // If the linear solver proved Unsat, we can terminate. Otherwise, we use the derived facts to in the next + // resolution round to derive more linear clauses, that we can feed back into the linear solver. Unless + // the set of derived facts is empty, then we have explored the entire search space and can also terminate: const auto result = linear_solver.get_analysis_result(); if (result == LinearSolver::Result::Unsat) { break; @@ -214,3 +230,278 @@ void all_resolvents_aux(const Clause& chc, const std::set::iterator pred // return res_without_head; } } + +const FunApp normalize_pred(unsigned int_var_count, unsigned bool_var_count, const FunApp &pred) { + std::vector int_args; + int_args.reserve(int_var_count); + std::vector bool_args; + bool_args.reserve(bool_var_count); + + for (const Var &arg: pred.args) { + if (std::holds_alternative(arg)) { + int_args.push_back(arg); + } else { + bool_args.push_back(arg); + } + } + + while (int_args.size() < int_var_count) { + int_args.push_back(NumVar::next()); + } + + while (bool_args.size() < bool_var_count) { + bool_args.push_back(BoolVar::next()); + } + + int_args.insert(int_args.end(), bool_args.begin(), bool_args.end()); + return FunApp(pred.name, int_args); +} + +/** + * Add and reorder int/bool arguments of each predicate in each clause in `chc_problem` until all + * of them have the same number of int/bool variables, that is: + * + * F(int_1, int_2, ..., int_n, bool_1, bool_2, ..., bool_m) + * + * This is necessary because the facts dervied by the linear solver (at least Reachability) have this shape. + * And to correctly resolve the facts with non-linear clauses, it's easier if all clauses have this shape to + * begin with. + */ +const std::vector normalize_all_preds(const std::vector& initial_chcs) { + std::vector chcs_normalized; + + auto [ max_int_arity, max_bool_arity ] = maxArity(initial_chcs); + + for (const auto& chc: initial_chcs) { + std::set lhs_normalized; + + for (const FunApp &pred: chc.lhs) { + const auto pred_normalized = normalize_pred( + max_int_arity, + max_bool_arity, + pred + ); + lhs_normalized.insert(pred_normalized); + } + + if (chc.rhs.has_value()) { + chcs_normalized.push_back(Clause( + lhs_normalized, + normalize_pred(max_int_arity, max_bool_arity, chc.rhs.value()), + chc.guard->simplify() + )); + } else { + chcs_normalized.push_back(Clause( + lhs_normalized, + {}, + chc.guard->simplify() + )); + } + } + + return chcs_normalized; +} + +/** + * Returns a clause in `chcs`, whose RHS predicate has the name `pred_name`, but only if + * this clause is the ONLY instance with this RHS predicate. For example, in the set of CHCs: + * + * (c1) F ==> G + * (c2) F ==> H + * (c3) G ==> H + * + * the predicate `G` occurs on the RHS of exactly one clause (namely c1), while the + * predicate `F` never occurs on the RHS and `H` occurs twice. Thus: + * + * unilaterally_resolvable_with(F, {c1,c2,c3}) == null + * unilaterally_resolvable_with(G, {c1,c2,c3}) == c1 + * unilaterally_resolvable_with(H, {c1,c2,c3}) == null + * + */ +const std::optional unilaterally_resolvable_with(const FunApp& pred, const std::set& chcs) { + auto candidate = chcs.end(); // Initialize to end iterator, indicating no candidate found + + for (auto it = chcs.begin(); it != chcs.end(); ++it) { + if (it->rhs.has_value() && it->rhs.value().name == pred.name) { + if (candidate != chcs.end()) { + // Predicate occurs on RHS of multiple clauses. + return {}; + } else { + candidate = it; + } + } + } + + if (candidate != chcs.end()) { + return *candidate; + } else { + return {}; + } +} + +/** + * Compute resolvent of `left` and `right`. If the RHS predicate of `left` occurs + * multiple times on the LHS of `right`, then keep resolving until all predicates + * are eliminated. For example for + * + * (left) F ==> G + * (right) G /\ G ==> H + * + * we would resolve `left` with `right` twice, until `G` is completely eliminated. + */ +const Clause eliminate_pred(const Clause& left, const Clause& right) { + if (left.isQuery()) { + return right; + } + + const auto& left_pred = left.rhs.value(); + const auto& right_pred = right.getLHSPredicate(left_pred.name); + + if (right_pred.has_value()) { + return eliminate_pred(left, left.resolutionWith(right, right_pred.value()).value()); + } else { + return right; + } +} + +/** + * If a predicate only occurs on the RHS of a single CHC, then there is only one + * choice how to eliminate the predicate on the LHS of other CHCs. Thus, we can + * precompute all resolvents where one of parent clauses has a unique RHS. + * + * This reduces the number of clauses the CHC problem and has propagation effects. + * This can turn a non-linear CHC problem into a linear one, or even make it trivially + * UNSAT/SAT. For example, consider the non-linear CHC problem: + * + * (c1) X>0 ==> F(X) + * (c2) X<0 ==> G(X) + * (c3) F(X) /\ G(X) ==> H + * (c4) F(X) ==> H + * (c5) H ==> false + * + * The predicate `F(X)` occurs uniquely on the RHS of `c1` so we can precompute the + * resolvents of `c1` with any all clauses that have `F(X)` on the LHS (namely c3`, `c4`). + * We obtain: + * + * (c2) X<0 ==> G(X) + * (c1+c3) G(X) /\ X>0 ==> H + * (c1+c4) X>0 ==> H + * (c5) H ==> false + * + * Note that we can remove `c1` since it cannot be used for any future resolution steps + * anymore. Analogously, the predicate `G(X)` also occurs uniquely on the RHS of `c2`, + * so we eliminate `G(X)` as well: + * + * (c1+c2+c3) X<0 /\ X>0 ==> H + * (c1+c4) X>0 ==> H + * (c5) H ==> false + * + * The constraint of `c1+c2+c3` become UNSAT so this CHC can not be used in any + * reachability proof and we can remove it: + * + * (c1+c4) X>0 ==> H + * (c5) H ==> false + * + * Due to this removal the predicate `H` now also uccurs uniquely on the RHS of + * a CHC, so we can eliminate it as well: + * + * (c1+c4+c5) X>0 ==> false + * + * Thus, we are left with a trivial CHC problem. + */ +const std::set presolve(const std::vector& initial_chcs) { + // first collect all RHS predicate that occur in the CHC problem. + std::set rhs_preds; + for (const auto& chc: initial_chcs) { + if (chc.rhs.has_value()) { + rhs_preds.insert(chc.rhs.value()); + } + } + + std::set chcs(initial_chcs.begin(), initial_chcs.end()); + + // iterate over each RHS predicate and ... + while (!rhs_preds.empty()) { + if (Config::Analysis::log) { + std::cout << "eliminating predicates: " << std::endl; + } + + for (const auto& pred: rhs_preds) { + // ... check whether it occur uniquely on the RHS of one CHC + const auto optional_uni_chc = unilaterally_resolvable_with(pred, chcs); + if (optional_uni_chc.has_value()) { + const Clause& uni_chc = optional_uni_chc.value(); + chcs.erase(uni_chc); + + if (Config::Analysis::log) { + std::cout << " - " << pred.name << std::endl; + } + + // An edge case to consider: if we have the clause set + // + // (c1) F ==> F + // (c2) F ==> false + // + // then `F` uniquely occurs on the RHS of `c1`. But after eliminating `c1` we get: + // + // (c1+c2) F ==> false + // + // We can't completely eliminate `F` because it also occurs on the LHS of `c1`. + // In fact, any clause that has `F` on the its LHS is "unreachable" so we can remove + // them all from the CHC problem. + if (uni_chc.getLHSPredicate(pred.name).has_value()) { + std::set chcs_without_pred; + for (const Clause& chc: chcs) { + if (!chc.getLHSPredicate(pred.name).has_value()) { + chcs_without_pred.insert(chc); + } + } + + chcs = chcs_without_pred; + } else { + std::set resolvents; + for (const Clause& chc: chcs) { + const Clause& resolvent = eliminate_pred(uni_chc, chc); + resolvents.insert(resolvent); + } + + chcs = resolvents; + } + } + } + + rhs_preds.clear(); + + if (Config::Analysis::log) { + std::cout << "remove resolvents with UNSAT constraint: " << std::endl; + } + // Checking resolvents for satisfiability in a separate loop here, + // because it reduces the number of calls to the SMT solver. + std::set chcs_with_sat_guard; + for (const auto& chc: chcs) { + if (SmtFactory::check(chc.guard) == Sat) { + chcs_with_sat_guard.insert(chc); + } else if (chc.rhs.has_value()) { + // Assume we have the resolvents: + // + // (r1) F /\ (X>0 /\ X<0) ==> G + // (r2) F ==> G + // + // We can remove `r1` because the constraint is UNSAT. After that + // removal, `r2` is now unilaterally resolvable because we removed the + // only other clause, which had `G` as its RHS predicate. Thus, whenever + // we remove a resolvent, we add its RHS predicate into `rhs_preds` + // to re-check it in the next while-loop iteration. + rhs_preds.insert(chc.rhs.value()); + } + } + + chcs = chcs_with_sat_guard; + } + + if (Config::Analysis::log) { + std::cout << std::endl; + } + + return chcs; +} diff --git a/src/nonlinear/nonlinear.hpp b/src/nonlinear/nonlinear.hpp index 27a34481d..5d745f999 100644 --- a/src/nonlinear/nonlinear.hpp +++ b/src/nonlinear/nonlinear.hpp @@ -9,10 +9,16 @@ class NonLinearSolver { public: - static void analyze(ILinearSolver &linear_solver); + static void analyze(const std::vector& chcs); }; const std::vector all_resolvents(const Clause& chc, const std::set& facts); void all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts, std::vector& resolvents); + +const std::vector normalize_all_preds(const std::vector& chc_problem); + +const std::optional unilaterally_resolvable_with(const FunApp& pred, const std::set& chcs); + +const std::set presolve(const std::vector& chc_problem); diff --git a/src/parser/chc/CHCParseVisitor.cpp b/src/parser/chc/CHCParseVisitor.cpp index bf5c57c19..d5ff6f450 100644 --- a/src/parser/chc/CHCParseVisitor.cpp +++ b/src/parser/chc/CHCParseVisitor.cpp @@ -32,19 +32,19 @@ Res::Res(const T &t): t(t) {} template Res::Res() {} -LocationIdx CHCParseVisitor::loc(const std::string &name) { - auto it = locations.find(name); - if (it == locations.end()) { - auto idx = its->addNamedLocation(name); - locations[name] = idx; - return idx; - } else { - return it->second; - } -} +// LocationIdx CHCParseVisitor::loc(const std::string &name) { +// auto it = locations.find(name); +// if (it == locations.end()) { +// auto idx = its->addNamedLocation(name); +// locations[name] = idx; +// return idx; +// } else { +// return it->second; +// } +// } antlrcpp::Any CHCParseVisitor::visitMain(CHCParser::MainContext *ctx) { - its->setInitialLocation(its->addNamedLocation("LoAT_init")); + // its->setInitialLocation(its->addNamedLocation("LoAT_init")); for (const auto &c: ctx->fun_decl()) { visit(c); } @@ -56,23 +56,23 @@ antlrcpp::Any CHCParseVisitor::visitMain(CHCParser::MainContext *ctx) { clauses.push_back(any_cast(visit(c))); } - std::vector vars; - for (unsigned i = 0; i < max_int_arity; ++i) { - vars.emplace_back(NumVar::nextProgVar()); - } - its->numProgVars = vars; + // std::vector vars; + // for (unsigned i = 0; i < max_int_arity; ++i) { + // vars.emplace_back(NumVar::nextProgVar()); + // } + // its->numProgVars = vars; - std::vector bvars; - for (unsigned i = 0; i < max_bool_arity; ++i) { - bvars.emplace_back(BoolVar::nextProgVar()); - } - its->boolProgVars = bvars; + // std::vector bvars; + // for (unsigned i = 0; i < max_bool_arity; ++i) { + // bvars.emplace_back(BoolVar::nextProgVar()); + // } + // its->boolProgVars = bvars; - for (const Clause &c: clauses) { - its->addClause(c); - } + // for (const Clause &c: clauses) { + // its->addClause(c); + // } - return its; + return clauses; } std::string unescape(std::string name) { @@ -84,24 +84,25 @@ std::string unescape(std::string name) { } antlrcpp::Any CHCParseVisitor::visitFun_decl(CHCParser::Fun_declContext *ctx) { - unsigned long int_arity {0}; - unsigned long bool_arity {0}; - for (const auto &s: ctx->sort()) { - switch (std::any_cast(visit(s))) { - case Int: - ++int_arity; - break; - case Bool: - ++bool_arity; - break; - } - } - max_int_arity = std::max(max_int_arity, int_arity); - max_bool_arity = std::max(max_bool_arity, bool_arity); + // unsigned long int_arity {0}; + // unsigned long bool_arity {0}; + // for (const auto &s: ctx->sort()) { + // switch (std::any_cast(visit(s))) { + // case Int: + // ++int_arity; + // break; + // case Bool: + // ++bool_arity; + // break; + // } + // } + // max_int_arity = std::max(max_int_arity, int_arity); + // max_bool_arity = std::max(max_bool_arity, bool_arity); const auto name = any_cast(visit(ctx->symbol())); - const LocationIdx idx = its->addNamedLocation(name); - locations[name] = idx; - return idx; + // const LocationIdx idx = its->addNamedLocation(name); + // locations[name] = idx; + fun_names.insert(name); + return name; } antlrcpp::Any CHCParseVisitor::visitChc_assert(CHCParser::Chc_assertContext *ctx) { @@ -155,7 +156,9 @@ antlrcpp::Any CHCParseVisitor::visitChc_query(CHCParser::Chc_queryContext *ctx) } const auto lhs = any_cast(visit(ctx->chc_tail())); vars.clear(); - return Clause(lhs.first, FunApp(its->getSink(), {}), lhs.second); + + // const auto sink_name = its->getLocationNames().at(its->getSink()); + return Clause(lhs.first, {}, lhs.second); } antlrcpp::Any CHCParseVisitor::visitVar_decl(CHCParser::Var_declContext *ctx) { @@ -167,15 +170,16 @@ antlrcpp::Any CHCParseVisitor::visitVar_decl(CHCParser::Var_declContext *ctx) { antlrcpp::Any CHCParseVisitor::visitU_pred_atom(CHCParser::U_pred_atomContext *ctx) { const auto name = any_cast(visit(ctx->symbol())); - const std::optional loc = its->getLocationIdx(name); - if (!loc) { + // const std::optional loc = its->getLocationIdx(name); + // TODO: + if (!fun_names.contains(name)) { throw std::invalid_argument("undeclared function symbol " + name); } std::vector args; for (const auto &c: ctx->var()) { args.push_back(any_cast(visit(c))); } - return FunApp(*loc, args); + return FunApp(name, args); } antlrcpp::Any CHCParseVisitor::visitI_formula(CHCParser::I_formulaContext *ctx) { @@ -522,9 +526,9 @@ antlrcpp::Any CHCParseVisitor::visitSort(CHCParser::SortContext *ctx) { antlrcpp::Any CHCParseVisitor::visitVar_or_atom(CHCParser::Var_or_atomContext *ctx) { if (ctx->var()) { - const std::optional loc = its->getLocationIdx(unescape(ctx->getText())); - if (loc) { - return std::variant(FunApp(*loc, {})); + const auto name = unescape(ctx->getText()); + if (fun_names.contains(name)) { // its->getLocationIdx(name).has_value()) { + return std::variant(FunApp(name, {})); } else { return std::variant(std::get(any_cast(visit(ctx->var())))); } diff --git a/src/parser/chc/CHCParseVisitor.h b/src/parser/chc/CHCParseVisitor.h index bf65b23c4..797802e3b 100644 --- a/src/parser/chc/CHCParseVisitor.h +++ b/src/parser/chc/CHCParseVisitor.h @@ -56,13 +56,15 @@ enum Sort { */ class CHCParseVisitor : public CHCVisitor { - ITSPtr its {std::make_shared()}; - std::map locations; + // ITSPtr its {std::make_shared()}; + // std::map locations; std::map vars; - unsigned long max_int_arity {0}; - unsigned long max_bool_arity {0}; + // unsigned long max_int_arity {0}; + // unsigned long max_bool_arity {0}; - LocationIdx loc(const std::string &name); + std::set> fun_names; + + // LocationIdx loc(const std::string &name); Var var(const std::string &name, Sort sort); public: diff --git a/src/parser/chc/chcparser.cpp b/src/parser/chc/chcparser.cpp index 97bdf401c..19a9ac693 100644 --- a/src/parser/chc/chcparser.cpp +++ b/src/parser/chc/chcparser.cpp @@ -24,7 +24,7 @@ using namespace antlr4; using namespace hornParser; -ITSPtr HornParser::loadFromFile(const std::string &filename) { +std::vector HornParser::loadFromFile(const std::string &filename) { std::ifstream file(filename); if (!file.is_open()) { throw std::invalid_argument("Unable to open file: " + filename); @@ -39,6 +39,6 @@ ITSPtr HornParser::loadFromFile(const std::string &filename) { if (parser.getNumberOfSyntaxErrors() > 0) { throw std::invalid_argument("parsing failed"); } else { - return any_cast(vis.visit(ctx)); + return any_cast>(vis.visit(ctx)); } } diff --git a/src/parser/chc/chcparser.hpp b/src/parser/chc/chcparser.hpp index db0645def..ce1955191 100644 --- a/src/parser/chc/chcparser.hpp +++ b/src/parser/chc/chcparser.hpp @@ -13,7 +13,7 @@ class HornParser { * @param path The file to load * @return The resulting ITSProblem (a FileError is thrown if parsing fails) */ - static ITSPtr loadFromFile(const std::string &path); + static std::vector loadFromFile(const std::string &path); }; From eb6bd51c608d112eb3b5bd070cc2d4cbddc25adc Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 22 Jan 2024 14:37:04 +0100 Subject: [PATCH 20/25] fix: still redundant resolvents in non-linear solver Assume we have facts F,G,H and a non-linear CHC: F /\ G /\ H ==> false When we recursively compute all resolvents, the iterator on the LHS predicates should match the call tree depth. So at depth 1 the iterator should point to the first predicate. At depth 2 the iterator should always point on the second predicate. Let `*` denote the iterator position and let `_` denote an eliminated predicate, then the (partial) call tree should look like this: F* /\ G /\ H ==> false --> _ /\ G* /\ H ==> false --> _ /\ _ /\ H* ==> false (...) --> F /\ G* /\ H ==> false --> F /\ _ /\ H* ==> false --> F /\ _ /\ _ ==> false (...) However, when we compute a resolvent the iterator was "reset" to the first predicate, which is a problem when we are in a sub-tree, where the iterator is not pointing on the first non-eliminated predicate. That is, we actually got: F* /\ G /\ H ==> false --> _ /\ G* /\ H ==> false --> _ /\ _ /\ H* ==> false (original resolvent) (...) --> F /\ G* /\ H ==> false --> F* /\ _ /\ H ==> false (iterator reset) --> _ /\ _ /\ H* ==> false (redundant resolvent) (...) The iterator is "reset" because iterators are only defined for a concrete collection. The resolvent stores its LHS predicate in a new collection. We fix this by using an normal index on the LHS predicates instead of an iterator. Another way we derive redundant resolvents is when clauses are "symmetric". For example, exchanging `F(x)` and `F(y)` in F(x) /\ F(y) /\ F(z) ==> G(x+y) does not change the meaning of the clause. Now, with the single fact `F(1)` we would get the derivation: F(x)* /\ F(y) /\ F(z) ==> G(x+y) --> ___ /\ F(y) /\ F(z) ==> G(1+y) (...) --> F(x) /\ F(y)* /\ F(z) ==> G(x+y) --> F(x) /\ ___ /\ F(z) ==> G(x+1) (...) We can detect that the two resolvents F(y) /\ F(z) ==> G(1+y) F(x) /\ F(z) ==> G(x+1) are syntactially equivalent (up-to renmaing). And in particular, all resolvents that derived from these clauses are also equivalent. So we now prune the call tree when we detect a redundant resolvent. --- benchmarks/LIA.txt | 6 +- benchmarks/comp23-LIA-nonlin.txt | 12 +- run_benchmark.sh | 21 ++-- src/nonlinear/clause.cpp | 63 ++++++++--- src/nonlinear/clause.hpp | 10 +- src/nonlinear/nonlinear.cpp | 174 ++++++++++++++--------------- src/nonlinear/nonlinear.hpp | 12 +- src/parser/chc/CHCParseVisitor.cpp | 6 +- 8 files changed, 169 insertions(+), 135 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index 285af5594..dece84566 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -1,4 +1,4 @@ -### Z3(117) ADCL(69) +### Z3 ADCL 000 sat timeout 001 sat timeout 002 sat timeout @@ -263,11 +263,11 @@ 261 sat unknown 262 sat unknown 263 unsat unsat -264 unsat timeout +264 unsat unsat 265 timeout timeout 266 unsat timeout 267 timeout timeout -268 unsat timeout +268 unsat unsat 269 sat timeout 270 sat timeout 271 unsat unsat diff --git a/benchmarks/comp23-LIA-nonlin.txt b/benchmarks/comp23-LIA-nonlin.txt index 128a0582f..58c9174f5 100644 --- a/benchmarks/comp23-LIA-nonlin.txt +++ b/benchmarks/comp23-LIA-nonlin.txt @@ -1,4 +1,4 @@ -### Z3(119) ADCL(40) +### Z3(119) ADCL(45) 000 sat timeout 001 sat timeout 002 sat timeout @@ -122,7 +122,7 @@ 120 timeout timeout 121 unsat unsat 122 unsat timeout -123 unsat timeout +123 unsat unsat 124 unsat timeout 125 sat timeout 126 unsat unsat @@ -203,10 +203,10 @@ 201 unsat unsat 202 unsat unsat 203 unsat unsat -204 unsat timeout -205 unsat timeout -206 unsat timeout -207 unsat timeout +204 unsat unsat +205 unsat unsat +206 unsat unsat +207 unsat unsat 208 sat timeout 209 sat timeout 210 sat timeout diff --git a/run_benchmark.sh b/run_benchmark.sh index 541d9a860..e53bc0135 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -3,7 +3,7 @@ set -e # Switch working directory to the folder, where this script is laying around. -# That way all paths are guaranteed to be relative to the script locaiton. +# That way all paths are guaranteed to be relative to the script location. pushd $(dirname ${BASH_SOURCE[0]}) cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo @@ -11,22 +11,25 @@ pushd build make -j4 popd -# DEBUG: non-deterministic unsat/unknown for 133,139 +# DEBUG: non-deterministic unsat/unknown for 133,139,157 + +# TODO: propagate equalities for Clauses # gdb --args \ time ./build/loat-static \ --mode reachability \ --format horn \ --proof-level 0 \ - --log \ - "../chc-comp22-benchmarks/LIA/chc-LIA_130.smt2" - # "../chc-comp23-benchmarks/test.smt2" - # "../chc-comp23-benchmarks/LIA-nonlin/chc-LIA_004.smt2" + "../chc-comp22-benchmarks/LIA/chc-LIA_130_mod.smt2" + # "../chc-comp22-benchmarks/LIA/chc-LIA_112.smt2" + # "../chc-comp23-benchmarks/LIA-nonlin/chc-LIA_111.smt2" popd exit -# CHECK: 073 () / 119 () / 126 () / 130 (1m13s) / 351 (2m50s) +# REVIEW (comp22) : 076,073,112 +# REVIEW (comp22) : 119 (1m20s) / 126 (2m19s) +# REVIEW (comp23) : 111 ########################################################################## @@ -45,11 +48,11 @@ do file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" # if true; then - # if [[ "$z3_result" == "unsat" ]] && [[ "$adcl_result" == "timeout" ]]; then + if [[ "$z3_result" == "unsat" ]] && [[ "$adcl_result" == "timeout" ]]; then # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then # if [[ "$z3_result" != "sat" ]]; then # if [[ "$adcl_result" == "unknown" ]]; then - if [[ "$adcl_result" == "unsat" ]]; then + # if [[ "$adcl_result" == "unsat" ]]; then set +e result=$(timeout 20 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") # result=$(timeout 5 z3 "$file") diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index ab2929b6f..2cb51f7a7 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -154,9 +154,9 @@ unsigned long FunApp::boolArity() const { * maps to compound expressions */ const Clause Clause::renameWith(const Subs &renaming) const { - std::set lhs_renamed; + std::vector lhs_renamed; for (const FunApp &pred : lhs) { - lhs_renamed.insert(pred.renameWith(renaming)); + lhs_renamed.push_back(pred.renameWith(renaming)); } const auto guard_renamed = guard->subs(renaming); @@ -187,6 +187,16 @@ const VarSet Clause::vars() const { return vs; } +const std::vector deletePredAt(const std::vector& preds, unsigned index) { + std::vector new_preds; + for (unsigned i=0; i G(x) - * chc : G(y) /\ G(z) /\ y < z ==> H(y,z) - * pred : G(z) + * this : F(x) ==> G(x) + * chc : G(y) /\ G(z) /\ y < z ==> H(y,z) + * pred_index : ^--- points on first predicate (i.e. pred_index = 0) * * the returned resolvent should be * * G(y) /\ F(z) /\ y < z ==> H(y,z) * */ -const std::optional Clause::resolutionWith(const Clause &chc, const FunApp &pred) const { - if (!chc.lhs.contains(pred)) { - throw std::logic_error("Given `pred` is not on the LHS of `chc`"); +const std::optional Clause::resolutionWith(const Clause &chc, unsigned pred_index) const { + if (pred_index >= chc.lhs.size()) { + throw std::logic_error("Clause::resolutionWith: `pred_index` out-of-bounds"); } + const auto& resolved_pred = chc.lhs.at(pred_index); // No resolvent if `this` is a query, ie has no RHS predicate. if (this->isQuery()) { @@ -224,7 +235,7 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA } const Clause this_with_disjoint_vars = this->renameWith(renaming); - const auto unifier = computeUnifier(this_with_disjoint_vars.rhs.value(), pred); + const auto unifier = computeUnifier(this_with_disjoint_vars.rhs.value(), resolved_pred); // If the predicates are not unifiable, we don't throw an error but return // nullopt. That way the caller can filter out unifiable predicates using this @@ -237,15 +248,15 @@ const std::optional Clause::resolutionWith(const Clause &chc, const FunA } const Clause this_unified = this_with_disjoint_vars.renameWith(unifier.value()); - std::set chc_lhs_without_pred = chc.lhs; - chc_lhs_without_pred.erase(pred); - const Clause chc_unified = Clause(chc_lhs_without_pred, chc.rhs, chc.guard) + const Clause chc_unified = Clause(deletePredAt(chc.lhs, pred_index), chc.rhs, chc.guard) .renameWith(unifier.value()); // LHS of resolvent is the union of the renamed LHS of `this` ... - std::set resolvent_lhs = this_unified.lhs; + std::vector resolvent_lhs = this_unified.lhs; // ... and the LHS of `chc` where `pred` is removed. - resolvent_lhs.insert(chc_unified.lhs.begin(), chc_unified.lhs.end()); + for (const auto& pred: chc_unified.lhs) { + resolvent_lhs.push_back(pred); + } const auto new_guard = this_unified.guard & chc_unified.guard; @@ -359,10 +370,10 @@ bool Clause::isQuery() const { * Return first predicate with given `name` if it occurs on the LHS of the clause. * Returns nullopt if there is no predicate with given `name`. */ -std::optional Clause::getLHSPredicate(const std::basic_string name) const { - for (const auto& pred: lhs) { - if (pred.name == name) { - return pred; +std::optional Clause::indexOfLHSPred(const std::basic_string name) const { + for (unsigned i=0; i "; + + if (chc.rhs.has_value()) { + s << chc.rhs.value().name; + } else { + s << "false"; + } + + return s; +} diff --git a/src/nonlinear/clause.hpp b/src/nonlinear/clause.hpp index b49c2a498..81658ea2d 100644 --- a/src/nonlinear/clause.hpp +++ b/src/nonlinear/clause.hpp @@ -26,7 +26,7 @@ class FunApp { class Clause { public: - const std::set lhs; + const std::vector lhs; const std::optional rhs; const BoolExpr guard; @@ -43,14 +43,14 @@ class Clause { * @param guard - a boolean expression describing the clause constraint. */ Clause( - const std::set lhs, + const std::vector lhs, const std::optional rhs, const BoolExpr guard ) : lhs(lhs), rhs(rhs), guard(guard) {} const Clause renameWith(const Subs &renaming) const; - const std::optional resolutionWith(const Clause &chc, const FunApp &pred) const; + const std::optional resolutionWith(const Clause &chc, unsigned pred_index) const; const Clause normalize() const; @@ -62,7 +62,7 @@ class Clause { const VarSet vars() const; - std::optional getLHSPredicate(const std::basic_string name) const; + std::optional indexOfLHSPred(const std::basic_string name) const; }; const std::tuple, std::set> partitionByDegree(const std::set& chcs); @@ -80,3 +80,5 @@ bool operator<(const FunApp &fun1, const FunApp &fun2); std::ostream& operator<<(std::ostream &s, const FunApp &fun_app); std::ostream& operator<<(std::ostream &s, const Clause &chc); + +std::ostream& printSimple(std::ostream &s, const Clause &chc); diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index cb2910865..505a3bc75 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -66,29 +66,10 @@ void NonLinearSolver::analyze(const std::vector& initial_chcs) { // Do resolution with all combinations of facts and non-linear clauses to // get every possible linear clause that we can derive in this iteration. - std::list resolvents; - for (const auto& non_linear_chc: non_linear_chcs) { - const auto res(all_resolvents(non_linear_chc, facts)); - resolvents.insert( - resolvents.end(), - res.begin(), - res.end() - ); - } - - // Filter-out syntactially redundant resolvents: - const std::set resolvents_distinct(resolvents.begin(), resolvents.end()); - if (Config::Analysis::log) { - std::cout - << (resolvents.size() - resolvents_distinct.size()) - << " of " - << resolvents.size() - << " resolvents are syntactially redundant" - << std::endl; - } + const std::set resolvents = all_resolvents(non_linear_chcs, facts); // Add linear resolvents to linear solver and store non-linear resolvents for the next resolutions round: - const auto [linear_resolvents, non_linear_resolvents] = partitionByDegree(resolvents_distinct); + const auto [linear_resolvents, non_linear_resolvents] = partitionByDegree(resolvents); linear_solver.add_clauses(linear_resolvents); non_linear_chcs.insert(non_linear_resolvents.begin(), non_linear_resolvents.end()); @@ -96,9 +77,9 @@ void NonLinearSolver::analyze(const std::vector& initial_chcs) { facts.clear(); facts = linear_solver.derive_new_facts(max_constraint_tier); if (Config::Analysis::log) { - for (const auto &fact: facts) { - std::cout << "new fact: " << fact << std::endl; - } + // for (const auto &fact: facts) { + // std::cout << "new fact: " << fact << std::endl; + // } } // If the linear solver proved Unsat, we can terminate. Otherwise, we use the derived facts to in the next @@ -132,9 +113,8 @@ void NonLinearSolver::analyze(const std::vector& initial_chcs) { } } - /** - * Compute all resolvents of `chc` with `facts`, while trying to avoid redundant derivations. + * Compute all resolvents of `non_linear_chcs` with `facts`, while trying to avoid redundant derivations. * For example, let `F(1)` be the only fact and `chc` be `F(x) /\ F(y) /\ F(z) ==> G(x,y,z)` * then a redundant derivation would be: * @@ -148,59 +128,77 @@ void NonLinearSolver::analyze(const std::vector& initial_chcs) { * * F(x) ==> G(x,1,1) F(x) ==> G(x,1,1) * - * The returned clause list should not contain any redundant clauses as long as the given - * `facts` are not redundant. The returned list should also not contain `chc` itself and - * no facts (i.e. clauses with no LHS predicates). + * The returned clause set should not contain any redundant clauses as long as the given + * `facts` and `non_linear_chcs` are not redundant. The returned clauses should also not + * include `non_linear_chcs` and themselves and no facts (i.e. clauses with no LHS predicates). */ -const std::vector all_resolvents(const Clause& chc, const std::set& facts) { - std::vector resolvents; - all_resolvents_aux(chc, chc.lhs.begin(), facts, resolvents); +const std::set all_resolvents(const std::set& non_linear_chcs, const std::set& facts) { + std::set resolvents; + + unsigned redundant_resolvent_count = 0; + + if (Config::Analysis::log) { + std::cout + << "resolving " + << non_linear_chcs.size() + << " non-linear CHCs with " + << facts.size() + << " facts" + << std::endl; + } + + for (const auto& chc: non_linear_chcs) { + // std::cout << "preds: " << chc.lhs.size() << std::endl; + all_resolvents_aux(chc, 0, facts, resolvents, redundant_resolvent_count); + } + + // if (Config::Analysis::log) { + std::cout + << "computed " + << resolvents.size() + << " resolvents (" + << redundant_resolvent_count + << " redundant)" + << std::endl; + // } + + for (const auto& res: resolvents) { + const std::set lhs_unique(res.lhs.begin(), res.lhs.end()); + int red_count = res.lhs.size() - lhs_unique.size(); + if (red_count != 0) { + std::cout << "redundant lhs preds: " << red_count << std::endl; + } + } + return resolvents; } -void all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts, std::vector& resolvents) { +void all_resolvents_aux(const Clause& chc, unsigned pred_index, const std::set& facts, std::set& resolvents, unsigned& redundant_resolvent_count) { // === Running example === - // `chc` : F(x) /\ F(y) /\ F(z) ==> G(x,y,z) - // `preds` : ^---- iterator pointing on first predicate of `chc` - // `facts` : [F(1), F(2)] + // `chc` : F(x) /\ F(y) /\ F(z) ==> G(x,y,z) + // `pred_index` : ^---- index on first LHS predicate of `chc` + // `facts` : [F(1), F(2)] - if (preds == chc.lhs.end() || chc.isLinear()) { - return; + if (pred_index >= chc.lhs.size() || chc.isLinear()) { + return; } else { - const auto current_pred = *preds; // == F(x) - - // All resolvents that DONT eliminate F(x) - all_resolvents_aux(chc, std::next(preds), facts, resolvents); - // F(x) /\ F(y) ==> G(x,y,1) - // F(x) /\ F(y) ==> G(x,y,2) - // - // F(x) /\ F(z) ==> G(x,1,z) - // F(x) ==> G(x,1,1) - // F(x) ==> G(x,1,2) - // - // F(x) /\ F(z) ==> G(x,2,z) - // F(x) ==> G(x,2,1) - // F(x) ==> G(x,2,2) - // All resolvents that DO eliminate F(x) - // std::list res_without_head; - for (const auto& fact: facts) { - const auto optional_resolvent = fact.resolutionWith(chc, current_pred); + for (const auto& fact: facts) { + const auto optional_resolvent = fact.resolutionWith(chc, pred_index); - if (optional_resolvent.has_value()) { + if (optional_resolvent.has_value()) { const auto resolvent = optional_resolvent.value(); // for fact = F(1) : F(y) /\ F(z) ==> G(1,y,z) // // for fact = F(2) : F(y) /\ F(z) ==> G(2,y,z) - if (SmtFactory::check(resolvent.guard) == Sat) { - resolvents.push_back(resolvent); - // if (Config::Analysis::log) { - // std::cout << "new resolvent: " << resolvent << std::endl; - // } + if (resolvents.contains(resolvent)) { + redundant_resolvent_count++; + std::cout << "red: " << resolvent << std::endl; + // throw std::logic_error(""); + } else if (SmtFactory::check(resolvent.guard) == Sat) { + resolvents.insert(resolvent); - - // const auto res(all_resolvents_aux(resolvent, resolvent.lhs.begin(), facts)); - all_resolvents_aux(resolvent, resolvent.lhs.begin(), facts, resolvents); + all_resolvents_aux(resolvent, pred_index, facts, resolvents, redundant_resolvent_count); // for fact = F(1) : F(y) ==> G(1,y,1) // F(y) ==> G(1,y,2) // F(z) ==> G(1,1,z) @@ -210,24 +208,22 @@ void all_resolvents_aux(const Clause& chc, const std::set::iterator pred // F(y) ==> G(2,y,2) // F(z) ==> G(2,1,z) // F(z) ==> G(2,2,z) - - // res_without_head.push_back(resolvent); - // res_without_head.insert( - // res_without_head.begin(), - // res.begin(), - // res.end() - // ); } - } + } } - - // join all collected resolvents and return: - // res_without_head.insert( - // res_without_head.end(), - // res_with_head.begin(), - // res_with_head.end() - // ); - // return res_without_head; + + // All resolvents that DONT eliminate F(x) + all_resolvents_aux(chc, pred_index+1, facts, resolvents, redundant_resolvent_count); + // F(x) /\ F(y) ==> G(x,y,1) + // F(x) /\ F(y) ==> G(x,y,2) + // + // F(x) /\ F(z) ==> G(x,1,z) + // F(x) ==> G(x,1,1) + // F(x) ==> G(x,1,2) + // + // F(x) /\ F(z) ==> G(x,2,z) + // F(x) ==> G(x,2,1) + // F(x) ==> G(x,2,2) } } @@ -273,7 +269,7 @@ const std::vector normalize_all_preds(const std::vector& initial auto [ max_int_arity, max_bool_arity ] = maxArity(initial_chcs); for (const auto& chc: initial_chcs) { - std::set lhs_normalized; + std::vector lhs_normalized; for (const FunApp &pred: chc.lhs) { const auto pred_normalized = normalize_pred( @@ -281,7 +277,7 @@ const std::vector normalize_all_preds(const std::vector& initial max_bool_arity, pred ); - lhs_normalized.insert(pred_normalized); + lhs_normalized.push_back(pred_normalized); } if (chc.rhs.has_value()) { @@ -355,10 +351,10 @@ const Clause eliminate_pred(const Clause& left, const Clause& right) { } const auto& left_pred = left.rhs.value(); - const auto& right_pred = right.getLHSPredicate(left_pred.name); + const auto& right_pred_index = right.indexOfLHSPred(left_pred.name); - if (right_pred.has_value()) { - return eliminate_pred(left, left.resolutionWith(right, right_pred.value()).value()); + if (right_pred_index.has_value()) { + return eliminate_pred(left, left.resolutionWith(right, right_pred_index.value()).value()); } else { return right; } @@ -410,7 +406,7 @@ const Clause eliminate_pred(const Clause& left, const Clause& right) { * Thus, we are left with a trivial CHC problem. */ const std::set presolve(const std::vector& initial_chcs) { - // first collect all RHS predicate that occur in the CHC problem. + // first collect all RHS predicates of all CHCs std::set rhs_preds; for (const auto& chc: initial_chcs) { if (chc.rhs.has_value()) { @@ -449,10 +445,10 @@ const std::set presolve(const std::vector& initial_chcs) { // We can't completely eliminate `F` because it also occurs on the LHS of `c1`. // In fact, any clause that has `F` on the its LHS is "unreachable" so we can remove // them all from the CHC problem. - if (uni_chc.getLHSPredicate(pred.name).has_value()) { + if (uni_chc.indexOfLHSPred(pred.name).has_value()) { std::set chcs_without_pred; for (const Clause& chc: chcs) { - if (!chc.getLHSPredicate(pred.name).has_value()) { + if (!chc.indexOfLHSPred(pred.name).has_value()) { chcs_without_pred.insert(chc); } } diff --git a/src/nonlinear/nonlinear.hpp b/src/nonlinear/nonlinear.hpp index 5d745f999..54d1c130d 100644 --- a/src/nonlinear/nonlinear.hpp +++ b/src/nonlinear/nonlinear.hpp @@ -13,9 +13,15 @@ class NonLinearSolver { }; -const std::vector all_resolvents(const Clause& chc, const std::set& facts); - -void all_resolvents_aux(const Clause& chc, const std::set::iterator preds, const std::set& facts, std::vector& resolvents); +const std::set all_resolvents(const std::set& non_linear_chcs, const std::set& facts); + +void all_resolvents_aux( + const Clause& chc, + unsigned pred_index, + const std::set& facts, + std::set& resolvents, + unsigned& redundant_resolvent_count +); const std::vector normalize_all_preds(const std::vector& chc_problem); diff --git a/src/parser/chc/CHCParseVisitor.cpp b/src/parser/chc/CHCParseVisitor.cpp index d5ff6f450..4378c5e23 100644 --- a/src/parser/chc/CHCParseVisitor.cpp +++ b/src/parser/chc/CHCParseVisitor.cpp @@ -20,7 +20,7 @@ using lit_type = Res; using assert_type = Clause; using query_type = Clause; using symbol_type = std::string; -using tail_type = std::pair, BoolExpr>; +using tail_type = std::pair, BoolExpr>; using head_type = FunApp; using var_or_atom_type = std::variant; using boolop_type = BoolOp; @@ -137,13 +137,13 @@ antlrcpp::Any CHCParseVisitor::visitChc_tail(CHCParser::Chc_tailContext *ctx) { guards.insert(guards.end(), r.refinement.begin(), r.refinement.end()); } - std::set predicates; + std::vector predicates; for (const auto &c: ctx->var_or_atom()) { const auto v = any_cast(visit(c)); if (std::holds_alternative(v)) { guards.push_back(BExpression::buildTheoryLit(BoolLit(std::get(v)))); } else { - predicates.insert(std::get(v)); + predicates.push_back(std::get(v)); } } From b65cfc67575809404153ac548d42c77374e7b87b Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Thu, 1 Feb 2024 16:54:52 +0100 Subject: [PATCH 21/25] non-linear: merge facts with same RHS predicate Computing all resolvents is exponential in the number of LHS predicates of a non-linear clause. So it's worth eliminating as many predicates as possible. In preprocessing we already eliminate predicates if there is only one resolution candidate. By merging facts we can reduce resolution candidates. For example, here have two candidates for `F(x)`: x=0 ==> F(x) x>0 ==> F(x) we can merge these facts into one clause by disjoining the constraints: (x=0 \/ x>0) ==> F(x) This can make all facts unilaterally resolvable as long as the RHS predicate does not also occur on the RHS of a rule. Arguably, that just off-loads dealing with disjunctions to the linear solver. We also loose some CHC structure pushing disjunctions into the clause constraint. However, ADCL is designed to deal with disjunctions. And computing all resolvents in the non-linear solver is worst-case exponential in the number of LHS predicates, so it's probably worth it. Benchmark results: - CHC comp 2022: +6 UNSAT - CHC comp 2023: +3 UNSAT --- benchmarks/LIA.txt | 12 +- benchmarks/comp23-LIA-nonlin.txt | 8 +- run_benchmark.sh | 27 ++-- src/analysis/guardtoolbox.cpp | 1 - src/nonlinear/clause.cpp | 76 +++++++++- src/nonlinear/clause.hpp | 7 + src/nonlinear/nonlinear.cpp | 237 ++++++++++++++++++++++++++----- src/nonlinear/nonlinear.hpp | 2 + 8 files changed, 305 insertions(+), 65 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index dece84566..e30f26536 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -75,7 +75,7 @@ 073 unsat unsat 074 sat timeout 075 unsat unsat -076 unsat timeout +076 unsat unsat 077 unsat unsat 078 sat timeout 079 sat timeout @@ -111,7 +111,7 @@ 109 unsat unsat 110 unsat unsat 111 unsat unsat -112 unsat timeout +112 unsat unsat 113 unsat unsat 114 unsat unsat 115 sat unknown @@ -386,9 +386,9 @@ 384 sat timeout 385 sat timeout 386 sat timeout -387 unsat timeout +387 unsat unsat 388 sat timeout -389 unsat timeout +389 unsat unsat 390 sat timeout 391 sat timeout 392 sat timeout @@ -441,9 +441,9 @@ 439 sat timeout 440 sat timeout 441 sat timeout -442 unsat timeout +442 unsat unsat 443 sat timeout -444 unsat timeout +444 unsat unsat 445 sat timeout 446 sat timeout 447 sat timeout diff --git a/benchmarks/comp23-LIA-nonlin.txt b/benchmarks/comp23-LIA-nonlin.txt index 58c9174f5..35943f9c0 100644 --- a/benchmarks/comp23-LIA-nonlin.txt +++ b/benchmarks/comp23-LIA-nonlin.txt @@ -1,4 +1,4 @@ -### Z3(119) ADCL(45) +### Z3 ADCL 000 sat timeout 001 sat timeout 002 sat timeout @@ -68,7 +68,7 @@ 066 timeout timeout 067 timeout timeout 068 sat timeout -069 unsat timeout +069 unsat unsat 070 unsat unsat 071 unsat unsat 072 unsat unsat @@ -382,7 +382,7 @@ 380 sat timeout 381 unsat timeout 382 sat timeout -383 unsat timeout +383 unsat unsat 384 sat timeout 385 sat timeout 386 sat timeout @@ -390,7 +390,7 @@ 388 sat timeout 389 sat timeout 390 sat timeout -391 unsat timeout +391 unsat unsat 392 unsat unsat 393 sat timeout 394 sat timeout diff --git a/run_benchmark.sh b/run_benchmark.sh index e53bc0135..f1aa8f741 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -15,17 +15,16 @@ popd # TODO: propagate equalities for Clauses -# gdb --args \ -time ./build/loat-static \ - --mode reachability \ - --format horn \ - --proof-level 0 \ - "../chc-comp22-benchmarks/LIA/chc-LIA_130_mod.smt2" - # "../chc-comp22-benchmarks/LIA/chc-LIA_112.smt2" - # "../chc-comp23-benchmarks/LIA-nonlin/chc-LIA_111.smt2" +# # gdb --args \ +# time ./build/loat-static \ +# --mode reachability \ +# --format horn \ +# --proof-level 0 \ +# "../chc-comp23-benchmarks/LIA-nonlin/chc-LIA_069.smt2" +# # "../chc-comp22-benchmarks/LIA/chc-LIA_112.smt2" -popd -exit +# popd +# exit # REVIEW (comp22) : 076,073,112 # REVIEW (comp22) : 119 (1m20s) / 126 (2m19s) @@ -44,15 +43,15 @@ do continue else read idx z3_result adcl_result <<< "$line" - # file="../chc-comp23-benchmarks/${benchmark}-nonlin/chc-${benchmark}_${idx}.smt2" file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" + # file="../chc-comp23-benchmarks/${benchmark}-nonlin/chc-${benchmark}_${idx}.smt2" # if true; then - if [[ "$z3_result" == "unsat" ]] && [[ "$adcl_result" == "timeout" ]]; then + # if [[ "$z3_result" == "unsat" ]] && [[ "$adcl_result" == "timeout" ]]; then # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then # if [[ "$z3_result" != "sat" ]]; then # if [[ "$adcl_result" == "unknown" ]]; then - # if [[ "$adcl_result" == "unsat" ]]; then + if [[ "$adcl_result" == "unsat" ]]; then set +e result=$(timeout 20 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") # result=$(timeout 5 z3 "$file") @@ -77,8 +76,8 @@ do fi fi -# done < "./benchmarks/comp23-${benchmark}-nonlin.txt" done < "./benchmarks/${benchmark}.txt" +# done < "./benchmarks/comp23-${benchmark}-nonlin.txt" # "undo" pushd popd diff --git a/src/analysis/guardtoolbox.cpp b/src/analysis/guardtoolbox.cpp index ba7f2fa2d..9b90ded8c 100644 --- a/src/analysis/guardtoolbox.cpp +++ b/src/analysis/guardtoolbox.cpp @@ -83,7 +83,6 @@ Result GuardToolbox::propagateEqualities(const Rule &rule, SolvingLevel ma return res; } - Result GuardToolbox::propagateBooleanEqualities(const Rule &rule) { Result res(rule); Proof subproof; diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index 2cb51f7a7..edd064788 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -286,6 +286,53 @@ const std::tuple, std::set> partitionByDegree(const std return std::make_tuple(linear_chcs, non_linear_chcs); } +/** + * Partition given `chcs` by their RHS predicate. Returns a map where the keys are RHS predicate names and + * values are the sets of clauses with that RHS predicate. The key is optional because clauses may have no + * RHS predicate (i.e. querys). + */ +const std::map>, std::vector> partitionByRHS(const std::set& chcs) { + std::map< + std::optional>, + std::vector + > partition; + + for (const auto& chc: chcs) { + std::optional> rhs_name; + if (chc.rhs.has_value()) { + rhs_name = chc.rhs.value().name; + } + + if (partition.contains(rhs_name)) { + std::vector& group = partition.at(chc.rhs->name); + group.push_back(chc); + } else { + partition.emplace(rhs_name, std::vector({ chc })); + } + } + + return partition; +} + +/** + * Filters facts from `chcs` and returns a tuple, where the first component contains all facts and + * the second component contains the remaining clauses. + */ +const std::tuple, std::set> partitionFacts(const std::set& chcs) { + std::set facts; + std::set non_facts; + + for (const auto& chc: chcs) { + if (chc.isFact()) { + facts.insert(chc); + } else { + non_facts.insert(chc); + } + } + + return std::make_tuple(facts, non_facts); +} + /** * Normalize variable names to detect syntactic equivalence of clauses up-to-renaming. * @@ -346,21 +393,42 @@ const Clause Clause::normalize() const { } /** - * Returns true iff the clause has at most one LHS predicate. + * Returns true iff the clause has at most one LHS predicate. For example: + * + * F(x) /\ x > 1 ==> F(x) (linear) + * + * F(x) /\ x > 1 ==> false (linear) + * + * x > 1 ==> F(x) (linear) + * + * F(x) /\ G(x) ==> false (non-linear) + * */ bool Clause::isLinear() const { return lhs.size() <= 1; } /** - * Returns true iff the clause has no LHS predicates. + * Returns true iff the clause has a RHS predicte but no LHS predicates. For example: + * + * x > 0 ==> F(x) (fact) + * + * F(x) /\ x > 0 ==> F(x) (not fact) + * + * x > 0 ==> false (not fact) + * */ bool Clause::isFact() const { - return lhs.size() == 0; + return lhs.size() == 0 && rhs.has_value(); } /** - * Returns true iff the clause has no RHS predicate. + * Returns true iff the clause has no RHS predicate. For example: + * + * F(x) /\ x > 1 ==> false (query) + * + * x > 1 ==> F(x) (not query) + * */ bool Clause::isQuery() const { return !rhs.has_value(); diff --git a/src/nonlinear/clause.hpp b/src/nonlinear/clause.hpp index 81658ea2d..e94d99158 100644 --- a/src/nonlinear/clause.hpp +++ b/src/nonlinear/clause.hpp @@ -67,6 +67,13 @@ class Clause { const std::tuple, std::set> partitionByDegree(const std::set& chcs); +const std::map, std::set> partitionFactsByRHS(const std::set& facts); + +const std::tuple, std::set> partitionFacts(const std::set& chcs); + +const std::map>, std::vector> partitionByRHS(const std::set& chcs); + +const std::optional computeUnifier(const FunApp &pred1, const FunApp &pred2); const std::optional computeUnifier(const std::vector &args1, const std::vector &args2); const std::pair maxArity(const std::vector& chc_problem); diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 505a3bc75..b6d1ad952 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -1,5 +1,6 @@ #include "nonlinear.hpp" #include "booltheory.hpp" +#include "boolexpr.hpp" #include "config.hpp" #include "expr.hpp" #include "itsproblem.hpp" @@ -8,7 +9,13 @@ #include "linearsolver.hpp" #include "smt.hpp" #include "smtfactory.hpp" +#include "theory.hpp" +#include #include +#include + +// for debugging: active logging only in this file +const bool logging_active = Config::Analysis::log; /* @@ -41,6 +48,12 @@ trace : void NonLinearSolver::analyze(const std::vector& initial_chcs) { const std::set chcs_preprocessed = presolve(normalize_all_preds(initial_chcs)); + // If preprocessing manages to eliminate all clauses we can already terminate with SAT. + if (chcs_preprocessed.size() == 0) { + std::cout << "sat" << std::endl; + return; + } + // Setup ITS and linear solver: ITSPtr its = ITSProblem::fromClauses(chcs_preprocessed); // TODO: why cant we give `linear_solver` the type `ILinearSolver` ? @@ -60,7 +73,7 @@ void NonLinearSolver::analyze(const std::vector& initial_chcs) { for (const auto max_constraint_tier: LinearSolver::constraint_tiers) { while (true) { - if (Config::Analysis::log) { + if (logging_active) { std::cout << "============= non-linear solver main loop =============" << std::endl; } @@ -75,11 +88,14 @@ void NonLinearSolver::analyze(const std::vector& initial_chcs) { // Hand over to linear solver and let it derive new facts: facts.clear(); - facts = linear_solver.derive_new_facts(max_constraint_tier); - if (Config::Analysis::log) { - // for (const auto &fact: facts) { - // std::cout << "new fact: " << fact << std::endl; - // } + const auto& derived_facts = linear_solver.derive_new_facts(max_constraint_tier); + for (const auto& [_, fact_group]: partitionByRHS(derived_facts)) { + const auto& merged_fact = merge_facts(fact_group); + facts.insert(merged_fact); + } + if (logging_active) { + std::cout << "total new facts : " << derived_facts.size() << std::endl; + std::cout << "new facts after merge : " << facts.size() << std::endl; } // If the linear solver proved Unsat, we can terminate. Otherwise, we use the derived facts to in the next @@ -130,29 +146,36 @@ void NonLinearSolver::analyze(const std::vector& initial_chcs) { * * The returned clause set should not contain any redundant clauses as long as the given * `facts` and `non_linear_chcs` are not redundant. The returned clauses should also not - * include `non_linear_chcs` and themselves and no facts (i.e. clauses with no LHS predicates). + * include `non_linear_chcs` themselves and no facts (i.e. clauses with no LHS predicates). */ const std::set all_resolvents(const std::set& non_linear_chcs, const std::set& facts) { std::set resolvents; unsigned redundant_resolvent_count = 0; - if (Config::Analysis::log) { + if (logging_active) { std::cout << "resolving " << non_linear_chcs.size() - << " non-linear CHCs with " - << facts.size() - << " facts" - << std::endl; + << " non-linear CHCs" + << std::endl; } for (const auto& chc: non_linear_chcs) { - // std::cout << "preds: " << chc.lhs.size() << std::endl; + if (logging_active) { + std::cout + << "resolving non-linear CHC with " + << chc.lhs.size() + << " LHS predicates with " + << facts.size() + << " facts" + << std::endl; + } + all_resolvents_aux(chc, 0, facts, resolvents, redundant_resolvent_count); } - // if (Config::Analysis::log) { + if (logging_active) { std::cout << "computed " << resolvents.size() @@ -160,16 +183,17 @@ const std::set all_resolvents(const std::set& non_linear_chcs, c << redundant_resolvent_count << " redundant)" << std::endl; - // } - - for (const auto& res: resolvents) { - const std::set lhs_unique(res.lhs.begin(), res.lhs.end()); - int red_count = res.lhs.size() - lhs_unique.size(); - if (red_count != 0) { - std::cout << "redundant lhs preds: " << red_count << std::endl; - } } + // TODO: check comp23 069 + // for (const auto& res: resolvents) { + // const std::set lhs_unique(res.lhs.begin(), res.lhs.end()); + // int red_count = res.lhs.size() - lhs_unique.size(); + // if (red_count != 0) { + // std::cout << "redundant lhs preds: " << red_count << std::endl; + // } + // } + return resolvents; } void all_resolvents_aux(const Clause& chc, unsigned pred_index, const std::set& facts, std::set& resolvents, unsigned& redundant_resolvent_count) { @@ -192,9 +216,16 @@ void all_resolvents_aux(const Clause& chc, unsigned pred_index, const std::set G(2,y,z) if (resolvents.contains(resolvent)) { + // There are still many ways how we can get syntactically redundant resolvents. + // For example, with facts G,H and rules + // + // G /\ F ==> false + // H /\ F ==> false + // + // we derive `F ==> false` in two different ways. However, when we encouter a + // redundant resolvent we can prune the recursive call, because all following + // resolvents are also redundant. redundant_resolvent_count++; - std::cout << "red: " << resolvent << std::endl; - // throw std::logic_error(""); } else if (SmtFactory::check(resolvent.guard) == Sat) { resolvents.insert(resolvent); @@ -360,6 +391,73 @@ const Clause eliminate_pred(const Clause& left, const Clause& right) { } } +/** + * Merges a collection of `facts` by building the disjunct of all constraints. + * For example, merging the facts: + * + * x=0 ==> F(x) + * + * y=1 ==> F(y) + * + * z>5 ==> F(z) + * + * results in: + * + * (x=0 \/ x=1 \/ x>5) ==> F(x) + * + * Notice, that the RHS predicates have to be unified for that. It's assumed that `facts`: + * + * 1) contains at least one item + * 2) contains only clauses that are indeed facts + * 3) all facts share the same RHS predicate + * + * Otherwise, this function throws an error. + */ +const Clause merge_facts(const std::vector facts) { + if (facts.size() == 0) { + throw std::logic_error("merge_facts: expect at least one fact"); + } + + std::unique_ptr fact_accum = std::make_unique(facts.at(0)); + + if (!fact_accum->isFact()) { + throw std::logic_error("merge_facts: got non-fact clause"); + } + + for (unsigned i=1; i < facts.size(); i++) { + const auto& fact_current = facts.at(i); + + if (!fact_current.isFact()) { + throw std::logic_error("merge_facts: got non-fact clause"); + } + + const auto& rhs_pred_accum = fact_accum->rhs.value(); + const auto& rhs_pred_current = fact_current.rhs.value(); + + if (rhs_pred_accum.name != rhs_pred_current.name) { + throw std::logic_error("merge_facts: all facts must have matching RHS"); + } + + const auto& unifier = computeUnifier(rhs_pred_current, rhs_pred_accum); + + if (!unifier.has_value()) { + throw std::logic_error("merge_facts: cannot unify RHS predicates"); + } + + const std::vector guards({ + fact_current.renameWith(unifier.value()).guard, + fact_accum->guard + }); + const auto& guard_disjunct = BExpression::buildOr(guards); + + fact_accum = std::make_unique( + Clause({}, rhs_pred_accum, guard_disjunct) + ); + } + + return *fact_accum; +} + /** * If a predicate only occurs on the RHS of a single CHC, then there is only one * choice how to eliminate the predicate on the LHS of other CHCs. Thus, we can @@ -407,29 +505,29 @@ const Clause eliminate_pred(const Clause& left, const Clause& right) { */ const std::set presolve(const std::vector& initial_chcs) { // first collect all RHS predicates of all CHCs - std::set rhs_preds; + std::set todo_rhs_preds; for (const auto& chc: initial_chcs) { if (chc.rhs.has_value()) { - rhs_preds.insert(chc.rhs.value()); + todo_rhs_preds.insert(chc.rhs.value()); } } std::set chcs(initial_chcs.begin(), initial_chcs.end()); // iterate over each RHS predicate and ... - while (!rhs_preds.empty()) { - if (Config::Analysis::log) { + while (!todo_rhs_preds.empty()) { + if (logging_active) { std::cout << "eliminating predicates: " << std::endl; } - for (const auto& pred: rhs_preds) { - // ... check whether it occur uniquely on the RHS of one CHC + for (const auto& pred: todo_rhs_preds) { + // ... check whether it occurs uniquely on the RHS of one CHC const auto optional_uni_chc = unilaterally_resolvable_with(pred, chcs); if (optional_uni_chc.has_value()) { const Clause& uni_chc = optional_uni_chc.value(); chcs.erase(uni_chc); - if (Config::Analysis::log) { + if (logging_active) { std::cout << " - " << pred.name << std::endl; } @@ -466,9 +564,9 @@ const std::set presolve(const std::vector& initial_chcs) { } } - rhs_preds.clear(); + todo_rhs_preds.clear(); - if (Config::Analysis::log) { + if (logging_active) { std::cout << "remove resolvents with UNSAT constraint: " << std::endl; } // Checking resolvents for satisfiability in a separate loop here, @@ -488,14 +586,81 @@ const std::set presolve(const std::vector& initial_chcs) { // only other clause, which had `G` as its RHS predicate. Thus, whenever // we remove a resolvent, we add its RHS predicate into `rhs_preds` // to re-check it in the next while-loop iteration. - rhs_preds.insert(chc.rhs.value()); + todo_rhs_preds.insert(chc.rhs.value()); } } - chcs = chcs_with_sat_guard; + // We can eliminate even more predicates by merging facts. For example, the + // facts: + // + // x=0 ==> F(x) + // + // x>0 ==> F(x) + // + // can be merged to: + // + // (x=0 \/ x>0) ==> F(x) + // + // This can make all facts unilaterally resolvable as long as the RHS predicate + // does not also occur on the RHS of a rule. + // + // Arguably, that just off-loads dealing with disjunctions to the linear solver. + // We also loose some CHC structure pushing disjunctions into the clause constraint. + // However, ADCL is designed to deal with disjunctions. And computing all resolvents + // in the non-linear solver is worst-case exponential in the number of LHS predicates, + // so it's probably worth it to eliminate as many LHS predicate as possible. + // + // For that we filter-out all the facts we currently have and partition them by their + // RHS predicates. Then we merge every group into a single fact. Notice, that we have + // to do this step repeatedly because some clauses might only later turn into facts. + // For example, here `G(x)` can not completely be merged into a single fact in the + // beginning: + // + // x=0 ==> F(x) + // + // x=1 ==> F(x) + // + // x=2 ==> G(x) + // + // F(x) ==> G(x) + // + // We merge all `F(x)` facts which makes the result unilaterally resolvable: + // + // (x=0 \/ x=1) ==> F(x) + // + // x=2 ==> G(x) + // + // F(x) /\ ==> G(x) + // + // We eliminate `F(x)` and get: + // + // x=2 ==> G(x) + // + // (x>0 \/ x=0) ==> G(x) + // + // Now, `G(x)` can be merged into a single fact: + // + // (x=0 \/ x=1 \/ x=2) ==> G(x) + // + const auto& [ facts, rest_chcs ] = partitionFacts(chcs_with_sat_guard); + chcs = rest_chcs; + for (const auto& [_, fact_group]: partitionByRHS(facts)) { + const Clause& merged_fact = merge_facts(fact_group); + + // If `fact_group.size() == 1` then `merge_facts` has nothing to merge and just + // returns the single fact unchanged. Thus, no facts where eliminated and nothing + // new became unilaterally resolvable. On the other hand, if `fact_group.size() > 1` + // then facts got eliminated, so the resulting clause might now be unilaterally + // resolvable and we add its RHS predicate back into `todo_rhs_preds`. + if (fact_group.size() > 1) { + todo_rhs_preds.insert(merged_fact.rhs.value()); + } + + chcs.insert(merged_fact); + } } - if (Config::Analysis::log) { + if (logging_active) { std::cout << std::endl; } diff --git a/src/nonlinear/nonlinear.hpp b/src/nonlinear/nonlinear.hpp index 54d1c130d..a549cdbed 100644 --- a/src/nonlinear/nonlinear.hpp +++ b/src/nonlinear/nonlinear.hpp @@ -23,6 +23,8 @@ void all_resolvents_aux( unsigned& redundant_resolvent_count ); +const Clause merge_facts(const std::vector facts); + const std::vector normalize_all_preds(const std::vector& chc_problem); const std::optional unilaterally_resolvable_with(const FunApp& pred, const std::set& chcs); From 39ae229d9f79dcfb2b5252759ba762bc5f1fc2aa Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 26 Feb 2024 20:20:23 +0100 Subject: [PATCH 22/25] non-linear: various post-merge fixes Even after the fixes getting a lot of regressions on the benchmarks. Since nonlinear preprocessing can eliminate all CHCs in some cases, we also get SAT sometimes now. Benchmark results: * chc comp 2022: -17 unsat, +1 unsat, +40 sat * chc comp 2023: -5 unsat, +2 unsat, +26 sat On instance 248 from chc comp 2023 we get SAT where Z3 gives Unsat. This seems to be unrelated to the nonlinear solver though. --- .gitignore | 3 + benchmarks/LIA.txt | 368 +++++++++--------- benchmarks/comp23-LIA-nonlin.txt | 136 +++---- benchmarks/review22.txt | 18 + run_benchmark.sh | 52 +-- src/analysis/reachability.cpp | 113 ++---- src/analysis/reachability.hpp | 14 +- src/its/itsproblem.cpp | 10 +- src/lib/rule_simplifications/guardtoolbox.cpp | 9 - src/nonlinear/clause.cpp | 92 ++++- src/nonlinear/clause.hpp | 10 +- src/nonlinear/linearsolver.hpp | 2 +- src/nonlinear/nonlinear.cpp | 202 +++++----- src/parser/chc/CHCParseVisitor.cpp | 4 +- 14 files changed, 543 insertions(+), 490 deletions(-) create mode 100644 benchmarks/review22.txt diff --git a/.gitignore b/.gitignore index 14d5b5a22..ee7eb9663 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ benchlog benchscores koat-examples benchmarks* +# Actually I find it kind of important to track benchmark results +# in git, to see how they are affected by changes in the codebase. +!benchmarks/ # hidden files .* diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index e30f26536..0c10bb3ba 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -48,7 +48,7 @@ 046 timeout timeout 047 timeout timeout 048 sat unknown -049 unsat unsat +049 unsat timeout 050 timeout timeout 051 sat unknown 052 timeout timeout @@ -59,121 +59,121 @@ 057 timeout timeout 058 timeout timeout 059 sat timeout -060 unsat unsat -061 sat timeout -062 unsat unsat -063 unsat unsat -064 sat timeout -065 sat unknown +060 unsat unsat +061 sat unknown +062 unsat unsat +063 unsat unsat +064 sat unknown +065 sat sat 066 sat timeout -067 unsat unsat +067 unsat unsat 068 sat timeout -069 unsat unsat -070 sat error +069 unsat unsat +070 sat timeout 071 sat unknown -072 unsat unsat +072 unsat unsat 073 unsat unsat 074 sat timeout -075 unsat unsat +075 unsat unsat 076 unsat unsat -077 unsat unsat +077 unsat unsat 078 sat timeout 079 sat timeout -080 unsat timeout -081 unsat unsat +080 unsat timeout +081 unsat unsat 082 sat timeout 083 sat timeout 084 sat timeout 085 sat timeout -086 unsat timeout -087 timeout unsat +086 unsat timeout +087 timeout timeout 088 sat timeout 089 sat timeout -090 unsat timeout +090 unsat timeout 091 sat timeout -092 timeout unknown -093 unsat timeout +092 timeout timeout +093 unsat timeout 094 timeout timeout -095 sat error +095 sat timeout 096 timeout unsat 097 timeout unsat -098 unsat timeout +098 unsat timeout 099 sat timeout 100 sat timeout 101 timeout timeout 102 timeout timeout -103 unsat unsat -104 unsat unsat -105 unsat unsat -106 unsat unsat +103 unsat unsat +104 unsat unsat +105 unsat unsat +106 unsat unsat 107 sat timeout 108 sat timeout -109 unsat unsat -110 unsat unsat -111 unsat unsat -112 unsat unsat -113 unsat unsat -114 unsat unsat +109 unsat unsat +110 unsat unsat +111 unsat unsat +112 unsat unsat +113 unsat unsat +114 unsat unsat 115 sat unknown -116 sat timeout +116 sat unknown 117 sat timeout -118 sat timeout -119 timeout unsat +118 sat unknown +119 timeout timeout 120 sat timeout 121 sat timeout -122 timeout unknown +122 timeout timeout 123 timeout timeout 124 sat timeout 125 sat timeout -126 timeout unsat +126 timeout timeout 127 sat timeout -128 sat timeout +128 sat unknown 129 sat timeout -130 timeout unsat +130 timeout timeout 131 timeout timeout 132 timeout unknown -133 unsat unsat -134 unsat unsat -135 sat timeout -136 unsat unsat -137 unsat unsat +133 unsat timeout +134 unsat unsat +135 sat unknown +136 unsat unsat +137 unsat unsat 138 sat unknown -139 unsat unsat -140 sat timeout -141 unsat unsat -142 unsat unsat +139 unsat timeout +140 sat unknown +141 unsat unsat +142 unsat unsat 143 sat unknown -144 sat timeout -145 unsat unsat +144 sat unknown +145 unsat unsat 146 sat unknown -147 unsat unsat -148 unsat unsat -149 sat unknown -150 unsat unsat +147 unsat unsat +148 unsat timeout +149 sat timeout +150 unsat unsat 151 sat timeout -152 unsat unsat +152 unsat timeout 153 sat unknown 154 sat timeout -155 sat timeout +155 sat unknown 156 sat timeout -157 unsat unsat -158 unsat unsat -159 sat timeout +157 unsat timeout +158 unsat timeout +159 sat unknown 160 sat timeout 161 sat timeout -162 sat timeout -163 sat timeout -164 unsat unsat -165 unsat unsat -166 sat timeout +162 sat unknown +163 sat unknown +164 unsat timeout +165 unsat timeout +166 sat unknown 167 sat timeout -168 unsat unsat +168 unsat timeout 169 timeout timeout 170 timeout timeout 171 timeout timeout 172 sat timeout 173 timeout timeout -174 timeout timeout +174 timeout unknown 175 timeout timeout 176 timeout timeout 177 timeout timeout @@ -185,10 +185,10 @@ 183 timeout timeout 184 sat timeout 185 unknown timeout -186 sat timeout +186 sat unknown 187 timeout timeout 188 timeout timeout -189 timeout timeout +189 timeout unknown 190 sat timeout 191 timeout timeout 192 sat timeout @@ -197,12 +197,12 @@ 195 timeout timeout 196 timeout timeout 197 sat timeout -198 unsat unsat -199 unsat unsat +198 unsat unsat +199 unsat unsat 200 sat timeout -201 sat unknown +201 sat sat 202 sat unknown -203 unsat unsat +203 unsat unsat 204 sat timeout 205 sat timeout 206 sat timeout @@ -210,11 +210,11 @@ 208 sat timeout 209 timeout timeout 210 sat timeout -211 unsat unsat +211 unsat unsat 212 timeout timeout 213 sat timeout 214 timeout timeout -215 timeout unknown +215 timeout timeout 216 sat timeout 217 timeout timeout 218 timeout timeout @@ -222,16 +222,16 @@ 220 timeout timeout 221 sat timeout 222 sat timeout -223 sat timeout +223 sat sat 224 sat timeout 225 timeout timeout 226 timeout timeout 227 sat timeout 228 sat timeout 229 sat timeout -230 sat unknown -231 sat timeout -232 sat unknown +230 sat timeout +231 sat timeout +232 sat sat 233 sat timeout 234 sat timeout 235 sat timeout @@ -244,118 +244,118 @@ 242 timeout timeout 243 timeout timeout 244 sat timeout -245 unsat unsat -246 sat timeout -247 unsat unsat -248 sat timeout -249 unsat unsat -250 sat timeout -251 sat timeout -252 sat timeout -253 unsat unsat -254 sat timeout -255 sat timeout -256 unsat unsat +245 unsat unsat +246 sat sat +247 unsat unsat +248 sat sat +249 unsat timeout +250 sat sat +251 sat sat +252 sat sat +253 unsat unsat +254 sat sat +255 sat sat +256 unsat unsat 257 sat timeout -258 sat unknown -259 unsat unsat -260 sat timeout -261 sat unknown -262 sat unknown -263 unsat unsat -264 unsat unsat +258 sat sat +259 unsat unsat +260 sat sat +261 sat sat +262 sat unknown +263 unsat unsat +264 unsat unsat 265 timeout timeout -266 unsat timeout +266 unsat timeout 267 timeout timeout -268 unsat unsat -269 sat timeout +268 unsat unsat +269 sat sat 270 sat timeout -271 unsat unsat +271 unsat unsat 272 sat unknown -273 unsat unsat +273 unsat unsat 274 timeout timeout -275 unsat timeout -276 unsat timeout +275 unsat timeout +276 unsat timeout 277 unsat unsat -278 sat timeout -279 unsat timeout -280 unsat timeout +278 sat sat +279 unsat timeout +280 unsat timeout 281 timeout timeout 282 timeout timeout -283 unsat timeout +283 unsat timeout 284 timeout timeout 285 timeout timeout -286 unsat timeout +286 unsat timeout 287 timeout timeout 288 sat timeout -289 unsat timeout +289 unsat timeout 290 timeout timeout 291 sat timeout 292 timeout timeout 293 sat timeout 294 timeout timeout -295 unsat timeout +295 unsat timeout 296 timeout timeout -297 unsat timeout +297 unsat timeout 298 timeout timeout 299 sat timeout 300 timeout timeout -301 unsat timeout +301 unsat timeout 302 timeout timeout 303 timeout timeout 304 timeout timeout -305 unsat timeout +305 unsat timeout 306 timeout timeout -307 unsat timeout +307 unsat timeout 308 timeout timeout 309 timeout timeout 310 timeout timeout 311 timeout timeout 312 sat timeout 313 timeout timeout -314 unsat timeout +314 unsat timeout 315 timeout timeout 316 timeout timeout -317 unsat timeout +317 unsat timeout 318 sat timeout -319 unsat timeout +319 unsat timeout 320 sat timeout 321 timeout timeout 322 timeout timeout 323 sat timeout 324 sat timeout 325 timeout timeout -326 unsat timeout +326 unsat timeout 327 timeout timeout -328 unsat timeout +328 unsat timeout 329 sat timeout 330 timeout timeout 331 timeout timeout 332 timeout timeout -333 unsat timeout +333 unsat timeout 334 sat timeout 335 sat timeout -336 unsat unsat +336 unsat unsat 337 sat timeout -338 unsat unsat -339 unsat unsat +338 unsat unsat +339 unsat unsat 340 sat timeout 341 sat timeout -342 unsat unsat +342 unsat unsat 343 unsat unsat -344 unsat timeout -345 unsat unsat +344 unsat timeout +345 unsat unsat 346 sat timeout 347 sat timeout 348 sat timeout -349 unsat unsat -350 unsat unsat -351 unsat unsat +349 unsat unsat +350 unsat unsat +351 unsat timeout 352 sat timeout -353 unsat timeout -354 unsat timeout +353 unsat timeout +354 unsat timeout 355 sat timeout -356 unsat timeout +356 unsat timeout 357 timeout timeout 358 sat timeout 359 sat timeout @@ -363,94 +363,94 @@ 361 timeout timeout 362 sat timeout 363 sat timeout -364 unsat timeout +364 unsat timeout 365 sat timeout -366 sat timeout -367 sat timeout -368 sat timeout -369 sat timeout -370 sat timeout -371 sat timeout -372 sat timeout -373 sat timeout -374 sat timeout -375 sat timeout -376 sat timeout +366 sat sat +367 sat sat +368 sat unknown +369 sat sat +370 sat sat +371 sat sat +372 sat sat +373 sat sat +374 sat sat +375 sat sat +376 sat sat 377 sat unknown -378 sat timeout -379 sat timeout -380 sat timeout -381 sat timeout -382 sat timeout -383 sat timeout +378 sat sat +379 sat sat +380 sat sat +381 sat sat +382 sat sat +383 sat sat 384 sat timeout -385 sat timeout +385 sat sat 386 sat timeout -387 unsat unsat -388 sat timeout -389 unsat unsat +387 unsat unsat +388 sat sat +389 unsat unsat 390 sat timeout -391 sat timeout -392 sat timeout -393 unsat timeout +391 sat sat +392 sat sat +393 unsat timeout 394 sat timeout 395 sat timeout -396 sat timeout -397 sat timeout +396 sat sat +397 sat sat 398 sat timeout -399 unsat unsat -400 unsat timeout +399 unsat unsat +400 unsat timeout 401 sat timeout 402 timeout timeout 403 timeout timeout -404 timeout timeout +404 sat timeout 405 timeout timeout 406 timeout timeout 407 sat timeout 408 sat timeout -409 unsat timeout +409 unsat timeout 410 sat timeout -411 unsat timeout +411 unsat timeout 412 sat timeout -413 sat timeout -414 unsat timeout +413 sat sat +414 unsat timeout 415 sat timeout 416 sat timeout 417 sat timeout -418 sat timeout -419 unsat timeout +418 sat sat +419 unsat timeout 420 sat timeout 421 sat timeout -422 unsat timeout -423 unsat timeout +422 unsat timeout +423 unsat unsat 424 sat timeout 425 sat timeout -426 unsat timeout -427 unsat unsat +426 unsat timeout +427 unsat unsat 428 sat timeout -429 unsat timeout -430 unsat timeout -431 unsat timeout +429 unsat timeout +430 unsat timeout +431 unsat timeout 432 sat timeout 433 sat timeout -434 unsat timeout +434 unsat timeout 435 sat timeout -436 unsat unsat -437 unsat timeout +436 unsat unsat +437 unsat timeout 438 timeout timeout 439 sat timeout 440 sat timeout 441 sat timeout -442 unsat unsat +442 unsat timeout 443 sat timeout -444 unsat unsat +444 unsat unsat 445 sat timeout 446 sat timeout 447 sat timeout -448 unsat timeout +448 unsat timeout 449 sat timeout 450 timeout timeout -451 unsat timeout -452 unsat timeout +451 unsat timeout +452 unsat timeout 453 sat timeout 454 sat timeout diff --git a/benchmarks/comp23-LIA-nonlin.txt b/benchmarks/comp23-LIA-nonlin.txt index 35943f9c0..f847e2494 100644 --- a/benchmarks/comp23-LIA-nonlin.txt +++ b/benchmarks/comp23-LIA-nonlin.txt @@ -52,9 +52,9 @@ 050 sat timeout 051 sat timeout 052 unsat unsat -053 sat timeout +053 sat unknown 054 sat timeout -055 sat timeout +055 sat unknown 056 unsat unsat 057 unsat timeout 058 sat timeout @@ -79,7 +79,7 @@ 077 sat timeout 078 timeout timeout 079 sat timeout -080 sat timeout +080 sat unknown 081 timeout timeout 082 sat timeout 083 timeout timeout @@ -105,12 +105,12 @@ 103 unsat unsat 104 unsat unsat 105 sat timeout -106 unsat unsat +106 unsat timeout 107 sat timeout -108 unsat unsat -109 unsat unsat +108 unsat timeout +109 unsat timeout 110 sat timeout -111 unsat unsat +111 unsat timeout 112 sat timeout 113 sat timeout 114 timeout timeout @@ -120,9 +120,9 @@ 118 timeout timeout 119 unsat unsat 120 timeout timeout -121 unsat unsat -122 unsat timeout -123 unsat unsat +121 unsat timeout +122 unsat unsat +123 unsat timeout 124 unsat timeout 125 sat timeout 126 unsat unsat @@ -171,8 +171,8 @@ 169 sat timeout 170 sat timeout 171 unsat unsat -172 sat timeout -173 sat timeout +172 sat sat +173 sat sat 174 sat timeout 175 timeout timeout 176 sat timeout @@ -184,8 +184,8 @@ 182 timeout timeout 183 timeout timeout 184 timeout timeout -185 sat timeout -186 sat timeout +185 sat sat +186 sat unknown 187 sat timeout 188 unsat timeout 189 unsat timeout @@ -196,10 +196,10 @@ 194 unsat unsat 195 unsat unsat 196 unsat timeout -197 sat timeout -198 sat timeout +197 sat sat +198 sat unknown 199 unsat unsat -200 sat timeout +200 sat sat 201 unsat unsat 202 unsat unsat 203 unsat unsat @@ -207,57 +207,57 @@ 205 unsat unsat 206 unsat unsat 207 unsat unsat -208 sat timeout -209 sat timeout -210 sat timeout -211 sat timeout -212 sat timeout +208 sat unknown +209 sat sat +210 sat sat +211 sat sat +212 sat sat 213 unsat unsat 214 unsat timeout 215 unsat timeout -216 sat timeout +216 sat sat 217 unsat timeout 218 unsat unsat -219 sat timeout -220 sat timeout +219 sat sat +220 sat sat 221 sat timeout 222 sat timeout 223 sat timeout 224 unsat timeout -225 sat timeout +225 sat unknown 226 sat timeout -227 sat timeout -228 sat timeout +227 sat unknown +228 sat unknown 229 unsat unsat 230 sat timeout 231 unsat timeout 232 sat timeout 233 sat timeout 234 unsat timeout -235 unsat timeout +235 unsat unsat 236 unsat timeout -237 unsat timeout +237 unsat unsat 238 unsat unsat 239 sat timeout 240 sat timeout 241 sat timeout 242 sat timeout -243 sat timeout +243 sat unknown 244 sat timeout 245 sat timeout 246 sat timeout 247 unsat timeout -248 unsat timeout +248 unsat sat 249 unsat timeout 250 unsat timeout 251 unsat unsat -252 sat timeout +252 sat sat 253 unsat timeout 254 unsat timeout 255 unsat unsat 256 timeout timeout -257 sat timeout -258 sat timeout +257 sat unknown +258 sat unknown 259 timeout timeout 260 unsat timeout 261 unsat timeout @@ -288,7 +288,7 @@ 286 timeout timeout 287 timeout timeout 288 unsat timeout -289 sat timeout +289 sat unknown 290 sat timeout 291 timeout timeout 292 timeout timeout @@ -326,7 +326,7 @@ 324 unsat timeout 325 unsat timeout 326 unsat timeout -327 sat timeout +327 sat unknown 328 timeout timeout 329 timeout timeout 330 unsat timeout @@ -364,62 +364,62 @@ 362 unsat timeout 363 unsat timeout 364 unsat timeout -365 sat timeout -366 sat timeout +365 sat unknown +366 sat unknown 367 unsat timeout 368 unsat unsat -369 sat timeout -370 sat timeout +369 sat unknown +370 sat unknown 371 unsat unsat -372 sat timeout -373 sat timeout +372 sat unknown +373 sat unknown 374 unsat timeout -375 sat timeout +375 sat unknown 376 timeout timeout -377 sat timeout -378 sat timeout +377 sat unknown +378 sat unknown 379 unsat timeout -380 sat timeout +380 sat sat 381 unsat timeout -382 sat timeout +382 sat unknown 383 unsat unsat -384 sat timeout -385 sat timeout -386 sat timeout -387 sat timeout -388 sat timeout -389 sat timeout -390 sat timeout +384 sat sat +385 sat sat +386 sat sat +387 sat sat +388 sat sat +389 sat unknown +390 sat sat 391 unsat unsat 392 unsat unsat -393 sat timeout -394 sat timeout -395 sat timeout +393 sat sat +394 sat sat +395 sat sat 396 sat timeout 397 sat timeout 398 sat timeout 399 sat timeout 400 sat timeout -401 sat timeout -402 sat timeout +401 sat unknown +402 sat sat 403 sat timeout -404 sat timeout +404 sat unknown 405 sat timeout 406 sat timeout 407 unsat timeout -408 sat timeout -409 sat timeout -410 sat timeout +408 sat sat +409 sat sat +410 sat unknown 411 sat timeout -412 sat timeout -413 sat timeout +412 sat unknown +413 sat unknown 414 sat timeout 415 sat timeout 416 timeout timeout 417 timeout timeout -418 sat timeout +418 sat unknown 419 timeout timeout -420 sat timeout +420 sat unknown 421 sat timeout 422 timeout timeout 423 timeout timeout diff --git a/benchmarks/review22.txt b/benchmarks/review22.txt new file mode 100644 index 000000000..3c710fcb1 --- /dev/null +++ b/benchmarks/review22.txt @@ -0,0 +1,18 @@ +### Z3 ADCL +087 timeout timeout +049 unsat timeout +119 timeout timeout +126 timeout timeout +130 timeout timeout +133 unsat timeout +139 unsat timeout +148 unsat timeout +152 unsat timeout +157 unsat timeout +158 unsat timeout +164 unsat timeout +165 unsat timeout +168 unsat timeout +249 unsat timeout +351 unsat timeout +442 unsat timeout diff --git a/run_benchmark.sh b/run_benchmark.sh index f1aa8f741..5d2b3f837 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -11,24 +11,24 @@ pushd build make -j4 popd -# DEBUG: non-deterministic unsat/unknown for 133,139,157 +# 103 unsat sat +# 247 unsat sat +# 253 unsat sat +# 256 unsat sat +# 259 unsat sat + +# gdb --args \ +./build/loat-static \ + --mode reachability \ + --format horn \ + --proof-level 0 \ + "../chc-comp23-benchmarks/LIA-nonlin/chc-LIA_248.smt2" + # "../chc-comp22-benchmarks/LIA/chc-LIA_235.smt2" + # "../chc-comp22-benchmarks/LIA/chc-LIA_148_unknown.smt2" + # "../chc-comp22-benchmarks/LIA/test3.smt2" -# TODO: propagate equalities for Clauses - -# # gdb --args \ -# time ./build/loat-static \ -# --mode reachability \ -# --format horn \ -# --proof-level 0 \ -# "../chc-comp23-benchmarks/LIA-nonlin/chc-LIA_069.smt2" -# # "../chc-comp22-benchmarks/LIA/chc-LIA_112.smt2" - -# popd -# exit - -# REVIEW (comp22) : 076,073,112 -# REVIEW (comp22) : 119 (1m20s) / 126 (2m19s) -# REVIEW (comp23) : 111 +popd +exit ########################################################################## @@ -43,22 +43,22 @@ do continue else read idx z3_result adcl_result <<< "$line" - file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" - # file="../chc-comp23-benchmarks/${benchmark}-nonlin/chc-${benchmark}_${idx}.smt2" + # file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" + file="../chc-comp23-benchmarks/${benchmark}-nonlin/chc-${benchmark}_${idx}.smt2" - # if true; then + if true; then # if [[ "$z3_result" == "unsat" ]] && [[ "$adcl_result" == "timeout" ]]; then # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then - # if [[ "$z3_result" != "sat" ]]; then - # if [[ "$adcl_result" == "unknown" ]]; then - if [[ "$adcl_result" == "unsat" ]]; then + # if [[ "$z3_result" != "timeout" ]]; then + # if [[ "$z3_result" == "sat" ]]; then + # if [[ "$adcl_result" == "unsat" ]]; then set +e result=$(timeout 20 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") # result=$(timeout 5 z3 "$file") exit_status=$? set -e - if [ $exit_status -eq 0 ]; then + if [[ $exit_status -eq 0 ]]; then result=$(echo "$result" | head -n 1) # result="$result" elif [ $exit_status -eq 124 ]; then @@ -76,8 +76,10 @@ do fi fi -done < "./benchmarks/${benchmark}.txt" +done < "./benchmarks/review23.txt" +# done < "./benchmarks/${benchmark}.txt" # done < "./benchmarks/comp23-${benchmark}-nonlin.txt" # "undo" pushd popd + diff --git a/src/analysis/reachability.cpp b/src/analysis/reachability.cpp index 1f9cae7eb..f1876ccbd 100644 --- a/src/analysis/reachability.cpp +++ b/src/analysis/reachability.cpp @@ -124,7 +124,6 @@ Reachability::Reachability(ITSProblem &chcs, bool incremental_mode): std::cout << "Initial ITS" << std::endl; ITSExport::printForProof(chcs, std::cout); } - const auto res {Preprocess::preprocess(chcs, incremental_mode)}; if (res) { proof.concat(res.getProof()); @@ -246,19 +245,11 @@ void Reachability::update_cpx() { } Rule Reachability::compute_resolvent(const TransIdx idx, const BoolExpr &implicant) const { - // static Rule dummy(top(), Subs()); - - // Resolvent only has to be computed in incremental mode (i.e. when the problem includes non linear CHCs) or - // for complexity analysis. Otherwise we can skip it. - // if (Config::Analysis::complexity() || incremental_mode) { - auto resolvent = idx->withGuard(implicant); - if (!trace.empty()) { - resolvent = Chaining::chain(trace.back().resolvent, resolvent).first; - } - return *Preprocess::preprocessRule(resolvent); - // } else { - // return dummy; - // } + auto resolvent = idx->withGuard(implicant); + if (!trace.empty()) { + resolvent = Chaining::chain(trace.back().resolvent, resolvent).first; + } + return *Preprocess::preprocessRule(resolvent); } bool Reachability::store_step(const TransIdx idx, const Rule &implicant) { @@ -355,7 +346,6 @@ void Reachability::init() { } } - void Reachability::luby_next() { for (const auto &x: chcs.getAllTransitions()) { if (is_learned_clause(&x) && !x.isPoly()) { @@ -437,9 +427,7 @@ std::optional Reachability::resolve(const TransIdx idx) { } else { block->second.clear(); } - if (Config::Analysis::log) { - std::cout << "could not find a model for " << idx << std::endl; - } + if (Config::Analysis::log) std::cout << "could not find a model for " << idx << std::endl; } } return {}; @@ -566,12 +554,7 @@ std::unique_ptr Reachability::learn_clause(const Rule &rule, cons } if (accel_res.accel) { // acceleration succeeded, simplify the result - auto simplified = Preprocess::preprocessRule(accel_res.accel->rule); - - // if (!simplified->isLinear()) { - // return std::make_unique(1); - // } - + auto simplified = Preprocess::preprocessRule(accel_res.accel->rule); if (simplified->getUpdate() != simp->getUpdate()) { // accelerated rule differs from the original one, update the result if (Config::Analysis::complexity()) { @@ -764,12 +747,6 @@ void Reachability::bump_penalty(const TransIdx idx) { * this function returns nullopt. */ const std::optional Reachability::trace_as_fact() { - // if (!incremental_mode) { - // // In non-incremental mode the trace resolvent is not really computed and only a dummy expression. - // // See `compute_resolvent` above. So `trace_as_fact` will silently return nonsense. - // throw std::logic_error("Calling `trace_as_fact` in non-incremental mode doesn't make sense."); - // } - if (trace.empty()) { return {}; } else { @@ -797,7 +774,6 @@ void Reachability::analyze() { // also try rules with polynomial guard. Otherwise also consider exponential // (arbitrary) guards. for (const auto tier: LinearSolver::constraint_tiers) { - blocked_clauses[0].clear(); derive_new_facts(tier); if (get_analysis_result() == LinearSolver::Result::Unsat) { @@ -823,23 +799,23 @@ void Reachability::analyze() { } const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTier max_constr_tier) { + luby_count = 0; + luby = {1,1}; + restart(); + std::set derived_facts; if (try_to_finish()) { return derived_facts; } - + blocked_clauses[0].clear(); do { + // QUESTION: still correct handling luby this way in incremental mode? ++luby_count; size_t next_restart = luby_unit * luby.second; std::unique_ptr state; if (Config::Analysis::log) std::cout << "trace: " << trace << std::endl; if (!trace.empty()) { - // true by default, because if no case in the following for-loop applies, then we still want to add a new fact, - // because we have a non-empty unseen trace. - bool should_add_fact = true; - - // QUESTION: why not exit loop after frist matching case? for (auto backlink = has_looping_suffix(trace.size() - 1); backlink; backlink = has_looping_suffix(*backlink - 1)) { @@ -852,8 +828,6 @@ const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTi backtrack(); proof.headline("Covered"); print_state(); - // if we run into `covered` we don't add a new fact, because backtrack to previously seen facts. - should_add_fact = false; break; } else if (state->succeeded()) { if (simple_loop) { @@ -864,9 +838,6 @@ const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTi if ((drop || simple_loop) && try_to_finish()) { return derived_facts; } - - // if we just applied acceleration successfully, then the trace contains a new (more general) fact. - should_add_fact = true; } else if (state->dropped()) { if (simple_loop) { block(step); @@ -874,8 +845,6 @@ const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTi proof.majorProofStep("Accelerate and Drop", state->dropped()->get_proof(), chcs); print_state(); - // QUESTION: why shouldn't we add a fact in this case again? - should_add_fact = false; break; } else if (state->unsat()) { proof.majorProofStep("Nonterm", **state->unsat(), chcs); @@ -885,36 +854,9 @@ const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTi return derived_facts; } else if (state->unroll() && state->unroll()->acceleration_failed()) { - // QUESTION: this case was added after with merge. How to set `should_add_fact` ? - // stop searching for longer loops if the current one was already too complicated break; } } - - if (should_add_fact) { // TODO: when trace not empty - // Using a crude (additional) redundancy criterion for facts here. We identify facts by the trace that lead to them. - // This is only sufficient because equivalent facts can have multiple traces. We don't need to memoize the entire - // trace structure. It's enough to store clause_idx/implicant for each trace step. - std::vector> trace_id; - for (const auto &step: trace) { - trace_id.push_back(std::make_pair( - step.clause_idx, - step.implicant - )); - } - - // Forwarding initial facts is redundant, because they are already obtained by calling `get_initial_facts`. - // TODO: Remove `get_initial_facts` and force non linear solver to get inital facts from first round of - // calling `derive_new_facts`. - // NOTE: tried the approach above but it definitely lead to more timeouts. - bool is_initial_fact = trace.size() == 1 && chcs.isInitialTransition(trace[0].clause_idx); - - if (!seen_traces.contains(trace_id) && !is_initial_fact) { - derived_facts.insert(trace_as_fact().value()); - seen_traces.insert(trace_id); - } - } - } if (luby_count >= next_restart || (state && state->restart()) || !check_consistency()) { if (Config::Analysis::log) std::cout << "restarting after " << luby_count << " iterations" << std::endl; @@ -923,7 +865,7 @@ const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTi auto try_set = trace.empty() ? chcs.getInitialTransitions() : chcs.getSuccessors(trace.back().clause_idx); for (auto it = try_set.begin(); it != try_set.end();) { - // Only consider rules for "Step" whose guard has at most the the constraint tier + // Only consider rules for "Step" whose guard has at most the constraint tier // configured for the current run. For example if `max_constr_tier` is `Polynomial`, // we only consider rules with `Linear` or `Polynomial` guard but ignore rules with // `Exponential` guard. @@ -980,6 +922,26 @@ const std::set Reachability::derive_new_facts(LinearSolver::ConstraintTi return derived_facts; } else if (all_failed) { + // Using a crude (additional) redundancy criterion for facts here. We identify facts by the trace that lead to them. + // This is only sufficient because equivalent facts can have multiple traces. We don't need to memoize the entire + // trace structure. It's enough to store clause_idx/implicant for each trace step. + std::vector> trace_id; + for (const auto &step: trace) { + trace_id.push_back(std::make_pair( + step.clause_idx, + step.implicant + )); + } + + // Assume non-linear solver already nows about facts that are initially part of the CHC problem, + // so don't forward them eihter: + bool is_initial_fact = trace.size() == 1 && chcs.isInitialTransition(trace[0].clause_idx); + + if (!seen_traces.contains(trace_id) && !is_initial_fact) { + derived_facts.insert(trace_as_fact().value()); + seen_traces.insert(trace_id); + } + backtrack(); proof.headline("Backtrack"); print_state(); @@ -1019,13 +981,6 @@ void Reachability::add_clauses(const std::set &clauses) { ITSExport::printForProof(chcs, std::cout); } } - - // TODO: shouldn't we clear all components? - blocked_clauses[0].clear(); - - // When we add a new linear clause we have to restart the solver, - // i.e. reset the trace, otherwise we might block a potential reachability path. - restart(); } } diff --git a/src/analysis/reachability.hpp b/src/analysis/reachability.hpp index d2e182647..5109633b5 100644 --- a/src/analysis/reachability.hpp +++ b/src/analysis/reachability.hpp @@ -183,11 +183,6 @@ class Reachability : public ILinearSolver { VarSet prog_vars {}; - /** - * clauses up to this one are original ones, all other clauses are learned - */ - unsigned last_orig_clause {0}; - std::pair luby {1,1}; unsigned luby_unit {10}; @@ -211,9 +206,6 @@ class Reachability : public ILinearSolver { RuleResult instantiate(const NumVar &n, const Rule &rule) const; - /** - * initializes all data structures after preprocessing - */ void init(); /** @@ -314,13 +306,9 @@ class Reachability : public ILinearSolver { void analyze(); - void analyze_incremental(); - - bool main_loop_body(); - void restart(); - LinearSolver::Result analysis_result; + LinearSolver::Result analysis_result = LinearSolver::Result::Pending; LinearSolver::Result analysis_result_from(std::string res) const; diff --git a/src/its/itsproblem.cpp b/src/its/itsproblem.cpp index 0d3589c3f..1b159b0ca 100644 --- a/src/its/itsproblem.cpp +++ b/src/its/itsproblem.cpp @@ -359,11 +359,15 @@ const Clause ITSProblem::clauseFrom(TransIdx rule) const { * Adds a linear clause to the ITS problem by converting it to an ITS rule. * Throws an error if the given clause is non-linear. */ -void ITSProblem::addClause(const Clause &c) { - if (!c.isLinear()) { +void ITSProblem::addClause(const Clause &initial_chc) { + if (!initial_chc.isLinear()) { throw std::logic_error("Tried to add non-linear clause to ITS"); } + // need to make sure that predicate arguments are pairwise distinct before + // converting to Rule. TODO: skip this + Clause c = removeDuplicatePredicateArguments(initial_chc); + // If the clause is linear, extract the single LHS predicate. Or in case there are zero // LHS predicates, construct a dummy predicate with the initial ITS location as the // predicate symbol. @@ -454,7 +458,7 @@ ITSPtr ITSProblem::fromClauses(const std::set& chcs) { ITSPtr ITSProblem::fromClauses(const std::vector& chc_problem) { ITSPtr its = std::make_shared(); - auto [max_int_arity, max_bool_arity] = maxArity(chc_problem); + const auto [max_int_arity, max_bool_arity] = maxArity(chc_problem); its->setInitialLocation(its->addNamedLocation("LoAT_init")); // Collect all predicate that appear in `chc_problem`, add them as diff --git a/src/lib/rule_simplifications/guardtoolbox.cpp b/src/lib/rule_simplifications/guardtoolbox.cpp index db2be396f..bfaca8456 100644 --- a/src/lib/rule_simplifications/guardtoolbox.cpp +++ b/src/lib/rule_simplifications/guardtoolbox.cpp @@ -103,18 +103,9 @@ RuleResult GuardToolbox::propagateBooleanEqualities(const Rule &rule) { // collect implied program variable equalities: for (const auto &var: equiv.domain()) { if (expr::isProgVar(var)) { - // if (res->getGuard()->countOccuranceOf(var) == 1) { - // // when the program variable occurs only once in the expression, - // // there is no need to substitute it. This also prevents an - // // infinite loop, because when we re-add the program variables to the - // // guard using `buildAnd`, we trigger `propagateBooleanEqualities` - // // again. - // equiv.erase(var); - // } else { program_var_equalities.push_back( expr::mkEq(expr::toExpr(var), equiv.get(var))->simplify() ); - // } } } diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index 0312e2fa2..d1e227eea 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -2,6 +2,7 @@ #include "boolexpr.hpp" #include "expr.hpp" #include "theory.hpp" +#include #include #include #include @@ -58,9 +59,9 @@ const std::optional computeUnifier(const std::vector &args1, const st if (subs.contains(var1)) { // If `var1` is already contained in `subs`, then `pred1` must // have `var1` multiple times as an argument. This is an implict - // equality that must also hold in `pred2` so we map `var2` to - // whatever `var1` already maps to. - subs.put(var2, subs.get(var1)); + // equality. We assume at this point that this doesn't occur and + // that all equalities are explicitly mentioned in the constraint. + throw std::logic_error("computeUnifier: predicate has duplicate arguments"); } else { // QUESTION: I suspect some C++ template magic can make this prettier bool both_nums = std::holds_alternative(var1) && @@ -108,6 +109,13 @@ const FunApp FunApp::renameWith(const Subs &renaming) const { return FunApp(name, args_renamed); } +/** + * Create a copy of predicate with argument vector replaced. + */ +const FunApp FunApp::withArgs(const std::vector new_args) const { + return FunApp(name, new_args); +} + /** * Collect set of variables used in predicate arguments. */ @@ -355,12 +363,14 @@ const std::tuple, std::set> partitionFacts(const std::s * equivalence. */ const Clause Clause::normalize() const { - if (!this->rhs.has_value()) { - return *this; + const auto& chc_uniq_args = removeDuplicatePredicateArguments(*this); + + if (!chc_uniq_args.rhs.has_value()) { + return chc_uniq_args; } - const auto rhs = this->rhs.value(); + const auto rhs = chc_uniq_args.rhs.value(); - // construct a vector of variables with the same length as `this->rhs.args` + // construct a vector of variables with the same length as `rhs.args` // and the same variable types in each position, except that the variable // indices are simply: 0,1,2,3,etc. std::vector target_args; @@ -380,7 +390,7 @@ const Clause Clause::normalize() const { const auto unifier = computeUnifier(rhs.args, target_args); if (unifier.has_value()) { - return this->renameWith(unifier.value()); + return chc_uniq_args.renameWith(unifier.value()); } else { // Variable vectors should always be unifiable unless the vectors have differnt length, // but the vectors should have the same length by construction so this error should not @@ -435,7 +445,7 @@ bool Clause::isQuery() const { * Return first predicate with given `name` if it occurs on the LHS of the clause. * Returns nullopt if there is no predicate with given `name`. */ -std::optional Clause::indexOfLHSPred(const std::basic_string name) const { +std::optional Clause::indexOfLHSPred(const std::string name) const { for (unsigned i=0; i maxArity(const std::vector return std::pair(max_int_arity, max_bool_arity); } +/** + * If a predicate has duplicate arguments as in + * + * F(x,y,x) + * + * then this constitutes an implict equality. This function makes the equality + * explicit by moving it into the guard. + */ +const Clause removeDuplicatePredicateArguments(const Clause& chc) { + std::vector extra_conditions = { chc.guard }; + + std::vector new_lhs; + new_lhs.reserve(chc.lhs.size()); + for (const auto& pred: chc.lhs) { + std::vector new_args; + + for (const auto& arg: pred.args) { + if (std::find(new_args.begin(), new_args.end(), arg) != new_args.end()) { + auto fresh_var = expr::next(arg); + extra_conditions.push_back(expr::mkEq(expr::toExpr(arg), expr::toExpr(fresh_var))); + new_args.push_back(fresh_var); + } else { + new_args.push_back(arg); + } + } + + new_lhs.push_back(pred.withArgs(new_args)); + } + + std::unique_ptr new_rhs; + if (chc.rhs.has_value()) { + std::vector new_args; + + for (const auto& arg: chc.rhs.value().args) { + if (std::find(new_args.begin(), new_args.end(), arg) != new_args.end()) { + auto fresh_var = expr::next(arg); + extra_conditions.push_back(expr::mkEq(expr::toExpr(arg), expr::toExpr(fresh_var))); + new_args.push_back(fresh_var); + } else { + new_args.push_back(arg); + } + } + + new_rhs = std::make_unique( + chc.rhs.value().withArgs(new_args) + ); + } + + const auto new_guard = BExpression::buildAnd(extra_conditions)->simplify(); + return Clause(new_lhs, chc.rhs.has_value() ? *new_rhs : chc.rhs, new_guard); +} + +// QUESTION: does C++ have no iterable type of somehting so we can abstract over the collection type? bool allLinear(const std::vector& chcs) { for (const auto& chc: chcs) { if (!chc.isLinear()) { @@ -481,6 +544,17 @@ bool allLinear(const std::vector& chcs) { return true; } +bool allLinear(const std::set& chcs) { + chcs.begin(); + + for (const auto& chc: chcs) { + if (!chc.isLinear()) { + return false; + } + } + + return true; +} bool operator<(const FunApp &fun1, const FunApp &fun2) { if (fun1.name < fun2.name) { diff --git a/src/nonlinear/clause.hpp b/src/nonlinear/clause.hpp index 05a5e6d7c..9f482b664 100644 --- a/src/nonlinear/clause.hpp +++ b/src/nonlinear/clause.hpp @@ -1,20 +1,21 @@ #include "theory.hpp" -#include "types.hpp" #include class FunApp { public: - const std::basic_string name; + const std::string name; const std::vector args; FunApp( - const std::basic_string name, + const std::string name, const std::vector args ): name(name), args(args) {} const FunApp renameWith(const Subs &renaming) const; + const FunApp withArgs(const std::vector new_args) const; + const VarSet vars() const; unsigned long intArity() const; @@ -64,6 +65,8 @@ class Clause { std::optional indexOfLHSPred(const std::basic_string name) const; }; +const Clause removeDuplicatePredicateArguments(const Clause& chc); + const std::tuple, std::set> partitionByDegree(const std::set& chcs); const std::map, std::set> partitionFactsByRHS(const std::set& facts); @@ -78,6 +81,7 @@ const std::optional computeUnifier(const std::vector &args1, const st const std::pair maxArity(const std::vector& chc_problem); bool allLinear(const std::vector& chcs); +bool allLinear(const std::set& chcs); // implement comparison operators so they can be stored in std::set bool operator<(const Clause &c1, const Clause &c2); diff --git a/src/nonlinear/linearsolver.hpp b/src/nonlinear/linearsolver.hpp index c699834cb..6f05d888a 100644 --- a/src/nonlinear/linearsolver.hpp +++ b/src/nonlinear/linearsolver.hpp @@ -6,7 +6,7 @@ namespace LinearSolver { enum ConstraintTier { Linear, Polynomial, Exponential }; /** - * Fixed list of all constaint tiers in order of solving difficulty. + * Fixed list of all constraint tiers in order of solving difficulty. */ const std::vector constraint_tiers = { LinearSolver::ConstraintTier::Linear, diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index b6d1ad952..5322925e5 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -1,102 +1,74 @@ #include "nonlinear.hpp" -#include "booltheory.hpp" #include "boolexpr.hpp" -#include "config.hpp" -#include "expr.hpp" #include "itsproblem.hpp" #include "reachability.hpp" -#include "linearizingsolver.hpp" #include "linearsolver.hpp" #include "smt.hpp" #include "smtfactory.hpp" #include "theory.hpp" -#include #include #include -// for debugging: active logging only in this file -const bool logging_active = Config::Analysis::log; +// for debugging: activate logging only in this file +const bool dbg = false; -/* - -c1) fib(0, 1) -c2) fib(2, 3) -c3) fib(x1, y1) /\ fib(x2, y2) /\ x1+1 = x2 ==> fib(x2+1, y1+y2) - - -res(c1,c3) = fib(x2, y2) /\ 0+1 = x2 ==> fib(x2+1, 1+y2) - = fib(1, y) ==> fib(2, y+1) - -res(c2,c3) = fib(x1, y1) /\ x1+1 = 2 ==> fib(2+1, y1+3) - = fib(1, y) ==> fib(3, y+3) - -fib(2,4) -fib(2,4) - - -fib(2,4) -fib(2,7) - -trace : - - [c2, res(c1, c3)] = [f(1,1), fib(1, y) ==> fib(2, y+1)] = fib(2,2) +bool has_entry(const std::set& chcs) { + for (const auto& chc: chcs) { + // Not necessarily a fact, because RHS predicate is not required. + // For example `true ==> false` is an entry but not a fact. + if (chc.lhs.empty()) { + return true; + } + } + return false; +} - [c1, res(c2, c3)] = [f(0,1), fib(0, y) ==> fib(2, y+1)] = fib(2,2) +bool has_sink(const std::set& chcs) { + for (const auto& chc: chcs) { + if (!chc.rhs.has_value()) { + return true; + } + } + return false; +} -*/ +bool is_trivially_sat(const std::set& chc_problem) { + // TODO: could check more generally: if no sink is reachable from an entry: + return chc_problem.empty() || !has_entry(chc_problem) || !has_sink(chc_problem); +} void NonLinearSolver::analyze(const std::vector& initial_chcs) { const std::set chcs_preprocessed = presolve(normalize_all_preds(initial_chcs)); - // If preprocessing manages to eliminate all clauses we can already terminate with SAT. - if (chcs_preprocessed.size() == 0) { + if (is_trivially_sat(chcs_preprocessed)) { + // If preprocessing manages to eliminate all clauses or at least all facts/queries + // we can terminate with SAT. std::cout << "sat" << std::endl; return; + } else if (allLinear(chcs_preprocessed)) { + ITSPtr its = ITSProblem::fromClauses(chcs_preprocessed); + reachability::Reachability::analyze(*its); + return; } + auto [linear_chcs, non_linear_chcs] = partitionByDegree(chcs_preprocessed); + // Setup ITS and linear solver: ITSPtr its = ITSProblem::fromClauses(chcs_preprocessed); // TODO: why cant we give `linear_solver` the type `ILinearSolver` ? auto linear_solver = reachability::Reachability(*its, true); - auto [linear_chcs, non_linear_chcs] = partitionByDegree(chcs_preprocessed); - - // For the first loop iteration, the list of facts is composed of the original facts - // given in the CHC problem. In subsequent iterations, the facts are whatever - // the linear solver managed to derive. - std::set facts; - for (const auto &chc: linear_chcs) { - if (chc.isFact()) { - facts.insert(chc); - } - } - + // First only derive "easy" to handle rules with linear guard. If that's not enough + // also try rules with polynomial guard. Otherwise also consider exponential + // (arbitrary) guards. for (const auto max_constraint_tier: LinearSolver::constraint_tiers) { while (true) { - if (logging_active) { + if (dbg || Config::Analysis::log) { std::cout << "============= non-linear solver main loop =============" << std::endl; } - // Do resolution with all combinations of facts and non-linear clauses to - // get every possible linear clause that we can derive in this iteration. - const std::set resolvents = all_resolvents(non_linear_chcs, facts); - - // Add linear resolvents to linear solver and store non-linear resolvents for the next resolutions round: - const auto [linear_resolvents, non_linear_resolvents] = partitionByDegree(resolvents); - linear_solver.add_clauses(linear_resolvents); - non_linear_chcs.insert(non_linear_resolvents.begin(), non_linear_resolvents.end()); - - // Hand over to linear solver and let it derive new facts: - facts.clear(); + // Hand over to linear solver to derive new facts: const auto& derived_facts = linear_solver.derive_new_facts(max_constraint_tier); - for (const auto& [_, fact_group]: partitionByRHS(derived_facts)) { - const auto& merged_fact = merge_facts(fact_group); - facts.insert(merged_fact); - } - if (logging_active) { - std::cout << "total new facts : " << derived_facts.size() << std::endl; - std::cout << "new facts after merge : " << facts.size() << std::endl; - } // If the linear solver proved Unsat, we can terminate. Otherwise, we use the derived facts to in the next // resolution round to derive more linear clauses, that we can feed back into the linear solver. Unless @@ -104,9 +76,42 @@ void NonLinearSolver::analyze(const std::vector& initial_chcs) { const auto result = linear_solver.get_analysis_result(); if (result == LinearSolver::Result::Unsat) { break; - } else if ((result == LinearSolver::Result::Sat || result == LinearSolver::Result::Unknown) && facts.empty()) { + } else if ((result == LinearSolver::Result::Sat || result == LinearSolver::Result::Unknown) && derived_facts.empty()) { + break; + } + + // reduce number of facts by merging facts with the same RHS predicate: + std::set facts; + for (const auto& [_, fact_group]: partitionByRHS(derived_facts)) { + const auto& merged_fact = merge_facts(fact_group); + facts.insert(merged_fact); + } + if (dbg || Config::Analysis::log) { + std::cout + << "total new facts: " + << derived_facts.size() + << " / after merge: " + << facts.size() + << std::endl; + } + + // Do resolution with all combinations of facts and non-linear clauses to + // get every possible linear clause that we can derive in this iteration. + const auto resolvents = all_resolvents(non_linear_chcs, facts); + + // Add linear resolvents to linear solver and store non-linear resolvents for the next resolutions round: + const auto [linear_resolvents, non_linear_resolvents] = partitionByDegree(resolvents); + + // If there are no new linear resolvents the linear solver can't derive new facts + // in the next iteration, so we can exit here. This can happen if all linear resolvents + // had UNSAT constraints or the CHC problem contains only linear clauses (because all + // non-linear clauses got eliminated in the preprocessing step). + if (linear_resolvents.empty()) { break; } + + linear_solver.add_clauses(linear_resolvents); + non_linear_chcs.insert(non_linear_resolvents.begin(), non_linear_resolvents.end()); } if (linear_solver.get_analysis_result() == LinearSolver::Result::Unsat) { @@ -153,7 +158,7 @@ const std::set all_resolvents(const std::set& non_linear_chcs, c unsigned redundant_resolvent_count = 0; - if (logging_active) { + if (dbg || Config::Analysis::log) { std::cout << "resolving " << non_linear_chcs.size() @@ -162,7 +167,7 @@ const std::set all_resolvents(const std::set& non_linear_chcs, c } for (const auto& chc: non_linear_chcs) { - if (logging_active) { + if (dbg || Config::Analysis::log) { std::cout << "resolving non-linear CHC with " << chc.lhs.size() @@ -175,7 +180,7 @@ const std::set all_resolvents(const std::set& non_linear_chcs, c all_resolvents_aux(chc, 0, facts, resolvents, redundant_resolvent_count); } - if (logging_active) { + if (dbg || Config::Analysis::log) { std::cout << "computed " << resolvents.size() @@ -345,11 +350,11 @@ const std::vector normalize_all_preds(const std::vector& initial * unilaterally_resolvable_with(H, {c1,c2,c3}) == null * */ -const std::optional unilaterally_resolvable_with(const FunApp& pred, const std::set& chcs) { +const std::optional unilaterally_resolvable_with(const std::string& pred_name, const std::set& chcs) { auto candidate = chcs.end(); // Initialize to end iterator, indicating no candidate found for (auto it = chcs.begin(); it != chcs.end(); ++it) { - if (it->rhs.has_value() && it->rhs.value().name == pred.name) { + if (it->rhs.has_value() && it->rhs.value().name == pred_name) { if (candidate != chcs.end()) { // Predicate occurs on RHS of multiple clauses. return {}; @@ -505,30 +510,30 @@ const Clause merge_facts(const std::vector facts) { */ const std::set presolve(const std::vector& initial_chcs) { // first collect all RHS predicates of all CHCs - std::set todo_rhs_preds; + std::set todo_rhs_preds; for (const auto& chc: initial_chcs) { if (chc.rhs.has_value()) { - todo_rhs_preds.insert(chc.rhs.value()); + todo_rhs_preds.insert(chc.rhs.value().name); } } - std::set chcs(initial_chcs.begin(), initial_chcs.end()); + std::set result_chcs(initial_chcs.begin(), initial_chcs.end()); // iterate over each RHS predicate and ... while (!todo_rhs_preds.empty()) { - if (logging_active) { + if (dbg || Config::Analysis::log) { std::cout << "eliminating predicates: " << std::endl; } for (const auto& pred: todo_rhs_preds) { // ... check whether it occurs uniquely on the RHS of one CHC - const auto optional_uni_chc = unilaterally_resolvable_with(pred, chcs); + const auto optional_uni_chc = unilaterally_resolvable_with(pred, result_chcs); if (optional_uni_chc.has_value()) { const Clause& uni_chc = optional_uni_chc.value(); - chcs.erase(uni_chc); + result_chcs.erase(uni_chc); - if (logging_active) { - std::cout << " - " << pred.name << std::endl; + if (dbg || Config::Analysis::log) { + std::cout << " - " << pred << std::endl; } // An edge case to consider: if we have the clause set @@ -543,36 +548,36 @@ const std::set presolve(const std::vector& initial_chcs) { // We can't completely eliminate `F` because it also occurs on the LHS of `c1`. // In fact, any clause that has `F` on the its LHS is "unreachable" so we can remove // them all from the CHC problem. - if (uni_chc.indexOfLHSPred(pred.name).has_value()) { + if (uni_chc.indexOfLHSPred(pred).has_value()) { std::set chcs_without_pred; - for (const Clause& chc: chcs) { - if (!chc.indexOfLHSPred(pred.name).has_value()) { + for (const Clause& chc: result_chcs) { + if (!chc.indexOfLHSPred(pred).has_value()) { chcs_without_pred.insert(chc); } } - chcs = chcs_without_pred; + result_chcs = chcs_without_pred; } else { std::set resolvents; - for (const Clause& chc: chcs) { + for (const Clause& chc: result_chcs) { const Clause& resolvent = eliminate_pred(uni_chc, chc); resolvents.insert(resolvent); } - chcs = resolvents; + result_chcs = resolvents; } } } todo_rhs_preds.clear(); - if (logging_active) { + if (dbg || Config::Analysis::log) { std::cout << "remove resolvents with UNSAT constraint: " << std::endl; } // Checking resolvents for satisfiability in a separate loop here, // because it reduces the number of calls to the SMT solver. std::set chcs_with_sat_guard; - for (const auto& chc: chcs) { + for (const auto& chc: result_chcs) { if (SmtFactory::check(chc.guard) == Sat) { chcs_with_sat_guard.insert(chc); } else if (chc.rhs.has_value()) { @@ -586,7 +591,7 @@ const std::set presolve(const std::vector& initial_chcs) { // only other clause, which had `G` as its RHS predicate. Thus, whenever // we remove a resolvent, we add its RHS predicate into `rhs_preds` // to re-check it in the next while-loop iteration. - todo_rhs_preds.insert(chc.rhs.value()); + todo_rhs_preds.insert(chc.rhs.value().name); } } @@ -643,7 +648,7 @@ const std::set presolve(const std::vector& initial_chcs) { // (x=0 \/ x=1 \/ x=2) ==> G(x) // const auto& [ facts, rest_chcs ] = partitionFacts(chcs_with_sat_guard); - chcs = rest_chcs; + result_chcs = rest_chcs; for (const auto& [_, fact_group]: partitionByRHS(facts)) { const Clause& merged_fact = merge_facts(fact_group); @@ -653,16 +658,25 @@ const std::set presolve(const std::vector& initial_chcs) { // then facts got eliminated, so the resulting clause might now be unilaterally // resolvable and we add its RHS predicate back into `todo_rhs_preds`. if (fact_group.size() > 1) { - todo_rhs_preds.insert(merged_fact.rhs.value()); + todo_rhs_preds.insert(merged_fact.rhs.value().name); } - chcs.insert(merged_fact); + result_chcs.insert(merged_fact); } } - if (logging_active) { + if (dbg || Config::Analysis::log) { std::cout << std::endl; } - return chcs; + // Use all facts and non-linear clauses that are already known at this point and + // do one round of resolution with them to derive all possible linear clauses that + // we can get at this point to give the linear solver as much information as possible + // for the first iteration. + const auto& [ linear_chcs, non_linear_chcs ] = partitionByDegree(result_chcs); + const auto& [ facts, _ ] = partitionFacts(linear_chcs); + std::set resolvents = all_resolvents(non_linear_chcs, facts); + result_chcs.insert(resolvents.begin(), resolvents.end()); + + return result_chcs; } diff --git a/src/parser/chc/CHCParseVisitor.cpp b/src/parser/chc/CHCParseVisitor.cpp index c5b536504..f49818afd 100644 --- a/src/parser/chc/CHCParseVisitor.cpp +++ b/src/parser/chc/CHCParseVisitor.cpp @@ -123,7 +123,7 @@ antlrcpp::Any CHCParseVisitor::visitChc_assert_head(CHCParser::Chc_assert_headCo antlrcpp::Any CHCParseVisitor::visitChc_assert_body(CHCParser::Chc_assert_bodyContext *ctx) { const auto lhs = any_cast(visit(ctx->chc_tail())); const auto rhs = any_cast(visit(ctx->chc_head())); - return Clause(lhs.first, rhs, lhs.second); + return Clause(lhs.first, rhs, lhs.second).normalize(); } antlrcpp::Any CHCParseVisitor::visitChc_head(CHCParser::Chc_headContext *ctx) { @@ -159,7 +159,7 @@ antlrcpp::Any CHCParseVisitor::visitChc_query(CHCParser::Chc_queryContext *ctx) vars.clear(); // const auto sink_name = its->getLocationNames().at(its->getSink()); - return Clause(lhs.first, {}, lhs.second); + return Clause(lhs.first, {}, lhs.second).normalize(); } antlrcpp::Any CHCParseVisitor::visitVar_decl(CHCParser::Var_declContext *ctx) { From d4385a95d98a7019e77973ac3e19d176c87adc15 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Sat, 2 Mar 2024 14:08:00 +0100 Subject: [PATCH 23/25] non-linear: fixed unsat/sat conflict of chc comp 2023 instance 248 by commit: c7f7c40f627328a6f1ae22bd689b62f84aa0f694 --- benchmarks/comp23-LIA-nonlin.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/comp23-LIA-nonlin.txt b/benchmarks/comp23-LIA-nonlin.txt index f847e2494..8dddd9943 100644 --- a/benchmarks/comp23-LIA-nonlin.txt +++ b/benchmarks/comp23-LIA-nonlin.txt @@ -247,7 +247,7 @@ 245 sat timeout 246 sat timeout 247 unsat timeout -248 unsat sat +248 unsat unsat 249 unsat timeout 250 unsat timeout 251 unsat unsat From 7c815d838efa8b30e93d07c9bf4d759c4036f971 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Fri, 8 Mar 2024 19:30:32 +0100 Subject: [PATCH 24/25] non-linear: compute resolvents with incremental SMT Consider the CHC problem: x>0 ==> F(x) x<0 ==> G(y) F(x) /\ G(x) /\ H(x) ==> false We derive linear clauses by recursively resolving all facts with previously computed non-linear resolvents: F(x) /\ G(x) /\ H(x) ==> false | +-- G(x) /\ H(x) /\ x>0 ==> false | +-- H(x) /\ x>0 /\ x<0 ==> false We stop expanding this call tree when a resolvents constraint is UNSAT, because all following resolvents will also have UNSAT constraint. Previously, we queried the SMT solver each time we get a new resolvent. This is costly. However, when going deeper into the tree, we only *add* constraints, so we can leverage incremental SMT. --- benchmarks/LIA.txt | 6 +- benchmarks/comp23-LIA-nonlin.txt | 766 +++++++++++++++---------------- run_benchmark.sh | 42 +- src/nonlinear/clause.cpp | 32 +- src/nonlinear/clause.hpp | 2 +- src/nonlinear/nonlinear.cpp | 65 ++- src/nonlinear/nonlinear.hpp | 4 +- 7 files changed, 469 insertions(+), 448 deletions(-) diff --git a/benchmarks/LIA.txt b/benchmarks/LIA.txt index 0c10bb3ba..87608873f 100644 --- a/benchmarks/LIA.txt +++ b/benchmarks/LIA.txt @@ -75,7 +75,7 @@ 073 unsat unsat 074 sat timeout 075 unsat unsat -076 unsat unsat +076 unsat timeout 077 unsat unsat 078 sat timeout 079 sat timeout @@ -342,7 +342,7 @@ 340 sat timeout 341 sat timeout 342 unsat unsat -343 unsat unsat +343 unsat timeout 344 unsat timeout 345 unsat unsat 346 sat timeout @@ -441,7 +441,7 @@ 439 sat timeout 440 sat timeout 441 sat timeout -442 unsat timeout +442 unsat unsat 443 sat timeout 444 unsat unsat 445 sat timeout diff --git a/benchmarks/comp23-LIA-nonlin.txt b/benchmarks/comp23-LIA-nonlin.txt index 8dddd9943..53f36f138 100644 --- a/benchmarks/comp23-LIA-nonlin.txt +++ b/benchmarks/comp23-LIA-nonlin.txt @@ -1,427 +1,427 @@ ### Z3 ADCL -000 sat timeout -001 sat timeout -002 sat timeout -003 sat timeout -004 unsat timeout -005 sat timeout -006 sat timeout -007 sat timeout -008 sat timeout -009 sat timeout -010 sat timeout -011 sat timeout -012 timeout timeout -013 timeout timeout -014 timeout timeout -015 timeout timeout -016 timeout timeout -017 timeout timeout -018 sat timeout -019 timeout timeout -020 sat timeout -021 timeout timeout -022 timeout timeout -023 timeout timeout -024 timeout timeout -025 timeout timeout -026 timeout timeout -027 timeout timeout -028 timeout timeout -029 timeout timeout -030 timeout timeout -031 timeout timeout -032 timeout timeout -033 timeout timeout -034 timeout timeout -035 timeout timeout -036 timeout timeout -037 timeout timeout -038 timeout timeout -039 timeout timeout -040 timeout timeout -041 timeout timeout -042 timeout timeout -043 timeout timeout -044 timeout timeout -045 timeout timeout -046 timeout timeout -047 timeout timeout -048 unsat unsat -049 unsat unsat -050 sat timeout -051 sat timeout -052 unsat unsat -053 sat unknown -054 sat timeout -055 sat unknown -056 unsat unsat -057 unsat timeout -058 sat timeout -059 sat timeout -060 sat timeout -061 sat timeout -062 sat timeout -063 timeout timeout -064 sat timeout -065 timeout timeout -066 timeout timeout -067 timeout timeout -068 sat timeout +000 sat timeout +001 sat timeout +002 sat timeout +003 sat timeout +004 unsat timeout +005 sat timeout +006 sat timeout +007 sat timeout +008 sat timeout +009 sat timeout +010 sat timeout +011 sat timeout +012 timeout timeout +013 timeout timeout +014 timeout timeout +015 timeout timeout +016 timeout timeout +017 timeout timeout +018 sat timeout +019 timeout timeout +020 sat timeout +021 timeout timeout +022 timeout timeout +023 timeout timeout +024 timeout timeout +025 timeout timeout +026 timeout timeout +027 timeout timeout +028 timeout timeout +029 timeout timeout +030 timeout timeout +031 timeout timeout +032 timeout timeout +033 timeout timeout +034 timeout timeout +035 timeout timeout +036 timeout timeout +037 timeout timeout +038 timeout timeout +039 timeout timeout +040 timeout timeout +041 timeout timeout +042 timeout timeout +043 timeout timeout +044 timeout timeout +045 timeout timeout +046 timeout timeout +047 timeout timeout +048 unsat unsat +049 unsat unsat +050 sat timeout +051 sat timeout +052 unsat unsat +053 sat unknown +054 sat timeout +055 sat unknown +056 unsat unsat +057 unsat timeout +058 sat timeout +059 sat timeout +060 sat timeout +061 sat timeout +062 sat timeout +063 timeout timeout +064 sat timeout +065 timeout timeout +066 timeout timeout +067 timeout timeout +068 sat timeout 069 unsat unsat -070 unsat unsat -071 unsat unsat -072 unsat unsat -073 sat timeout -074 sat timeout -075 sat timeout -076 sat timeout -077 sat timeout -078 timeout timeout -079 sat timeout -080 sat unknown -081 timeout timeout -082 sat timeout -083 timeout timeout -084 timeout timeout -085 timeout timeout -086 timeout timeout -087 timeout timeout -088 sat timeout -089 timeout timeout -090 timeout timeout -091 timeout timeout -092 timeout timeout -093 timeout timeout -094 sat timeout -095 sat timeout -096 sat timeout -097 sat timeout -098 sat timeout -099 unsat unsat -100 sat timeout -101 unsat unsat -102 unsat unsat -103 unsat unsat -104 unsat unsat -105 sat timeout +070 unsat unsat +071 unsat unsat +072 unsat unsat +073 sat timeout +074 sat timeout +075 sat timeout +076 sat timeout +077 sat timeout +078 timeout timeout +079 sat timeout +080 sat unknown +081 timeout timeout +082 sat timeout +083 timeout timeout +084 timeout timeout +085 timeout timeout +086 timeout timeout +087 timeout timeout +088 sat timeout +089 timeout timeout +090 timeout timeout +091 timeout timeout +092 timeout timeout +093 timeout timeout +094 sat timeout +095 sat timeout +096 sat timeout +097 sat timeout +098 sat timeout +099 unsat unsat +100 sat timeout +101 unsat unsat +102 unsat timeout +103 unsat timeout +104 unsat unsat +105 sat timeout 106 unsat timeout -107 sat timeout -108 unsat timeout -109 unsat timeout -110 sat timeout +107 sat timeout +108 unsat unsat +109 unsat unsat +110 sat timeout 111 unsat timeout -112 sat timeout -113 sat timeout -114 timeout timeout -115 timeout timeout -116 sat timeout -117 unsat unsat -118 timeout timeout -119 unsat unsat -120 timeout timeout +112 sat timeout +113 sat timeout +114 timeout timeout +115 timeout timeout +116 sat timeout +117 unsat timeout +118 timeout timeout +119 unsat timeout +120 timeout timeout 121 unsat timeout -122 unsat unsat +122 unsat timeout 123 unsat timeout -124 unsat timeout -125 sat timeout -126 unsat unsat -127 sat timeout -128 sat timeout -129 unsat timeout -130 timeout timeout -131 sat timeout -132 timeout timeout -133 timeout timeout -134 timeout timeout -135 timeout timeout -136 timeout timeout -137 sat timeout -138 sat timeout -139 sat timeout -140 timeout timeout -141 timeout timeout -142 timeout timeout -143 sat timeout -144 timeout timeout -145 timeout timeout -146 sat timeout -147 timeout timeout -148 timeout timeout -149 timeout timeout -150 timeout timeout -151 sat timeout -152 sat timeout -153 sat timeout -154 sat timeout -155 timeout timeout -156 sat timeout -157 timeout timeout -158 unsat unsat -159 timeout timeout -160 sat timeout -161 sat timeout -162 sat timeout -163 timeout timeout -164 timeout timeout -165 timeout timeout -166 timeout timeout -167 timeout timeout -168 timeout timeout -169 sat timeout -170 sat timeout -171 unsat unsat -172 sat sat -173 sat sat -174 sat timeout -175 timeout timeout -176 sat timeout -177 sat timeout -178 timeout timeout -179 timeout timeout -180 timeout timeout -181 sat timeout -182 timeout timeout -183 timeout timeout -184 timeout timeout +124 unsat timeout +125 sat timeout +126 unsat unsat +127 sat timeout +128 sat timeout +129 unsat timeout +130 timeout timeout +131 sat timeout +132 timeout timeout +133 timeout timeout +134 timeout timeout +135 timeout timeout +136 timeout timeout +137 sat timeout +138 sat timeout +139 sat timeout +140 timeout timeout +141 timeout timeout +142 timeout timeout +143 sat timeout +144 timeout timeout +145 timeout timeout +146 sat timeout +147 timeout timeout +148 timeout timeout +149 timeout timeout +150 timeout timeout +151 sat timeout +152 sat timeout +153 sat timeout +154 sat timeout +155 timeout timeout +156 sat timeout +157 timeout timeout +158 unsat unsat +159 timeout timeout +160 sat timeout +161 sat timeout +162 sat timeout +163 timeout timeout +164 timeout timeout +165 timeout timeout +166 timeout timeout +167 timeout timeout +168 timeout timeout +169 sat timeout +170 sat timeout +171 unsat unsat +172 sat sat +173 sat sat +174 sat timeout +175 timeout timeout +176 sat timeout +177 sat timeout +178 timeout timeout +179 timeout timeout +180 timeout timeout +181 sat timeout +182 timeout timeout +183 timeout timeout +184 timeout timeout 185 sat sat -186 sat unknown -187 sat timeout -188 unsat timeout -189 unsat timeout -190 unsat unsat -191 unsat timeout +186 sat unknown +187 sat timeout +188 unsat timeout +189 unsat unsat +190 unsat unsat +191 unsat timeout 192 unsat unsat -193 unsat unsat -194 unsat unsat -195 unsat unsat -196 unsat timeout +193 unsat unsat +194 unsat unsat +195 unsat unsat +196 unsat timeout 197 sat sat -198 sat unknown -199 unsat unsat +198 sat unknown +199 unsat unsat 200 sat sat -201 unsat unsat +201 unsat unsat 202 unsat unsat -203 unsat unsat +203 unsat unsat 204 unsat unsat 205 unsat unsat 206 unsat unsat 207 unsat unsat -208 sat unknown +208 sat unknown 209 sat sat 210 sat sat 211 sat sat 212 sat sat -213 unsat unsat +213 unsat unsat 214 unsat timeout -215 unsat timeout +215 unsat timeout 216 sat sat -217 unsat timeout -218 unsat unsat +217 unsat timeout +218 unsat unsat 219 sat sat 220 sat sat -221 sat timeout -222 sat timeout -223 sat timeout -224 unsat timeout +221 sat timeout +222 sat timeout +223 sat timeout +224 unsat timeout 225 sat unknown -226 sat timeout -227 sat unknown -228 sat unknown +226 sat timeout +227 sat unknown +228 sat unknown 229 unsat unsat -230 sat timeout -231 unsat timeout -232 sat timeout -233 sat timeout -234 unsat timeout +230 sat timeout +231 unsat timeout +232 sat timeout +233 sat timeout +234 unsat timeout 235 unsat unsat -236 unsat timeout +236 unsat timeout 237 unsat unsat 238 unsat unsat -239 sat timeout -240 sat timeout -241 sat timeout -242 sat timeout -243 sat unknown -244 sat timeout -245 sat timeout -246 sat timeout -247 unsat timeout +239 sat timeout +240 sat timeout +241 sat timeout +242 sat timeout +243 sat unknown +244 sat timeout +245 sat timeout +246 sat timeout +247 unsat timeout 248 unsat unsat -249 unsat timeout -250 unsat timeout +249 unsat timeout +250 unsat timeout 251 unsat unsat -252 sat sat -253 unsat timeout -254 unsat timeout +252 sat sat +253 unsat unsat +254 unsat timeout 255 unsat unsat -256 timeout timeout -257 sat unknown -258 sat unknown -259 timeout timeout -260 unsat timeout -261 unsat timeout -262 unsat timeout -263 timeout timeout -264 unsat timeout -265 timeout timeout -266 timeout timeout -267 timeout timeout -268 unsat timeout -269 unsat timeout -270 timeout timeout -271 timeout timeout -272 timeout timeout -273 sat timeout -274 unsat timeout -275 unsat timeout -276 timeout timeout -277 unsat timeout -278 timeout timeout -279 unsat timeout -280 timeout timeout -281 sat timeout -282 unsat timeout -283 unsat timeout -284 timeout timeout -285 timeout timeout -286 timeout timeout -287 timeout timeout -288 unsat timeout -289 sat unknown -290 sat timeout -291 timeout timeout -292 timeout timeout -293 timeout timeout -294 timeout timeout -295 timeout timeout -296 timeout timeout -297 timeout timeout -298 timeout timeout -299 timeout timeout -300 unsat timeout -301 timeout timeout -302 timeout timeout -303 timeout timeout -304 timeout timeout -305 unsat timeout -306 unsat timeout -307 timeout timeout -308 unsat timeout -309 timeout timeout -310 timeout timeout -311 unsat timeout -312 sat timeout -313 timeout timeout -314 timeout timeout -315 unsat timeout -316 timeout timeout -317 unsat timeout -318 unsat timeout -319 unsat timeout -320 timeout timeout -321 sat timeout -322 unsat timeout -323 unsat timeout -324 unsat timeout -325 unsat timeout -326 unsat timeout -327 sat unknown -328 timeout timeout -329 timeout timeout -330 unsat timeout -331 timeout timeout -332 timeout timeout -333 unsat timeout -334 timeout timeout -335 timeout timeout -336 unsat timeout -337 timeout timeout -338 unsat timeout -339 sat timeout -340 timeout timeout -341 timeout timeout -342 timeout timeout -343 sat timeout -344 timeout timeout -345 timeout timeout -346 unsat timeout -347 timeout timeout -348 timeout timeout -349 timeout timeout -350 timeout timeout -351 unsat timeout -352 unsat timeout -353 unsat timeout -354 unsat timeout -355 unsat timeout -356 timeout timeout -357 sat timeout -358 timeout timeout -359 unsat timeout -360 unsat timeout -361 timeout timeout -362 unsat timeout -363 unsat timeout -364 unsat timeout -365 sat unknown -366 sat unknown -367 unsat timeout -368 unsat unsat -369 sat unknown -370 sat unknown -371 unsat unsat -372 sat unknown -373 sat unknown -374 unsat timeout -375 sat unknown -376 timeout timeout -377 sat unknown -378 sat unknown -379 unsat timeout +256 timeout timeout +257 sat unknown +258 sat unknown +259 timeout timeout +260 unsat timeout +261 unsat timeout +262 unsat timeout +263 timeout timeout +264 unsat timeout +265 timeout timeout +266 timeout timeout +267 timeout timeout +268 unsat timeout +269 unsat timeout +270 timeout timeout +271 timeout timeout +272 timeout timeout +273 sat timeout +274 unsat timeout +275 unsat timeout +276 timeout timeout +277 unsat timeout +278 timeout timeout +279 unsat timeout +280 timeout timeout +281 sat timeout +282 unsat timeout +283 unsat timeout +284 timeout timeout +285 timeout timeout +286 timeout timeout +287 timeout timeout +288 unsat timeout +289 sat unknown +290 sat timeout +291 timeout timeout +292 timeout timeout +293 timeout timeout +294 timeout timeout +295 timeout timeout +296 timeout timeout +297 timeout timeout +298 timeout timeout +299 timeout timeout +300 unsat timeout +301 timeout timeout +302 timeout timeout +303 timeout timeout +304 timeout timeout +305 unsat timeout +306 unsat timeout +307 timeout timeout +308 unsat timeout +309 timeout timeout +310 timeout timeout +311 unsat timeout +312 sat timeout +313 timeout timeout +314 timeout timeout +315 unsat timeout +316 timeout timeout +317 unsat timeout +318 unsat timeout +319 unsat timeout +320 timeout timeout +321 sat timeout +322 unsat timeout +323 unsat timeout +324 unsat timeout +325 unsat timeout +326 unsat timeout +327 sat unknown +328 timeout timeout +329 timeout timeout +330 unsat timeout +331 timeout timeout +332 timeout timeout +333 unsat timeout +334 timeout timeout +335 timeout timeout +336 unsat timeout +337 timeout timeout +338 unsat timeout +339 sat timeout +340 timeout timeout +341 timeout timeout +342 timeout timeout +343 sat timeout +344 timeout timeout +345 timeout timeout +346 unsat timeout +347 timeout timeout +348 timeout timeout +349 timeout timeout +350 timeout timeout +351 unsat timeout +352 unsat timeout +353 unsat timeout +354 unsat timeout +355 unsat timeout +356 timeout timeout +357 sat timeout +358 timeout timeout +359 unsat timeout +360 unsat timeout +361 timeout timeout +362 unsat timeout +363 unsat timeout +364 unsat timeout +365 sat unknown +366 sat unknown +367 unsat timeout +368 unsat unsat +369 sat unknown +370 sat unknown +371 unsat unsat +372 sat unknown +373 sat unknown +374 unsat timeout +375 sat unknown +376 timeout timeout +377 sat unknown +378 sat unknown +379 unsat timeout 380 sat sat -381 unsat timeout -382 sat unknown +381 unsat unsat +382 sat unknown 383 unsat unsat 384 sat sat 385 sat sat 386 sat sat 387 sat sat -388 sat sat -389 sat unknown +388 sat sat +389 sat unknown 390 sat sat 391 unsat unsat 392 unsat unsat 393 sat sat 394 sat sat 395 sat sat -396 sat timeout -397 sat timeout -398 sat timeout -399 sat timeout -400 sat timeout -401 sat unknown +396 sat timeout +397 sat timeout +398 sat timeout +399 sat timeout +400 sat timeout +401 sat unknown 402 sat sat -403 sat timeout -404 sat unknown -405 sat timeout -406 sat timeout -407 unsat timeout -408 sat sat -409 sat sat -410 sat unknown -411 sat timeout -412 sat unknown -413 sat unknown -414 sat timeout -415 sat timeout -416 timeout timeout -417 timeout timeout -418 sat unknown -419 timeout timeout -420 sat unknown -421 sat timeout -422 timeout timeout -423 timeout timeout -424 timeout timeout -425 timeout timeout +403 sat timeout +404 sat unknown +405 sat timeout +406 sat timeout +407 unsat timeout +408 sat sat +409 sat sat +410 sat unknown +411 sat timeout +412 sat unknown +413 sat unknown +414 sat timeout +415 sat timeout +416 timeout timeout +417 timeout timeout +418 sat unknown +419 timeout timeout +420 sat unknown +421 sat timeout +422 timeout timeout +423 timeout timeout +424 timeout timeout +425 timeout timeout diff --git a/run_benchmark.sh b/run_benchmark.sh index 5d2b3f837..9bdec3bb4 100755 --- a/run_benchmark.sh +++ b/run_benchmark.sh @@ -11,24 +11,18 @@ pushd build make -j4 popd -# 103 unsat sat -# 247 unsat sat -# 253 unsat sat -# 256 unsat sat -# 259 unsat sat - -# gdb --args \ -./build/loat-static \ - --mode reachability \ - --format horn \ - --proof-level 0 \ - "../chc-comp23-benchmarks/LIA-nonlin/chc-LIA_248.smt2" - # "../chc-comp22-benchmarks/LIA/chc-LIA_235.smt2" - # "../chc-comp22-benchmarks/LIA/chc-LIA_148_unknown.smt2" - # "../chc-comp22-benchmarks/LIA/test3.smt2" - -popd -exit +# # gdb --args \ +# time ./build/loat-static \ +# --mode reachability \ +# --format horn \ +# --proof-level 0 \ +# "../chc-comp22-benchmarks/LIA/chc-LIA_076.smt2" +# # "../chc-comp23-benchmarks/LIA-nonlin/chc-LIA_075.smt2" +# # "../chc-comp22-benchmarks/LIA/chc-LIA_148_unknown.smt2" +# # "../chc-comp22-benchmarks/LIA/test3.smt2" + +# popd +# exit ########################################################################## @@ -43,14 +37,14 @@ do continue else read idx z3_result adcl_result <<< "$line" - # file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" - file="../chc-comp23-benchmarks/${benchmark}-nonlin/chc-${benchmark}_${idx}.smt2" + file="../chc-comp22-benchmarks/${benchmark}/chc-${benchmark}_${idx}.smt2" + # file="../chc-comp23-benchmarks/${benchmark}-nonlin/chc-${benchmark}_${idx}.smt2" - if true; then + # if true; then # if [[ "$z3_result" == "unsat" ]] && [[ "$adcl_result" == "timeout" ]]; then # if [[ "$z3_result" != "sat" ]] && [[ "$z3_result" != "timeout" ]]; then # if [[ "$z3_result" != "timeout" ]]; then - # if [[ "$z3_result" == "sat" ]]; then + if [[ "$z3_result" == "unsat" ]]; then # if [[ "$adcl_result" == "unsat" ]]; then set +e result=$(timeout 20 ./build/loat-static --mode reachability --format horn --proof-level 0 "$file") @@ -76,8 +70,8 @@ do fi fi -done < "./benchmarks/review23.txt" -# done < "./benchmarks/${benchmark}.txt" +# done < "./benchmarks/review23.txt" +done < "./benchmarks/${benchmark}.txt" # done < "./benchmarks/comp23-${benchmark}-nonlin.txt" # "undo" pushd diff --git a/src/nonlinear/clause.cpp b/src/nonlinear/clause.cpp index d1e227eea..c83d916fe 100644 --- a/src/nonlinear/clause.cpp +++ b/src/nonlinear/clause.cpp @@ -1,6 +1,7 @@ #include "clause.hpp" #include "boolexpr.hpp" #include "expr.hpp" +#include "smtfactory.hpp" #include "theory.hpp" #include #include @@ -164,6 +165,11 @@ const Clause Clause::renameWith(const Subs &renaming) const { lhs_renamed.push_back(pred.renameWith(renaming)); } + // TODO: bottleneck applying large subsitution with 20_000 vars + if (renaming.size() > 15000) { + std::cout << "subs size: " << renaming.size() << std::endl; + std::cout << guard << std::endl; + } const auto guard_renamed = guard->subs(renaming); if (rhs.has_value()) { @@ -219,7 +225,7 @@ const std::vector deletePredAt(const std::vector& preds, unsigne * G(y) /\ F(z) /\ y < z ==> H(y,z) * */ -const std::optional Clause::resolutionWith(const Clause &chc, unsigned pred_index) const { +const std::optional> Clause::resolutionWith(const Clause &chc, unsigned pred_index) const { if (pred_index >= chc.lhs.size()) { throw std::logic_error("Clause::resolutionWith: `pred_index` out-of-bounds"); } @@ -253,23 +259,20 @@ const std::optional Clause::resolutionWith(const Clause &chc, unsigned p } const Clause this_unified = this_with_disjoint_vars.renameWith(unifier.value()); - const Clause chc_unified = Clause(deletePredAt(chc.lhs, pred_index), chc.rhs, chc.guard) - .renameWith(unifier.value()); // LHS of resolvent is the union of the renamed LHS of `this` ... std::vector resolvent_lhs = this_unified.lhs; // ... and the LHS of `chc` where `pred` is removed. - for (const auto& pred: chc_unified.lhs) { - resolvent_lhs.push_back(pred); + for (unsigned i=0; isimplify() - ).normalize(); + return std::make_tuple(resolvent, this_unified.guard); } /** @@ -296,11 +299,8 @@ const std::tuple, std::set> partitionByDegree(const std * values are the sets of clauses with that RHS predicate. The key is optional because clauses may have no * RHS predicate (i.e. querys). */ -const std::map>, std::vector> partitionByRHS(const std::set& chcs) { - std::map< - std::optional>, - std::vector - > partition; +const std::map, std::vector> partitionByRHS(const std::set& chcs) { + std::map, std::vector> partition; for (const auto& chc: chcs) { std::optional> rhs_name; diff --git a/src/nonlinear/clause.hpp b/src/nonlinear/clause.hpp index 9f482b664..3617508f2 100644 --- a/src/nonlinear/clause.hpp +++ b/src/nonlinear/clause.hpp @@ -50,7 +50,7 @@ class Clause { const Clause renameWith(const Subs &renaming) const; - const std::optional resolutionWith(const Clause &chc, unsigned pred_index) const; + const std::optional> resolutionWith(const Clause &chc, unsigned pred_index) const; const Clause normalize() const; diff --git a/src/nonlinear/nonlinear.cpp b/src/nonlinear/nonlinear.cpp index 5322925e5..54c79d1e1 100644 --- a/src/nonlinear/nonlinear.cpp +++ b/src/nonlinear/nonlinear.cpp @@ -166,6 +166,8 @@ const std::set all_resolvents(const std::set& non_linear_chcs, c << std::endl; } + std::unique_ptr> solver {SmtFactory::solver()}; + for (const auto& chc: non_linear_chcs) { if (dbg || Config::Analysis::log) { std::cout @@ -175,9 +177,14 @@ const std::set all_resolvents(const std::set& non_linear_chcs, c << facts.size() << " facts" << std::endl; + + printSimple(std::cout << "NL: ", chc) << std::endl; } - all_resolvents_aux(chc, 0, facts, resolvents, redundant_resolvent_count); + solver->push(); + solver->add(chc.guard); + all_resolvents_aux(chc, 0, facts, resolvents, redundant_resolvent_count, *solver); + solver->pop(); } if (dbg || Config::Analysis::log) { @@ -201,7 +208,14 @@ const std::set all_resolvents(const std::set& non_linear_chcs, c return resolvents; } -void all_resolvents_aux(const Clause& chc, unsigned pred_index, const std::set& facts, std::set& resolvents, unsigned& redundant_resolvent_count) { +void all_resolvents_aux( + const Clause& chc, + unsigned pred_index, + const std::set& facts, + std::set& resolvents, + unsigned& redundant_resolvent_count, + Smt& solver +) { // === Running example === // `chc` : F(x) /\ F(y) /\ F(z) ==> G(x,y,z) // `pred_index` : ^---- index on first LHS predicate of `chc` @@ -212,10 +226,12 @@ void all_resolvents_aux(const Clause& chc, unsigned pred_index, const std::set G(1,y,z) // // for fact = F(2) : F(y) /\ F(z) ==> G(2,y,z) @@ -231,25 +247,33 @@ void all_resolvents_aux(const Clause& chc, unsigned pred_index, const std::set G(1,y,1) - // F(y) ==> G(1,y,2) - // F(z) ==> G(1,1,z) - // F(z) ==> G(1,2,z) - // - // for fact = F(2) : F(y) ==> G(2,y,1) - // F(y) ==> G(2,y,2) - // F(z) ==> G(2,1,z) - // F(z) ==> G(2,2,z) - } - } + } else { + solver.push(); + solver.add(extra_constraints); + + // if (SmtFactory::check(resolvent.guard) == Sat) { + if (solver.check() == Sat) { + resolvents.insert(resolvent); + + all_resolvents_aux(resolvent, pred_index, facts, resolvents, redundant_resolvent_count, solver); + // for fact = F(1) : F(y) ==> G(1,y,1) + // F(y) ==> G(1,y,2) + // F(z) ==> G(1,1,z) + // F(z) ==> G(1,2,z) + // + // for fact = F(2) : F(y) ==> G(2,y,1) + // F(y) ==> G(2,y,2) + // F(z) ==> G(2,1,z) + // F(z) ==> G(2,2,z) + } + + solver.pop(); + } + } } // All resolvents that DONT eliminate F(x) - all_resolvents_aux(chc, pred_index+1, facts, resolvents, redundant_resolvent_count); + all_resolvents_aux(chc, pred_index+1, facts, resolvents, redundant_resolvent_count, solver); // F(x) /\ F(y) ==> G(x,y,1) // F(x) /\ F(y) ==> G(x,y,2) // @@ -390,7 +414,8 @@ const Clause eliminate_pred(const Clause& left, const Clause& right) { const auto& right_pred_index = right.indexOfLHSPred(left_pred.name); if (right_pred_index.has_value()) { - return eliminate_pred(left, left.resolutionWith(right, right_pred_index.value()).value()); + const auto [resolvent, _] = left.resolutionWith(right, right_pred_index.value()).value(); + return eliminate_pred(left, resolvent); } else { return right; } diff --git a/src/nonlinear/nonlinear.hpp b/src/nonlinear/nonlinear.hpp index a549cdbed..4b70306e0 100644 --- a/src/nonlinear/nonlinear.hpp +++ b/src/nonlinear/nonlinear.hpp @@ -1,5 +1,6 @@ #pragma once #include "linearsolver.hpp" +#include "smt.hpp" class NonLinearSolver { @@ -20,7 +21,8 @@ void all_resolvents_aux( unsigned pred_index, const std::set& facts, std::set& resolvents, - unsigned& redundant_resolvent_count + unsigned& redundant_resolvent_count, + Smt& solver ); const Clause merge_facts(const std::vector facts); From 9a39015e585cae2816ca82903d360274fced98a5 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Sat, 9 Mar 2024 00:22:11 +0100 Subject: [PATCH 25/25] non-linear: correct Z3 benchmark results Can't reproduce many UNSAT anymore that Z3 gave on CHC comp 23. Did we use a different version? Now tested with: Z3 version 4.12.5 - 64 bit --- benchmarks/comp23-LIA-nonlin.txt | 70 ++++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/benchmarks/comp23-LIA-nonlin.txt b/benchmarks/comp23-LIA-nonlin.txt index 53f36f138..4b1b5fadd 100644 --- a/benchmarks/comp23-LIA-nonlin.txt +++ b/benchmarks/comp23-LIA-nonlin.txt @@ -116,13 +116,13 @@ 114 timeout timeout 115 timeout timeout 116 sat timeout -117 unsat timeout +117 timeout timeout 118 timeout timeout 119 unsat timeout 120 timeout timeout 121 unsat timeout -122 unsat timeout -123 unsat timeout +122 timeout timeout +123 timeout timeout 124 unsat timeout 125 sat timeout 126 unsat unsat @@ -214,7 +214,7 @@ 212 sat sat 213 unsat unsat 214 unsat timeout -215 unsat timeout +215 timeout timeout 216 sat sat 217 unsat timeout 218 unsat unsat @@ -259,35 +259,35 @@ 257 sat unknown 258 sat unknown 259 timeout timeout -260 unsat timeout +260 timeout timeout 261 unsat timeout -262 unsat timeout +262 timeout timeout 263 timeout timeout -264 unsat timeout +264 timeout timeout 265 timeout timeout 266 timeout timeout 267 timeout timeout -268 unsat timeout +268 timeout timeout 269 unsat timeout 270 timeout timeout 271 timeout timeout 272 timeout timeout 273 sat timeout 274 unsat timeout -275 unsat timeout +275 timeout timeout 276 timeout timeout -277 unsat timeout +277 timeout timeout 278 timeout timeout -279 unsat timeout +279 timeout timeout 280 timeout timeout 281 sat timeout -282 unsat timeout +282 timeout timeout 283 unsat timeout 284 timeout timeout 285 timeout timeout 286 timeout timeout 287 timeout timeout -288 unsat timeout +288 timeout timeout 289 sat unknown 290 sat timeout 291 timeout timeout @@ -304,35 +304,35 @@ 302 timeout timeout 303 timeout timeout 304 timeout timeout -305 unsat timeout -306 unsat timeout +305 timeout timeout +306 timeout timeout 307 timeout timeout -308 unsat timeout +308 timeout timeout 309 timeout timeout 310 timeout timeout -311 unsat timeout +311 timeout timeout 312 sat timeout 313 timeout timeout 314 timeout timeout 315 unsat timeout 316 timeout timeout -317 unsat timeout +317 timeout timeout 318 unsat timeout -319 unsat timeout +319 timeout timeout 320 timeout timeout 321 sat timeout -322 unsat timeout -323 unsat timeout -324 unsat timeout -325 unsat timeout -326 unsat timeout +322 timeout timeout +323 timeout timeout +324 timeout timeout +325 timeout timeout +326 timeout timeout 327 sat unknown 328 timeout timeout 329 timeout timeout -330 unsat timeout +330 timeout timeout 331 timeout timeout 332 timeout timeout -333 unsat timeout +333 timeout timeout 334 timeout timeout 335 timeout timeout 336 unsat timeout @@ -350,20 +350,20 @@ 348 timeout timeout 349 timeout timeout 350 timeout timeout -351 unsat timeout -352 unsat timeout -353 unsat timeout -354 unsat timeout -355 unsat timeout +351 timeout timeout +352 timeout timeout +353 timeout timeout +354 timeout timeout +355 timeout timeout 356 timeout timeout 357 sat timeout 358 timeout timeout -359 unsat timeout -360 unsat timeout +359 timeout timeout +360 timeout timeout 361 timeout timeout 362 unsat timeout -363 unsat timeout -364 unsat timeout +363 timeout timeout +364 timeout timeout 365 sat unknown 366 sat unknown 367 unsat timeout