From b50dffdf708d6ff53786d16fbc8a083f8c966f8c Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 24 Jun 2024 13:16:55 +0200 Subject: [PATCH 01/24] Added Xcfa2CHC transformation --- .gitignore | 1 + settings.gradle.kts | 1 + .../bme/mit/theta/core/utils/ExprUtils.java | 140 +++++ subprojects/xcfa/xcfa-cli/build.gradle.kts | 1 + .../bme/mit/theta/xcfa/cli/ExecuteConfig.kt | 8 + .../mit/theta/xcfa/cli/params/XcfaConfig.kt | 6 + subprojects/xcfa/xcfa2chc/build.gradle.kts | 25 + .../hu/bme/mit/theta/xcfa2chc/ChcUtils.kt | 122 ++++ .../hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt | 71 +++ .../hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt | 537 ++++++++++++++++++ 10 files changed, 912 insertions(+) create mode 100644 subprojects/xcfa/xcfa2chc/build.gradle.kts create mode 100644 subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/ChcUtils.kt create mode 100644 subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt create mode 100644 subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt diff --git a/.gitignore b/.gitignore index 6a151e2b52..7fa1e2dabb 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ xcfa.c xcfa.dot xcfa.json *.plantuml +xcfa-*.smt2 diff --git a/settings.gradle.kts b/settings.gradle.kts index 91b45a0f0e..5abd19937b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -39,6 +39,7 @@ include( "xcfa/c2xcfa", "xcfa/litmus2xcfa", "xcfa/llvm2xcfa", + "xcfa/xcfa2chc", "xcfa/xcfa-analysis", "xcfa/xcfa-cli", diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java index 940719a148..bce86a7db8 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java @@ -18,6 +18,7 @@ import hu.bme.mit.theta.common.container.Containers; import hu.bme.mit.theta.core.decl.ConstDecl; import hu.bme.mit.theta.core.decl.Decl; +import hu.bme.mit.theta.core.decl.IndexedConstDecl; import hu.bme.mit.theta.core.decl.ParamDecl; import hu.bme.mit.theta.core.decl.VarDecl; import hu.bme.mit.theta.core.model.ImmutableValuation; @@ -27,6 +28,8 @@ import hu.bme.mit.theta.core.type.anytype.RefExpr; import hu.bme.mit.theta.core.type.booltype.AndExpr; import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.type.booltype.ExistsExpr; +import hu.bme.mit.theta.core.type.booltype.ForallExpr; import hu.bme.mit.theta.core.type.booltype.NotExpr; import hu.bme.mit.theta.core.utils.IndexedVars.Builder; import hu.bme.mit.theta.core.utils.indexings.VarIndexing; @@ -35,12 +38,15 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; +import static hu.bme.mit.theta.core.utils.TypeUtils.cast; /** * Utility functions related to expressions. @@ -112,6 +118,69 @@ public static Collection> getConjuncts(final Expr expr) } } + /** + * Collect params of an expression into a given collection. + * + * @param expr Expression + * @param collectTo Collection where the params should be put + */ + public static void collectParams(final Expr expr, final Collection> collectTo) { + if (expr instanceof RefExpr refExpr) { + final Decl decl = refExpr.getDecl(); + if (decl instanceof ParamDecl param) { + collectTo.add(param); + return; + } + } + + if (expr instanceof ForallExpr forall) { + Set> params = new LinkedHashSet<>(getParams(forall.getOp())); + forall.getParamDecls().forEach(params::remove); + collectTo.addAll(params); + } else if (expr instanceof ExistsExpr exists) { + Set> params = new LinkedHashSet<>(getParams(exists.getOp())); + exists.getParamDecls().forEach(params::remove); + collectTo.addAll(params); + } else { + expr.getOps().forEach(op -> collectParams(op, collectTo)); + } + } + + /** + * Collect params from expressions into a given collection. + * + * @param exprs Expressions + * @param collectTo Collection where the variables should be put + */ + public static void collectParams(final Iterable> exprs, final Collection> collectTo) { + exprs.forEach(e -> collectParams(e, collectTo)); + } + + /** + * Get variables of an expression. + * + * @param expr Expression + * @return Set of variables appearing in the expression + */ + public static Set> getParams(final Expr expr) { + final Set> vars = Containers.createSet(); + collectParams(expr, vars); + return vars; + } + + /** + * Get variables of expressions. + * + * @param exprs Expressions + * @return Set of variables appearing in the expressions + */ + public static Set> getParams(final Iterable> exprs) { + final Set> vars = Containers.createSet(); + collectParams(exprs, vars); + return vars; + } + + /** * Collect variables of an expression into a given collection. * @@ -165,6 +234,59 @@ public static Set> getVars(final Iterable> exprs) { return vars; } + /** + * Collect indexed constants of an expression into a given collection. + * + * @param expr Expression + * @param collectTo Collection where the constants should be put + */ + public static void collectIndexedConstants(final Expr expr, final Collection> collectTo) { + if (expr instanceof RefExpr) { + final RefExpr refExpr = (RefExpr) expr; + final Decl decl = refExpr.getDecl(); + if (decl instanceof IndexedConstDecl) { + final IndexedConstDecl constDecl = (IndexedConstDecl) decl; + collectTo.add(constDecl); + return; + } + } + expr.getOps().forEach(op -> collectIndexedConstants(op, collectTo)); + } + + /** + * Collect indexed constants from expressions into a given collection. + * + * @param exprs Expressions + * @param collectTo Collection where the constants should be put + */ + public static void collectIndexedConstants(final Iterable> exprs, final Collection> collectTo) { + exprs.forEach(e -> collectIndexedConstants(e, collectTo)); + } + + /** + * Get indexed constants of an expression. + * + * @param expr Expression + * @return Set of constants appearing in the expression + */ + public static Set> getIndexedConstants(final Expr expr) { + final Set> consts = new HashSet<>(); + collectIndexedConstants(expr, consts); + return consts; + } + + /** + * Get indexed constants of expressions. + * + * @param exprs Expressions + * @return Set of constants appearing in the expressions + */ + public static Set> getIndexedConstants(final Iterable> exprs) { + final Set> consts = new HashSet<>(); + collectIndexedConstants(exprs, consts); + return consts; + } + /** * Collect constants of an expression into a given collection. * @@ -363,4 +485,22 @@ public static int nodeCountSize(final Expr expr) { return 1 + expr.getOps().stream().map(ExprUtils::nodeCountSize).reduce(0, (x, y) -> x + y); } + /** + * Change fixed subexpressions using a lookup + * + * @param expr the expr to change subexpressions in + * @param lookup the lookup mapping subexpression to replacements + * @return the changed expression + */ + public static Expr changeSubexpr(Expr expr, Map, Expr> lookup) { + if (lookup.containsKey(expr)) { + return cast(lookup.get(expr), expr.getType()); + } else { + return expr.map(e -> changeSubexpr(e, lookup)); + } + } + + public static Expr changeDecls(Expr expr, Map, ? extends Decl> lookup) { + return changeSubexpr(expr, lookup.entrySet().stream().map(entry -> Map.entry(entry.getKey().getRef(), entry.getValue().getRef())).collect(Collectors.toMap(Entry::getKey, Entry::getValue))); + } } diff --git a/subprojects/xcfa/xcfa-cli/build.gradle.kts b/subprojects/xcfa/xcfa-cli/build.gradle.kts index 5e1c150d4c..dc92ba6f04 100644 --- a/subprojects/xcfa/xcfa-cli/build.gradle.kts +++ b/subprojects/xcfa/xcfa-cli/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { implementation(project(":theta-analysis")) implementation(project(":theta-xcfa")) implementation(project(":theta-xcfa-analysis")) + implementation(project(":theta-xcfa2chc")) implementation(project(":theta-c2xcfa")) implementation(project(":theta-solver-z3")) implementation(project(":theta-solver-z3-legacy")) diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt index 46ad6180f8..61794654c7 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt @@ -59,6 +59,7 @@ import hu.bme.mit.theta.xcfa.passes.LbePass import hu.bme.mit.theta.xcfa.passes.LoopUnrollPass import hu.bme.mit.theta.xcfa.passes.StaticCoiPass import hu.bme.mit.theta.xcfa.toC +import hu.bme.mit.theta.xcfa2chc.toSMT2CHC import java.io.File import java.util.concurrent.TimeUnit import kotlin.random.Random @@ -245,6 +246,13 @@ private fun preVerificationLogging( "Writing pre-verification artifacts to directory ${resultFolder.absolutePath}\n" ) + if (!config.outputConfig.chcOutputConfig.disable) { + xcfa.procedures.forEach { + val chcFile = File(resultFolder, "xcfa-${it.name}.smt2") + chcFile.writeText(it.toSMT2CHC()) + } + } + if (!config.outputConfig.xcfaOutputConfig.disable) { val xcfaDotFile = File(resultFolder, "xcfa.dot") xcfaDotFile.writeText(xcfa.toDot()) diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt index ff6d663986..5c5af6f576 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt @@ -330,6 +330,7 @@ data class OutputConfig( val cOutputConfig: COutputConfig = COutputConfig(), val xcfaOutputConfig: XcfaOutputConfig = XcfaOutputConfig(), + val chcOutputConfig: ChcOutputConfig = ChcOutputConfig(), val witnessConfig: WitnessConfig = WitnessConfig(), val argConfig: ArgConfig = ArgConfig(), ) : Config { @@ -347,6 +348,11 @@ data class XcfaOutputConfig( var disable: Boolean = false, ) : Config +data class ChcOutputConfig( + @Parameter(names = ["--disable-chc-serialization"]) + var disable: Boolean = false, +) : Config + data class COutputConfig( @Parameter(names = ["--disable-c-serialization"]) var disable: Boolean = false, diff --git a/subprojects/xcfa/xcfa2chc/build.gradle.kts b/subprojects/xcfa/xcfa2chc/build.gradle.kts new file mode 100644 index 0000000000..6bd82ed776 --- /dev/null +++ b/subprojects/xcfa/xcfa2chc/build.gradle.kts @@ -0,0 +1,25 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +plugins { + id("kotlin-common") +} + +dependencies { + implementation(project(":theta-common")) + implementation(project(":theta-core")) + implementation(project(":theta-xcfa")) + implementation(project(":theta-solver-smtlib")) +} diff --git a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/ChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/ChcUtils.kt new file mode 100644 index 0000000000..60d0a1c5ff --- /dev/null +++ b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/ChcUtils.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hu.bme.mit.theta.xcfa2chc + +import com.google.common.base.Preconditions.checkArgument +import hu.bme.mit.theta.core.decl.Decls.Const +import hu.bme.mit.theta.core.decl.Decls.Param +import hu.bme.mit.theta.core.decl.ParamDecl +import hu.bme.mit.theta.core.type.Expr +import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.type.booltype.BoolExprs.* +import hu.bme.mit.theta.core.type.booltype.BoolType +import hu.bme.mit.theta.core.type.functype.FuncExprs +import hu.bme.mit.theta.core.type.functype.FuncType +import hu.bme.mit.theta.core.utils.ExprUtils +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager +import java.util.* + +/* +We want to be able to write logical derivations in the following way: + +head_name(vars) += // derivations + expr(vars). + +head_name(grounds) // facts + +!head_name(vars) with exprs(vars) // queries + */ + +open class Relation(val name: String, vararg paramTypes: Type) { + companion object { + private fun funcType(params: List, finalType: Type): FuncType<*, *> { + return if(params.size == 1) { + FuncType.of(params[0], finalType) + } else if(params.size > 1) { + FuncType.of(params[0], funcType(params.subList(1, params.size), finalType)) + } else { + error("Nullary functions aren't handled here.") + } + } + } + val arity: Int = paramTypes.size + val rules: MutableList = LinkedList() + val constDecl = if(arity == 0) Const(name, Bool()) else Const(name, funcType(paramTypes.toList(), Bool())) + open operator fun invoke(params: List>) = RelationApp(this, params) + open operator fun invoke(vararg params: Expr<*>) = RelationApp(this, params.toList()) +} + +data class RelationApp(val relation: Relation, val params: List>, val constraints: List> = emptyList()) { + init { + checkArgument(params.size == relation.arity) + } + val expr: Expr by lazy { + val coreExpr = if(params.size >= 1) { + FuncExprs.App(relation.constDecl.ref as Expr>, params.map { it }) + } else { + relation.constDecl.ref as Expr + } + if(constraints.isEmpty()) { + coreExpr + } else { + And(constraints + coreExpr) + } + } + + operator fun plusAssign(constraints: List>) { relation.rules.add(Rule(expr, constraints)) } + operator fun plusAssign(constraint: Expr) { relation.rules.add(Rule(expr, listOf(constraint))) } + + operator fun not() { relation.rules.add(Rule(False(), listOf(expr))) } + operator fun unaryPlus() { relation.rules.add(Rule(expr, listOf())) } + infix fun with(constraints: List>) = copy(constraints = this.constraints + constraints) + infix fun with(constraint: Expr) = copy(constraints = this.constraints + constraint) +} +data class Rule(val head: Expr, val constraints: List>) { + fun toExpr() = Forall(ExprUtils.getParams(head) + ExprUtils.getParams(constraints), Imply(And(constraints), head)) +} + +operator fun Expr.plus(other: Expr) = listOf(this, other) + +data class ParamHolder(private val type: T) { + private val lookup = LinkedHashMap>() + operator fun get(i: Int) = lookup.getOrPut(i) { Param("P$i", type) }.ref +} + +fun List.toSMT2(): String { + val symbolTable = GenericSmtLibSymbolTable() + val transformationManager = GenericSmtLibTransformationManager(symbolTable) + val terms = flatMap { it.rules.map { "(assert " + transformationManager.toTerm(it.toExpr()) + ")" } } + val decls = map { symbolTable.getDeclaration(it.constDecl) } + + return """ +; generated by Theta +; https://github.com/ftsrg/theta/ + +(set-logic HORN) + +; declarations +${decls.joinToString("\n")} + +; facts, rules, queries +${terms.joinToString("\n")} + +(check-sat) +(exit) +""".trimIndent() +} +fun Relation.toSMT2() = listOf(this).toSMT2() diff --git a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt new file mode 100644 index 0000000000..8c3849de4d --- /dev/null +++ b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hu.bme.mit.theta.xcfa2chc + +import hu.bme.mit.theta.core.decl.Decls.Param +import hu.bme.mit.theta.core.type.arraytype.ArrayType +import hu.bme.mit.theta.core.type.booltype.BoolExprs.And +import hu.bme.mit.theta.core.type.booltype.BoolExprs.True +import hu.bme.mit.theta.core.type.inttype.IntExprs.Int +import hu.bme.mit.theta.core.utils.ExprUtils +import hu.bme.mit.theta.core.utils.PathUtils +import hu.bme.mit.theta.core.utils.StmtUtils +import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory +import hu.bme.mit.theta.xcfa.collectVars +import hu.bme.mit.theta.xcfa.model.XcfaProcedure + +fun XcfaProcedure.toCHC(): List { + val i2i = ArrayType.of(Int(), Int()) + + val vars = edges.flatMap { it.label.collectVars() }.toSet().toList() + + val types = vars.map { it.type }.toTypedArray() + val oldParams = vars.associateWith { Param("|" + it.name + "|", it.type) } + val oldParamList = vars.map { oldParams[it]!!.ref }.toTypedArray() + val newParams = vars.associateWith { Param("|" + it.name + "_new|", it.type) } + + val ufs = locs.associateWith { Relation(it.name, *types) } // br, co, rf, com + + edges.forEach { + val unfoldResult = StmtUtils.toExpr(it.label.toStmt(), VarIndexingFactory.basicVarIndexing(0)) + val expr = PathUtils.unfold(And(unfoldResult.exprs), VarIndexingFactory.indexing(0)) + // var[0] is oldParam, var[-1]is newParam, everything else is a fresh param + var cnt = 0 + val consts = ExprUtils.getIndexedConstants(expr).associateWith { + if(it.index == 0) oldParams[it.varDecl]!! + else if (it.index == unfoldResult.indexing[it.varDecl]) newParams[it.varDecl]!! + else Param("__tmp_${cnt++}", it.type) + } + val newParamList = vars.map { if(unfoldResult.indexing[it] == 0) oldParams[it]!!.ref else newParams[it]!!.ref }.toTypedArray() + val paramdExpr = ExprUtils.changeDecls(expr, consts) + (ufs[it.target]!!)(*newParamList) += (ufs[it.source]!!)(*oldParamList).expr + paramdExpr + } + + if(errorLoc.isPresent) { + !(ufs[errorLoc.get()]!!(*oldParamList)) + } + + ufs[initLoc]!!(*oldParamList) += True() + + return ufs.values.toList() +} + +fun XcfaProcedure.toSMT2CHC(): String { + val chc = toCHC() + val smt2 = chc.toSMT2() + return smt2 +} diff --git a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt new file mode 100644 index 0000000000..2e9f99aade --- /dev/null +++ b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt @@ -0,0 +1,537 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hu.bme.mit.theta.xcfa2chc + +import hu.bme.mit.theta.core.decl.Decls +import hu.bme.mit.theta.core.decl.ParamDecl +import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.* +import hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Read +import hu.bme.mit.theta.core.type.arraytype.ArrayType +import hu.bme.mit.theta.core.type.booltype.BoolExprs.* +import hu.bme.mit.theta.core.type.inttype.IntExprs +import hu.bme.mit.theta.core.type.inttype.IntExprs.Int +import hu.bme.mit.theta.core.type.inttype.IntType +import org.junit.jupiter.api.Test + +private val iParamLut = LinkedHashMap>() +private fun iP(name: String) = iParamLut.getOrPut(name) { Decls.Param(name, IntExprs.Int()) } + + +class TestChcUtils { + + @Test + fun testPetersonManualCounting() { + val i2i = ArrayType.of(Int(), Int()) + + val pI = ParamHolder(Int()) + val pA = ParamHolder(i2i) + val br = pA[9] + val po = pA[10] + val co = pA[11] + val rf = pA[12] + val com = pA[13] + val prevW = pI[14] + val eid = pI[15] + val eid2 = pI[17] + val vid = pI[18] + val vid3 = pI[19] + val eid3 = pI[20] + val eid4 = pI[21] + val eid5 = pI[22] + val eid6 = pI[23] + val vid4 = pI[24] + val eid7 = pI[25] + val eid8 = pI[26] + val vid5 = pI[27] + val eid9 = pI[28] + val eid10 = pI[29] + val eid11 = pI[50] + + val turn = pI[30] + val flag1 = pI[31] + val flag2 = pI[32] + val cnt = pI[33] + val turn_old = pI[40] + val flag1_old = pI[41] + val flag2_old = pI[42] + val cnt_old = pI[43] + + val init = Relation("init", i2i, i2i, i2i, i2i, Int()) // br, co, rf, com + + val T0 = Relation("T0", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0G = Relation("T0_gate", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0C = Relation("T0_critical", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0CF = Relation("T0_critical_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0F = Relation("T0_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val T1 = Relation("T1", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1G = Relation("T1_gate", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1C = Relation("T1_critical", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1CF = Relation("T1_critical_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1F = Relation("T1_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val W = Relation("W", i2i, i2i, i2i, i2i, Int(), Int(), Int()) // br, co, rf, com, eid, vid, val + + // problem: unique rf values (W->R) will disable some possible reads + + init(br, co, rf, com, eid) += + Eq(eid, Int(0)) + + Eq(Read(co, Int(0)), Int(0)) + + Eq(Read(com, Int(0)), Int(0)) + + W(br, co, rf, com, eid, vid, pI[0]) += // turn := 0 + init(br, co, rf, com, eid).expr + + Eq(vid, Int(0)) + + Eq(pI[0], Int(0)) + W(br, co, rf, com, eid, vid, pI[0]) += // flag0 := 0 + init(br, co, rf, com, eid).expr + + Eq(vid, Int(1)) + + Eq(pI[0], Int(0)) + W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 0 + init(br, co, rf, com, eid).expr + + Eq(vid, Int(2)) + + Eq(pI[0], Int(0)) + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 + init(br, co, rf, com, eid).expr + + Eq(vid, Int(3)) + + Eq(pI[0], Int(0)) + + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + init(br, co, rf, com, eid2).expr + Eq(eid, Int(1)) + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + init(br, co, rf, com, eid2).expr + Eq(eid, Int(2)) + + W(br, co, rf, com, eid, vid, pI[0]) += // flag0 := 1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(1)) + + Eq(pI[0], Int(1)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(2)) + + Eq(pI[0], Int(1)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + W(br, co, rf, com, eid, vid, pI[0]) += // turn := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(0)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // turn := 1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(0)) + + Eq(pI[0], Int(1)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid3, vid3, pI[1]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source + Eq(Add(eid, Int(2)), eid9) + // eid update + Eq(vid3, Int(2)) + // flag[1] read + Eq(Read(rf, eid3), eid9) + // rf + Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) + Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) + Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 + Eq(pI[1], flag2) + + W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn + Eq(Add(eid, Int(4)), eid10) + // eid update + Eq(vid4, Int(0)) + // turn read + Eq(Read(rf, eid10), eid5) + // rf + Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) + Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) + Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 + Eq(pI[2], turn) + + Or(Eq(turn, Int(0)), Eq(flag2, Int(0))) + + W(br, co, rf, com, eid7, vid5, pI[3]).expr + // turn + W(br, co, rf, com, eid8, vid5, pI[4]).expr + // turn + Eq(Add(eid, Int(6)), eid11) + // eid update + Eq(vid5, Int(3)) + // turn read + Eq(Read(rf, eid11), eid7) + // rf + Lt(Read(com, eid7), Read(com, eid11)) + // com constraint (because rf) + Lt(Read(com, eid11), Read(com, eid8)) + // com constraint (because fr) + Eq(Add(Read(co, eid7), Int(1)), Read(co, eid8)) + // co-after is eid8 + Eq(pI[3], cnt) + + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0G(br, co, rf, com, eid, turn_old, flag1, flag2_old, cnt_old).expr + // previous loc + Eq(Add(eid, Int(8)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) + Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) + Lt(Read(com, eid10), Read(com, eid11)) + // com constraint (because po) + Lt(Read(com, eid11), Read(com, eid2)) // com constraint (because po) + + T1C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid3, vid3, pI[1]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source + Eq(Add(eid, Int(2)), eid9) + // eid update + Eq(vid3, Int(1)) + // flag[0] read + Eq(Read(rf, eid9), eid3) + // rf + Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) + Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) + Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 + Eq(pI[1], flag1) + + W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn + Eq(Add(eid, Int(4)), eid10) + // eid update + Eq(vid4, Int(0)) + // turn read + Eq(Read(rf, eid10), eid5) + // rf + Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) + Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) + Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 + Eq(pI[2], turn) + + Or(Eq(turn, Int(1)), Eq(flag1, Int(0))) + + W(br, co, rf, com, eid7, vid5, pI[3]).expr + // cnt + W(br, co, rf, com, eid8, vid5, pI[4]).expr + // cnt + Eq(Add(eid, Int(6)), eid11) + // eid update + Eq(vid5, Int(3)) + // turn + Eq(Read(rf, eid11), eid7) + // rf + Lt(Read(com, eid7), Read(com, eid11)) + // com constraint (because rf) + Lt(Read(com, eid11), Read(com, eid8)) + // com constraint (because fr) + Eq(Add(Read(co, eid7), Int(1)), Read(co, eid8)) + // co-after is eid8 + Eq(pI[3], cnt) + + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(8)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) + Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) + Lt(Read(com, eid10), Read(com, eid11)) + // com constraint (because po) + Lt(Read(com, eid11), Read(com, eid2)) // com constraint (because po) + + + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := cnt+1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(3)) + + Eq(pI[0], Add(cnt, Int(1))) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := cnt+1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(3)) + + Eq(pI[0], Add(cnt, Int(1))) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0CF(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1CF(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(3)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(3)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + + + T0F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + + W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(1)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // flag2 := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(2)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + !(T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with Eq(cnt, Int(1))) + !(T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with Eq(cnt, Int(1))) + + val expr = listOf(init, T0, T0G, T0C, T0CF, T0F, T1, T1G, T1C, T1CF, T1F, W).toSMT2() + println(expr) + } + + + @Test + fun testPetersonNoCounting() { + val i2i = ArrayType.of(Int(), Int()) + + val pI = ParamHolder(Int()) + val pB = ParamHolder(Bool()) + val pA = ParamHolder(i2i) + val br = pA[9] + val po = pA[10] + val co = pA[11] + val rf = pA[12] + val com = pA[13] + val prevW = pI[14] + val eid = pI[15] + val eid2 = pI[17] + val vid = pI[18] + val vid3 = pI[19] + val eid3 = pI[20] + val eid4 = pI[21] + val eid5 = pI[22] + val eid6 = pI[23] + val vid4 = pI[24] + val eid7 = pI[25] + val eid8 = pI[26] + val vid5 = pI[27] + val eid9 = pI[28] + val eid10 = pI[29] + val eid11 = pI[50] + + val turn = pB[30] + val flag1 = pB[31] + val flag2 = pB[32] + val cnt = pB[33] + val turn_old = pB[40] + val flag1_old = pB[41] + val flag2_old = pB[42] + + val init = Relation("init", i2i, i2i, i2i, i2i, Int()) // br, co, rf, com + + val T0 = Relation("T0", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0G = Relation("T0_gate", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0C = Relation("T0_critical", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0F = Relation("T0_final", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val T1 = Relation("T1", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1G = Relation("T1_gate", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1C = Relation("T1_critical", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1F = Relation("T1_final", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val W = Relation("W", i2i, i2i, i2i, i2i, Int(), Int(), Bool()) // br, co, rf, com, eid, vid, val + + init(br, co, rf, com, eid) += + Eq(eid, Int(0)) + + Eq(Read(co, Int(0)), Int(0)) + + Eq(Read(com, Int(0)), Int(0)) + + W(br, co, rf, com, eid, vid, pB[0]) += // turn := 0 + init(br, co, rf, com, eid).expr + + Eq(vid, Int(0)) + + Eq(pB[0], False()) + W(br, co, rf, com, eid, vid, pB[0]) += // flag0 := 0 + init(br, co, rf, com, eid).expr + + Eq(vid, Int(1)) + + Eq(pB[0], False()) + W(br, co, rf, com, eid, vid, pB[0]) += // flag1 := 0 + init(br, co, rf, com, eid).expr + + Eq(vid, Int(2)) + + Eq(pB[0], False()) + + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + init(br, co, rf, com, eid2).expr + Eq(eid, Int(1)) + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + init(br, co, rf, com, eid2).expr + Eq(eid, Int(2)) + + W(br, co, rf, com, eid, vid, pB[0]) += // flag0 := 1 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(1)) + + Eq(pB[0], True()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pB[0]) += // flag1 := 1 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(2)) + + Eq(pB[0], True()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + W(br, co, rf, com, eid, vid, pB[0]) += // turn := 1 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T0G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(0)) + + Eq(pB[0], True()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pB[0]) += // turn := 0 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(0)) + + Eq(pB[0], False()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid3, vid3, pB[1]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source + Eq(Add(eid, Int(2)), eid9) + // eid update + Eq(vid3, Int(2)) + // flag[1] read + Eq(Read(rf, eid3), eid9) + // rf + Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) + Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) + Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 + Eq(pB[1], flag2) + + W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn + Eq(Add(eid, Int(4)), eid10) + // eid update + Eq(vid4, Int(0)) + // turn read + Eq(Read(rf, eid10), eid5) + // rf + Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) + Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) + Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 + Eq(pB[2], turn) + + Or(Not(turn), Not(flag2)) + + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + T0G(br, co, rf, com, eid, turn_old, flag1, flag2_old, cnt).expr + // previous loc + Eq(Add(eid, Int(6)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) + Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) + Lt(Read(com, eid10), Read(com, eid2)) // com constraint (because po) + + T1C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid3, vid3, pB[1]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source + Eq(Add(eid, Int(2)), eid9) + // eid update + Eq(vid3, Int(1)) + // flag[0] read + Eq(Read(rf, eid9), eid3) + // rf + Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) + Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) + Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 + Eq(pB[1], flag1) + + W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn + Eq(Add(eid, Int(4)), eid10) + // eid update + Eq(vid4, Int(0)) + // turn read + Eq(Read(rf, eid10), eid5) + // rf + Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) + Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) + Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 + Eq(pB[2], turn) + + Or(turn, Not(flag1)) + + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + T1G(br, co, rf, com, eid, turn_old, flag1_old, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(6)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) + Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) + Lt(Read(com, eid10), Read(com, eid2)) // com constraint (because po) + + T0F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + + W(br, co, rf, com, eid, vid, pB[0]) += // cnt := 0 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(1)) + + Eq(pB[0], False()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pB[0]) += // cnt := 0 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(2)) + + Eq(pB[0], False()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + +// T0(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += +// W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write +// T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc +// Eq(Add(eid, Int(2)), eid2) + // eid update +// Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) +// T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += +// W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write +// T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc +// Eq(Add(eid, Int(2)), eid2) + // eid update +// Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + !(T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with + T1C(br, co, rf, com, eid2, turn_old, flag1_old, flag2_old, cnt).expr + + Eq(Read(com, eid), Read(com, eid2))) + + val expr = listOf(init, T0, T0G, T0C, T0F, T1, T1G, T1C, T1F, W).toSMT2() + println(expr) + } + +} From fda8b723ed40e6d932cb872736b1403f52181781 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Sun, 7 Jul 2024 23:40:50 +0200 Subject: [PATCH 02/24] Added CHC utils and solver --- .../java/hu/bme/mit/theta/core}/ChcUtils.kt | 30 +--- subprojects/solver/solver-z3/build.gradle.kts | 1 + .../theta/solver/z3/Z3ExprTransformer.java | 14 +- .../hu/bme/mit/theta/solver/z3/Z3Solver.java | 92 ++++++++++- .../mit/theta/solver/z3/Z3SolverFactory.java | 15 ++ .../theta/solver/z3/Z3TermTransformer.java | 27 ++-- .../mit/theta/solver/z3/Z3HornSolverTest.kt | 149 ++++++++++++++++++ .../hu/bme/mit/theta/solver/HornSolver.java | 43 +++++ .../hu/bme/mit/theta/solver/ProofNode.java | 83 ++++++++++ .../hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt | 29 ++++ .../hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt | 3 + 11 files changed, 447 insertions(+), 39 deletions(-) rename subprojects/{xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc => common/core/src/main/java/hu/bme/mit/theta/core}/ChcUtils.kt (82%) create mode 100644 subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt create mode 100644 subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java create mode 100644 subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ProofNode.java diff --git a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/ChcUtils.kt b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt similarity index 82% rename from subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/ChcUtils.kt rename to subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt index 60d0a1c5ff..d057582d05 100644 --- a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/ChcUtils.kt +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package hu.bme.mit.theta.xcfa2chc +package hu.bme.mit.theta.core import com.google.common.base.Preconditions.checkArgument import hu.bme.mit.theta.core.decl.Decls.Const @@ -27,8 +27,6 @@ import hu.bme.mit.theta.core.type.booltype.BoolType import hu.bme.mit.theta.core.type.functype.FuncExprs import hu.bme.mit.theta.core.type.functype.FuncType import hu.bme.mit.theta.core.utils.ExprUtils -import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable -import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager import java.util.* /* @@ -95,28 +93,4 @@ operator fun Expr.plus(other: Expr) = listOf(this, other) data class ParamHolder(private val type: T) { private val lookup = LinkedHashMap>() operator fun get(i: Int) = lookup.getOrPut(i) { Param("P$i", type) }.ref -} - -fun List.toSMT2(): String { - val symbolTable = GenericSmtLibSymbolTable() - val transformationManager = GenericSmtLibTransformationManager(symbolTable) - val terms = flatMap { it.rules.map { "(assert " + transformationManager.toTerm(it.toExpr()) + ")" } } - val decls = map { symbolTable.getDeclaration(it.constDecl) } - - return """ -; generated by Theta -; https://github.com/ftsrg/theta/ - -(set-logic HORN) - -; declarations -${decls.joinToString("\n")} - -; facts, rules, queries -${terms.joinToString("\n")} - -(check-sat) -(exit) -""".trimIndent() -} -fun Relation.toSMT2() = listOf(this).toSMT2() +} \ No newline at end of file diff --git a/subprojects/solver/solver-z3/build.gradle.kts b/subprojects/solver/solver-z3/build.gradle.kts index b4596b0c3a..c174647f4d 100644 --- a/subprojects/solver/solver-z3/build.gradle.kts +++ b/subprojects/solver/solver-z3/build.gradle.kts @@ -15,6 +15,7 @@ */ plugins { id("java-common") + id("kotlin-common") } dependencies { diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java index 191e9ff030..b64cfc72cf 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java @@ -39,12 +39,15 @@ import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.fptype.*; import hu.bme.mit.theta.core.type.functype.FuncAppExpr; +import hu.bme.mit.theta.core.type.functype.FuncLitExpr; import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.core.type.inttype.*; import hu.bme.mit.theta.core.type.rattype.*; import hu.bme.mit.theta.core.utils.BvUtils; +import hu.bme.mit.theta.core.utils.ExprUtils; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.stream.Stream; @@ -1140,15 +1143,24 @@ private com.microsoft.z3.Expr transformArrayInit(final ArrayInitExpr expr) private com.microsoft.z3.Expr transformFuncApp(final FuncAppExpr expr) { final Tuple2, List>> funcAndArgs = extractFuncAndArgs(expr); final Expr func = funcAndArgs.get1(); + final List> args = funcAndArgs.get2(); if (func instanceof RefExpr) { final RefExpr ref = (RefExpr) func; final Decl decl = ref.getDecl(); final com.microsoft.z3.FuncDecl funcDecl = transformer.toSymbol(decl); - final List> args = funcAndArgs.get2(); final com.microsoft.z3.Expr[] argTerms = args.stream() .map(this::toTerm) .toArray(com.microsoft.z3.Expr[]::new); return context.mkApp(funcDecl, argTerms); + } else if (func instanceof FuncLitExpr) { + Expr replaced = func; + int i = 0; + while (replaced instanceof FuncLitExpr) { + final ParamDecl param = ((FuncLitExpr) replaced).getParam(); + final Expr funcExpr = ((FuncLitExpr) replaced).getResult(); + replaced = ExprUtils.changeDecls(funcExpr, Map.of(param, ((RefExpr) args.get(i++)).getDecl())); + } + return toTerm(replaced); } else { throw new UnsupportedOperationException( "Higher order functions are not supported: " + func); diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java index 390da9a8c3..f90692771b 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java @@ -18,6 +18,8 @@ import com.google.common.collect.ImmutableList; import com.microsoft.z3.FuncDecl; import com.microsoft.z3.Status; +import com.microsoft.z3.Z3Exception; +import com.microsoft.z3.enumerations.Z3_decl_kind; import hu.bme.mit.theta.common.container.Containers; import hu.bme.mit.theta.core.decl.ConstDecl; import hu.bme.mit.theta.core.decl.Decl; @@ -32,16 +34,36 @@ import hu.bme.mit.theta.core.type.enumtype.EnumLitExpr; import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.solver.HornSolver; +import hu.bme.mit.theta.solver.ProofNode; +import hu.bme.mit.theta.solver.ProofNode.Builder; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverStatus; import hu.bme.mit.theta.solver.Stack; import hu.bme.mit.theta.solver.*; import hu.bme.mit.theta.solver.impl.StackImpl; import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.decl.Decls.Const; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.App; +import static hu.bme.mit.theta.core.utils.TypeUtils.cast; -final class Z3Solver implements UCSolver, Solver { +final class Z3Solver implements UCSolver, Solver, HornSolver { private final Z3SymbolTable symbolTable; private final Z3TransformationManager transformationManager; @@ -227,6 +249,74 @@ public void close() { z3Context.interrupt(); } + private Expr funcApp(Expr func, Expr param) { + final Expr> funcTyped = (Expr>) func; + final Expr paramTyped = (Expr) param; + return App(funcTyped, paramTyped); + } + + private Expr toProofExpr(com.microsoft.z3.Expr expr) { + final var args = expr.getArgs(); + final var lastArg = args[args.length - 1]; + checkState(lastArg.isApp()); + final var name = lastArg.getFuncDecl().getName().toString(); + final var params = lastArg.getArgs(); + final var paramValues = Arrays.stream(params).map(termTransformer::toExpr).toList(); + final List paramTypes = paramValues.stream().map(expr1 -> (Type) expr1.getType()).toList(); + + final var funcType = paramTypes.stream().reduce(Bool(), (res, param) -> FuncType.of(param, res)); + final var decl = Const(name, funcType); + Expr func = decl.getRef(); + for (Expr paramValue : paramValues) { + func = funcApp(func, paramValue); + } + return (Expr) func; + } + + /** + * This is a best-effort solution, hopefully would support (most) CHCs at least. + * Taken from https://github.com/ethereum/solidity/blob/5917fd82b3ca4cab5f817f78b8da8ebe409dd02e/libsmtutil/Z3CHCInterface.cpp#L130 + * and adapted to the Java API. + */ + @Override + public ProofNode getProof() { + checkState(status == SolverStatus.UNSAT, "Cannot get proof if status is not UNSAT"); + com.microsoft.z3.Expr proof = z3Solver.getProof(); + + Deque> proofStack = new LinkedList<>(); + proofStack.push(proof.getArgs()[0]); + + Expr root = cast(False(), Bool()); + final var rootBuilder = new ProofNode.Builder(root); + + Map visited = new LinkedHashMap<>(); + visited.put(proofStack.peek().getId(), rootBuilder); + + while (!proofStack.isEmpty()) { + final var proofNodeExpr = proofStack.pop(); + if (!visited.containsKey(proofNodeExpr.getId())) { + throw new Z3Exception("Node should exist in the graph nodes"); + } + final var proofNode = visited.get(proofNodeExpr.getId()); + + if (proofNodeExpr.isApp() && proofNodeExpr.getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_HYPER_RESOLVE) { + if (proofNodeExpr.getArgs().length > 0) { + for (int i = 1; i < proofNodeExpr.getArgs().length - 1; ++i) { + final var child = proofNodeExpr.getArgs()[i]; + if (!visited.containsKey(child.getId())) { + visited.put(child.getId(), new Builder(toProofExpr(child))); + proofStack.push(child); + } + proofNode.addChild(visited.get(child.getId())); + } + } + } + } + + + return rootBuilder.build(); + } + //// private final class Z3Model extends Valuation { diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java index 8c1d13c520..73a67e3b86 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java @@ -16,6 +16,7 @@ package hu.bme.mit.theta.solver.z3; import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.solver.HornSolver; import hu.bme.mit.theta.solver.ItpSolver; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverFactory; @@ -93,4 +94,18 @@ public ItpSolver createItpSolver() { z3Solver); } + public HornSolver createHornSolver() { + final com.microsoft.z3.Context z3Context = new com.microsoft.z3.Context(); + z3Context.updateParamValue("proof", "true"); + final com.microsoft.z3.Solver z3Solver = z3Context.mkSolver("HORN"); + + final Z3SymbolTable symbolTable = new Z3SymbolTable(); + final Z3TransformationManager transformationManager = new Z3TransformationManager( + symbolTable, z3Context); + final Z3TermTransformer termTransformer = new Z3TermTransformer(symbolTable); + + return new Z3Solver(symbolTable, transformationManager, termTransformer, z3Context, + z3Solver); + } + } diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java index d1999f5c94..d8ed556556 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java @@ -19,6 +19,13 @@ import com.microsoft.z3.*; import com.microsoft.z3.enumerations.Z3_decl_kind; import com.microsoft.z3.enumerations.Z3_sort_kind; +import com.microsoft.z3.ArrayExpr; +import com.microsoft.z3.ArraySort; +import com.microsoft.z3.FPNum; +import com.microsoft.z3.FuncDecl; +import com.microsoft.z3.Model; +import com.microsoft.z3.Sort; +import com.microsoft.z3.Z3Exception; import hu.bme.mit.theta.common.TernaryOperator; import hu.bme.mit.theta.common.TriFunction; import hu.bme.mit.theta.common.Tuple2; @@ -280,12 +287,13 @@ private Expr transformApp(final com.microsoft.z3.Expr term, final Model model private Expr transformFuncInterp(final com.microsoft.z3.FuncInterp funcInterp, final Model model, final List> vars) { checkArgument(funcInterp.getArity() >= 1); - final ParamDecl paramDecl = (ParamDecl) vars.get(vars.size() - 1); - final Expr op = createFuncLitExprBody( - vars.subList(vars.size() - funcInterp.getArity(), vars.size()).stream() - .map(decl -> (ParamDecl) decl).collect(Collectors.toList()), funcInterp, model, - vars); - return Func(paramDecl, op); + final List> params = vars.subList(vars.size() - funcInterp.getArity(), vars.size()).stream() + .map(decl -> (ParamDecl) decl).collect(Collectors.toList()); + Expr op = createFuncLitExprBody(params, funcInterp, model, vars); + for (ParamDecl param : params) { + op = Func(param, op); + } + return op; } private Expr createFuncLitExprBody(final List> paramDecl, @@ -410,8 +418,9 @@ private Expr transformExists(final com.microsoft.z3.Expr term, final Model mo private List> transformParams(final List> vars, final com.microsoft.z3.Sort[] sorts) { final ImmutableList.Builder> builder = ImmutableList.builder(); + int parambase = vars.size(); for (final com.microsoft.z3.Sort sort : sorts) { - final ParamDecl param = transformParam(vars, sort); + final ParamDecl param = transformParam(vars, sort, parambase++); builder.add(param); } final List> paramDecls = builder.build(); @@ -419,9 +428,9 @@ private List> transformParams(final List> vars, } private ParamDecl transformParam(final List> vars, - final com.microsoft.z3.Sort sort) { + final Sort sort, int i) { final Type type = transformSort(sort); - final ParamDecl param = Param(format(PARAM_NAME_FORMAT, vars.size()), type); + final ParamDecl param = Param(format(PARAM_NAME_FORMAT, i), type); return param; } diff --git a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt new file mode 100644 index 0000000000..ea15cfc59a --- /dev/null +++ b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.z3 + +import hu.bme.mit.theta.core.ParamHolder +import hu.bme.mit.theta.core.Relation +import hu.bme.mit.theta.core.decl.Decls.Const +import hu.bme.mit.theta.core.plus +import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.* +import hu.bme.mit.theta.core.type.booltype.BoolType +import hu.bme.mit.theta.core.type.functype.FuncExprs.App +import hu.bme.mit.theta.core.type.functype.FuncLitExpr +import hu.bme.mit.theta.core.type.functype.FuncType +import hu.bme.mit.theta.core.type.inttype.IntExprs.Int +import hu.bme.mit.theta.core.type.inttype.IntType +import hu.bme.mit.theta.solver.HornSolver +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.util.stream.Stream + + +class Z3HornSolverTest { + companion object { + + @JvmStatic + fun solvers(): Stream { + return Stream.of(Arguments.of(Z3SolverFactory.getInstance().createHornSolver())) + } + } + + @ParameterizedTest + @MethodSource("solvers") + fun testSolvable(solver: HornSolver) { + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init = Relation("init", Int(), Int()) + val inv = Relation("inv", Int(), Int()) + + init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) + inv(p[0], p[1]) += init(p[0], p[1]).expr + inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) + + !(inv(p[0], p[1]) with Lt(p[1], Int(1))) + + hornSolver.add(init) + hornSolver.add(inv) + + val status = hornSolver.check() + Assertions.assertTrue(status.isSat) + val model = hornSolver.model.toMap() + + Assertions.assertTrue(model.containsKey(inv.constDecl)) + Assertions.assertTrue(model.containsKey(init.constDecl)) + + val checkerSolver = Z3SolverFactory.getInstance().createSolver() + checkerSolver.use { + val p0 = Const("p0", Int()) + val p1 = Const("p1", Int()) + checkerSolver.add( + App( + App( + model.get(init.constDecl) as FuncLitExpr>, + p0.ref), + p1.ref)) + + checkerSolver.add(Lt(p1.ref, Int(0))) + Assertions.assertTrue(checkerSolver.check().isUnsat) + } + } + } + + @ParameterizedTest + @MethodSource("solvers") + fun testUnsolvable(solver: HornSolver) { + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init = Relation("init", Int(), Int()) + val inv = Relation("inv", Int(), Int()) + + init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) + inv(p[0], p[1]) += init(p[0], p[1]).expr + inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) + + !(inv(p[0], p[1]) with Leq(p[1], Int(1))) + + hornSolver.add(init) + hornSolver.add(inv) + + val status = hornSolver.check() + Assertions.assertTrue(status.isUnsat) + + val proof = hornSolver.proof + Assertions.assertTrue(proof != null) + } + } + + + @ParameterizedTest + @MethodSource("solvers") + fun testNonlinearUnsolvable(solver: HornSolver) { + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init1 = Relation("init1", Int(), Int()) + val init2 = Relation("init2", Int(), Int()) + val inv1 = Relation("inv1", Int(), Int()) + val inv2 = Relation("inv2", Int(), Int()) + + val err = Relation("err", Int(), Int()) + + init1(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) + init2(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) + inv1(p[0], p[1]) += init1(p[0], p[1]).expr + inv1(p[0], p[2]) += inv1(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(3))) + inv2(p[0], p[1]) += init2(p[0], p[1]).expr + inv2(p[0], p[2]) += inv2(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(5))) + + err(p[0], p[2]) += inv1(p[0], p[1]).expr + inv2(p[0], p[1]).expr + Gt(p[1], Int(0)) + + !err(p[0], p[1]) + + hornSolver.add(init1) + hornSolver.add(init2) + hornSolver.add(inv1) + hornSolver.add(inv2) + hornSolver.add(err) + + val status = hornSolver.check() + Assertions.assertTrue(status.isUnsat) + + val proof = hornSolver.proof + Assertions.assertTrue(proof != null) + } + } +} diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java new file mode 100644 index 0000000000..3f419a8edb --- /dev/null +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java @@ -0,0 +1,43 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver; + +import hu.bme.mit.theta.core.Relation; +import hu.bme.mit.theta.core.Rule; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; + +import java.util.Collection; + +/** + * The HornSolver can provide proofs, and accept Relations + */ +public interface HornSolver extends Solver { + + default void add(Relation relation) { + add(relation.getRules().stream().map(Rule::toExpr).toList()); + } + + default void add(Collection relations) { + add(relations.stream().flatMap(it -> it.getRules().stream().map(Rule::toExpr)).toList()); + } + + /** + * Get the proof found in the solver. + * + */ + ProofNode getProof(); +} diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ProofNode.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ProofNode.java new file mode 100644 index 0000000000..65d72bca1b --- /dev/null +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ProofNode.java @@ -0,0 +1,83 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hu.bme.mit.theta.solver; + +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.AndExpr; +import hu.bme.mit.theta.core.type.booltype.BoolType; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; +import java.util.stream.Stream; + +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; + +public record ProofNode(int id, Expr expr, List children) { + public AndExpr toExpr() { + return And(Stream.concat(Stream.of(expr), children.stream().flatMap(it -> it.toExpr().getOps().stream())).toList()); + } + + public String toString() { + final var sj = new StringJoiner(", "); + children.stream().map(ProofNode::id).forEach(it -> sj.add(it.toString())); + StringBuilder sb = new StringBuilder(); + sb.append(id). + append(": "). + append(expr.toString().replaceAll("[\r\n]", " ")); + + if(!children.isEmpty()) { + sb.append(" -> ").append(sj); + } + + sb.append("\n"); + children.forEach(it -> sb.append(it.toString())); + return sb.toString(); + } + + public static class Builder { + final Expr expr; + final List children; + + public Builder(Expr expr) { + this.expr = expr; + children = new ArrayList<>(); + } + + public Builder addChild(ProofNode.Builder child) { + children.add(child); + return this; + } + + /** + * This will fail for non-acyclic proofs, but we don't care, we should never have those. + */ + public ProofNode build() { + return build(0); + } + + private ProofNode build(int id) { + final var builtChildren = new ArrayList(); + for (Builder child : children) { + final var built = child.build(id); + builtChildren.add(built); + id = built.id + 1; + } + return new ProofNode(id, expr, builtChildren); + } + } +} diff --git a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt index 8c3849de4d..e37e31e91b 100644 --- a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt +++ b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt @@ -16,7 +16,9 @@ package hu.bme.mit.theta.xcfa2chc +import hu.bme.mit.theta.core.Relation import hu.bme.mit.theta.core.decl.Decls.Param +import hu.bme.mit.theta.core.plus import hu.bme.mit.theta.core.type.arraytype.ArrayType import hu.bme.mit.theta.core.type.booltype.BoolExprs.And import hu.bme.mit.theta.core.type.booltype.BoolExprs.True @@ -25,6 +27,8 @@ import hu.bme.mit.theta.core.utils.ExprUtils import hu.bme.mit.theta.core.utils.PathUtils import hu.bme.mit.theta.core.utils.StmtUtils import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager import hu.bme.mit.theta.xcfa.collectVars import hu.bme.mit.theta.xcfa.model.XcfaProcedure @@ -69,3 +73,28 @@ fun XcfaProcedure.toSMT2CHC(): String { val smt2 = chc.toSMT2() return smt2 } + + +fun List.toSMT2(): String { + val symbolTable = GenericSmtLibSymbolTable() + val transformationManager = GenericSmtLibTransformationManager(symbolTable) + val terms = flatMap { it.rules.map { "(assert " + transformationManager.toTerm(it.toExpr()) + ")" } } + val decls = map { symbolTable.getDeclaration(it.constDecl) } + + return """ +; generated by Theta +; https://github.com/ftsrg/theta/ + +(set-logic HORN) + +; declarations +${decls.joinToString("\n")} + +; facts, rules, queries +${terms.joinToString("\n")} + +(check-sat) +(exit) +""".trimIndent() +} +fun Relation.toSMT2() = listOf(this).toSMT2() diff --git a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt index 2e9f99aade..6c448dba39 100644 --- a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt +++ b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt @@ -16,8 +16,11 @@ package hu.bme.mit.theta.xcfa2chc +import hu.bme.mit.theta.core.ParamHolder +import hu.bme.mit.theta.core.Relation import hu.bme.mit.theta.core.decl.Decls import hu.bme.mit.theta.core.decl.ParamDecl +import hu.bme.mit.theta.core.plus import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.* import hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Read import hu.bme.mit.theta.core.type.arraytype.ArrayType From 984bc7e7c7ffa88f1621b4f23ac4fcf4f88b368e Mon Sep 17 00:00:00 2001 From: "ThetaBotMaintainer[bot]" <139346997+ThetaBotMaintainer[bot]@users.noreply.github.com> Date: Sun, 7 Jul 2024 21:44:29 +0000 Subject: [PATCH 03/24] Reformatted code --- .../java/hu/bme/mit/theta/core/ChcUtils.kt | 41 +++-- .../mit/theta/solver/z3/Z3HornSolverTest.kt | 1 - .../hu/bme/mit/theta/solver/HornSolver.java | 1 - .../hu/bme/mit/theta/solver/ProofNode.java | 2 +- .../hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt | 10 +- .../hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt | 155 ++++++++++-------- 6 files changed, 121 insertions(+), 89 deletions(-) diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt index d057582d05..5b7789b350 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt @@ -42,55 +42,74 @@ head_name(grounds) // facts open class Relation(val name: String, vararg paramTypes: Type) { companion object { + private fun funcType(params: List, finalType: Type): FuncType<*, *> { - return if(params.size == 1) { + return if (params.size == 1) { FuncType.of(params[0], finalType) - } else if(params.size > 1) { + } else if (params.size > 1) { FuncType.of(params[0], funcType(params.subList(1, params.size), finalType)) } else { error("Nullary functions aren't handled here.") } } } + val arity: Int = paramTypes.size val rules: MutableList = LinkedList() - val constDecl = if(arity == 0) Const(name, Bool()) else Const(name, funcType(paramTypes.toList(), Bool())) + val constDecl = if (arity == 0) Const(name, Bool()) else Const(name, funcType(paramTypes.toList(), Bool())) open operator fun invoke(params: List>) = RelationApp(this, params) open operator fun invoke(vararg params: Expr<*>) = RelationApp(this, params.toList()) } -data class RelationApp(val relation: Relation, val params: List>, val constraints: List> = emptyList()) { +data class RelationApp(val relation: Relation, val params: List>, + val constraints: List> = emptyList()) { + init { checkArgument(params.size == relation.arity) } + val expr: Expr by lazy { - val coreExpr = if(params.size >= 1) { + val coreExpr = if (params.size >= 1) { FuncExprs.App(relation.constDecl.ref as Expr>, params.map { it }) } else { relation.constDecl.ref as Expr } - if(constraints.isEmpty()) { + if (constraints.isEmpty()) { coreExpr } else { And(constraints + coreExpr) } } - operator fun plusAssign(constraints: List>) { relation.rules.add(Rule(expr, constraints)) } - operator fun plusAssign(constraint: Expr) { relation.rules.add(Rule(expr, listOf(constraint))) } + operator fun plusAssign(constraints: List>) { + relation.rules.add(Rule(expr, constraints)) + } + + operator fun plusAssign(constraint: Expr) { + relation.rules.add(Rule(expr, listOf(constraint))) + } + + operator fun not() { + relation.rules.add(Rule(False(), listOf(expr))) + } + + operator fun unaryPlus() { + relation.rules.add(Rule(expr, listOf())) + } - operator fun not() { relation.rules.add(Rule(False(), listOf(expr))) } - operator fun unaryPlus() { relation.rules.add(Rule(expr, listOf())) } infix fun with(constraints: List>) = copy(constraints = this.constraints + constraints) infix fun with(constraint: Expr) = copy(constraints = this.constraints + constraint) } + data class Rule(val head: Expr, val constraints: List>) { + fun toExpr() = Forall(ExprUtils.getParams(head) + ExprUtils.getParams(constraints), Imply(And(constraints), head)) } operator fun Expr.plus(other: Expr) = listOf(this, other) -data class ParamHolder(private val type: T) { +data class ParamHolder(private val type: T) { + private val lookup = LinkedHashMap>() operator fun get(i: Int) = lookup.getOrPut(i) { Param("P$i", type) }.ref } \ No newline at end of file diff --git a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt index ea15cfc59a..675f1bd14b 100644 --- a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt +++ b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt @@ -109,7 +109,6 @@ class Z3HornSolverTest { } } - @ParameterizedTest @MethodSource("solvers") fun testNonlinearUnsolvable(solver: HornSolver) { diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java index 3f419a8edb..d5166d7e39 100644 --- a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java @@ -37,7 +37,6 @@ default void add(Collection relations) { /** * Get the proof found in the solver. - * */ ProofNode getProof(); } diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ProofNode.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ProofNode.java index 65d72bca1b..9cf1ade5e0 100644 --- a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ProofNode.java +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ProofNode.java @@ -40,7 +40,7 @@ public String toString() { append(": "). append(expr.toString().replaceAll("[\r\n]", " ")); - if(!children.isEmpty()) { + if (!children.isEmpty()) { sb.append(" -> ").append(sj); } diff --git a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt index e37e31e91b..dc4d11d2b1 100644 --- a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt +++ b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt @@ -50,16 +50,17 @@ fun XcfaProcedure.toCHC(): List { // var[0] is oldParam, var[-1]is newParam, everything else is a fresh param var cnt = 0 val consts = ExprUtils.getIndexedConstants(expr).associateWith { - if(it.index == 0) oldParams[it.varDecl]!! + if (it.index == 0) oldParams[it.varDecl]!! else if (it.index == unfoldResult.indexing[it.varDecl]) newParams[it.varDecl]!! else Param("__tmp_${cnt++}", it.type) } - val newParamList = vars.map { if(unfoldResult.indexing[it] == 0) oldParams[it]!!.ref else newParams[it]!!.ref }.toTypedArray() + val newParamList = vars.map { if (unfoldResult.indexing[it] == 0) oldParams[it]!!.ref else newParams[it]!!.ref } + .toTypedArray() val paramdExpr = ExprUtils.changeDecls(expr, consts) (ufs[it.target]!!)(*newParamList) += (ufs[it.source]!!)(*oldParamList).expr + paramdExpr } - if(errorLoc.isPresent) { + if (errorLoc.isPresent) { !(ufs[errorLoc.get()]!!(*oldParamList)) } @@ -78,7 +79,7 @@ fun XcfaProcedure.toSMT2CHC(): String { fun List.toSMT2(): String { val symbolTable = GenericSmtLibSymbolTable() val transformationManager = GenericSmtLibTransformationManager(symbolTable) - val terms = flatMap { it.rules.map { "(assert " + transformationManager.toTerm(it.toExpr()) + ")" } } + val terms = flatMap { it.rules.map { "(assert " + transformationManager.toTerm(it.toExpr()) + ")" } } val decls = map { symbolTable.getDeclaration(it.constDecl) } return """ @@ -97,4 +98,5 @@ ${terms.joinToString("\n")} (exit) """.trimIndent() } + fun Relation.toSMT2() = listOf(this).toSMT2() diff --git a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt index 6c448dba39..67b63f51df 100644 --- a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt +++ b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt @@ -75,17 +75,27 @@ class TestChcUtils { val init = Relation("init", i2i, i2i, i2i, i2i, Int()) // br, co, rf, com - val T0 = Relation("T0", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0G = Relation("T0_gate", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0C = Relation("T0_critical", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0CF = Relation("T0_critical_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0F = Relation("T0_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - - val T1 = Relation("T1", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1G = Relation("T1_gate", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1C = Relation("T1_critical", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1CF = Relation("T1_critical_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1F = Relation("T1_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0 = Relation("T0", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0G = Relation("T0_gate", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0C = Relation("T0_critical", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0CF = Relation("T0_critical_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0F = Relation("T0_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val T1 = Relation("T1", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1G = Relation("T1_gate", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1C = Relation("T1_critical", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1CF = Relation("T1_critical_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1F = Relation("T1_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), + Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt val W = Relation("W", i2i, i2i, i2i, i2i, Int(), Int(), Int()) // br, co, rf, com, eid, vid, val @@ -93,8 +103,8 @@ class TestChcUtils { init(br, co, rf, com, eid) += Eq(eid, Int(0)) + - Eq(Read(co, Int(0)), Int(0)) + - Eq(Read(com, Int(0)), Int(0)) + Eq(Read(co, Int(0)), Int(0)) + + Eq(Read(com, Int(0)), Int(0)) W(br, co, rf, com, eid, vid, pI[0]) += // turn := 0 init(br, co, rf, com, eid).expr + @@ -120,14 +130,14 @@ class TestChcUtils { W(br, co, rf, com, eid, vid, pI[0]) += // flag0 := 1 W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + Eq(vid, Int(1)) + Eq(pI[0], Int(1)) + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next Lt(Read(com, eid2), Read(com, eid)) W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 1 W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + Eq(vid, Int(2)) + Eq(pI[0], Int(1)) + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next @@ -135,7 +145,7 @@ class TestChcUtils { T0G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc Eq(Add(eid, Int(2)), eid2) + // eid update Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) T1G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += @@ -161,7 +171,7 @@ class TestChcUtils { T0C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += W(br, co, rf, com, eid3, vid3, pI[1]).expr + // rf-source - W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source Eq(Add(eid, Int(2)), eid9) + // eid update Eq(vid3, Int(2)) + // flag[1] read Eq(Read(rf, eid3), eid9) + // rf @@ -169,36 +179,36 @@ class TestChcUtils { Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 Eq(pI[1], flag2) + - W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn - W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn + W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn Eq(Add(eid, Int(4)), eid10) + // eid update Eq(vid4, Int(0)) + // turn read - Eq(Read(rf, eid10), eid5) + // rf + Eq(Read(rf, eid10), eid5) + // rf Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 Eq(pI[2], turn) + - Or(Eq(turn, Int(0)), Eq(flag2, Int(0))) + - W(br, co, rf, com, eid7, vid5, pI[3]).expr + // turn - W(br, co, rf, com, eid8, vid5, pI[4]).expr + // turn + Or(Eq(turn, Int(0)), Eq(flag2, Int(0))) + + W(br, co, rf, com, eid7, vid5, pI[3]).expr + // turn + W(br, co, rf, com, eid8, vid5, pI[4]).expr + // turn Eq(Add(eid, Int(6)), eid11) + // eid update Eq(vid5, Int(3)) + // turn read - Eq(Read(rf, eid11), eid7) + // rf + Eq(Read(rf, eid11), eid7) + // rf Lt(Read(com, eid7), Read(com, eid11)) + // com constraint (because rf) Lt(Read(com, eid11), Read(com, eid8)) + // com constraint (because fr) Eq(Add(Read(co, eid7), Int(1)), Read(co, eid8)) + // co-after is eid8 Eq(pI[3], cnt) + - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write T0G(br, co, rf, com, eid, turn_old, flag1, flag2_old, cnt_old).expr + // previous loc Eq(Add(eid, Int(8)), eid2) + // eid update Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) - Lt(Read(com, eid10), Read(com, eid11)) + // com constraint (because po) + Lt(Read(com, eid10), Read(com, eid11)) + // com constraint (because po) Lt(Read(com, eid11), Read(com, eid2)) // com constraint (because po) T1C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += W(br, co, rf, com, eid3, vid3, pI[1]).expr + // rf-source - W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source Eq(Add(eid, Int(2)), eid9) + // eid update Eq(vid3, Int(1)) + // flag[0] read Eq(Read(rf, eid9), eid3) + // rf @@ -206,26 +216,26 @@ class TestChcUtils { Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 Eq(pI[1], flag1) + - W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn - W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn + W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn Eq(Add(eid, Int(4)), eid10) + // eid update Eq(vid4, Int(0)) + // turn read - Eq(Read(rf, eid10), eid5) + // rf + Eq(Read(rf, eid10), eid5) + // rf Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 Eq(pI[2], turn) + - Or(Eq(turn, Int(1)), Eq(flag1, Int(0))) + - W(br, co, rf, com, eid7, vid5, pI[3]).expr + // cnt - W(br, co, rf, com, eid8, vid5, pI[4]).expr + // cnt + Or(Eq(turn, Int(1)), Eq(flag1, Int(0))) + + W(br, co, rf, com, eid7, vid5, pI[3]).expr + // cnt + W(br, co, rf, com, eid8, vid5, pI[4]).expr + // cnt Eq(Add(eid, Int(6)), eid11) + // eid update Eq(vid5, Int(3)) + // turn - Eq(Read(rf, eid11), eid7) + // rf + Eq(Read(rf, eid11), eid7) + // rf Lt(Read(com, eid7), Read(com, eid11)) + // com constraint (because rf) Lt(Read(com, eid11), Read(com, eid8)) + // com constraint (because fr) Eq(Add(Read(co, eid7), Int(1)), Read(co, eid8)) + // co-after is eid8 Eq(pI[3], cnt) + - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc Eq(Add(eid, Int(8)), eid2) + // eid update Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) @@ -233,7 +243,6 @@ class TestChcUtils { Lt(Read(com, eid10), Read(com, eid11)) + // com constraint (because po) Lt(Read(com, eid11), Read(com, eid2)) // com constraint (because po) - W(br, co, rf, com, eid, vid, pI[0]) += // cnt := cnt+1 W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + @@ -260,7 +269,6 @@ class TestChcUtils { Eq(Add(eid, Int(2)), eid2) + // eid update Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW T0CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + @@ -289,7 +297,6 @@ class TestChcUtils { Eq(Add(eid, Int(2)), eid2) + // eid update Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 0 W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + @@ -323,7 +330,6 @@ class TestChcUtils { println(expr) } - @Test fun testPetersonNoCounting() { val i2i = ArrayType.of(Int(), Int()) @@ -363,22 +369,30 @@ class TestChcUtils { val init = Relation("init", i2i, i2i, i2i, i2i, Int()) // br, co, rf, com - val T0 = Relation("T0", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0G = Relation("T0_gate", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0C = Relation("T0_critical", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0F = Relation("T0_final", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - - val T1 = Relation("T1", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1G = Relation("T1_gate", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1C = Relation("T1_critical", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1F = Relation("T1_final", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0 = Relation("T0", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), + Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0G = Relation("T0_gate", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), + Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0C = Relation("T0_critical", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), + Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0F = Relation("T0_final", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), + Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val T1 = Relation("T1", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), + Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1G = Relation("T1_gate", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), + Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1C = Relation("T1_critical", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), + Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1F = Relation("T1_final", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), + Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt val W = Relation("W", i2i, i2i, i2i, i2i, Int(), Int(), Bool()) // br, co, rf, com, eid, vid, val init(br, co, rf, com, eid) += Eq(eid, Int(0)) + - Eq(Read(co, Int(0)), Int(0)) + - Eq(Read(com, Int(0)), Int(0)) + Eq(Read(co, Int(0)), Int(0)) + + Eq(Read(com, Int(0)), Int(0)) W(br, co, rf, com, eid, vid, pB[0]) += // turn := 0 init(br, co, rf, com, eid).expr + @@ -400,14 +414,14 @@ class TestChcUtils { W(br, co, rf, com, eid, vid, pB[0]) += // flag0 := 1 W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + Eq(vid, Int(1)) + Eq(pB[0], True()) + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next Lt(Read(com, eid2), Read(com, eid)) W(br, co, rf, com, eid, vid, pB[0]) += // flag1 := 1 W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW - T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + Eq(vid, Int(2)) + Eq(pB[0], True()) + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next @@ -415,7 +429,7 @@ class TestChcUtils { T0G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc Eq(Add(eid, Int(2)), eid2) + // eid update Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) T1G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += @@ -441,7 +455,7 @@ class TestChcUtils { T0C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += W(br, co, rf, com, eid3, vid3, pB[1]).expr + // rf-source - W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source Eq(Add(eid, Int(2)), eid9) + // eid update Eq(vid3, Int(2)) + // flag[1] read Eq(Read(rf, eid3), eid9) + // rf @@ -449,17 +463,17 @@ class TestChcUtils { Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 Eq(pB[1], flag2) + - W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn - W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn + W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn Eq(Add(eid, Int(4)), eid10) + // eid update Eq(vid4, Int(0)) + // turn read - Eq(Read(rf, eid10), eid5) + // rf + Eq(Read(rf, eid10), eid5) + // rf Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 Eq(pB[2], turn) + - Or(Not(turn), Not(flag2)) + - W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + Or(Not(turn), Not(flag2)) + + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write T0G(br, co, rf, com, eid, turn_old, flag1, flag2_old, cnt).expr + // previous loc Eq(Add(eid, Int(6)), eid2) + // eid update Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) @@ -468,7 +482,7 @@ class TestChcUtils { T1C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += W(br, co, rf, com, eid3, vid3, pB[1]).expr + // rf-source - W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source Eq(Add(eid, Int(2)), eid9) + // eid update Eq(vid3, Int(1)) + // flag[0] read Eq(Read(rf, eid9), eid3) + // rf @@ -476,17 +490,17 @@ class TestChcUtils { Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 Eq(pB[1], flag1) + - W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn - W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn + W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn Eq(Add(eid, Int(4)), eid10) + // eid update Eq(vid4, Int(0)) + // turn read - Eq(Read(rf, eid10), eid5) + // rf + Eq(Read(rf, eid10), eid5) + // rf Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 Eq(pB[2], turn) + - Or(turn, Not(flag1)) + - W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + Or(turn, Not(flag1)) + + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write T1G(br, co, rf, com, eid, turn_old, flag1_old, flag2, cnt).expr + // previous loc Eq(Add(eid, Int(6)), eid2) + // eid update Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) @@ -495,13 +509,12 @@ class TestChcUtils { T0F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) T1F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) W(br, co, rf, com, eid, vid, pB[0]) += // cnt := 0 W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW @@ -531,7 +544,7 @@ class TestChcUtils { !(T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with T1C(br, co, rf, com, eid2, turn_old, flag1_old, flag2_old, cnt).expr + - Eq(Read(com, eid), Read(com, eid2))) + Eq(Read(com, eid), Read(com, eid2))) val expr = listOf(init, T0, T0G, T0C, T0F, T1, T1G, T1C, T1F, W).toSMT2() println(expr) From 0ea35e2538e1eb78b3ce62e77fe91853084413fd Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 8 Jul 2024 00:53:29 +0200 Subject: [PATCH 04/24] Added support for smtlib horn solving --- .../solver-smtlib/src/main/antlr/SMTLIBv2.g4 | 10 ++++ .../solver/smtlib/SmtLibSolverManager.java | 26 ++++++++-- .../generic/GenericSmtLibExprTransformer.java | 14 +++++- .../generic/GenericSmtLibSolverFactory.java | 12 +++++ .../generic/GenericSmtLibTermTransformer.java | 9 ++-- .../solver/smtlib/solver/SmtLibSolver.java | 50 +++++++++++++++++-- .../solver/parser/GetProofResponse.java | 46 +++++++++++++++++ .../solver/parser/SpecificResponse.java | 15 ++++++ .../mit/theta/solver/z3/Z3HornSolverTest.kt | 48 +++++++++++++++++- .../hu/bme/mit/theta/solver/HornSolver.java | 4 +- .../bme/mit/theta/solver/SolverFactory.java | 9 ++++ 11 files changed, 228 insertions(+), 15 deletions(-) create mode 100644 subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java diff --git a/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 b/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 index 0e5543397d..56d17db953 100644 --- a/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 +++ b/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 @@ -54,6 +54,7 @@ specific_success_response : check_sat_response | get_unsat_core_response | get_model_response + | proof_response | get_interpolants_response_smtinterpol ; @@ -102,6 +103,10 @@ function_dec : ParOpen symbol ParOpen sorted_var* ParClose sort ParClose ; +proof_response + : ParOpen PROOF term ParClose + ; + get_interpolants_response_smtinterpol : ParOpen term* ParClose ; @@ -504,6 +509,11 @@ CMD_SetOption ; +// proof + +PROOF + : 'proof' + ; // General reserved words diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java index 2c3dd8f167..3e6ba24e75 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java @@ -18,7 +18,13 @@ import com.google.common.collect.ImmutableList; import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.logging.Logger; -import hu.bme.mit.theta.solver.*; +import hu.bme.mit.theta.solver.HornSolver; +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverBase; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.SolverManager; +import hu.bme.mit.theta.solver.UCSolver; import hu.bme.mit.theta.solver.smtlib.impl.bitwuzla.BitwuzlaSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.boolector.BoolectorSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.cvc4.CVC4SmtLibSolverInstaller; @@ -35,11 +41,17 @@ import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.*; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; public final class SmtLibSolverManager extends SolverManager { @@ -367,5 +379,13 @@ public ItpSolver createItpSolver() { instantiatedSolvers.add(solver); return solver; } + + @Override + public HornSolver createHornSolver() { + checkState(!closed, "Solver manager was closed"); + final var solver = solverFactory.createHornSolver(); + instantiatedSolvers.add(solver); + return solver; + } } } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java index bed493ea87..86096611a7 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java @@ -113,6 +113,7 @@ import hu.bme.mit.theta.core.type.fptype.FpToBvExpr; import hu.bme.mit.theta.core.type.fptype.FpToFpExpr; import hu.bme.mit.theta.core.type.functype.FuncAppExpr; +import hu.bme.mit.theta.core.type.functype.FuncLitExpr; import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.core.type.inttype.IntAddExpr; import hu.bme.mit.theta.core.type.inttype.IntDivExpr; @@ -145,12 +146,14 @@ import hu.bme.mit.theta.core.type.rattype.RatSubExpr; import hu.bme.mit.theta.core.type.rattype.RatToIntExpr; import hu.bme.mit.theta.core.utils.BvUtils; +import hu.bme.mit.theta.core.utils.ExprUtils; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibExprTransformer; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; import java.math.BigInteger; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import static com.google.common.base.Preconditions.checkState; @@ -1171,16 +1174,25 @@ private String transformFpRoundingMode(FpRoundingMode roundingMode) { protected String transformFuncApp(final FuncAppExpr expr) { final Tuple2, List>> funcAndArgs = extractFuncAndArgs(expr); final Expr func = funcAndArgs.get1(); + final List> args = funcAndArgs.get2(); if (func instanceof RefExpr) { final RefExpr ref = (RefExpr) func; final Decl decl = ref.getDecl(); final String funcDecl = transformer.toSymbol(decl); - final List> args = funcAndArgs.get2(); final String[] argTerms = args.stream() .map(this::toTerm) .toArray(String[]::new); return String.format("(%s %s)", funcDecl, String.join(" ", argTerms)); + } else if (func instanceof FuncLitExpr) { + Expr replaced = func; + int i = 0; + while (replaced instanceof FuncLitExpr) { + final ParamDecl param = ((FuncLitExpr) replaced).getParam(); + final Expr funcExpr = ((FuncLitExpr) replaced).getResult(); + replaced = ExprUtils.changeDecls(funcExpr, Map.of(param, ((RefExpr) args.get(i++)).getDecl())); + } + return toTerm(replaced); } else { throw new UnsupportedOperationException( "Higher order functions are not supported: " + func); diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java index 17f00eb5be..2bd45bf15d 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java @@ -16,6 +16,7 @@ package hu.bme.mit.theta.solver.smtlib.impl.generic; +import hu.bme.mit.theta.solver.HornSolver; import hu.bme.mit.theta.solver.ItpSolver; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverFactory; @@ -92,4 +93,15 @@ public ItpSolver createItpSolver() { throw new UnsupportedOperationException( "The generic driver does not support interpolation"); } + + @Override + public HornSolver createHornSolver() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibSolverBinary(solverPath, args, solverOverride); + + return new SmtLibSolver(symbolTable, transformationManager, termTransformer, solverBinary, + false, "HORN"); + } } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java index 2f2e33d8be..cdbf39e2ba 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java @@ -323,13 +323,14 @@ protected Expr transformFuncDef(final SMTLIBv2Parser.Function_defContext ctx, final List> paramDecls = ctx.sorted_var().stream() .map(sv -> Param(sv.symbol().getText(), transformSort(sv.sort()))) .collect(toList()); - checkArgument(paramDecls.size() == 1, "Only unary functions are supported"); pushParams(paramDecls, vars); - final var op = transformTerm(ctx.term(), model, vars); + var op = transformTerm(ctx.term(), model, vars); popParams(paramDecls, vars); - - return Func(paramDecls.get(0), op); + for (ParamDecl param : paramDecls) { + op = Func(param, op); + } + return op; } protected Expr transformTerm(final TermContext ctx, final SmtLibModel model, diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java index 6f285c5292..66b3330ed0 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java @@ -22,14 +22,25 @@ import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.utils.ExprUtils; +import hu.bme.mit.theta.solver.HornSolver; +import hu.bme.mit.theta.solver.ProofNode; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverStatus; import hu.bme.mit.theta.solver.Stack; import hu.bme.mit.theta.solver.*; import hu.bme.mit.theta.solver.impl.StackImpl; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; import hu.bme.mit.theta.solver.smtlib.solver.parser.*; +import hu.bme.mit.theta.solver.smtlib.solver.parser.CheckSatResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GeneralResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GetModelResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GetProofResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GetUnsatCoreResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; @@ -40,14 +51,18 @@ import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; -public class SmtLibSolver implements UCSolver, Solver { +public class SmtLibSolver implements UCSolver, Solver, HornSolver { private static final String ASSUMPTION_LABEL = "_LABEL_%d"; protected final SmtLibSymbolTable symbolTable; protected final SmtLibTransformationManager transformationManager; protected final SmtLibTermTransformer termTransformer; protected final SmtLibSolverBinary solverBinary; + private final boolean unsatCoreEnabled; + private final String logic; + protected final Stack> assertions; protected final Map> assumptions; protected final Stack> declarationStack; @@ -66,7 +81,7 @@ public SmtLibSolver( final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary, boolean unsatCoreEnabled ) { - this(symbolTable, transformationManager, termTransformer, solverBinary, unsatCoreEnabled, SmtLibEnumStrategy.getDefaultStrategy()); + this(symbolTable, transformationManager, termTransformer, solverBinary, unsatCoreEnabled, SmtLibEnumStrategy.getDefaultStrategy(), "ALL"); } public SmtLibSolver( @@ -74,7 +89,8 @@ public SmtLibSolver( final SmtLibTransformationManager transformationManager, final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary, boolean unsatCoreEnabled, - final SmtLibEnumStrategy enumStrategy + final SmtLibEnumStrategy enumStrategy, + final String logic ) { this.solverBinary = solverBinary; this.symbolTable = symbolTable; @@ -83,6 +99,7 @@ public SmtLibSolver( this.enumStrategy = enumStrategy; this.unsatCoreEnabled = unsatCoreEnabled; + this.logic = logic; assertions = new StackImpl<>(); assumptions = new HashMap<>(); @@ -267,7 +284,10 @@ private void init() { if (unsatCoreEnabled) { issueGeneralCommand("(set-option :produce-unsat-cores true)"); } - issueGeneralCommand("(set-logic ALL)"); + if (logic.equals("HORN")) { + issueGeneralCommand("(set-option :produce-proofs true)"); + } + issueGeneralCommand("(set-logic " + logic + ")"); } protected void clearState() { @@ -297,4 +317,26 @@ protected final GeneralResponse parseResponse(final String response) { throw new SmtLibSolverException("Could not parse solver output: " + response, e); } } + + @Override + public ProofNode getProof() { + solverBinary.issueCommand("(get-proof)"); + var response = solverBinary.readResponse(); + if (response.charAt(0) == '(' && response.charAt(response.length() - 1) == ')') { + // for some reason, this is encapsulated in brackets + response = response.substring(1, response.length() - 1); + } + response = response.substring(response.indexOf("(proof")); + // hopefully the proof is always the last line + final var res = parseResponse(response); + if (res.isError()) { + throw new SmtLibSolverException(res.getReason()); + } else if (res.isSpecific()) { + final GetProofResponse getModelResponse = res.asSpecific().asGetProofResponse(); + final var proof = termTransformer.toExpr(getModelResponse.getProof(), Bool(), new SmtLibModel(Collections.emptyMap())); + return null; + } else { + throw new AssertionError(); + } + } } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java new file mode 100644 index 0000000000..93258dcab0 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java @@ -0,0 +1,46 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib.solver.parser; + +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Proof_responseContext; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.misc.Interval; + +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_funContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_fun_recContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_funs_recContext; + +public class GetProofResponse extends SpecificResponse { + + private final String proofTerm; + + private GetProofResponse(String proofNode) { + this.proofTerm = proofNode; + } + + public static GetProofResponse fromContext(final Proof_responseContext ctx) { + return new GetProofResponse(extractString(ctx.term())); + } + + public static String extractString(final ParserRuleContext ctx) { + return ctx.start.getInputStream() + .getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex())); + } + + public String getProof() { + return proofTerm; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/SpecificResponse.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/SpecificResponse.java index a3cd715e39..b7cbff1b58 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/SpecificResponse.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/SpecificResponse.java @@ -17,6 +17,7 @@ import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2BaseVisitor; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Proof_responseContext; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Specific_success_responseContext; import static com.google.common.base.Preconditions.checkState; @@ -42,6 +43,11 @@ public SpecificResponse visitGet_model_response( SMTLIBv2Parser.Get_model_responseContext ctx) { return GetModelResponse.fromContext(ctx); } + + @Override + public SpecificResponse visitProof_response(Proof_responseContext ctx) { + return GetProofResponse.fromContext(ctx); + } }); } @@ -63,6 +69,10 @@ public boolean isGetModelResponse() { && ((GetUnsatCoreResponse) this).getLabels().size() == 0; } + public boolean isGetProofResponse() { + return this instanceof GetProofResponse; + } + public CheckSatResponse asCheckSatResponse() { checkState(isCheckSatResponse()); return (CheckSatResponse) this; @@ -89,4 +99,9 @@ public GetModelResponse asGetModelResponse() { throw new AssertionError(); } } + + public GetProofResponse asGetProofResponse() { + checkState(isGetProofResponse()); + return (GetProofResponse) this; + } } diff --git a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt index 675f1bd14b..dc6619ff19 100644 --- a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt +++ b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt @@ -15,6 +15,8 @@ */ package hu.bme.mit.theta.solver.z3 +import hu.bme.mit.theta.common.OsHelper +import hu.bme.mit.theta.common.logging.NullLogger import hu.bme.mit.theta.core.ParamHolder import hu.bme.mit.theta.core.Relation import hu.bme.mit.theta.core.decl.Decls.Const @@ -27,7 +29,10 @@ import hu.bme.mit.theta.core.type.functype.FuncType import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.type.inttype.IntType import hu.bme.mit.theta.solver.HornSolver -import org.junit.jupiter.api.Assertions +import hu.bme.mit.theta.solver.SolverFactory +import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException +import org.junit.jupiter.api.* import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -37,10 +42,49 @@ import java.util.stream.Stream class Z3HornSolverTest { companion object { + private var solverInstalled: Boolean = false + private var solverManager: SmtLibSolverManager? = null + private var solverFactory: SolverFactory? = null + + private val SOLVER: String = "z3" + private val VERSION: String = "4.13.0" + @JvmStatic fun solvers(): Stream { - return Stream.of(Arguments.of(Z3SolverFactory.getInstance().createHornSolver())) + return Stream.of( + Arguments.of(Z3SolverFactory.getInstance().createHornSolver()), + Arguments.of(solverFactory!!.createHornSolver()), + ) + } + + @BeforeAll + @JvmStatic + fun init() { + if (OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) { + val home = SmtLibSolverManager.HOME + + solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()) + try { + solverManager!!.install(SOLVER, VERSION, VERSION, null, false) + solverInstalled = true + } catch (e: SmtLibSolverInstallerException) { + e.printStackTrace() + } + + solverFactory = solverManager!!.getSolverFactory(SOLVER, VERSION) + } } + + @AfterAll + @JvmStatic + fun destroy() { + if (solverInstalled) solverManager!!.uninstall(SOLVER, VERSION) + } + } + + @BeforeEach + fun before() { + Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) } @ParameterizedTest diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java index d5166d7e39..93f96b0fed 100644 --- a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/HornSolver.java @@ -38,5 +38,7 @@ default void add(Collection relations) { /** * Get the proof found in the solver. */ - ProofNode getProof(); + default ProofNode getProof() { + throw new UnsupportedOperationException("Cannot get proof."); + } } diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java index f6deba9a21..cdcddc3f21 100644 --- a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java @@ -42,4 +42,13 @@ public interface SolverFactory { */ ItpSolver createItpSolver(); + /** + * Create a solver that is capable of horn solving (CHC). + * + * @return Solver instance + */ + default HornSolver createHornSolver() { + throw new UnsupportedOperationException(); + } + } From 363c30f8a7b1db15b0ef683a929e2983470c6b15 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 8 Jul 2024 14:28:56 +0200 Subject: [PATCH 05/24] Updated function application utilities, and added logic for proof generation --- .../theta/core/type/functype/FuncExprs.java | 13 ++ .../bme/mit/theta/core/utils/ExprUtils.java | 22 ++ .../javasmt/JavaSMTExprTransformer.java | 1 + .../solver/solver-smtlib/build.gradle.kts | 1 + .../solver-smtlib/src/main/antlr/SMTLIBv2.g4 | 13 ++ .../generic/GenericSmtLibExprTransformer.java | 17 +- .../generic/GenericSmtLibTermTransformer.java | 110 +++++++++- .../solver/smtlib/solver/SmtLibSolver.java | 95 ++++++++- .../solver/parser/GetProofResponse.java | 26 ++- .../solver/smtlib/SmtLibHornSolverTest.kt | 197 ++++++++++++++++++ .../solver/z3legacy/Z3ExprTransformer.java | 17 +- .../theta/solver/z3/Z3ExprTransformer.java | 23 +- .../hu/bme/mit/theta/solver/z3/Z3Solver.java | 9 +- .../mit/theta/solver/z3/Z3HornSolverTest.kt | 45 +--- 14 files changed, 470 insertions(+), 119 deletions(-) create mode 100644 subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java index ceba2b56eb..c6fbfdf480 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java @@ -22,6 +22,7 @@ import java.util.List; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; public final class FuncExprs { @@ -62,4 +63,16 @@ public static FuncAppExpr Expr UnsafeApp(final Expr func, final Expr param) { + checkState(func.getType() instanceof FuncType fFunc && fFunc.getParamType().equals(param.getType()), "Parameter of type " + param.getType() + " is not suitable for function of type " + func.getType()); + final Expr> funcTyped = (Expr>) func; + final Expr paramTyped = (Expr) param; + return App(funcTyped, paramTyped); + } + + public static Expr UnsafeApp(final Expr func, final List> param) { + checkState(func.getType() instanceof FuncType fFunc, "Supposed function is of type " + func.getType()); + final Expr> funcTyped = (Expr>) func; + return App(funcTyped, param.stream().map(it -> (Expr) it).toList()); + } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java index bce86a7db8..6873033f60 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java @@ -15,6 +15,8 @@ */ package hu.bme.mit.theta.core.utils; +import com.google.common.collect.ImmutableList; +import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.container.Containers; import hu.bme.mit.theta.core.decl.ConstDecl; import hu.bme.mit.theta.core.decl.Decl; @@ -31,6 +33,7 @@ import hu.bme.mit.theta.core.type.booltype.ExistsExpr; import hu.bme.mit.theta.core.type.booltype.ForallExpr; import hu.bme.mit.theta.core.type.booltype.NotExpr; +import hu.bme.mit.theta.core.type.functype.FuncAppExpr; import hu.bme.mit.theta.core.utils.IndexedVars.Builder; import hu.bme.mit.theta.core.utils.indexings.VarIndexing; @@ -503,4 +506,23 @@ public static Expr changeSubexpr(Expr expr, Map, public static Expr changeDecls(Expr expr, Map, ? extends Decl> lookup) { return changeSubexpr(expr, lookup.entrySet().stream().map(entry -> Map.entry(entry.getKey().getRef(), entry.getValue().getRef())).collect(Collectors.toMap(Entry::getKey, Entry::getValue))); } + + /** + * Extracts function and its arguments from a nested expression + */ + public static Tuple2, List>> extractFuncAndArgs(final FuncAppExpr expr) { + final Expr func = expr.getFunc(); + final Expr arg = expr.getParam(); + if (func instanceof FuncAppExpr) { + final FuncAppExpr funcApp = (FuncAppExpr) func; + final Tuple2, List>> funcAndArgs = extractFuncAndArgs(funcApp); + final Expr resFunc = funcAndArgs.get1(); + final List> args = funcAndArgs.get2(); + final List> resArgs = ImmutableList.>builder().addAll(args).add(arg) + .build(); + return Tuple2.of(resFunc, resArgs); + } else { + return Tuple2.of(func, ImmutableList.of(arg)); + } + } } diff --git a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java index 240d5a4e07..024890cb7a 100644 --- a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java +++ b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java @@ -58,6 +58,7 @@ import static com.google.common.base.Preconditions.checkState; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static hu.bme.mit.theta.core.utils.ExprUtils.extractFuncAndArgs; final class JavaSMTExprTransformer { diff --git a/subprojects/solver/solver-smtlib/build.gradle.kts b/subprojects/solver/solver-smtlib/build.gradle.kts index 83cc26bf96..c578f4dba8 100644 --- a/subprojects/solver/solver-smtlib/build.gradle.kts +++ b/subprojects/solver/solver-smtlib/build.gradle.kts @@ -15,6 +15,7 @@ */ plugins { id("java-common") + id("kotlin-common") id("antlr-grammar") } diff --git a/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 b/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 index 56d17db953..d58649115c 100644 --- a/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 +++ b/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 @@ -104,9 +104,22 @@ function_dec ; proof_response + : ParOpen proof_logic? proof_funs* proof_term ParClose + | proof_logic? proof_funs* proof_term + ; + +proof_term : ParOpen PROOF term ParClose ; +proof_logic + : ParOpen CMD_SetLogic 'HORN' ParClose + ; + +proof_funs + : ParOpen CMD_DeclareFun symbol ParOpen in+=sort* ParClose out=sort ParClose + ; + get_interpolants_response_smtinterpol : ParOpen term* ParClose ; diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java index 86096611a7..753dc0a462 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java @@ -157,6 +157,7 @@ import java.util.concurrent.ExecutionException; import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.utils.ExprUtils.extractFuncAndArgs; public class GenericSmtLibExprTransformer implements SmtLibExprTransformer { @@ -1199,22 +1200,6 @@ protected String transformFuncApp(final FuncAppExpr expr) { } } - private static Tuple2, List>> extractFuncAndArgs(final FuncAppExpr expr) { - final Expr func = expr.getFunc(); - final Expr arg = expr.getParam(); - if (func instanceof FuncAppExpr) { - final FuncAppExpr funcApp = (FuncAppExpr) func; - final Tuple2, List>> funcAndArgs = extractFuncAndArgs(funcApp); - final Expr resFunc = funcAndArgs.get1(); - final List> args = funcAndArgs.get2(); - final List> resArgs = ImmutableList.>builder().addAll(args).add(arg) - .build(); - return Tuple2.of(resFunc, resArgs); - } else { - return Tuple2.of(func, ImmutableList.of(arg)); - } - } - /* * Arrays */ diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java index cdbf39e2ba..f1172fe33c 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java @@ -39,6 +39,62 @@ import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.fptype.*; import hu.bme.mit.theta.core.type.functype.FuncExprs; +import hu.bme.mit.theta.core.type.booltype.AndExpr; +import hu.bme.mit.theta.core.type.booltype.BoolExprs; +import hu.bme.mit.theta.core.type.booltype.IffExpr; +import hu.bme.mit.theta.core.type.booltype.ImplyExpr; +import hu.bme.mit.theta.core.type.booltype.NotExpr; +import hu.bme.mit.theta.core.type.booltype.OrExpr; +import hu.bme.mit.theta.core.type.booltype.XorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAddExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAndExpr; +import hu.bme.mit.theta.core.type.bvtype.BvArithShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvConcatExpr; +import hu.bme.mit.theta.core.type.bvtype.BvExprs; +import hu.bme.mit.theta.core.type.bvtype.BvExtractExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLogicShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNegExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNotExpr; +import hu.bme.mit.theta.core.type.bvtype.BvOrExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSExtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSModExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSRemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvShiftLeftExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSubExpr; +import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.bvtype.BvUDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvURemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvXorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvZExtExpr; +import hu.bme.mit.theta.core.type.fptype.FpAbsExpr; +import hu.bme.mit.theta.core.type.fptype.FpAddExpr; +import hu.bme.mit.theta.core.type.fptype.FpDivExpr; +import hu.bme.mit.theta.core.type.fptype.FpEqExpr; +import hu.bme.mit.theta.core.type.fptype.FpExprs; +import hu.bme.mit.theta.core.type.fptype.FpGeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpGtExpr; +import hu.bme.mit.theta.core.type.fptype.FpIsNanExpr; +import hu.bme.mit.theta.core.type.fptype.FpLeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpLtExpr; +import hu.bme.mit.theta.core.type.fptype.FpMaxExpr; +import hu.bme.mit.theta.core.type.fptype.FpMinExpr; +import hu.bme.mit.theta.core.type.fptype.FpMulExpr; +import hu.bme.mit.theta.core.type.fptype.FpNegExpr; +import hu.bme.mit.theta.core.type.fptype.FpRemExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundToIntegralExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundingMode; +import hu.bme.mit.theta.core.type.fptype.FpSqrtExpr; +import hu.bme.mit.theta.core.type.fptype.FpSubExpr; import hu.bme.mit.theta.core.type.functype.FuncLitExpr; import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.core.type.inttype.IntDivExpr; @@ -66,10 +122,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static hu.bme.mit.theta.core.decl.Decls.Const; import static hu.bme.mit.theta.core.decl.Decls.Param; import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.*; import static hu.bme.mit.theta.core.type.functype.FuncExprs.Func; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.UnsafeApp; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; import static hu.bme.mit.theta.core.type.rattype.RatExprs.Rat; import static hu.bme.mit.theta.core.utils.TypeUtils.cast; @@ -176,6 +234,11 @@ public GenericSmtLibTermTransformer(final SmtLibSymbolTable symbolTable, final S // Array put("select", exprArrayReadOperator()); put("store", exprArrayWriteOperator()); + + // Proof + put("hyper-res", expectedFunc("hyper-res")); + put("asserted", expectedFunc("asserted")); + put("mp", expectedFunc("mp")); }}; } @@ -411,11 +474,11 @@ protected Expr transformGenericTerm(final Generic_termContext ctx, final SmtL } else if (funAppTransformer.containsKey(funName)) { // known function application return funAppTransformer.get(funName).apply(funParams, funAppParams, model, vars); } else { // custom function application - checkArgument(funParams.size() == 0, - "Custom unary function application cannot have parameter"); - checkArgument(funAppParams.size() == 1, "Only unary functions are supported"); +// checkArgument(funParams.size() == 0, +// "Custom unary function application cannot have parameter"); +// checkArgument(funAppParams.size() == 1, "Only unary functions are supported"); - return createFuncAppExpr(funName, funAppParams.get(0), model, vars); + return createFuncAppExpr(funName, funAppParams, model, vars); } } @@ -426,7 +489,7 @@ private Expr createArrayLitExpr(final Expr Expr createFuncAppExpr(final String funName, - final TermContext funAppParam, final SmtLibModel model, + final List funAppParams, final SmtLibModel model, final BiMap, String> vars) { final Expr funcExpr; if (symbolTable.definesSymbol(funName)) { @@ -440,10 +503,14 @@ private

Expr createFuncAppExpr(final String } assert funcExpr.getType() instanceof FuncType; - @SuppressWarnings("unchecked") final var funType = (FuncType) funcExpr.getType(); - final var paramExpr = transformTerm(funAppParam, model, vars); - return FuncExprs.App(cast(funcExpr, funType), cast(paramExpr, funType.getParamType())); + var expr = funcExpr; + for (TermContext funAppParam : funAppParams) { + final var paramExpr = transformTerm(funAppParam, model, vars); + expr = UnsafeApp(expr, paramExpr); + } + + return expr; } protected Expr transformLetTerm(final Let_termContext ctx, final SmtLibModel model, @@ -646,6 +713,33 @@ protected void popParams(final List> paramDecls, /* Utilities */ + /** + * We don't want to provide a separate Expr class for these, but we need to parse them. + * + * @param funcName: the name of the function to use in backtransformation + */ + private OperatorCreatorFunction expectedFunc(String funcName) { + return (params, ops, model, vars) -> { + var opCnt = ops.size(); + var name = funcName + opCnt; + if (!symbolTable.definesSymbol(name)) { + Type type = Bool(); + var prefix = "(declare-fun " + name + " ("; + var suffix = ") Bool)"; + for (int i = 0; i < opCnt; i++) { + type = FuncType.of(Bool(), type); + suffix = " Bool" + suffix; + } + symbolTable.put(Const(name, type), name, prefix + suffix); + } + Expr expr = symbolTable.getConst(name).getRef(); + for (TermContext op : ops) { + expr = UnsafeApp(expr, transformTerm(op, model, vars)); + } + return expr; + }; + } + @SuppressWarnings("unused") private OperatorCreatorFunction exprNullaryOperator(final Supplier> function) { return (params, ops, model, vars) -> { diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java index 66b3330ed0..e845d3455e 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java @@ -15,15 +15,22 @@ */ package hu.bme.mit.theta.solver.smtlib.solver; +import com.google.common.base.Function; +import com.microsoft.z3.Z3Exception; import hu.bme.mit.theta.core.decl.ConstDecl; import hu.bme.mit.theta.core.model.Valuation; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.core.type.enumtype.EnumType; +import hu.bme.mit.theta.core.type.anytype.RefExpr; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.type.functype.FuncAppExpr; +import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.core.utils.ExprUtils; import hu.bme.mit.theta.solver.HornSolver; import hu.bme.mit.theta.solver.ProofNode; +import hu.bme.mit.theta.solver.ProofNode.Builder; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverStatus; import hu.bme.mit.theta.solver.Stack; @@ -49,9 +56,21 @@ import java.util.*; import java.util.stream.Collectors; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.decl.Decls.Const; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static hu.bme.mit.theta.core.utils.ExprUtils.extractFuncAndArgs; +import static hu.bme.mit.theta.core.utils.TypeUtils.cast; public class SmtLibSolver implements UCSolver, Solver, HornSolver { @@ -322,21 +341,83 @@ protected final GeneralResponse parseResponse(final String response) { public ProofNode getProof() { solverBinary.issueCommand("(get-proof)"); var response = solverBinary.readResponse(); - if (response.charAt(0) == '(' && response.charAt(response.length() - 1) == ')') { - // for some reason, this is encapsulated in brackets - response = response.substring(1, response.length() - 1); - } - response = response.substring(response.indexOf("(proof")); - // hopefully the proof is always the last line final var res = parseResponse(response); if (res.isError()) { throw new SmtLibSolverException(res.getReason()); } else if (res.isSpecific()) { final GetProofResponse getModelResponse = res.asSpecific().asGetProofResponse(); + getModelResponse.getFunDeclarations().forEach((name, def) -> { + final Function stringToType = (String it) -> switch (it.toLowerCase()) { + case "int" -> Int(); + case "bool" -> Bool(); + default -> + throw new UnsupportedOperationException("Currently not supporting type " + it); + }; + var type = stringToType.apply(def.get2()); + for (String s : def.get1()) { + type = FuncType.of(stringToType.apply(s), type); + } + symbolTable.put(Const(name, type), name, def.get3()); + }); final var proof = termTransformer.toExpr(getModelResponse.getProof(), Bool(), new SmtLibModel(Collections.emptyMap())); - return null; + return proofFromExpr(proof); } else { throw new AssertionError(); } } + + private ProofNode proofFromExpr(Expr proof) { + checkState(proof instanceof FuncAppExpr, "Proof must be a function application."); + int id = 0; + final Map, Integer> lookup = new LinkedHashMap<>(); + + final var args = extractFuncAndArgs((FuncAppExpr) proof).get2(); + + Deque> proofStack = new LinkedList<>(); + proofStack.push(args.get(0)); + lookup.put(args.get(0), id++); + + Expr root = cast(False(), Bool()); + final var rootBuilder = new ProofNode.Builder(root); + + Map visited = new LinkedHashMap<>(); + visited.put(lookup.get(args.get(0)), rootBuilder); + + while (!proofStack.isEmpty()) { + final var proofNodeExpr = proofStack.pop(); + if (!visited.containsKey(lookup.getOrDefault(proofNodeExpr, -1))) { + throw new Z3Exception("Node should exist in the graph nodes"); + } + final var proofNode = visited.get(lookup.get(proofNodeExpr)); + + if (proofNodeExpr instanceof FuncAppExpr funcAppExpr) { + final var nameAndArgs = extractFuncAndArgs(funcAppExpr); + if (nameAndArgs.get1() instanceof RefExpr refName && refName.getDecl().getName().startsWith("hyper-res")) { + if (!nameAndArgs.get2().isEmpty()) { + for (int i = 1; i < nameAndArgs.get2().size() - 1; ++i) { + final var child = nameAndArgs.get2().get(i); + if (!visited.containsKey(lookup.getOrDefault(child, -1))) { + if (!lookup.containsKey(child)) { + lookup.put(child, id++); + } + visited.put(lookup.get(child), new Builder(extractProofExpr(child))); + proofStack.push(child); + } + proofNode.addChild(visited.get(lookup.get(child))); + } + } + } + } + } + return rootBuilder.build(); + } + + private Expr extractProofExpr(Expr expr) { + checkState(expr instanceof FuncAppExpr, "Proof should be function application."); + final var nameAndArgs = extractFuncAndArgs((FuncAppExpr) expr); + final var args = nameAndArgs.get2(); + final var lastArg = args.get(args.size() - 1); + checkState(lastArg instanceof FuncAppExpr, "Proof should be function application."); + return (Expr) lastArg; + } } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java index 93258dcab0..4d46feb071 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java @@ -15,24 +15,36 @@ */ package hu.bme.mit.theta.solver.smtlib.solver.parser; +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.common.Tuple3; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Proof_responseContext; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.misc.Interval; -import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_funContext; -import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_fun_recContext; -import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_funs_recContext; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; public class GetProofResponse extends SpecificResponse { private final String proofTerm; + private final Map, String, String>> funDeclarations; // name -> [inSorts, outSort, declaration] - private GetProofResponse(String proofNode) { + private GetProofResponse(String proofNode, Map, String, String>> funDeclarations) { this.proofTerm = proofNode; + this.funDeclarations = funDeclarations; } public static GetProofResponse fromContext(final Proof_responseContext ctx) { - return new GetProofResponse(extractString(ctx.term())); + return new GetProofResponse( + extractString(ctx.proof_term().term()), + ctx.proof_funs().stream().map(it -> Tuple2.of( + extractString(it.symbol()), + Tuple3.of( + it.in.stream().map(GetProofResponse::extractString).toList(), + extractString(it.out), + extractString(it) + ))).collect(Collectors.toMap(Tuple2::get1, Tuple2::get2))); } public static String extractString(final ParserRuleContext ctx) { @@ -43,4 +55,8 @@ public static String extractString(final ParserRuleContext ctx) { public String getProof() { return proofTerm; } + + public Map, String, String>> getFunDeclarations() { + return funDeclarations; + } } diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt new file mode 100644 index 0000000000..6a897a3400 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt @@ -0,0 +1,197 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib + +import hu.bme.mit.theta.common.OsHelper +import hu.bme.mit.theta.common.logging.NullLogger +import hu.bme.mit.theta.core.ParamHolder +import hu.bme.mit.theta.core.Relation +import hu.bme.mit.theta.core.decl.Decls.Const +import hu.bme.mit.theta.core.plus +import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.* +import hu.bme.mit.theta.core.type.booltype.BoolType +import hu.bme.mit.theta.core.type.functype.FuncExprs.App +import hu.bme.mit.theta.core.type.functype.FuncLitExpr +import hu.bme.mit.theta.core.type.functype.FuncType +import hu.bme.mit.theta.core.type.inttype.IntExprs.Int +import hu.bme.mit.theta.core.type.inttype.IntType +import hu.bme.mit.theta.solver.SolverFactory +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException +import org.junit.jupiter.api.* +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + + +class SmtLibHornSolverTest { + companion object { + private var solverManager: SmtLibSolverManager? = null + private val solverFactories: MutableMap, SolverFactory> = LinkedHashMap() + + private val SOLVERS: List> = listOf(Pair("z3", "4.13.0"), Pair("z3", "4.12.6")) + + @JvmStatic + fun solvers(): List { + return solverFactories.map { Arguments.of(it.key, it.value) } + } + + @BeforeAll + @JvmStatic + fun init() { + if (OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) { + val home = SmtLibSolverManager.HOME + + solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()) + for ((solver, version) in SOLVERS) { + + try { + solverManager!!.install(solver, version, version, null, false) + } catch (e: SmtLibSolverInstallerException) { + e.printStackTrace() + } + + solverFactories.put(Pair(solver, version), solverManager!!.getSolverFactory(solver, version)) + } + } + } + + @AfterAll + @JvmStatic + fun destroy() { + for ((solver, version) in SOLVERS) { + try { + solverManager!!.uninstall(solver, version) + } catch (e: SmtLibSolverInstallerException) { + e.printStackTrace() + } + } + } + } + + @BeforeEach + fun before() { + Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testSolvable(name: Pair, solverFactory: SolverFactory) { + val solver = solverFactory.createHornSolver() + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init = Relation("init", Int(), Int()) + val inv = Relation("inv", Int(), Int()) + + init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) + inv(p[0], p[1]) += init(p[0], p[1]).expr + inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) + + !(inv(p[0], p[1]) with Lt(p[1], Int(1))) + + hornSolver.add(init) + hornSolver.add(inv) + + val status = hornSolver.check() + Assertions.assertTrue(status.isSat) + val model = hornSolver.model.toMap() + + Assertions.assertTrue(model.containsKey(inv.constDecl)) + Assertions.assertTrue(model.containsKey(init.constDecl)) + + val checkerSolver = solverFactory!!.createSolver() + checkerSolver.use { + val p0 = Const("p0", Int()) + val p1 = Const("p1", Int()) + checkerSolver.add( + App( + App( + model.get(init.constDecl) as FuncLitExpr>, + p0.ref), + p1.ref)) + + checkerSolver.add(Lt(p1.ref, Int(0))) + Assertions.assertTrue(checkerSolver.check().isUnsat) + } + } + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testUnsolvable(name: Pair, solverFactory: SolverFactory) { + val solver = solverFactory.createHornSolver() + + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init = Relation("init", Int(), Int()) + val inv = Relation("inv", Int(), Int()) + + init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) + inv(p[0], p[1]) += init(p[0], p[1]).expr + inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) + + !(inv(p[0], p[1]) with Leq(p[1], Int(1))) + + hornSolver.add(init) + hornSolver.add(inv) + + val status = hornSolver.check() + Assertions.assertTrue(status.isUnsat) + + val proof = hornSolver.proof + Assertions.assertTrue(proof != null) + } + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testNonlinearUnsolvable(name: Pair, solverFactory: SolverFactory) { + val solver = solverFactory.createHornSolver() + + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init1 = Relation("init1", Int(), Int()) + val init2 = Relation("init2", Int(), Int()) + val inv1 = Relation("inv1", Int(), Int()) + val inv2 = Relation("inv2", Int(), Int()) + + val err = Relation("err", Int(), Int()) + + init1(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) + init2(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) + inv1(p[0], p[1]) += init1(p[0], p[1]).expr + inv1(p[0], p[2]) += inv1(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(3))) + inv2(p[0], p[1]) += init2(p[0], p[1]).expr + inv2(p[0], p[2]) += inv2(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(5))) + + err(p[0], p[2]) += inv1(p[0], p[1]).expr + inv2(p[0], p[1]).expr + Gt(p[1], Int(0)) + + !err(p[0], p[1]) + + hornSolver.add(init1) + hornSolver.add(init2) + hornSolver.add(inv1) + hornSolver.add(inv2) + hornSolver.add(err) + + val status = hornSolver.check() + Assertions.assertTrue(status.isUnsat) + + val proof = hornSolver.proof + Assertions.assertTrue(proof != null) + System.err.println(proof) + } + } +} diff --git a/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3ExprTransformer.java b/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3ExprTransformer.java index e219fa7260..128fa0d8c9 100644 --- a/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3ExprTransformer.java +++ b/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3ExprTransformer.java @@ -51,6 +51,7 @@ import static com.google.common.base.Preconditions.checkState; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static hu.bme.mit.theta.core.utils.ExprUtils.extractFuncAndArgs; final class Z3ExprTransformer { @@ -321,22 +322,6 @@ public Z3ExprTransformer(final Z3TransformationManager transformer, final Contex .build(); } - private static Tuple2, List>> extractFuncAndArgs(final FuncAppExpr expr) { - final Expr func = expr.getFunc(); - final Expr arg = expr.getParam(); - if (func instanceof FuncAppExpr) { - final FuncAppExpr funcApp = (FuncAppExpr) func; - final Tuple2, List>> funcAndArgs = extractFuncAndArgs(funcApp); - final Expr resFunc = funcAndArgs.get1(); - final List> args = funcAndArgs.get2(); - final List> resArgs = ImmutableList.>builder().addAll(args).add(arg) - .build(); - return Tuple2.of(resFunc, resArgs); - } else { - return Tuple2.of(func, ImmutableList.of(arg)); - } - } - //// /* diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java index b64cfc72cf..ceada6f538 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java @@ -19,6 +19,11 @@ import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableList; import com.microsoft.z3.*; +import com.microsoft.z3.BitVecExpr; +import com.microsoft.z3.BoolExpr; +import com.microsoft.z3.Context; +import com.microsoft.z3.FPExpr; +import com.microsoft.z3.FPSort; import hu.bme.mit.theta.common.DispatchTable; import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.dsl.Env; @@ -51,6 +56,8 @@ import java.util.concurrent.ExecutionException; import java.util.stream.Stream; +import static hu.bme.mit.theta.core.utils.ExprUtils.extractFuncAndArgs; + final class Z3ExprTransformer { private static final int CACHE_SIZE = 1000; @@ -316,22 +323,6 @@ public Z3ExprTransformer(final Z3TransformationManager transformer, final Contex .build(); } - private static Tuple2, List>> extractFuncAndArgs(final FuncAppExpr expr) { - final Expr func = expr.getFunc(); - final Expr arg = expr.getParam(); - if (func instanceof FuncAppExpr) { - final FuncAppExpr funcApp = (FuncAppExpr) func; - final Tuple2, List>> funcAndArgs = extractFuncAndArgs(funcApp); - final Expr resFunc = funcAndArgs.get1(); - final List> args = funcAndArgs.get2(); - final List> resArgs = ImmutableList.>builder().addAll(args).add(arg) - .build(); - return Tuple2.of(resFunc, resArgs); - } else { - return Tuple2.of(func, ImmutableList.of(arg)); - } - } - //// /* diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java index f90692771b..c2c53a7c9a 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java @@ -61,6 +61,7 @@ import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; import static hu.bme.mit.theta.core.type.functype.FuncExprs.App; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.UnsafeApp; import static hu.bme.mit.theta.core.utils.TypeUtils.cast; final class Z3Solver implements UCSolver, Solver, HornSolver { @@ -249,12 +250,6 @@ public void close() { z3Context.interrupt(); } - private Expr funcApp(Expr func, Expr param) { - final Expr> funcTyped = (Expr>) func; - final Expr paramTyped = (Expr) param; - return App(funcTyped, paramTyped); - } - private Expr toProofExpr(com.microsoft.z3.Expr expr) { final var args = expr.getArgs(); final var lastArg = args[args.length - 1]; @@ -268,7 +263,7 @@ private Expr toProofExpr(com.microsoft.z3.Expr expr) { final var decl = Const(name, funcType); Expr func = decl.getRef(); for (Expr paramValue : paramValues) { - func = funcApp(func, paramValue); + func = UnsafeApp(func, paramValue); } return (Expr) func; } diff --git a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt index dc6619ff19..6f2dddf842 100644 --- a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt +++ b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt @@ -15,8 +15,6 @@ */ package hu.bme.mit.theta.solver.z3 -import hu.bme.mit.theta.common.OsHelper -import hu.bme.mit.theta.common.logging.NullLogger import hu.bme.mit.theta.core.ParamHolder import hu.bme.mit.theta.core.Relation import hu.bme.mit.theta.core.decl.Decls.Const @@ -29,10 +27,7 @@ import hu.bme.mit.theta.core.type.functype.FuncType import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.type.inttype.IntType import hu.bme.mit.theta.solver.HornSolver -import hu.bme.mit.theta.solver.SolverFactory -import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager -import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException -import org.junit.jupiter.api.* +import org.junit.jupiter.api.Assertions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -41,50 +36,12 @@ import java.util.stream.Stream class Z3HornSolverTest { companion object { - - private var solverInstalled: Boolean = false - private var solverManager: SmtLibSolverManager? = null - private var solverFactory: SolverFactory? = null - - private val SOLVER: String = "z3" - private val VERSION: String = "4.13.0" - @JvmStatic fun solvers(): Stream { return Stream.of( Arguments.of(Z3SolverFactory.getInstance().createHornSolver()), - Arguments.of(solverFactory!!.createHornSolver()), ) } - - @BeforeAll - @JvmStatic - fun init() { - if (OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) { - val home = SmtLibSolverManager.HOME - - solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()) - try { - solverManager!!.install(SOLVER, VERSION, VERSION, null, false) - solverInstalled = true - } catch (e: SmtLibSolverInstallerException) { - e.printStackTrace() - } - - solverFactory = solverManager!!.getSolverFactory(SOLVER, VERSION) - } - } - - @AfterAll - @JvmStatic - fun destroy() { - if (solverInstalled) solverManager!!.uninstall(SOLVER, VERSION) - } - } - - @BeforeEach - fun before() { - Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) } @ParameterizedTest From 047ccbf03694bdab22f49fd1e7a60aba98b8995c Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 8 Jul 2024 16:55:21 +0200 Subject: [PATCH 06/24] Added eldarica --- .../solver/smtlib/SmtLibSolverManager.java | 2 + .../eldarica/EldaricaSmtLibSolverFactory.java | 63 ++++++++ .../EldaricaSmtLibSolverInstaller.java | 121 ++++++++++++++ .../smtlib/impl/eldarica/EldaricaSolver.java | 152 ++++++++++++++++++ .../impl/eldarica/EldaricaSolverBinary.java | 113 +++++++++++++ .../solver/smtlib/solver/SmtLibSolver.java | 8 +- .../solver/smtlib/SmtLibHornSolverTest.kt | 8 +- 7 files changed, 461 insertions(+), 6 deletions(-) create mode 100644 subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java create mode 100644 subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverInstaller.java create mode 100644 subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolver.java create mode 100644 subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolverBinary.java diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java index 3e6ba24e75..9688489718 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java @@ -29,6 +29,7 @@ import hu.bme.mit.theta.solver.smtlib.impl.boolector.BoolectorSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.cvc4.CVC4SmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.cvc5.CVC5SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.impl.eldarica.EldaricaSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.mathsat.MathSATSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.princess.PrincessSmtLibSolverInstaller; @@ -69,6 +70,7 @@ public final class SmtLibSolverManager extends SolverManager { registerInstaller("bitwuzla", BitwuzlaSmtLibSolverInstaller.class); registerInstaller("smtinterpol", SMTInterpolSmtLibSolverInstaller.class); registerInstaller("princess", PrincessSmtLibSolverInstaller.class); + registerInstaller("eldarica", EldaricaSmtLibSolverInstaller.class); registerGenericInstaller("generic", GenericSmtLibSolverInstaller.class); } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java new file mode 100644 index 0000000000..9223dc5e79 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java @@ -0,0 +1,63 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib.impl.eldarica; + +import hu.bme.mit.theta.solver.HornSolver; +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager; + +import java.nio.file.Path; + +public class EldaricaSmtLibSolverFactory extends GenericSmtLibSolverFactory { + + private EldaricaSmtLibSolverFactory(Path solverPath, String[] args) { + super(solverPath, args); + } + + public static EldaricaSmtLibSolverFactory create(Path solverPath, String[] args) { + return new EldaricaSmtLibSolverFactory(solverPath, args); + } + + @Override + public Solver createSolver() { + throw new UnsupportedOperationException("Eldarica factory cannot create conventional solvers"); + } + + @Override + public UCSolver createUCSolver() { + throw new UnsupportedOperationException("Eldarica factory cannot create conventional solvers"); + } + + @Override + public HornSolver createHornSolver() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new EldaricaSolverBinary(solverPath, args); + + return new EldaricaSolver(symbolTable, transformationManager, termTransformer, solverBinary); + } + + @Override + public ItpSolver createItpSolver() { + throw new UnsupportedOperationException("Eldarica factory cannot create conventional solvers"); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverInstaller.java new file mode 100644 index 0000000000..0d7d638fff --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverInstaller.java @@ -0,0 +1,121 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib.impl.eldarica; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.utils.Compress; +import hu.bme.mit.theta.solver.smtlib.utils.SemVer; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static hu.bme.mit.theta.common.OsHelper.Architecture.X64; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.LINUX; + +public class EldaricaSmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + + private final List versions; + + public EldaricaSmtLibSolverInstaller(final Logger logger) { + super(logger); + + versions = new ArrayList<>(); + versions.add(SemVer.VersionDecoder.create(SemVer.of("2.1")) + .addString(LINUX, X64, "zip") + .build() + ); + } + + @Override + protected String getSolverName() { + return "eldarica"; + } + + @Override + protected void installSolver(final Path installDir, final String version) + throws SmtLibSolverInstallerException { + final var semVer = SemVer.of(version); + String archStr = null; + + for (final var versionDecoder : versions) { + if (semVer.compareTo(versionDecoder.getVersion()) >= 0) { + archStr = versionDecoder.getOsArchString(OsHelper.getOs(), OsHelper.getArch()); + break; + } + } + if (archStr == null) { + throw new SmtLibSolverInstallerException( + String.format("%s on operating system %s and architecture %s is not supported", + getSolverName(), OsHelper.getOs(), OsHelper.getArch())); + } + + final var downloadUrl = URI.create(String.format( + "https://github.com/uuverifiers/eldarica/releases/download/v%s/eldarica-bin-%s.%s", + version, version, archStr + )); + + logger.write(Logger.Level.MAINSTEP, "Starting download (%s)...\n", downloadUrl.toString()); + try (final var inputStream = downloadUrl.toURL().openStream()) { + Compress.extract(inputStream, installDir, Compress.CompressionType.ZIP); + installDir.resolve(getSolverBinaryName()).toFile() + .setExecutable(true, true); + } catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + + logger.write(Logger.Level.MAINSTEP, "Download finished\n"); + } + + @Override + protected void uninstallSolver(Path installDir, String version) { + // Default uninstall is suitable + } + + @Override + protected String[] getDefaultSolverArgs(String version) { + return new String[]{"-ssol", "-scex", "-in"}; + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, + final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + final var solverFilePath = solverPath != null ? solverPath + : installDir.resolve(getSolverBinaryName()); + return EldaricaSmtLibSolverFactory.create(solverFilePath, solverArgs); + } + + @Override + public List getSupportedVersions() { + return Arrays.asList("2.1"); + } + + private String getSolverBinaryName() { + switch (OsHelper.getOs()) { + case LINUX: + return "eld"; + default: + throw new AssertionError(); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolver.java new file mode 100644 index 0000000000..8c3ba3de65 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolver.java @@ -0,0 +1,152 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hu.bme.mit.theta.solver.smtlib.impl.eldarica; + +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.solver.ProofNode; +import hu.bme.mit.theta.solver.ProofNode.Builder; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolver; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; + +public class EldaricaSolver extends SmtLibSolver { + private static final Pattern CEX_PATTERN = Pattern.compile("([0-9]+): *(.*)(?=->)->( *([0-9]+)(, *[0-9]+)*)?"); + private static final Pattern CEX_ROOT = Pattern.compile("([0-9]+): *(.*)"); + + private ProofNode proof = null; + + public EldaricaSolver(SmtLibSymbolTable symbolTable, SmtLibTransformationManager transformationManager, SmtLibTermTransformer termTransformer, SmtLibSolverBinary solverBinary) { + super(symbolTable, transformationManager, termTransformer, solverBinary, false, "HORN"); + } + + @Override + public void track(Expr assertion) { + throw new UnsupportedOperationException("Tracking is not supported by Eldarica."); + } + + @Override + public SolverStatus check() { + solverBinary.issueCommand("(check-sat)"); + final var response = solverBinary.readResponse().lines().toList(); + status = response.get(0).equals("sat") ? SolverStatus.SAT : response.get(0).equals("unsat") ? SolverStatus.UNSAT : null; + if(status == SolverStatus.SAT) { + // we have a model + final var sb = new StringBuilder(); + sb.append("("); + for (int i = 1; i < response.size(); i++) { + sb.append(response.get(i)).append("\n"); + } + sb.append(")"); + final var generalResponse = parseResponse(sb.toString()); + if(generalResponse.isSpecific() && generalResponse.asSpecific().isGetModelResponse()) { + model = new SmtLibValuation(symbolTable, transformationManager, termTransformer, + generalResponse.asSpecific().asGetModelResponse().getModel()); + } + } else if (status == SolverStatus.UNSAT) { + // we have a cex (beginning with an empty line) + final Map builderMap = new LinkedHashMap<>(); + final Map> dependencyMap = new LinkedHashMap<>(); + Builder root = null; + for (int i = 2; i < response.size(); i++) { + final var matcher = CEX_PATTERN.matcher(response.get(i)); + int idx = -1; + String term = ""; + List dependencies = null; + if(matcher.matches()) { + idx = Integer.parseInt(matcher.group(1)); + term = matcher.group(2); + if(matcher.group(3) != null) { + dependencies = Arrays.stream(matcher.group(3).split(",")).map(it -> Integer.parseInt(it.trim())).toList(); + } else { + dependencies = List.of(); + } + } else { + final var rootMatcher = CEX_ROOT.matcher(response.get(i)); + if(rootMatcher.matches()) { + idx = Integer.parseInt(rootMatcher.group(1)); + term = rootMatcher.group(2); + dependencies = List.of(); + } + } + if(idx != -1) { + final var expr = termTransformer.toExpr(term, Bool(), new SmtLibModel(Collections.emptyMap())); + final var builder = new ProofNode.Builder(expr); + if(root == null) { + root = builder; + } + builderMap.put(idx, builder); + dependencyMap.put(builder, dependencies); + } + } + dependencyMap.forEach((builder, integers) -> + integers.forEach(integer -> builder.addChild(builderMap.get(integer))) + ); + proof = root.build(); + + } + return status; + } + + @Override + public void push() { + throw new UnsupportedOperationException("Push is not supported."); + } + + @Override + public void pop(int n) { + throw new UnsupportedOperationException("Pop is not supported."); + } + + @Override + public Collection> getUnsatCore() { + throw new UnsupportedOperationException("Eldarica cannot return unsat cores"); + } + + @Override + protected void issueGeneralCommand(String command) { + solverBinary.issueCommand(command); + } + + @Override + public ProofNode getProof() { + checkState(proof != null, "Proof cannot be null! Did you call check()?"); + return proof; + } + + @Override + public void clearState() { + super.clearState(); + proof = null; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolverBinary.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolverBinary.java new file mode 100644 index 0000000000..66cb3f7b6e --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolverBinary.java @@ -0,0 +1,113 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hu.bme.mit.theta.solver.smtlib.impl.eldarica; + +import com.zaxxer.nuprocess.NuAbstractProcessHandler; +import com.zaxxer.nuprocess.NuProcessBuilder; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class EldaricaSolverBinary implements SmtLibSolverBinary { + + private final List commands; + private final Path solverPath; + private final String[] args; + + public EldaricaSolverBinary(Path solverPath, String[] args) { + this.solverPath = solverPath; + this.args = args; + commands = new ArrayList<>(); + } + + @Override + public void issueCommand(String command) { + commands.add(command); + } + + @Override + public String readResponse() { + try { + final File file = File.createTempFile("input", ".smt2"); + file.deleteOnExit(); + + try(BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file))) { + for (String command : commands) { + bufferedWriter.write(command); + bufferedWriter.newLine(); + } + } + final var processCmd = new ArrayList(); + processCmd.add(solverPath.toAbsolutePath().toString()); + processCmd.addAll(Arrays.asList(args)); + processCmd.add(file.getAbsolutePath()); + + final var solverProcessBuilder = new NuProcessBuilder(processCmd); + final var handler = new ProcessHandler(); + solverProcessBuilder.setProcessListener(handler); + + final var proc = solverProcessBuilder.start(); + + proc.waitFor(0, TimeUnit.SECONDS); + + return handler.getResponse(); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public void close() throws Exception { + // no-op + } + + private static class ProcessHandler extends NuAbstractProcessHandler { + private final StringBuilder response = new StringBuilder(); + + @Override + public void onStdout(ByteBuffer buffer, boolean closed) { + if (!closed) { + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + final var str = new String(bytes); + response.append(str); + } + } + + @Override + public void onStderr(ByteBuffer buffer, boolean closed) { + if (!closed) { + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + System.err.println(new String(bytes)); + } + } + + public String getResponse() { + return response.toString(); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java index e845d3455e..c0b81ba241 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java @@ -90,9 +90,9 @@ public class SmtLibSolver implements UCSolver, Solver, HornSolver { private final boolean unsatCoreEnabled; private int labelNum = 0; - private Valuation model; - private Collection> unsatCore; - private SolverStatus status; + protected Valuation model; + protected Collection> unsatCore; + protected SolverStatus status; public SmtLibSolver( final SmtLibSymbolTable symbolTable, @@ -315,7 +315,7 @@ protected void clearState() { unsatCore = null; } - protected final void issueGeneralCommand(String command) { + protected void issueGeneralCommand(String command) { solverBinary.issueCommand(command); var res = parseResponse(solverBinary.readResponse()); if (res.isError()) { diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt index 6a897a3400..b525c3107c 100644 --- a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt @@ -41,7 +41,11 @@ class SmtLibHornSolverTest { private var solverManager: SmtLibSolverManager? = null private val solverFactories: MutableMap, SolverFactory> = LinkedHashMap() - private val SOLVERS: List> = listOf(Pair("z3", "4.13.0"), Pair("z3", "4.12.6")) + private val SOLVERS: List> = listOf( + Pair("z3", "4.13.0"), + Pair("z3", "4.12.6"), + Pair("eldarica", "2.1") + ) @JvmStatic fun solvers(): List { @@ -111,7 +115,7 @@ class SmtLibHornSolverTest { Assertions.assertTrue(model.containsKey(inv.constDecl)) Assertions.assertTrue(model.containsKey(init.constDecl)) - val checkerSolver = solverFactory!!.createSolver() + val checkerSolver = solverFactories.filter { it.key.first.equals("z3") }.values.first().createSolver() checkerSolver.use { val p0 = Const("p0", Int()) val p1 = Const("p1", Int()) From 29c4d52e05459cceb847e01e8576c16a63070e3c Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 8 Jul 2024 21:15:02 +0200 Subject: [PATCH 07/24] Moved to more robust type parsing --- .../solver/smtlib/solver/SmtLibSolver.java | 36 +++++++++++++------ .../solver/parser/GetProofResponse.java | 11 +++--- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java index c0b81ba241..c7dbf49b1c 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java @@ -15,7 +15,6 @@ */ package hu.bme.mit.theta.solver.smtlib.solver; -import com.google.common.base.Function; import com.microsoft.z3.Z3Exception; import hu.bme.mit.theta.core.decl.ConstDecl; import hu.bme.mit.theta.core.model.Valuation; @@ -25,6 +24,7 @@ import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.anytype.RefExpr; import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.type.bvtype.BvExprs; import hu.bme.mit.theta.core.type.functype.FuncAppExpr; import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.core.utils.ExprUtils; @@ -38,6 +38,7 @@ import hu.bme.mit.theta.solver.impl.StackImpl; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.SortContext; import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; @@ -66,9 +67,11 @@ import static com.google.common.base.Preconditions.checkState; import static hu.bme.mit.theta.core.decl.Decls.Const; +import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static hu.bme.mit.theta.core.type.rattype.RatExprs.Rat; import static hu.bme.mit.theta.core.utils.ExprUtils.extractFuncAndArgs; import static hu.bme.mit.theta.core.utils.TypeUtils.cast; @@ -337,6 +340,25 @@ protected final GeneralResponse parseResponse(final String response) { } } + + public static Type transformSort(final SortContext ctx) { + final String name = ctx.identifier().symbol().getText(); + return switch (name) { + case "Int" -> Int(); + case "Bool" -> Bool(); + case "Real" -> Rat(); + case "BitVec" -> { + assert ctx.identifier().index().size() == 1; + yield BvExprs.BvType(Integer.parseInt(ctx.identifier().index().get(0).getText())); + } + case "Array" -> { + assert ctx.sort().size() == 2; + yield Array(transformSort(ctx.sort().get(0)), transformSort(ctx.sort().get(1))); + } + default -> throw new UnsupportedOperationException(); + }; + } + @Override public ProofNode getProof() { solverBinary.issueCommand("(get-proof)"); @@ -347,15 +369,9 @@ public ProofNode getProof() { } else if (res.isSpecific()) { final GetProofResponse getModelResponse = res.asSpecific().asGetProofResponse(); getModelResponse.getFunDeclarations().forEach((name, def) -> { - final Function stringToType = (String it) -> switch (it.toLowerCase()) { - case "int" -> Int(); - case "bool" -> Bool(); - default -> - throw new UnsupportedOperationException("Currently not supporting type " + it); - }; - var type = stringToType.apply(def.get2()); - for (String s : def.get1()) { - type = FuncType.of(stringToType.apply(s), type); + var type = transformSort(def.get2()); + for (SortContext s : def.get1()) { + type = FuncType.of(transformSort(s), type); } symbolTable.put(Const(name, type), name, def.get3()); }); diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java index 4d46feb071..67643c6779 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetProofResponse.java @@ -18,6 +18,7 @@ import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.Tuple3; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Proof_responseContext; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.SortContext; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.misc.Interval; @@ -28,9 +29,9 @@ public class GetProofResponse extends SpecificResponse { private final String proofTerm; - private final Map, String, String>> funDeclarations; // name -> [inSorts, outSort, declaration] + private final Map, SortContext, String>> funDeclarations; // name -> [inSorts, outSort, declaration] - private GetProofResponse(String proofNode, Map, String, String>> funDeclarations) { + private GetProofResponse(String proofNode, Map, SortContext, String>> funDeclarations) { this.proofTerm = proofNode; this.funDeclarations = funDeclarations; } @@ -41,8 +42,8 @@ public static GetProofResponse fromContext(final Proof_responseContext ctx) { ctx.proof_funs().stream().map(it -> Tuple2.of( extractString(it.symbol()), Tuple3.of( - it.in.stream().map(GetProofResponse::extractString).toList(), - extractString(it.out), + it.in, + it.out, extractString(it) ))).collect(Collectors.toMap(Tuple2::get1, Tuple2::get2))); } @@ -56,7 +57,7 @@ public String getProof() { return proofTerm; } - public Map, String, String>> getFunDeclarations() { + public Map, SortContext, String>> getFunDeclarations() { return funDeclarations; } } From d06ab1079cb757de4fcb375e9e2d87fada1d406c Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 8 Jul 2024 21:25:12 +0200 Subject: [PATCH 08/24] Moved solver to generic class --- .../impl/eldarica/EldaricaSmtLibSolverFactory.java | 6 ++++-- .../eldarica/EldaricaSmtLibSolverInstaller.java | 2 +- .../GenericSmtLibHornSolver.java} | 13 +++++++------ .../GenericSmtLibOneshotSolverBinary.java} | 10 +++++++--- 4 files changed, 19 insertions(+), 12 deletions(-) rename subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/{eldarica/EldaricaSolver.java => generic/GenericSmtLibHornSolver.java} (91%) rename subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/{eldarica/EldaricaSolverBinary.java => generic/GenericSmtLibOneshotSolverBinary.java} (89%) diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java index 9223dc5e79..d1f5545fb2 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java @@ -19,6 +19,8 @@ import hu.bme.mit.theta.solver.ItpSolver; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibHornSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibOneshotSolverBinary; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTermTransformer; @@ -51,9 +53,9 @@ public HornSolver createHornSolver() { final var symbolTable = new GenericSmtLibSymbolTable(); final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); - final var solverBinary = new EldaricaSolverBinary(solverPath, args); + final var solverBinary = new GenericSmtLibOneshotSolverBinary(solverPath, args); - return new EldaricaSolver(symbolTable, transformationManager, termTransformer, solverBinary); + return new GenericSmtLibHornSolver(symbolTable, transformationManager, termTransformer, solverBinary); } @Override diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverInstaller.java index 0d7d638fff..adbfa821c7 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverInstaller.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverInstaller.java @@ -94,7 +94,7 @@ protected void uninstallSolver(Path installDir, String version) { @Override protected String[] getDefaultSolverArgs(String version) { - return new String[]{"-ssol", "-scex", "-in"}; + return new String[]{"-ssol", "-scex"}; } @Override diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java similarity index 91% rename from subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolver.java rename to subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java index 8c3ba3de65..dec9da9c84 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package hu.bme.mit.theta.solver.smtlib.impl.eldarica; +package hu.bme.mit.theta.solver.smtlib.impl.generic; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.booltype.BoolType; @@ -39,20 +39,21 @@ import static com.google.common.base.Preconditions.checkState; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; -public class EldaricaSolver extends SmtLibSolver { +public class GenericSmtLibHornSolver extends SmtLibSolver { private static final Pattern CEX_PATTERN = Pattern.compile("([0-9]+): *(.*)(?=->)->( *([0-9]+)(, *[0-9]+)*)?"); private static final Pattern CEX_ROOT = Pattern.compile("([0-9]+): *(.*)"); private ProofNode proof = null; - public EldaricaSolver(SmtLibSymbolTable symbolTable, SmtLibTransformationManager transformationManager, SmtLibTermTransformer termTransformer, SmtLibSolverBinary solverBinary) { + public GenericSmtLibHornSolver(SmtLibSymbolTable symbolTable, SmtLibTransformationManager transformationManager, SmtLibTermTransformer termTransformer, SmtLibSolverBinary solverBinary) { super(symbolTable, transformationManager, termTransformer, solverBinary, false, "HORN"); } @Override public void track(Expr assertion) { - throw new UnsupportedOperationException("Tracking is not supported by Eldarica."); + throw new UnsupportedOperationException("Tracking is not supported by this solver."); } @Override @@ -102,7 +103,7 @@ public SolverStatus check() { if(idx != -1) { final var expr = termTransformer.toExpr(term, Bool(), new SmtLibModel(Collections.emptyMap())); final var builder = new ProofNode.Builder(expr); - if(root == null) { + if (root == null && expr.equals(False())) { root = builder; } builderMap.put(idx, builder); @@ -130,7 +131,7 @@ public void pop(int n) { @Override public Collection> getUnsatCore() { - throw new UnsupportedOperationException("Eldarica cannot return unsat cores"); + throw new UnsupportedOperationException("This solver cannot return unsat cores"); } @Override diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolverBinary.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibOneshotSolverBinary.java similarity index 89% rename from subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolverBinary.java rename to subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibOneshotSolverBinary.java index 66cb3f7b6e..c8c872d8ed 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSolverBinary.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibOneshotSolverBinary.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package hu.bme.mit.theta.solver.smtlib.impl.eldarica; +package hu.bme.mit.theta.solver.smtlib.impl.generic; import com.zaxxer.nuprocess.NuAbstractProcessHandler; import com.zaxxer.nuprocess.NuProcessBuilder; @@ -31,13 +31,17 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class EldaricaSolverBinary implements SmtLibSolverBinary { +/** + * Instead of an interactive solver, these binaries can only work with an input file. + * Therefore, we keep track of commands, and only execute them when readResponse() is called. + */ +public class GenericSmtLibOneshotSolverBinary implements SmtLibSolverBinary { private final List commands; private final Path solverPath; private final String[] args; - public EldaricaSolverBinary(Path solverPath, String[] args) { + public GenericSmtLibOneshotSolverBinary(Path solverPath, String[] args) { this.solverPath = solverPath; this.args = args; commands = new ArrayList<>(); From d2a8aa81686afcfb13581ef93a53824598738dd7 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 8 Jul 2024 22:41:52 +0200 Subject: [PATCH 09/24] Added golem, fixed some bugs --- .../theta/core/type/functype/FuncExprs.java | 8 +- .../solver/smtlib/SmtLibSolverManager.java | 2 + .../impl/generic/GenericSmtLibHornSolver.java | 8 +- .../impl/golem/GolemSmtLibSolverFactory.java | 65 ++++++++++ .../golem/GolemSmtLibSolverInstaller.java | 121 ++++++++++++++++++ .../solver/smtlib/solver/SmtLibSolver.java | 3 +- .../theta/solver/smtlib/utils/Compress.java | 30 +++-- .../solver/smtlib/SmtLibHornSolverTest.kt | 5 +- 8 files changed, 224 insertions(+), 18 deletions(-) create mode 100644 subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java create mode 100644 subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverInstaller.java diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java index c6fbfdf480..7182c6f8c4 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java @@ -47,8 +47,8 @@ public static FuncAppExpr FuncAppExpr App( final Expr> func, final Expr paramHead, final List paramTail) { if (!paramTail.isEmpty()) { - final var newParamHead = paramTail.get(paramTail.size() - 1); - final var newParamTail = paramTail.subList(0, paramTail.size() - 1); + final var newParamHead = paramTail.get(0); + final var newParamTail = paramTail.subList(1, paramTail.size()); return App(App(func, newParamHead, newParamTail), paramHead); } else { return App(func, paramHead); @@ -58,8 +58,8 @@ private static FuncAppExpr FuncAppExpr App( final Expr> func, final List params) { checkArgument(!params.isEmpty()); - final var paramHead = params.get(params.size() - 1); - final var paramTail = params.subList(0, params.size() - 1); + final var paramHead = params.get(0); + final var paramTail = params.subList(1, params.size()); return App(func, paramHead, paramTail); } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java index 9688489718..8f2499c012 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java @@ -31,6 +31,7 @@ import hu.bme.mit.theta.solver.smtlib.impl.cvc5.CVC5SmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.eldarica.EldaricaSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.impl.golem.GolemSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.mathsat.MathSATSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.princess.PrincessSmtLibSolverInstaller; import hu.bme.mit.theta.solver.smtlib.impl.smtinterpol.SMTInterpolSmtLibSolverInstaller; @@ -71,6 +72,7 @@ public final class SmtLibSolverManager extends SolverManager { registerInstaller("smtinterpol", SMTInterpolSmtLibSolverInstaller.class); registerInstaller("princess", PrincessSmtLibSolverInstaller.class); registerInstaller("eldarica", EldaricaSmtLibSolverInstaller.class); + registerInstaller("golem", GolemSmtLibSolverInstaller.class); registerGenericInstaller("generic", GenericSmtLibSolverInstaller.class); } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java index dec9da9c84..1b33cc8264 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java @@ -42,8 +42,8 @@ import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; public class GenericSmtLibHornSolver extends SmtLibSolver { - private static final Pattern CEX_PATTERN = Pattern.compile("([0-9]+): *(.*)(?=->)->( *([0-9]+)(, *[0-9]+)*)?"); - private static final Pattern CEX_ROOT = Pattern.compile("([0-9]+): *(.*)"); + private static final Pattern CEX_PATTERN = Pattern.compile("([0-9]+):\s*(.*)(?=->)->(\s*([0-9]+)(,?\s+[0-9]+)*)?"); + private static final Pattern CEX_ROOT = Pattern.compile("([0-9]+):\s*(.*)"); private ProofNode proof = null; @@ -79,7 +79,7 @@ public SolverStatus check() { final Map builderMap = new LinkedHashMap<>(); final Map> dependencyMap = new LinkedHashMap<>(); Builder root = null; - for (int i = 2; i < response.size(); i++) { + for (int i = 1; i < response.size(); i++) { final var matcher = CEX_PATTERN.matcher(response.get(i)); int idx = -1; String term = ""; @@ -88,7 +88,7 @@ public SolverStatus check() { idx = Integer.parseInt(matcher.group(1)); term = matcher.group(2); if(matcher.group(3) != null) { - dependencies = Arrays.stream(matcher.group(3).split(",")).map(it -> Integer.parseInt(it.trim())).toList(); + dependencies = Arrays.stream(matcher.group(3).split(",?\s+")).filter(it -> !it.isEmpty()).map(it -> Integer.parseInt(it.trim())).toList(); } else { dependencies = List.of(); } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java new file mode 100644 index 0000000000..6eb9aec300 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib.impl.golem; + +import hu.bme.mit.theta.solver.HornSolver; +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibHornSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibOneshotSolverBinary; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager; + +import java.nio.file.Path; + +public class GolemSmtLibSolverFactory extends GenericSmtLibSolverFactory { + + private GolemSmtLibSolverFactory(Path solverPath, String[] args) { + super(solverPath, args); + } + + public static GolemSmtLibSolverFactory create(Path solverPath, String[] args) { + return new GolemSmtLibSolverFactory(solverPath, args); + } + + @Override + public Solver createSolver() { + throw new UnsupportedOperationException("Golem factory cannot create conventional solvers"); + } + + @Override + public UCSolver createUCSolver() { + throw new UnsupportedOperationException("Golem factory cannot create conventional solvers"); + } + + @Override + public HornSolver createHornSolver() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibOneshotSolverBinary(solverPath, args); + + return new GenericSmtLibHornSolver(symbolTable, transformationManager, termTransformer, solverBinary); + } + + @Override + public ItpSolver createItpSolver() { + throw new UnsupportedOperationException("Golem factory cannot create conventional solvers"); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverInstaller.java new file mode 100644 index 0000000000..028cecf44a --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverInstaller.java @@ -0,0 +1,121 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib.impl.golem; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.utils.Compress; +import hu.bme.mit.theta.solver.smtlib.utils.Compress.CompressionType; +import hu.bme.mit.theta.solver.smtlib.utils.SemVer; + +import java.net.URI; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static hu.bme.mit.theta.common.OsHelper.Architecture.X64; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.LINUX; + +public class GolemSmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + + private final List versions; + + public GolemSmtLibSolverInstaller(final Logger logger) { + super(logger); + + versions = new ArrayList<>(); + versions.add(SemVer.VersionDecoder.create(SemVer.of("0.5.0")) + .addString(LINUX, X64, "x64-linux.tar.bz2") + .build() + ); + } + + @Override + protected String getSolverName() { + return "golem"; + } + + @Override + protected void installSolver(final Path installDir, final String version) + throws SmtLibSolverInstallerException { + final var semVer = SemVer.of(version); + String archStr = null; + + for (final var versionDecoder : versions) { + if (semVer.compareTo(versionDecoder.getVersion()) >= 0) { + archStr = versionDecoder.getOsArchString(OsHelper.getOs(), OsHelper.getArch()); + break; + } + } + if (archStr == null) { + throw new SmtLibSolverInstallerException( + String.format("%s on operating system %s and architecture %s is not supported", + getSolverName(), OsHelper.getOs(), OsHelper.getArch())); + } + + final var downloadUrl = URI.create(String.format( + "https://github.com/usi-verification-and-security/golem/releases/download/v%s/golem-%s-%s", + version, version, archStr + )); + + logger.write(Logger.Level.MAINSTEP, "Starting download (%s)...\n", downloadUrl.toString()); + try (final var inputStream = downloadUrl.toURL().openStream()) { + Compress.extractTarbomb(inputStream, installDir, CompressionType.TARBZ2); + installDir.resolve(getSolverBinaryName()).toFile() + .setExecutable(true, true); + } catch (Exception e) { + throw new SmtLibSolverInstallerException(e); + } + + logger.write(Logger.Level.MAINSTEP, "Download finished\n"); + } + + @Override + protected void uninstallSolver(Path installDir, String version) { + // Default uninstall is suitable + } + + @Override + protected String[] getDefaultSolverArgs(String version) { + return new String[]{"--print-witness"}; + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, + final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + final var solverFilePath = solverPath != null ? solverPath + : installDir.resolve(getSolverBinaryName()); + return GolemSmtLibSolverFactory.create(solverFilePath, solverArgs); + } + + @Override + public List getSupportedVersions() { + return Arrays.asList("0.5.0"); + } + + private String getSolverBinaryName() { + switch (OsHelper.getOs()) { + case LINUX: + return "golem"; + default: + throw new AssertionError(); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java index c7dbf49b1c..afe66be36e 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java @@ -64,6 +64,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkState; import static hu.bme.mit.theta.core.decl.Decls.Const; @@ -139,7 +140,7 @@ public void add(Expr assertion) { } public void add(final Expr assertion, final String term) { - final var consts = ExprUtils.getConstants(assertion); + final var consts = ExprUtils.getConstants(assertion).stream().filter(symbolTable::definesConst).collect(Collectors.toSet()); consts.removeAll(declarationStack.toCollection()); declarationStack.add(consts); diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/Compress.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/Compress.java index 0a5647d63c..bb385b9caf 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/Compress.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/Compress.java @@ -19,6 +19,7 @@ import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; import java.io.BufferedInputStream; @@ -33,26 +34,39 @@ private Compress() { } public enum CompressionType { - ZIP, TARGZ + ZIP, TARGZ, TARBZ2 } - public static void extract(final InputStream inputStream, final Path extractDir, - final CompressionType compressionType) throws IOException { + public static void extract(final InputStream inputStream, final Path extractDir, final CompressionType compressionType) throws IOException { + extract(inputStream, extractDir, compressionType, false); + } + + public static void extractTarbomb(final InputStream inputStream, final Path extractDir, final CompressionType compressionType) throws IOException { + extract(inputStream, extractDir, compressionType, true); + } + + private static void extract(final InputStream inputStream, final Path extractDir, + final CompressionType compressionType, final boolean tarbomb) throws IOException { switch (compressionType) { case ZIP: - extract(new ZipArchiveInputStream(inputStream), extractDir); + extract(new ZipArchiveInputStream(inputStream), extractDir, tarbomb); break; case TARGZ: extract(new TarArchiveInputStream( new GzipCompressorInputStream(new BufferedInputStream(inputStream))), - extractDir); + extractDir, tarbomb); + break; + case TARBZ2: + extract(new TarArchiveInputStream( + new BZip2CompressorInputStream(new BufferedInputStream(inputStream))), + extractDir, tarbomb); break; default: throw new AssertionError(); } } - private static void extract(final ArchiveInputStream archiveInputStream, final Path extractDir) + private static void extract(final ArchiveInputStream archiveInputStream, final Path extractDir, final boolean tarbomb) throws IOException { for (ArchiveEntry entry = archiveInputStream.getNextEntry(); entry != null; entry = archiveInputStream.getNextEntry()) { @@ -60,12 +74,12 @@ private static void extract(final ArchiveInputStream archiveInputStream, final P if (entry.isDirectory()) { if (entryPath.getNameCount() > 1) { final var entryResolvedPath = extractDir.resolve( - entryPath.subpath(1, entryPath.getNameCount())); + entryPath.subpath(tarbomb ? 0 : 1, entryPath.getNameCount())); Files.createDirectories(entryResolvedPath); } } else { final var entryResolvedPath = extractDir.resolve( - entryPath.subpath(1, entryPath.getNameCount())); + entryPath.subpath(tarbomb ? 0 : 1, entryPath.getNameCount())); Files.createDirectories(entryResolvedPath.getParent()); Files.copy(archiveInputStream, entryResolvedPath); } diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt index b525c3107c..77102d1930 100644 --- a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt @@ -44,7 +44,8 @@ class SmtLibHornSolverTest { private val SOLVERS: List> = listOf( Pair("z3", "4.13.0"), Pair("z3", "4.12.6"), - Pair("eldarica", "2.1") + Pair("eldarica", "2.1"), + Pair("golem", "0.5.0"), ) @JvmStatic @@ -128,6 +129,7 @@ class SmtLibHornSolverTest { checkerSolver.add(Lt(p1.ref, Int(0))) Assertions.assertTrue(checkerSolver.check().isUnsat) + System.err.println(model.toMap()) } } } @@ -156,6 +158,7 @@ class SmtLibHornSolverTest { val proof = hornSolver.proof Assertions.assertTrue(proof != null) + System.err.println(proof) } } From 07f64851a6466af5cd1994e2182275046facefbf Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 8 Jul 2024 22:51:07 +0200 Subject: [PATCH 10/24] Fix oversight --- .../src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliParseTest.kt | 2 ++ .../src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliParseTest.kt b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliParseTest.kt index c1e5f7855d..a6aa6b8bb5 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliParseTest.kt +++ b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliParseTest.kt @@ -15,6 +15,7 @@ */ package hu.bme.mit.theta.xcfa.cli +import com.google.common.base.Preconditions.checkState import hu.bme.mit.theta.frontend.chc.ChcFrontend import hu.bme.mit.theta.xcfa.cli.XcfaCli.Companion.main import org.junit.jupiter.params.ParameterizedTest @@ -263,6 +264,7 @@ class XcfaCliParseTest { "--debug" )) val xcfaC = temp.resolve("xcfa.c").toFile() + checkState(xcfaC.exists(), "File does not exist: $xcfaC") main(arrayOf( "--input-type", "C", "--input", xcfaC.absolutePath.toString(), diff --git a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt index dc4d11d2b1..23c1f29e3c 100644 --- a/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt +++ b/subprojects/xcfa/xcfa2chc/src/main/java/hu/bme/mit/theta/xcfa2chc/Xcfa2Chc.kt @@ -80,7 +80,7 @@ fun List.toSMT2(): String { val symbolTable = GenericSmtLibSymbolTable() val transformationManager = GenericSmtLibTransformationManager(symbolTable) val terms = flatMap { it.rules.map { "(assert " + transformationManager.toTerm(it.toExpr()) + ")" } } - val decls = map { symbolTable.getDeclaration(it.constDecl) } + val decls = filter { symbolTable.definesConst(it.constDecl) }.map { symbolTable.getDeclaration(it.constDecl) } return """ ; generated by Theta From c45212d00d5ef0bc203b396f2e3a33cfff32f3b9 Mon Sep 17 00:00:00 2001 From: "ThetaBotMaintainer[bot]" <139346997+ThetaBotMaintainer[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 20:52:44 +0000 Subject: [PATCH 11/24] Reformatted code --- .../impl/generic/GenericSmtLibHornSolver.java | 16 ++++++++-------- .../GenericSmtLibOneshotSolverBinary.java | 2 +- .../impl/golem/GolemSmtLibSolverFactory.java | 6 +++--- .../theta/solver/smtlib/SmtLibHornSolverTest.kt | 1 + .../bme/mit/theta/solver/z3/Z3HornSolverTest.kt | 1 + 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java index 1b33cc8264..96cbecf5cb 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java @@ -61,7 +61,7 @@ public SolverStatus check() { solverBinary.issueCommand("(check-sat)"); final var response = solverBinary.readResponse().lines().toList(); status = response.get(0).equals("sat") ? SolverStatus.SAT : response.get(0).equals("unsat") ? SolverStatus.UNSAT : null; - if(status == SolverStatus.SAT) { + if (status == SolverStatus.SAT) { // we have a model final var sb = new StringBuilder(); sb.append("("); @@ -70,7 +70,7 @@ public SolverStatus check() { } sb.append(")"); final var generalResponse = parseResponse(sb.toString()); - if(generalResponse.isSpecific() && generalResponse.asSpecific().isGetModelResponse()) { + if (generalResponse.isSpecific() && generalResponse.asSpecific().isGetModelResponse()) { model = new SmtLibValuation(symbolTable, transformationManager, termTransformer, generalResponse.asSpecific().asGetModelResponse().getModel()); } @@ -84,23 +84,23 @@ public SolverStatus check() { int idx = -1; String term = ""; List dependencies = null; - if(matcher.matches()) { + if (matcher.matches()) { idx = Integer.parseInt(matcher.group(1)); term = matcher.group(2); - if(matcher.group(3) != null) { + if (matcher.group(3) != null) { dependencies = Arrays.stream(matcher.group(3).split(",?\s+")).filter(it -> !it.isEmpty()).map(it -> Integer.parseInt(it.trim())).toList(); } else { dependencies = List.of(); } } else { final var rootMatcher = CEX_ROOT.matcher(response.get(i)); - if(rootMatcher.matches()) { + if (rootMatcher.matches()) { idx = Integer.parseInt(rootMatcher.group(1)); term = rootMatcher.group(2); dependencies = List.of(); } } - if(idx != -1) { + if (idx != -1) { final var expr = termTransformer.toExpr(term, Bool(), new SmtLibModel(Collections.emptyMap())); final var builder = new ProofNode.Builder(expr); if (root == null && expr.equals(False())) { @@ -111,8 +111,8 @@ public SolverStatus check() { } } dependencyMap.forEach((builder, integers) -> - integers.forEach(integer -> builder.addChild(builderMap.get(integer))) - ); + integers.forEach(integer -> builder.addChild(builderMap.get(integer))) + ); proof = root.build(); } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibOneshotSolverBinary.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibOneshotSolverBinary.java index c8c872d8ed..808d5b309b 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibOneshotSolverBinary.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibOneshotSolverBinary.java @@ -58,7 +58,7 @@ public String readResponse() { final File file = File.createTempFile("input", ".smt2"); file.deleteOnExit(); - try(BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file))) { + try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file))) { for (String command : commands) { bufferedWriter.write(command); bufferedWriter.newLine(); diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java index 6eb9aec300..a908b1f7c9 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java @@ -1,12 +1,12 @@ /* * Copyright 2024 Budapest University of Technology and Economics - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt index 77102d1930..73d869a91f 100644 --- a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt @@ -38,6 +38,7 @@ import org.junit.jupiter.params.provider.MethodSource class SmtLibHornSolverTest { companion object { + private var solverManager: SmtLibSolverManager? = null private val solverFactories: MutableMap, SolverFactory> = LinkedHashMap() diff --git a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt index 6f2dddf842..24bb990839 100644 --- a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt +++ b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt @@ -36,6 +36,7 @@ import java.util.stream.Stream class Z3HornSolverTest { companion object { + @JvmStatic fun solvers(): Stream { return Stream.of( From 52875a337a9124519bb6048e780f3f0f925788b4 Mon Sep 17 00:00:00 2001 From: "ThetaBotMaintainer[bot]" <139346997+ThetaBotMaintainer[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 20:54:43 +0000 Subject: [PATCH 12/24] Version bump --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 710b02b927..7b3b832b9b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,7 +28,7 @@ buildscript { allprojects { group = "hu.bme.mit.theta" - version = "5.3.0" + version = "5.4.0" apply(from = rootDir.resolve("gradle/shared-with-buildSrc/mirrors.gradle.kts")) } From ec8aa7acfb00276c7a10c920f0a7ece5a61f6131 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 8 Jul 2024 23:24:48 +0200 Subject: [PATCH 13/24] Fixed order of function args --- .../src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java | 2 +- .../src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java index 6873033f60..db4ee0b410 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java @@ -518,7 +518,7 @@ public static Tuple2, List>> extractFuncAndArgs(final FuncAppExp final Tuple2, List>> funcAndArgs = extractFuncAndArgs(funcApp); final Expr resFunc = funcAndArgs.get1(); final List> args = funcAndArgs.get2(); - final List> resArgs = ImmutableList.>builder().addAll(args).add(arg) + final List> resArgs = ImmutableList.>builder().add(arg).addAll(args) .build(); return Tuple2.of(resFunc, resArgs); } else { diff --git a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt index 67b63f51df..1324342474 100644 --- a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt +++ b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt @@ -28,6 +28,7 @@ import hu.bme.mit.theta.core.type.booltype.BoolExprs.* import hu.bme.mit.theta.core.type.inttype.IntExprs import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.type.inttype.IntType +import hu.bme.mit.theta.solver.smtlib.impl.z3.Z3SmtLibSolverFactory import org.junit.jupiter.api.Test private val iParamLut = LinkedHashMap>() From 9c17003c7454de9b52dc1307e62f70d6bb5bab74 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Mon, 8 Jul 2024 23:58:59 +0200 Subject: [PATCH 14/24] Added assertions to test --- subprojects/xcfa/xcfa2chc/build.gradle.kts | 2 + .../hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt | 102 ++++++++++++++---- 2 files changed, 84 insertions(+), 20 deletions(-) diff --git a/subprojects/xcfa/xcfa2chc/build.gradle.kts b/subprojects/xcfa/xcfa2chc/build.gradle.kts index 6bd82ed776..59d9d068bd 100644 --- a/subprojects/xcfa/xcfa2chc/build.gradle.kts +++ b/subprojects/xcfa/xcfa2chc/build.gradle.kts @@ -22,4 +22,6 @@ dependencies { implementation(project(":theta-core")) implementation(project(":theta-xcfa")) implementation(project(":theta-solver-smtlib")) + testImplementation(project(":theta-solver-z3")) + testImplementation(project(":theta-solver")) } diff --git a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt index 1324342474..01a0f2003a 100644 --- a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt +++ b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt @@ -16,6 +16,8 @@ package hu.bme.mit.theta.xcfa2chc +import hu.bme.mit.theta.common.OsHelper +import hu.bme.mit.theta.common.logging.NullLogger import hu.bme.mit.theta.core.ParamHolder import hu.bme.mit.theta.core.Relation import hu.bme.mit.theta.core.decl.Decls @@ -28,8 +30,14 @@ import hu.bme.mit.theta.core.type.booltype.BoolExprs.* import hu.bme.mit.theta.core.type.inttype.IntExprs import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.type.inttype.IntType -import hu.bme.mit.theta.solver.smtlib.impl.z3.Z3SmtLibSolverFactory -import org.junit.jupiter.api.Test +import hu.bme.mit.theta.solver.SolverFactory +import hu.bme.mit.theta.solver.SolverStatus +import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException +import org.junit.jupiter.api.* +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource private val iParamLut = LinkedHashMap>() private fun iP(name: String) = iParamLut.getOrPut(name) { Decls.Param(name, IntExprs.Int()) } @@ -37,8 +45,62 @@ private fun iP(name: String) = iParamLut.getOrPut(name) { Decls.Param(name, IntE class TestChcUtils { - @Test - fun testPetersonManualCounting() { + companion object { + + private var solverManager: SmtLibSolverManager? = null + private val solverFactories: MutableMap, SolverFactory> = LinkedHashMap() + + private val SOLVERS: List> = listOf( + Pair("z3", "4.12.6"), + Pair("z3", "4.13.0"), + ) + + @JvmStatic + fun solvers(): List { + return solverFactories.map { Arguments.of(it.key, it.value) } + } + + @BeforeAll + @JvmStatic + fun init() { + if (OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) { + val home = SmtLibSolverManager.HOME + + solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()) + for ((solver, version) in SOLVERS) { + + try { + solverManager!!.install(solver, version, version, null, false) + } catch (e: SmtLibSolverInstallerException) { + e.printStackTrace() + } + + solverFactories.put(Pair(solver, version), solverManager!!.getSolverFactory(solver, version)) + } + } + } + + @AfterAll + @JvmStatic + fun destroy() { + for ((solver, version) in SOLVERS) { + try { + solverManager!!.uninstall(solver, version) + } catch (e: SmtLibSolverInstallerException) { + e.printStackTrace() + } + } + } + } + + @BeforeEach + fun before() { + Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testPetersonManualCounting(name: Pair, solverFactory: SolverFactory) { val i2i = ArrayType.of(Int(), Int()) val pI = ParamHolder(Int()) @@ -237,7 +299,7 @@ class TestChcUtils { Eq(Add(Read(co, eid7), Int(1)), Read(co, eid8)) + // co-after is eid8 Eq(pI[3], cnt) + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt_old).expr + // previous loc Eq(Add(eid, Int(8)), eid2) + // eid update Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) @@ -327,12 +389,18 @@ class TestChcUtils { !(T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with Eq(cnt, Int(1))) !(T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with Eq(cnt, Int(1))) - val expr = listOf(init, T0, T0G, T0C, T0CF, T0F, T1, T1G, T1C, T1CF, T1F, W).toSMT2() + val relations = listOf(init, T0, T0G, T0C, T0CF, T0F, T1, T1G, T1C, T1CF, T1F, W) + val expr = relations.toSMT2() println(expr) + val solver = solverFactory.createHornSolver() + solver.add(relations) + solver.check() + Assertions.assertTrue(solver.status == SolverStatus.SAT) } - @Test - fun testPetersonNoCounting() { + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testPetersonNoCounting(name: Pair, solverFactory: SolverFactory) { val i2i = ArrayType.of(Int(), Int()) val pI = ParamHolder(Int()) @@ -532,23 +600,17 @@ class TestChcUtils { Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next Lt(Read(com, eid2), Read(com, eid)) -// T0(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += -// W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write -// T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc -// Eq(Add(eid, Int(2)), eid2) + // eid update -// Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) -// T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += -// W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write -// T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc -// Eq(Add(eid, Int(2)), eid2) + // eid update -// Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - !(T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with T1C(br, co, rf, com, eid2, turn_old, flag1_old, flag2_old, cnt).expr + Eq(Read(com, eid), Read(com, eid2))) - val expr = listOf(init, T0, T0G, T0C, T0F, T1, T1G, T1C, T1F, W).toSMT2() + val relations = listOf(init, T0, T0G, T0C, T0F, T1, T1G, T1C, T1F, W) + val expr = relations.toSMT2() println(expr) + val solver = solverFactory.createHornSolver() + solver.add(relations) + solver.check() + Assertions.assertTrue(solver.status == SolverStatus.SAT) } } From 5304b34eb79e3ad44a9255489cb5427fe130653c Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 00:02:59 +0200 Subject: [PATCH 15/24] logging if CHC serialization encounters a problem --- .../main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt index 61794654c7..14b570e3aa 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt @@ -248,8 +248,12 @@ private fun preVerificationLogging( if (!config.outputConfig.chcOutputConfig.disable) { xcfa.procedures.forEach { - val chcFile = File(resultFolder, "xcfa-${it.name}.smt2") - chcFile.writeText(it.toSMT2CHC()) + try { + val chcFile = File(resultFolder, "xcfa-${it.name}.smt2") + chcFile.writeText(it.toSMT2CHC()) + } catch (e: Exception) { + logger.write(INFO, "Could not write CHC file: " + e.stackTraceToString()) + } } } From b8a379f4c0e1d5afe1275b2f98407d6ff417f345 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 00:36:58 +0200 Subject: [PATCH 16/24] fixed tests for mac and win --- .../solver/smtlib/SmtLibHornSolverTest.kt | 12 ++++++++---- .../hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt | 19 +++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt index 73d869a91f..47f3e05a1d 100644 --- a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt @@ -30,6 +30,7 @@ import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.type.inttype.IntType import hu.bme.mit.theta.solver.SolverFactory import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException +import org.junit.Assume import org.junit.jupiter.api.* import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -51,7 +52,7 @@ class SmtLibHornSolverTest { @JvmStatic fun solvers(): List { - return solverFactories.map { Arguments.of(it.key, it.value) } + return SOLVERS.map { Arguments.of(it) } } @BeforeAll @@ -94,7 +95,8 @@ class SmtLibHornSolverTest { @ParameterizedTest(name = "[{index}] {0}") @MethodSource("solvers") - fun testSolvable(name: Pair, solverFactory: SolverFactory) { + fun testSolvable(name: Pair) { + val solverFactory = solverFactories[name]!! val solver = solverFactory.createHornSolver() solver.use { hornSolver -> val p = ParamHolder(Int()) @@ -137,7 +139,8 @@ class SmtLibHornSolverTest { @ParameterizedTest(name = "[{index}] {0}") @MethodSource("solvers") - fun testUnsolvable(name: Pair, solverFactory: SolverFactory) { + fun testUnsolvable(name: Pair) { + val solverFactory = solverFactories[name]!! val solver = solverFactory.createHornSolver() solver.use { hornSolver -> @@ -165,7 +168,8 @@ class SmtLibHornSolverTest { @ParameterizedTest(name = "[{index}] {0}") @MethodSource("solvers") - fun testNonlinearUnsolvable(name: Pair, solverFactory: SolverFactory) { + fun testNonlinearUnsolvable(name: Pair) { + val solverFactory = solverFactories[name]!! val solver = solverFactory.createHornSolver() solver.use { hornSolver -> diff --git a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt index 01a0f2003a..d248d52529 100644 --- a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt +++ b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt @@ -34,7 +34,9 @@ import hu.bme.mit.theta.solver.SolverFactory import hu.bme.mit.theta.solver.SolverStatus import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException -import org.junit.jupiter.api.* +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -56,8 +58,8 @@ class TestChcUtils { ) @JvmStatic - fun solvers(): List { - return solverFactories.map { Arguments.of(it.key, it.value) } + fun solvers(): List { + return SOLVERS.map { Arguments.of(it) } } @BeforeAll @@ -93,14 +95,10 @@ class TestChcUtils { } } - @BeforeEach - fun before() { - Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) - } - @ParameterizedTest(name = "[{index}] {0}") @MethodSource("solvers") - fun testPetersonManualCounting(name: Pair, solverFactory: SolverFactory) { + fun testPetersonManualCounting(name: Pair) { + val solverFactory = solverFactories[name]!! val i2i = ArrayType.of(Int(), Int()) val pI = ParamHolder(Int()) @@ -400,7 +398,8 @@ class TestChcUtils { @ParameterizedTest(name = "[{index}] {0}") @MethodSource("solvers") - fun testPetersonNoCounting(name: Pair, solverFactory: SolverFactory) { + fun testPetersonNoCounting(name: Pair) { + val solverFactory = solverFactories[name]!! val i2i = ArrayType.of(Int(), Int()) val pI = ParamHolder(Int()) From 0347609105bf1a63921a7175a2baf68c9016a848 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 12:22:18 +0200 Subject: [PATCH 17/24] Extracted Horn solver to separate smtlib class --- .../eldarica/EldaricaSmtLibSolverFactory.java | 4 +- .../impl/generic/GenericHornSolver.java | 158 ++++++++++++++ .../impl/generic/GenericSmtLibHornSolver.java | 205 +++++++++--------- .../generic/GenericSmtLibSolverFactory.java | 3 +- .../impl/golem/GolemSmtLibSolverFactory.java | 4 +- .../solver/smtlib/solver/SmtLibSolver.java | 119 +--------- ...Test.kt => GenericSmtLibHornSolverTest.kt} | 3 +- 7 files changed, 272 insertions(+), 224 deletions(-) create mode 100644 subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericHornSolver.java rename subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/{SmtLibHornSolverTest.kt => GenericSmtLibHornSolverTest.kt} (99%) diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java index d1f5545fb2..46ccc72a69 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/eldarica/EldaricaSmtLibSolverFactory.java @@ -19,7 +19,7 @@ import hu.bme.mit.theta.solver.ItpSolver; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.UCSolver; -import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibHornSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericHornSolver; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibOneshotSolverBinary; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; @@ -55,7 +55,7 @@ public HornSolver createHornSolver() { final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); final var solverBinary = new GenericSmtLibOneshotSolverBinary(solverPath, args); - return new GenericSmtLibHornSolver(symbolTable, transformationManager, termTransformer, solverBinary); + return new GenericHornSolver(symbolTable, transformationManager, termTransformer, solverBinary); } @Override diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericHornSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericHornSolver.java new file mode 100644 index 0000000000..1d0d28d410 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericHornSolver.java @@ -0,0 +1,158 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.solver.HornSolver; +import hu.bme.mit.theta.solver.ProofNode; +import hu.bme.mit.theta.solver.ProofNode.Builder; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolver; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; + + +/** + * This solver expects the Horn solver-style proofs for counterexamples + */ +public class GenericHornSolver extends SmtLibSolver implements HornSolver { + private static final Pattern CEX_PATTERN = Pattern.compile("([0-9]+):\s*(.*)(?=->)->(\s*([0-9]+)(,?\s+[0-9]+)*)?"); + private static final Pattern CEX_ROOT = Pattern.compile("([0-9]+):\s*(.*)"); + + private ProofNode proof = null; + + public GenericHornSolver(SmtLibSymbolTable symbolTable, SmtLibTransformationManager transformationManager, SmtLibTermTransformer termTransformer, SmtLibSolverBinary solverBinary) { + super(symbolTable, transformationManager, termTransformer, solverBinary, false, "HORN"); + } + + @Override + public void track(Expr assertion) { + throw new UnsupportedOperationException("Tracking is not supported by this solver."); + } + + @Override + public SolverStatus check() { + solverBinary.issueCommand("(check-sat)"); + final var response = solverBinary.readResponse().lines().toList(); + status = response.get(0).equals("sat") ? SolverStatus.SAT : response.get(0).equals("unsat") ? SolverStatus.UNSAT : null; + if (status == SolverStatus.SAT) { + // we have a model + final var sb = new StringBuilder(); + sb.append("("); + for (int i = 1; i < response.size(); i++) { + sb.append(response.get(i)).append("\n"); + } + sb.append(")"); + final var generalResponse = parseResponse(sb.toString()); + if (generalResponse.isSpecific() && generalResponse.asSpecific().isGetModelResponse()) { + model = new SmtLibValuation(symbolTable, transformationManager, termTransformer, + generalResponse.asSpecific().asGetModelResponse().getModel()); + } + } else if (status == SolverStatus.UNSAT) { + // we have a cex (beginning with an empty line) + final Map builderMap = new LinkedHashMap<>(); + final Map> dependencyMap = new LinkedHashMap<>(); + Builder root = null; + for (int i = 1; i < response.size(); i++) { + final var matcher = CEX_PATTERN.matcher(response.get(i)); + int idx = -1; + String term = ""; + List dependencies = null; + if (matcher.matches()) { + idx = Integer.parseInt(matcher.group(1)); + term = matcher.group(2); + if (matcher.group(3) != null) { + dependencies = Arrays.stream(matcher.group(3).split(",?\s+")).filter(it -> !it.isEmpty()).map(it -> Integer.parseInt(it.trim())).toList(); + } else { + dependencies = List.of(); + } + } else { + final var rootMatcher = CEX_ROOT.matcher(response.get(i)); + if (rootMatcher.matches()) { + idx = Integer.parseInt(rootMatcher.group(1)); + term = rootMatcher.group(2); + dependencies = List.of(); + } + } + if (idx != -1) { + final var expr = termTransformer.toExpr(term, Bool(), new SmtLibModel(Collections.emptyMap())); + final var builder = new ProofNode.Builder(expr); + if (root == null && expr.equals(False())) { + root = builder; + } + builderMap.put(idx, builder); + dependencyMap.put(builder, dependencies); + } + } + dependencyMap.forEach((builder, integers) -> + integers.forEach(integer -> builder.addChild(builderMap.get(integer))) + ); + proof = root.build(); + + } + return status; + } + + @Override + public void push() { + throw new UnsupportedOperationException("Push is not supported."); + } + + @Override + public void pop(int n) { + throw new UnsupportedOperationException("Pop is not supported."); + } + + @Override + public Collection> getUnsatCore() { + throw new UnsupportedOperationException("This solver cannot return unsat cores"); + } + + @Override + protected void issueGeneralCommand(String command) { + solverBinary.issueCommand(command); + } + + @Override + public ProofNode getProof() { + checkState(proof != null, "Proof cannot be null! Did you call check()?"); + return proof; + } + + @Override + public void clearState() { + super.clearState(); + proof = null; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java index 96cbecf5cb..7d68f5547e 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java @@ -13,141 +13,150 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.solver.smtlib.impl.generic; +import com.microsoft.z3.Z3Exception; import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.RefExpr; import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.type.bvtype.BvExprs; +import hu.bme.mit.theta.core.type.functype.FuncAppExpr; +import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.solver.HornSolver; import hu.bme.mit.theta.solver.ProofNode; import hu.bme.mit.theta.solver.ProofNode.Builder; -import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.SortContext; import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolver; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; -import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GetProofResponse; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; -import java.util.Arrays; -import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.LinkedHashMap; -import java.util.List; +import java.util.LinkedList; import java.util.Map; -import java.util.regex.Pattern; import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.decl.Decls.Const; +import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; - -public class GenericSmtLibHornSolver extends SmtLibSolver { - private static final Pattern CEX_PATTERN = Pattern.compile("([0-9]+):\s*(.*)(?=->)->(\s*([0-9]+)(,?\s+[0-9]+)*)?"); - private static final Pattern CEX_ROOT = Pattern.compile("([0-9]+):\s*(.*)"); - - private ProofNode proof = null; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static hu.bme.mit.theta.core.type.rattype.RatExprs.Rat; +import static hu.bme.mit.theta.core.utils.ExprUtils.extractFuncAndArgs; +import static hu.bme.mit.theta.core.utils.TypeUtils.cast; + +/** + * This class is a HornSolver that expects the proofs to be in the style of Z3 (using hyper-res + * predicates) + */ +public class GenericSmtLibHornSolver extends SmtLibSolver implements HornSolver { public GenericSmtLibHornSolver(SmtLibSymbolTable symbolTable, SmtLibTransformationManager transformationManager, SmtLibTermTransformer termTransformer, SmtLibSolverBinary solverBinary) { super(symbolTable, transformationManager, termTransformer, solverBinary, false, "HORN"); } - @Override - public void track(Expr assertion) { - throw new UnsupportedOperationException("Tracking is not supported by this solver."); + public static Type transformSort(final SortContext ctx) { + final String name = ctx.identifier().symbol().getText(); + return switch (name) { + case "Int" -> Int(); + case "Bool" -> Bool(); + case "Real" -> Rat(); + case "BitVec" -> { + assert ctx.identifier().index().size() == 1; + yield BvExprs.BvType(Integer.parseInt(ctx.identifier().index().get(0).getText())); + } + case "Array" -> { + assert ctx.sort().size() == 2; + yield Array(transformSort(ctx.sort().get(0)), transformSort(ctx.sort().get(1))); + } + default -> throw new UnsupportedOperationException(); + }; } @Override - public SolverStatus check() { - solverBinary.issueCommand("(check-sat)"); - final var response = solverBinary.readResponse().lines().toList(); - status = response.get(0).equals("sat") ? SolverStatus.SAT : response.get(0).equals("unsat") ? SolverStatus.UNSAT : null; - if (status == SolverStatus.SAT) { - // we have a model - final var sb = new StringBuilder(); - sb.append("("); - for (int i = 1; i < response.size(); i++) { - sb.append(response.get(i)).append("\n"); - } - sb.append(")"); - final var generalResponse = parseResponse(sb.toString()); - if (generalResponse.isSpecific() && generalResponse.asSpecific().isGetModelResponse()) { - model = new SmtLibValuation(symbolTable, transformationManager, termTransformer, - generalResponse.asSpecific().asGetModelResponse().getModel()); - } - } else if (status == SolverStatus.UNSAT) { - // we have a cex (beginning with an empty line) - final Map builderMap = new LinkedHashMap<>(); - final Map> dependencyMap = new LinkedHashMap<>(); - Builder root = null; - for (int i = 1; i < response.size(); i++) { - final var matcher = CEX_PATTERN.matcher(response.get(i)); - int idx = -1; - String term = ""; - List dependencies = null; - if (matcher.matches()) { - idx = Integer.parseInt(matcher.group(1)); - term = matcher.group(2); - if (matcher.group(3) != null) { - dependencies = Arrays.stream(matcher.group(3).split(",?\s+")).filter(it -> !it.isEmpty()).map(it -> Integer.parseInt(it.trim())).toList(); - } else { - dependencies = List.of(); - } - } else { - final var rootMatcher = CEX_ROOT.matcher(response.get(i)); - if (rootMatcher.matches()) { - idx = Integer.parseInt(rootMatcher.group(1)); - term = rootMatcher.group(2); - dependencies = List.of(); - } - } - if (idx != -1) { - final var expr = termTransformer.toExpr(term, Bool(), new SmtLibModel(Collections.emptyMap())); - final var builder = new ProofNode.Builder(expr); - if (root == null && expr.equals(False())) { - root = builder; - } - builderMap.put(idx, builder); - dependencyMap.put(builder, dependencies); + public ProofNode getProof() { + solverBinary.issueCommand("(get-proof)"); + var response = solverBinary.readResponse(); + final var res = parseResponse(response); + if (res.isError()) { + throw new SmtLibSolverException(res.getReason()); + } else if (res.isSpecific()) { + final GetProofResponse getModelResponse = res.asSpecific().asGetProofResponse(); + getModelResponse.getFunDeclarations().forEach((name, def) -> { + var type = transformSort(def.get2()); + for (SortContext s : def.get1()) { + type = FuncType.of(transformSort(s), type); } - } - dependencyMap.forEach((builder, integers) -> - integers.forEach(integer -> builder.addChild(builderMap.get(integer))) - ); - proof = root.build(); - + symbolTable.put(Const(name, type), name, def.get3()); + }); + final var proof = termTransformer.toExpr(getModelResponse.getProof(), Bool(), new SmtLibModel(Collections.emptyMap())); + return proofFromExpr(proof); + } else { + throw new AssertionError(); } - return status; } - @Override - public void push() { - throw new UnsupportedOperationException("Push is not supported."); - } + private ProofNode proofFromExpr(Expr proof) { + checkState(proof instanceof FuncAppExpr, "Proof must be a function application."); + int id = 0; + final Map, Integer> lookup = new LinkedHashMap<>(); - @Override - public void pop(int n) { - throw new UnsupportedOperationException("Pop is not supported."); - } + final var args = extractFuncAndArgs((FuncAppExpr) proof).get2(); - @Override - public Collection> getUnsatCore() { - throw new UnsupportedOperationException("This solver cannot return unsat cores"); - } + Deque> proofStack = new LinkedList<>(); + proofStack.push(args.get(0)); + lookup.put(args.get(0), id++); - @Override - protected void issueGeneralCommand(String command) { - solverBinary.issueCommand(command); - } + Expr root = cast(False(), Bool()); + final var rootBuilder = new ProofNode.Builder(root); - @Override - public ProofNode getProof() { - checkState(proof != null, "Proof cannot be null! Did you call check()?"); - return proof; + Map visited = new LinkedHashMap<>(); + visited.put(lookup.get(args.get(0)), rootBuilder); + + while (!proofStack.isEmpty()) { + final var proofNodeExpr = proofStack.pop(); + if (!visited.containsKey(lookup.getOrDefault(proofNodeExpr, -1))) { + throw new Z3Exception("Node should exist in the graph nodes"); + } + final var proofNode = visited.get(lookup.get(proofNodeExpr)); + + if (proofNodeExpr instanceof FuncAppExpr funcAppExpr) { + final var nameAndArgs = extractFuncAndArgs(funcAppExpr); + if (nameAndArgs.get1() instanceof RefExpr refName && refName.getDecl().getName().startsWith("hyper-res")) { + if (!nameAndArgs.get2().isEmpty()) { + for (int i = 1; i < nameAndArgs.get2().size() - 1; ++i) { + final var child = nameAndArgs.get2().get(i); + if (!visited.containsKey(lookup.getOrDefault(child, -1))) { + if (!lookup.containsKey(child)) { + lookup.put(child, id++); + } + visited.put(lookup.get(child), new Builder(extractProofExpr(child))); + proofStack.push(child); + } + proofNode.addChild(visited.get(lookup.get(child))); + } + } + } + } + } + return rootBuilder.build(); } - @Override - public void clearState() { - super.clearState(); - proof = null; + private Expr extractProofExpr(Expr expr) { + checkState(expr instanceof FuncAppExpr, "Proof should be function application."); + final var nameAndArgs = extractFuncAndArgs((FuncAppExpr) expr); + final var args = nameAndArgs.get2(); + final var lastArg = args.get(args.size() - 1); + checkState(lastArg instanceof FuncAppExpr, "Proof should be function application."); + return (Expr) lastArg; } + + } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java index 2bd45bf15d..f307e7c356 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java @@ -101,7 +101,6 @@ public HornSolver createHornSolver() { final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); final var solverBinary = new GenericSmtLibSolverBinary(solverPath, args, solverOverride); - return new SmtLibSolver(symbolTable, transformationManager, termTransformer, solverBinary, - false, "HORN"); + return new GenericSmtLibHornSolver(symbolTable, transformationManager, termTransformer, solverBinary); } } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java index a908b1f7c9..6abdd24d4a 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/golem/GolemSmtLibSolverFactory.java @@ -19,7 +19,7 @@ import hu.bme.mit.theta.solver.ItpSolver; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.UCSolver; -import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibHornSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericHornSolver; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibOneshotSolverBinary; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; @@ -55,7 +55,7 @@ public HornSolver createHornSolver() { final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); final var solverBinary = new GenericSmtLibOneshotSolverBinary(solverPath, args); - return new GenericSmtLibHornSolver(symbolTable, transformationManager, termTransformer, solverBinary); + return new GenericHornSolver(symbolTable, transformationManager, termTransformer, solverBinary); } @Override diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java index afe66be36e..e3a8ab545e 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java @@ -15,7 +15,6 @@ */ package hu.bme.mit.theta.solver.smtlib.solver; -import com.microsoft.z3.Z3Exception; import hu.bme.mit.theta.core.decl.ConstDecl; import hu.bme.mit.theta.core.model.Valuation; import hu.bme.mit.theta.core.type.Expr; @@ -24,13 +23,7 @@ import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.anytype.RefExpr; import hu.bme.mit.theta.core.type.booltype.BoolType; -import hu.bme.mit.theta.core.type.bvtype.BvExprs; -import hu.bme.mit.theta.core.type.functype.FuncAppExpr; -import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.core.utils.ExprUtils; -import hu.bme.mit.theta.solver.HornSolver; -import hu.bme.mit.theta.solver.ProofNode; -import hu.bme.mit.theta.solver.ProofNode.Builder; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverStatus; import hu.bme.mit.theta.solver.Stack; @@ -38,15 +31,12 @@ import hu.bme.mit.theta.solver.impl.StackImpl; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; -import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.SortContext; import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; -import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; import hu.bme.mit.theta.solver.smtlib.solver.parser.*; import hu.bme.mit.theta.solver.smtlib.solver.parser.CheckSatResponse; import hu.bme.mit.theta.solver.smtlib.solver.parser.GeneralResponse; import hu.bme.mit.theta.solver.smtlib.solver.parser.GetModelResponse; -import hu.bme.mit.theta.solver.smtlib.solver.parser.GetProofResponse; import hu.bme.mit.theta.solver.smtlib.solver.parser.GetUnsatCoreResponse; import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; @@ -59,24 +49,14 @@ import java.util.stream.Collectors; import java.util.Collection; import java.util.Collections; -import java.util.Deque; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkState; -import static hu.bme.mit.theta.core.decl.Decls.Const; -import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static hu.bme.mit.theta.core.type.rattype.RatExprs.Rat; -import static hu.bme.mit.theta.core.utils.ExprUtils.extractFuncAndArgs; -import static hu.bme.mit.theta.core.utils.TypeUtils.cast; -public class SmtLibSolver implements UCSolver, Solver, HornSolver { +public class SmtLibSolver implements UCSolver, Solver { private static final String ASSUMPTION_LABEL = "_LABEL_%d"; protected final SmtLibSymbolTable symbolTable; @@ -340,101 +320,4 @@ protected final GeneralResponse parseResponse(final String response) { throw new SmtLibSolverException("Could not parse solver output: " + response, e); } } - - - public static Type transformSort(final SortContext ctx) { - final String name = ctx.identifier().symbol().getText(); - return switch (name) { - case "Int" -> Int(); - case "Bool" -> Bool(); - case "Real" -> Rat(); - case "BitVec" -> { - assert ctx.identifier().index().size() == 1; - yield BvExprs.BvType(Integer.parseInt(ctx.identifier().index().get(0).getText())); - } - case "Array" -> { - assert ctx.sort().size() == 2; - yield Array(transformSort(ctx.sort().get(0)), transformSort(ctx.sort().get(1))); - } - default -> throw new UnsupportedOperationException(); - }; - } - - @Override - public ProofNode getProof() { - solverBinary.issueCommand("(get-proof)"); - var response = solverBinary.readResponse(); - final var res = parseResponse(response); - if (res.isError()) { - throw new SmtLibSolverException(res.getReason()); - } else if (res.isSpecific()) { - final GetProofResponse getModelResponse = res.asSpecific().asGetProofResponse(); - getModelResponse.getFunDeclarations().forEach((name, def) -> { - var type = transformSort(def.get2()); - for (SortContext s : def.get1()) { - type = FuncType.of(transformSort(s), type); - } - symbolTable.put(Const(name, type), name, def.get3()); - }); - final var proof = termTransformer.toExpr(getModelResponse.getProof(), Bool(), new SmtLibModel(Collections.emptyMap())); - return proofFromExpr(proof); - } else { - throw new AssertionError(); - } - } - - private ProofNode proofFromExpr(Expr proof) { - checkState(proof instanceof FuncAppExpr, "Proof must be a function application."); - int id = 0; - final Map, Integer> lookup = new LinkedHashMap<>(); - - final var args = extractFuncAndArgs((FuncAppExpr) proof).get2(); - - Deque> proofStack = new LinkedList<>(); - proofStack.push(args.get(0)); - lookup.put(args.get(0), id++); - - Expr root = cast(False(), Bool()); - final var rootBuilder = new ProofNode.Builder(root); - - Map visited = new LinkedHashMap<>(); - visited.put(lookup.get(args.get(0)), rootBuilder); - - while (!proofStack.isEmpty()) { - final var proofNodeExpr = proofStack.pop(); - if (!visited.containsKey(lookup.getOrDefault(proofNodeExpr, -1))) { - throw new Z3Exception("Node should exist in the graph nodes"); - } - final var proofNode = visited.get(lookup.get(proofNodeExpr)); - - if (proofNodeExpr instanceof FuncAppExpr funcAppExpr) { - final var nameAndArgs = extractFuncAndArgs(funcAppExpr); - if (nameAndArgs.get1() instanceof RefExpr refName && refName.getDecl().getName().startsWith("hyper-res")) { - if (!nameAndArgs.get2().isEmpty()) { - for (int i = 1; i < nameAndArgs.get2().size() - 1; ++i) { - final var child = nameAndArgs.get2().get(i); - if (!visited.containsKey(lookup.getOrDefault(child, -1))) { - if (!lookup.containsKey(child)) { - lookup.put(child, id++); - } - visited.put(lookup.get(child), new Builder(extractProofExpr(child))); - proofStack.push(child); - } - proofNode.addChild(visited.get(lookup.get(child))); - } - } - } - } - } - return rootBuilder.build(); - } - - private Expr extractProofExpr(Expr expr) { - checkState(expr instanceof FuncAppExpr, "Proof should be function application."); - final var nameAndArgs = extractFuncAndArgs((FuncAppExpr) expr); - final var args = nameAndArgs.get2(); - final var lastArg = args.get(args.size() - 1); - checkState(lastArg instanceof FuncAppExpr, "Proof should be function application."); - return (Expr) lastArg; - } } diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt similarity index 99% rename from subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt rename to subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt index 47f3e05a1d..fbbafab2c5 100644 --- a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibHornSolverTest.kt +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt @@ -30,14 +30,13 @@ import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.type.inttype.IntType import hu.bme.mit.theta.solver.SolverFactory import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException -import org.junit.Assume import org.junit.jupiter.api.* import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource -class SmtLibHornSolverTest { +class GenericSmtLibHornSolverTest { companion object { private var solverManager: SmtLibSolverManager? = null From 9de4ca36bca069d09a8abdd44bf11cf1a134f93a Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 12:25:11 +0200 Subject: [PATCH 18/24] Extracted horn solving from Z3 --- .../bme/mit/theta/solver/z3/Z3HornSolver.java | 134 ++++++++++++++++++ .../hu/bme/mit/theta/solver/z3/Z3Solver.java | 95 ++----------- .../mit/theta/solver/z3/Z3SolverFactory.java | 2 +- 3 files changed, 144 insertions(+), 87 deletions(-) create mode 100644 subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3HornSolver.java diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3HornSolver.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3HornSolver.java new file mode 100644 index 0000000000..6753c87f41 --- /dev/null +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3HornSolver.java @@ -0,0 +1,134 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.z3; + +import com.google.common.collect.ImmutableList; +import com.microsoft.z3.Context; +import com.microsoft.z3.FuncDecl; +import com.microsoft.z3.Solver; +import com.microsoft.z3.Status; +import com.microsoft.z3.Z3Exception; +import com.microsoft.z3.enumerations.Z3_decl_kind; +import hu.bme.mit.theta.common.container.Containers; +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.decl.Decl; +import hu.bme.mit.theta.core.model.Valuation; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.LitExpr; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.arraytype.ArrayType; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; +import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.solver.HornSolver; +import hu.bme.mit.theta.solver.ProofNode; +import hu.bme.mit.theta.solver.ProofNode.Builder; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.Stack; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.UnknownSolverStatusException; +import hu.bme.mit.theta.solver.impl.StackImpl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.decl.Decls.Const; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.UnsafeApp; +import static hu.bme.mit.theta.core.utils.TypeUtils.cast; + +final class Z3HornSolver extends Z3Solver implements HornSolver { + public Z3HornSolver(Z3SymbolTable symbolTable, Z3TransformationManager transformationManager, Z3TermTransformer termTransformer, Context z3Context, Solver z3Solver) { + super(symbolTable, transformationManager, termTransformer, z3Context, z3Solver); + } + + //// + + private Expr toProofExpr(com.microsoft.z3.Expr expr) { + final var args = expr.getArgs(); + final var lastArg = args[args.length - 1]; + checkState(lastArg.isApp()); + final var name = lastArg.getFuncDecl().getName().toString(); + final var params = lastArg.getArgs(); + final var paramValues = Arrays.stream(params).map(termTransformer::toExpr).toList(); + final List paramTypes = paramValues.stream().map(expr1 -> (Type) expr1.getType()).toList(); + + final var funcType = paramTypes.stream().reduce(Bool(), (res, param) -> FuncType.of(param, res)); + final var decl = Const(name, funcType); + Expr func = decl.getRef(); + for (Expr paramValue : paramValues) { + func = UnsafeApp(func, paramValue); + } + return (Expr) func; + } + + /** + * This is a best-effort solution, hopefully would support (most) CHCs at least. + * Taken from https://github.com/ethereum/solidity/blob/5917fd82b3ca4cab5f817f78b8da8ebe409dd02e/libsmtutil/Z3CHCInterface.cpp#L130 + * and adapted to the Java API. + */ + @Override + public ProofNode getProof() { + checkState(status == SolverStatus.UNSAT, "Cannot get proof if status is not UNSAT"); + com.microsoft.z3.Expr proof = z3Solver.getProof(); + + Deque> proofStack = new LinkedList<>(); + proofStack.push(proof.getArgs()[0]); + + Expr root = cast(False(), Bool()); + final var rootBuilder = new ProofNode.Builder(root); + + Map visited = new LinkedHashMap<>(); + visited.put(proofStack.peek().getId(), rootBuilder); + + while (!proofStack.isEmpty()) { + final var proofNodeExpr = proofStack.pop(); + if (!visited.containsKey(proofNodeExpr.getId())) { + throw new Z3Exception("Node should exist in the graph nodes"); + } + final var proofNode = visited.get(proofNodeExpr.getId()); + + if (proofNodeExpr.isApp() && proofNodeExpr.getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_HYPER_RESOLVE) { + if (proofNodeExpr.getArgs().length > 0) { + for (int i = 1; i < proofNodeExpr.getArgs().length - 1; ++i) { + final var child = proofNodeExpr.getArgs()[i]; + if (!visited.containsKey(child.getId())) { + visited.put(child.getId(), new Builder(toProofExpr(child))); + proofStack.push(child); + } + proofNode.addChild(visited.get(child.getId())); + } + } + } + } + + + return rootBuilder.build(); + } + +} diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java index c2c53a7c9a..1783f9fff6 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java @@ -18,8 +18,6 @@ import com.google.common.collect.ImmutableList; import com.microsoft.z3.FuncDecl; import com.microsoft.z3.Status; -import com.microsoft.z3.Z3Exception; -import com.microsoft.z3.enumerations.Z3_decl_kind; import hu.bme.mit.theta.common.container.Containers; import hu.bme.mit.theta.core.decl.ConstDecl; import hu.bme.mit.theta.core.decl.Decl; @@ -34,9 +32,6 @@ import hu.bme.mit.theta.core.type.enumtype.EnumLitExpr; import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.functype.FuncType; -import hu.bme.mit.theta.solver.HornSolver; -import hu.bme.mit.theta.solver.ProofNode; -import hu.bme.mit.theta.solver.ProofNode.Builder; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverStatus; import hu.bme.mit.theta.solver.Stack; @@ -45,33 +40,23 @@ import java.util.*; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Deque; -import java.util.LinkedHashMap; import java.util.LinkedList; -import java.util.List; import java.util.Map; import java.util.Optional; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static hu.bme.mit.theta.core.decl.Decls.Const; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; -import static hu.bme.mit.theta.core.type.functype.FuncExprs.App; -import static hu.bme.mit.theta.core.type.functype.FuncExprs.UnsafeApp; -import static hu.bme.mit.theta.core.utils.TypeUtils.cast; -final class Z3Solver implements UCSolver, Solver, HornSolver { +class Z3Solver implements UCSolver, Solver { - private final Z3SymbolTable symbolTable; - private final Z3TransformationManager transformationManager; - private final Z3TermTransformer termTransformer; + protected final Z3SymbolTable symbolTable; + protected final Z3TransformationManager transformationManager; + protected final Z3TermTransformer termTransformer; - private final com.microsoft.z3.Context z3Context; - private final com.microsoft.z3.Solver z3Solver; + protected final com.microsoft.z3.Context z3Context; + protected final com.microsoft.z3.Solver z3Solver; private final Stack> assertions; private final Map> assumptions; @@ -79,9 +64,9 @@ final class Z3Solver implements UCSolver, Solver, HornSolver { private static final String ASSUMPTION_LABEL = "_LABEL_%d"; private int labelNum = 0; - private Valuation model; - private Collection> unsatCore; - private SolverStatus status; + protected Valuation model; + protected Collection> unsatCore; + protected SolverStatus status; public Z3Solver(final Z3SymbolTable symbolTable, final Z3TransformationManager transformationManager, @@ -250,68 +235,6 @@ public void close() { z3Context.interrupt(); } - private Expr toProofExpr(com.microsoft.z3.Expr expr) { - final var args = expr.getArgs(); - final var lastArg = args[args.length - 1]; - checkState(lastArg.isApp()); - final var name = lastArg.getFuncDecl().getName().toString(); - final var params = lastArg.getArgs(); - final var paramValues = Arrays.stream(params).map(termTransformer::toExpr).toList(); - final List paramTypes = paramValues.stream().map(expr1 -> (Type) expr1.getType()).toList(); - - final var funcType = paramTypes.stream().reduce(Bool(), (res, param) -> FuncType.of(param, res)); - final var decl = Const(name, funcType); - Expr func = decl.getRef(); - for (Expr paramValue : paramValues) { - func = UnsafeApp(func, paramValue); - } - return (Expr) func; - } - - /** - * This is a best-effort solution, hopefully would support (most) CHCs at least. - * Taken from https://github.com/ethereum/solidity/blob/5917fd82b3ca4cab5f817f78b8da8ebe409dd02e/libsmtutil/Z3CHCInterface.cpp#L130 - * and adapted to the Java API. - */ - @Override - public ProofNode getProof() { - checkState(status == SolverStatus.UNSAT, "Cannot get proof if status is not UNSAT"); - com.microsoft.z3.Expr proof = z3Solver.getProof(); - - Deque> proofStack = new LinkedList<>(); - proofStack.push(proof.getArgs()[0]); - - Expr root = cast(False(), Bool()); - final var rootBuilder = new ProofNode.Builder(root); - - Map visited = new LinkedHashMap<>(); - visited.put(proofStack.peek().getId(), rootBuilder); - - while (!proofStack.isEmpty()) { - final var proofNodeExpr = proofStack.pop(); - if (!visited.containsKey(proofNodeExpr.getId())) { - throw new Z3Exception("Node should exist in the graph nodes"); - } - final var proofNode = visited.get(proofNodeExpr.getId()); - - if (proofNodeExpr.isApp() && proofNodeExpr.getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_HYPER_RESOLVE) { - if (proofNodeExpr.getArgs().length > 0) { - for (int i = 1; i < proofNodeExpr.getArgs().length - 1; ++i) { - final var child = proofNodeExpr.getArgs()[i]; - if (!visited.containsKey(child.getId())) { - visited.put(child.getId(), new Builder(toProofExpr(child))); - proofStack.push(child); - } - proofNode.addChild(visited.get(child.getId())); - } - } - } - } - - - return rootBuilder.build(); - } - //// private final class Z3Model extends Valuation { diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java index 73a67e3b86..613f1e772c 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java @@ -104,7 +104,7 @@ public HornSolver createHornSolver() { symbolTable, z3Context); final Z3TermTransformer termTransformer = new Z3TermTransformer(symbolTable); - return new Z3Solver(symbolTable, transformationManager, termTransformer, z3Context, + return new Z3HornSolver(symbolTable, transformationManager, termTransformer, z3Context, z3Solver); } From 544ea249365dabf4885448267915b6d2248033fd Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 12:47:13 +0200 Subject: [PATCH 19/24] Fixed tests --- .../bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt | 2 +- .../src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt index fbbafab2c5..9383d479b8 100644 --- a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt @@ -79,7 +79,7 @@ class GenericSmtLibHornSolverTest { fun destroy() { for ((solver, version) in SOLVERS) { try { - solverManager!!.uninstall(solver, version) + solverManager?.uninstall(solver, version) } catch (e: SmtLibSolverInstallerException) { e.printStackTrace() } diff --git a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt index d248d52529..6dd22e800d 100644 --- a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt +++ b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt @@ -87,7 +87,7 @@ class TestChcUtils { fun destroy() { for ((solver, version) in SOLVERS) { try { - solverManager!!.uninstall(solver, version) + solverManager?.uninstall(solver, version) } catch (e: SmtLibSolverInstallerException) { e.printStackTrace() } From 389021de06fa5a6a4a1dba0eecbbeef3fc45dc95 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 13:39:04 +0200 Subject: [PATCH 20/24] added assume to win/mac tests, and changed 20.04 to 24.04 --- .github/workflows/linux-build-test-deploy.yml | 2 +- .../test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux-build-test-deploy.yml b/.github/workflows/linux-build-test-deploy.yml index 2aed93145a..054988762b 100644 --- a/.github/workflows/linux-build-test-deploy.yml +++ b/.github/workflows/linux-build-test-deploy.yml @@ -101,7 +101,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, ubuntu-22.04, ubuntu-20.04] + os: [ubuntu-latest, ubuntu-22.04, ubuntu-24.04] needs: build runs-on: ${{ matrix.os }} steps: diff --git a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt index 6dd22e800d..487a206379 100644 --- a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt +++ b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt @@ -34,9 +34,7 @@ import hu.bme.mit.theta.solver.SolverFactory import hu.bme.mit.theta.solver.SolverStatus import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.* import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -95,6 +93,11 @@ class TestChcUtils { } } + @BeforeEach + fun before() { + Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) + } + @ParameterizedTest(name = "[{index}] {0}") @MethodSource("solvers") fun testPetersonManualCounting(name: Pair) { From 83a3c44525fe2dd93c656037e67d56187e11d583 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 13:50:19 +0200 Subject: [PATCH 21/24] removed 24.04 --- .github/workflows/linux-build-test-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux-build-test-deploy.yml b/.github/workflows/linux-build-test-deploy.yml index 054988762b..1ff709451b 100644 --- a/.github/workflows/linux-build-test-deploy.yml +++ b/.github/workflows/linux-build-test-deploy.yml @@ -101,7 +101,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, ubuntu-22.04, ubuntu-24.04] + os: [ubuntu-latest, ubuntu-22.04] needs: build runs-on: ${{ matrix.os }} steps: From 59539dcaea82497de52100adc8a4500c6688f6e3 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 18:16:56 +0200 Subject: [PATCH 22/24] Fix stuff after rebase --- .../javasmt/JavaSMTExprTransformer.java | 150 +++++++++++++++--- .../impl/generic/GenericHornSolver.java | 3 +- .../impl/generic/GenericSmtLibHornSolver.java | 3 +- .../solver/smtlib/solver/SmtLibSolver.java | 21 ++- 4 files changed, 143 insertions(+), 34 deletions(-) diff --git a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java index 024890cb7a..2e2cf0c32c 100644 --- a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java +++ b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java @@ -17,7 +17,6 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import com.google.common.collect.ImmutableList; import hu.bme.mit.theta.common.DispatchTable; import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.dsl.Env; @@ -31,23 +30,142 @@ import hu.bme.mit.theta.core.type.anytype.Dereference; import hu.bme.mit.theta.core.type.anytype.IteExpr; import hu.bme.mit.theta.core.type.anytype.RefExpr; -import hu.bme.mit.theta.core.type.arraytype.*; -import hu.bme.mit.theta.core.type.booltype.*; -import hu.bme.mit.theta.core.type.bvtype.*; +import hu.bme.mit.theta.core.type.arraytype.ArrayEqExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayInitExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayNeqExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayWriteExpr; +import hu.bme.mit.theta.core.type.booltype.AndExpr; +import hu.bme.mit.theta.core.type.booltype.ExistsExpr; +import hu.bme.mit.theta.core.type.booltype.FalseExpr; +import hu.bme.mit.theta.core.type.booltype.ForallExpr; +import hu.bme.mit.theta.core.type.booltype.IffExpr; +import hu.bme.mit.theta.core.type.booltype.ImplyExpr; +import hu.bme.mit.theta.core.type.booltype.NotExpr; +import hu.bme.mit.theta.core.type.booltype.OrExpr; +import hu.bme.mit.theta.core.type.booltype.TrueExpr; +import hu.bme.mit.theta.core.type.booltype.XorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAddExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAndExpr; +import hu.bme.mit.theta.core.type.bvtype.BvArithShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvConcatExpr; +import hu.bme.mit.theta.core.type.bvtype.BvEqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvExtractExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLogicShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvMulExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNegExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNotExpr; +import hu.bme.mit.theta.core.type.bvtype.BvOrExpr; +import hu.bme.mit.theta.core.type.bvtype.BvPosExpr; +import hu.bme.mit.theta.core.type.bvtype.BvRotateLeftExpr; +import hu.bme.mit.theta.core.type.bvtype.BvRotateRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSExtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSModExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSRemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvShiftLeftExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSignChangeExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSubExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvURemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvXorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvZExtExpr; import hu.bme.mit.theta.core.type.enumtype.EnumEqExpr; import hu.bme.mit.theta.core.type.enumtype.EnumLitExpr; import hu.bme.mit.theta.core.type.enumtype.EnumNeqExpr; import hu.bme.mit.theta.core.type.enumtype.EnumType; -import hu.bme.mit.theta.core.type.fptype.*; +import hu.bme.mit.theta.core.type.fptype.FpAbsExpr; +import hu.bme.mit.theta.core.type.fptype.FpAddExpr; +import hu.bme.mit.theta.core.type.fptype.FpAssignExpr; +import hu.bme.mit.theta.core.type.fptype.FpDivExpr; +import hu.bme.mit.theta.core.type.fptype.FpEqExpr; +import hu.bme.mit.theta.core.type.fptype.FpFromBvExpr; +import hu.bme.mit.theta.core.type.fptype.FpGeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpGtExpr; +import hu.bme.mit.theta.core.type.fptype.FpIsInfiniteExpr; +import hu.bme.mit.theta.core.type.fptype.FpIsNanExpr; +import hu.bme.mit.theta.core.type.fptype.FpLeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpLitExpr; +import hu.bme.mit.theta.core.type.fptype.FpLtExpr; +import hu.bme.mit.theta.core.type.fptype.FpMaxExpr; +import hu.bme.mit.theta.core.type.fptype.FpMinExpr; +import hu.bme.mit.theta.core.type.fptype.FpMulExpr; +import hu.bme.mit.theta.core.type.fptype.FpNegExpr; +import hu.bme.mit.theta.core.type.fptype.FpNeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpPosExpr; +import hu.bme.mit.theta.core.type.fptype.FpRemExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundToIntegralExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundingMode; +import hu.bme.mit.theta.core.type.fptype.FpSqrtExpr; +import hu.bme.mit.theta.core.type.fptype.FpSubExpr; +import hu.bme.mit.theta.core.type.fptype.FpToBvExpr; +import hu.bme.mit.theta.core.type.fptype.FpToFpExpr; import hu.bme.mit.theta.core.type.functype.FuncAppExpr; import hu.bme.mit.theta.core.type.functype.FuncType; -import hu.bme.mit.theta.core.type.inttype.*; -import hu.bme.mit.theta.core.type.rattype.*; +import hu.bme.mit.theta.core.type.inttype.IntAddExpr; +import hu.bme.mit.theta.core.type.inttype.IntDivExpr; +import hu.bme.mit.theta.core.type.inttype.IntEqExpr; +import hu.bme.mit.theta.core.type.inttype.IntGeqExpr; +import hu.bme.mit.theta.core.type.inttype.IntGtExpr; +import hu.bme.mit.theta.core.type.inttype.IntLeqExpr; +import hu.bme.mit.theta.core.type.inttype.IntLitExpr; +import hu.bme.mit.theta.core.type.inttype.IntLtExpr; +import hu.bme.mit.theta.core.type.inttype.IntModExpr; +import hu.bme.mit.theta.core.type.inttype.IntMulExpr; +import hu.bme.mit.theta.core.type.inttype.IntNegExpr; +import hu.bme.mit.theta.core.type.inttype.IntNeqExpr; +import hu.bme.mit.theta.core.type.inttype.IntPosExpr; +import hu.bme.mit.theta.core.type.inttype.IntRemExpr; +import hu.bme.mit.theta.core.type.inttype.IntSubExpr; +import hu.bme.mit.theta.core.type.inttype.IntToRatExpr; +import hu.bme.mit.theta.core.type.rattype.RatAddExpr; +import hu.bme.mit.theta.core.type.rattype.RatDivExpr; +import hu.bme.mit.theta.core.type.rattype.RatEqExpr; +import hu.bme.mit.theta.core.type.rattype.RatGeqExpr; +import hu.bme.mit.theta.core.type.rattype.RatGtExpr; +import hu.bme.mit.theta.core.type.rattype.RatLeqExpr; +import hu.bme.mit.theta.core.type.rattype.RatLitExpr; +import hu.bme.mit.theta.core.type.rattype.RatLtExpr; +import hu.bme.mit.theta.core.type.rattype.RatMulExpr; +import hu.bme.mit.theta.core.type.rattype.RatNegExpr; +import hu.bme.mit.theta.core.type.rattype.RatNeqExpr; +import hu.bme.mit.theta.core.type.rattype.RatPosExpr; +import hu.bme.mit.theta.core.type.rattype.RatSubExpr; +import hu.bme.mit.theta.core.type.rattype.RatToIntExpr; import hu.bme.mit.theta.core.utils.BvUtils; -import org.sosy_lab.java_smt.api.*; +import org.sosy_lab.java_smt.api.ArrayFormula; +import org.sosy_lab.java_smt.api.ArrayFormulaManager; +import org.sosy_lab.java_smt.api.BitvectorFormula; +import org.sosy_lab.java_smt.api.BitvectorFormulaManager; +import org.sosy_lab.java_smt.api.BooleanFormula; +import org.sosy_lab.java_smt.api.BooleanFormulaManager; +import org.sosy_lab.java_smt.api.EnumerationFormula; +import org.sosy_lab.java_smt.api.EnumerationFormulaManager; +import org.sosy_lab.java_smt.api.FloatingPointFormula; +import org.sosy_lab.java_smt.api.FloatingPointFormulaManager; +import org.sosy_lab.java_smt.api.FloatingPointRoundingMode; +import org.sosy_lab.java_smt.api.Formula; +import org.sosy_lab.java_smt.api.FormulaType; import org.sosy_lab.java_smt.api.FormulaType.FloatingPointType; +import org.sosy_lab.java_smt.api.FunctionDeclaration; +import org.sosy_lab.java_smt.api.IntegerFormulaManager; +import org.sosy_lab.java_smt.api.NumeralFormula; import org.sosy_lab.java_smt.api.NumeralFormula.IntegerFormula; import org.sosy_lab.java_smt.api.NumeralFormula.RationalFormula; +import org.sosy_lab.java_smt.api.QuantifiedFormulaManager; +import org.sosy_lab.java_smt.api.RationalFormulaManager; +import org.sosy_lab.java_smt.api.SolverContext; import java.util.ArrayList; import java.util.List; @@ -364,22 +482,6 @@ private static FloatingPointRoundingMode transformRoundingMode(final FpRoundingM }; } - private static Tuple2, List>> extractFuncAndArgs(final FuncAppExpr expr) { - final Expr func = expr.getFunc(); - final Expr arg = expr.getParam(); - if (func instanceof FuncAppExpr) { - final FuncAppExpr funcApp = (FuncAppExpr) func; - final Tuple2, List>> funcAndArgs = extractFuncAndArgs(funcApp); - final Expr resFunc = funcAndArgs.get1(); - final List> args = funcAndArgs.get2(); - final List> resArgs = ImmutableList.>builder().addAll(args).add(arg) - .build(); - return Tuple2.of(resFunc, resArgs); - } else { - return Tuple2.of(func, ImmutableList.of(arg)); - } - } - //// /* diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericHornSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericHornSolver.java index 1d0d28d410..e11d2b4fbe 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericHornSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericHornSolver.java @@ -22,6 +22,7 @@ import hu.bme.mit.theta.solver.ProofNode; import hu.bme.mit.theta.solver.ProofNode.Builder; import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibEnumStrategy; import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolver; import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; @@ -53,7 +54,7 @@ public class GenericHornSolver extends SmtLibSolver implements HornSolver { private ProofNode proof = null; public GenericHornSolver(SmtLibSymbolTable symbolTable, SmtLibTransformationManager transformationManager, SmtLibTermTransformer termTransformer, SmtLibSolverBinary solverBinary) { - super(symbolTable, transformationManager, termTransformer, solverBinary, false, "HORN"); + super(symbolTable, transformationManager, termTransformer, solverBinary, false, SmtLibEnumStrategy.getDefaultStrategy(), "HORN"); } @Override diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java index 7d68f5547e..ef13afcc93 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibHornSolver.java @@ -27,6 +27,7 @@ import hu.bme.mit.theta.solver.ProofNode; import hu.bme.mit.theta.solver.ProofNode.Builder; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.SortContext; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibEnumStrategy; import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolver; import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; @@ -59,7 +60,7 @@ public class GenericSmtLibHornSolver extends SmtLibSolver implements HornSolver { public GenericSmtLibHornSolver(SmtLibSymbolTable symbolTable, SmtLibTransformationManager transformationManager, SmtLibTermTransformer termTransformer, SmtLibSolverBinary solverBinary) { - super(symbolTable, transformationManager, termTransformer, solverBinary, false, "HORN"); + super(symbolTable, transformationManager, termTransformer, solverBinary, false, SmtLibEnumStrategy.getDefaultStrategy(), "HORN"); } public static Type transformSort(final SortContext ctx) { diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java index e3a8ab545e..4a30919ba3 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java @@ -21,19 +21,17 @@ import hu.bme.mit.theta.core.type.Type; import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.core.type.enumtype.EnumType; -import hu.bme.mit.theta.core.type.anytype.RefExpr; -import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.core.utils.ExprUtils; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverStatus; import hu.bme.mit.theta.solver.Stack; -import hu.bme.mit.theta.solver.*; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.UnknownSolverStatusException; import hu.bme.mit.theta.solver.impl.StackImpl; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; -import hu.bme.mit.theta.solver.smtlib.solver.parser.*; import hu.bme.mit.theta.solver.smtlib.solver.parser.CheckSatResponse; import hu.bme.mit.theta.solver.smtlib.solver.parser.GeneralResponse; import hu.bme.mit.theta.solver.smtlib.solver.parser.GetModelResponse; @@ -45,8 +43,6 @@ import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; -import java.util.*; -import java.util.stream.Collectors; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -71,7 +67,6 @@ public class SmtLibSolver implements UCSolver, Solver { protected final Stack> declarationStack; protected final Stack typeStack; protected final SmtLibEnumStrategy enumStrategy; - private final boolean unsatCoreEnabled; private int labelNum = 0; protected Valuation model; @@ -87,6 +82,16 @@ public SmtLibSolver( this(symbolTable, transformationManager, termTransformer, solverBinary, unsatCoreEnabled, SmtLibEnumStrategy.getDefaultStrategy(), "ALL"); } + public SmtLibSolver( + final SmtLibSymbolTable symbolTable, + final SmtLibTransformationManager transformationManager, + final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary, + boolean unsatCoreEnabled, + final SmtLibEnumStrategy enumStrategy + ) { + this(symbolTable, transformationManager, termTransformer, solverBinary, unsatCoreEnabled, enumStrategy, "ALL"); + } + public SmtLibSolver( final SmtLibSymbolTable symbolTable, final SmtLibTransformationManager transformationManager, @@ -127,7 +132,7 @@ public void add(final Expr assertion, final String term) { assertions.add(assertion); enumStrategy.declareDatatypes((Collection) consts.stream().map(ConstDecl::getType).toList(), typeStack, this::issueGeneralCommand); consts.stream().map(symbolTable::getDeclaration).forEach(this::issueGeneralCommand); - issueGeneralCommand(String.format("(assert %s)", enumStrategy.wrapAssertionExpression(term, ExprUtils.getConstants(assertion).stream().collect(Collectors.toMap(c -> c, symbolTable::getSymbol))))); + issueGeneralCommand(String.format("(assert %s)", enumStrategy.wrapAssertionExpression(term, ExprUtils.getConstants(assertion).stream().filter(symbolTable::definesConst).collect(Collectors.toMap(c -> c, symbolTable::getSymbol))))); clearState(); } From 9853615d5a34fdff838dc01e0ed95b055d6148d1 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 19:23:33 +0200 Subject: [PATCH 23/24] fixed hol<->fol translation --- .../theta/core/type/functype/FuncExprs.java | 8 +-- .../bme/mit/theta/core/utils/ExprUtils.java | 2 +- .../javasmt/JavaSMTTermTransformer.java | 40 +++++++++-- .../generic/GenericSmtLibTermTransformer.java | 66 +++++++++++++------ .../solver/smtlib/solver/SmtLibSolver.java | 2 +- .../theta/solver/z3/Z3TermTransformer.java | 39 +++++++---- 6 files changed, 111 insertions(+), 46 deletions(-) diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java index 7182c6f8c4..c6fbfdf480 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncExprs.java @@ -47,8 +47,8 @@ public static FuncAppExpr FuncAppExpr App( final Expr> func, final Expr paramHead, final List paramTail) { if (!paramTail.isEmpty()) { - final var newParamHead = paramTail.get(0); - final var newParamTail = paramTail.subList(1, paramTail.size()); + final var newParamHead = paramTail.get(paramTail.size() - 1); + final var newParamTail = paramTail.subList(0, paramTail.size() - 1); return App(App(func, newParamHead, newParamTail), paramHead); } else { return App(func, paramHead); @@ -58,8 +58,8 @@ private static FuncAppExpr FuncAppExpr App( final Expr> func, final List params) { checkArgument(!params.isEmpty()); - final var paramHead = params.get(0); - final var paramTail = params.subList(1, params.size()); + final var paramHead = params.get(params.size() - 1); + final var paramTail = params.subList(0, params.size() - 1); return App(func, paramHead, paramTail); } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java index db4ee0b410..6873033f60 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java @@ -518,7 +518,7 @@ public static Tuple2, List>> extractFuncAndArgs(final FuncAppExp final Tuple2, List>> funcAndArgs = extractFuncAndArgs(funcApp); final Expr resFunc = funcAndArgs.get1(); final List> args = funcAndArgs.get2(); - final List> resArgs = ImmutableList.>builder().add(arg).addAll(args) + final List> resArgs = ImmutableList.>builder().addAll(args).add(arg) .build(); return Tuple2.of(resFunc, resArgs); } else { diff --git a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTermTransformer.java b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTermTransformer.java index ae859fc10d..c0c17aeed0 100644 --- a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTermTransformer.java +++ b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTermTransformer.java @@ -30,38 +30,66 @@ import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.booltype.BoolType; -import hu.bme.mit.theta.core.type.bvtype.*; +import hu.bme.mit.theta.core.type.bvtype.BvExtractExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSExtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.bvtype.BvZExtExpr; import hu.bme.mit.theta.core.type.enumtype.EnumLitExpr; import hu.bme.mit.theta.core.type.enumtype.EnumType; -import hu.bme.mit.theta.core.type.fptype.*; +import hu.bme.mit.theta.core.type.fptype.FpFromBvExpr; +import hu.bme.mit.theta.core.type.fptype.FpLitExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundingMode; +import hu.bme.mit.theta.core.type.fptype.FpToBvExpr; +import hu.bme.mit.theta.core.type.fptype.FpToFpExpr; +import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.core.type.functype.FuncExprs; import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.core.type.inttype.IntLitExpr; import hu.bme.mit.theta.core.utils.BvUtils; import hu.bme.mit.theta.core.utils.TypeUtils; import org.sosy_lab.common.rationals.Rational; -import org.sosy_lab.java_smt.api.*; +import org.sosy_lab.java_smt.api.BitvectorFormula; +import org.sosy_lab.java_smt.api.BooleanFormula; +import org.sosy_lab.java_smt.api.FloatingPointFormula; +import org.sosy_lab.java_smt.api.FloatingPointNumber; +import org.sosy_lab.java_smt.api.Formula; +import org.sosy_lab.java_smt.api.FormulaType; import org.sosy_lab.java_smt.api.FormulaType.ArrayFormulaType; import org.sosy_lab.java_smt.api.FormulaType.BitvectorType; import org.sosy_lab.java_smt.api.FormulaType.FloatingPointType; +import org.sosy_lab.java_smt.api.FunctionDeclaration; +import org.sosy_lab.java_smt.api.FunctionDeclarationKind; +import org.sosy_lab.java_smt.api.Model; import org.sosy_lab.java_smt.api.QuantifiedFormulaManager.Quantifier; +import org.sosy_lab.java_smt.api.SolverContext; import org.sosy_lab.java_smt.api.visitors.FormulaVisitor; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.function.*; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.*; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; import static hu.bme.mit.theta.core.decl.Decls.Const; import static hu.bme.mit.theta.core.decl.Decls.Param; import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.*; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Exists; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Forall; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.True; import static hu.bme.mit.theta.core.type.fptype.FpExprs.FpAssign; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; import static hu.bme.mit.theta.core.type.rattype.RatExprs.Rat; diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java index f1172fe33c..0a3dbbb376 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java @@ -18,6 +18,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import hu.bme.mit.theta.common.QuadFunction; import hu.bme.mit.theta.common.TernaryOperator; import hu.bme.mit.theta.common.TriFunction; @@ -28,17 +29,21 @@ import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.LitExpr; import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.abstracttype.*; +import hu.bme.mit.theta.core.type.abstracttype.AddExpr; +import hu.bme.mit.theta.core.type.abstracttype.EqExpr; +import hu.bme.mit.theta.core.type.abstracttype.GeqExpr; +import hu.bme.mit.theta.core.type.abstracttype.GtExpr; +import hu.bme.mit.theta.core.type.abstracttype.LeqExpr; +import hu.bme.mit.theta.core.type.abstracttype.LtExpr; +import hu.bme.mit.theta.core.type.abstracttype.ModExpr; +import hu.bme.mit.theta.core.type.abstracttype.MulExpr; +import hu.bme.mit.theta.core.type.abstracttype.NegExpr; +import hu.bme.mit.theta.core.type.abstracttype.RemExpr; +import hu.bme.mit.theta.core.type.abstracttype.SubExpr; import hu.bme.mit.theta.core.type.anytype.IteExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.arraytype.ArrayWriteExpr; -import hu.bme.mit.theta.core.type.booltype.*; -import hu.bme.mit.theta.core.type.bvtype.*; -import hu.bme.mit.theta.core.type.enumtype.EnumLitExpr; -import hu.bme.mit.theta.core.type.enumtype.EnumType; -import hu.bme.mit.theta.core.type.fptype.*; -import hu.bme.mit.theta.core.type.functype.FuncExprs; import hu.bme.mit.theta.core.type.booltype.AndExpr; import hu.bme.mit.theta.core.type.booltype.BoolExprs; import hu.bme.mit.theta.core.type.booltype.IffExpr; @@ -76,6 +81,8 @@ import hu.bme.mit.theta.core.type.bvtype.BvURemExpr; import hu.bme.mit.theta.core.type.bvtype.BvXorExpr; import hu.bme.mit.theta.core.type.bvtype.BvZExtExpr; +import hu.bme.mit.theta.core.type.enumtype.EnumLitExpr; +import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.fptype.FpAbsExpr; import hu.bme.mit.theta.core.type.fptype.FpAddExpr; import hu.bme.mit.theta.core.type.fptype.FpDivExpr; @@ -116,8 +123,16 @@ import java.math.BigDecimal; import java.math.BigInteger; -import java.util.*; -import java.util.function.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; @@ -125,14 +140,30 @@ import static hu.bme.mit.theta.core.decl.Decls.Const; import static hu.bme.mit.theta.core.decl.Decls.Param; import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.*; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Exists; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Forall; import static hu.bme.mit.theta.core.type.functype.FuncExprs.Func; import static hu.bme.mit.theta.core.type.functype.FuncExprs.UnsafeApp; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; import static hu.bme.mit.theta.core.type.rattype.RatExprs.Rat; import static hu.bme.mit.theta.core.utils.TypeUtils.cast; import static hu.bme.mit.theta.core.utils.TypeUtils.castBv; -import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.*; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.BinaryContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.DecimalContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Exists_termContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Forall_termContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Generic_termContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.HexadecimalContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.IdentifierContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.IndexContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Let_termContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.NumeralContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Qual_identifierContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.SortContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Spec_constantContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.SymbolContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.TermContext; import static java.util.stream.Collectors.toList; public class GenericSmtLibTermTransformer implements SmtLibTermTransformer { @@ -390,7 +421,7 @@ protected Expr transformFuncDef(final SMTLIBv2Parser.Function_defContext ctx, pushParams(paramDecls, vars); var op = transformTerm(ctx.term(), model, vars); popParams(paramDecls, vars); - for (ParamDecl param : paramDecls) { + for (ParamDecl param : Lists.reverse(paramDecls)) { op = Func(param, op); } return op; @@ -502,15 +533,8 @@ private

Expr createFuncAppExpr(final String funcExpr = toFuncLitExpr(funDefImpl, model); } - assert funcExpr.getType() instanceof FuncType; - - var expr = funcExpr; - for (TermContext funAppParam : funAppParams) { - final var paramExpr = transformTerm(funAppParam, model, vars); - expr = UnsafeApp(expr, paramExpr); - } - - return expr; + final List> params = funAppParams.stream().map(it -> (Expr) transformTerm(it, model, vars)).collect(Collectors.toUnmodifiableList()); + return UnsafeApp(funcExpr, params); } protected Expr transformLetTerm(final Let_termContext ctx, final SmtLibModel model, diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java index 4a30919ba3..1fd60aa256 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java @@ -130,7 +130,7 @@ public void add(final Expr assertion, final String term) { declarationStack.add(consts); assertions.add(assertion); - enumStrategy.declareDatatypes((Collection) consts.stream().map(ConstDecl::getType).toList(), typeStack, this::issueGeneralCommand); + enumStrategy.declareDatatypes(consts.stream().map(ConstDecl::getType).toList(), typeStack, this::issueGeneralCommand); consts.stream().map(symbolTable::getDeclaration).forEach(this::issueGeneralCommand); issueGeneralCommand(String.format("(assert %s)", enumStrategy.wrapAssertionExpression(term, ExprUtils.getConstants(assertion).stream().filter(symbolTable::definesConst).collect(Collectors.toMap(c -> c, symbolTable::getSymbol))))); diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java index d8ed556556..c91c77d0d1 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java @@ -16,9 +16,6 @@ package hu.bme.mit.theta.solver.z3; import com.google.common.collect.ImmutableList; -import com.microsoft.z3.*; -import com.microsoft.z3.enumerations.Z3_decl_kind; -import com.microsoft.z3.enumerations.Z3_sort_kind; import com.microsoft.z3.ArrayExpr; import com.microsoft.z3.ArraySort; import com.microsoft.z3.FPNum; @@ -26,6 +23,8 @@ import com.microsoft.z3.Model; import com.microsoft.z3.Sort; import com.microsoft.z3.Z3Exception; +import com.microsoft.z3.enumerations.Z3_decl_kind; +import com.microsoft.z3.enumerations.Z3_sort_kind; import hu.bme.mit.theta.common.TernaryOperator; import hu.bme.mit.theta.common.TriFunction; import hu.bme.mit.theta.common.Tuple2; @@ -34,12 +33,25 @@ import hu.bme.mit.theta.core.decl.ParamDecl; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.abstracttype.*; +import hu.bme.mit.theta.core.type.abstracttype.AddExpr; +import hu.bme.mit.theta.core.type.abstracttype.EqExpr; +import hu.bme.mit.theta.core.type.abstracttype.GeqExpr; +import hu.bme.mit.theta.core.type.abstracttype.GtExpr; +import hu.bme.mit.theta.core.type.abstracttype.LeqExpr; +import hu.bme.mit.theta.core.type.abstracttype.LtExpr; +import hu.bme.mit.theta.core.type.abstracttype.MulExpr; import hu.bme.mit.theta.core.type.anytype.IteExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.arraytype.ArrayWriteExpr; -import hu.bme.mit.theta.core.type.booltype.*; +import hu.bme.mit.theta.core.type.booltype.AndExpr; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.type.booltype.FalseExpr; +import hu.bme.mit.theta.core.type.booltype.IffExpr; +import hu.bme.mit.theta.core.type.booltype.ImplyExpr; +import hu.bme.mit.theta.core.type.booltype.NotExpr; +import hu.bme.mit.theta.core.type.booltype.OrExpr; +import hu.bme.mit.theta.core.type.booltype.TrueExpr; import hu.bme.mit.theta.core.type.enumtype.EnumLitExpr; import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.fptype.FpRoundingMode; @@ -67,16 +79,21 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.*; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; import static hu.bme.mit.theta.common.Utils.head; import static hu.bme.mit.theta.common.Utils.tail; import static hu.bme.mit.theta.core.decl.Decls.Param; import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.*; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Exists; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Forall; import static hu.bme.mit.theta.core.type.bvtype.BvExprs.BvType; -import static hu.bme.mit.theta.core.type.functype.FuncExprs.App; import static hu.bme.mit.theta.core.type.functype.FuncExprs.Func; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.UnsafeApp; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; import static hu.bme.mit.theta.core.type.rattype.RatExprs.Rat; import static java.lang.String.format; @@ -378,11 +395,7 @@ private

Expr toApp(Expr> expr if (terms.size() == 0) { return expr; } - final com.microsoft.z3.Expr term = terms.get(0); - terms.remove(0); - final Expr

transformed = (Expr

) transform(term, model, vars); - return toApp((Expr, R>>) App(expr, transformed), terms, model, - vars); + return UnsafeApp(expr, terms.stream().map(it -> transform(it, model, vars)).collect(Collectors.toUnmodifiableList())); } //// From ca7baccbf77dfcbf409dbfde63a685f0538acde8 Mon Sep 17 00:00:00 2001 From: Levente Bajczi Date: Tue, 9 Jul 2024 19:47:34 +0200 Subject: [PATCH 24/24] Disable horn test on non-linux machines --- .../java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt index 24bb990839..859165d9cf 100644 --- a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt +++ b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt @@ -15,6 +15,7 @@ */ package hu.bme.mit.theta.solver.z3 +import hu.bme.mit.theta.common.OsHelper import hu.bme.mit.theta.core.ParamHolder import hu.bme.mit.theta.core.Relation import hu.bme.mit.theta.core.decl.Decls.Const @@ -28,6 +29,8 @@ import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.type.inttype.IntType import hu.bme.mit.theta.solver.HornSolver import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assumptions +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -45,6 +48,11 @@ class Z3HornSolverTest { } } + @BeforeEach + fun before() { + Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) + } + @ParameterizedTest @MethodSource("solvers") fun testSolvable(solver: HornSolver) {